看雪CTF2017第15题WP

这是看雪CTF2017比赛的第15题。题目地址:传送门

此题壳及混淆比较麻烦,没搞定,但是可以dump绕过壳,主要算法在luajit虚拟机内,整体算法比较简单,就是两个异或。所以完全可以不用理会混淆。下面说下过程。

拖进ida,发现有壳,其中还有混淆,有大量跳转。于是动态了下,由于真实指令在就混淆跳转中,没找到入口,放弃此办法。

直接运行程序,查看了进程,只有一个进程,直接dump。原文件大小是999kb,dump出来的有3M之多。

将dump出来的文件直接拖进ida,查看字串,发现多处含有lua字样的字串,包括LuaJIT 2.1.0-beta3Lua 5.1,估计是lua的虚拟机了。继续翻看,发现了程序的输入提示Pls Input Serial Number:,其上下还有mainWrong!\r\nCongratulations!\r\n,查看其引用,只有main找到了显式引用,位置在4021EC,大致看了下,此位置代码很可疑,而且main这个词也很敏感啊,很有可以就是lua脚本的主函数名。

代码比较长,我就不全部上了。此函数或过程的入口应该在401000401005,一波跳转后,过两条nop,来到40103D40103D4021C0为向栈区写数据,这么大段的基本是硬编码的数据很可能是lua的脚本相关数据。

之后就是调用了几个函数,在402210处有个根据函数返回的条件跳转。如果不跳,则返回0。

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
.text:004021FE call sub_4130E0
.text:00402203 push 1
.text:00402205 push esi
.text:00402206 call sub_413DB0
.text:0040220B add esp, 38h
.text:0040220E test eax, eax
.text:00402210 jz short loc_40222C
.text:00402212 pop edi
.text:00402213 pop esi
.text:00402214 xor eax, eax
.text:00402216 pop ebx
.text:00402217 mov ecx, [esp+29Ch]
.text:0040221E xor ecx, esp
.text:00402220 call sub_4023E0
.text:00402225 add esp, 2A0h
.text:0040222B retn
.text:0040222C ; ---------------------------------------------------------------------------
.text:0040222C
.text:0040222C loc_40222C: ; CODE XREF: .text:00402210j
.text:0040222C push ebp
.text:0040222D push 0FFFFFFF4h
.text:0040222F push esi
.text:00402230 call sub_412CE0
.text:00402235 mov edi, eax
.text:00402237 push 0FFFFFFF5h
.text:00402239 push esi
.text:0040223A xor edi, 5
.text:0040223D call sub_412CE0
.text:00402242 mov ebx, eax
.text:00402244 push 0FFFFFFF6h
.text:00402246 push esi
.text:00402247 xor ebx, 12h
.text:0040224A call sub_412CE0
.text:0040224F mov ebp, eax
.text:00402251 push 0FFFFFFF7h
.text:00402253 push esi
.text:00402254 xor ebp, 0Ah
.text:00402257 call sub_412CE0
.text:0040225C xor eax, 29h
.text:0040225F push 0FFFFFFF8h
.text:00402261 push esi
.text:00402262 mov [esp+58h], eax
.text:00402266 call sub_412CE0
.text:0040226B xor eax, 42h
.text:0040226E push 0FFFFFFF9h
.text:00402270 push esi
.text:00402271 mov [esp+48h], eax
.text:00402275 call sub_412CE0
.text:0040227A xor eax, 41h
.text:0040227D push 0FFFFFFFAh
.text:0040227F push esi
.text:00402280 mov [esp+60h], eax
.text:00402284 call sub_412CE0
.text:00402289 xor eax, 75h
.text:0040228C push 0FFFFFFFBh
.text:0040228E push esi
.text:0040228F mov [esp+60h], eax
.text:00402293 call sub_412CE0
.text:00402298 add esp, 40h
.text:0040229B xor eax, 61h
.text:0040229E push 0FFFFFFFCh
.text:004022A0 push esi
.text:004022A1 mov [esp+18h], eax
.text:004022A5 call sub_412CE0
.text:004022AA xor eax, 35h
.text:004022AD push 0FFFFFFFDh
.text:004022AF push esi
.text:004022B0 mov [esp+24h], eax
.text:004022B4 call sub_412CE0
.text:004022B9 xor eax, 83h
.text:004022BE push 0FFFFFFFEh
.text:004022C0 push esi
.text:004022C1 mov [esp+34h], eax
.text:004022C5 call sub_412CE0
.text:004022CA xor eax, 55h
.text:004022CD push 0FFFFFFFFh
.text:004022CF push esi
.text:004022D0 mov [esp+44h], eax
.text:004022D4 call sub_412CE0
.text:004022D9 xor eax, 94h
.text:004022DE push 0FFFFFFF3h
.text:004022E0 push esi
.text:004022E1 mov [esp+54h], eax
.text:004022E5 call sub_412810
.text:004022EA push esi
.text:004022EB call sub_414370
.text:004022F0 add esp, 2Ch
.text:004022F3 cmp edi, 18h
.text:004022F6 jnz short loc_40234C
.text:004022F8 cmp ebx, 16h
.text:004022FB jnz short loc_40234C
.text:004022FD cmp ebp, 1Eh
.text:00402300 jnz short loc_40234C
.text:00402302 cmp dword ptr [esp+30h], 2Fh
.text:00402307 jnz short loc_40234C
.text:00402309 cmp dword ptr [esp+18h], 48h
.text:0040230E jnz short loc_40234C
.text:00402310 cmp dword ptr [esp+28h], 11h
.text:00402315 jnz short loc_40234C
.text:00402317 cmp dword ptr [esp+20h], 21h
.text:0040231C jnz short loc_40234C
.text:0040231E cmp dword ptr [esp+10h], 37h
.text:00402323 jnz short loc_40234C
.text:00402325 cmp dword ptr [esp+14h], 33h
.text:0040232A jnz short loc_40234C
.text:0040232C cmp dword ptr [esp+1Ch], 86h
.text:00402334 jnz short loc_40234C
.text:00402336 cmp dword ptr [esp+24h], 52h
.text:0040233B jnz short loc_40234C
.text:0040233D cmp dword ptr [esp+2Ch], 94h
.text:00402345 jnz short loc_40234C
.text:00402347 lea eax, [edi-17h]
.text:0040234A jmp short loc_40234E
.text:0040234C ; ---------------------------------------------------------------------------
.text:0040234C
.text:0040234C loc_40234C: ; CODE XREF: .text:004022F6j
.text:0040234C ; .text:004022FBj ...
.text:0040234C xor eax, eax
.text:0040234E
.text:0040234E loc_40234E: ; CODE XREF: .text:0040234Aj
.text:0040234E mov ecx, [esp+2ACh]
.text:00402355 pop ebp
.text:00402356 pop edi
.text:00402357 pop esi
.text:00402358 pop ebx
.text:00402359 xor ecx, esp
.text:0040235B call sub_4023E0
.text:00402360 add esp, 2A0h
.text:00402366 retn

