多时候CALL里的一些数值,都是变化的,或者不同的电脑值也不一样,如何寻找这些值的基址呢?不同的值有不同的找法下面就列出几条常用的方法。
1.CE搜索
这个方法在我找CALL入门篇一中就有说过而且,很多篇章中都用过这种方法,例子:模拟器 游戏找CALL练习实例ONE
这里我们找的是EAX的值,EAX的值 是不同电脑他的值也是不同的。这个我在找CALL篇一中说过。
绿色的就是基址了, 如果搜出来没有绿色如何?可以用找血基址的方法找偏移,然后跟踪出他的基址。
2.代码分析 从汇编代码中顺藤摸瓜摸出基址,这个也是比较常用的方法。例子:CALL入门篇一
武易打坐
例子:
从上面我们要找EAX的值,才能得到CALL的地址,EAX是如何来的呢?这要往上寻找了
首先 我们找到的是 mov eax,[ecx] 这里 把ECX里的值 赋值给EAX 那么EAX的来源就确定了,但这还不是明确的地址,我们继续往上找
然后 到了mov ecx,[4fa818] 这里我们找到了 赋值给ECX的值 ,而且也是 明确的地址。(摸到瓜了)
好了 EAX的值就是 [4fa818]里的值 赋值给ECX 在从[ECX]里的值赋值给EAX
公式 [[4fa818]+0] (读了2次基址)
3.CALL的返回值
如果你寻找某个寄存器的值,苦苦追了3层还追不到,那么你就要注意了,其实这个值可能就在你原来CALL的附近。
详细的请看 CALL提升篇一:剑侠3的喊话CALL分析后部分
4.子程序参数的跟踪
这个是 入门篇三中的模拟器代码, 我们要找EBX的值
从代码上可以很清楚的找到 EBX的值 是 [EBP+8] 中传入的,而EBP 确实从ESP传入,大家都知道ESP是堆栈指针的寄存器,是不断变化的,但如何取到这个值呢?
首先,我们要明白[EBP+*] 这种偏移的含义, 其实我前面也讲到过 这个就是 参数的值
这个子程序 被传入 包头 和 数据 2个参数,然后组合。
代码中 [ebp+8] 其实就等于 数据 值, 而[ebp+c]就是 包头的参数值(如果是ebp-*)这种类型 就是 临时变量
从这里我们可以看出,EBX的值 并不是系统里的,而是我们自己应该写入的参数堆栈值,固也无法跟踪了(因为这个值是需要你自己写入的)
这里有朋友可能要问了 如果我要写这个CALL 而不写上面那一层的CALL这里改如何写呢? 其实直接赋值就可以了, 比如说 PUSH [ebx] 这里如果需要传入40这个值 那么直接 PUSH 40便可, (我在前面几篇中强调过,CALL只要传入适当的参数,便可以调用)这里适当的参数就是这层CALL所需要的参数数据,而不是傻傻的跟上去找原值。
这里 具体的可以看看 提升篇一 文章中后期 对寄存器值的跟踪过程 寄存器eax值的跟踪! R)Dh;XA
FYH^axpp
在调用约定里 所有CALL的返回值都是靠EAX返回的,如果EAX放不下就放在EDX里。如果返回的结果是文本类型的话就会返回一个指针地址。在游戏里,一般的功能CALL返回值很少有。 1~vv<`
-
如 iO iXo6YE
int myadd(int a, int b) :fQN_*B4@4
{ *2Q x69`
int c=a+b; e‑ r"gPW
return c; 29NP!W /g
} \$ :)Ka
Pcr;+'q
在反汇编的表现形式大概就是 vr$z6m
^
[H>/N7v19*
push ebp //寄存器环境保护 ‑ 3,Bm"'b6
mov ebp,esp //将堆栈指针的值传给EBP用于开辟临时变量 %Z(lTvqG
sub esp,4 //开辟一个临时空间 也就是我们定义的临时变量C Y 4*?QBYA
mov [esp-4],0 //将 c 初始化 也就是C=0 尽管我们没有写不过编译器会自动完成 {>l`P{{y
mov eax,a //将A放入寄存器EAX里 op|x~Thf
add eax,b //将EAX加上B w4Ku1G#jC
mov [esp-4],eax // 将计算结果保存到临时变量C k)JwCt.% mov eax,[esp-4] //将保存结果的变量C传递到EAX n/IDq$[1]/P mov esp,ebp //将堆栈指针恢复到调用CALL之前。 pop ebp //将寄存器保存的值取出来。 9wv 7 HD|
jI807g+
retn P 4~C0z
这个就是一个简单加法子程序的运行过程。所以很多时候我们跟踪一下自己写的程序,看看机器是如何运行你的代码的,这个是一件很有趣的事情,也会增加你对编程和汇编的了解。这里的反汇编代码是我手动写的可能和真正的有误差,但大概的过程是应该没有错的。 7 *HBb-
所以很多人在跟踪寄存器EAX的时候往往会放过挨着的CALL指令,直接找上面的汇编代码。要看看EAX是否是一个CALL的返回值其实很简单,看看运行CALL指令后EAX的值是否有变化就可以了。 寄存器esp,ebp值的跟踪! i‑'$V'x'k
上面说到 堆栈参数的传递,当参数压入到堆栈CALL的内部是如何读取的呢? o-))R| ~z
很多朋友都看见过这样的寻址指令 mov ebx,[esp+1c] 这个时候很多朋友都去寻找赋值 ESP 相关的指令去了,但找了半天都没找到.其实这里直接指向了堆栈. ESP是一个特殊的寄存器,他永远指向了堆栈顶部. I‑wfJDJJ 所以一般轻易不会去变动这个值. 当 CPU 执行 PUSH指令时 ,ESP就会-4 然后把数据存放到当前ESP的地址里.这个时候如果读取刚刚压入的参数 就是 mov ,eax,[esp] 这样堆栈顶部的数据就会被读取. %q~YJ*\
但是这个时候又压入一个数据呢? 压入的时候ESP会-4 那么 指向刚刚那个参数就是 mov ebx,[esp+4] ,co~@a@9
如果压入了5个 要指向第一个参数 就是 [esp+10] 没错就是10 一个堆栈是4字节 4个就是16字节 16在16进制里就是10 Rd?8LLz[1]
这里要说明的一下是 CALL指令也会压入一个数值.所以 PUSH EAX 3tO= [1]
CALL ******** =N62 ){{ 在CALL内部指向 EAX 就是 [ESP+4] 这里其实压入了2个堆栈 ,一个是PUSH EAX 还有一个是CALL. r%c[1]raf 临时参数的表达方式是 [ebp-*] 一般是在CALL头部把ESP的数值赋值到EBP 那么 EBP永远指向CALL头部时的堆栈指针,对于下面新增的堆栈就是 以减法表示 因为 压入一个堆栈ESP就会-4 .故[EBP-*]在反汇编里代表临时变量. sew0n`d1
|