---
# System prepended metadata

title: Cyber Apocalypse CTF 2025
tags: [CTF]

---

# 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)

