1. Си / Говнокод #27973

    +1

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    // тред лоулевел-хаков #1
    
    #include <stdio.h>
    #include <stdlib.h>
    
    void hello(void)
    {
            puts("Hello!");
    }
    
    void goodbye(void)
    {
            puts("Goodbye!");
    }
    
    void set_sp_(void *p)
    {
            void *s[0];
            s[0] = p;
    }
    
    void set_sp(void *p)
    {
            set_sp_((void*)p);
    }
    
    int main(void)
    {
            void *new_stack[1024] = {
                    [1019] = (void*)0,
                    [1020] = (void*)&hello,
                    [1021] = (void*)&goodbye,
                    [1022] = (void*)&exit
            };
            set_sp((void*)&new_stack[1019]);
            return 0;
    }

    UB-ы, платформенно зависимый кот, битовая магия

    всё сюда
    |
    |
    \/

    Запостил: Hu3KoypoBHeBblunemyx, 28 Января 2022

    Комментарии (35) RSS

    • Циклы реализованые через фрейм пойнтер и фикс адреса возврата, автор, предположительно, inkanus_gray:
      #include <stdio.h>
      typedef struct {
        int endValue;
        void * startBody;
      } loop_record;
      
      loop_record stack[1000];
      int head = 0;
      
      void push(loop_record value) {
          stack[head] = value;
          head++;
      }
      
      loop_record pop() {
          head--;
          if (head < 0) exit(255);
          return stack[head];
      }
      
      void _do(void * label, int * counter, int from, int to) {
        void ** frame = __builtin_frame_address(0);
        *counter = from;
      
        if (*counter > to) {
          frame[1] = label;  // Эквивалент goto *label
          return;
        }
      
        loop_record current;
        current.endValue = to;
        current.startBody = frame[1];
        push(current);
      }
      
      void _next(int * counter) {
        void ** frame = __builtin_frame_address(0);
        (*counter)++;
      
        loop_record current = pop();
        if (*counter <= current.endValue) {
          push(current);
          frame[1] = current.startBody; // Эквивалент goto *startBody (на след. инструкцию после вызова _do)
        }
      }
      
      int main() {
        int i;
        _do(&&end,&i, 1, 5);
          printf("Хорошо живёт на свете Винни-пух (%d).\n", i);
        _next(&i);
        end:
          printf("Оттого поёт он эти песни вслух.\n");
      }
      Ответить
      • #include <stdio.h>
        typedef struct {
          int endValue;
          void * startBody;
        } loop_record;
        
        loop_record stack[1000];
        int head = 0;
        
        void push(loop_record value) {
            stack[head] = value;
            head++;
        }
        
        loop_record pop() {
            head--;
            if (head < 0) exit(255);
            return stack[head];
        }
        
        void _for(int * counter, int from, int to) {
          void ** frame = __builtin_frame_address(0);
          *counter = from;
        
          loop_record current;
          current.endValue = to;
          current.startBody = frame[1];
          push(current);
        }
        
        void _next(int * counter) {
          void ** frame = __builtin_frame_address(0);
          (*counter)++;
        
          loop_record current = pop();
          if (*counter <= current.endValue) {
            push(current);
            frame[1] = current.startBody; // Эквивалент goto *startBody (на след. инструкцию после вызова _do)
          }
        }
        
        int main() {
          int i;
          _for(&i, 1, 5);
            printf("Хорошо живёт на свете Винни-пух (%d).\n", i);
          _next(&i);
          printf("Оттого поёт он эти песни вслух.\n");
        }
        Ответить
        • таблица переходов оттуда же
          #include <stdio.h>
          #include <setjmp.h>
          #include <string.h>
          typedef struct {
            jmp_buf environ;
          } loop_record;
          
          loop_record stack[1000];
          int head = 0;
          
          void push(loop_record value) {
              stack[head] = value;
              head++;
          }
          
          loop_record pop() {
              head--;
              if (head < 0) exit(255);
              return stack[head];
          }
          
          void _switch(int counter, void ** labels) {
            jmp_buf env;
            int jmpval = setjmp(env);
          
            if (jmpval == 0) {
              loop_record current;
              memmove(current.environ, env, sizeof(env));
              push(current);
              void ** frame = __builtin_frame_address(0);
              frame[1] = labels[counter];
            } else {
              void ** frame = __builtin_frame_address(0);
              frame[1] = labels[0]; // выходим
            }
          }
          
          void _break() {
            loop_record current = pop();
            longjmp(current.environ, 1);
          }
          
          int main() {
            void * jmpTable[] = {&&end, &&case_1, &&case_2, &&case_3};
            _switch(2, jmpTable);
              case_1:
              printf("Первый багор.\n");
              _break();
              case_2:
              printf("Второй багор.\n");
              _break();
              case_3:
              printf("Третий багор.\n");
              _break();
            end:
            printf("Всё!.\n");
          }
          Ответить
          • #include <stdio.h>
            typedef struct {
              void * ret;
            } loop_record;
            
            loop_record stack[1000];
            int head = 0;
            
            void push(loop_record value) {
                stack[head] = value;
                head++;
            }
            
            loop_record pop() {
                head--;
                if (head < 0) exit(255);
                return stack[head];
            }
            
            void _switch(int counter, void ** labels) {
                loop_record current;
                current.ret = labels[0];
                push(current);
                void ** frame = __builtin_frame_address(0);
                frame[1] = labels[counter];
            }
            
            void _break() {
                loop_record current = pop();
                void ** frame = __builtin_frame_address(0);
                frame[1] = current.ret;
            }
            
            int main() {
              void * jmpTable[] = {&&end, &&case_1, &&case_2, &&case_3};
              _switch(2, jmpTable);
                case_1:
                printf("Первый багор.\n");
                _break();
                case_2:
                printf("Второй багор.\n");
                _break();
                case_3:
                printf("Третий багор.\n");
                _break();
              end:
              printf("Всё!.\n");
            }
            Ответить
        • puts("Хорошо живёт на свете Лоулевельный Питух\n");
          puts("Оттого поёт он эти хаки вслух.\n");
          Ответить
      • Простите, перепутал с inkanus-gray.

        Какой багор )))
        Ответить
    • DaveMustAim - файка нидлеса?
      Ответить
    • Мне всё лень было разбиратся, а оказывается там на стеке просто питуз
      nemyx@pryschi:~$ cat test.c
      #include <stdio.h>
      #include <stdlib.h>
      
      void hello(void)
      {
              puts("Hello!");
      }
      
      void goodbye(void)
      {
              puts("Goodbye!");
      }
      
      void set_sp_(void *p)
      {
              void *s[0];
              s[1] = p;
      }
      
      void set_sp(void *p)
      {
              set_sp_((void*)p);
      }
      
      int main(void)
      {
              void *new_stack[1024] = {
                      [1019] = (void*)0,
                      [1020] = (void*)&hello,
                      [1021] = (void*)&goodbye,
                      [1022] = (void*)&exit
              };
              set_sp((void*)&new_stack[1019]);
              return 0;
      }
      
      nemyx@pryschi:~$ gcc -g -fstack-protector test.c
      nemyx@pryschi:~$ ./a.out
      Hello!
      Goodbye!
      nemyx@pryschi:~$
      Ответить
      • Теперь -fstack-protector-strong не проходит
        Ответить
        • Ого питушня какая
          (gdb) disassemble set_sp_
          Dump of assembler code for function set_sp_:
             0x000000000000116b <+0>:     push   %rbp
             0x000000000000116c <+1>:     mov    %rsp,%rbp
             0x000000000000116f <+4>:     sub    $0x20,%rsp
             0x0000000000001173 <+8>:     mov    %rdi,-0x18(%rbp)
             0x0000000000001177 <+12>:    mov    %fs:0x28,%rax
             0x0000000000001180 <+21>:    mov    %rax,-0x8(%rbp)
             0x0000000000001184 <+25>:    xor    %eax,%eax
             0x0000000000001186 <+27>:    mov    -0x18(%rbp),%rax
             0x000000000000118a <+31>:    mov    %rax,-0x8(%rbp)
             0x000000000000118e <+35>:    nop
             0x000000000000118f <+36>:    mov    -0x8(%rbp),%rax
             0x0000000000001193 <+40>:    xor    %fs:0x28,%rax
             0x000000000000119c <+49>:    je     0x11a3 <set_sp_+56>
             0x000000000000119e <+51>:    callq  0x1040 <__stack_chk_fail@plt>
             0x00000000000011a3 <+56>:    leaveq
             0x00000000000011a4 <+57>:    retq
          End of assembler dump.
          Ответить
          • Хм, просто канарейку положили перед сохранёнными rbp/rip и называют это strong?

            Впрочем, от переполнения буфера это действительно спасёт т.к. задев сохранённый rbp или rip ты неизбежно заденешь и канарейку (т.к. она отделяет их от локалок).
            Ответить
            • Блу-блу-блу канари,
              Кви-кви-кви си перде леко...
              Ответить
            • Ага, а с твоим -fstack-protector на стеке просто какой-то лишний петух. Только от переполнения на 1 спасёт
              Dump of assembler code for function set_sp_:
                 0x000000000000115b <+0>:     push   %rbp
                 0x000000000000115c <+1>:     mov    %rsp,%rbp
                 0x000000000000115f <+4>:     mov    %rdi,-0x8(%rbp)
                 0x0000000000001163 <+8>:     mov    -0x8(%rbp),%rax
                 0x0000000000001167 <+12>:    mov    %rax,0x0(%rbp)
                 0x000000000000116b <+16>:    nop
                 0x000000000000116c <+17>:    pop    %rbp
                 0x000000000000116d <+18>:    retq
              End of assembler dump.
              Ответить
              • > какой-то лишний петух

                Я здесь вообще не вижу никаких стек протекторов, просто кто-то забыл включить оптимизацию и конпелятор был вынужден сохранять аргумент на стек чтобы отладчику потом не пришлось выковыривать его из других фреймов.
                Ответить
                • Ой, я не то запостил)
                  Dump of assembler code for function set_sp_:
                     0x000000000000115b <+0>:     push   %rbp
                     0x000000000000115c <+1>:     mov    %rsp,%rbp
                     0x000000000000115f <+4>:     mov    %rdi,-0x18(%rbp)
                     0x0000000000001163 <+8>:     mov    -0x18(%rbp),%rax
                     0x0000000000001167 <+12>:    mov    %rax,0x0(%rbp)
                     0x000000000000116b <+16>:    nop
                     0x000000000000116c <+17>:    pop    %rbp
                     0x000000000000116d <+18>:    retq
                  End of assembler dump.
                  Ответить
                  • Мне нужно найти 10 отличий?

                    Возможно, конпелятор охуел от обращения к массиву из 0 элементов, подумал что это в любом случае UB и не стал вставлять протектор.

                    З.Ы. А, вижу, там просто зазор от локалок до границы фрейма больше стал. Кстати прикольно, на этом примере видно, как конпелятор юзает редзону не двигая rsp. В ядре такой код пизданулся бы на первом прерывании.
                    Ответить
                    • Да, зазорчик между концом мосива и фрейм пойнтером.

                      А я сперва подумал, что там блу канари)

                      > В ядре такой код пизданулся бы
                      А для едра с каким ключиком конпелять?
                      Ответить
                    • Потому что обработчик прерывания в душе не ебет что ты там в крсную зону насрал?
                      Ответить
                      • Ну да. Хотя в теории можно было бы немного переделать проц, чтобы он мог немного отступать на входе в прерывание. Пирфоманс ядрёного кода был бы выше...
                        Ответить
                  • А, понятно.

                    This includes functions that call alloca, and functions with buffers larger than or equal to 8 bytes. У тебя буфер маловат, гцц считает, что его невыгодно/нинужно защищать.

                    А strong уже на все функции с массивами работает.
                    Ответить
                    • Какое классовое неравенство )))
                      Ответить
                      • >>> class A:
                        ...     pass
                        ...
                        >>> class B:
                        ...     pass
                        ...
                        >>> A == B
                        False
                        Ответить
                        • А в «ПХП» классы — не first-class citizens, и так не сделать. Поэтому я не за «ПХП»
                          Ответить
                          • В «ПХП» вообще нельзя сравнивать типы. Можно сравнивать значения.

                            Поэтому нужно либо создавать экземпляры классов, либо сравнивать их статические свойства. Например, есть встроенное свойство class, которое возвращает его имя:

                            A::class == B::class

                            Вернёт false, потому что 'A' не равно 'B'.
                            Ответить
          • З.Ы. И ты как раз в канарейку и хуйнул свой указатель судя по строке 0x118a. Попробуй s[2] = p.
            Ответить
            • Угу роботает, не понятно только, зачме xor %eax, %eax и nop
              Ответить
              • Подправил версию для ideone: https://ideone.com/m4xsxq
                Походу они с оптимизациями собирают
                (gdb) disassemble set_sp_
                Dump of assembler code for function set_sp_:
                   0x000000000000115f <+0>:     retq
                End of assembler dump.
                (gdb) Quit
                (gdb) disassemble set_sp
                Dump of assembler code for function set_sp:
                   0x0000000000001160 <+0>:     retq
                End of assembler dump.
                Какой багор )))
                Ответить
              • > xor %eax, %eax

                Чтобы предотвратить утечку печеньки, видимо.

                > nop

                Не помню, если честно. Он то ли для раскрутки стека нужен то ли для отладчика, чтобы однозначно понимать, в теле функции мы навернулись или в эпилоге (иначе не отличить трапа в инструкции n от фолта в инструкции n + 1). Заметь, что стоит он между джвумя инструкциями, которые лезут к памяти.
                Ответить
    • https://youtu.be/qHshIUnezgs
      Ответить
    • а не проще через longjmp?
      Ответить

    Добавить комментарий