今年遇到一个很纠结的问题,代码改好后Debug模式下测试无误,
于是在又在Release模式下编译了一遍,后来无意中运行并测试了一遍,突然报错。
检查了一遍代码没有被修改过,Debug模式下编译正常。
这时候只好看看报错时给出的信息了。
既然是内存不能写入,追踪EIP寄存器的值,执行的指令是:
mov byte ptr[ebx],al
其中ebx的值自己取一个函数指针的值,并且在此之前,修改过该对应页的保护属性,运行写入。
一开始因为发现(DEBUG模式下)函数指针并没有指向该函数,而是指向IAT(导入表)中对应的地方,
这样一来写入的内容等于是写到了导入表中,但是采取了一点小策略:
(pfun是函数指针)
mov eax,pfun
add eax,[eax+1]
add eax,5
mov pfun,eax
这段代码的作用是修正函数指针使其指向函数的位置。具体原理:
PE格式中导入表其实是一串跳转指令,具体跳转的位置由PE Loader在加载时填入,使得程序编译时不用考虑
加载时的位置情况。
其中使用的是JMP Near
指令为 E9 XX XX XX XX(函数地址)
上面的代码先获取这条指令的地址,再得到函数具体的地址(跳过头上的字节就OK),
该地址为偏移量,加上这个偏移量后再加5(为什么?这条指令的长度是5,得跳过这条指令本身),就能得到函数地址。
//===================回归正题================================
调试后发现,上诉过程得到的地址在一块空内存之中,显然出现了某些错误,
继续调试,偶然间发现一个更奇怪的现象:函数指针取到的值正式该函数的地址。
经过多次验证后,可以确认:
XXXXlease模式下编译后,函数指针得到的是该函数的地址。XXXXBUG模式下编译后,函数指针得到的是该函数在IAT中对应的项目。==
这个现象究竟是因为编译模式不同导致函数指针的赋值方式不同还是因为编译模式不同,函数调用模式也不同(Release下不通过IAT跳转,而是直接call函数对应的地址?),还有待探究……
200字以内,仅用于支线交流,主线讨论请采用回复功能。