这个题目久只有一个64位的ELF文件。

我写这个题目的主要目的是:1.提升自己IDApython能力。3.加强对IDA流程图的利用。4.对算法的过程的理解。

首先拿到文件然后用IDA Pro 7.7 打开

找到主要的位置,进行一个简单的分析:

image-20230926194613109

这里主要的是check_value这个函数

哇靠,这个函数IDA F5分析有错误。首先我们来看一下吧:

image-20230926194952932

通过F5来看,它就是直接的return a2+8。

感觉久很“答辩”

这里我们再来看看roots:

image-20230926194933296

它是一个充满了指针的一个数组。

我们随便看一个node_吧

image-20230926195055350

可以看到一个node里面有好多的0,然后其他的就不是0。感觉这些数据就很符合while ( !*(_BYTE *)a2 ) 的构造。

但是这里F5出来有一点点的小问题:它就循环一下然后就退出去了。

我们这里看看汇编代码:

image-20230926195431552

这里汇编看到的是:循环调用自身,而不是简单的一个while循环。感觉也大差不差的。

大致看了check的代码,我们回到main

大概就是传入的flag值,进入check函数里面去比较。值需要相同,这里用爆破的思想来爆破出flag的值。

我们循环遍历可见字符,然后去模拟这个check函数。

这里一个59次的循环:

image-20230926200701909

可以看到这里的cmp rdx,3Bh【控制循环】

每一次循环的时候 有:rsi, [r8 + rdx*8] 和 inc rdx

也就是每一次循环出来后roots +8

简单的模拟一下:

1
2
3
4
5
6
7
8
9
10
for i in range(59):
node = get_qword(roots) #读取roots的地址值是node,等会就需要从这个地址出发去寻找。
c = 0x20
while c<= 0x7f: #这里的作用是让c来遍历每一个可见字符,当检测到的时候就添加在flag后面
if check_value(c,node) == 1:
flag +=chr(c)
break
c = c+1
roors += 8 #这里就是 每找到一个字符,roots的起始地址就需要改变
#.text:0000564E35E56397 8A 46 08 mov al, [rsi+8]

看汇编代码更加的清晰。

现就就是需要来模拟check_value函数:

image-20230926203209656

1
2
3
4
5
6
7
8
9
10
11
12
def check_value(c,node):
if get_byte(node) == 1:
if get_byte(node+8) == 1:
return 1
else:
return 0
b1 = get_byte(node+8)
b2 = get_byte(node+9)
if c<b1 or c>=b2:
return check_value((node + 24),c)
else:
return check_value((node + 16),c)

这里你可能会想为什么会有if get_byte(node+8) == 1:

image-20230927163619644

最后总结一下这2个代码:

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
def check_value(c, node):
if get_byte(node) == 1:
r = get_byte(node + 8) & 0x1
if r == 1:
return 1
else:
return 0
b1 = get_byte(node + 8)
b2 = get_byte(node + 9)
if c < b1 or c >= b2:
return check_value(c,get_qword(node + 24))
else:
return check_value(c,get_qword(node + 16))


flag = ""
roots = 0x000000000028F900
for i in range(59):
node = get_qword(roots) # 读取roots的地址值是node,等会就需要从这个地址出发去寻找。
c = 0x20
while c <= 0x7f: # 这里的作用是让c来遍历每一个可见字符,当检测到的时候就添加在flag后面
if check_value(c, node) == 1:
flag += chr(c)
break
c = c + 1
roots = roots +8 # 这里就是 每找到一个字符,roots的起始地址就需要改变
# .text:0000564E35E56397 8A 46 08
print(flag)
print("ok")

image-20230926210015139

原本的程序是:flag字符串一个一个的进入check函数,然后返回的是ax 但是在返回之前有一个mov al,[rsi + 8]。然后进行and操作。and ecx , eax

在程序开始的时候有mov cl,1。在将flag中的字符串遍历完了后有一个test cl,cl的操作【test 也就是按位与】

我们这里不能让他跳转。也就是jz【zf标志位=1就跳转】

cl =1的情况下【zf = 0】所以这里需要cl == 1

也就是说: r = get_byte(node + 8) & 0x1
if r == 1:

这个就是说 r = 返回出来的[node + 8] & cl【0x1】 ==1

上面有一个and ecx ,eax。ecx :也就是定义好的0x1,eax 是node+8。需要操作后的cl还是==1

这里用python 写出来就是 r = get_byte[node+8] & 0x1 ==1,则就是正确的。

【这里有点点说不清楚…】

因为程序中是最后的一次出来cl == 1,所以需要每一个flag 在进行check 后的cl ==1

所以可以利用这一点去进行爆破。

循环遍历每一个可见字符,让他每一次通过一次check函数,当 cl == 1 则能得出该可见字符是正确的。