在Windows DLL中通过它签名查找函数

Did*_*nov 4 c c++ assembly signature disassembly

在DLL中找到了一个函数地址.没有这个DLL的源代码,不是我的.这个DLL并没有经常更改,但是当更改时,通过反汇编找到它是一个问题.在网上看到一些关于使其签名的注释,然后通过这个保存的签名找到它.您能否就如何实施这一点提出一些想法或工作实例?

Mik*_*wan 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
Run Code Online (Sandbox Code Playgroud)

诀窍是猜测你认为在更新中可能保持不变的某些代码块,但更重要的是这个函数是唯一的.通常,扫描结尾/序言是没用的.我可能会采取以下块:

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>; 
Run Code Online (Sandbox Code Playgroud)

选择块的长度时必须保持平衡.块越长,唯一标识函数的可能性越大,但更新期间插入某些代码的可能性越大,这意味着它被拆分等等.请注意,我选择的块有多个内存引用.我们不能依赖任何数据或函数地址,因为这些地址可能会在下次更新时重新定位,因此我们使用通配符填充这些字节:

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] 
Run Code Online (Sandbox Code Playgroud)

这意味着我们的字节签名现在是:

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)

实际上,不是枚举所有可执行页面,而是user32.dll在这种情况下找到函数所在的模块(),并且仅在该模块内搜索.