unholy Writeup

这周末和小伙伴们一起打BKPCTF,我一个web狗,竟然撸逆向停不下来。。。撸了两个晚上。。。终于撸出来了。。

unholy这题说是ruby+python,其实我觉得主要还是逆C。。

1
2
3
4
5
6
7
8
9
10
main.rb
require_relative 'unholy'
include UnHoly
python_hi
puts ruby_hi
puts "Programming Skills: PRIMARILY RUBY AND PYTHON BUT I CAN USE ANY TYPE OF GEM TO CONTROL ANY TYPE OF SNAKE"
puts "give me your flag"
flag = gets.chomp!
arr = flag.unpack("V*")
is_key_correct? arr

一个很简单的ruby脚本,不过这里需要装libruby2.1才能跑。。。(自己装装就知道了,国内装libruby2.1有点坑)
然后重点在于unholy.so,丢到ida中逆向看看,有几个重要的地方,主要是method_check_key函数,也就是ruby里的is_key_correct?
这个函数规定了只能传入长度为9的int型数组

1
mov     [rsp+13E8h+matrix+24h], 61735320h ; matrix[9] = 0x61735320

形成长度为10的数组,然后经过如下算法

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
38
39
40
41
42
43
44
loc_BFC:                                ; CODE XREF: method_check_key+112j
.text:0000000000000BFC block = rdx ; uint32_t[2]
.text:0000000000000BFC mov rax, [rbx+rdi]
.text:0000000000000C00 block = rax ; uint32_t[2]
.text:0000000000000C00 xor r8d, r8d
.text:0000000000000C03 mov edx, eax
.text:0000000000000C05 shr block, 20h
.text:0000000000000C09
loc_C09: ; CODE XREF: method_check_key+FDj
.text:0000000000000C09 mov ecx, r8d
.text:0000000000000C0C mov esi, eax
.text:0000000000000C0E and ecx, 3
.text:0000000000000C11 shr esi, 5
.text:0000000000000C14 mov r9d, [rsp+rcx*4+13E8h+key]
.text:0000000000000C19 mov ecx, eax
.text:0000000000000C1B shl ecx, 4
.text:0000000000000C1E xor esi, ecx
.text:0000000000000C20 lea ecx, [rsi+rax]
.text:0000000000000C23 add r9d, r8d
.text:0000000000000C26 sub r8d, 61C88647h
.text:0000000000000C2D xor ecx, r9d
.text:0000000000000C30 add edx, ecx
.text:0000000000000C32 mov ecx, r8d
.text:0000000000000C35 shr ecx, 0Bh
.text:0000000000000C38 mov esi, edx
.text:0000000000000C3A and ecx, 3
.text:0000000000000C3D shr esi, 5
.text:0000000000000C40 mov r9d, [rsp+rcx*4+13E8h+key]
.text:0000000000000C45 mov ecx, edx
.text:0000000000000C47 shl ecx, 4
.text:0000000000000C4A xor esi, ecx
.text:0000000000000C4C lea ecx, [rsi+rdx]
.text:0000000000000C4F add r9d, r8d
.text:0000000000000C52 xor ecx, r9d
.text:0000000000000C55 add eax, ecx
.text:0000000000000C57 cmp r8d, 0C6EF3720h
.text:0000000000000C5E jnz short loc_C09
.text:0000000000000C60 shl rax, 20h
.text:0000000000000C64 block = rdx ; uint32_t[2]
.text:0000000000000C64 or rax, block
.text:0000000000000C67 mov [rbx+rdi], rax
.text:0000000000000C6B add rdi, 8
.text:0000000000000C6F cmp rdi, 28h
.text:0000000000000C73 jnz short loc_BFC

这个算法转换用python写是:

