在Windows DLL中通过签名查找函数
Find a function by it signature in Windows DLL
在DLL中找到函数地址。没有这个DLL的源代码,不是我的。这个DLL并没有经常更改,但当更改时,我很难通过分解找到它。在网上看到了一些关于让它签名的笔记,然后通过这个保存的签名找到了它你能就如何实现这一点给出一些想法或工作示例吗
您可以通过代码签名扫描来实现这一点,这是我过去做过的事情。这一概念主要是基于这样一个事实,即功能在更新之间通常不会发生太大变化,而是简单地重新定位,因为它们被其他扩展或收缩的功能向前或向后推。
让我们以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 0x6A0x00 0xFF 0x70 0x24 0x68 0x?0x?0x?0x?0xFF 0x15 0x?0x?0x?0x?
0x?
字节表示通配符,这些通配符是我们希望更改的字节。其他字节是我们预计在更新中不会更改的字节。要在运行时使用字节来定位函数,需要扫描这些字节(考虑通配符)。过程大致如此:
- 枚举进程的所有可执行页面(
VirtualQueryEx
) - 扫描我们找到的字节签名(考虑通配符-这对于作为跳过通配符字节的
for
循环实现来说是微不足道的) - 要获得真正的函数地址,请用块与原始函数的偏移量(在本例中为
0x765DEA16 - 0x765DEA11 => 0x5
)来固定您获得的地址
实际上,在这种情况下,与其枚举所有可执行页面,不如查找函数所在的模块(user32.dll
),并仅在该模块中搜索。
- 递归ASMVisitor 和查找函数调用站点
- C++ - 查找函数无法在子字符串上执行
- 查找函数是否为常量
- GDB 如何查找函数退出的位置
- 查找函数在unordered_map中的工作方式是搜索键值
- 在 C++ <algorithm>中查找函数
- 错误:二进制表达式的操作数无效(映射使用查找函数错误)
- 哪个查找规则阻止编译器查找函数
- 在C++中查找函数的调用方(Visual Studio)
- 关于获取行和字符串查找函数的问题
- 查找函数在失败结果中应该返回什么
- 如何使用调用和别名指令在 llvm 字节码中查找函数名称
- std::字符串类查找函数不返回预期结果.我可能用错了
- 在成对向量中查找函数时出错
- 从堆栈指针中查找函数参数值
- 如何定义查找函数
- 如何查找函数是否可重入
- 在c++中查找函数
- GNU g++ 4.9.2 查找函数调用的编译错误
- 使用GDB查找函数对应的内存地址/调试