0x00 前言

rust的题,主打一个看不懂,全部交由动调,动调功底要求很高

0x01 解

无壳,直接上IDA

查找字符串,

交叉索引一下去到函数sub_40B2E0

由于是rust,反编译就不看了,直接看汇编,来到input的地方

往下走可以发现两个cmp

一个个试,先输入小于7的,然后再输入大于0x15的,会发现小于7的直接call了个HeapFree,因此输入要大于0x15

继续向下走,又一个CMP,注意这里rdi存储的是输入的长度(十六进制),发现小于0x47是正确的

继续向下,由于下面有SIMD指令,因此打开xmm寄存器窗口便于观察

之后一直向下走,发现在这里又进行了一次对比

从上图可以发现在最后还对比了一个0x7D(即}

因此合理推断flag长度为0x28(40)个字符

重新进行调试,经过一些HeapAlloc之类的初始化(不过这个Alloc之后没几步就Free了,不知道在干啥)

一直往下走,发现只有这里才开始取输入内容

取了三个字节到三个不同的寄存器

r13d的内容丢到这里面了

edi的内容在这

r14d的内容在这

最后还要进行一次与运算

以上这些都在一个循环内,循环会对输入的全部进行计算,但是输入去掉flag头和尾只有32字节,因此最后还要补0x00,整个循环如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
input = ['0','1']
out = []

#r13d = 0
#edi = 1
#r14d = 2

while len(input) % 3 !=0:
input.append(chr(0x00))

for i in range(0,len(input),3):
tmp0 = ord(input[i])
tmp0 = (tmp0>>2) & 0xff # 取了input[i]的高6位
out.append(tmp0)

tmp1 = ord(input[i])
tmp2 = ord(input[i+1])
tmp2 = (tmp2>>4) & 0xff # 取了input[i+1]的高四位(放在低四位)
tmp1 = (tmp1<<4) & 0xff # 取了input[i]的低四位(放在高四位)
tmp1|=tmp2 # 取的位进行合并
tmp1&=0x3f # 取了input[i]的低二位(放在高二位,前面补0)
out.append(tmp1)

tmp3 = ord(input[i+2])
tmp4 = ord(input[i+1])
tmp3 = (tmp3>>6) & 0xff # 取了input[i+2]的高二位(放在低二位)
tmp4 = (tmp4<<2) & 0xff # 取了input[i+1]的低六位(放在高六位)
tmp4|=tmp3 # 取的位进行合并
tmp4&=0x3f # 取了input[i+1]的低四位(放在高四位,前面补0)
out.append(tmp4)

tmp5=ord(input[i+2])
tmp5&=0x3f # 取了input[i+2]的低六位
out.append(tmp5)

for i in out:
print(hex(i))

之后跳出循环,继续走,会发现有很多运算,下图中rsi存的就是变换后的结果,图中可以看到rsi被两次调用,分别丢进了ecx和eax,这两个寄存器经过移位,最后放入edx中,然后edx与一个值做加法,最后和rcx和r8d一起丢入函数sub_40ABA

后面的流程与这段大同小异,会不断调用sub_40ABA0

进去这个sub_40ABA0

反汇编可以看到很多switch

看不懂,继续动调(下个硬断会发现一直在一段循环,所以这边直接下在switch jmp的位置,然后一步步执行,遇到循环就下switch jmp),最后会到这个地方,一看就不太对劲

进去sub_40A800发现参数是rsi的内容

之后会在这个地方进行循环,猜测这个应该是解释器

继续动调发现后面还有调用sub_40A800的地方,且这些地方在进函数之前会使用一个rsi+0x410左右的地方

动过动调,发现这个区域会反复变化

应该是寄存器了

不得不说sub_40ABA太乱了,无法分析,只能去看看sub_40A800如何

所幸这玩意结合动调能看,虽然不是完全能看

a1[1052]处下硬件断点,会发现最后进了一个比较,下面就是成功的字符串

由此可见这个a1[1052]最后的结果要为0,所以它是一个错误数的统计,根据这个就能找到操作了

由于a1[1052]依赖于a1[1048]进行判断,而a1[1048]又与a1[1049]以及其他东西有关系,但a1[1049]和其他东西不太好跟踪,所以直接只跟踪a1[1048],其中要重点关注这个值的赋值和判断

首先是几个mov,基本都是赋值0

