关于代码重定位的疑惑解答

转载看雪上的一个讨论

问:

在罗聪的网站(当然在很多地方都能看到)我看到关于代码重定位的代码,即
    call nStart
nStart:
    pop ebp
    sub ebp,offset nStart ;对这句代码我有一点疑惑
    是这样的,call执行的操作应当是将call的下一条指令(在此处即为"pop ebx")的地址压入堆栈,然后jmp到nStart,之后执行pop ebp,那么现在ebp中
    的内容就应当是"pop ebp"这句代码的地址,那么下一句代码"sub ebp,offset nStart"的作用又是什么呢?我的理解是ebp减去nStart标号的地址,而
    nStart标号的地址与"pop ebx"这句的地址应该相同吧,那么最后ebp的值就总是为0(并且我认为无论将这段代码放到何处,ebp总会为0),代码不就失去  
    了作用么?
    小弟资质愚钝,希望各位大哥赐教。

答:

假如下面那段代码是在自己程序中的代码。因为pop ebp的地址也是00401005,这时sub ebp,00401005的结果ebp就是等于0了。不过现在假如将这段编译好的二进制代码拷贝到00402000处运行,你想会怎样?
00401000 call 00401005 ; call nStart
00401005 pop ebp
00401006 sub ebp, 00401005 ; sub ebp,offset nStart

假如将这段代码拷贝到00402000处运行,就会变成如下面这样,你还认为是没用的吗?
00402000 call 00402005
00402005 pop ebp ; ebp == 00402005
00402006 sub ebp, 00401005 ; 注nStart的标号地址在被编译为二进制后就是固定的00401005了,不会再变了,这时你认为ebp还是等于0?

总结一句,编译的时候,标签的地址是固定的,但是call的地址是随着程序所在位置变化的

作者: wsnhyjj   发布时间: 2010-09-16