再看跳转目标处loc_40222C的代码。先是12个sub_412CE0调用,再接着是12个比较,如果不相同则返回0。
根据此处代码,我们可以推测,输入可能是12个,输出肯定是12个,12个比较是最终的校验。
动态验证下,运行程序,od附加,在40103D下断并运行,程序中随便输入回车。成功断下。最后获得栈区的写入数据(前面似乎是luajit的头,也加上了)。

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
0019FA0C 1B 4C 4A 02 J
0019FA1C 02 3B 00 02 07 00 03 00 09 36 02 00 00 39 02 01 ;....6..9
0019FA2C 02 36 03 00 00 39 03 02 03 12 04 00 00 12 05 01 6..9..
0019FA3C 00 12 06 01 00 42 03 04 00 43 02 00 00 08 73 75 ..B.C..su
0019FA4C 62 09 62 79 74 65 0B 73 74 72 69 6E 67 F3 03 00 b.byte string?.
0019FA5C 01 19 00 05 01 75 36 01 00 00 39 01 01 01 12 02 .u6..9
0019FA6C 00 00 42 01 02 02 08 01 00 00 58 01 02 80 29 01 ..B..X€)
0019FA7C 00 00 4C 01 02 00 36 01 02 00 39 01 03 01 36 02 ..L.6.96
0019FA8C 04 00 12 03 00 00 29 04 01 00 42 02 03 02 29 03 ...).B)
0019FA9C 70 00 42 01 03 02 36 02 02 00 39 02 03 02 36 03 p.B6.96
0019FAAC 04 00 12 04 00 00 29 05 02 00 42 03 03 02 29 04 ...).B)
0019FABC 65 00 42 02 03 02 36 03 02 00 39 03 03 03 36 04 e.B6.96
0019FACC 04 00 12 05 00 00 29 06 03 00 42 04 03 02 29 05 ...).B)
0019FADC 64 00 42 03 03 02 36 04 02 00 39 04 03 04 36 05 d.B6.96
0019FAEC 04 00 12 06 00 00 29 07 04 00 42 05 03 02 29 06 ...).B)
0019FAFC 69 00 42 04 03 02 36 05 02 00 39 05 03 05 36 06 i.B6.96
0019FB0C 04 00 12 07 00 00 29 08 05 00 42 06 03 02 29 07 ...).B)
0019FB1C 79 00 42 05 03 02 36 06 02 00 39 06 03 06 36 07 y.B6.96
0019FB2C 04 00 12 08 00 00 29 09 06 00 42 07 03 02 29 08 ...)..B)
0019FB3C 31 00 42 06 03 02 36 07 02 00 39 07 03 07 36 08 1.B6.96
0019FB4C 04 00 12 09 00 00 29 0A 07 00 42 08 03 02 29 09 ....)..B).
0019FB5C 32 00 42 07 03 02 36 08 02 00 39 08 03 08 36 09 2.B6.96.
0019FB6C 04 00 12 0A 00 00 29 0B 08 00 42 09 03 02 29 0A ....) .B.).
0019FB7C 33 00 42 08 03 02 36 09 02 00 39 09 03 09 36 0A 3.B6..9..6.
0019FB8C 04 00 12 0B 00 00 29 0C 09 00 42 0A 03 02 29 0B . ..)...B.)
0019FB9C 34 00 42 09 03 02 36 0A 02 00 39 0A 03 0A 36 0B 4.B.6..9..6
0019FBAC 04 00 12 0C 00 00 29 0D 0A 00 42 0B 03 02 29 0C ....)...B ).
0019FBBC 35 00 42 0A 03 02 36 0B 02 00 39 0B 03 0B 36 0C 5.B.6 .9  6.
0019FBCC 04 00 12 0D 00 00 29 0E 0B 00 42 0C 03 02 29 0D ....) .B.).
0019FBDC 36 00 42 0B 03 02 36 0C 02 00 39 0C 03 0C 36 0D 6.B 6..9..6.
0019FBEC 04 00 12 0E 00 00 29 0F 0C 00 42 0D 03 02 29 0E ...)..B.)
0019FBFC 37 00 42 0C 03 02 12 0D 01 00 12 0E 02 00 12 0F 7.B....
0019FC0C 03 00 12 10 04 00 12 11 05 00 12 12 06 00 12 13 ....
0019FC1C 07 00 12 14 08 00 12 15 09 00 12 16 0A 00 12 17 ......
0019FC2C 0B 00 12 18 0C 00 4A 0D 0D 00 07 62 79 09 62 78 ...J...by.bx
0019FC3C 6F 72 08 62 69 74 08 6C 65 6E 0B 73 74 72 69 6E orbitlen strin
0019FC4C 67 18 3D 03 00 02 00 06 00 08 36 00 00 00 27 01 g=...6...'
0019FC5C 01 00 42 00 02 01 33 00 02 00 37 00 03 00 33 00 .B.3..7..3.
0019FC6C 04 00 37 00 05 00 4B 00 01 00 09 6D 61 69 6E 00 .7..K...main.
0019FC7C 07 62 79 00 08 62 69 74 0C 72 65 71 75 69 72 65 by.bit.require
0019FC8C 00 .

