# Cyber Apocalypse CTF 2025 ## Reverse ### singlestep - Đầu tiên ta run thử chương trình và mở vào trong ida xem sao. ![image](https://hackmd.io/_uploads/rkDIdnWa1l.png) ![image](https://hackmd.io/_uploads/SJbWKhWp1x.png) - Ta dễ dàng nhận thấy đây là một dạng bài self-modify code. Đầu tiên nó `pushfq` để lưu trạng thái thanh ghi lúc đó sau đó xor để decode câu lệnh và `popfq` để trả về trạng thái thanh ghi lúc đầu. - Sau khi thực thi câu lệnh được giải mã thì nó sẽ mã hóa lại. Và cứ như đến hết chương trình - Dựa vào `pushfq` và `popfq` ta có thể viết một đoạn code để decode và patch lại ```py= import idaapi import idc with open("log.txt", "w") as f: start = 0x0000000000001CA0 end = 0x0000000000002161 addr = set() current_addr = start while current_addr <= end: idc.del_items(current_addr, 0, 1) idc.create_byte(current_addr) current_addr += 1 current_addr = start while current_addr <= end: if idc.create_insn(current_addr): instruction_size = idc.get_item_size(current_addr) mnemonic = idc.print_insn_mnem(current_addr).lower() if mnemonic in ("pushfq", "popfq"): current_addr += instruction_size else: current_addr += 1 else: current_addr += 1 def patch_nop(start, size): for i in range(size): idc.patch_byte(start + i, 0x90) current_addr = start checking = False pushfq_addr = [] popfq_addr = [] index = 0 while current_addr <= end: if current_addr in addr: if idc.create_insn(current_addr): current_addr += idc.get_item_size(current_addr) else: current_addr += 1 continue if not idc.create_insn(current_addr): current_addr += 1 continue instruction_size = idc.get_item_size(current_addr) mnemonic = idc.print_insn_mnem(current_addr).lower() if mnemonic == "pushfq": pushfq_addr.append(current_addr) checking = True index += 1 elif mnemonic == "popfq": popfq_addr.append(current_addr) checking = False if mnemonic in ("pushfq", "popfq"): current_addr += instruction_size continue if index % 2 == 1 and checking and mnemonic == "xor": try: disasm = idaapi.generate_disasm_line(current_addr, 0).lower() if "dword" in disasm and instruction_size == 10: addr_patch = (idc.get_wide_dword(current_addr + 2) + current_addr + instruction_size) & 0xffff if addr_patch not in addr: addr.add(addr_patch) number_xor = idc.get_wide_dword(current_addr + 6) number_patch = idc.get_wide_dword(addr_patch) number_patch ^= number_xor idc.patch_dword(addr_patch, number_patch) f.write(f"Patched DWORD at {hex(addr_patch)}: {hex(number_patch)}\n") elif "word" in disasm and instruction_size == 9: addr_patch = (idc.get_wide_word(current_addr + 3) + current_addr + instruction_size) & 0xffff if addr_patch not in addr: addr.add(addr_patch) number_xor = idc.get_wide_word(current_addr + 7) number_patch = idc.get_wide_word(addr_patch) number_patch ^= number_xor idc.patch_word(addr_patch, number_patch) f.write(f"Patched WORD at {hex(addr_patch)}: {hex(number_patch)}\n") elif "byte" in disasm and instruction_size == 7: addr_patch = (idc.get_wide_dword(current_addr + 2) + current_addr + instruction_size) & 0xffff if addr_patch not in addr: addr.add(addr_patch) number_xor = idc.get_wide_byte(current_addr + 6) number_patch = idc.get_wide_byte(addr_patch) number_patch ^= number_xor idc.patch_byte(addr_patch, number_patch) f.write(f"Patched BYTE at {hex(addr_patch)}: {hex(number_patch)}\n") except Exception as e: print(f"Error at {hex(current_addr)}: {e}") current_addr += instruction_size continue current_addr += instruction_size for i in range(len(pushfq_addr)): patch_nop(pushfq_addr[i], popfq_addr[i] - pushfq_addr[i]+1) print("Patching complete.") ``` - Đoạn code trên của mình vẫn bị lỗi ở chỗ này, tuy nhiên vẫn có đọc code được ![image](https://hackmd.io/_uploads/Hy7P93bp1g.png) - Đây là đoạn code chính: ```c= unsigned __int64 __fastcall sub_43E0() { __int64 v0; // rbp int v1; // eax bool v2; // al int v3; // eax bool v5; // [rsp-29Eh] [rbp-29Eh] bool v6; // [rsp-29Dh] [rbp-29Dh] int v7; // [rsp-29Ch] [rbp-29Ch] int i; // [rsp-298h] [rbp-298h] int j; // [rsp-294h] [rbp-294h] int k; // [rsp-290h] [rbp-290h] int m; // [rsp-28Ch] [rbp-28Ch] int n; // [rsp-288h] [rbp-288h] int v13; // [rsp-284h] [rbp-284h] __int64 v14; // [rsp-280h] [rbp-280h] BYREF mt v15; // [rsp-278h] [rbp-278h] BYREF mt v16; // [rsp-258h] [rbp-258h] BYREF mt v17; // [rsp-238h] [rbp-238h] BYREF _BYTE v18[256]; // [rsp-218h] [rbp-218h] BYREF _BYTE v19[264]; // [rsp-118h] [rbp-118h] BYREF unsigned __int64 v20; // [rsp-10h] [rbp-10h] __int64 v21; // [rsp-8h] [rbp-8h] v21 = v0; v20 = __readfsqword(0x28u); memset(v18, 0, sizeof(v18)); memset(v19, 0, 256); sub_1820(&v15, 4LL, 4LL); sub_1820(&v16, 4LL, 4LL); sub_1820(&v17, 4LL, 4LL); mov(&v15.rows, 0LL, 0LL, 88LL); mov(&v15.rows, 0LL, 1uLL, -17LL); mov(&v15.rows, 0LL, 2uLL, 19LL); mov(&v15.rows, 0LL, 3uLL, -57LL); mov(&v15.rows, 1uLL, 0LL, 45LL); mov(&v15.rows, 1uLL, 1uLL, -9LL); mov(&v15.rows, 1uLL, 2uLL, 10LL); mov(&v15.rows, 1uLL, 3uLL, -29LL); mov(&v15.rows, 2uLL, 0LL, -56LL); mov(&v15.rows, 2uLL, 1uLL, 11LL); mov(&v15.rows, 2uLL, 2uLL, -12LL); mov(&v15.rows, 2uLL, 3uLL, 36LL); mov(&v15.rows, 3uLL, 0LL, -40LL); mov(&v15.rows, 3uLL, 1uLL, 8LL); mov(&v15.rows, 3uLL, 2uLL, -9LL); mov(&v15.rows, 3uLL, 3uLL, 26LL); puts(a135m); puts(&byte_1C918); puts(off_1F010); printf(a136mpleaseEnte); v13 = read(0, v18, 0x100uLL); if ( v13 >= 0 && v18[v13 - 1] == 10 ) v18[v13 - 1] = 0; if ( sub_3690(v18) != 19 ) goto LABEL_5; v7 = 0; v5 = 1; for ( i = 0; i <= 18; ++i ) { if ( i % 5 == 4 ) { v5 = v5 && v18[i] == 45; } else { v1 = v7++; v19[v1] = v18[i]; v2 = (char)v18[i] > 64 && (char)v18[i] <= 90; v5 = v5 && v2; } } if ( !v5 ) { LABEL_5: sub_3EA0(); } else { for ( j = 0; j <= 3; ++j ) { for ( k = 0; k <= 3; ++k ) mov(&v16.rows, j, k, (char)v19[4 * j + k] - 65 - k * j); } v6 = 1; sub_2630(&v15, &v16, &v17); for ( m = 0; m <= 3; ++m ) { for ( n = 0; n <= 3; ++n ) { getnum(&v17, m, n, &v14); if ( m == n ) v3 = v6 && v14 == 1; else v3 = v6 && v14 == 0; v6 = v3 != 0; } } if ( v6 ) sub_3FC0(v18); else sub_3EA0(); } return v20 - __readfsqword(0x28u); } ``` - Đọc thì ta có thể dễ dàng nhận thấy nó thực hiện nó thực hiện biến đổi input và chuyển nó thành một ma trận sau đó với ma trận được tạo trong đoạn code rồi kiểm tra ma trận đó có phải là ma trận đơn vị không. - Ta có thể giải mã như sau: ```py= import numpy as np A = np.array([[88, -17, 19, -57], [45, -9, 10, -29], [-56, 11, -12, 36], [-40, 8, -9, 26]]) if np.linalg.det(A) == 0: print("Ma trận không khả nghịch!") exit() B = np.linalg.inv(A) B = np.round(B, decimals=0).astype(int) flag = [0]*16 for j in range(4): for k in range(4): flag[j*4+k] = int(B[j][k]) + 65 + k * j for i in range(0,len(flag),4): print(bytes(flag[i:i+4]).decode(), end='-') # HTB{t00_mUcH_x0R!!} # BFCF-EJJL-CKKL-BLJQ ``` - Ta run lại và có được flag: `HTB{t00_mUcH_x0R!!}` ![image](https://hackmd.io/_uploads/r1-njnba1x.png) ### gateway - Ta sẽ mở file bằng IDA ![image](https://hackmd.io/_uploads/Sk8I23Z6Jx.png) ![image](https://hackmd.io/_uploads/rkGvhnW61x.png) - Ta thấy mã giải khá là khó đọc. Ở đây ta chú ý vào các lệnh gần `retf` (far return). Chúng ta có thể nhận thấy rằng nó sử dụng kĩ thuật `Heaven's Gate`. - **Heaven's Gate'** là kĩ thuật chạy một đoạn code 64-bit trong tiến trình 32-bit và ngược lại. - Ta biết rằng trong kiến trúc x86, thanh ghi CS đóng vai trò trong việc xác định chế độ thực thi hiện tại của bộ xử lý với giá trị của thanh ghi CS: - Trong chế độ x86 (32-bit): 0x23 - Trong chế độ x64 (64-bit): 0x33 - Dựa vào đó mã kĩ thuật này có thể thực hiện chuyển đổi giữa các chế độ - Khi `push 0x33` rồi sử dụng `retf`, thanh ghi **CS** sẽ thay đổi làm chuyển đổi chế độ trên **x86/x64**. - Để hiểu rõ hơn ta nói về cách hoạt động của lệnh `retf`. Trước khi thực hiện lệnh `retf`, stack sẽ có câu trúc như sau: ``` | IP (EIP/RIP) | <-- Top of Stack (ESP/RSP) | CS (Segment) | ``` - Khi thực hiện retf, CPU pop giá trị CS từ stack, cập nhật thanh ghi CS, rồi tiếp tục thực thi tại địa chỉ mới (IP/EIP/RIP) và như vậy đã thành công chuyển đổi chế độ. - Khi phân tích một chương trình sử dụng kĩ thuật này ta có lấy các bytecode (32 bit hoặc 64 bit) ra và ghi vào một file khác và mở bằng IDA để dễ đọc hơn hoặc có thể viết đoạn code như sau: ```py= from capstone.x86 import * shell = [ 0xF3, 0x0F, 0x1E, 0xFA, 0x55, 0x48, 0x89, 0xE5, 0x48, 0x89, 0x7D, 0xD8, 0x48, 0x89, 0x75, 0xD0, 0x48, 0xC7, 0x45, 0xE8, 0xFF, 0xFF, 0xFF, 0xFF, 0x48, 0xC7, 0x45, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xEB, 0x5D, 0x48, 0x8B, 0x55, 0xD8, 0x48, 0x8B, 0x45, 0xF0, 0x48, 0x01, 0xD0, 0x0F, 0xB6, 0x00, 0x0F, 0xB6, 0xC0, 0x48, 0x31, 0x45, 0xE8, 0x48, 0xC7, 0x45, 0xF8, 0x00, 0x00, 0x00, 0x00, 0xEB, 0x32, 0x48, 0x8B, 0x45, 0xE8, 0x83, 0xE0, 0x01, 0x48, 0x85, 0xC0, 0x74, 0x1D, 0x48, 0x8B, 0x45, 0xE8, 0x48, 0xD1, 0xE8, 0x48, 0x89, 0xC2, 0x48, 0xB8, 0x42, 0x0F, 0x87, 0xD7, 0x95, 0x57, 0x6C, 0xC9, 0x48, 0x31, 0xD0, 0x48, 0x89, 0x45, 0xE8, 0xEB, 0x04, 0x48, 0xD1, 0x6D, 0xE8, 0x48, 0x83, 0x45, 0xF8, 0x01, 0x48, 0x83, 0x7D, 0xF8, 0x07, 0x76, 0xC7, 0x48, 0x83, 0x45, 0xF0, 0x01, 0x48, 0x8B, 0x45, 0xF0, 0x48, 0x3B, 0x45, 0xD0, 0x72, 0x99, 0x48, 0x8B, 0x45, 0xE8, 0x48, 0xF7, 0xD0, 0x5D, 0xC3] # shell = [ 0xF3, 0x0F, 0x1E, 0xFA, 0x55, 0x48, 0x89, 0xE5, 0x89, 0xF8, # 0x88, 0x45, 0xEC, 0x48, 0xC7, 0x45, 0xF8, 0x00, 0x00, 0x00, # 0x00, 0x0F, 0xB6, 0x45, 0xEC, 0x48, 0x01, 0xC0, 0x25, 0xAA, # 0x00, 0x00, 0x00, 0x48, 0x89, 0x45, 0xF8, 0x0F, 0xB6, 0x45, # 0xEC, 0x48, 0xD1, 0xE8, 0x83, 0xE0, 0x55, 0x48, 0x09, 0x45, # 0xF8, 0x48, 0x8B, 0x45, 0xF8, 0x5D, 0xC3] shell = bytes(shell) md = Cs(CS_ARCH_X86, CS_MODE_64) for i in md.disasm(shell, 0x1000): print("0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str)) ``` > Tham khảo: > https://github.com/NTTITON/Malware/blob/master/Heavens%20Gate/Heavens%20Gate.c > https://sachiel-archangel.medium.com/analysis-of-heavens-gate-part-1-62cca0ace6f0 - Trở lại với bài này thì kiểm tra độ dài input bằng 33 (cả `\n`) rồi thực hiện mã hóa sau đó kiểm tra với một mảng khác. Ta có thể giải mã như sau: ```py= def cal(a1): return ((((a1) >> 1) & 0x55)) | (((2 * (a1)) & 0xaa)) def shr(value, shift, bits=64): return (value & (2**bits - 1)) >> shift flag ='12345678901234561234567890123456' flag = [ord(i) for i in flag] flag = [cal(i) for i in flag] tran_flag = flag # print(bytes(tran_flag).decode()) index = [20, 18, 27, 8, 24, 4, 16, 5, 21, 10, 16, 28, 26, 13, 23, 27, 3, 19, 28, 31, 20, 26, 0, 31, 31, 0, 24, 2, 24, 23, 28, 21] for i in range(32): tran_flag[index[i]], tran_flag[i] = tran_flag[i], tran_flag[index[i]] # print(bytes(tran_flag).decode()) import string character = string.printable cipher = [3056211200, 492570721, 1282371112, 1125303727, 1020619446, 510309723, 1020619446, 3228338623, 3980337024, 3136348648, 3215664806, 1020619446, 3017814043, 3136348648, 3789941608, 2957471604, 3017814043, 1020619446, 3215664806, 3898923214, 1282371112, 1125303727, 3017814043, 2635397451, 4002955503, 1020619446, 3215664806, 3215664806, 2635397451, 3136348648, 350043079, 2536674690] enc = [0]*32 flag = [0]*32 for i in range(32): for char in range(0xff): x = (-1) & 0xffffffffffffffff x ^= char for j in range(8): if (x & 1) == 0: x = shr(x, 1) else: x = shr(x, 1) x ^= 0xc96c5795d7870f42 x = (~x) & 0xffffffffffffffff enc[i] = x & 0xffffffff if enc[i] == cipher[i]: flag[i] = char break # print(flag) for i in range(31,-1,-1): flag[index[i]], flag[i] = flag[i], flag[index[i]] res = [0]*32 for i in range(32): for char in range(0x20, 0x80): x = cal(char) if x == flag[i]: res[i] = char break print(bytes(res).decode()) # HTB{r3tf@r_t0_tH3_h3@V3n5g@t3!!} ``` ![image](https://hackmd.io/_uploads/Bk6O4pbpkl.png) - Flag: `HTB{r3tf@r_t0_tH3_h3@V3n5g@t3!!}` ### heart protector ```py= import idaapi import ida_dbg import time def get_all_registers(): """Get all register values for x64 architecture after ensuring debugger is synchronized.""" ida_dbg.refresh_debugger_memory() # time.sleep(0.1) register_names = ["rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp", "rsp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "rip"] registers = {} for reg in register_names: value = idaapi.get_reg_val(reg) registers[reg] = value if value is not None else "N/A" return registers class VMDebugHook(ida_dbg.DBG_Hooks): def __init__(self, breakpoint_list): super().__init__() self.count = 0 self.log_file = open("log.txt", "w") self.breakpoint_list = breakpoint_list def dbg_process_start(self, pid, tid, ea, name, base, size): print(f"Process started: PID={pid}, TID={tid}, EA={hex(ea)}") self.continue_execution() return 0 def dbg_process_exit(self, pid, tid, ea, code): print(f"Process exited with code {code}. Unhooking...") self.log_file.close() self.unhook() return 0 def dbg_run_to(self, tid, ea): print(f"Running to {hex(ea)}") self.continue_execution() return 0 def dbg_bpt(self, tid, ea): """Handle breakpoint hits and log detailed VM operations.""" print(f"Breakpoint hit at {hex(ea)}, TID={tid}") post_registers = get_all_registers() registers = post_registers breakpoint_ops = { 0: f"vm->stack[{registers['rax']}] = vm->memory[{registers['rdi']}] = {registers['rcx'] & 0xff}\n", 1: f"vm->memory[{registers['rdi']}] = {registers['rax']}\n", 2: f"++vm->memory[{registers['rdi']}]\n", 3: f"--vm->memory[{registers['rdi']}]\n", 4: f"vm->memory[{registers['rdi']}] += vm->memory[{registers['rbp']}] = {registers['rax']}\n", 5: f"vm->memory[{registers['rdi']}] += {registers['r14']}\n", 6: f"vm->memory[{registers['rdi']}] -= {registers['r14']}\n", 7: f"vm->memory[{registers['rdi']}] = __ROR4__(vm->memory[{registers['rdi']}], 32 - ({registers['rbp']} & 0x1F)); | ror = {idaapi.get_dword(registers['rbx'] + registers['rdi']*4 + 0x90)} >> {registers['rcx'] & 0xff}\n", 8: f"vm->memory[{registers['rdi']}] = __ROR4__(vm->memory[{registers['rdi']}], {registers['r12']}); | ror = {idaapi.get_dword(registers['rbx'] + registers['rdi']*4 + 0x90)} >> {registers['rcx'] & 0xff}\n", 9: f"v15 = vm->memory[{registers['rbp']}]\n", 10: f"vm->memory[{registers['rdi']}] = {registers['r12']}\n", 11: f"vm->memory[{registers['r14']}] == {registers['r12']} | {idaapi.get_dword(registers['rbx'] + registers['r14']*4 + 0x90)} == {registers['r12']}\n", 12: f"vm->memory[{registers['rbp']}] == vm->memory[{registers['r13']}] | {idaapi.get_dword(registers['rbx'] + registers['rbp']*4 + 0x90)} == {registers['rax']}\n", 13: f"vm->regs[0] = vm->memory[{registers['rdi']}] == 0\n", 14: f"mov key to stack\n", 15: f"vm->memory[{registers['rdi']}] *= {registers['rbp']} | x = {(registers['rbp'] & 0xffffffff)} * {idaapi.get_dword(registers['rbx'] + registers['rdi']*4 + 0x90)}\n", 16: f"v141 = vm->memory[{registers['rbp']}] * *((_DWORD *)&vm->size + v140) * {registers['rax']} | x = {(registers['rax'] & 0xffffffff)} * {idaapi.get_dword(registers['rbx'] + registers['rbp']*4 + 0x90)}\n", 17: f"v141 = vm->memory[{registers['rbp']}] ^ *((_DWORD *)&vm->size + v140) ^ {registers['rax']} | x = {(registers['rax'] & 0xffffffff)} ^ {idaapi.get_dword(registers['rbx'] + registers['rbp']*4 + 0x90)}\n" } if ea in self.breakpoint_list: op_index = self.breakpoint_list.index(ea) op_str = breakpoint_ops[op_index] self.log_file.write(f"Operation: {op_str}") if 'rdi' in op_str and registers['rdi'] != 'N/A' and registers['rbx'] != 'N/A': mem_addr = registers['rbx'] + registers['rdi'] * 4 + 0x90 mem_val = idaapi.get_dword(mem_addr) self.log_file.write(f"Memory at {hex(mem_addr)}: {hex(mem_val)}\n") self.log_file.flush() self.continue_execution() return 0 def continue_execution(self): """Continue process execution.""" idaapi.continue_process() self.count += 1 print(f"Continued execution #{self.count}") # Setup breakpoints offset = [55260, 56824, 55522, 55627, 55799, 56113, 56288, 56470, 56665, 56817, 56982, 57145, 57398, 57588, 59709, 59889, 60107, 60290] base = 0x00007FF607701000 breakpoint_list = [base + i for i in offset] for addr in breakpoint_list: if idaapi.add_bpt(addr, 0, idaapi.BPT_SOFT): print(f"Breakpoint set at {hex(addr)}") else: print(f"Failed to set breakpoint at {hex(addr)}") try: debug_hook = VMDebugHook(breakpoint_list) if debug_hook.hook(): print("Debugger hook installed successfully.") else: print("Failed to install debugger hook.") idaapi.continue_process() except Exception as e: print(f"Error during setup: {str(e)}") ``` ```py= import struct def ror(a, r): return 0xffffffff & ((a >> r) | (a << (32-r))) def rol(a, r): return ror(a, 32-r) def fnv1(data): val = 0x811c9dc5 for c in data: val = ((0x1000193 * val) ^ c) & 0xffffffff return val def add_ror13(data): val = 0 for i in data: val += i val = ror(val, 0xd) return val def add_mul(data): val = 0 for i in range(len(data)): val = val + data[i] * (211 ** i) val -= 1869 return val ^ 9944768 key = b'a9060b622a6d95eb' key = [i for i in key] sum_key1 = fnv1(key) sum_key2 = fnv1([key[5], key[6], key[7]]) sum_key3 = fnv1([key[10], key[12], key[14]]) sum_key4 = key[4] ^ 137 sum_key_5 = add_mul(key[0:4]) sum_key_6 = add_ror13([key[11], key[13], key[15]]) # print(hex(sum_key1), hex(sum_key2), hex(sum_key3), hex(sum_key4), hex(sum_key_5), hex(sum_key_6)) # print(sum_key1, sum_key2 & 0xff, sum_key3 & 0xff, sum_key4 & 0xff, sum_key_5 & 0xff, sum_key_6 & 0xff) check_sum = [0x7586cbd1] data = open("./code.bin","rb").read() data = data[-20:] for i in range(0, len(data), 4): check_sum.append(struct.unpack("<I", data[i:i+4])[0]) assert check_sum[0] == sum_key1 assert check_sum[3] == sum_key2 assert check_sum[1] == sum_key3 assert check_sum[4] == sum_key4 assert check_sum[2] == sum_key_5 assert check_sum[5] == sum_key_6 # a9060b622a6d95eb ``` ```py= import struct def ror(a, r): return 0xffffffff & ((a >> r) | (a << (32-r))) def rol(a, r): return ror(a, 32-r) def fnv1(data): val = 0x811c9dc5 for c in data: val = ((0x1000193 * val) ^ c) & 0xffffffff return val def add_ror13(data): val = 0 for i in data: val += i val = ror(val, 0xd) return val def add_mul(data): val = 0 for i in range(len(data)): val = val + data[i] * (211 ** i) val -= 1869 return val ^ 9944768 key = b'a9060b622a6d95eb' key = [i for i in key] sum_key1 = fnv1(key) sum_key2 = fnv1([key[5], key[6], key[7]]) sum_key3 = fnv1([key[10], key[12], key[14]]) sum_key4 = key[4] ^ 137 sum_key_5 = add_mul(key[0:4]) sum_key_6 = add_ror13([key[11], key[13], key[15]]) check_sum = [0x7586cbd1] data = open("./code.bin","rb").read() data = data[-20:] for i in range(0, len(data), 4): check_sum.append(struct.unpack("<I", data[i:i+4])[0]) # assert check_sum[0] == sum_key1 # assert check_sum[3] == sum_key2 # assert check_sum[1] == sum_key3 # assert check_sum[4] == sum_key4 # assert check_sum[2] == sum_key_5 # assert check_sum[5] == sum_key_6 key_4 = 137 ^ check_sum[4] check_fnv1 = [check_sum[3],check_sum[1]] check_add_ror13 = check_sum[5] check_add_mul = check_sum[2] key_add_ror13 = "" key_fnv1 = ['']*2 import itertools char = '0123456789abcdef' for k in itertools.product(char,repeat=3): k = [ord(i) for i in k] sum_k = fnv1(k) add_ror13_k = add_ror13(k) k = "".join(chr(i) for i in k) if sum_k in check_fnv1: index = check_fnv1.index(sum_k) key_fnv1[index] = k if add_ror13_k == check_add_ror13: key_add_ror13 = k for k in itertools.product(char,repeat=4): k = [ord(i) for i in k] add_mul_k = add_mul(k) k = "".join(chr(i) for i in k) if add_mul_k == check_add_mul: key_add_mul = k key = ['']*16 key[0] = key_add_mul[0] key[1] = key_add_mul[1] key[2] = key_add_mul[2] key[3] = key_add_mul[3] key[4] = chr(key_4) key[5] = key_fnv1[0][0] key[6] = key_fnv1[0][1] key[7] = key_fnv1[0][2] key[10] = key_fnv1[1][0] key[12] = key_fnv1[1][1] key[14] = key_fnv1[1][2] key[11] = key_add_ror13[0] key[13] = key_add_ror13[1] key[15] = key_add_ror13[2] for k in itertools.product(char,repeat=2): key[8] = k[0] key[9] = k[1] k = [ord(i) for i in key] sum_key1 = fnv1(k) if sum_key1 == check_sum[0]: key = "".join(chr(i) for i in k) break print(key) # a9060b622a6d95eb ``` ```py= from Crypto.Cipher import AES key = b'a9060b622a6d95eb' cipher = open("./heart.png.malakar","rb").read() iv = cipher[:10] cipher = cipher[10:] p = AES.new(key, AES.MODE_GCM, iv) plaintext = p.decrypt(cipher) png = b'\x89PNG' + plaintext[5:] with open("heart.png", "wb") as f: f.write(plaintext) ``` ![image](https://hackmd.io/_uploads/r1xErp-ayl.png)