【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}

posted @ 2020-12-31 23:42  c10udlnk  阅读(712)  评论(0编辑  收藏  举报