继续往下走,最后到了校验的地方,全部改标志位通过,最后程序打印出成功信息。看来推测是正确的。

更新了下luajit,使用luajit-decomp反解数据,竟然解不出。
我想想觉得luajit太明显了,是不是坑,毕竟函数都没跟,不知道发生了什么,有可能只是luac的脚本。于是更改脚本数据,用luadec解,也是解不出。

后来得到高人指点:应该是luajit,看看版本对不对。我边看边想:不应该啊,我刚更新的,然后就傻眼了:版本不对。我又上了luajit的官网下载页,原来第一个并不是最新版本,只是稳定版本,当时着急没看版本号,就直接下了第一个。

重新下载编译,用luajit-decomp反解,得到:

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
function someFunc0(INPUT_VAR_0_,INPUT_VAR_1_)
local var_0_4 = INPUT_VAR_0_
local var_0_5 = INPUT_VAR_1_
local var_0_6 = INPUT_VAR_1_
string.byte( string.sub(var_0_4, var_0_5, var_0_6) )
end
function someFunc1(INPUT_VAR_0_)
local var_1_2 = INPUT_VAR_0_
local var_1_1 = string.len(var_1_2)
if var_1_1 ~= 0 then
--jump to 0009 (if previous if statement is false)
return 0
--location 0009
local var_1_3 = INPUT_VAR_0_
var_1_2 = by(var_1_3, 1 )
var_1_1 = bit.bxor(var_1_2, 112 )
local var_1_4 = INPUT_VAR_0_
var_1_3 = by(var_1_4, 2 )
var_1_2 = bit.bxor(var_1_3, 101 )
local var_1_5 = INPUT_VAR_0_
var_1_4 = by(var_1_5, 3 )
var_1_3 = bit.bxor(var_1_4, 100 )
local var_1_6 = INPUT_VAR_0_
var_1_5 = by(var_1_6, 4 )
var_1_4 = bit.bxor(var_1_5, 105 )
local var_1_7 = INPUT_VAR_0_
var_1_6 = by(var_1_7, 5 )
var_1_5 = bit.bxor(var_1_6, 121 )
local var_1_8 = INPUT_VAR_0_
var_1_7 = by(var_1_8, 6 )
var_1_6 = bit.bxor(var_1_7, 49 )
local var_1_9 = INPUT_VAR_0_
var_1_8 = by(var_1_9, 7 )
var_1_7 = bit.bxor(var_1_8, 50 )
local var_1_10 = INPUT_VAR_0_
var_1_9 = by(var_1_10, 8 )
var_1_8 = bit.bxor(var_1_9, 51 )
local var_1_11 = INPUT_VAR_0_
var_1_10 = by(var_1_11, 9 )
var_1_9 = bit.bxor(var_1_10, 52 )
local var_1_12 = INPUT_VAR_0_
var_1_11 = by(var_1_12, 10 )
var_1_10 = bit.bxor(var_1_11, 53 )
local var_1_13 = INPUT_VAR_0_
var_1_12 = by(var_1_13, 11 )
var_1_11 = bit.bxor(var_1_12, 54 )
local var_1_14 = INPUT_VAR_0_
var_1_13 = by(var_1_14, 12 )
var_1_12 = bit.bxor(var_1_13, 55 )
var_1_13 = var_1_1
var_1_14 = var_1_2
local var_1_15 = var_1_3
local var_1_16 = var_1_4
local var_1_17 = var_1_5
local var_1_18 = var_1_6
local var_1_19 = var_1_7
local var_1_20 = var_1_8
local var_1_21 = var_1_9
local var_1_22 = var_1_10
local var_1_23 = var_1_11
local var_1_24 = var_1_12
return var_1_13, var_1_14, var_1_15, var_1_16, var_1_17, var_1_18, var_1_19, var_1_20, var_1_21, var_1_22, var_1_23, var_1_24
end
function someFunc2()
require("bit")
local randomFunction0 = function() end -- starts at test.luac:0
by = randomFunction0
local randomFunction1 = function() end -- starts at test.luac:0
main = randomFunction1
return
end

脚本比较简单,也就没有整理成lua本来的样子。也就是输入的12字节分别与某常量异或,得到12字节的输出。
sub_412CE0应该就是取输出,取了12次,取出的输出也分别与某常量异或,最后校验。

最后来个简单py脚本计算:

1
2
3
4
5
6
7
l1 = [0x18,0x16,0x1e,0x2f,0x48,0x11,0x21,0x37,0x33,0x86,0x52,0x94]
l2 = [112,101,100,105,121,49,50,51,52,53,54,55]
l3 = [0x05,0x12,0x0a,0x29,0x42,0x41,0x75,0x61,0x35,0x83,0x55,0x94]
s = ''
for i in range(12):
s += chr(l1[i]^l2[i]^l3[i])
print 'The key is:'+s

最终结果为maposafe2017

说明:本文首发于看雪论坛。

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
,