首先进行查壳.
发现并没有查出信息,(我不认识)
而后运行程序查看信息.
发现要好久之后才会算出信息. 首先拖入IDA进行分析
程序开头进行两个 VirtualProtect 进行修改.修改属性为可读写执行. 下面会不断的进行代码重定位.并且操作 FS:[4] FS:[8] 等来进行保存用户输入的UserName 以及 序列号
如下:
请不要看后面的 分析注释.因为后来是在x64dbg中重新进行了分析.毕竟壳子还是要在程序中动态分析
这里说一下 FS:[4] FS:[8] 代表啥意思 fs:[4] fs:{8] 分别代表 堆栈顶部以及堆栈底部 Fs:[0] 是属于TIB 具体信息可以看 浅谈FS寄存器 这篇文章
其实就是操作堆栈
根据上方分析.并没有得到价值结果.倒是可以看出是很有规律的一段代码. 直到在内存中看到 如下
call eax 最终会调用eax 进行解密. 也就是说.用户输入的数据. 最终会传送给 eax. eax的地址里面就是进行解密的.
到了这心里想.这不简单了.直接定位call eax. 进去把代码抠出来.解密. (在想这个问题的时候,看到风神不到2 or 2个多 个小时就做出来了.所以心想就很简单.)所以傻愣愣的定位. 发现被坑.
其实call eax之后会有很多重复代码. 所以并不是最终的解密. 因为call eax之后.里面会不断的进行调用call eax.并且进行解密.
如果调试过这道题的人可能知道. call eax之后会有很多相同的call eax. 所以要总结规律. 而第一次调用call eax是固定的. 所以大家第一想法是写脚本来定位call eax 之后的数据.并且进行解密.
说到这里.相比很多人就可能不做了. MD 脚本 太难了.不做. 我也这样认为.简单的看了一下脚本资料.(OD的.并不是x64的.基本通用)真的花了不到半个小时就学会了脚本编写. 这道题没做出的原因是因为 脚本写的不熟练 + 调试脚本浪费的时间比较长. 为啥浪费后面讲下就知道了. 最后要普及一下脚本知识.
说到这里.可能就像跃跃欲试的去脱壳.写脚本了.没错我也是这样做的. 首先找规律 我们第一次可以看到. 他操作 fs:[8] 之后.在附近的位置就能找到call eax. 所以这里我普及一下脚本知识
所谓脚本.就是帮助你做一些复杂性的事情. 比如 按照你手动脱壳 你可能会以下操作
1.在FS:[8]位置下断点(内存,cc 硬件..)
2.在call eax 位置下断点
3.单步进入
4.找到代码进行手工dump
第一步可能大家不需要. 可能直接在 2 3 4执行了.
所以这里就普及下脚本的知识
bph 下硬件断点
find 查找特征 (OD中是 findop --> find opecode的缩写)
sti 单步步入
savedata 保存内存
其他:
erun 运行
log 输出
mov $xxx,0 变量赋值
jxx 跳转指令
dbh 去掉反调试
利用这几条命令就可以完成自动寻找call eax, 自动下断点 自动dump内存了
具体的命令可以看x64 Dbg的文档 x64Dbg文档
其实脚本不难.难的是调试以及分析
因为要程序的动态解析call eax 所以需要对 fs:[8] 下断点.这样才可以动态的做.
简单的脚本如下:
dbh bph fs:[18]+8,w,1 erun find cip,FFD0,100 erun $RESULT
来说下脚本意思
然后屡一下逻辑
通过对 fs:[8]下硬件写入.当断下来之后,在附近100个字节的范围内寻找call eax. 如果找到就执行到RESULT(当前最简单的是没有用cmp来判断结果是否为零)
为了便于x64查看是否断到call eax 所以我并没有继续添加 sti(步进)指令. 添加了可以直接跳转到call eax 内部,相当于 F7 (sto则是 F8)
看下X64
所以这就是脚本为我们带来的好处.一下就行为到了. 脚本还可以用于常见的脱壳. 因为脚本大都是类似的.所以学习下就可以.
然后进入call eax查看 如下图:
覆盖之前内存,如下
重复解密
根据上方三个图得知
1.首先往下跳转
2.将之前跳转过来的内存进行覆盖,全部覆盖为C3 (恶心,没法回溯)
3.又调用新的一次解密
所以结论就是.不断的call eax call eax call eax 解密解密解解密
所以我们的简单脚本又要复杂起来了
根据上方得知.我们可以将脚本修改为如下.
dbh //去掉反调试 mov $CountCallEax,0 //定义伪变量 bph fs:[18]+8,w,1 //对FS:[8] 下下入断点 start: erun //运行起来. run g 都可以 find cip,FFD0,100 //查找call eax 特征 cmp $RESULT,0 jz End erun $RESULT //执行到call eax sti //进入call eax inc $CountCallEax log "Hit {$CountCallEax}" //打印循环次数 jmp start //继续新的一轮循环 End: bphc fs:[18]+8 //清除硬件断点 msg "End Value"
可以看出我们脚本加了一个判断.并且循环寻找call eax 并且进入
因为很少写脚本所以调试了很久才写出来. 所以别看很简单.其实一点都不简单. 因为脱一次壳要10分钟...最好加个打印次数
开始执行脚本寻找结果
执行到这里发现,循环到1D 就没找到call eax了.看反汇编窗口可以看出.确实对 fs:[8] 进行操作了.但是下面不再是 call EAX.而变成了. "E8 01000000"
其实到这里.入口特征变了但是里面执行基本是一样.所以代码还能重用.
所以继续升级脚本.
dbh //去掉反调试 mov $CountCallEax,0 bph fs:[18]+8,w,1 //对FS:[8] 下下入断点 start: erun //运行起来. run g 都可以 find cip,FFD0,100 //查找call eax 特征 cmp $RESULT,0 jnz Opt // 不是零跳转到OPT操作 find cip,E801000000,100 //为零寻找新的特征 cmp $RESULT,0 jnz Opt //不为零跳转到OPT操作 jz End //为零跳转到END Opt: erun $RESULT //执行到call eax sti //进入call eax inc $CountCallEax log "Hit {$CountCallEax}" //打印循环次数 jmp start //继续新的一轮循环 End: bphc fs:[18]+8 //清除硬件断点 msg "End Value"
新的脚本加了判断E8的类型.所以继续跑.
十分钟过去后...... 如下图
发现脚本跑了578次.而看反汇编窗口已经内存都无效了. 原因就是上面分析的.会清空.所以我直接断到577次. 最后在手动进行解密
完整脚本如下:
注意:
上方的脚本是在写帖子的时候重新写的脚本一步步实验的结果.
下面的脚本才是调了两天的脚本.原理一样.有少许不同. 因为跑一次10几分钟.现在发帖也是晚上10点多.要休息了.所以直接完整脚本.如果前边看了.那么下面的脚本也能看明白.
dbh bph 004014E0 erun bp printf bphc 004014E0 mov $Count,0 mov $MaxWhile,577 //遍历577次. bph fs:[18]+8,w,1 START: erun find cip,FFD0,100//查找特征 FFD0 Call eax cmp $RESULT,0 jnz NOZEROVALUE find cip,E801000000,100 //查找特征call 0x0100000000 cmp $RESULT,0 jnz NOZEROVALUE //e8特征比对 jmp End NOZEROVALUE: erun $RESULT add $Count,1 log "Hit {$Count}" cmp $Count,$MaxWhile jnz START //不是0就是不相等.然后继续执行 jz LastCall //如果是最后一次.手动的进入. LastCall: sti //步进, 最后会遇到ret 最后一次一次的手工解密 find cip,c3,300 cmp $RESULT,0 jz End erun $RESULT //到达RET,最后步过,寻找特征码 sto //步过 find cip,0F85????????,0x300 //寻找jne位置.popad popfd等解密出来才能找到 cmp $RESULT,0 jz End erun $RESULT+6 //执行到jne下一行指令来寻找ffd0 find cip,FFD0,0x200 cmp $RESULT,0 jz End erun $RESULT //运行到最后一个call eax位置. sti //不是0暂停 sti find cip,55,0x30 //寻找push ebp cmp $RESULT,0 jz End erun $RESULT //执行到push ebp savedata "E:\CTF\6\sdbf.bin",cip,3454 //计算push ebp -- pop ebp内存大小写死了,最后修改为ret即可 3454是 pause End: bpc printf msg "End Value" bphc fs:[18]+8
最后修改出来的bin如下.
你dump出来的bin 最后要修改为C3 然后使用IDA的功能进行创建函数.来创建一个函数.此时就可以F5了 (这个函数就是解密函数了.根据CrackMe Call Eax之前的代码,拼接起来就是一个完整的程序)
鉴于调试脚本花费大量时间.然后解密出来的题也没看了.最后看看大佬们怎么写.
这道题虽然没有做出来.但也希望能给个精华(内心偷笑,明目张胆好吗 手动滑稽). 希望这个普及贴能帮助到更多人.
最后于 6小时前 被kanxue编辑 ,原因: