同一API在各个平台的内存地址都不同,所以要用用一种通用的办法得到API正确的地址。
原理大概如下:
找到kernel32.dll的基址,之后根据PE文件的格式找到DLL的输出表,之后找到相应的函数的地址。
如果想调用的函数存在于别的DLL,则先找到LoadLibraryA函数的地址,之后调用它得到目标DLL的基址,之后根据PE文件的格式找到输出表,最后找到要调用函数的地址,调用之。
原理是摆出来了,但是有好几个问题。
·怎么得到kernel32.dll基址。
1)首先通过段选择字FS 在内存中找到当前的线程控制块TEB
2)TEB偏移位置为030H的地方存放着 该线程所属进程的 进程控制块PEB 的指针。
3)PEB中偏移为 0CH 的地方存放着指向 PEB_LDR_DATA 结构体的指针,它存放着已经被进程装载的动态链接库的信息。
4)PEB_LDR_DATA结构体偏移位置为01CH的地方存放着指向 模块初始化链表 的头指针InInitializationOrderModuleList
5)模块初始化链表 InInitializationOrderModuleList 中按顺序存放着PE装入运行时初始化模块的信息,第一个链表结点是ntdl.dll,第二个链表结点就是kernel32.dll。
6)找到属于kernel32.dll的结点后,在其基础上再偏移080H就是kernel32.dll在内存中的加载基地址。
汇编代码如下
;find base addr of kernel32.dll
mov ebx, fs:[edx + 0x30] ;ebx = addres of PEB
mov ecx, [ebx + 0x0C] ;ecx = pointer to loader data
mov ecx, [ecx + 0x1C] ;ecx = fist node in list (ntdll.dll)
mov ecx, [ecx] ;ecx = second node in list (kernel32.dll)
mov ebp, [ecx + 0x08] ;ebp = base address of kernel32.dll
|
我细心地看了一下代码,开始时想不明白为什么 mov ecx, [ecx + 0x1C]就已经得到链表的第一个节点,之后的mov ecx, [ecx]竟然还得到链表的第二个节点!
后来我想到有一种可能,就是 链表的节点的第一个成员变量是指向下一个节点的。
之后我在网上查看了一些资料证明了我的想法只对了一半。原因是这些结点的结构都用LIST_ENTRY连在一起。而LIST_ENTRY刚好是这些结点结构的第一个成员,同时LIST_ENTRY的第一个成员FLINK的作用是指向下一个LIST_ENTRY结构!
笔记做到这里不认真看的话真的会晕~~~ 复习时认真多读几遍前面的一段话,加上写在P99的笔记应该就OK
得到kernel32.dll的基址一切就好办了, 都是PE格式的问题了。在看这章前已经恶补了一下PE格式,现在是记得,但不知一年半载后会怎么样? 有空还是再作一遍关于PE格式的笔记吧。
PE格式无非就是 BASE ADDRESS,RVA,VA,IMPORT TABLE,EXPORT TABLE罢了。
阅读(1884) | 评论(0) | 转发(0) |