【wp】2020XCTF_逆向


前幾天的XCTF最后一場終於打完了,三場比賽下來對逆向部分的大概感覺是從第一場的啥都不會做到后來的終於能有參與度了TvT,至少后兩場的題目都是pc逆向,雖然特殊架構但好歹能做(tcl

本文是三場XCTF所有逆向題目的wp+復現整理。賽中拿到了7/10的flag,很多是跟着隊里的大佬做出來的(tqltql),這邊就試着獨立復現一下,至少打完應該長長記性(

官方wp出了,借助wp提供的思路復現填坑√。(9/10)

1220那場的apk逆向還沒復現完,因為博客要更新點配置所以就先放上來了qvq。

比賽官網:XCTF高校網絡安全專題挑戰賽

官方wp:

  1. XCTF高校網絡安全專題挑戰賽-華為雲專場 官方Writeup - XCTF社區
  2. XCTF高校網絡安全專題挑戰賽-鯤鵬計算專場 官方Writeup - XCTF社區
  3. XCTF高校網絡安全專題挑戰賽-HarmonyOS和HMS專場 官方Writeup - XCTF社區

[12.20] 華為雲專場

Weird_lua

根據官方wp,說是改了loadbyte和opcode順序。這里假裝沒看過wp,看能不能分析出怎么看出來是這里魔改的。

首先看附件是luacheck_license_out.lua,可以用file分別看一下

image-20210225152906252

image-20210225152915828

單獨運行./lua,可以看到版本號是5.3.3

image-20210225160651288

這里運行目標文件的方式是./lua ./check_license_out.lua

image-20210225153920925

感覺這個.lua文件實際可能是.luac文件,即lua字節碼文件。

於是先隨便寫一個lua源碼(除了不知名check以外,大致對目標文件check_license_out.lua進行復刻),用v5.3.3的lua(可以在lua官網Lua: download下載)編譯成字節碼,看看正常版和魔改版兩個文件的區別。

-- test.lua
function check(s)
    local len=#s
    if len==42 then
        return "Check!"
    else 
        return "length error"
    end 
end 

io.write("input: ")
str=io.read()
print(check(str))

lua test.lua運行:(注意這里是用正常的lua解釋器而不是題目給的)

image-20210225154050280

然后用luac -o test.luac test.lua得到test.luac字節碼文件,然后把check_license_out.lua(上)和test.luac(下)用010Editor打開對比十六進制碼。

image-20210225154521444

image-20210225154542779

可以看到,除了最開始的一字節為"1B"和"1C"以外,其余文件頭中不同的字節都相差較大,感覺是某個特定的換算關系,位置看起來卻沒什么規律。

定位到lua-5.3.3/src文件夾中,有兩個執行luac dump相關操作的頭文件,分別是ldump.h(save precompiled Lua chunks)和lundump.h(load precompiled Lua chunks),這里猜測是用於load的lundump.c被魔改。

image-20210302235800922

從這里(類似於主函數的函數)開始是整個的load步驟,可以看到上面對比中第一個差距比較大的字節是在0004h的位置,應該是在checkHeader部分,跟進這個checkHeader()函數。

image-20210303000036934

237行是check文件頭的"\x1bLua"部分(題目開頭0x1c這個很容易看出就是硬改),而下面開始是check LUAC_VERSION即lua的版本號,本來應該是0x53,但是題目文件對應位置是0xAC,而且往后對照可以看到,不同字節部分都用了LoadByte()函數,猜測是在加載字節的時候有魔改,至於具體怎么魔改那可能得靠直覺了吧(。),大概能靠猜?(或者看lua文件的反編譯= =

在官方wp里,魔改的具體信息已經給了:

static lu_byte LoadByte (LoadState *S) {
    lu_byte x;
    LoadVar(S, x);
    //x ^= 0xff; 此行為魔改部分
    return x;
}

跟着官方wp走不通的思路

本部分可跳過,最后因為剛剛接觸lua,對這個語言不熟悉,卡在創造不出帖子里提到的 “用於 dump 的函數,盡量覆蓋所有的指令操作,否則只能提取出已有的指令” 的lua函數,帖子里的test無法在5.3版本的lua中提取出所有字節碼,需要自己動手創造(。

因為這里LoadByte的魔改會影響到下一步的opcode順序還原,所以要先把這些錯誤字節xor回去。

又因為LoadByte的更改只影響到部分字節,我們並不清楚具體哪些字節遭到了禍害,所以想到了一個比較笨的方法就是重新編譯一個lua解釋器,這樣我們就有了opcode順序魔改前的解釋器和魔改后的解釋器(題目所給的)。

(如果有師傅有更好的辦法求告知><)

將之前的lua-5.3.3文件夾復制到一個新路徑,cd到文件夾中,執行make clean將文件中上一次的編譯臨時文件清空。其實理論上說不復制出來也應該不會影響到原來的lua,但為了保險起見還是這么干吧。

然后把lua-5.3.3/src/ldump.c(注意文件名!!)的52-56行即DumpByte()函數部分改為:

static void DumpByte (int y, DumpState *D) {
  lu_byte x = (lu_byte)y;
  x^=0xff;//增加此行
  DumpVar(x, D);
}

保存,退回到lua-5.3.3根目錄下,在WSL中用make linux編譯。

至於為什么是改ldump.c而不是改官方wp指定魔改的lundump.c,因為ldump.c的作用是保存字節碼,而lundump.c的作用是加載字節碼。這里參照[原創]用 Lua 簡單還原 OpCode 順序文中的方法進行還原。

帖子里的lua版本為v5.1.5,對這里v5.3.3並不能直接用(opcode有增加+有些5.3中新增的指令),需要對源碼(Check_OpCode/check_opcode.lua)做一些修改,修改后的文件如下。

-- check_opcode.lua

local bit = require('bit')
local test = require('test')

-- 加載用正常 lua 的 dump 文件
local fp = io.open("test.luac","rb")
local ori_data = fp:read("*all")
fp:close()

-- print('data len '.. #data)
-- print('ori_data len ' .. #ori_data)

local ori_op_name = {
    "MOVE",
    "LOADK",
    "LOADKX",
    "LOADBOOL",
    "LOADNIL",
    "GETUPVAL",
    "GETTABUP",
    "GETTABLE",
    "SETTABUP",
    "SETUPVAL",
    "SETTABLE",
    "NEWTABLE",
    "SELF",
    "ADD",
    "SUB",
    "MUL",
    "MOD",
    "POW",
    "DIV",
    "IDIV",
    "BAND",
    "BOR",
    "BXOR",
    "SHL",
    "SHR",
    "UNM",
    "BNOT",
    "NOT",
    "LEN",
    "CONCAT",
    "JMP",
    "EQ",
    "LT",
    "LE",
    "TEST",
    "TESTSET",
    "CALL",
    "TAILCALL",
    "RETURN",
    "FORLOOP",
    "FORPREP",
    "TFORCALL",
    "TFORLOOP",
    "SETLIST",
    "CLOSURE",
    "VARARG",
    "EXTRAARG",
}
local data = string.dump(test)	-- dump
local new_op = {}
-- 用目標 lua 和正常 lua 的 dump 數據對比
for i = 2, #data do
	local by_ori = string.byte(ori_data,i)
	local by_new = string.byte(data,i)
	if by_ori ~= by_new then
        -- 字節碼被xor 0xff處理
        local op_name = ori_op_name[by_ori + 1]
        local op_idx = by_new
        -- if by_ori>47 then
        --     op_name = ori_op_name[(by_ori~0xff) + 1]
        --     op_idx = (by_new~0xff)
        -- end
        if op_name~=nil then
            if new_op[op_name]~=nil and new_op[op_name]~=op_idx then
                print(op_name,new_op[op_name])
            end
		    new_op[op_name] = op_idx
        end
	end
end

print("old \t new \t name")
local cnt=0
for idx, op_name in pairs(ori_op_name) do
	local tmp = ''
	if new_op[op_name] ~= nil then
		tmp = new_op[op_name]
        cnt=cnt+1
	end
	print((idx - 1) .. "\t" .. tmp .. "\t" .. op_name )
end
print(cnt)

將題目附件的lua重命名為lua_problem並復制到Check_OpCode文件夾中,同時將剛剛xor了字節的lua-5.3.3/src/lua也復制過來。

image-20210303223220203

P.S. 由於帖子中給出的test.lua與前文中最開始用到的lua文件重名,故以下test.lua均代表為帖子中的lua文件,前文中的test.lua不再被引用。

依次在WSL中運行:

./lua dump_test_luac.lua
./lua_problem check_opcode.lua

(然后發現寫不出能覆蓋所有字節碼的test(),遂放棄該思路。

自創思路

我們知道在lua源碼里,opcode是按順序存放在lopcodes.hlopcodes.c里的,故順序魔改其實可以追蹤lua解釋器在ida中的反編譯即可。(這里的lua就是題目給的那個。

源碼中的opcode處:

image-20210304125916447

image-20210304125958675

lua加載進ida中,用shift+F12提取字符串,並用ctrl+F搜索“MOVE”

image-20210304130048988

雙擊,按x找到交叉引用

image-20210304130133374

image-20210304130146457

這里就是魔改后的opcode順序!

那么我們現在就掌握了所有魔改的信息,開始恢復還原。

這里用到的是著名的lua反匯編/反編譯項目viruscamp/luadec: Lua Decompiler for lua 5.1 , 5.2 and 5.3,由於lua解釋器被魔改(xor 0xff和opcode順序),所以我們需要在原安裝步驟的基礎上做些調整。

git clone https://github.com/viruscamp/luadec
cd luadec
git submodule update --init lua-5.3
cd lua-5.3
###### 此處在make之前需要調整源碼!
make linux
cd ../luadec
make LUAVER=5.3

調整源碼如下:

/* luadec/lua-5.3/src/lopcodes.h 原line165-232 */
typedef enum {
OP_MOVE,
OP_LOADK,
OP_LOADKX,
OP_LOADBOOL,
OP_LOADNIL,
OP_GETUPVAL,
OP_LT,
OP_RETURN,
OP_GETTABLE,
OP_TESTSET,
OP_SELF,
OP_NEWTABLE,
OP_LE,
OP_ADD,
OP_SUB,
OP_MUL,
OP_MOD,
OP_POW,
OP_DIV,
OP_IDIV,
OP_BAND,
OP_BOR,
OP_BXOR,
OP_SHL,
OP_SHR,
OP_UNM,
OP_BNOT,
OP_NOT,
OP_LEN,
OP_GETTABUP,
OP_JMP,
OP_TEST,
OP_SETTABUP,
OP_TFORCALL,
OP_FORPREP,
OP_EXTRAARG,
OP_CALL,
OP_FORLOOP,
OP_SETLIST,
OP_CONCAT,
OP_TAILCALL,
OP_TFORLOOP,
OP_VARARG,
OP_SETTABLE,
OP_SETUPVAL,
OP_CLOSURE,
OP_EQ,
} OpCode;
/* luadec/lua-5.3/src/lopcodes.c 原line20-69 */
LUAI_DDEF const char *const luaP_opnames[NUM_OPCODES+1] = {
  "MOVE",
  "LOADK",
  "LOADKX",
  "LOADBOOL",
  "LOADNIL",
  "GETUPVAL",
  "LT",
  "RETURN",
  "GETTABLE",
  "TESTSET",
  "SELF",
  "NEWTABLE",
  "LE",
  "ADD",
  "SUB",
  "MUL",
  "MOD",
  "POW",
  "DIV",
  "IDIV",
  "BAND",
  "BOR",
  "BXOR",
  "SHL",
  "SHR",
  "UNM",
  "BNOT",
  "NOT",
  "LEN",
  "GETTABUP",
  "JMP",
  "TEST",
  "SETTABUP",
  "TFORCALL",
  "FORPREP",
  "EXTRAARG",
  "CALL",
  "FORLOOP",
  "SETLIST",
  "CONCAT",
  "TAILCALL",
  "TFORLOOP",
  "VARARG",
  "SETTABLE",
  "SETUPVAL",
  "CLOSURE",
  "EQ",
  NULL
};
/* luadec/lua-5.3/src/lundump.c 原line60-64 */
static lu_byte LoadByte (LoadState *S) {
  lu_byte x;
  LoadVar(S, x);
  x^=0xff;
  return x;
}
/* luadec/lua-5.3/src/Makefile 原line9 */
CC= gcc -std=gnu99 -m32
//因為題目所給的lua解釋器為32位
/* luadec/luadec/Makefile 原line16 */
CC= gcc -m32
//同上

修改完源碼后接着make。

走完流程后把check_license_out.lua的第一個字節改為"\x1b",以對上LUA_SIGNATURE,並復制到luadec/luadec文件夾中。

image-20210304133241178

然后在luadec/luadec下運行:

./luadec -dis check_license_out.lua > discheck

(如wp所說,反編譯會報錯,只能反匯編,估計是亂魔改的原因哈哈哈哈。

得到反匯編的discheck。

; Disassembled using luadec 2.2 rev: 895d923 for Lua 5.3 from https://github.com/viruscamp/luadec
; Command line: -dis check_license_out.lua 

; Function:        0
; Defined at line: 0
; #Upvalues:       1
; #Parameters:     0
; Is_vararg:       2
; Max Stack Size:  53

    0 [-]: __eq      R0 0         ; R0 := closure(Function #0_0)
    1 [-]: SETTABUP  U0 K0 R0     ; U0["to_v"] := R0
    2 [-]: NEWTABLE  R0 21 0      ; R0 := {} (size = 21,0)
    3 [-]: LOADK     R1 K1        ; R1 := 172
    4 [-]: LOADK     R2 K2        ; R2 := 25
    5 [-]: LOADK     R3 K3        ; R3 := 60
    6 [-]: LOADK     R4 K4        ; R4 := 95
    7 [-]: LOADK     R5 K5        ; R5 := 5
    8 [-]: LOADK     R6 K6        ; R6 := 27
    9 [-]: LOADK     R7 K7        ; R7 := 49
   10 [-]: LOADK     R8 K8        ; R8 := 58
   11 [-]: LOADK     R9 K9        ; R9 := 171
   12 [-]: LOADK     R10 K5       ; R10 := 5
   13 [-]: LOADK     R11 K10      ; R11 := 253
   14 [-]: LOADK     R12 K11      ; R12 := 45
   15 [-]: LOADK     R13 K12      ; R13 := 87
   16 [-]: LOADK     R14 K13      ; R14 := 246
   17 [-]: LOADK     R15 K14      ; R15 := 197
   18 [-]: LOADK     R16 K15      ; R16 := 12
   19 [-]: LOADK     R17 K16      ; R17 := 97
   20 [-]: LOADK     R18 K17      ; R18 := 234
   21 [-]: LOADK     R19 K18      ; R19 := 159
   22 [-]: LOADK     R20 K19      ; R20 := 119
   23 [-]: LOADK     R21 K20      ; R21 := 157
   24 [-]: LOADK     R22 K21      ; R22 := 169
   25 [-]: LOADK     R23 K22      ; R23 := 121
   26 [-]: LOADK     R24 K23      ; R24 := 54
   27 [-]: LOADK     R25 K24      ; R25 := 242
   28 [-]: (null)    R0 25 1      ; R0[0] to R0[24] := R1 to R25 ; R(a)[(c-1)*FPF+i] := R(a+i), 1 <= i <= b, a=0, b=25, c=1, FPF=50
   29 [-]: NEWTABLE  R1 21 0      ; R1 := {} (size = 21,0)
   30 [-]: LOADK     R2 K25       ; R2 := 94
   31 [-]: LOADK     R3 K26       ; R3 := 117
   32 [-]: LOADK     R4 K27       ; R4 := 57
   33 [-]: LOADK     R5 K28       ; R5 := 37
   34 [-]: LOADK     R6 K23       ; R6 := 54
   35 [-]: LOADK     R7 K29       ; R7 := 110
   36 [-]: LOADK     R8 K30       ; R8 := 15
   37 [-]: LOADK     R9 K31       ; R9 := 223
   38 [-]: LOADK     R10 K32      ; R10 := 163
   39 [-]: LOADK     R11 K33      ; R11 := 133
   40 [-]: LOADK     R12 K34      ; R12 := 99
   41 [-]: LOADK     R13 K35      ; R13 := 237
   42 [-]: LOADK     R14 K36      ; R14 := 8
   43 [-]: LOADK     R15 K37      ; R15 := 128
   44 [-]: LOADK     R16 K6       ; R16 := 27
   45 [-]: LOADK     R17 K23      ; R17 := 54
   46 [-]: LOADK     R18 K38      ; R18 := 233
   47 [-]: LOADK     R19 K39      ; R19 := 181
   48 [-]: LOADK     R20 K24      ; R20 := 242
   49 [-]: LOADK     R21 K40      ; R21 := 55
   50 [-]: LOADK     R22 K41      ; R22 := 230
   51 [-]: LOADK     R23 K42      ; R23 := 62
   52 [-]: LOADK     R24 K43      ; R24 := 42
   53 [-]: LOADK     R25 K44      ; R25 := 252
   54 [-]: LOADK     R26 K45      ; R26 := 116
   55 [-]: (null)    R1 25 1      ; R1[0] to R1[24] := R2 to R26 ; R(a)[(c-1)*FPF+i] := R(a+i), 1 <= i <= b, a=1, b=25, c=1, FPF=50
   56 [-]: NEWTABLE  R2 48 0      ; R2 := {} (size = 48,0)
   57 [-]: LOADK     R3 K46       ; R3 := 81
   58 [-]: LOADK     R4 K47       ; R4 := 138
   59 [-]: LOADK     R5 K48       ; R5 := 85
   60 [-]: LOADK     R6 K49       ; R6 := 142
   61 [-]: LOADK     R7 K50       ; R7 := 185
   62 [-]: LOADK     R8 K51       ; R8 := 35
   63 [-]: LOADK     R9 K52       ; R9 := 229
   64 [-]: LOADK     R10 K53      ; R10 := 83
   65 [-]: LOADK     R11 K36      ; R11 := 8
   66 [-]: LOADK     R12 K54      ; R12 := 225
   67 [-]: LOADK     R13 K55      ; R13 := 92
   68 [-]: LOADK     R14 K31      ; R14 := 223
   69 [-]: LOADK     R15 K56      ; R15 := 222
   70 [-]: LOADK     R16 K57      ; R16 := 47
   71 [-]: LOADK     R17 K58      ; R17 := 182
   72 [-]: LOADK     R18 K59      ; R18 := 158
   73 [-]: LOADK     R19 K60      ; R19 := 17
   74 [-]: LOADK     R20 K61      ; R20 := 74
   75 [-]: LOADK     R21 K62      ; R21 := 34
   76 [-]: LOADK     R22 K63      ; R22 := 100
   77 [-]: LOADK     R23 K64      ; R23 := 43
   78 [-]: LOADK     R24 K65      ; R24 := 103
   79 [-]: LOADK     R25 K66      ; R25 := 102
   80 [-]: LOADK     R26 K67      ; R26 := 147
   81 [-]: LOADK     R27 K35      ; R27 := 237
   82 [-]: LOADK     R28 K68      ; R28 := 88
   83 [-]: LOADK     R29 K69      ; R29 := 73
   84 [-]: LOADK     R30 K70      ; R30 := 28
   85 [-]: LOADK     R31 K71      ; R31 := 224
   86 [-]: LOADK     R32 K72      ; R32 := 23
   87 [-]: LOADK     R33 K73      ; R33 := 44
   88 [-]: LOADK     R34 K74      ; R34 := 40
   89 [-]: LOADK     R35 K75      ; R35 := 154
   90 [-]: LOADK     R36 K76      ; R36 := 127
   91 [-]: LOADK     R37 K77      ; R37 := 16
   92 [-]: LOADK     R38 K21      ; R38 := 169
   93 [-]: LOADK     R39 K78      ; R39 := 160
   94 [-]: LOADK     R40 K79      ; R40 := 118
   95 [-]: LOADK     R41 K80      ; R41 := 51
   96 [-]: LOADK     R42 K81      ; R42 := 194
   97 [-]: LOADK     R43 K82      ; R43 := 31
   98 [-]: LOADK     R44 K83      ; R44 := 68
   99 [-]: LOADK     R45 K84      ; R45 := 89
  100 [-]: LOADK     R46 K85      ; R46 := 65
  101 [-]: LOADK     R47 K86      ; R47 := 162
  102 [-]: LOADK     R48 K87      ; R48 := 13
  103 [-]: LOADK     R49 K88      ; R49 := 141
  104 [-]: LOADK     R50 K89      ; R50 := 0
  105 [-]: LOADK     R51 K90      ; R51 := 244
  106 [-]: LOADK     R52 K19      ; R52 := 119
  107 [-]: (null)    R2 50 1      ; R2[0] to R2[49] := R3 to R52 ; R(a)[(c-1)*FPF+i] := R(a+i), 1 <= i <= b, a=2, b=50, c=1, FPF=50
  108 [-]: LOADK     R3 K91       ; R3 := 161
  109 [-]: LOADK     R4 K92       ; R4 := 198
  110 [-]: LOADK     R5 K93       ; R5 := 228
  111 [-]: LOADK     R6 K4        ; R6 := 95
  112 [-]: LOADK     R7 K94       ; R7 := 10
  113 [-]: LOADK     R8 K95       ; R8 := 78
  114 [-]: LOADK     R9 K28       ; R9 := 37
  115 [-]: LOADK     R10 K22      ; R10 := 121
  116 [-]: LOADK     R11 K96      ; R11 := 236
  117 [-]: LOADK     R12 K97      ; R12 := 59
  118 [-]: LOADK     R13 K3       ; R13 := 60
  119 [-]: LOADK     R14 K98      ; R14 := 91
  120 [-]: LOADK     R15 K99      ; R15 := 146
  121 [-]: LOADK     R16 K100     ; R16 := 46
  122 [-]: LOADK     R17 K101     ; R17 := 77
  123 [-]: LOADK     R18 K102     ; R18 := 218
  124 [-]: LOADK     R19 K103     ; R19 := 66
  125 [-]: LOADK     R20 K104     ; R20 := 200
  126 [-]: LOADK     R21 K105     ; R21 := 61
  127 [-]: LOADK     R22 K106     ; R22 := 241
  128 [-]: LOADK     R23 K107     ; R23 := 70
  129 [-]: LOADK     R24 K40      ; R24 := 55
  130 [-]: LOADK     R25 K108     ; R25 := 39
  131 [-]: LOADK     R26 K109     ; R26 := 227
  132 [-]: LOADK     R27 K43      ; R27 := 42
  133 [-]: LOADK     R28 K110     ; R28 := 2
  134 [-]: LOADK     R29 K111     ; R29 := 231
  135 [-]: LOADK     R30 K112     ; R30 := 235
  136 [-]: LOADK     R31 K113     ; R31 := 122
  137 [-]: LOADK     R32 K114     ; R32 := 135
  138 [-]: LOADK     R33 K115     ; R33 := 152
  139 [-]: LOADK     R34 K116     ; R34 := 137
  140 [-]: LOADK     R35 K117     ; R35 := 173
  141 [-]: LOADK     R36 K118     ; R36 := 232
  142 [-]: LOADK     R37 K119     ; R37 := 101
  143 [-]: LOADK     R38 K120     ; R38 := 75
  144 [-]: LOADK     R39 K38      ; R39 := 233
  145 [-]: LOADK     R40 K121     ; R40 := 21
  146 [-]: LOADK     R41 K44      ; R41 := 252
  147 [-]: LOADK     R42 K30      ; R42 := 15
  148 [-]: LOADK     R43 K33      ; R43 := 133
  149 [-]: LOADK     R44 K122     ; R44 := 111
  150 [-]: LOADK     R45 K123     ; R45 := 205
  151 [-]: LOADK     R46 K27      ; R46 := 57
  152 [-]: LOADK     R47 K124     ; R47 := 132
  153 [-]: LOADK     R48 K125     ; R48 := 187
  154 [-]: LOADK     R49 K126     ; R49 := 96
  155 [-]: LOADK     R50 K7       ; R50 := 49
  156 [-]: LOADK     R51 K127     ; R51 := 124
  157 [-]: LOADK     R52 K128     ; R52 := 86
  158 [-]: (null)    R2 50 2      ; R2[50] to R2[99] := R3 to R52 ; R(a)[(c-1)*FPF+i] := R(a+i), 1 <= i <= b, a=2, b=50, c=2, FPF=50
  159 [-]: LOADK     R3 K129      ; R3 := 19
  160 [-]: LOADK     R4 K130      ; R4 := 188
  161 [-]: LOADK     R5 K131      ; R5 := 80
  162 [-]: LOADK     R6 K132      ; R6 := 213
  163 [-]: LOADK     R7 K133      ; R7 := 106
  164 [-]: LOADK     R8 K134      ; R8 := 214
  165 [-]: LOADK     R9 K135      ; R9 := 203
  166 [-]: LOADK     R10 K136     ; R10 := 177
  167 [-]: LOADK     R11 K137     ; R11 := 56
  168 [-]: LOADK     R12 K138     ; R12 := 104
  169 [-]: LOADK     R13 K139     ; R13 := 82
  170 [-]: LOADK     R14 K29      ; R14 := 110
  171 [-]: LOADK     R15 K140     ; R15 := 196
  172 [-]: LOADK     R16 K141     ; R16 := 113
  173 [-]: LOADK     R17 K142     ; R17 := 155
  174 [-]: LOADK     R18 K143     ; R18 := 170
  175 [-]: LOADK     R19 K144     ; R19 := 150
  176 [-]: LOADK     R20 K26      ; R20 := 117
  177 [-]: LOADK     R21 K145     ; R21 := 26
  178 [-]: LOADK     R22 K146     ; R22 := 140
  179 [-]: LOADK     R23 K147     ; R23 := 144
  180 [-]: LOADK     R24 K148     ; R24 := 11
  181 [-]: LOADK     R25 K1       ; R25 := 172
  182 [-]: LOADK     R26 K149     ; R26 := 67
  183 [-]: LOADK     R27 K150     ; R27 := 209
  184 [-]: LOADK     R28 K151     ; R28 := 125
  185 [-]: LOADK     R29 K23      ; R29 := 54
  186 [-]: LOADK     R30 K8       ; R30 := 58
  187 [-]: LOADK     R31 K37      ; R31 := 128
  188 [-]: LOADK     R32 K152     ; R32 := 204
  189 [-]: LOADK     R33 K153     ; R33 := 186
  190 [-]: LOADK     R34 K154     ; R34 := 199
  191 [-]: LOADK     R35 K155     ; R35 := 189
  192 [-]: LOADK     R36 K156     ; R36 := 208
  193 [-]: LOADK     R37 K157     ; R37 := 239
  194 [-]: LOADK     R38 K158     ; R38 := 143
  195 [-]: LOADK     R39 K159     ; R39 := 249
  196 [-]: LOADK     R40 K13      ; R40 := 246
  197 [-]: LOADK     R41 K160     ; R41 := 1
  198 [-]: LOADK     R42 K161     ; R42 := 139
  199 [-]: LOADK     R43 K162     ; R43 := 33
  200 [-]: LOADK     R44 K12      ; R44 := 87
  201 [-]: LOADK     R45 K163     ; R45 := 64
  202 [-]: LOADK     R46 K45      ; R46 := 116
  203 [-]: LOADK     R47 K164     ; R47 := 84
  204 [-]: LOADK     R48 K165     ; R48 := 254
  205 [-]: LOADK     R49 K166     ; R49 := 126
  206 [-]: LOADK     R50 K167     ; R50 := 202
  207 [-]: LOADK     R51 K168     ; R51 := 148
  208 [-]: LOADK     R52 K169     ; R52 := 76
  209 [-]: (null)    R2 50 3      ; R2[100] to R2[149] := R3 to R52 ; R(a)[(c-1)*FPF+i] := R(a+i), 1 <= i <= b, a=2, b=50, c=3, FPF=50
  210 [-]: LOADK     R3 K170      ; R3 := 247
  211 [-]: LOADK     R4 K171      ; R4 := 115
  212 [-]: LOADK     R5 K172      ; R5 := 109
  213 [-]: LOADK     R6 K173      ; R6 := 3
  214 [-]: LOADK     R7 K174      ; R7 := 238
  215 [-]: LOADK     R8 K175      ; R8 := 114
  216 [-]: LOADK     R9 K176      ; R9 := 156
  217 [-]: LOADK     R10 K177     ; R10 := 195
  218 [-]: LOADK     R11 K32      ; R11 := 163
  219 [-]: LOADK     R12 K18      ; R12 := 159
  220 [-]: LOADK     R13 K178     ; R13 := 52
  221 [-]: LOADK     R14 K179     ; R14 := 36
  222 [-]: LOADK     R15 K180     ; R15 := 245
  223 [-]: LOADK     R16 K181     ; R16 := 240
  224 [-]: LOADK     R17 K182     ; R17 := 63
  225 [-]: LOADK     R18 K183     ; R18 := 153
  226 [-]: LOADK     R19 K184     ; R19 := 166
  227 [-]: LOADK     R20 K185     ; R20 := 167
  228 [-]: LOADK     R21 K186     ; R21 := 175
  229 [-]: LOADK     R22 K187     ; R22 := 9
  230 [-]: LOADK     R23 K188     ; R23 := 151
  231 [-]: LOADK     R24 K9       ; R24 := 171
  232 [-]: LOADK     R25 K189     ; R25 := 216
  233 [-]: LOADK     R26 K190     ; R26 := 207
  234 [-]: LOADK     R27 K191     ; R27 := 179
  235 [-]: LOADK     R28 K192     ; R28 := 72
  236 [-]: LOADK     R29 K193     ; R29 := 176
  237 [-]: LOADK     R30 K194     ; R30 := 48
  238 [-]: LOADK     R31 K195     ; R31 := 178
  239 [-]: LOADK     R32 K20      ; R32 := 157
  240 [-]: LOADK     R33 K196     ; R33 := 20
  241 [-]: LOADK     R34 K39      ; R34 := 181
  242 [-]: LOADK     R35 K197     ; R35 := 149
  243 [-]: LOADK     R36 K198     ; R36 := 53
  244 [-]: LOADK     R37 K199     ; R37 := 184
  245 [-]: LOADK     R38 K200     ; R38 := 4
  246 [-]: LOADK     R39 K201     ; R39 := 136
  247 [-]: LOADK     R40 K202     ; R40 := 165
  248 [-]: LOADK     R41 K203     ; R41 := 217
  249 [-]: LOADK     R42 K204     ; R42 := 50
  250 [-]: LOADK     R43 K205     ; R43 := 190
  251 [-]: LOADK     R44 K206     ; R44 := 191
  252 [-]: LOADK     R45 K207     ; R45 := 192
  253 [-]: LOADK     R46 K208     ; R46 := 193
  254 [-]: LOADK     R47 K209     ; R47 := 98
  255 [-]: LOADK     R48 K210     ; R48 := 215
  256 [-]: LOADK     R49 K42      ; R49 := 62
  257 [-]: LOADK     R50 K211     ; R50 := 112
  258 [-]: LOADK     R51 K212     ; R51 := 38
  259 [-]: LOADK     R52 K213     ; R52 := 90
  260 [-]: (null)    R2 50 4      ; R2[150] to R2[199] := R3 to R52 ; R(a)[(c-1)*FPF+i] := R(a+i), 1 <= i <= b, a=2, b=50, c=4, FPF=50
  261 [-]: LOADK     R3 K214      ; R3 := 123
  262 [-]: LOADK     R4 K215      ; R4 := 105
  263 [-]: LOADK     R5 K25       ; R5 := 94
  264 [-]: LOADK     R6 K216      ; R6 := 221
  265 [-]: LOADK     R7 K34       ; R7 := 99
  266 [-]: LOADK     R8 K217      ; R8 := 201
  267 [-]: LOADK     R9 K218      ; R9 := 206
  268 [-]: LOADK     R10 K219     ; R10 := 251
  269 [-]: LOADK     R11 K220     ; R11 := 14
  270 [-]: LOADK     R12 K221     ; R12 := 211
  271 [-]: LOADK     R13 K222     ; R13 := 220
  272 [-]: LOADK     R14 K223     ; R14 := 131
  273 [-]: LOADK     R15 K224     ; R15 := 212
  274 [-]: LOADK     R16 K225     ; R16 := 130
  275 [-]: LOADK     R17 K226     ; R17 := 134
  276 [-]: LOADK     R18 K10      ; R18 := 253
  277 [-]: LOADK     R19 K227     ; R19 := 120
  278 [-]: LOADK     R20 K228     ; R20 := 145
  279 [-]: LOADK     R21 K229     ; R21 := 18
  280 [-]: LOADK     R22 K230     ; R22 := 219
  281 [-]: LOADK     R23 K231     ; R23 := 79
  282 [-]: LOADK     R24 K232     ; R24 := 129
  283 [-]: LOADK     R25 K15      ; R25 := 12
  284 [-]: LOADK     R26 K233     ; R26 := 93
  285 [-]: LOADK     R27 K5       ; R27 := 5
  286 [-]: LOADK     R28 K234     ; R28 := 183
  287 [-]: LOADK     R29 K235     ; R29 := 107
  288 [-]: LOADK     R30 K236     ; R30 := 71
  289 [-]: LOADK     R31 K237     ; R31 := 226
  290 [-]: LOADK     R32 K238     ; R32 := 180
  291 [-]: LOADK     R33 K239     ; R33 := 24
  292 [-]: LOADK     R34 K17      ; R34 := 234
  293 [-]: LOADK     R35 K240     ; R35 := 7
  294 [-]: LOADK     R36 K241     ; R36 := 108
  295 [-]: LOADK     R37 K242     ; R37 := 174
  296 [-]: LOADK     R38 K243     ; R38 := 6
  297 [-]: LOADK     R39 K11      ; R39 := 45
  298 [-]: LOADK     R40 K244     ; R40 := 29
  299 [-]: LOADK     R41 K245     ; R41 := 32
  300 [-]: LOADK     R42 K246     ; R42 := 168
  301 [-]: LOADK     R43 K41      ; R43 := 230
  302 [-]: LOADK     R44 K14      ; R44 := 197
  303 [-]: LOADK     R45 K247     ; R45 := 41
  304 [-]: LOADK     R46 K2       ; R46 := 25
  305 [-]: LOADK     R47 K248     ; R47 := 255
  306 [-]: LOADK     R48 K249     ; R48 := 164
  307 [-]: LOADK     R49 K6       ; R49 := 27
  308 [-]: LOADK     R50 K250     ; R50 := 210
  309 [-]: LOADK     R51 K251     ; R51 := 248
  310 [-]: LOADK     R52 K16      ; R52 := 97
  311 [-]: (null)    R2 50 5      ; R2[200] to R2[249] := R3 to R52 ; R(a)[(c-1)*FPF+i] := R(a+i), 1 <= i <= b, a=2, b=50, c=5, FPF=50
  312 [-]: LOADK     R3 K252      ; R3 := 250
  313 [-]: LOADK     R4 K253      ; R4 := 22
  314 [-]: LOADK     R5 K24       ; R5 := 242
  315 [-]: LOADK     R6 K254      ; R6 := 243
  316 [-]: LOADK     R7 K255      ; R7 := 30
  317 [-]: LOADK     R8 K256      ; R8 := 69
  318 [-]: (null)    R2 6 6       ; R2[250] to R2[255] := R3 to R8 ; R(a)[(c-1)*FPF+i] := R(a+i), 1 <= i <= b, a=2, b=6, c=6, FPF=50
  319 [-]: LOADK     R3 K257      ; R3 := "encrypt"
  320 [-]: __eq      R4 1         ; R4 := closure(Function #0_1)
  321 [-]: SETTABUP  U0 R3 R4     ; U0[R3] := R4
  322 [-]: LOADK     R3 K258      ; R3 := "check_license"
  323 [-]: __eq      R4 2         ; R4 := closure(Function #0_2)
  324 [-]: SETTABUP  U0 R3 R4     ; U0[R3] := R4
  325 [-]: LOADK     R3 K259      ; R3 := "io"
  326 [-]: GETTABUP  R3 U0 R3     ; R3 := U0[R3]
  327 [-]: LOADK     R4 K260      ; R4 := "write"
  328 [-]: GETTABLE  R3 R3 R4     ; R3 := R3[R4]
  329 [-]: LOADK     R4 K261      ; R4 := "input: "
  330 [-]: CALL      R3 2 1       ;  := R3(R4)
  331 [-]: LOADK     R3 K262      ; R3 := "license"
  332 [-]: LOADK     R4 K259      ; R4 := "io"
  333 [-]: GETTABUP  R4 U0 R4     ; R4 := U0[R4]
  334 [-]: LOADK     R5 K263      ; R5 := "read"
  335 [-]: GETTABLE  R4 R4 R5     ; R4 := R4[R5]
  336 [-]: LOADK     R5 K264      ; R5 := "*l"
  337 [-]: CALL      R4 2 2       ; R4 := R4(R5)
  338 [-]: SETTABUP  U0 R3 R4     ; U0[R3] := R4
  339 [-]: LOADK     R3 K258      ; R3 := "check_license"
  340 [-]: GETTABUP  R3 U0 R3     ; R3 := U0[R3]
  341 [-]: LOADK     R4 K262      ; R4 := "license"
  342 [-]: GETTABUP  R4 U0 R4     ; R4 := U0[R4]
  343 [-]: CALL      R3 2 1       ;  := R3(R4)
  344 [-]: RETURN    R0 1         ; return 


; Function:        0_0
; Defined at line: 2
; #Upvalues:       1
; #Parameters:     1
; Is_vararg:       0
; Max Stack Size:  10

    0 [-]: NEWTABLE  R1 0 0       ; R1 := {} (size = 0,0)
    1 [-]: LOADK     R2 K0        ; R2 := 1
    2 [-]: GETTABUP  R3 U0 K1     ; R3 := U0["string"]
    3 [-]: GETTABLE  R3 R3 K2     ; R3 := R3["len"]
    4 [-]: MOVE      R4 R0        ; R4 := R0
    5 [-]: CALL      R3 2 2       ; R3 := R3(R4)
    6 [-]: LOADK     R4 K0        ; R4 := 1
    7 [-]: FORPREP   R2 8         ; R2 -= R4; pc += 8 (goto 16)
    8 [-]: LEN       R6 R1        ; R6 := #R1
    9 [-]: ADD       R6 R6 K0     ; R6 := R6 + 1
   10 [-]: GETTABUP  R7 U0 K1     ; R7 := U0["string"]
   11 [-]: GETTABLE  R7 R7 K3     ; R7 := R7["byte"]
   12 [-]: MOVE      R8 R0        ; R8 := R0
   13 [-]: MOVE      R9 R5        ; R9 := R5
   14 [-]: CALL      R7 3 2       ; R7 := R7(R8 to R9)
   15 [-]: __mode    R1 R6 R7     ; R1[R6] := R7
   16 [-]: (null)    R2 -9        ; R2 += R4; if R2 <= R3 then R5 := R2; PC += -9 , goto 8 end
   17 [-]: RETURN    R1 2         ; return R1
   18 [-]: RETURN    R0 1         ; return 


; Function:        0_1
; Defined at line: 15
; #Upvalues:       4
; #Parameters:     1
; Is_vararg:       0
; Max Stack Size:  10

    0 [-]: GETTABUP  R1 U0 K0     ; R1 := U0["to_v"]
    1 [-]: MOVE      R2 R0        ; R2 := R0
    2 [-]: CALL      R1 2 2       ; R1 := R1(R2)
    3 [-]: NEWTABLE  R2 0 0       ; R2 := {} (size = 0,0)
    4 [-]: LOADK     R3 K1        ; R3 := 1
    5 [-]: LEN       R4 R1        ; R4 := #R1
    6 [-]: LOADK     R5 K1        ; R5 := 1
    7 [-]: FORPREP   R3 10        ; R3 -= R5; pc += 10 (goto 18)
    8 [-]: GETTABLE  R7 R1 R6     ; R7 := R1[R6]
    9 [-]: GETTABUP  R8 U1 R6     ; R8 := U1[R6]
   10 [-]: BXOR      R7 R7 R8     ; R7 := R7 ~ R8
   11 [-]: __mode    R1 R6 R7     ; R1[R6] := R7
   12 [-]: LEN       R7 R2        ; R7 := #R2
   13 [-]: ADD       R7 R7 K1     ; R7 := R7 + 1
   14 [-]: GETTABLE  R8 R1 R6     ; R8 := R1[R6]
   15 [-]: ADD       R8 R8 K1     ; R8 := R8 + 1
   16 [-]: GETTABUP  R8 U2 R8     ; R8 := U2[R8]
   17 [-]: __mode    R2 R7 R8     ; R2[R7] := R8
   18 [-]: (null)    R3 -11       ; R3 += R5; if R3 <= R4 then R6 := R3; PC += -11 , goto 8 end
   19 [-]: LOADK     R3 K2        ; R3 := 0
   20 [-]: LOADK     R4 K1        ; R4 := 1
   21 [-]: LEN       R5 R1        ; R5 := #R1
   22 [-]: LOADK     R6 K1        ; R6 := 1
   23 [-]: FORPREP   R4 7         ; R4 -= R6; pc += 7 (goto 31)
   24 [-]: GETTABLE  R8 R2 R7     ; R8 := R2[R7]
   25 [-]: GETTABUP  R9 U3 R7     ; R9 := U3[R7]
   26 [-]: __add     0 R8 R9      ; if R8 == R9 then goto 28 else goto 30
   27 [-]: JMP       R0 2         ; PC += 2 (goto 30)
   28 [-]: ADD       R3 R3 K1     ; R3 := R3 + 1
   29 [-]: JMP       R0 1         ; PC += 1 (goto 31)
   30 [-]: ADD       R3 R3 K3     ; R3 := R3 + 2
   31 [-]: (null)    R4 -8        ; R4 += R6; if R4 <= R5 then R7 := R4; PC += -8 , goto 24 end
   32 [-]: LEN       R4 R1        ; R4 := #R1
   33 [-]: __add     1 R3 R4      ; if R3 ~= R4 then goto 35 else goto 39
   34 [-]: JMP       R0 4         ; PC += 4 (goto 39)
   35 [-]: GETTABUP  R4 U0 K4     ; R4 := U0["print"]
   36 [-]: LOADK     R5 K5        ; R5 := "bad"
   37 [-]: CALL      R4 2 1       ;  := R4(R5)
   38 [-]: JMP       R0 3         ; PC += 3 (goto 42)
   39 [-]: GETTABUP  R4 U0 K4     ; R4 := U0["print"]
   40 [-]: LOADK     R5 K6        ; R5 := "good"
   41 [-]: CALL      R4 2 1       ;  := R4(R5)
   42 [-]: RETURN    R0 1         ; return 


; Function:        0_2
; Defined at line: 38
; #Upvalues:       1
; #Parameters:     1
; Is_vararg:       0
; Max Stack Size:  3

    0 [-]: LEN       R1 R0        ; R1 := #R0
    1 [-]: SETTABUP  U0 K0 R1     ; U0["size"] := R1
    2 [-]: GETTABUP  R1 U0 K0     ; R1 := U0["size"]
    3 [-]: __add     1 R1 K1      ; if R1 ~= 25 then goto 5 else goto 9
    4 [-]: JMP       R0 4         ; PC += 4 (goto 9)
    5 [-]: GETTABUP  R1 U0 K2     ; R1 := U0["print"]
    6 [-]: LOADK     R2 K3        ; R2 := "length error"
    7 [-]: CALL      R1 2 1       ;  := R1(R2)
    8 [-]: RETURN    R0 1         ; return 
    9 [-]: GETTABUP  R1 U0 K4     ; R1 := U0["encrypt"]
   10 [-]: MOVE      R2 R0        ; R2 := R0
   11 [-]: CALL      R1 2 1       ;  := R1(R2)
   12 [-]: RETURN    R0 1         ; return 

說是反匯編其實還是有一點反編譯注釋的吼。

根據注釋來走就很容易理順邏輯啦(可以參考深入理解 Lua 虛擬機_騰訊技術工程 - MdEditor),算法用python還原是:

# Function 0_0:  to_v()
# Function 0_1:  encrypt()
# Function 0_2:  check_license()

def to_v(s):
    return [ord(c) for c in s]
def encrypt(s):
    global tb0,tb1,tb2
    u1,u2,u3=tb0,tb2,tb1 #這個upvalue對應我也不知道是怎么看出來的,可能靠試吧
    l1=to_v(s)
    size=len(l1)
    l2=[]
    for i in range(size):
        l1[i]^=u1[i]
        l2.append(u2[l1[i]+1])
    r3=0
    for i in range(size):
        if l2[i]==u3[i]:
            r3+=1
        else:
            r3+=2
    if r3!=size:
        print("bad")
    else:
        print("good")
    return
def check_license(s):
    if len(s)!=25:
        print("length error")
    else:
        encrypt(s)
    return
if __name__ == '__main__':
    tb0=[172, 25, 60, 95, 5, 27, 49, 58, 171, 5, 253, 45, 87, 246, 197, 12, 97, 234, 159, 119, 157, 169, 121, 54, 242]
    tb1=[94, 117, 57, 37, 54, 110, 15, 223, 163, 133, 99, 237, 8, 128, 27, 54, 233, 181, 242, 55, 230, 62, 42, 252, 116]
    tb2=[81, 138, 85, 142, 185, 35, 229, 83, 8, 225, 92, 223, 222, 47, 182, 158, 17, 74, 34, 100, 43, 103, 102, 147, 237, 88, 73, 28, 224, 23, 44, 40, 154, 127, 16, 169, 160, 118, 51, 194, 31, 68, 89, 65, 162, 13, 141, 0, 244, 119, 161, 198, 228, 95, 10, 78, 37, 121, 236, 59, 60, 91, 146, 46, 77, 218, 66, 200, 61, 241, 70, 55, 39, 227, 42, 2, 231, 235, 122, 135, 152, 137, 173, 232, 101, 75, 233, 21, 252, 15, 133, 111, 205, 57, 132, 187, 96, 49, 124, 86, 19, 188, 80, 213, 106, 214, 203, 177, 56, 104, 82, 110, 196, 113, 155, 170, 150, 117, 26, 140, 144, 11, 172, 67, 209, 125, 54, 58, 128, 204, 186, 199, 189, 208, 239, 143, 249, 246, 1, 139, 33, 87, 64, 116, 84, 254, 126, 202, 148, 76, 247, 115, 109, 3, 238, 114, 156, 195, 163, 159, 52, 36, 245, 240, 63, 153, 166, 167, 175, 9, 151, 171, 216, 207, 179, 72, 176, 48, 178, 157, 20, 181, 149, 53, 184, 4, 136, 165, 217, 50, 190, 191, 192, 193, 98, 215, 62, 112, 38, 90, 123, 105, 94, 221, 99, 201, 206, 251, 14, 211, 220, 131, 212, 130, 134, 253, 120, 145, 18, 219, 79, 129, 12, 93, 5, 183, 107, 71, 226, 180, 24, 234, 7, 108, 174, 6, 45, 29, 32, 168, 230, 197, 41, 25, 255, 164, 27, 210, 248, 97, 250, 22, 242, 243, 30, 69]
    print("input: ",end='')
    license=input()
    check_license(license)

就可以根據邏輯寫出exp:

tb0=[172, 25, 60, 95, 5, 27, 49, 58, 171, 5, 253, 45, 87, 246, 197, 12, 97, 234, 159, 119, 157, 169, 121, 54, 242]
tb1=[94, 117, 57, 37, 54, 110, 15, 223, 163, 133, 99, 237, 8, 128, 27, 54, 233, 181, 242, 55, 230, 62, 42, 252, 116]
tb2=[81, 138, 85, 142, 185, 35, 229, 83, 8, 225, 92, 223, 222, 47, 182, 158, 17, 74, 34, 100, 43, 103, 102, 147, 237, 88, 73, 28, 224, 23, 44, 40, 154, 127, 16, 169, 160, 118, 51, 194, 31, 68, 89, 65, 162, 13, 141, 0, 244, 119, 161, 198, 228, 95, 10, 78, 37, 121, 236, 59, 60, 91, 146, 46, 77, 218, 66, 200, 61, 241, 70, 55, 39, 227, 42, 2, 231, 235, 122, 135, 152, 137, 173, 232, 101, 75, 233, 21, 252, 15, 133, 111, 205, 57, 132, 187, 96, 49, 124, 86, 19, 188, 80, 213, 106, 214, 203, 177, 56, 104, 82, 110, 196, 113, 155, 170, 150, 117, 26, 140, 144, 11, 172, 67, 209, 125, 54, 58, 128, 204, 186, 199, 189, 208, 239, 143, 249, 246, 1, 139, 33, 87, 64, 116, 84, 254, 126, 202, 148, 76, 247, 115, 109, 3, 238, 114, 156, 195, 163, 159, 52, 36, 245, 240, 63, 153, 166, 167, 175, 9, 151, 171, 216, 207, 179, 72, 176, 48, 178, 157, 20, 181, 149, 53, 184, 4, 136, 165, 217, 50, 190, 191, 192, 193, 98, 215, 62, 112, 38, 90, 123, 105, 94, 221, 99, 201, 206, 251, 14, 211, 220, 131, 212, 130, 134, 253, 120, 145, 18, 219, 79, 129, 12, 93, 5, 183, 107, 71, 226, 180, 24, 234, 7, 108, 174, 6, 45, 29, 32, 168, 230, 197, 41, 25, 255, 164, 27, 210, 248, 97, 250, 22, 242, 243, 30, 69]
flag=""
for i in range(25):
    flag+=chr(tb2.index(tb1[i])^tb0[i])
print(flag)

得到flag

image-20210304150822710

flag{th15_15_v3r7_c0mm3n}

divination[TO DO]

先用模擬器跑一下,發現是一個輸入字符串判斷的邏輯。

因為物理機里裝了WSL2,所以用的是能兼容Hyper-V的BlueStacks模擬器。

image-20210221004556852

走流程,先用pxb1988/dex2jar: Tools to work with android .dex and java .class files這個工具把將apk解壓后文件夾中的classes.dex文件轉換成jar文件,再用Java Decompiler中的JD-GUI查看反編譯出的java源代碼。

然后可以看到MainActivity.class這里也有一句“大事不妙”,找到主函數。

image-20210221005218142

看了一下邏輯就是調用libdivination.so(在apk解壓后的路徑divination/lib/arm64-v8a/libdivination.so),只要返回值為真就行。

所以其實做安卓逆向看so硬逆也能逆出來hhh。

於是現在轉去逆so。

image-20210221102702774

關鍵入口函數顯而易見,就是這個跟包同名的函數。

image-20210221110434855

為了方便看反編譯代碼,按y把a1-a3三個參數的變量類型進行更改(ida無法自動識別。

image-20210221110645265

下邊的函數也一目了然。

從后往前看,先拿到目標數組unk_3000:

image-20210221111251068


[12.23] 鯤鵬計算專場

mips

真·送分題,可惜當時要上課沒來得及搶一血(下午2點放題絕了

老傳統走迷宮

mips架構。

ida反編譯以后可以看到

image-20201229125551113

v4是我們輸入的字符串,很明顯是迷宮邏輯,上下左右用wasd走,迷宮存在dword_100111F0里。

sub_10000744()這個初始函數是用來找起點用的(就是迷宮中3所在的地方,在后面可以看到3其實表示的是當前位置)。

image-20201229125822518

這里也可以看到應該有多個迷宮(dword_10011D10是用來表示第幾個迷宮的,且<=2,一個迷宮有225個數)+一個迷宮寬為15=三個迷宮,每個迷宮為15*15。

然后就是下面的四個函數,隨便挑一個出來(比如sub_10000D28())可以看到

image-20201229130146475

很明顯是個往右走的函數,3表示當前位置,並把上一個當前位置標為1(可走路徑)。並且可以看到終點是4,就是說我們要把每個迷宮從3走到4。

dump迷宮數組,寫腳本打印迷宮:

aMap=[1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 3, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 3, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0]
for i in range(45):
    for j in range(15):
        if aMap[i*15+j]==0:
            tmp='*'
        elif aMap[i*15+j]==1:
            tmp='.'
        elif aMap[i*15+j]==3:
            tmp='@'
        else:
            tmp='#'
        print(tmp,end='')
    print()
    if i==14 or i==29:
        print()

可以看到打印出了三個迷宮,為了看得清楚所以選用幾個特定字符打印。

.....**********
.....*@*.******
.....*.*.******
.....*.*.******
.....*.*.....**
.....*.*****.**
.....*.*****.**
.....*.*****..*
.....*........*
.....********#*
...............
...............
...............
...............
...............
#sssssssddddddds

..*************
..*@*....******
..*.****.******
..*.****.******
..*..***.....**
..*..*******.**
..*..*******.**
..*..*****....*
..*..*****.**.*
..*..*****.****
..*......*.*..*
..*...........*
..***********#*
...............
...............
#ssssssssssdddddddddds

***************
*@..***********
***.*...*******
***...*.*******
****.**.*******
*..*.**.*******
**...**.*******
*******.*******
*******....****
**********.****
**********.****
**********.****
**********....*
*************.*
*************#*
#ddssddwddssssssdddssssdddss

走迷宮,然后把路徑拼起來,根據提示轉md5,get flag。

(有個疑惑哈,第二個迷宮理論上說就算是最短路也有多解?是題目出鍋了還是我哪里看漏了= =

(再補一句,題目似乎甚至沒要求最短路???神奇.jpg

image-20201229163439245

import hashlib
s=b"sssssssdddddddsssssssssssddddddddddsddssddwddssssssdddssssdddss"
print("flag{%s}"%hashlib.md5(s).hexdigest())

flag{999ea6aa6c365ab43eec2a0f0e5968d5}

pypy

把題目文件拖進ida,搜索字符串能看到

image-20201229164109092

猜測是pyinstaller打包的文件。

也就是這個題讓我突然發現pyinstaller還能打包成elf的,於是比賽結束以后趕緊把之前總結的解包指南更新了:RE套路 - 關於pyinstaller打包文件的復原 | c10udlnk_Log

走流程解包,得到python源碼。

image-20201229165610468

看到這種混淆變量名,果斷替換成ida style變量名(。

放一下源碼:

# uncompyle6 version 3.7.4
# Python bytecode 3.8 (3413)
# Decompiled from: Python 2.7.18 (v2.7.18:8d21aa21f2, Apr 20 2020, 13:25:05) [MSC v.1500 64 bit (AMD64)]
# Warning: this version of Python has problems handling the Python 3 "byte" type in constants properly.

# Embedded file name: main.py
# Compiled at: 1995-09-28 00:18:56
# Size of source mod 2**32: 257 bytes
import random, codecs, sys, time, pygame
from pygame.locals import *
from collections import deque
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 480
SIZE = 20
LINE_WIDTH = 1
flag = 'flag{this is a fake flag}'
SCOPE_X = (0, SCREEN_WIDTH // SIZE - 1)
SCOPE_Y = (2, SCREEN_HEIGHT // SIZE - 1)
FOOD_STYLE_LIST = [(10, (255, 100, 100)), (20, (100, 255, 100)), (30, (100, 100, 255))]
LIGHT = (100, 100, 100)
DARK = (200, 200, 200)
BLACK = (0, 0, 0)
RED = (200, 30, 30)
BGCOLOR = (40, 40, 60)

def print_text(v1, v2, v3, v4, v5, fcolor=(255, 255, 255)):
    v6 = v2.render(v5, True, fcolor)
    v1.blit(v6, (v3, v4))


def init_snake():
    v7 = deque()
    v7.append((2, SCOPE_Y[0]))
    v7.append((1, SCOPE_Y[0]))
    v7.append((0, SCOPE_Y[0]))
    return v7


def create_food(v8):
    v9 = random.randint(SCOPE_X[0], SCOPE_X[1])
    v10 = random.randint(SCOPE_Y[0], SCOPE_Y[1])
    while (v9, v10) in v8:
        v9 = random.randint(SCOPE_X[0], SCOPE_X[1])
        v10 = random.randint(SCOPE_Y[0], SCOPE_Y[1])

    return (
     v9, v10)


def get_food_style():
    return FOOD_STYLE_LIST[random.randint(0, 2)]


DEFAULT_KEY = u'Y\xf3\x02\xc3%\x9a\x820\x0b\xbb%\x7f~;\xd2\xdc'

def rc4(v11, key=DEFAULT_KEY, skip=1024):
    v12 = 0
    v13 = bytearray([v14 for v14 in range(256)])
    v12 = 0
    for v15 in range(256):
        v12 = (v12 + v13[v15] + ord(key[(v15 % len(key))])) % 256
        v16 = v13[v15]
        v17 = v13[v12]
        v13[v15] = v13[v12]
        v13[v12] = v16
    else:
        v12 = 0
        v18 = 0
        v19 = []
        if skip > 0:
            for v15 in range(skip):
                v12 = (v12 + 1) % 256
                v18 = (v18 + v13[v12]) % 256
                v13[v12], v13[v18] = v13[v18], v13[v12]

        for v20 in v11:
            v12 = (v12 + 1) % 256
            v18 = (v18 + v13[v12]) % 256
            v13[v12], v13[v18] = v13[v18], v13[v12]
            v21 = v13[((v13[v12] + v13[v18]) % 256)]
            v19.append(chr(ord(v20) ^ v21))
        else:
            return ''.join(v19)


def func(v22):
    v23 = rc4(v22)
    if v23.encode('utf-8').hex() == '275b39c381c28b701ac3972338456022c2ba06c3b04f5501471c47c38ac380c29b72c3b5c38a7ec2a5c2a0':
        return 'YOU WIN'
    return 'YOU LOSE'


def main():
    pygame.init()
    v24 = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    pygame.display.set_caption(u'\u8d2a\u5403\u86c7')
    v25 = pygame.font.SysFont('SimHei', 24)
    v26 = pygame.font.Font(None, 72)
    v27, v28 = v26.size('GAME OVER')
    v29 = True
    v30 = init_snake()
    v31 = create_food(v30)
    v32 = get_food_style()
    v33 = (1, 0)
    v34 = True
    v35 = False
    v36 = 0
    v37 = 0.5
    v38 = v37
    v39 = None
    v41 = False
    for v40 in pygame.event.get():
        if v40.type == QUIT:
            sys.exit()
        elif v40.type == KEYDOWN:
            if v40.key == K_RETURN:
                if v34:
                    v35 = True
                    v34 = False
                    v29 = True
                    v30 = init_snake()
                    v31 = create_food(v30)
                    v32 = get_food_style()
                    v33 = (1, 0)
                    v36 = 0
                    v39 = time.time()
            elif v40.key == K_SPACE:
                if not v34:
                    v41 = not v41
            elif v40.key in (K_w, K_UP):
                if v29:
                    v33 = v33[1] or (0, -1)
                    v29 = False
            elif v40.key in (K_s, K_DOWN):
                if v29:
                    v33 = v33[1] or (0, 1)
                    v29 = False
            elif v40.key in (K_a, K_LEFT):
                if v29:
                    if not v33[0]:
                        v33 = (-1, 0)
                        v29 = False
                    elif v40.key in (K_d, K_RIGHT):
                        if v29:
                            if not v33[0]:
                                v33 = (1, 0)
                                v29 = False
        else:
            v24.fill(BGCOLOR)
            for v42 in range(SIZE, SCREEN_WIDTH, SIZE):
                pygame.draw.line(v24, BLACK, (v42, SCOPE_Y[0] * SIZE), (v42, SCREEN_HEIGHT), LINE_WIDTH)
            else:
                for v43 in range(SCOPE_Y[0] * SIZE, SCREEN_HEIGHT, SIZE):
                    pygame.draw.line(v24, BLACK, (0, v43), (SCREEN_WIDTH, v43), LINE_WIDTH)
                else:
                    v44 = v34 or time.time()

            if v44 - v39 > v38 and not v41:
                v29 = True
                v39 = v44
                v45 = (v30[0][0] + v33[0], v30[0][1] + v33[1])
                if v45 == v31:
                    v30.appendleft(v45)
                    v36 += v32[0]
                    v38 = v37 - 0.03 * (v36 // 100)
                    v31 = create_food(v30)
                    v32 = get_food_style()
                else:
                    if SCOPE_X[0] <= v45[0] <= SCOPE_X[1]:
                        if SCOPE_Y[0] <= v45[1] <= SCOPE_Y[1]:
                            if v45 not in v30:
                                v30.appendleft(v45)
                                v30.pop()
                            else:
                                v34 = True
                    if not v34:
                        pygame.draw.rect(v24, v32[1], (v31[0] * SIZE, v31[1] * SIZE, SIZE, SIZE), 0)
                for v46 in v30:
                    pygame.draw.rect(v24, DARK, (v46[0] * SIZE + LINE_WIDTH, v46[1] * SIZE + LINE_WIDTH, SIZE - LINE_WIDTH * 2, SIZE - LINE_WIDTH * 2), 0)
                else:
                    print_text(v24, v25, 30, 7, f"speed: {v36 // 100}")
                    print_text(v24, v25, 450, 7, f"score: {v36}")
                    if v36 >= 5192296858534827628530496329220096:
                        v47 = flag
                        print_text(v24, v26, (SCREEN_WIDTH - v27) // 2, (SCREEN_HEIGHT - v28) // 2, func(v47), RED)
                    if v34:
                        if v35:
                            print_text(v24, v26, (SCREEN_WIDTH - v27) // 2, (SCREEN_HEIGHT - v28) // 2, 'GAME OVER', RED)
                    pygame.display.update()


if __name__ == '__main__':
    main()
# okay decompiling main.pyc

可以看到最后getflag這里(func())的程序邏輯就一個rc4加密,由rc4的特性可知加密和解密流程相同,故復用程序中的rc4()來得到flag。

uncompyle反編譯出來的源碼是python3,但是題目本身的源碼是python2,注意編碼問題。

關於編碼問題,可以看:

Unicode之痛 — PyCoder's Weelky CN

關於python2中的unicode和str以及python3中的str和bytes - 明王不動心 - 博客園

這里因為反編譯做了轉換成python3的處理,所以腳本用python3寫。

DEFAULT_KEY = u'Y\xf3\x02\xc3%\x9a\x820\x0b\xbb%\x7f~;\xd2\xdc'
def rc4(v11, key=DEFAULT_KEY, skip=1024):
    v12 = 0
    v13 = bytearray([v14 for v14 in range(256)])
    v12 = 0
    for v15 in range(256):
        v12 = (v12 + v13[v15] + ord(key[(v15 % len(key))])) % 256
        v16 = v13[v15]
        v17 = v13[v12]
        v13[v15] = v13[v12]
        v13[v12] = v16
    else:
        v12 = 0
        v18 = 0
        v19 = []
        if skip > 0:
            for v15 in range(skip):
                v12 = (v12 + 1) % 256
                v18 = (v18 + v13[v12]) % 256
                v13[v12], v13[v18] = v13[v18], v13[v12]

        for v20 in v11:
            v12 = (v12 + 1) % 256
            v18 = (v18 + v13[v12]) % 256
            v13[v12], v13[v18] = v13[v18], v13[v12]
            v21 = v13[((v13[v12] + v13[v18]) % 256)]
            v19.append(chr(ord(v20) ^ v21))
        else:
            return ''.join(v19)
# def func(v22):
#     v23 = rc4(v22)
#     if v23.encode('utf-8').hex() == '275b39c381c28b701ac3972338456022c2ba06c3b04f5501471c47c38ac380c29b72c3b5c38a7ec2a5c2a0':
#         return 'YOU WIN'
#     return 'YOU LOSE'

# -=-=-=以上所有為源碼中原函數-=-=-=

cipher='275b39c381c28b701ac3972338456022c2ba06c3b04f5501471c47c38ac380c29b72c3b5c38a7ec2a5c2a0'
flag=bytes.fromhex(cipher).decode('utf-8')
print(rc4(flag))

image-20201229190337791

flag{snake_bao_is_really_lucky}

print

官方wp說的是給了一個brainfuck引擎,跟我賽中攻擊格式化字符串漏洞的思路完全不一樣(。),一開始還以為我想得太偏了XD,然而在復現的時候發現這個brainfuck解釋器的原理就是攻擊格式化字符串漏洞,學到了學到了。

以下試圖從頭開始復現。

通過在DuckDuckGo上搜索setup()中的特殊字符串"%1$.*1$d %2$hn"查到一個github項目HexHive/printbf: Brainfuck interpreter inside printf,發現其余的字符串與題目中的十分相似,並且pbf_pre.c跟程序邏輯幾乎一樣,推測是題目的來源。

image-20210123170914416

image-20210123171023639

所以先通過動態調試,把斷點設在前面所有的賦值語句之后,用idapython的get_bytes(0x5577FCB50000,16000)提取出progn的內容。

image-20210123181852813

通過printbf項目中的pbf_pre.c源碼可以知道progn是int *,故將提取出來的byte對象進行處理,然后按照token.py的邏輯逆向寫出由progn反向生成的brainfuck程序。

# token.py from "HexHive/printbf: Brainfuck interpreter inside printf"
#!/usr/bin/python
# -*- coding: utf-8 -*-

import itertools
import sys
from argparse import ArgumentParser

__author__ = "Nicholas Carlini <npc@berkeley.edu> and Mathias Payer <mathias.payer@nebelwelt.net>"
__description__ = "Script to tokenize a BF script into a printf interpreter."
__version__ = filter(str.isdigit, "$Revision: 1 $")

parser = ArgumentParser(description=__description__)
parser.add_argument('-v', '--version', action='version', version='%(prog)s {:s}'.format(__version__))
parser.add_argument('-t', '--template', type=str, metavar='template filename', help='Filename for the template to use.', required=False, default='bf_pre.c')
parser.add_argument('-bf', '--brainfuck', type=str, metavar='bf file', help='BF program', required=True)
parser.add_argument('-i', '--input', type=str, metavar='input to bf', help='BF input', required=False, default='')
args = parser.parse_args()
                    
prog = open(args.brainfuck).read()
prog = "".join([x for x in prog if x in "<>+-.,[]"])
prog = prog.replace("[-]", "Z")

remap = {'<':[1],
         '>':[2],
         '+':[3],
         '-':[4],
         '.':[5,6],
         ',':[7,8],
         '[':[9,10,11],
         ']':[12],
         'Z':[13],
         'F':[14],
         'B':[15],#,16,17],
         'A':[18],
         }

newprog = []
progiter = iter(prog)
while len(prog):
    e = prog[0]
    if e in '<>+-':
        res = itertools.takewhile(lambda x: x == e, prog)
        count = len(list(res))
        if e == '>':
            newprog.append(('F',count))
        elif e == '<':
            assert count < 256
            newprog.append(('B',65536-count))
        elif e == '+':
            newprog.append(('A',count))
        elif e == '-':
            newprog.append(('A',256-count))
        prog = prog[count:]
    else:
        newprog.append(e)
        prog = prog[1:]

stack = []
index = 2

txt = open(args.template).read()
txt = txt.replace('###INPUT###', args.input)
print txt[0:txt.find('###TOKER###')],

for e in newprog:
    count = 0
    if type(e) == type(tuple()):
        count = e[1]
        e = e[0]
    for i,insn in enumerate(remap[e]):
        print '  progn[%d] = %d;'%(index+i*2,insn)

    if count != 0:
        assert i == 0
        print '  progn[%d] = %d;'%(index+1,count)

    if e == '[': # this is a cgoto
        stack.append(index)
    elif e == ']':
        backto = stack.pop()
        print '  progn[%d] = %d;'%(backto+1,index*4-4)
        print '  progn[%d] = %d;'%(backto+3,index*4-4)
        print '  progn[%d] = %d;'%(backto+5,index*4-4)
        
        print '  progn[%d] = %d;'%(index+1,(backto-2)*4) # we always increment PC by 1

    index += 2*len(remap[e])

print txt[txt.find('###TOKER###')+11:],

可以看到實際上就是從progn[2*k]這里看到brainfuck的每一步操作符,而對於"+-><"這四種操作符的數量由progn[2*k+1]給出,故從這些位置即可提取出bf程序的操作符,得到bf程序:

p=b'' # 這里是動態調試提取出的progn字節對象,太長了這里不貼
progn=[]
for i in range(0,16000,4):
    progn.append(int.from_bytes(p[i:i+4],byteorder='little'))
# 轉化成printbf中的int* progn
i=2
bfCode=""
while i<len(progn) and progn[i]!=0:
    if progn[i]==5:
        bfCode+='.'
        i+=4
    elif progn[i]==7:
        bfCode+=','
        i+=4
    elif progn[i]==9:
        bfCode+='['
        i+=6
    elif progn[i]==12:
        bfCode+=']'
        i+=2
    elif progn[i]==13:
        bfCode+='[-]'
        i+=2
    elif progn[i]==14:
        bfCode+='>'*progn[i+1]
        i+=2
    elif progn[i]==15:
        bfCode+='<'*(65536-progn[i+1])
        i+=2
    elif progn[i]==18:
        # 其實這里+和-沒法准確區分,且對於無符號char型數據來說-n和+(256-n)得到的結果是一樣的,這里選擇字符數更少的操作符
        if progn[i+1]>0x7F:
            bfCode+='-'*(256-progn[i+1])
        else:
            bfCode+='+'*progn[i+1]
        i+=2
    else:
        print("[-] UknCode "+str(progn[i])+" in progn["+str(i)+"]")
        i+=2
print(bfCode)

得到:

>,>,>,>,>,>,<<<<<[->>>>>>+<<<<<<<+>]<[->+<]>[->>>>>>+<<<<<<<+>]<[->+<]>>[->>>>>>+<<<<<<<<+>>]<<[->>+<<]>>[->>>>>>+<<<<<<<<+>>]<<[->>+<<]>>>[->>>>>>+<<<<<<<<<+>>>]<<<[->>>+<<<]>>>[->>>>>>+<<<<<<<<<+>>>]<<<[->>>+<<<]>>>>[->>>>>>+<<<<<<<<<<+>>>>]<<<<[->>>>+<<<<]>>>>[->>>>>>+<<<<<<<<<<+>>>>]<<<<[->>>>+<<<<]>>>>>[->>>>>>+<<<<<<<<<<<+>>>>>]<<<<<[->>>>>+<<<<<]>>>>>[->>>>>>+<<<<<<<<<<<+>>>>>]<<<<<[->>>>>+<<<<<]>>>>>>[->>>>>>+<<<<<<<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<]>>>>>>[->>>>>>+<<<<<<<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<]>>>>>>>[->>>>>>+<<<<<<<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<<<<<<]>[->>>>>>>>>>>>+<<<<<<<<<<<<<+>]<[->+<]>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<+>>>>>>>>]<<<<<<<<[->>>>>>>>+<<<<<<<<]>>[->>>>>>>>>>>>+<<<<<<<<<<<<<<+>>]<<[->>+<<]>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<+>>>>>>>>>]<<<<<<<<<[->>>>>>>>>+<<<<<<<<<]>>>[->>>>>>>>>>>>+<<<<<<<<<<<<<<<+>>>]<<<[->>>+<<<]>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<+>>>>>>>>>>]<<<<<<<<<<[->>>>>>>>>>+<<<<<<<<<<]>>>>[->>>>>>>>>>>>+<<<<<<<<<<<<<<<<+>>>>]<<<<[->>>>+<<<<]>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<+>>>>>>>>>>>]<<<<<<<<<<<[->>>>>>>>>>>+<<<<<<<<<<<]>>>>>[->>>>>>>>>>>>+<<<<<<<<<<<<<<<<<+>>>>>]<<<<<[->>>>>+<<<<<]>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>]<<<<<<<<<<<<[->>>>>>>>>>>>+<<<<<<<<<<<<]>>>>>>[->>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<]>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>]<<<<<<<<<<<<<[->>>>>>>>>>>>>+<<<<<<<<<<<<<]>[->>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<+>]<[->+<]>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>]<<<<<<<<<<<<<<[->>>>>>>>>>>>>>+<<<<<<<<<<<<<<]>>[->>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<+>>]<<[->>+<<]>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<]>>>[->>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<+>>>]<<<[->>>+<<<]>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<]>>>>[->>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<+>>>>]<<<<[->>>>+<<<<]>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<]>>>>>[->>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<+>>>>>]<<<<<[->>>>>+<<<<<]>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<]>>>>>>[->>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<]>>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<]>[->>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<+>]<[->+<]>>>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<]>>[->>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<+>>]<<[->>+<<]>>>>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<]>>>[->>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>]<<<[->>>+<<<]>>>>>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<]>>>>[->>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>]<<<<[->>>>+<<<<]>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<]>>>>>[->>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>]<<<<<[->>>>>+<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>[->>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<]>>[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>]<<[->>+<<]>>>>>>>>>[->>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>]<<<<<<<<<[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>>>>>>>>[->>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>[->>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>[->>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<]>>>>>>>>>>[->>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>]<<<<<<<<<<[->>>>>>>>>>+<<<<<<<<<<]>>>>>[->>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>]<<<<<[->>>>>+<<<<<]>>>>>>>>>>>>>[->>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>]<<<<<<<<<<<<<[->>>>>>>>>>>>>+<<<<<<<<<<<<<]>>>>>>>>[->>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>]<<<<<<<<[->>>>>>>>+<<<<<<<<]>>>[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>]<<<[->>>+<<<]>>>>>>>>>>>>>>>>>>>>>>[->>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>[->>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<<<<<<]>>[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>]<<[->>+<<]>>>>>>>>>>>>>>>>>>>>>[->>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>[->>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<]>>>>>>>>[->>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>]<<<<<<<<[->>>>>>>>+<<<<<<<<]>>>>>>>>>>>>>>>[->>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>[->>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<]>>>>>>>[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<<<<<<]>>>>>>>>>>>>>>>>>>>>[->>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<]>>>[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>]<<<[->>>+<<<]>>>>>>>>>>>>>>>>>[->>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]---------------------------->[-]+++++>[-]-------------------------------------------------------------------------------------------------->[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++<<<<<<<<<<<[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]++++++++++++++++++++++++++++++++++++++++++++++++<<<<<<[>>>>>>+<<<<<<[-]]>[>>>>>+<<<<<[-]]>[>>>>+<<<<[-]]>[>>>+<<<[-]]>[>>+<<[-]]>[>+<[-]]>>,>,>,>,>,>,>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]++++++++++++++++++++++++++++++++++++++++++++++++>[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++<<<<<<<<<<<[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[<<<<<<<+>>>>>>>[-]]>[<<<<<<<<+>>>>>>>>[-]]>[<<<<<<<<<+>>>>>>>>>[-]]>[<<<<<<<<<<+>>>>>>>>>>[-]]>[<<<<<<<<<<<+>>>>>>>>>>>[-]]>[<<<<<<<<<<<<+>>>>>>>>>>>>[-]]<<<<<<<<<<<<.

進一步的,為了方便甚至可以直接轉化成c程序,最后的處理腳本為:

p=b'' #依舊是太長不貼(
progn=[]
for i in range(0,16000,4):
    progn.append(int.from_bytes(p[i:i+4],byteorder='little'))
# 轉化成printbf中的int* progn
i=2
indent=0
ptr=0
print(' '*indent+"#include <stdio.h>")
print(' '*indent+"int main(){")
indent+=4
print(' '*indent+"char mem[1000000]={0};")
while i<len(progn) and progn[i]!=0:
    if progn[i]==5:
        print(' '*indent+"printf(\"%c\",mem["+str(ptr)+"]);")
        i+=4
    elif progn[i]==7:
        print(' '*indent+"scanf(\"%c\",&mem["+str(ptr)+"]);")
        i+=4
    elif progn[i]==9:
        print(' '*indent+"while(mem["+str(ptr)+"]){")
        indent+=4
        i+=6
    elif progn[i]==12:
        i+=2
        indent-=4
        print(' '*indent+"}")
    elif progn[i]==13:
        print(' '*indent+"mem["+str(ptr)+"]=0;")
        i+=2
    elif progn[i]==14:
        ptr+=progn[i+1]
        i+=2
    elif progn[i]==15:
        ptr-=(65536-progn[i+1])
        i+=2
    elif progn[i]==18:
        # 其實這里+和-沒法准確區分,且對於無符號char型數據來說-n和+(256-n)得到的結果是一樣的,這里選擇字符數更少的操作符
        if progn[i+1]>0x7F:
            print(' '*indent+"mem["+str(ptr)+"]-="+str(256-progn[i+1])+";")
        else:
            print(' '*indent+"mem["+str(ptr)+"]+="+str(progn[i+1])+";")
        i+=2
    else:
        print("[-] UknCode "+str(progn[i])+" in progn["+str(i)+"]")
        i+=2
print(' '*indent+"return 0;")
indent-=4
print(' '*indent+"}")
# 直接轉化成C程序

得到C程序:

#include <stdio.h>
int main(){
    char mem[1000000]={0};
    scanf("%c",&mem[1]);
    scanf("%c",&mem[2]);
    scanf("%c",&mem[3]);
    scanf("%c",&mem[4]);
    scanf("%c",&mem[5]);
    scanf("%c",&mem[6]);
    while(mem[1]){
        mem[1]-=1;
        mem[7]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[1]+=1;
    }
    while(mem[1]){
        mem[1]-=1;
        mem[7]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[1]+=1;
    }
    while(mem[2]){
        mem[2]-=1;
        mem[8]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[2]+=1;
    }
    while(mem[2]){
        mem[2]-=1;
        mem[8]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[2]+=1;
    }
    while(mem[3]){
        mem[3]-=1;
        mem[9]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[3]+=1;
    }
    while(mem[3]){
        mem[3]-=1;
        mem[9]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[3]+=1;
    }
    while(mem[4]){
        mem[4]-=1;
        mem[10]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[4]+=1;
    }
    while(mem[4]){
        mem[4]-=1;
        mem[10]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[4]+=1;
    }
    while(mem[5]){
        mem[5]-=1;
        mem[11]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[5]+=1;
    }
    while(mem[5]){
        mem[5]-=1;
        mem[11]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[5]+=1;
    }
    while(mem[6]){
        mem[6]-=1;
        mem[12]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[6]+=1;
    }
    while(mem[6]){
        mem[6]-=1;
        mem[12]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[6]+=1;
    }
    while(mem[7]){
        mem[7]-=1;
        mem[13]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[7]+=1;
    }
    while(mem[1]){
        mem[1]-=1;
        mem[13]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[1]+=1;
    }
    while(mem[8]){
        mem[8]-=1;
        mem[14]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[8]+=1;
    }
    while(mem[2]){
        mem[2]-=1;
        mem[14]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[2]+=1;
    }
    while(mem[9]){
        mem[9]-=1;
        mem[15]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[9]+=1;
    }
    while(mem[3]){
        mem[3]-=1;
        mem[15]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[3]+=1;
    }
    while(mem[10]){
        mem[10]-=1;
        mem[16]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[10]+=1;
    }
    while(mem[4]){
        mem[4]-=1;
        mem[16]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[4]+=1;
    }
    while(mem[11]){
        mem[11]-=1;
        mem[17]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[11]+=1;
    }
    while(mem[5]){
        mem[5]-=1;
        mem[17]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[5]+=1;
    }
    while(mem[12]){
        mem[12]-=1;
        mem[18]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[12]+=1;
    }
    while(mem[6]){
        mem[6]-=1;
        mem[18]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[6]+=1;
    }
    while(mem[13]){
        mem[13]-=1;
        mem[19]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[13]+=1;
    }
    while(mem[1]){
        mem[1]-=1;
        mem[19]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[1]+=1;
    }
    while(mem[14]){
        mem[14]-=1;
        mem[20]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[14]+=1;
    }
    while(mem[2]){
        mem[2]-=1;
        mem[20]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[2]+=1;
    }
    while(mem[15]){
        mem[15]-=1;
        mem[21]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[15]+=1;
    }
    while(mem[3]){
        mem[3]-=1;
        mem[21]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[3]+=1;
    }
    while(mem[16]){
        mem[16]-=1;
        mem[22]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[16]+=1;
    }
    while(mem[4]){
        mem[4]-=1;
        mem[22]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[4]+=1;
    }
    while(mem[17]){
        mem[17]-=1;
        mem[23]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[17]+=1;
    }
    while(mem[5]){
        mem[5]-=1;
        mem[23]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[5]+=1;
    }
    while(mem[18]){
        mem[18]-=1;
        mem[24]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[18]+=1;
    }
    while(mem[6]){
        mem[6]-=1;
        mem[24]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[6]+=1;
    }
    while(mem[19]){
        mem[19]-=1;
        mem[25]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[19]+=1;
    }
    while(mem[1]){
        mem[1]-=1;
        mem[25]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[1]+=1;
    }
    while(mem[20]){
        mem[20]-=1;
        mem[26]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[20]+=1;
    }
    while(mem[2]){
        mem[2]-=1;
        mem[26]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[2]+=1;
    }
    while(mem[21]){
        mem[21]-=1;
        mem[27]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[21]+=1;
    }
    while(mem[3]){
        mem[3]-=1;
        mem[27]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[3]+=1;
    }
    while(mem[22]){
        mem[22]-=1;
        mem[28]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[22]+=1;
    }
    while(mem[4]){
        mem[4]-=1;
        mem[28]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[4]+=1;
    }
    while(mem[23]){
        mem[23]-=1;
        mem[29]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[23]+=1;
    }
    while(mem[5]){
        mem[5]-=1;
        mem[29]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[5]+=1;
    }
    while(mem[24]){
        mem[24]-=1;
        mem[30]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[24]+=1;
    }
    while(mem[6]){
        mem[6]-=1;
        mem[30]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[6]+=1;
    }
    while(mem[2]){
        mem[2]-=1;
        mem[31]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[2]+=1;
    }
    while(mem[9]){
        mem[9]-=1;
        mem[31]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[9]+=1;
    }
    while(mem[16]){
        mem[16]-=1;
        mem[31]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[16]+=1;
    }
    while(mem[23]){
        mem[23]-=1;
        mem[31]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[23]+=1;
    }
    while(mem[30]){
        mem[30]-=1;
        mem[31]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[30]+=1;
    }
    while(mem[25]){
        mem[25]-=1;
        mem[32]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[25]+=1;
    }
    while(mem[20]){
        mem[20]-=1;
        mem[32]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[20]+=1;
    }
    while(mem[15]){
        mem[15]-=1;
        mem[32]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[15]+=1;
    }
    while(mem[10]){
        mem[10]-=1;
        mem[32]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[10]+=1;
    }
    while(mem[5]){
        mem[5]-=1;
        mem[32]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[5]+=1;
    }
    while(mem[13]){
        mem[13]-=1;
        mem[33]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[13]+=1;
    }
    while(mem[8]){
        mem[8]-=1;
        mem[33]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[8]+=1;
    }
    while(mem[3]){
        mem[3]-=1;
        mem[33]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[3]+=1;
    }
    while(mem[22]){
        mem[22]-=1;
        mem[33]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[22]+=1;
    }
    while(mem[30]){
        mem[30]-=1;
        mem[33]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[30]+=1;
    }
    while(mem[7]){
        mem[7]-=1;
        mem[34]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[7]+=1;
    }
    while(mem[2]){
        mem[2]-=1;
        mem[34]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[2]+=1;
    }
    while(mem[21]){
        mem[21]-=1;
        mem[34]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[21]+=1;
    }
    while(mem[28]){
        mem[28]-=1;
        mem[34]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[28]+=1;
    }
    while(mem[17]){
        mem[17]-=1;
        mem[34]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[17]+=1;
    }
    while(mem[8]){
        mem[8]-=1;
        mem[35]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[8]+=1;
    }
    while(mem[15]){
        mem[15]-=1;
        mem[35]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[15]+=1;
    }
    while(mem[22]){
        mem[22]-=1;
        mem[35]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[22]+=1;
    }
    while(mem[29]){
        mem[29]-=1;
        mem[35]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[29]+=1;
    }
    while(mem[6]){
        mem[6]-=1;
        mem[35]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[6]+=1;
    }
    while(mem[7]){
        mem[7]-=1;
        mem[36]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[7]+=1;
    }
    while(mem[20]){
        mem[20]-=1;
        mem[36]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[20]+=1;
    }
    while(mem[3]){
        mem[3]-=1;
        mem[36]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[3]+=1;
    }
    while(mem[17]){
        mem[17]-=1;
        mem[36]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[17]+=1;
    }
    while(mem[30]){
        mem[30]-=1;
        mem[36]+=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[30]+=1;
    }
    mem[37]=0;
    mem[37]+=53;
    mem[38]=0;
    mem[38]-=28;
    mem[39]=0;
    mem[39]+=5;
    mem[40]=0;
    mem[40]-=98;
    mem[41]=0;
    mem[41]+=73;
    mem[42]=0;
    mem[42]+=123;
    while(mem[31]){
        mem[31]-=1;
        mem[37]-=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[31]+=1;
    }
    while(mem[32]){
        mem[32]-=1;
        mem[38]-=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[32]+=1;
    }
    while(mem[33]){
        mem[33]-=1;
        mem[39]-=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[33]+=1;
    }
    while(mem[34]){
        mem[34]-=1;
        mem[40]-=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[34]+=1;
    }
    while(mem[35]){
        mem[35]-=1;
        mem[41]-=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[35]+=1;
    }
    while(mem[36]){
        mem[36]-=1;
        mem[42]-=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[36]+=1;
    }
    mem[43]=0;
    mem[43]+=48;
    while(mem[37]){
        mem[43]+=1;
        mem[37]=0;
    }
    while(mem[38]){
        mem[43]+=1;
        mem[38]=0;
    }
    while(mem[39]){
        mem[43]+=1;
        mem[39]=0;
    }
    while(mem[40]){
        mem[43]+=1;
        mem[40]=0;
    }
    while(mem[41]){
        mem[43]+=1;
        mem[41]=0;
    }
    while(mem[42]){
        mem[43]+=1;
        mem[42]=0;
    }
    scanf("%c",&mem[44]);
    scanf("%c",&mem[45]);
    scanf("%c",&mem[46]);
    scanf("%c",&mem[47]);
    scanf("%c",&mem[48]);
    scanf("%c",&mem[49]);
    mem[50]=0;
    mem[50]+=101;
    mem[51]=0;
    mem[51]+=95;
    mem[52]=0;
    mem[52]+=67;
    mem[53]=0;
    mem[53]+=48;
    mem[54]=0;
    mem[54]+=100;
    mem[55]=0;
    mem[55]+=51;
    while(mem[44]){
        mem[44]-=1;
        mem[50]-=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[44]+=1;
    }
    while(mem[45]){
        mem[45]-=1;
        mem[51]-=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[45]+=1;
    }
    while(mem[46]){
        mem[46]-=1;
        mem[52]-=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[46]+=1;
    }
    while(mem[47]){
        mem[47]-=1;
        mem[53]-=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[47]+=1;
    }
    while(mem[48]){
        mem[48]-=1;
        mem[54]-=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[48]+=1;
    }
    while(mem[49]){
        mem[49]-=1;
        mem[55]-=1;
        mem[0]+=1;
    }
    while(mem[0]){
        mem[0]-=1;
        mem[49]+=1;
    }
    while(mem[50]){
        mem[43]+=1;
        mem[50]=0;
    }
    while(mem[51]){
        mem[43]+=1;
        mem[51]=0;
    }
    while(mem[52]){
        mem[43]+=1;
        mem[52]=0;
    }
    while(mem[53]){
        mem[43]+=1;
        mem[53]=0;
    }
    while(mem[54]){
        mem[43]+=1;
        mem[54]=0;
    }
    while(mem[55]){
        mem[43]+=1;
        mem[55]=0;
    }
    printf("%c",mem[43]);
    return 0;
}

於是現在的任務就成了分兩次輸入6個字符,經過處理后若mem[43]==48(原二進制文件中的判斷邏輯)就能得到flag。

這個C程序還是比較冗雜(畢竟九百多行),所以對其進行找規律分析化簡。

先拿出前四個while即[line 10, line 27]進行分析:

while(mem[1]){
    mem[1]-=1;
    mem[7]+=1;
    mem[0]+=1;
} // mem[7]+=mem[1];mem[0]+=mem[1];mem[1]=0;
while(mem[0]){
    mem[0]-=1;
    mem[1]+=1;
} // mem[1]+=mem[0];mem[0]=0;
while(mem[1]){
    mem[1]-=1;
    mem[7]+=1;
    mem[0]+=1;
} // mem[7]+=mem[1];mem[0]+=mem[1];mem[1]=0;
while(mem[0]){
    mem[0]-=1;
    mem[1]+=1;
} // mem[1]+=mem[0];mem[0]=0;

也就是說整段等同於:

mem[7]+=mem[1]*2;
// mem[0]和mem[1]沒變

所以接下來的(6-1)*4個循環也做相同處理,即[line 10, line 117]可以簡化為:

mem[7]+=mem[1]*2;
mem[8]+=mem[2]*2;
mem[9]+=mem[3]*2;
mem[10]+=mem[4]*2;
mem[11]+=mem[5]*2;
mem[12]+=mem[6]*2;

然后再拿出四個while即[line 118, line 135]進行分析:

while(mem[7]){
    mem[7]-=1;
    mem[13]+=1;
    mem[0]+=1;
} // mem[13]+=mem[7];mem[0]+=mem[7];mem[7]=0;
while(mem[0]){
    mem[0]-=1;
    mem[7]+=1;
} // mem[7]+=mem[0];mem[0]=0;
while(mem[1]){
    mem[1]-=1;
    mem[13]+=1;
    mem[0]+=1;
} // mem[13]+=mem[1];mem[0]+=mem[1];mem[1]=0;
while(mem[0]){
    mem[0]-=1;
    mem[1]+=1;
} // mem[1]+=mem[0];mem[0]=0;

所以整段等同於:

mem[13]=mem[1]+mem[7];

所以[line 118, line 225]簡化為:

mem[13]=mem[1]+mem[7];
mem[14]=mem[2]+mem[8];
mem[15]=mem[3]+mem[9];
mem[16]=mem[4]+mem[10];
mem[17]=mem[5]+mem[11];
mem[18]=mem[6]+mem[12];

最后[line 10, line 803]可簡化為

mem[7]+=mem[1]*2;
mem[8]+=mem[2]*2;
mem[9]+=mem[3]*2;
mem[10]+=mem[4]*2;
mem[11]+=mem[5]*2;
mem[12]+=mem[6]*2;
// [line 10, line 117]
mem[13]=mem[1]+mem[7];
mem[14]=mem[2]+mem[8];
mem[15]=mem[3]+mem[9];
mem[16]=mem[4]+mem[10];
mem[17]=mem[5]+mem[11];
mem[18]=mem[6]+mem[12];
// [line 118, line 225]
mem[19]=mem[1]+mem[13];
mem[20]=mem[2]+mem[14];
mem[21]=mem[3]+mem[15];
mem[22]=mem[4]+mem[16];
mem[23]=mem[5]+mem[17];
mem[24]=mem[6]+mem[18];
// [line 226, line 333]
mem[25]=mem[1]+mem[19];
mem[26]=mem[2]+mem[20];
mem[27]=mem[3]+mem[21];
mem[28]=mem[4]+mem[22];
mem[29]=mem[5]+mem[23];
mem[30]=mem[6]+mem[24];
// [line 334, line 441]
mem[31]=mem[2]+mem[9]+mem[16]+mem[23]+mem[30];
mem[32]=mem[25]+mem[20]+mem[15]+mem[10]+mem[5];
mem[33]=mem[13]+mem[8]+mem[3]+mem[22]+mem[30];
mem[34]=mem[7]+mem[2]+mem[21]+mem[28]+mem[17];
mem[35]=mem[8]+mem[15]+mem[22]+mem[29]+mem[6];
mem[36]=mem[7]+mem[20]+mem[3]+mem[17]+mem[30];
// [line 442, line 711]
mem[37]=53;
mem[38]=-28;
mem[39]=5;
mem[40]=-98;
mem[41]=73;
mem[42]=123;
// [line 712, line 723]
mem[37]-=mem[31];
mem[38]-=mem[32];
mem[39]-=mem[33];
mem[40]-=mem[34];
mem[41]-=mem[35];
mem[42]-=mem[36];
// [line 724, line 777]
mem[43]=48;
// [line 778, line 779]
for(int i=37;i<=42;i++){
    if(mem[i]) mem[43]+=1;
    mem[i]=0;
}
// [line 780, line 803]

后半段輸入后,處理的簡化程序為:

mem[50]=101;
mem[51]=95;
mem[52]=67;
mem[53]=48;
mem[54]=100;
mem[55]=51;
// [line 810, line 821]
mem[50]-=mem[44];
mem[51]-=mem[45];
mem[52]-=mem[46];
mem[53]-=mem[47];
mem[54]-=mem[48];
mem[55]-=mem[49];
// [line 822, line 875]
for(int i=50;i<=55;i++){
    if(mem[i]) mem[43]+=1;
    mem[i]=0;
}
// [line 876, line 899]

至此分析完成。

而mem[43]一開始賦值就是48,也就是說兩個for循環都不能進到if里。令s[12]為所求字符串,則可以得出方程組:

s[1]+s[2]*2+s[3]*3+s[4]*4+s[5]*5==53;//mem[2]+mem[9]+mem[16]+mem[23]+mem[30]==53;
s[0]*5+s[1]*4+s[2]*3+s[3]*2+s[4]*1==-28;//mem[25]+mem[20]+mem[15]+mem[10]+mem[5]==-28;
s[0]*3+s[1]*2+s[2]*1+s[3]*4+s[5]*5==5;//mem[13]+mem[8]+mem[3]+mem[22]+mem[30]==5;
s[0]*2+s[1]*1+s[2]*4+s[3]*5+s[4]*3==-98;//mem[7]+mem[2]+mem[21]+mem[28]+mem[17]==-98;
s[1]*2+s[2]*3+s[3]*4+s[4]*5+s[5]*1==73;//mem[8]+mem[15]+mem[22]+mem[29]+mem[6]==73;
s[0]*2+s[1]*4+s[2]*1+s[4]*3+s[5]*5==123;//mem[7]+mem[20]+mem[3]+mem[17]+mem[30]==123;
s[6]==101;
s[7]==95;
s[8]==67;
s[9]==48;
s[10]==100;
s[11]==51;

因為涉及到char型數據解方程的問題,s[0]-s[5]不好直接出,爆破又太慢了,所以決定構造一個程序用angr破:

// useAngr.c
#include <stdio.h>
int main(){
    char s[6]={0};
    scanf("%6s",s);
    char x1=s[1]+s[2]*2+s[3]*3+s[4]*4+s[5]*5;
    char x2=s[0]*5+s[1]*4+s[2]*3+s[3]*2+s[4]*1;
    char x3=s[0]*3+s[1]*2+s[2]*1+s[3]*4+s[5]*5;
    char x4=s[0]*2+s[1]*1+s[2]*4+s[3]*5+s[4]*3;
    char x5=s[1]*2+s[2]*3+s[3]*4+s[4]*5+s[5]*1;
    char x6=s[0]*2+s[1]*4+s[2]*1+s[4]*3+s[5]*5;
    if(x1==53&&x2==-28&&x3==5&&x4==-98&&x5==73&&x6==123)
        printf("Congratulations!!!!!!");
    return 0;
}

useAngr_exp:

import time
import angr
import sys

t=time.clock()
path_to_binary='./useAngr'
project=angr.Project(path_to_binary,auto_load_libs=False)
initial_state=project.factory.entry_state()
simulation=project.factory.simgr(initial_state)

find_address=0x4008CC
simulation.one_active.options.add(angr.options.LAZY_SOLVES)
simulation.explore(find=find_address)

print('time:',round(time.clock()-t,2),'s')
if simulation.found:
    solution_state=simulation.found[0]
    print(solution_state.posix.dumps(sys.stdin.fileno()))
else:
    raise Exception('Could not find the solution. _0x0_')

解得:

image-20210123225114504

有不可見字符,不過可以通過-0x80得到可見字符(估計是前面判定+和-的地方有點出入,問題不大)。

最后整理得到字符串:

key1=b'\xd23v\xe5\xd2\xd3'
for c in key1:
    if c>0x7f:
        print(chr(c-0x80),end='')
    else:
        print(chr(c),end='')
key2=[101,95,67,48,100,51]
for x in key2:
    print(chr(x),end='')

image-20210123225812567

由於要連遠程拿flag,所以復現的時候只有本地打通沒有flag,打通撒花~

image-20210123225914910

R3veRSe_C0d3


[12.27] HarmonyOS和HMS專場

難得有一場ak逆向了!(雖然有大佬帶着

有三道題都是卡着四血交,實慘TAT

re123

用file命令可以看到是MS Windows HtmlHelp Data文件(即.chm),查看文件頭也可以知道。

image-20201229191137544

image-20201229191253926

所以添加后綴名.chm。

關於chm文件有一個常用的反編譯器ChmDecompiler,可以釋放CHM里面的全部源文件(包括網頁、文本、圖片、CHM、ZIP、EXE等全部源文件),並且完美地恢復源文件的全部目錄結構 (摘抄的簡介

所以用ChmDecompiler打開re.chm,解壓縮,可以看到目錄下出現一個包含四個文件的文件夾(其實源文件只有三個,.hhp是ChmDecompiler自動生成的)。

image-20201229191554582

一個一個翻可以看到doc.htm里有一段奇怪的Item1。

image-20201229192220277

大概可以看到是powershell的語法?(感覺像win后門,這么多no的參數

查了一下其實就是把后面那大段進行base64解碼而已,用wsl解一下base64有

img

然后得到了一段.NET代碼(白字)。

通過查微軟文檔可以知道,這里是把base64解碼以后的字符進行Deflate解壓的過程,所以用腳本把中間那段base64解碼,並整理輸出。

import base64
import zlib
 
def deflate(data):
    try:
        return zlib.decompress(data, -zlib.MAX_WBITS)
    except zlib.error:
        return zlib.decompress(data)

code='TY5BC4IwGIbvgv9hjB2McJhEhNChJMGTkN2qg7qvFHQT/bL575vpoV2/53n2skJJBInkQG5xwqOqhkcQXCATx7q+gkaHsvYj7kIVvCgburItVgm9MTxbVB5LATp5OlQvb6IMV0LdQvdPpu+8x66SL2eOrMl+Ck7naUA69ggND5UcoEOzI+pUc8p62G3TRZubv34K6IbLespADoGR27vv+R7HpqXzt8Q9y0IJI5N8RLCtLw=='
de_code=deflate(base64.b64decode(code)).decode()
for x in de_code.split('\r\n'):
    print(x)

image-20201229193302890

很明顯的邏輯了,把doc.chm(應該是原來的re.chm)中"xxxxxxxx"后面的部分提取出來,還是用base64解碼得到文件。

img

把這后面的內容手動復制出來到cont.txt里,進行base64解碼,最后存在theFile中。

base64 -d cont.txt > theFile

查看theFile可以猜測是exe(畢竟最開始給的就是有powershell指令的base64),把文件頭補上,並改后綴名(即theFile.exe)。

img

用ida打開,通過FindCrypt插件可以看到AES,跟過去能看到AES加密時的S盒(其實這里前兩個都是S盒,第三個是逆S盒),猜測用到了AES加密。

image-20201229195213434

image-20201229195456197

往上回溯找到主函數

image-20201229195646671

顯然,這里是AES加密過程,sub_180001100()是密鑰拓展過程,sub_1800015B0()是AES加密。

關於逆向中各種常見密碼的記錄,指路:對稱加密算法&&Hash算法 文檔 | feng's blog

看了一下感覺是原裝無魔改的AES,密文密鑰都給了,那就直接寫腳本解密。

注意這里是以整數形式給出的,別忘了小端序。

from Crypto.Cipher import AES
from binascii import *

arr=[0x16157E2B,0xA6D2AE28,0x8815F7AB,0x3C4FCF09]
key=""
for i in range(4):
    key=hex(arr[i])[2:]+key
key=unhexlify(key)[::-1] #注意小端序的問題
tmp=0x46C42084AA2A1B56E799D643453FF4B5
cipher=unhexlify(hex(tmp)[2:])[::-1]
enc=AES.new(key,AES.MODE_ECB)
print(enc.decrypt(cipher))

image-20201229202801230

flag{youcangues}

puzzle

mips架構。

加載進ida以后,通過字符串回溯找到主函數。

image-20201229203258179

image-20201229203319240

可以看到很明顯的sub_401134()這個check,先往這里面看。

image-20201229203405086

看到是一個疑似maze的邏輯(

不過sub_400FA8()點進去以后可以看到是swap的功能

image-20201229203651570

所以應該不是maze,是一個以交換為主的邏輯。

至於dword_4A0010,可以看到是一個九個數的數組。

image-20201229203733240

v4和v5的出處在switch邏輯上面一點

image-20201229204029832

可以看到最后(v4,v5)其實表示了數組里0的位置,且數組實際可以看成是3*3。

即:

4 0 3
7 2 6
8 1 5

最后sub_400FFC()的檢查邏輯:

image-20201229204442580

實際上就是要讓這個3*3等於

1 2 3
4 5 6
7 8 0

把0看成空位的話,很容易就想到3*3的華容道了。

(或者玩算法的小伙伴可能對八數碼問題這個名字更熟悉?

有本事下次出數織啊!20*20我都給你火速解出來(來自數織愛好者的吐槽)

這里實際上是求最短能得到的路徑(15步),懶得想了,直接去網上抓了個現成代碼下來改了改。

八數碼問題的代碼見:八數碼問題-A*(AStar)算法實現_Broken Geeker-CSDN博客

#include <iostream>
#include <vector>
#include <ctime>
#include <cstdlib>
#define maxState 10000
#define N 3
using namespace std;

bool isEqual(int a[N][N][maxState],int b[N][N],int n){
    for(int i = 0;i < N;i ++){
        for(int j = 0;j < N;j ++){
            if(a[i][j][n] != b[i][j]) return false;
        }
    }
    return true;
}
bool isEqual(int a[N][N],int b[N][N]){
    for(int i = 0;i < N;i ++){
        for(int j = 0;j < N;j ++){
            if(a[i][j] != b[i][j]) return false;
        }
    }
    return true;
}
int evalute(int state[N][N],int target[N][N]){
    int num = 0;
    for(int i = 0;i < N;i ++){
        for(int j = 0;j < N;j ++)
            if(state[i][j] != target[i][j]) num ++;
    }
    return num;
}
void findBrack(int a[N][N],int x,int y){
    for(int i = 0;i < N;i ++){
        for(int j = 0;j < N;j ++){
            if(a[i][j] == 0) {
                x = i;y = j;return;
            }
        }
    }
}
bool move(int a[N][N],int b[N][N],int dir){
    //1 up 2 down 3 left 4 right
    int x = 0,y = 0;
    for(int i = 0;i < N;i ++){
        for(int j = 0;j < N;j ++){
            b[i][j] = a[i][j];
            if(a[i][j] == 0) {
                x = i;y = j;
            }
        }
    }
    if(x == 0 && dir == 1) return false;
    if(x == N-1 && dir == 2) return false;
    if(y == 0 && dir == 3) return false;
    if(y == N-1 && dir == 4) return false;
    if(dir == 1){b[x-1][y] = 0;b[x][y] = a[x-1][y];}
    else if(dir == 2){b[x+1][y] = 0;b[x][y] = a[x+1][y];}
    else if(dir == 3){b[x][y-1] = 0;b[x][y] = a[x][y-1];}
    else if(dir == 4){b[x][y+1] = 0;b[x][y] = a[x][y+1];}
    else return false;
    return true;
}
void statecpy(int a[N][N][maxState],int b[N][N],int n){
    for(int i = 0;i < N;i ++){
        for(int j = 0;j < N;j ++){
            a[i][j][n] = b[i][j];
        }
    }
}
void getState(int a[N][N][maxState],int b[N][N],int n){
    for(int i = 0;i < N;i ++){
        for(int j = 0;j < N;j ++){
            b[i][j] = a[i][j][n];
        }
    }
}
void statecpy(int a[N][N],int b[N][N]){
    for(int i = 0;i < N;i++){
        for(int j = 0;j < N;j++)
            a[i][j] = b[i][j];
    }
}
int checkAdd(int a[N][N][maxState],int b[N][N],int n){
    for(int i = 0;i < n;i ++){
        if(isEqual(a,b,i)) return i;
    }
    return -1;
}
int Astar(int a[N][N][maxState],int start[N][N],int target[N][N],int path[maxState]){
    bool visited[maxState] = {false};
    int fitness[maxState] = {0};
    int passLen[maxState] = {0};
    int curpos[N][N];
    statecpy(curpos,start);
    int id = 0,Curid = 0;
    fitness[id] = evalute(curpos,target);
    statecpy(a,start,id++);
    while(!isEqual(curpos,target)){
        for(int i = 1;i < 5;i ++){//向四周找方向
            int tmp[N][N] = {0};
            if(move(curpos,tmp,i)){
                int state = checkAdd(a,tmp,id);
                if(state == -1){//not add
                    path[id] = Curid;
                    passLen[id] = passLen[Curid] + 1;
                    fitness[id] = evalute(tmp,target) + passLen[id];
                    statecpy(a,tmp,id++);
                }else{//add
                    int len = passLen[Curid] + 1,fit = evalute(tmp,target) + len;
                    if(fit < fitness[state]){
                        path[state] = Curid;
                        passLen[state] = len;
                        fitness[state] = fit;
                        visited[state] = false;
                    }
                }
            }
        }
        visited[Curid] = true;
        //找到適應度最小的最為下一個帶搜索節點
        int minCur = -1;
        for(int i = 0;i < id;i ++)
            if(!visited[i] && (minCur == -1 || fitness[i] < fitness[minCur])) minCur = i;
        Curid = minCur;
        getState(a,curpos,Curid);
        if(id == maxState) return -1;
    }
    return Curid;
}
void show(int a[N][N][maxState],int n){
    cout << "-------------------------------\n";
    for(int i = 0;i < N;i ++){
        for(int j =0;j < N;j ++){
            cout << a[i][j][n] << " ";
        }
        cout << endl;
    }
    cout << "-------------------------------\n";
}
int calDe(int a[N][N]){
    int sum = 0;
    for(int i = 0;i < N*N;i ++){
        for(int j = i+1;j < N*N;j ++){
            int m,n,c,d;
            m = i/N;n = i%N;
            c = j/N;d = j%N;
            if(a[c][d] == 0) continue;
            if(a[m][n] > a[c][d]) sum ++;
        }
    }
    return sum;
}
void autoGenerate(int a[N][N]){
    int maxMove = 50;
    srand((unsigned)time(NULL));
    int tmp[N][N];
    while(maxMove --){
        int dir = rand()%4 + 1;
        if(move(a,tmp,dir)) statecpy(a,tmp);
    }
}
int main(){
    int a[N][N][maxState] = {0};
    // int start[N][N] = {1,2,3,4,5,6,7,8,0};
    // autoGenerate(start);
    // cout << start[0][0] << start[1][1];
    int start[N][N] = {4,0,3,7,2,6,8,1,5};
    int target[N][N] = {1,2,3,4,5,6,7,8,0};
    if(!(calDe(start)%2 == calDe(target)%2)){
        cout << "無解\n";
        return 0;
    }
    int path[maxState] = {0};
    int res =  Astar(a,start,target,path);
    if(res == -1){
        cout << "達到最大搜索能力\n";
        return 0;
    }
    int shortest[maxState] = {0},j = 0;
    while(res != 0){
        shortest[j++] = res;
        res = path[res];
    }
    cout << "第 0 步\n";
    show(a,0);
    for(int i = j - 1;i >= 0;i --){
        cout << "第 " << j-i << " 步\n";
        show(a,shortest[i]);
    }
    return 0;
}

得到每一步的情況,進而根據switch寫出路徑。

第 0 步
-------------------------------
4 0 3
7 2 6
8 1 5
-------------------------------
第 1 步
-------------------------------
4 2 3
7 0 6
8 1 5
-------------------------------
第 2 步
-------------------------------
4 2 3
7 1 6
8 0 5
-------------------------------
第 3 步
-------------------------------
4 2 3
7 1 6
8 5 0
-------------------------------
第 4 步
-------------------------------
4 2 3
7 1 0
8 5 6
-------------------------------
第 5 步
-------------------------------
4 2 0
7 1 3
8 5 6
-------------------------------
第 6 步
-------------------------------
4 0 2
7 1 3
8 5 6
-------------------------------
第 7 步
-------------------------------
4 1 2
7 0 3
8 5 6
-------------------------------
第 8 步
-------------------------------
4 1 2
7 5 3
8 0 6
-------------------------------
第 9 步
-------------------------------
4 1 2
7 5 3
0 8 6
-------------------------------
第 10 步
-------------------------------
4 1 2
0 5 3
7 8 6
-------------------------------
第 11 步
-------------------------------
0 1 2
4 5 3
7 8 6
-------------------------------
第 12 步
-------------------------------
1 0 2
4 5 3
7 8 6
-------------------------------
第 13 步
-------------------------------
1 2 0
4 5 3
7 8 6
-------------------------------
第 14 步
-------------------------------
1 2 3
4 5 0
7 8 6
-------------------------------
第 15 步
-------------------------------
1 2 3
4 5 6
7 8 0
-------------------------------

6 左
2 上
4 右
8 下
// 884226886224488

路徑為“884226886224488”。

接下來看主函數里check上面的部分,看到sub_409070()實際上是一個scanf,而dword_4A1B60是我們的輸入,也就是最后的flag,中間對輸入進行處理以后才得到“884226886224488”這個字符串。

在里面翻可以翻到一個sub_400B58(),猜測是base64換表編碼。

image-20201229205535378

於是嘗試寫腳本編碼。

import base64
b64table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
mytable=""
offset=-18
for i in range(len(b64table)):
    mytable+=b64table[(i+offset)%len(b64table)]
text="884226886224488".encode()
cipher=base64.b64encode(text).decode().translate(str.maketrans(b64table,mytable))
print(cipher)

image-20201229210535246

試試能不能過check。

wsl運行:(要裝qemu才能執行,畢竟特殊架構。

cp $(which qemu-mips) .
./qemu-mips -L . ./puzzle

執行mips程序,輸入腳本中解出的字符串,發現成功了,get flag。

image-20201229210455631

flag{8xOi6R2k8xOk6R2i7xOm}

aRm

arm架構。

照例通過字符串回溯找到主函數。

image-20201229220910936

image-20201229222635806

v1是key,v9是輸入的flag,對輸入的限制就是長度為42且頭尾是“flag{”和“}”。

動態調一下可以發現,sub_27770()這個函數實際上是把unk_723A0數組里的42個數據復制到v8里。

./qemu-arm -L ./ -g 12345 ./aRm

(Debugger選Remote GDB debugger,把端口號填上就好,其余配置具體見RE套路 - 關於使用IDA 7.0前端進行的動態調試 | c10udlnk_Log中調試elf部分。

現在我們未知的數就剩v5和v6了,v5要看sub_1714C()的輸出,v6這里相當於是42條42元一次方程組(輸入未知的情況下)。

而sub_105B4()是輸出42個結果,於是可以知道只要輸出了output.txt里的42個數就是正確的flag了。

由於前面有一個sub_169AC(key),這邊又是一個無參的sub_1714C()+1,於是猜測是srand(seed)和rand()。

為了證明猜測,多次運行程序輸入同一個key和相同/不同的flag,發現每一次的v5是一樣的,結合rand()的偽隨機性,確定這就是隨機函數。

由於key只有一字節(0~255),干脆直接爆破。把output.txt的數據讀入,用sympy庫解方程,只要第一個解x0等於ord('f')^v8[0]=102^0xA0=198,就說明這個key有極大可能性是正確的key。

當然,在此之前,我們得先知道每一次的v5(即方程的系數)是多少。

於是hook函數,在v5生成之后復用程序原來就有的print函數及格式符,把每次生成的v5都打印出來。

還記得有個函數是可以輸出八位十六進制數的吧,就是那個sub_105B4(),我們可以用這里面的printf,然后把調用這個函數的地方nop掉(目標要明確,現在是為了爆破key,沒必要管程序的正常性hahah)。

本來是想自己堆個調用printf出來的,不知道為什么keypatch對LDR R0, =a08x解釋不了,於是只好繞個小路了。

image-20201230134100558

轉到匯編窗口,記一下這里的loc,等會要跳過來的。

image-20201230134235154

看回去原來二重循環里出v5那個地方

image-20201230134326684

這幾條語句的意思就是f5里面的那行v5 = (unsigned __int8)(sub_1714C() + 1);,我們從再下一行開始改。

注意可以改的范圍在藍框這里,這是我們不需要的v6[j] += (unsigned __int8)v9[k] * v5;,在這個范圍里可以盡情修改,剩下的nop掉。

image-20201230134904926

用keypatch直接輸入匯編,patch后面的語句為

image-20201230135257443

(其實就是改了一行B loc_105D4,剩下的直接Fill with NOPs就好)

接下來去往loc_105D4,改造一下。

我們知道,現在R3寄存器里實際上存的是v5的值,我們調用printf直接輸出R3的值就能達成目標。

在ARM匯編里,函數傳參用R0、R1……所以我們這里給R1一個R3的值就好。

image-20201230135756039

這里本來就是MOV R1, R3不用改,所以直接把前面nop掉。

因為v5那里是取(unsigned __int8),所以把這里改一下,把"%08x"改成"%02x",就是出來的v5。

image-20201230141148584

別忘了后面還要跳回去,找到地址:

image-20201230140112007

patch:

image-20201230140621270

記得把調用sub_105B4()的地方也nop掉。

image-20201230140838366

最后把patch的字節保存一下。

image-20201230140754908

運行測試一下,有:

image-20201230142921253

ok,hook成功,開始爆破。

用pexpect進行批量的自動化交互見:【wp】2020ChaMd5聖誕題 | c10udlnk_Log

多虧了周五做的那個題,才有了這個題的爆破腳本(Doge。

import pexpect
from sympy import *

data=[]
with open('output.txt','r') as f:
    tmp=f.read().split('\r\n')
    data=[int(x,16) for x in tmp]
src=[0xA0, 0xE4, 0xBA, 0xFB, 0x10, 0xDD, 0xAC, 0x65, 0x8D, 0x0B, 0x57, 0x1A, 0xE4, 0x28, 0x96, 0xB3, 0x0C, 0x79, 0x4D, 0x80, 0x90, 0x99, 0x58, 0xFE, 0x50, 0xD3, 0xF9, 0x3C, 0x0F, 0xC1, 0xE3, 0xA6, 0x39, 0xC3, 0x28, 0x75, 0xF8, 0xC9, 0xC8, 0xCD, 0x78, 0x26]
flag='flag{000000000000000000000000000000000000}'

var=[]
for num in range(42):
    exec("x"+str(num)+"=Symbol('x'+str(num))")
    var.append("x"+str(num)) #創建42個變量x0~x41
for i in range(256):
    r=pexpect.spawn('./qemu-arm -L ./ ./aRm_getRand')
    r.sendline(str(i))
    r.sendline(flag)
    r.readline()
    r.readline()
    rand=[]
    for j in range(42*42):
        s=r.readline()
        rand.append(int(str(s)[2:-5],16))
    r.wait()
    exper=[]
    for j in range(42):
        anEx=""
        for k in range(42):
            anEx+=str(rand[j*42+k])+"*"+var[k]+"+"
        anEx=anEx[:-1]+"-"+str(data[j])
        exper.append(anEx)
    res=solve(exper,var)
    print(str(i)+": ")
    print(res.values())

爆破得到:

image-20201228135416441

可知key是82,而v9在xor以后的數組也爆出來了,簡單xor得flag:

arr=[0xA0, 0xE4, 0xBA, 0xFB, 0x10, 0xDD, 0xAC, 0x65, 0x8D, 0x0B, 0x57, 0x1A, 0xE4, 0x28, 0x96, 0xB3, 0x0C, 0x79, 0x4D, 0x80, 0x90, 0x99, 0x58, 0xFE, 0x50, 0xD3, 0xF9, 0x3C, 0x0F, 0xC1, 0xE3, 0xA6, 0x39, 0xC3, 0x28, 0x75, 0xF8, 0xC9, 0xC8, 0xCD, 0x78, 0x26]
x=[198, 136, 219, 156, 107, 228, 152, 7, 239, 63, 97, 127, 134, 5, 247, 131, 109, 75, 96, 180, 241, 173, 57, 211, 49, 224, 157, 9, 34, 243, 129, 199, 1, 244, 31, 17, 157, 171, 252, 249, 64, 91]
flag=""
for i in range(42):
    flag+=chr(x[i]^arr[i])
print(flag)

image-20201230002706300

flag{94bb46eb-a0a2-4a4a-a3d5-2ba877deb448}

pe

arm架構,沒環境調不動,只能硬看了XD。這題有好多奇怪的函數,而且通過偽代碼跟的話就能看到函數套函數套函數……所以基本靠猜出來的(

繼續通過字符串回溯找主函數。

image-20201230004038936

image-20201230004436275

根據參數猜測,sub_1400023C8()是strcmp()的作用,我們需要讓v9="KIMLXDWRZXTHXTHQTXTXHZWC"。

再往上走,sub_1400015B0這個函數調用了v9,於是跟進去看功能。

image-20201230085247002

感覺是某種加密,以相鄰的兩字符為一組,對這兩個字符做相同的操作,再做后續處理。

跟進sub_1400012B8()里看,可以看到大概是一個搜索的過程

image-20201230122448387

如果不等於-1就說明在表中找到了這個元素,然后返回一個索引(?

再往下看好像就看不太懂了,然后就是玄學的猜猜猜= =

回去看string可以看到一個這個,猜測是密鑰表之類的?

image-20201230123304290

往上回溯也看不到什么線索,不過可以發現這25個數字剛好沒有相同的。

現在總結一下這個古典加密算法的特點,大概是兩個為一組處理+已定義的密鑰表(即不是通過輸入生成的)5*5+處理時用到索引。

很久很久以前想寫某對cp的AU同人時想把ctf元素混進去,就看了很多簡單又奇奇怪怪的編碼/古典密碼(現代密碼太學術了XD),沒想到現在有用武之地了(手動狗頭。

安利一個編碼/古典密碼的集合:CTF中那些腦洞大開的編碼和加密 - jack_Meng - 博客園

然后翻到了一個符合這個特點的密碼,Playfair Cipher:

image-20201230124909385

不同的是密碼表是直接給出的,不過加密流程再對回ida里的反編譯感覺挺像的,於是果斷試試。

按照Playfair Cipher的加解密流程寫出腳本:

def getIndex(c):
    for i in range(len(key)):
        if key[i].find(c)!=-1:
            return i,key[i].find(c)
letter_list="ABCDEFGHJKLMNOPQRSTUVWXYZ"
key=["CREIH","TQGNU","AOVXL","DZKYM","PBWFS"]
cipher="KIMLXDWRZXTHXTHQTXTXHZWC"
text=""
for i in range(0,len(cipher),2):
    j=i+1
    x1,y1=getIndex(cipher[i])
    x2,y2=getIndex(cipher[j])
    if x1==x2:
        text+=key[x1][(y1+1)%5]+key[x2][(y2+1)%5]
    elif y1==y2:
        text+=key[(x1+1)%5][y1]+key[(x2+1)%5][y2]
    else:
        text+=key[x1][y2]+key[x2][y1]
print(text)

走一遍腳本解密可以得到:

image-20201230132557395

YES MAYBE YOU CAN RUN AN ARM PE

No, I can't 😦

看起來能讀的通,成功get flag。

flag{YESMAYBEYOUCANRUNANARMPE}

crash

用binwalk分析給的crash文件

image-20210203135834833

可以看到0x1000處有一個 ELF, 32-bit LSB executable,先把這個可執行文件用dd分離出來。

image-20210203135922549

其中if=輸入文件名(被分離的),of=輸出文件名(分離出來的),skip=跳過的塊(bs指定字節)數,count=塊長度,最后得到file_0x1000文件。

用ida打開如果出現這個warning,可以用 Reinose/Useful-IDAScripts 的方法解決。

image-20210203140139308

老樣子,通過shift+F12尋找字符串,通過交叉引用找到主函數。

image-20210203140825026

先通過一些參數和常用邏輯的猜測給系統函數命名,方便后續分析。

image-20210203141153998

然后就剩sub_8048737()sub_80487E7這兩個加密函數了。

image-20210203141241346

image-20210203141308404

第一個就很簡單啦,簡單xor。

第二個點進sub_80489E9()可以看到md5經典常數,說明這一系列大概是個md5加密(當然md5的實現還是要看看的。

image-20210203143123837

退回去,看到0x804B060這里,很像數組的始地址。

image-20210203143333398

於是轉到IDA View,按G跳到這個地址,可以看到一堆可見字符,按A轉成字符串。

image-20210203143531099

image-20210203143612250

再對回去邏輯就很明顯了,就是依次取四個字符進行md5加密,然后跟這八個字符串比。

於是exp就可以寫啦,爆破md5:

# python2
from hashlib import md5

dst=['bf2b36d56f5757c13cad80494b385e78','3fe9dbae5dc4408350500affa20074aa','1fa6770eca6b57e47a042ffe52eca8ff','1aad6b7da1122b4b5a53bf5a4d3b11b0','e7b77d9e0ab19fc9ea98154f994fccc5','75d9128cfeb61b8949664f6a067f6469','d8b0a52c64d6075017b7346140550c46','306529c7cdedfb06e27b39f7b2babf4d']
res=[]
for a in range(33,127):
    for b in range(33,127):
        for c in range(33,127):
            for d in range(33,127):
                l=[a^0x17,b^0x17,c^0x17,d^0x17]
                tmp=''.join(list(map(chr,l)))
                tmpmd5=md5(tmp).hexdigest()
                if tmpmd5 in dst:
                    s=''.join(list(map(chr,[a,b,c,d])))
                    res.append((dst.index(tmpmd5),s))
                    if len(res)==8:
                        break
            else:
                continue
            break
        else:
            continue
        break
    else:
        continue
    break
res.sort(key=(lambda x:x[0]))
flag=''.join([x[1] for x in res])
print(flag)

得到flag:

image-20210203145808703

flag{ux1cy1x4iltkahbc9nu1nk00d9akpp7w}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM