首先写一个很简单的main函数:html
int main(){ printf("main的地址(?):%08x",main); }
单步调试,可得知 main函数的真实入口地址是:00be91a0算法
然而咱们控制台输出的值是函数
为何会出现这样的差异呢?院子里有一篇大牛写的有关注入的文章:http://www.cnblogs.com/fanzhidongyzby/archive/2012/08/30/2664287.html,里面就提到了这个问题。编码
其中提到一个解析真实地址的算法:spa
//将函数地址转换为真实地址 unsigned int getFunRealAddr(LPVOID fun) { unsigned int realaddr = (unsigned int)fun;//虚拟函数地址 // 计算函数真实地址 unsigned char* funaddr = (unsigned char*)fun; if (funaddr[0] == 0xE9)// 判断是否为虚拟函数地址,E9为jmp指令 { int disp = *(int*)(funaddr + 1);//获取跳转指令的偏移量 realaddr += 5 + disp;//修正为真实函数地址 } return realaddr; }
相信新手朋友都和我同样为这段代码怎么来的摸不着头脑。特此我单步调试研究了一番,才大体明白原理:指针
main函数名的地址其实就是一个 Jmp xxx的指令,调试
从vs的反汇编调试代码能够看出,第一个参数main入栈,对应汇编代码就是 push 0be135Ch ,这个入栈的数字,其实就是刚刚控制台输出的那个地址。因此很显然这个地址就是函数main的“函数名”所在的地址(其实在汇编里应该是不存在 “main”这样的字符串的,之因此可能能够在vs或在od这样的调试器里看到,是这些调试器经过经验推断出来的结果。)。而后 咱们在地址栏里输入 0be135Ch这个地址,就能够跳到 相对的位置处。code
jmp 对应的汇编码为 E9, 然后面的4个字节"3F 7E 00 00",其实是倒序的相对地址 0x00007e3f.也就是咱们看到是main。固然这个地址只是代码的相对位置,若是运行以后,就要加上当前模块的基址才是绝对地址。htm
那么既然,这个“main”的位置只是一个跳转指令(JMP),确定是跳转到了jmp后面跟的这个地址的位置了,也就是说这个地址就是真正的main函数的地址了。blog
固然,0be135Ch是从e9 3F 7E 00 00这条指令开始算的。也就是说,从e9 3F 7E 00 00的位置开始算,跳过去0x00007e3f个字节,就到了真正的main函数入口处。
因此真正的地址就是
0be135Ch+5(指令占的字节数 JMP main)+0x00007e3f=00be91a0.
因此咱们再回到函数部分来。
UINT realaddr = (UINT)main;// 拆箱转换成 unsigned int 数据。表明main函数名所表明的地址 unsigned char* funcaddr = (UCHAR*)main; //这里是获取main地址所在的字节内容,其内容为“e9 3F 7E 00 00”(JMP main),因此只要判断前面的字节指令是不是e9就知道是否是真实地址了,若是是Jmp(e9)的话就不是真正的地址。 if (funcaddr[0] == 0xE9){ //由于 UCHAR* fa = funcaddr + 1;//指针移后一位,从e9到 3f. int* as = (int*)(fa); //取指针=处的int数据(共4个字节),实际上这里就是一个跳转地址。 int disp = *as; //取指针的内容 realaddr += 5 + disp;//在main地址的基础上+JMP main所占的字节+要正向跳转的字节数 }