2012-03-10 1 views
3

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

+0

Я не понимаю, как вы можете работать без файла заголовка или документации. –

+0

Функция, которую я хочу использовать, не экспортируется из DLL. Это внутренняя функция DLL. –

+0

Да, это тонкое сообщение о том, что вы не должны называть эту функцию. –

ответ

7

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

Давайте возьмем пример 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)
  • Scan для байта сигнатуры мы обнаружили (с учетом групповых символов - это просто реализовать как for цикл, который пропускает подстановочные байты)
  • Чтобы получить истинную функцию адрес, исправить адрес, который вы получаете с смещением блока от исходной функции (в данном случае, 0x765DEA16 - 0x765DEA11 => 0x5)

на самом деле, а не enumerat во всех исполняемых страницах часто бывает достаточно найти, какой модуль находится внутри (user32.dll) в этом случае, и выполнять поиск только в этом модуле.

+0

Интересно, как мы можем назвать эту функцию из c. Назначить его указателю и вызвать его? Это работает? –

+0

@alFReDNSH Если функция не принимает аргументов, да, вы можете это сделать, потому что нормально игнорировать различия в вызовах. В противном случае вы должны быть более осторожны, чтобы убедиться, что вы выбрали его соответствующим образом. –

+0

Если он принимает значение int в качестве первого аргумента и возвращает int, имеет ли это следующее право? int lolz = 1; int (* foo) (int); foo = <адрес функции>; lolz = foo (lolz); –

 Смежные вопросы

  • Нет связанных вопросов^_^