Как дизассемблер извлекает код операции из памяти?

Я пытаюсь понять, как работает дизассемблер. В частности, как содержимое в памяти отображается на соответствующий код операции на ассемблере.

Ниже содержимое в памяти, адрес первого столбца:

773eed5c  50 ff 15 0c 17 3a 77 90-90 90 90 90 8b ff 55 8b  P....:w.......U.
773eed6c  ec 51 51 83 7d 10 00 74-38 ff 75 10 8d 45 f8 50  .QQ.}..t8.u..E.P
773eed7c  e8 14 d7 ff ff 85 c0 74-24 56 ff 75 fc ff 75 0c  .......t$V.u..u.
773eed8c  ff 75 08 e8 ab ff ff ff-83 7d 10 00 8b f0 74 0a  .u.......}....t.
773eed9c  8d 45 f8 50 ff 15 9c 15-3a 77 8b c6 5e c9 c2 0c  .E.P....:w..^...
773eedac  00 83 65 fc 00 eb d2 90-90 90 90 90 8b ff 55 8b  ..e...........U.
773eedbc  ec 57 e8 c7 d6 ff ff 8b-4d 0c 6a 34 5f 03 c7 0f  .W......M.j4_...
773eedcc  b7 00 40 40 03 c9 3b c8-0f 82 07 e5 00 00 56 e8  ..@@..;.......V.

И соответствующий результат дизассемблирования, адрес памяти первого столбца, код операции второго столбца инструкции, инструкции по сборке остальных столбцов:

0x773eed5c 50 push    eax
0x773eed63 90 nop
0x773eed65 90 nop
0x773eed67 90 nop
0x773eed6a 55 push    ebp
0x773eed6d 51 push    ecx
0x773eed6f 837d1000 cmp     dword ptr [ebp+10h],0 ss:0023:056cfa8c=778237eb
0x773eed7c e814d7ffff call    kernel32!Basep8BitStringToDynamicUnicodeString (773ec495)

Теперь я вижу код операции e814d7ffff в памяти буквально (e8 14 d7 ff ff)

Но как интерпретировать контент в памяти по адресу 0x773eed5c? Как код операции для push eax и последовательный nop с отображением в памяти 0c15ff50 90773a17 90909090 8b55ff8b?

ОБНОВИТЬ:

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

0x773eed5c 50 push    eax
0x773eed5d ff150c173a77 call    dword ptr [kernel32+0x170c (773a170c)] ds:0023:773a170c={ntdll!RtlExitUserThread (777ef608)}
0x773eed63 90 nop
0x773eed64 90 nop
0x773eed65 90 nop
0x773eed66 90 nop
0x773eed67 90 nop
0x773eed68 8bff mov     edi,edi
0x773eed6a 55 push    ebp
0x773eed6b 8bec mov     ebp,esp
0x773eed6d 51 push    ecx
0x773eed6e 51 push    ecx
0x773eed6f 837d1000 cmp     dword ptr [ebp+10h],0 ss:0023:0447fc24=778237eb
0x773eed73 7438 je      kernel32!OpenFileMappingA+0x45 (773eedad) [br=1]
0x773eed75 ff7510 push    dword ptr [ebp+10h]  ss:0023:0447fc24=778237eb
0x773eed78 8d45f8 lea     eax,[ebp-8]
0x773eed7b 50 push    eax
0x773eed7c e814d7ffff call    kernel32!Basep8BitStringToDynamicUnicodeString (773ec495)

Подробнее о моей ошибке: я использую pykd разработать инструмент вокруг WinDbg. Документация о его disasm Модуль не охватывает детали, поэтому я использовал неправильный параметр disasm.jumprel функция, которая приводит к неполному результату дизассемблирования.

2 ответа

Решение

Кажется, чего-то не хватает.

50                push eax
ff 15 0c 17 3a 77 call [0x0c173a77] ; where did this thing go?
90                nop
90                nop
90                nop
90                nop
90                nop
8b ff             mov edi, edi  ; wut?
55                push ebp      ; this looks like the beginning of a function
8b ec             mov ebp, esp
51                push ecx
51                push ecx
83 7d 10 00       cmp [ebp + 10], 0

Я разобрал это вручную, возможно, я допустил ошибки. Этот код странный. Ваша разборка еще более странная, и я понятия не имею, как это произошло.

Это довольно просто на самом деле.

Посмотрите на: http://www.mathemainzel.info/files/x86asmref.html

Это ссылка на набор инструкций x86.

Если вы ищете "PUSH AX", вы увидите, что код операции 50, Если вы ищете "NOP", вы увидите, что его код операции 90,

Итак, что происходит, у вас есть коллекция того, как выглядит каждый код операции (50 == PUSH AX, 90 == NOP, так далее.). Некоторые коды операций требуют больше параметров, чем другие. CALL Опкод имеет 4 режима, первый, E8, для "ближнего указателя".

Теперь у x86 разные режимы работы (16b, 32b, 64b), поэтому он использует одни и те же коды операций, но настраивает параметры для разных режимов. Это то, что дизассемблер должен знать заранее. Потому что "ближний указатель" отличается в режимах 16b, 32b и 64b (помимо всего прочего, они занимают больше места).

Но, в конце концов, простой дизассемблер ищет свой текущий код операции, потребляет столько байтов, сколько требуется на основе кода операции, а затем создает соответствующую инструкцию на ассемблере для этого фрагмента памяти.

Более сложные дизассемблеры понимают языки более высокого уровня, могут указывать на области, к которым код не обращается (например, он может отслеживать переходы, вызовы и ветви и знать, какой код он не дизассемблирует).

Дезассемблеры могут быть довольно сложными, но простой прост.

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