首页
论坛
课程
招聘
[原创]CTF2019_第6题 三道八佛_脱壳详解.
2019-12-15 22:34 3501

[原创]CTF2019_第6题 三道八佛_脱壳详解.

2019-12-15 22:34
3501

目录

CT2019第六题三道八佛脱壳笔记

一丶CrackMe的初步分析

1.1 PEID 查壳

首先进行查壳.
图片描述

 

发现并没有查出信息,(我不认识)
而后运行程序查看信息.
图片描述

 

发现要好久之后才会算出信息. 首先拖入IDA进行分析

1.2 IDA的初步分析

图片描述

 

程序开头进行两个 VirtualProtect 进行修改.修改属性为可读写执行. 下面会不断的进行代码重定位.并且操作 FS:[4] FS:[8] 等来进行保存用户输入的UserName 以及 序列号

 

如下:
图片描述

 

请不要看后面的 分析注释.因为后来是在x64dbg中重新进行了分析.毕竟壳子还是要在程序中动态分析
这里说一下 FS:[4] FS:[8] 代表啥意思 fs:[4] fs:{8] 分别代表 堆栈顶部以及堆栈底部 Fs:[0] 是属于TIB 具体信息可以看 浅谈FS寄存器 这篇文章
其实就是操作堆栈

1.3 认识壳核心

根据上方分析.并没有得到价值结果.倒是可以看出是很有规律的一段代码. 直到在内存中看到 如下

 

图片描述

 

call eax 最终会调用eax 进行解密. 也就是说.用户输入的数据. 最终会传送给 eax. eax的地址里面就是进行解密的.
到了这心里想.这不简单了.直接定位call eax. 进去把代码抠出来.解密. (在想这个问题的时候,看到风神不到2 or 2个多 个小时就做出来了.所以心想就很简单.)所以傻愣愣的定位. 发现被坑.
其实call eax之后会有很多重复代码. 所以并不是最终的解密. 因为call eax之后.里面会不断的进行调用call eax.并且进行解密.

1.4 总结壳规律

如果调试过这道题的人可能知道. call eax之后会有很多相同的call eax. 所以要总结规律. 而第一次调用call eax是固定的. 所以大家第一想法是写脚本来定位call eax 之后的数据.并且进行解密.
说到这里.相比很多人就可能不做了. MD 脚本 太难了.不做. 我也这样认为.简单的看了一下脚本资料.(OD的.并不是x64的.基本通用)真的花了不到半个小时就学会了脚本编写. 这道题没做出的原因是因为 脚本写的不熟练 + 调试脚本浪费的时间比较长. 为啥浪费后面讲下就知道了. 最后要普及一下脚本知识.

二丶脚本学习

2.1 编写简单的脱壳脚本

说到这里.可能就像跃跃欲试的去脱壳.写脚本了.没错我也是这样做的. 首先找规律 我们第一次可以看到. 他操作 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

来说下脚本意思

  1. dbh 去掉反调试.也就是x64dbg中的高级中的 隐藏调试器功能
    2.bph fs:[18]+8,w,1 对寄存器 fs:[8]下硬件写入断点,长度为1. 为什么是fs:[18] fs:[18] 32位下就是指向tib +8才是你要断的地址
    3.erun 运行程序
    4.find cip,FFD0,100 查找FFD0 也就是 call eax的特征.在附近100个字节位置寻找
    5.erun $RESULT 当find查找到结果的时候.则会将查找到的地址放到伪变量 $RESULT中(OD中也是) erun $RESULT的意思就是执行到 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 解密解密解解密
所以我们的简单脚本又要复杂起来了

2.2 脚本升级,对多次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"
其实到这里.入口特征变了但是里面执行基本是一样.所以代码还能重用.
所以继续升级脚本.

2.3 脚本升级 定位E8 (call 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之前的代码,拼接起来就是一个完整的程序)
鉴于调试脚本花费大量时间.然后解密出来的题也没看了.最后看看大佬们怎么写.


[公告] 2021 KCTF 春季赛 防守方征题火热进行中!

最后于 2020-9-14 13:38 被TkBinary编辑 ,原因:
收藏
点赞0
打赏
分享
最新回复 (2)
雪    币: 8850
活跃值: 活跃值 (4497)
能力值: ( LV13,RANK:370 )
在线值:
发帖
回帖
粉丝
TkBinary 活跃值 5 2019-12-15 22:57
2
0
最后说一下,脚本的单步调试是 TAB按键. 可以再脚本上面按Tab让它单步执行 空格键是执行起来. 
雪    币: 479
活跃值: 活跃值 (1318)
能力值: ( LV9,RANK:176 )
在线值:
发帖
回帖
粉丝
nevinhappy 活跃值 2 2019-12-17 11:15
3
0
好吧,壳是误报,内容挺清楚的。
最后于 2019-12-17 11:26 被nevinhappy编辑 ,原因:
游客
登录 | 注册 方可回帖
返回