1
2
3
4
5
6
for i in xrange(0, 10, 2):
flag = 0
while flag != 0xC6EF3720:
arry[i] += (flag + key[(flag)&3])^(arry[i+1] * 16 ^ (arry[i+1] / 32) + arry[i+1])
flag = ctypes.c_uint32(flag - 0x61C88647).value
arry[i+1] += (flag + key[(flag / 2048)&3])^(((arry[i] * 16) ^ (arry[i] / 32)) + arry[i]);

最后输出的数组

1
cmp     [rsp+13E8h+matrix+24h], 4DE3F9FDh ;           matrix[9] == 0x4DE3F9FD

如果第十个数的值为0x4DE3F9FD, 则把前9个值传入下面的python脚本

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
import struct
import sys
X=[
[%d,%d,%d],
[%d,%d,%d],
[%d,%d,%d]
]
Y = [
[383212,38297,8201833],
[382494 ,348234985,3492834886],
[3842947 ,984328,38423942839]
]
n=[5034563854941868,252734795015555591,55088063485350767967,
-2770438152229037,142904135684288795,-33469734302639376803,
-3633507310795117,195138776204250759,-34639402662163370450]
y=[
[0,0,0],
[0,0,0],
[0,0,0]
]
A=[0,0,0,0,0,0,0,0,0]
for i in range(3):
for j in range(3):
for k in range(3):
y[i][j]+=X[i][k]*Y[k][j]
c=0
for r in y:
for x in r:
if x!=n[c]:
print "dang..."
sys.exit(47)
c=c+1
print ":)"

然后算出X

1
2
3
4
5
X=[
[-1304886577,722035088,1368334760],
[1473172750,412774077,-908901225],
[-490967005,563111828,-952589187]
]

所以上面那个算法的输出要为:

1
matrix = [-1304886577,722035088,1368334760,1473172750,412774077,-908901225,-490967005,563111828,-952589187, 1306786301] 

然后逆向算法最后得出脚本

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
# reserve.py
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import ctypes

arry =[-1304886577,722035088,1368334760, 1473172750,412774077,-908901225, -490967005,563111828,-952589187, 1306786301]
key = [1952540791, 1768908659, 1852794734, 1701995880]
for x in range(len(arry)):
arry[x] = ctypes.c_uint32(arry[x]).value

for i in xrange(0, 10, 2):
flag = 0xC6EF3720
while flag != 0:
v12 = flag + key[(flag / 2048)&3]
ecx4 = ((arry[i] * 16) ^ (arry[i] / 32)) + arry[i]
x = v12^ecx4
arry[i+1] = arry[i+1] - x
arry[i+1] = ctypes.c_uint32(arry[i+1]).value

flag = ctypes.c_uint32(flag + 0x61C88647).value

v11 = flag + key[flag&3]
ecx2 = ((arry[i+1] * 16) ^ (arry[i+1] / 32)) + arry[i+1]
y = v11^ecx2
arry[i] = arry[i] - y
arry[i] = ctypes.c_uint32(arry[i]).value

for x in range(len(arry)):
arry[x] = ctypes.c_int32(arry[x]).value
print arry

这里有几个重要的地方,坑了我好久,一个是优先级的问题,比如x ^ y + z这个其实是 x ^ (y + z)
还有一个,就是python不需要定义变量类型,而matrix十个值为uint_32, 所以python 就要通过ctypes进行转换,还有flag也需要进行转换.

最后得出

1
[1129335618, 1752909396, 544042349, 2036889439, 1684628512, 1696622880, 544105846, 1948282724, 2104715624, 1634947872]

这个数组就是flag

1
2
3
4
5
6
7
8
9
10
11
flag = ""
for x in arry:
tem_hex = hex(x)[2:]
tem_list = []
for y in xrange(0, len(tem_hex), 2):
tem_list.append(chr(int(tem_hex[y:y+2], 16)))
tem_list.reverse()
flag += "".join(tem_list)

print flag
# BKPCTF{hmmm _why did i even do this}
Author

Hcamael

Posted on

2016-03-07

Updated on

2017-08-08

Licensed under