---
# System prepended metadata

title: Wanagame Champion

---

# Buzzing

Đề bài kêu ta chạy file readflag bằng cách sử dụng absolute path (/readflag). Tuy nhiên khi chạy nó thông báo rằng ta không có quyền!

![image](https://hackmd.io/_uploads/ry0ZMfEzbg.png)
Khi chúng ta xem xét file ta thấy readflag có quyền suid tức là cho dù mình có là user nào thì khi thực thi file này thì bắt buộc phải chạy dưới quyền root -> nên khả năng permission của file không thể hiện chuẩn được vì sao file không chạy được

Rất có thể có một cái gì đó không cho phép chạy readflag cho dù có là root!

Nhìn sơ qua ta thấy có 1 file locker.bpf.o, bpf là một bộ lọc có thể giúp linux chạy chương trình một cách an toàn hơn bằng cách có thể ban/allow một số syscall nhất định chúng ta hãy thử xem các chuỗi trong file là gì

![image](https://hackmd.io/_uploads/HkBDQfVfbe.png)

Thấy có dòng 
```
.relkprobe/__x64_sys_execve
```

Nhìn vào ta thấy kprobe attach file của ebpf
Suy đoán: từ dòng đó ta suy đoán có một bpf + kprobe hook vào syscall execve. Do đó khi chạy có thể bị chặn bởi quyền hạng bị từ chối

Vì vậy chúng ta có thể viết 1 script C để chạy readflag sử dụng open và execveat

Tuy nhiên trong máy không có gcc -> không thể viết script C được.

Kiểm tra /usr/bin để kiểm tra coi thử có ngôn ngữ nào ta có thể dùng không

![image](https://hackmd.io/_uploads/BJM5Hf4Mbx.png)

Nhìn nhanh ta thấy có perl là có thể dùng được vậy chúng ta cần viết 1 script perl

```perl
#!/usr/bin/perl
use strict;
use warnings;
use Fcntl qw(O_RDONLY);


my $path = "/readflag";
sysopen(my $fh, $path, O_RDONLY) or die "open $path: $!";

my $fd = fileno($fh);

my $arg0  = "readflag\0";  
my $empty = "\0";         
my $arg0_ptr = unpack('Q', pack('P', $arg0));

my $argv_buf = pack('Q2', $arg0_ptr, 0);
my $argv_ptr = unpack('Q', pack('P', $argv_buf));

my $envp_buf = pack('Q', 0);
my $envp_ptr = unpack('Q', pack('P', $envp_buf));

my $path_ptr = unpack('Q', pack('P', $empty));

my $SYS_execveat  = 322;     
my $AT_EMPTY_PATH = 0x1000; 

# execveat(fd, "", argv, envp, AT_EMPTY_PATH)
my $rc = syscall($SYS_execveat, $fd, $path_ptr, $argv_ptr, $envp_ptr, $AT_EMPTY_PATH);

die "execveat failed";
```

Sau đó chạy

```
perl execveat_readflag.pl
```

Nhận được flag

```
W1{just_4_s1mpl3_3bpf_l04d3r_buzz1n'_4r0und_fufu_76274bc788378a36b3345a49948045e9}
```

# Checker (Unsolved):

Bài này thì em chưa giải ra chỉ mới tìm được part 1 và part 2 của flag cũng như phân tích được các hàm mã hóa được 1 tí. Dự định là part 3 sẽ được giấu trong đống mã hóa đó hiện tại em chưa giải được em xin trình bày cách giải part 1 và part 2 của em


----

Sau khi phân tích file checker.exe nó sẽ tìm kiếm các resource trong file tương ứng với flag_checker.exe cho part 1 và part 2 + 3. Sau đó chạy flag_checker.exe sau khi chạy xong nó sẽ xóa đi file resource đó. Để giữa nguyên không cho xóa chúng ta patch lại file 

![image](https://hackmd.io/_uploads/BJS8sGEG-x.png)

Thành

![image](https://hackmd.io/_uploads/r1QwjGVG-x.png)

Sau đó khi sử dụng checker.exe file flag_checker.exe sẽ không bị xóa nữa và chúng ta có thể dịch ngược file đó.

Đầu tiên là flag_checker cho part 1

```C
int __fastcall main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // r8
  _QWORD *v4; // rax
  __int64 v5; // r8
  char v7; // [rsp+30h] [rbp-128h] BYREF
  _BYTE v8[3]; // [rsp+31h] [rbp-127h] BYREF
  unsigned int v9; // [rsp+34h] [rbp-124h]
  int v10; // [rsp+38h] [rbp-120h]
  __int64 v11; // [rsp+40h] [rbp-118h]
  _QWORD *v12; // [rsp+48h] [rbp-110h]
  __int64 v13; // [rsp+50h] [rbp-108h]
  _BYTE v14[8]; // [rsp+58h] [rbp-100h] BYREF
  _BYTE v15[16]; // [rsp+60h] [rbp-F8h] BYREF
  _BYTE dst[16]; // [rsp+70h] [rbp-E8h] BYREF
  _BYTE v17[24]; // [rsp+80h] [rbp-D8h] BYREF
  _BYTE v18[24]; // [rsp+98h] [rbp-C0h] BYREF
  _BYTE v19[24]; // [rsp+B0h] [rbp-A8h] BYREF
  _BYTE v20[24]; // [rsp+C8h] [rbp-90h] BYREF
  _BYTE v21[24]; // [rsp+E0h] [rbp-78h] BYREF
  _BYTE v22[24]; // [rsp+F8h] [rbp-60h] BYREF
  _BYTE v23[16]; // [rsp+110h] [rbp-48h] BYREF
  _BYTE v24[15]; // [rsp+120h] [rbp-38h] BYREF
  char v25; // [rsp+12Fh] [rbp-29h] BYREF

  if ( (word_140042018[2] & 1) != 0 )
    printf_more(&offset_of_printf, "Flag checker 1 !!!!!!!!!!!\n", envp);
  else
    printf_more(&offset_of_printf, "Flag checker 1 !!!!!!!!!!\n", envp);
  printf_more(&offset_of_printf, "Your flag: ", v3);
  sub_1400040C0(&qword_140043580, &unk_1400432E8);
  v11 = unknown_libname_59(&v7);
  v12 = (_QWORD *)sub_1400099D0(&unk_1400432E8, v14);
  v4 = (_QWORD *)sub_140009480(&unk_1400432E8, v15);
  sub_140003F40(v22, *v4, *v12, v11);
  sub_140001460(v21, v22, &unk_1400432A0);
  sub_140001570(v20, v21, &unk_1400432B8);
  v9 = sub_140002370(1337LL);
  sub_1400039F0(v19, v20, v9);
  sub_140001C50((unsigned int)v18, (unsigned int)v19, (unsigned int)&unk_1400432D0, (unsigned int)&unk_140043288, 0);
  v24[0] = -4;
  v24[1] = 118;
  v24[2] = -44;
  v24[3] = 9;
  v24[4] = -93;
  v24[5] = -40;
  v24[6] = 80;
  v24[7] = 47;
  v24[8] = -71;
  v24[9] = -41;
  v24[10] = -70;
  v24[11] = -32;
  v24[12] = -80;
  v24[13] = 52;
  v24[14] = -78;
  v13 = unknown_libname_59(v8);
  qmemcpy(dst, (const void *)unknown_libname_63(v23, v24, &v25), sizeof(dst));
  sub_140006820(v17, dst, v13);
  if ( (unsigned __int8)normal_compare(v18, v17) )
    printf_more(&offset_of_printf, "Correct!\n", v5);
  else
    printf_more(&offset_of_printf, "Incorrect!\n", v5);
  v10 = 0;
  sub_1400074D0(v17);
  sub_1400074D0(v18);
  sub_1400074D0(v19);
  sub_1400074D0(v20);
  sub_1400074D0(v21);
  sub_1400074D0(v22);
  return v10;
}
```

Nhìn sơ qua pseudocode thì chúng ta thấy sau khi nhập input (flag part 1) chương trình sẽ thực hiện 1 số loại mã hóa nào đó sau đó họ sẽ kiểm tra với 15 hằng số ở v24 như trong chương trình! Bởi vì v24 đã được cho trước nên chúng ta chỉ cần dịch ngược đoạn mã hóa

Tuy nhiên có một nhận xét khá thú vị ở đây là thuật toán mã hóa là tuyến tính và độc lập. Nghĩa là việc biến đổi ký tự ở vị trí này không làm ảnh hưởng đến giá trị ở các vị trí khác.

Chúng ta có thể kiểm chứng điều này bằng thực nghiệm!

Đầu tiên nhảy vào hàm so sánh `normal_compare` và đặt breakpoint ở đầu hàm
![image](https://hackmd.io/_uploads/r1lh6MNGWl.png)

Sau đó chúng ta nhập một input test `aaaaaaaaaaaaaaa`

Đây là giá trị của input sau khi bị mã hóa
![image](https://hackmd.io/_uploads/BJxf0fVMZl.png)

Thử một input khác `aaaxaaaaaaaaaaa`

![image](https://hackmd.io/_uploads/ByaECGNGZl.png)

Chúng ta thấy nó chỉ khác duy nhất vị trí thứ 4 0x2B -> 0x32 đó là vị trí mà chúng ta thay đổi input

Vì vậy sử dụng nhận xét này chúng ta có thể không cần dịch ngược hàm mã hóa!
Vì vậy chúng ta có thể duyệt trâu tuyến tính với độ phức tạp là O(95 * n) với n là độ dài input (n = 15)


Chương trình hiện tại hoạt động như một "oracle" chỉ trả về kết quả Đúng hoặc Sai, không cung cấp đủ thông tin để thực hiện brute force.

Cần sửa đổi chương trình để tạo ra một phản hồi, cụ thể là cho biết độ dài prefix của input đã khớp bao nhiêu với flag chuẩn.

Thực hiện Binary Patching lại hàm so sánh. Chiến thuật là tận dụng lại các instruction in ấn đã có sẵn trong binary để xuất thông tin này ra màn hình.

```
lea     rdx, aCorrect 
lea     rcx, offset_of_printf
call    printf_more
```

`Vì file bị stripped nên đây chỉ là tên được rename một cách ngẫu hứng`

Ta sửa lại aCorrect một tí
![image](https://hackmd.io/_uploads/r1RSAd-fbe.png)

Tóm tắt sơ quá ý tưởng
* Khi mã hóa input -> sinh ra một cái list tương ứng 
* Nhiệm vụ của chúng ta là với mỗi một số trong list in ra số lượng ký C liên tiếp (khi đó độ dài chuỗi sẽ là giá trị của số đó)
* Với mỗi một số xuống hàng

Giả sử như sau khi mã hóa mảng nó là [2, 1, 5] thì chương trình sẽ in ra
```
CC
C
CCCCC
```
Dùng python để xử lý dữ liệu chúng ta khôi phục được mảng [2, 1, 5]. Lý do phải dùng cách rườm rà này là bởi vì khi patch tài nguyên của chúng ta có giới hạn (không đủ bytes nop để thay thế các instruction phức tạp hơn) nên chúng ta sử dụng những thứ có sẵn sau đó dùng python để xử lý dữ liệu sẽ ổn hơn khá nhiều

Đây là sau khi ta patch lại 

```asm
.text:00007FF66BC422C0 normal_compare  proc near               ; CODE XREF: main+256↓p
.text:00007FF66BC422C0                                         ; DATA XREF: .pdata:00007FF66BC850D8↓o
.text:00007FF66BC422C0
.text:00007FF66BC422C0 var_50          = byte ptr -50h
.text:00007FF66BC422C0 var_4F          = byte ptr -4Fh
.text:00007FF66BC422C0 var_4E          = byte ptr -4Eh
.text:00007FF66BC422C0 var_4D          = word ptr -4Dh
.text:00007FF66BC422C0 var_46          = dword ptr -46h
.text:00007FF66BC422C0 var_40          = dword ptr -40h
.text:00007FF66BC422C0 var_20          = qword ptr -20h
.text:00007FF66BC422C0 var_18          = qword ptr -18h
.text:00007FF66BC422C0 arg_0           = qword ptr  8
.text:00007FF66BC422C0 arg_8           = qword ptr  10h
.text:00007FF66BC422C0
.text:00007FF66BC422C0                 mov     [rsp+arg_8], rdx
.text:00007FF66BC422C5                 mov     [rsp+arg_0], rcx
.text:00007FF66BC422CA                 sub     rsp, 80
.text:00007FF66BC422CE                 mov     rcx, [rsp+58h]
.text:00007FF66BC422D3                 call    getLen
.text:00007FF66BC422D8                 mov     [rsp+38h], rax
.text:00007FF66BC422DD                 mov     qword ptr [rsp+48], 0
.text:00007FF66BC422E6                 jmp     short start_loop
.text:00007FF66BC422E8 ; ---------------------------------------------------------------------------
.text:00007FF66BC422E8
.text:00007FF66BC422E8 increase_loop:                          ; CODE XREF: normal_compare+94↓j
.text:00007FF66BC422E8                 mov     rax, [rsp+30h]
.text:00007FF66BC422ED                 inc     rax
.text:00007FF66BC422F0                 mov     [rsp+30h], rax
.text:00007FF66BC422F5
.text:00007FF66BC422F5 start_loop:                             ; CODE XREF: normal_compare+26↑j
.text:00007FF66BC422F5                 mov     rax, [rsp+38h]
.text:00007FF66BC422FA                 cmp     [rsp+30h], rax
.text:00007FF66BC422FF                 jnb     short exittt
.text:00007FF66BC42301                 mov     rdx, [rsp+48]
.text:00007FF66BC42306                 mov     rcx, [rsp+88]
.text:00007FF66BC4230B                 call    getPos
.text:00007FF66BC42310                 movzx   eax, byte ptr [rax]
.text:00007FF66BC42313
.text:00007FF66BC42313 start_loop_1:                           ; CODE XREF: normal_compare+7B↓j
.text:00007FF66BC42313                 mov     [rsp+50h+var_40], eax
.text:00007FF66BC42317                 nop
.text:00007FF66BC42318                 nop
.text:00007FF66BC42319                 nop
.text:00007FF66BC4231A                 nop
.text:00007FF66BC4231B                 nop
.text:00007FF66BC4231C                 cmp     eax, 0
.text:00007FF66BC4231F                 jz      short label2
.text:00007FF66BC42321                 lea     rdx, aCorrect   ; "C\x00\n\n\n\n\n"
.text:00007FF66BC42328                 lea     rcx, offset_of_printf
.text:00007FF66BC4232F                 call    printf_more
.text:00007FF66BC42334                 mov     eax, [rsp+50h+var_40]
.text:00007FF66BC42338                 dec     eax
.text:00007FF66BC4233A                 nop
.text:00007FF66BC4233B                 jmp     short start_loop_1
.text:00007FF66BC4233D ; ---------------------------------------------------------------------------
.text:00007FF66BC4233D                 nop
.text:00007FF66BC4233E                 nop
.text:00007FF66BC4233F                 nop
.text:00007FF66BC42340                 nop
.text:00007FF66BC42341
.text:00007FF66BC42341 label2:                                 ; CODE XREF: normal_compare+5F↑j
.text:00007FF66BC42341                 lea     rdx, aCorrect+2 ; "\n\n\n\n\n"
.text:00007FF66BC42348                 lea     rcx, offset_of_printf
.text:00007FF66BC4234F                 call    printf_more
.text:00007FF66BC42354                 jmp     short increase_loop
.text:00007FF66BC42356 ; ---------------------------------------------------------------------------
.text:00007FF66BC42356
.text:00007FF66BC42356 exittt:                                 ; CODE XREF: normal_compare+3F↑j
.text:00007FF66BC42356                 add     rsp, 50h
.text:00007FF66BC4235A                 retn
.text:00007FF66BC4235A normal_compare  endp
```

Sau khi patch xong một script python đơn giản dùng để trâu phục hồi part1 của flag!

```python
#!/home/ryou/.venvs/re/bin/python

import subprocess

BINARY_PATH = "./flag_checker.exe"

def run_flag_checker(payload: str) -> str:\
    result = subprocess.run(
        [BINARY_PATH],
        input=payload + "\n",       
        capture_output=True,
        text=True                    
    )
    return result.stdout

def handle(inp: str):
    res = [int(len(i)) for i in inp.split('\n') if len(i) > 1]
    return res
    

p = "testtesttest"
handle(run_flag_checker(p)[38:]) 

v22 = [0xFC, 0x76, 0xD4, 0x09, 0xA3, 0xD8, 0x50, 0x2F, 0xB9, 0xD7, 0xBA, 0xE0, 0xB0, 0x34, 0xB2]

flag = ''
for _ in range(15):
    for ch in range(0x21, 0x7f):
        payload = 'A' * _ + chr(ch) + 'A' * (15 - _ - 1)
        # print(payload, handle(run_flag_checker(payload)[38:])[_], v22[_])
        if handle(run_flag_checker(payload)[38:])[_] == v22[_]:
            flag += chr(ch)
            break
    # print(flag)
print(flag)
```

```
First part: W1{Ch4ng1ng_d4t
```

Part2 hoàn toàn tương tự

```asm
.text:00000001400022C0 sub_1400022C0   proc near               ; CODE XREF: main+256↓p
.text:00000001400022C0                                         ; DATA XREF: .pdata:00000001400450D8↓o
.text:00000001400022C0
.text:00000001400022C0 var_40          = dword ptr -40h
.text:00000001400022C0 var_20          = qword ptr -20h
.text:00000001400022C0 var_18          = qword ptr -18h
.text:00000001400022C0 arg_0           = qword ptr  8
.text:00000001400022C0 arg_8           = qword ptr  10h
.text:00000001400022C0
.text:00000001400022C0                 mov     [rsp+16], rdx
.text:00000001400022C5                 mov     [rsp+8], rcx
.text:00000001400022CA                 sub     rsp, 80
.text:00000001400022CE                 mov     rcx, [rsp+88]
.text:00000001400022D3                 call    sub_14000A3F0
.text:00000001400022D8                 mov     [rsp+56], rax
.text:00000001400022DD                 mov     qword ptr [rsp+48], 0
.text:00000001400022E6                 jmp     short loc_1400022F5
.text:00000001400022E8 ; ---------------------------------------------------------------------------
.text:00000001400022E8
.text:00000001400022E8 loc_1400022E8:                          ; CODE XREF: sub_1400022C0+94↓j
.text:00000001400022E8                 mov     rax, [rsp+48]
.text:00000001400022ED                 inc     rax
.text:00000001400022F0                 mov     [rsp+48], rax
.text:00000001400022F5
.text:00000001400022F5 loc_1400022F5:                          ; CODE XREF: sub_1400022C0+26↑j
.text:00000001400022F5                 mov     rax, [rsp+56]
.text:00000001400022FA                 cmp     [rsp+48], rax
.text:00000001400022FF                 jnb     short loc_140002356
.text:0000000140002301                 mov     rdx, [rsp+48]
.text:0000000140002306                 mov     rcx, [rsp+88]
.text:000000014000230B                 call    sub_140007930
.text:0000000140002310                 movzx   eax, byte ptr [rax]
.text:0000000140002313
.text:0000000140002313 loc_140002313:                          ; CODE XREF: sub_1400022C0+7B↓j
.text:0000000140002313                 mov     [rsp+16], eax
.text:0000000140002317                 nop
.text:0000000140002318                 nop
.text:0000000140002319                 nop
.text:000000014000231A                 nop
.text:000000014000231B                 nop
.text:000000014000231C                 cmp     eax, 0
.text:000000014000231F                 jz      short loc_140002341
.text:0000000140002321                 lea     rdx, aC         ; "C"
.text:0000000140002328                 lea     rcx, qword_140043620
.text:000000014000232F                 call    sub_1400043D0
.text:0000000140002334                 mov     eax, [rsp+16]
.text:0000000140002338                 dec     eax
.text:000000014000233A                 nop
.text:000000014000233B                 jmp     short loc_140002313
.text:000000014000233B ; ---------------------------------------------------------------------------
.text:000000014000233D                 db 4 dup(90h)
.text:0000000140002341 ; ---------------------------------------------------------------------------
.text:0000000140002341
.text:0000000140002341 loc_140002341:                          ; CODE XREF: sub_1400022C0+5F↑j
.text:0000000140002341                 lea     rdx, asc_14002F482 ; "\n\n\n\n\n"
.text:0000000140002348                 lea     rcx, qword_140043620
.text:000000014000234F                 call    sub_1400043D0
.text:0000000140002354                 jmp     short loc_1400022E8
.text:0000000140002356 ; ---------------------------------------------------------------------------
.text:0000000140002356
.text:0000000140002356 loc_140002356:                          ; CODE XREF: sub_1400022C0+3F↑j
.text:0000000140002356                 add     rsp, 50h
.text:000000014000235A                 retn
.text:000000014000235A sub_1400022C0   endp
```

```python 
#!/home/ryou/.venvs/re/bin/python

import subprocess

BINARY_PATH = "./flag_checker.exe"

def run_flag_checker(payload: str) -> str:
    result = subprocess.run(
        [BINARY_PATH],
        input=payload + "\n",     
        capture_output=True,
        text=True                 
    )

    res = result.stdout
    
    return res.split(':')[1].strip()

def handle(inp: str):
    res = [int(len(i)) for i in inp.split('\n') if len(i) > 1]
    return res
    
p = "aAAAAAAAAAAAAAAA}"
print(run_flag_checker(p))

v22 = [0x4E, 0xDD, 0x81, 0xCD, 0x84, 0x8F, 0xDD, 0x2B, 0xC2, 0x5A, 0xA6, 0xB7, 0xA9, 0x1A, 0xF7, 0xED, 0xF3]

flag = ''
for _ in range(14, 17):
    for ch in range(0x21, 0x7f):
        payload = 'A' * _ + chr(ch) + 'A' * (17 - _ - 1)
        print(payload, handle(run_flag_checker(payload)[38:])[_], v22[_])
        if handle(run_flag_checker(payload))[_] == v22[_]:
            flag += chr(ch)
            break
    print(f'[+] Current Flag is {flag}')
print(flag)
```

```
Second part: 4_1n_run71me_Is_q
```

### Một cách khác
Một cách chúng ta có thể dùng để tìm part1 và part2 mà em nhận ra trong lúc làm là
![image](https://hackmd.io/_uploads/r1R_IXEGbg.png)
Trong hàm này ở 2 chương trình chúng ta thấy `sub_14000AC40()` giống như một lệnh `push_back` và chuỗi `((n1337 % 0x61 + 31 * kk) ^ (unsigned __int64)(unsigned __int8)(Buf1a >> (kk % 24))) % 0x5F + 32)` luôn luôn nằm trong phạm vi [0x20, 0x7E] `ký tự in được` do vậy chúng ta có thể đặt breakpoint ở đây và khám nghiệm từng giá trị một! Lưu ý ở part2 có phần check_debugger 

![image](https://hackmd.io/_uploads/r1kevmNzZx.png)
`Có thể tìm bằng cách CTRL + F và search gs:60h` và hàm check_debugger này có thể làm thay đổi giá trị của chúng ta khi chạy debug nên chúng ta cần patch lại để không bị check

----
Bây giờ là phần part3 phần mà em chưa tìm ra được nhưng em sẽ trình bày hướng đã suy nghĩ được của em!

Hướng đầu tên em nghĩ đó chính là trong file checker.exe vẫn còn 1 resource nữa dành cho flag_checker.exe part 3 mà có thể chương trình đã giấu vì thế ta cần check coi thử còn resource ẩn nào trong chương trình không

```python 
import pefile

pe = pefile.PE("checker.exe")

if hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'):
    print(f"{'TYPE':<15} {'NAME/ID':<15} {'LANG':<10} {'SIZE (Bytes)':<10}")
    print("-" * 55)

    for resource_type in pe.DIRECTORY_ENTRY_RESOURCE.entries:
        try:
            if resource_type.name is not None:
                name = str(resource_type.name)
            else:
                name = pefile.RESOURCE_TYPE.get(resource_type.struct.Id, str(resource_type.struct.Id))
        except:
            name = str(resource_type.struct.Id)

        for resource_id in resource_type.directory.entries:
            for resource_lang in resource_id.directory.entries:
                data_rva = resource_lang.data.struct.OffsetToData
                size = resource_lang.data.struct.Size
                
                print(f"{str(name):<15} {str(resource_id.struct.Id):<15} {str(resource_lang.struct.Id):<10} {size:<10}")
else:
    print("File này không có Resource directory.")
```

![image](https://hackmd.io/_uploads/BJFSY7VMZx.png)

Vậy không có resource ẩn. Checker.exe đã làm hết nhiệm vụ của mình, theo em nghĩ part 3 chắc chắn được giấu trong flag_checker.exe sau

Ngoài ra em còn có giả thuyết có khi nào part3 được ẩn giấu tức là chỉ được check thì điều kiện có phải đạng chạy debugger không? cho nên cũng thử patch lại chương trình sao cho khi chạy file bình thường thì chương trình xác định đang chạy debugger để dùng script brute force ở trên nhưng cũng không thành công

Vì không còn manh mối nào em chỉ còn cách dịch ngược chương trình để tìm manh mối tiếp theo 

Cấu trúc của hàm main như sau

```C
v8 = sub_140002970(1337LL);           // sinh seed pseudo-random
sub_140004000(v15, v16, v8);          // XOR với PRNG kiểu LCG
sub_140002140(v14, v15, &unk_140044600, &unk_140044588, 0); // ChaCha-like / block transform
unknown_libname_1(v14, &unk_1400446A0); // so sánh với “đáp án”
```
Hiện tại em chỉ mới hiểu được và khái quát được cách thức mã hóa của nó một cách khái quát, do có quá nhiều phép toán nên em không kịp để hình dung một cách cụ thể chương trình được.


Em xin hết
