使用CE的传统方法找基址一般是先搜到内存地址以后,然后再查看是什么代码访问或者改写了该地址,再根据这行代码中的地址和偏移量一步步查找,例如我分析大闹天宫时,代表锁鱼类型的内存地址是:0x9BAFB28,然后查看改写这个地址的代码是:
00C9B75C |. 8B4C90 58 mov ecx,dword ptr ds:[eax+edx*4+0x58] ;
再往前找:
00C9B755 |. 8B43 7C mov eax,dword ptr ds:[ebx+0x7C] ;
然后再按照这种方法找就找不出什么准确的内容了。 使用CE找基址这时候陷入困境。实在没办法,用OD找找看,先附加游戏进程,然后定位到上面的代码,如图:
可以看出,这个基址的关键是EBX(099F4B00),附近没有改写EBX的代码。然后按CTRL+F9运行到返回,然后找到这个代码块的call地址,再进入这个call看看开头,发现给EBX赋值的是:mov ebx,dword ptr ss:[ebp+0x8],ebp+0x8也就是这个call的第一个参数,再返回调用这个call的指令处查看:
发现这个参数是EBP传入的,也就是要找的基址EBX等于此处的EBP,而EBP一般只在子程序的开头赋值,所以再CTRL+F9运行到返回,然后F7单步补进一下,停留在下图这个地方:
发现上面的call是call eax,call地址不是一个确定的数字,不能直接进入。在此处下断,然后F7进入,看到下面代码:
,发现EBP的值来自ECX,然后返回刚才的call eax处再看,发现:
00C87400 |. 8B08 mov ecx,dword ptr ds:[eax]
ECX的值是来自[eax],再记录一下关系,免得一会步数太多,记不清了。此时要找的基址=[EAX]
这个EAX是怎么来的呢,在他上面最近的一个call处下断分析:
很明显,eax的值来自这个call,进入看一下:
因为函数返回值总是用eax来返回,这个eax应该在该子程序最下面,所以从下往上找,依次找到:
00C84089 |. 8BC6 mov eax,esi
00C84076 |> \8D77 FC lea esi,dword ptr ds:[edi-0x4]
00C8403F |. 8B7E 10 mov edi,dword ptr ds:[esi+0x10]
注意这是从下到上的顺序,分析得到:EAX=ESI=[EDI-4]=[[ESI+0X10]-4],找到这里,感觉差不多了,因为不像刚才那样都是call之间直接传值的样子,先用CE搜一下这个ESI的值:708cf8,如果CE还是找不到基址再回OD继续找。
在CE中搜到3个地址,有一个地址是绿的(绿色是静态地址,一般就是基址),查看一下这个静态地址,是:dntg2.exe+87F18,说明基址找到了。最终这个ebx=[[[[dntg2.exe+87F18]+0x10]]-4]
进而找到锁鱼类型的基址:
分析:为什么有时候用CE不能直接找到基址?感觉是因为CE只能查找全部通过[寄存器+偏移]这种形式访问的基址,如果在基址传递过程中有其他的形式,用CE就不好找了。就像这个例子,[[[dntg2.exe+87F18]+0x10]]-4这个值先是给了EAX,然后通过[eax]给ebp,再调用call又给ebx,这些都是直接在寄存器之间互相传值,所以CE的“查看什么指令访问了内存地址”就没有用了,因为寄存器之间传递值不会访问内存。这时候就只能用OD下断分析这些值具体的来源了。当然,CE也有简单的下断、运行到返回、步进、步过等功能,就是功能相对很简单,不如OD强大。
以上分析过程是我个人长时间琢磨出来的,自己记录一下,以后遇到类似问题可以座位参考。网上有其他刚入门的小白也可以参考一下。本人也是小白,深知没有师傅的情况下研究这些问题有多么困难,希望有志同道合的朋友来一起交流。
最新评论