MINIRE
有upx壳,并且把upx改为gcc了
两种解法 一种是hook运算操作,尝试还原算法,我比赛时就是用的这种,但是这题校验非常难找,导致比赛时一直没找到,另一种是单字节爆破
方法一
脱壳,f5发现没法反编译
但是ghidra是可以的,它没有栈限制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | case 'X': local_20 = 0; while (local_1c = local_1c + 1, *(char *)(param_1 + local_1c) != 'x') { (&stack0xffffffffffc24328)[local_20] = *(undefined *)(param_1 + local_1c); local_20 = local_20 + 1; } (&stack0xffffffffffc24328)[local_20] = 0; if (1 < local_20) { if (in_stack_ffffffffffc24328 != 'V') { for (local_24 = 1; ((local_24 < local_20 && ('/' < (char)(&stack0xffffffffffc24328)[local_24])) && ((char)(&stack0xffffffffffc24328)[local_24] < ':')); local_24 = local_24 + 1) { } } iVar7 = (int)in_stack_ffffffffffc24328; if (iVar7 == 0x20) { bVar1 = (&DAT_005175a0)[DAT_00768090]; bVar5 = FUN_00405c10(&stack0xffffffffffc24329); (&DAT_005175a0)[DAT_00768090] = bVar1 ^ bVar5; iVar6 = DAT_00768094; } else if (((0x1f < iVar7) && (iVar7 < 0x6b)) && (0x40 < iVar7)) { switch(iVar7) { case 0x41: cVar2 = (&DAT_005175a0)[DAT_00768090]; cVar4 = FUN_00405c10(&stack0xffffffffffc24329); (&DAT_005175a0)[DAT_00768090] = cVar2 * cVar4; iVar6 = DAT_00768094; break; case 0x4a: iVar7 = FUN_00405c10(&stack0xffffffffffc24329); iVar6 = DAT_00768094; if (iVar7 != 0) { cVar2 = (&DAT_005175a0)[DAT_00768090]; cVar4 = FUN_00405c10(&stack0xffffffffffc24329); (&DAT_005175a0)[DAT_00768090] = cVar2 + cVar4; iVar6 = DAT_00768094; } break; case 0x51: cVar2 = (&DAT_005175a0)[DAT_00768090]; cVar4 = FUN_00405c10(&stack0xffffffffffc24329); (&DAT_005175a0)[DAT_00768090] = cVar2 - cVar4; iVar6 = DAT_00768094; break; case 0x54: bVar1 = (&DAT_005175a0)[DAT_00768090]; bVar5 = FUN_00405c10(&stack0xffffffffffc24329); (&DAT_005175a0)[DAT_00768090] = bVar1 | bVar5; iVar6 = DAT_00768094; break; case 0x55: uVar3 = FUN_00405c10(&stack0xffffffffffc24329); (&DAT_005175a0)[DAT_00768090] = uVar3; iVar6 = DAT_00768094; break; case 0x5f: cVar2 = (&DAT_005175a0)[DAT_00768090]; cVar4 = FUN_00405c10(&stack0xffffffffffc24329); (&DAT_005175a0)[DAT_00768090] = cVar2 + cVar4; iVar6 = DAT_00768094; break; case 0x61: iVar7 = FUN_00405c10(&stack0xffffffffffc24329); iVar6 = DAT_00768094; if (iVar7 != 0) { bVar1 = (&DAT_005175a0)[DAT_00768090]; bVar5 = FUN_00405c10(&stack0xffffffffffc24329); (&DAT_005175a0)[DAT_00768090] = bVar1 / bVar5; iVar6 = DAT_00768094; } break; case 0x6a: bVar1 = (&DAT_005175a0)[DAT_00768090]; bVar5 = FUN_00405c10(&stack0xffffffffffc24329); (&DAT_005175a0)[DAT_00768090] = bVar1 & bVar5; iVar6 = DAT_00768094; |
对应的找到各个运算的位置下断点,很容易就能得到加密逻辑
xor: 0x66,0x4 add: 0xC,0x62,0xC xor: 0x6E,0xB xor: 0x65,0xC sub: 0x69,0x2 sub: 0x67,0x1 xor: 0x66,0x6 add: 0xA,0x6C,0xA xor: 0x76,0xC add: 0x5,0x7A,0x5 add: 0x4,0x61,0x4 add: 0xE,0x65,0xE sub: 0x67,0xA sub: 0x5D,0x3 add: 0xD,0x5A,0xD add: 0x7,0x67,0x7 sub: 0x6E,0x9 xor: 0x65,0xE sub: 0x7B,0x7 sub: 0x74,0xF xor: 0x30,0xE xor: 0x31,0x2 add: 0x0,0x33,0x0 xor: 0x33,0x2 add: 0x9,0x31,0x9 add: 0x3,0x3A,0x3 sub: 0x3D,0x2 add: 0xD,0x3B,0xD xor: 0x48,0x1 add: 0x8,0x32,0x8 add: 0xC,0x3A,0xC xor: 0x46,0xF add: 0x5,0x49,0x5 xor: 0x4E,0x3 add: 0x8,0x4D,0x8 add: 0x8,0x33,0x8 add: 0x2,0x3B,0x2 xor: 0x3D,0x8 sub: 0x35,0xC add: 0xB,0x29,0xB sub: 0x34,0x3 xor: 0x31,0x9 sub: 0x38,0x2 add: 0x8,0x36,0x8 sub: 0x34,0x0 sub: 0x34,0x2 add: 0xB,0x35,0xB sub: 0x36,0x2 xor: 0x34,0x7 sub: 0x33,0x1 sub: 0x32,0x5 add: 0x1,0x37,0x1 sub: 0x38,0xD add: 0xC,0x2B,0xC sub: 0x38,0x2 sub: 0x36,0xE sub: 0x28,0x9 xor: 0x1F,0xE add: 0x1,0x11,0x1 add: 0xA,0x12,0xA sub: 0x1C,0x7 sub: 0x15,0x1 add: 0x9,0x14,0x9 sub: 0x39,0x0 sub: 0x39,0xB xor: 0x2E,0x4 add: 0x9,0x2A,0x9 xor: 0x61,0xE add: 0x5,0x6F,0x5 sub: 0x74,0xC sub: 0x68,0x4 sub: 0x64,0x4 add: 0xF,0x60,0xF add: 0xE,0x6F,0xE sub: 0x7D,0x4 xor: 0x79,0x5 sub: 0x7C,0xC xor: 0x62,0xB add: 0xD,0x69,0xD sub: 0x76,0xB xor: 0x6B,0x4 add: 0xA,0x6F,0xA sub: 0x79,0x8 add: 0xA,0x63,0xA add: 0xF,0x6D,0xF add: 0x2,0x7C,0x2 sub: 0x7E,0x6 sub: 0x78,0x9 sub: 0x6F,0x9 add: 0x3,0x66,0x3 add: 0x5,0x69,0x5 sub: 0x6E,0xF add: 0x2,0x5F,0x2 sub: 0x64,0xE sub: 0x56,0xD xor: 0x65,0x7 sub: 0x62,0xF sub: 0x53,0x1 xor: 0x52,0x3 sub: 0x51,0x3 add: 0xB,0x4E,0xB add: 0xA,0x59,0xA add: 0xA,0x63,0xA add: 0x8,0x66,0x8 sub: 0x6E,0x6 sub: 0x68,0x7 xor: 0x61,0x4 sub: 0x65,0x1 xor: 0x64,0x3 add: 0x5,0x30,0x5 add: 0x2,0x35,0x2 add: 0xA,0x37,0xA xor: 0x41,0x0 sub: 0x31,0x7 add: 0x0,0x2A,0x0 add: 0x8,0x2A,0x8 add: 0x1,0x32,0x1 add: 0x1,0x33,0x1 xor: 0x34,0xA sub: 0x3E,0xE xor: 0x30,0xF sub: 0x3F,0x7 xor: 0x38,0x8 xor: 0x32,0x4 sub: 0x36,0x9 add: 0xF,0x2D,0xF add: 0xC,0x33,0xC xor: 0x3F,0x2 xor: 0x3D,0x4 sub: 0x39,0x7 add: 0x1,0x32,0x1 add: 0x0,0x34,0x0 add: 0x9,0x34,0x9 xor: 0x3D,0x2 add: 0xE,0x3F,0xE xor: 0x4D,0xB xor: 0x46,0x1 add: 0x2,0x47,0x2 add: 0x9,0x49,0x9 sub: 0x52,0x9 add: 0x6,0x35,0x6 xor: 0x36,0xA sub: 0x3C,0x4 sub: 0x38,0xD xor: 0x2B,0x8 sub: 0x23,0xE add: 0xE,0x15,0xE xor: 0x23,0xE add: 0x1,0x37,0x1 xor: 0x38,0x4 sub: 0x3C,0x9 add: 0x9,0x33,0x9 sub: 0x3C,0x5 add: 0xD,0x37,0xD add: 0x1,0x44,0x1 xor: 0x45,0xA xor: 0x38,0x9 add: 0xE,0x39,0xE add: 0xC,0x47,0xC xor: 0x53,0x0 sub: 0x53,0x0 sub: 0x53,0xF xor: 0x44,0xB sub: 0x4F,0xB add: 0x8,0x44,0x8 add: 0xB,0x61,0xB add: 0x6,0x6C,0x6 add: 0x1,0x72,0x1 xor: 0x73,0xC sub: 0x7F,0x6 sub: 0x79,0xE sub: 0x62,0xB xor: 0x57,0xD add: 0x5,0x5A,0x5 xor: 0x5F,0x2 sub: 0x5D,0x2 add: 0xD,0x63,0xD xor: 0x70,0x6 add: 0xF,0x76,0xF xor: 0x85,0x0 sub: 0x85,0x3 sub: 0x82,0xC sub: 0x76,0xF xor: 0x67,0xB add: 0x9,0x64,0x9 add: 0xA,0x6D,0xA add: 0xA,0x77,0xA xor: 0x81,0xA sub: 0x65,0xD xor: 0x66,0x7 sub: 0x61,0x0 sub: 0x7D,0xB add: 0xE,0x72,0xE sub: 0x80,0x8 add: 0x4,0x78,0x4 add: 0x3,0x7C,0x3 add: 0xD,0x7F,0xD xor: 0x8C,0x3 xor: 0x8F,0xA
这里打印的时候可以格式化一下,便于后续写脚本,因为没想到能直接写z3()
参考0xcafebabe师傅的题解给出的脚本格式,会非常方便搞成z3,但是这里需要看出来eax - 46 就是index
1 2 3 4 5 | import ida_dbgindex = ida_dbg.get_reg_value('RAX') - 0x46# 获取 R8D 寄存器的值EDX_value = ida_dbg.get_reg_val("EDX")print(f"x[{value}]^= 0x{EDX_value:X}") |
接着就是找密文或者校验逻辑了,但是比赛卡这地方卡了一下午ww
后来将多个点全hook,并根据已知正确的前五个字符'flag{'的加密结果,可以发现其读的方式


猜测应该是存于某一个连续内存的,hook方式如下

1 2 3 | import ida_dbgEAX_value = ida_dbg.get_reg_val("EAX")print(f"case 69: 0x{EAX_value:X}") |
1 2 3 4 5 | import ida_dbgEAX_value = ida_dbg.get_reg_val("EAX")print(f"case 69: 0x{EAX_value:X}")if EAX_value == 0X99: fuck |
通过上述脚本可断于特定位置,跟以下发现了以下内存块存储了enc

然后即可用z3解了
脚本可参考https://blog.hxzzz.asia/,属实是懒得写z3了
方法二
比赛的时候原本是有想用的,但是看波动范围比较大就没用,下来试了下发现没问题
插桩点分别是handler与数据清理出

hook.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | var number = 0function main(){ var base = Module.findBaseAddress("minire") if(base){ // MemoryAccessMonitor.enable([{ base: base.add(0x1175Ec), size: 6 }], { // onAccess: function(details) { // number += 1 // } // }); Interceptor.attach(base.add(0x2B57), { onEnter: function(args) { number += 1 } }); Interceptor.attach(base.add(0x60F0), { onEnter: function(args) { send(number) Thread.sleep(0.001) } }); }}setImmediate(main); |
brute.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | import subprocessimport fridaimport sysimport timevisible_chars = [ ' ', '!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~']# 定义全局变量number = 0flaglen = 32filename = "./minire" # 在 Linux 中,确保此文件是可执行文件flag = bytearray(b'flag{' + b'!'*32 + b'}') # 初始 flag 值jscode = open("hook.js", "rb").read().decode()new_number = 0result = 0def test(F): def on_message(message, data): global result if message['type'] == 'send': result = message['payload'] else: print(message) # 写入输入数据 process = subprocess.Popen([filename,"root"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) time.sleep(0.1) session = frida.attach("minire") script = session.create_script(jscode) script.on('message', on_message) script.load() process.stdin.write(F.decode()) # 读取输出并终止进程 output, error = process.communicate() process.terminate() session.detach() return resultmax_number = 0right_chr = 0flag = bytearray(b'flag{' + b'!' * 32 + b'}')for i in range(flaglen): for j in visible_chars: flag[5+i] = ord(j) number = test(flag) if number >= max_number: max_number = number right_chr = ord(j) flag[5+i] = right_chr max_number = 0 right_chr = 0 print(flag) print(number) |
继续学习一下楼主别的帖子