Найти функцию по ее подписи в Windows DLL - PullRequest
4 голосов
/ 10 марта 2012

Нашли адрес функции в DLL. У меня нет исходного кода для этой DLL, не моя. Эта DLL не часто меняется, но когда меняются, мне сложно найти ее путем разборки. Видел в Интернете заметки о том, как сделать подпись, а затем найти ее по этой сохраненной подписи. Не могли бы вы дать несколько идей или рабочий пример того, как это реализовать?

1 Ответ

7 голосов
/ 10 марта 2012

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

Давайте рассмотрим пример MessageBoxA, чья разборка выглядит для меня так:

765DEA11 > 8BFF             MOV EDI,EDI
765DEA13   55               PUSH EBP
765DEA14   8BEC             MOV EBP,ESP
765DEA16   833D 749A5E76 00 CMP DWORD PTR DS:[765E9A74],0
765DEA1D   74 24            JE SHORT USER32.765DEA43
765DEA1F   64:A1 18000000   MOV EAX,DWORD PTR FS:[18]
765DEA25   6A 00            PUSH 0
765DEA27   FF70 24          PUSH DWORD PTR DS:[EAX+24]
765DEA2A   68 A49E5E76      PUSH USER32.765E9EA4
765DEA2F   FF15 34145876    CALL DWORD PTR DS:[<&KERNEL32.Interlocke>; kernel32.InterlockedCompareExchange
765DEA35   85C0             TEST EAX,EAX
765DEA37   75 0A            JNZ SHORT USER32.765DEA43
765DEA39   C705 A09E5E76 01>MOV DWORD PTR DS:[765E9EA0],1
765DEA43   6A 00            PUSH 0
765DEA45   FF75 14          PUSH DWORD PTR SS:[EBP+14]
765DEA48   FF75 10          PUSH DWORD PTR SS:[EBP+10]
765DEA4B   FF75 0C          PUSH DWORD PTR SS:[EBP+C]
765DEA4E   FF75 08          PUSH DWORD PTR SS:[EBP+8]
765DEA51   E8 73FFFFFF      CALL USER32.MessageBoxExA
765DEA56   5D               POP EBP
765DEA57   C2 1000          RETN 10

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

765DEA16   833D 749A5E76 00 CMP DWORD PTR DS:[765E9A74],0
765DEA1D   74 24            JE SHORT USER32.765DEA43
765DEA1F   64:A1 18000000   MOV EAX,DWORD PTR FS:[18]
765DEA25   6A 00            PUSH 0
765DEA27   FF70 24          PUSH DWORD PTR DS:[EAX+24]
765DEA2A   68 A49E5E76      PUSH USER32.765E9EA4
765DEA2F   FF15 34145876    CALL DWORD PTR DS:[<&KERNEL32.Interlocke>; 

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

765DEA16   833D XXXXXXXX 00 CMP DWORD PTR DS:[XXXXXXXX],0
765DEA1D   74 XX            JE SHORT XXXXXXXX
765DEA1F   64:A1 18000000   MOV EAX,DWORD PTR FS:[18]
765DEA25   6A 00            PUSH 0
765DEA27   FF70 24          PUSH DWORD PTR DS:[EAX+24]
765DEA2A   68 XXXXXXXX      PUSH XXXXXXXX
765DEA2F   FF15 XXXXXXXX    CALL DWORD PTR DS:[XXXXXXXX] 

Это означает, что наша байтовая подпись теперь:

0x83 0x3D 0x?0x?0x?0x?0x74 0x?0x64 0xA1 0x18 0x00 0x00 0x00 0x6A 0x00 0xFF 0x70 0x24 0x68 0x?0x?0x?0x?0xFF 0x15 0x?0x?0x?0x?

Байты 0x? указывают на символы подстановки, которые мы ожидаем изменить.Другие байты, которые мы ожидаем, не изменятся в обновлении.Чтобы использовать байты для определения местоположения функции во время выполнения, вам необходимо выполнить поиск этих байтов (с учетом подстановочных знаков).Процесс примерно такой:

  • Перечисление всех исполняемых страниц процесса (VirtualQueryEx)
  • Сканирование на найденную нами сигнатуру байта (с учетом подстановочных знаков - это тривиальнореализовать в виде цикла for, который пропускает подстановочные байты)
  • Чтобы получить истинный адрес функции, исправьте полученный адрес со смещением блока из исходной функции (в данном случае 0x765DEA16 - 0x765DEA11 => 0x5)

На самом деле, вместо перечисления всех исполняемых страниц, часто достаточно найти модуль, в котором находится функция (в данном случае (1033 *)), и выполнить поиск только в этом модуле.

...