上周五下了个碧之轨迹来玩,当然是破解过的,打开之后,经典的falcom开头,接下来居然是更经典的"健康游戏忠告",真是....
立马打开od,载入发现有壳,peid查看是yoda1.02,上网看一下相关的文章,貌似是反调试的壳,在od里面运行也确实是提示检测到调试器了
单步跟了一阵,发现却和网上的描述不同,虽然都是反调试...
打开peid的userdb.txt,里面查找一下yoda的特征码,发现居然从头到尾都是问号,看来这数据库将识别不到的都归为yoda了
去掉这特征码就什么都查不到了,没办法,硬着头皮脱吧
打开"收藏"已久的<<脱壳的艺术>>看了一遍,基本了解了一下
像IsDebuggerPresent这样的Sod已经灭掉了
但是CheckRemoteDebuggerPresent还有几个,ODAdvance有选项但是却不工作的,手动修改一下也过了
但是还是会被检测到调试器
在单步的时候我发现一个地方在对比字符串,就是api的函数
这壳干脆不用GetProcAddress,直接从dll文件映射里面找
来到对比成功的地方,下断,看一下取了什么函数出来
有VirtualProtect之类的,还有反调试相关的
重要的还有一个CloseHandle,我看其调用这个函数的时候参数很可以
上网查了一下,这函数也能反调试的,就是传一个无效的句柄,具体就上百度看吧
将这个函数搞掉,接下来就用内存断点法很快就来到oep了
将上面过程写成脚本,以后轻轻一点就能直达oep
然后就是修复输入表了,脱壳的流程虽然都懂,但是一直都不知道是什么回事
这次却不同了,imprec只找到了5个无效的指针,但是程序里面确实是用了很多的api
无奈继续跟踪,从oep开始
单步跟进了2个call,结果就来到了花指令
这花指令有意思,虽然跳来跳去,但是重要的代码就3个地方:
1)
算出api入口
mov eax,XXXXXXXX
mov eax,[eax+XXXXXXXX]
lea eax,[eax+XXXXXXXX]
然后将eax放到栈顶
最后一个retn XX,就来到了api的入口
这里XXXXXXXX就是一个立即数,eax可以是esp以外的7个寄存器
而且这些花指令全在同一区段中,对于每一次调用都会有这样一段代码
2)
处理返回地址
lea eax,[eax+1]
eax代表7个寄存器
这里增加的是返回的地址
有时api调用会在后面加一个垃圾字节,然后在花指令里面处理返回地址
3)
破坏堆栈
这个没什么特征,有时会在call之前多push一两次,然后花指令里面pop回来
对付这个花指令,有个一箭三雕的方法
从call开始,使用脚本的单步进入跟踪,条件就是eip在程序代码的外面则停下来
停下来的时候,eip就是api的地址
[esp]就是返回地址,对比一下就知道有没有垃圾字节
有的话就将垃圾字节变成nop
如果没有破坏堆栈的话,esp的值应该减少了4
如果没有减少,就将call前面的push去掉
如果前面没有push,就将call变成jmp
如果esp反而增加4,就将push去掉,同时将call变成jmp
这时候call前面肯定是有push的,就这几种情况了
获得api地址之后就找块空地放着,然后再找一块空地放jmp
然后原来的call就指向这个jmp
保存之后,打开imprec,手动输入自己填充的iat,修复
弄好这里,我感觉对输入表和iat终于有相当的理解了
其实到这里还不能运行,我用od载入修复之后的程序
发现入口居然还是在原来的地方
将od设在系统入口停下
跟了一下,发现在另一个地方保存着壳的入口
将其设0,然后就能来到oep了
(之后翻加密与解密的时候发现这叫tls,不管如何,这次是停在了oep)
看看运行如何,窗口出来了,然后立马就出错,还真是兴奋了一下
立刻接上od,发现实在一个叫做读档的导出函数里面出错的
还没见过exe也有导出函数
跟踪了一下,全是花指令,无奈之下干脆去掉这函数,居然就成功运行了
对于存档的函数也是同样,发现去掉这两个函数也能正常读档,存档
真不知道是用来干什么的
成功进入游戏,心想这下终于可以玩了,(貌似忘记了一开始的目的)
找个小怪虐一下吧,战斗场面出来,一按攻击,又挂掉了
调试,发现和存档一样,是一个攻击的导出函数出错了,如法炮制
这次就不同了,将这函数去掉之后就无法选定怪物了
来到这里感觉想要放弃了,这么复杂的花指令还没见过
上课的时候我就想,这会不会就是虚拟机
然后就逛了一下虚拟机版,发现虚拟机有个指令表的东西
在这个游戏里面我也见过,应该就是虚拟机了
下了那个vmp分析插件,确实是找到了虚拟机
但是分析却有很多错误指令,跟了几下,发现就是所谓的vPushReg4和vPopReg4
认真看了一下插件的使用说明,对着修改了一下虚拟指令的配置,然后就分析成功了
接下来就是将这几个函数爆破掉
感觉这个爆破起来会比其他注册码程序的爆破会简单很多
因为我有一个能够正常运行的事例,两od对照法就行了
这个不好说,贴一下代码吧
00E56439 |. B9 vPushVEsp DWORD _t332 = 1C
00E5643A |. 0D vReadMemSs4 DWORD _t333 = m1
00E5643B |. 10 C5 vPopReg4 vR9 DWORD _t334 = m1
00E5643D |. C7 CE vPushReg4 vR9 DWORD _t335 = m1
00E5643F |. 75 vNand4 DWORD _t336 = ~m1; DWORD _t337 = NotFlag(_m1)
00E56440 |. 5A 1B vPopReg4 vR8 DWORD _t338 = NotFlag(_m1)
00E56442 |. 9A F8E35E43 vPushImm4 41526210 DWORD _t339 = 41526210
00E56447 |. 08 vNand4 DWORD _t340 = 0BEAD9DEF & m1; DWORD _t341 = AndFlag(0BEAD9DEF, _m1)
00E56448 |. CF 3C vPopReg4 vR2 DWORD _t342 = AndFlag(0BEAD9DEF, _m1)
00E5644A |. 66 BB vPushReg4 vR9 DWORD _t343 = m1
00E5644C |. 33 C73AD76D vPushImm4 0BEAD9DEF DWORD _t344 = 0BEAD9DEF
00E56451 |. F5 vNand4 DWORD _t345 = 0BEAD9DEF ~& m1; DWORD _t346 = NandFlag(0BEAD9DEF, _m1)
00E56452 |. DB B7 vPopReg4 vR0 DWORD _t347 = NandFlag(0BEAD9DEF, _m1)
00E56454 |. 8C vNand4 DWORD _t348 = 0BEAD9DEF ^ m1; DWORD _t349 = XorFlag(0BEAD9DEF, _m1)
00E56455 |. 16 7C vPopReg4 vR2 DWORD _t350 = XorFlag(0BEAD9DEF, _m1)
00E56457 |. B4 2D vPopReg4 vR8 DWORD _t351 = 0BEAD9DEF ^ m1
00E56459 |. 5F 66 vPushReg4 vR13 DWORD _t352 = AddFlag(_v5, 0EA17A315) & 100
00E5645B |. 46 51 vPushReg4 vR8 DWORD _t353 = 0BEAD9DEF ^ _m1
00E5645D |. 33 2A vPushReg4 vR7 DWORD _v6 = ESI
00E5645F |. 6A 89 vPushReg4 vR13 DWORD _t355 = AddFlag(_v5, 0EA17A315) & 100
00E56461 |. 0B E2 vPushReg4 vR15 DWORD _v7 = EBP
00E56463 |. E8 51 vPushReg4 vR3 DWORD _t357 = SubFlag((AddFlag(_v5, 0EA17A315) & 100) + (0 - ((0BC471B04 ^ ((CpuidEax(1) & 0FFFFFFF0 ^ 959E2235) + (CpuidEbx(1) & 0FFFFFF ^ 36D4A87C) ^ 11D9DE8)) <<> 0B) + 0EA17A315), _v5 + 0EA17A315)
00E56465 |. 19 BE vPushReg4 vR4 DWORD _t358 = (AddFlag(_v5, 0EA17A315) & 100) + (0 - ((0BC471B04 ^ ((CpuidEax(1) & 0FFFFFFF0 ^ 959E2235) + (CpuidEbx(1) & 0FFFFFF ^ 36D4A87C) ^ 11D9DE8)) <<> 0B) + 0EA17A315)
00E56467 |. FE 05 vPushReg4 vR1 DWORD _v8 = EDI
00E56469 |. AD 02 vPushReg4 vR14 DWORD _t360 = _v5 + 0EA17A315
00E5646B |. 88 29 vPushReg4 vR13 DWORD _t361 = AddFlag(_v5, 0EA17A315) & 100
00E5646D |. 7B 6A vPushReg4 vR5 DWORD _t362 = CpuidEdx(1) ^ 91204B4B
00E5646F |. C4 16F9455D vPushImm4 0E4EFBE71 DWORD _t363 = 0E4EFBE71
00E56474 |. 47 96 vPushReg4 vR11 DWORD _t364 = 11D9DE8
00E56476 |. 60 vAdd4 DWORD _t365 = 0E60D5C59; DWORD _t366 = AddFlag(11D9DE8, 0E4EFBE71)
00E56477 |. 35 CE vPopReg4 vR0 DWORD _t367 = AddFlag(11D9DE8, 0E4EFBE71)
00E56479 |. 94 39 vPushReg4 vR6 DWORD _v9 = 0
00E5647B |. 7B 6E vPushReg4 vR8 DWORD _t369 = 0BEAD9DEF ^ m1
00E5647D |. 69 vJmp_00E53292 if (unpacked) goto ED_AO.00E77BAC
看到最后一句,神奇的插件已经分析出来了
脱壳的话就要跳走,而没脱壳的就运行到了下一句
再看控制跳走的数据,就是前一句push的vR8
来到我贴的代码的开头,有几个nand的调用
大概分析一下就是vR8 = ~ ( vR9 & ~41526210 ) & (~vR9 & ~0BEAD9DEF)
要想vR8最后出来的是00E77149(不跳)
就将两个立即数都该成00E77149就行了,离散数学~~
还有其他几个导出函数,都是一样的,手动改一下,保存,运行
测试结果是,普通攻击,技能,魔法,S技能,存档,读档完全正常
接下来就是将中国人都知道的"健康游戏忠告"搞掉.....这个还没开始弄就不说了
之前看帖子,各位大神都表示vmp是浪费生命
一直没碰过,真正试了一下,感觉虽然是难
但是爆破还是可行的,浪费一点时间重要的还是学到了很多东西
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课