看雪CTF第十五题

时间:2023-03-10 05:13:46
看雪CTF第十五题

1、直接运行起来,再用OD附加

 在此处luajit加载并调用main函数

004021C7    E8 64FE0000     call CrackMe.                    ; luaL_newstate
004021CC 8BF0 mov esi,eax
004021CE push esi
004021CF E8 9C000100 call CrackMe. ; luaL_openlibs(lua_State *L)
004021D4 push ebx
004021D5 push 0x275
004021DA 8D4424 3C lea eax,dword ptr ss:[esp+0x3C]
004021DE push eax
004021DF push esi
004021E0 E8 5B040100 call CrackMe. ; int luaL_loadbuffer(lua_State*L,const char*buff,size_t size,const char*name
004021E5 push ebx
004021E6 push esi
004021E7 E8 C41B0100 call CrackMe.00413DB0
004021EC 883E4800 push CrackMe.00483E88 ; ASCII "main"
004021F1 EED8FFFF push -0x2712
004021F6 push esi
004021F7 E8 call CrackMe. ; void (lua_getfield) (lua_State *L, int idx, const char *k);
004021FC push edi
004021FD push esi
004021FE E8 DD0E0100 call CrackMe.004130E0 ; lua_pushstring
6A push 0x1
push esi
E8 A51B0100 call CrackMe.00413DB0 ; lua_call

2、在luaL_loadbuffer出下断开dump下luajit的bytecode

 使用luajit-decomp进行反编译,注意:可以此处是LuaJIT-2.1.0-beta3,需编译替换luajit-decomp中对应的文件

在IDA中字符串列表中搜索lua可以知道lua和luajit的版本。注意:由于有反调试,先运行程序在用OD附加然后dump

Address        Length   Type String
------- ------ ---- ------
.text:00471CFC 0000002E C PANIC: unprotected error in call to Lua API (
.text:00473EA0 C 'module' not called from a Lua function
.text:00473F40 C .\\?.lua;!\\lua\\?.lua;!\\lua\\?\\init.lua;
.text: C Lua function expected
.text: C LuaJIT 2.1.-beta3
.text:00473D1C 0000000D C luaJIT_BC_%s
.text: 0000000C C lua_debug>
.text:00473D2C 0000000B C luaopen_%s
.text:00473F34 0000000A C LUA_CPATH
.text:00473F74 0000000A C LUA_NOENV
.text:00473F68 C LUA_PATH
.text: C Lua 5.1

dump下luajit的bytecode:

-- BYTECODE -- lua.bytes:-
function someFunc0()
var_0_4 = INPUT_VAR_0_
var_0_5 = INPUT_VAR_1_
var_0_6 = INPUT_VAR_1_
var_0_3 = string.sub(var_0_4, var_0_5, var_0_6) --var_0_3 REPLACE-REPLACE
string.byte(var_0_3)
end -- BYTECODE -- lua.bytes:-
function someFunc1()
var_1_2 = INPUT_VAR_0_
var_1_1 = string.len(var_1_2)
if var_1_1 ~= then
--jump to (if previous if statement is false) -- JMP-JMP
var_1_1 = --var_1_1 NUMBER-NUMBER
return var_1_1
--location -- LOCATION-LOCATION
var_1_3 = INPUT_VAR_0_
var_1_4 = --var_1_4 NUMBER-NUMBER
var_1_2 = by(var_1_3, var_1_4)
var_1_3 = --var_1_3 NUMBER-NUMBER
var_1_1 = bit.bxor(var_1_2, var_1_3)
var_1_4 = INPUT_VAR_0_
var_1_5 = --var_1_5 NUMBER-NUMBER
var_1_3 = by(var_1_4, var_1_5)
var_1_4 = --var_1_4 NUMBER-NUMBER
var_1_2 = bit.bxor(var_1_3, var_1_4)
var_1_5 = INPUT_VAR_0_
var_1_6 = --var_1_6 NUMBER-NUMBER
var_1_4 = by(var_1_5, var_1_6)
var_1_5 = --var_1_5 NUMBER-NUMBER
var_1_3 = bit.bxor(var_1_4, var_1_5)
var_1_6 = INPUT_VAR_0_
var_1_7 = --var_1_7 NUMBER-NUMBER
var_1_5 = by(var_1_6, var_1_7)
var_1_6 = --var_1_6 NUMBER-NUMBER
var_1_4 = bit.bxor(var_1_5, var_1_6)
var_1_7 = INPUT_VAR_0_
var_1_8 = --var_1_8 NUMBER-NUMBER
var_1_6 = by(var_1_7, var_1_8)
var_1_7 = --var_1_7 NUMBER-NUMBER
var_1_5 = bit.bxor(var_1_6, var_1_7)
var_1_8 = INPUT_VAR_0_
var_1_9 = --var_1_9 NUMBER-NUMBER
var_1_7 = by(var_1_8, var_1_9)
var_1_8 = --var_1_8 NUMBER-NUMBER
var_1_6 = bit.bxor(var_1_7, var_1_8)
var_1_9 = INPUT_VAR_0_
var_1_10 = --var_1_10 NUMBER-NUMBER
var_1_8 = by(var_1_9, var_1_10)
var_1_9 = --var_1_9 NUMBER-NUMBER
var_1_7 = bit.bxor(var_1_8, var_1_9)
var_1_10 = INPUT_VAR_0_
var_1_11 = --var_1_11 NUMBER-NUMBER
var_1_9 = by(var_1_10, var_1_11)
var_1_10 = --var_1_10 NUMBER-NUMBER
var_1_8 = bit.bxor(var_1_9, var_1_10)
var_1_11 = INPUT_VAR_0_
var_1_12 = --var_1_12 NUMBER-NUMBER
var_1_10 = by(var_1_11, var_1_12)
var_1_11 = --var_1_11 NUMBER-NUMBER
var_1_9 = bit.bxor(var_1_10, var_1_11)
var_1_12 = INPUT_VAR_0_
var_1_13 = --var_1_13 NUMBER-NUMBER
var_1_11 = by(var_1_12, var_1_13)
var_1_12 = --var_1_12 NUMBER-NUMBER
var_1_10 = bit.bxor(var_1_11, var_1_12)
var_1_13 = INPUT_VAR_0_
var_1_14 = --var_1_14 NUMBER-NUMBER
var_1_12 = by(var_1_13, var_1_14)
var_1_13 = --var_1_13 NUMBER-NUMBER
var_1_11 = bit.bxor(var_1_12, var_1_13)
var_1_14 = INPUT_VAR_0_
var_1_15 = --var_1_15 NUMBER-NUMBER
var_1_13 = by(var_1_14, var_1_15)
var_1_14 = --var_1_14 NUMBER-NUMBER
var_1_12 = bit.bxor(var_1_13, var_1_14)
var_1_13 = var_1_1
var_1_14 = var_1_2
var_1_15 = var_1_3
var_1_16 = var_1_4
var_1_17 = var_1_5
var_1_18 = var_1_6
var_1_19 = var_1_7
var_1_20 = var_1_8
var_1_21 = var_1_9
var_1_22 = var_1_10
var_1_23 = var_1_11
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 -- BYTECODE -- lua.bytes:-
function someFunc2()
var_2_1 = "bit" --var_2_1 STRING-STRING
require(var_2_1)
local randomFunction0 = function() end -- starts at lua.bytes:
by = randomFunction0
local randomFunction1 = function() end -- starts at lua.bytes:
main = randomFunction1
return
end

可以看出

  someFunc0函数截取字符串指定位置的字符 并转换成byte

    someFunc1函数对字符串调用someFunc0函数每个字符截取 并 xor

someFunc2中可以看出 by = randomFunction0 应该就是 by = someFunc0,  main = randomFunction1 应该就是 main = someFunc1

3、调用完lua中的main函数后,从lua栈中依次取出每个字符加密后的byte, 然后再次进行 xor

0040222C                  push ebp
0040222D 6A F4 push -0xC
0040222F push esi
E8 AB0A0100 call CrackMe.00412CE0 ; lua_Integer lua_tointegerx (lua_State *L, int idx, int *isnum);
8BF8 mov edi,eax
6A F5 push -0xB
push esi
0040223A 83F7 xor edi,0x5
0040223D E8 9E0A0100 call CrackMe.00412CE0
8BD8 mov ebx,eax
6A F6 push -0xA
push esi
83F3 xor ebx,0x12
0040224A E8 910A0100 call CrackMe.00412CE0
0040224F 8BE8 mov ebp,eax
6A F7 push -0x9
push esi
83F5 0A xor ebp,0xA
E8 840A0100 call CrackMe.00412CE0
0040225C 83F0 xor eax,0x29
0040225F 6A F8 push -0x8
push esi
mov dword ptr ss:[esp+0x58],eax
E8 750A0100 call CrackMe.00412CE0
0040226B 83F0 xor eax,0x42
0040226E 6A F9 push -0x7
push esi
mov dword ptr ss:[esp+0x48],eax
E8 660A0100 call CrackMe.00412CE0
0040227A 83F0 xor eax,0x41
0040227D 6A FA push -0x6
0040227F push esi
mov dword ptr ss:[esp+0x60],eax
E8 570A0100 call CrackMe.00412CE0
83F0 xor eax,0x75
0040228C 6A FB push -0x5
0040228E push esi
0040228F mov dword ptr ss:[esp+0x60],eax
E8 480A0100 call CrackMe.00412CE0
83C4 add esp,0x40
0040229B 83F0 xor eax,0x61
0040229E 6A FC push -0x4
004022A0 push esi
004022A1 mov dword ptr ss:[esp+0x18],eax
004022A5 E8 360A0100 call CrackMe.00412CE0
004022AA 83F0 xor eax,0x35
004022AD 6A FD push -0x3
004022AF push esi
004022B0 mov dword ptr ss:[esp+0x24],eax
004022B4 E8 270A0100 call CrackMe.00412CE0
004022B9 xor eax,0x83
004022BE 6A FE push -0x2
004022C0 push esi
004022C1 mov dword ptr ss:[esp+0x34],eax
004022C5 E8 160A0100 call CrackMe.00412CE0
004022CA 83F0 xor eax,0x55
004022CD 6A FF push -0x1
004022CF push esi
004022D0 mov dword ptr ss:[esp+0x44],eax
004022D4 E8 070A0100 call CrackMe.00412CE0
004022D9 xor eax,0x94
004022DE 6A F3 push -0xD
004022E0 push esi
004022E1 mov dword ptr ss:[esp+0x54],eax
004022E5 E8 call CrackMe.
004022EA push esi
004022EB E8 call CrackMe. ; lua_close

4、最后每个byte和固定的值进行比较

004022F3    83FF          cmp edi,0x18
004022F6 jnz short CrackMe.0040234C
004022F8 83FB cmp ebx,0x16
004022FB 4F jnz short CrackMe.0040234C
004022FD 83FD 1E cmp ebp,0x1E
4A jnz short CrackMe.0040234C
837C24 2F cmp dword ptr ss:[esp+0x30],0x2F
jnz short CrackMe.0040234C
837C24 cmp dword ptr ss:[esp+0x18],0x48
0040230E 3C jnz short CrackMe.0040234C
837C24 cmp dword ptr ss:[esp+0x28],0x11
jnz short CrackMe.0040234C
837C24 cmp dword ptr ss:[esp+0x20],0x21
0040231C 2E jnz short CrackMe.0040234C
0040231E 837C24 cmp dword ptr ss:[esp+0x10],0x37
jnz short CrackMe.0040234C
837C24 cmp dword ptr ss:[esp+0x14],0x33
0040232A jnz short CrackMe.0040234C
0040232C 817C24 1C >cmp dword ptr ss:[esp+0x1C],0x86
jnz short CrackMe.0040234C
837C24 cmp dword ptr ss:[esp+0x24],0x52
0040233B 0F jnz short CrackMe.0040234C
0040233D 817C24 2C >cmp dword ptr ss:[esp+0x2C],0x94
jnz short CrackMe.0040234C

5、可以提取3处xor的值

lua中main函数对每个字符xor的值

    [112,101,100,105,121,49,50,51,52,53,54,55]

0040222C   处对lua输出的每个值进行异或

    [0x5,0x12,0xa,0x29,0x42,0x41,0x75,0x61,0x35,0x83,0x55,0x94]

004022F3    处对上面两次异或后的结果和下面的值进行比较

    [0x18,0x16,0x1e,0x2f,0x48,0x11,0x21,0x37,0x33,0x86,0x52,0x94]

6、用python进行解码

result = [0x18,0x16,0x1E,0x2F,0x48,0x11,0x21,0x37,0x33,0x86,0x52,0x94]
luaxor = [,,,,,,,,,,,]
codexor = [0x5,0x12,0xa,0x29,0x42,0x41,0x75,0x61,0x35,0x83,0x55,0x94] code = ''
for i in range(len(result)):
code += chr(result[i]^luaxor[i]^codexor[i]) print code

结果:maposafe2017