首页
论坛
课程
招聘
[原创]看雪CTF2018-第八题writeup
2018-7-1 23:57 1647

[原创]看雪CTF2018-第八题writeup

2018-7-1 23:57
1647

0x00 程序大体流程分析

用ida插件Findcrypt发现程序中可能使用到了CRC32算法

程序先打印了提示信息后,用fgets函数获取了用户输入,然后执行了sub_404A60()函数,这个函数十分关键,后面也曾多次使用,主要功能为对一段代码进行CRC32检验,然后根据校验值获得函数地址后跳转执行或者作为函数的返回值,当你在一些地方下断点或者修改程序代码时,程序就会执行错误的流程,这正是题目含义所在

第一次执行这个函数时根据代码段0x40143C到0x4023c3的CRC32值0x004013C0,跳转到函数40013C0处执行,对输入进行了检查,必须为0-9,a-z,A-Z
然后就是一些作者自己写的大整数运算函数了
大整数的数据结构大致如下
struct Bignum{
unsigned int base;//基数
int mark; //好像是标识base是否在2到256之间
int length; //数的长度
char flag; //标识正负,0为正数,1为负数
char num[515]; //具体数值
}
首先对输入查表0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz,转换为下标,然后转化为大整数结构,注意基数为表的长度0x3e,并且输入的字符串低位放在了大整数num数组的高位,例如输入123456789,就换成了下面的数据结构

然后是第二次执行sub_404A60()函数,根据40158e到4023a0的CRC32值返回了0x100作为后面大整数的基数
然后是一些大整数的初始化和运算函数,刚开始根据代码识别出来了大整数加法函数和比较函数,但后面的函数太复杂,所以采用了动态调试来标识函数,由于编译器优化,传参变得比较奇怪,根据汇编代码,动态调试时,主要关注三个地方的变化,ecx所指的地方,0019d334处的数据和函数ret处上方的repe movsd时esi所指的数据。
标识完所有的大整数运算函数,大致如下

主要逻辑如下:
首先把输入的基数为0x3e的大整数A转换为了基数为0x100的大整数B,然后把B的数据部分分成了四段,结构如下
struct data{
char a_len;
char b_len;
char c_len;
char d_len;
char a[a_len];
char pad[4];
char c[c_len];
char d[d_len];
}
并做了一些限制
b_len=a_len+4;
a_len+c_len+d_len+8=data_len;(由此推出pad的长度为4)
然后就是分别对各个部分进行校验

0x01 5层汉罗塔

首先把数组a转化为基数为0x100的大整数结构,然后转为了基数为0x24的大整数结构,然后做替换表为abcdefghijklmnopqrstuvwxyz0123456789运算,传入函数sub_4010A0(),函数又做了解码,等于还是为0x24进制的数据
如何发现是汉罗塔的呢
由于该函数的返回值必须为1才算正确,遍历函数发现只有一处函数返回1

发现最后*v6必须为0,刚开始时*v6为0x1f,每次获取参数后,模6分为2部分,传入swith结构,对*v6进行加与减

 

*v6减多少,别的数据就加多少,有点像把一个东西移到另一个地方
再看 sub_401080()函数


传入0返回0,传入11111(二进制)返回1,传入11110返回10,传入11100返回100,传入11000返回1000,像是取出最右边的东西
switch执行相应操作时,还有一些限制条件,也符合汉罗塔的结构
知道了是5层汉罗塔就好解决了,题目为了防止多解限定了第二步必须为0,最后得到31步的数据依次为10 51 23 10 54 25 10 51 23 12 54 23 10 51 23 1(6进制)
转化为24进制为0x01 0x0b 0x14 0x01 0x1d 0x20 0x01 0x0b 0x14 0x0d 0x1d 0x14 0x01 0x0b 0x14 0x01
转化为基数为0x100的数据为0x3dd7c4ddec9ae7c5e8c1,可得到a_len=0x0a,b_len=0x0e,char a[0x0a]={0x0a,0x0e,0x13,0x13,0xc1,0xe8,0xc5,0xe7,0x9a,0xec,0xdd,0xc4,0xd7,0x3d}

0x02 解方程

首先把字符串'welcome to bbs.pediy.com.'和'Author by Lookhc'查表'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz. '分别转为基数为0x40的大整数M和N
校验时采用了C++异常机制做了混淆

根据C++异常的数据结构找到catch中的处理代码,发现给参数lpMem赋值为2

然后发现真实的校验逻辑为解方程

4*Z+2*Y=M
Y-Z=N
Y为数组c转为来的大整数
Z为数组d转化来的大整数

代码如下

a=[0x3E ,0x30 ,0x32 ,0x26 ,0x3E ,0x3C ,0x2C ,0x27 ,0x28 ,0x33 ,0x3E ,0x36 ,0x25 ,0x25 ,0x3F ,0x32 ,0x37 ,0x3F ,0x28 ,0x30 ,0x32 ,0x26 ,0x2F ,0x28 ,0x3A]
b=[0x26,0x2B,0x2E,0x32 ,0x32 ,0x15 ,0x3F ,0x3C ,0x25 ,0x3F ,0x35 ,0x32 ,0x2B ,0x37 ,0x38 ,0x0A]
#4*Z+2*Y=M
#Y-Z=N
M=0
N=0
for i in range(len(a)):
    M=M+a[i]*(64**i)

for j in range(len(b)):
    N=N+b[j]*(64**j)

Z=((M-2*N)>>1)//3
Y=N+Z

print("Y:"+hex(Y))
print("Z:"+hex(Z))

0x03 CRC32爆破

程序中还有一个校验字符串data的CRC32值是否为0,在网上找了段C语言CRC32的代码爆破得到填充的字节为0xA7,0x1C,0x2F,0x2C
最后利用程序中的函数sub_4041E0()把基数为0x100的数据转为基数为0x3e的大整数结构,最后替换表0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
得到注册码为:6OqTbC16uYclIp3aqSoJuSpYO4I9JodXMs1oaaI40wwzue79rqVxXflyoZeLxs3Q1yxO1yUoz06


[招聘] 欢迎你加入看雪团队!

最后于 2018-7-2 00:15 被新手慢慢来编辑 ,原因:
上传的附件:
收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回