然后是这个add,里面的结果与输入没什么关系

之后又是几个mov也没看到输入的影子

最后在经历几次如上的循环后来到了xor

发现第一次xor的内容是输入

第二次的内容是固定的

两次xor之后就到了cmp了

这下看懂了,要获得转成四字节后的密文,只需要将执行到第一次xor时a1[1048]的值和第二次xor时al的值进行异或即可

这边学习一下IDA python的动调脚本:动调脚本化 — ida_python

IDApython帮助文档

以及中文互联网没有的:ida-pro-scripting-debugger-with-python-fails-on-step-over-run-to

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import idaapi
import idc
import ida_dbg


entered = ['0xc', '0x3', '0x4', '0x32', '0xc', '0x33', '0x10', '0x35', '0xd', '0x23', '0x1c', '0x38', '0xe', '0x13', '0x0', '0x31', '0xc', '0x23', '0xc', '0x34', '0xd', '0x13', '0x18', '0x37', '0xe', '0x3', '0x24', '0x30', '0xc', '0x13', '0x8', '0x33', '0xd', '0x3', '0x14', '0x36', '0xd', '0x33', '0x20', '0x39', '0xc', '0x3', '0x4', '0x0']
out = []
al = []
flag = 0
i=0
while 1:
if i >= len(entered):
break
ida_dbg.run_to(0x40A937)
ida_dbg.wait_for_next_event(ida_dbg.WFNE_SUSP, -1)
try:
rsi = ida_dbg.get_reg_val("RSI")
rax = ida_dbg.get_reg_val("RAX")
except:
break
if hex(rax) == entered[i] and i != len(entered)-1:
out.append(idc.get_wide_byte(rsi+0x418))
flag = 1
i += 1
continue
if flag == 1:
al.append(rax)
flag = 0
print(out)
print(al)
for i in out:
print(hex(i),end=', ')
print()
for i in al:
print(hex(i),end=', ')

最后可以得到两份密文

1
2
s1 = [0x0, 0x82, 0x11, 0x92, 0xa8, 0x39, 0x82, 0x28, 0x9a, 0x61, 0x58, 0x8b, 0xa2, 0x43, 0x68, 0x89, 0x4, 0x8f, 0xb0, 0x43, 0x49, 0x3a, 0x18, 0x39, 0x72, 0xc, 0xba, 0x76, 0x98, 0x13, 0x8b, 0x46, 0x33, 0x2b, 0x25, 0xa2, 0x8b, 0x27, 0xb7, 0x61, 0x7c, 0x3f, 0x58]
s2 = [0x18, 0xb1, 0x9, 0xa4, 0xa6, 0x2a, 0x9e, 0x1b, 0x96, 0x57, 0x5d, 0xad, 0xae, 0x75, 0x65, 0xac, 0x9, 0x8c, 0xa0, 0x76, 0x47, 0x2c, 0x10, 0x1, 0x7c, 0xf, 0xba, 0x47, 0x95, 0x30, 0x9b, 0x74, 0x3f, 0x2d, 0x2d, 0x9a, 0x87, 0x31, 0xba, 0x43, 0x70, 0x2c, 0x4c]

然后就是异或和四字节转三字节的操作了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
s1 = [0, 130, 17, 146, 168, 57, 130, 40, 154, 97, 88, 139, 162, 67, 104, 137, 4, 143, 176, 67, 73, 58, 24, 57, 114, 12, 186, 118, 152, 19, 139, 70, 51, 43, 37, 162, 139, 39, 183, 97, 124, 63, 88, 0]
s2 = [24, 177, 9, 164, 166, 42, 158, 27, 150, 87, 93, 173, 174, 117, 101, 172, 9, 140, 160, 118, 71, 44, 16, 1, 124, 15, 186, 71, 149, 48, 155, 116, 63, 45, 45, 154, 135, 49, 186, 67, 112, 44, 76, 0]
for i in range(len(s1)):
s1[i]^=s2[i]

f = []

for i in range(0,44,4):
f.append((s1[i]<<2)|(s1[i + 1] >> 4))
f.append((s1[i+1] << 4) | (s1[i + 2] >> 2))
f.append((s1[i+2] << 6) | s1[i + 3])

for i in f:
print(chr(i&0xff),end="")

# c669733af3ce4459b88016420b81cb15

包上DASCTF{}就行了