---
# System prepended metadata

title: Dreamhack (only reverse)

---

# Dreamhack (only reverse)
Đây là writeup khi mình mới chập chững học reverse. 
Update ngày 23/12/2024: Hơn 2 tuần rồi mình chưa update gì, mình bắt đầu viết blog này từ giữa tháng 11. Mình vẫn còn làm trên [đấy](https://dreamhack.io/users/62830/), nhưng sẽ không update nữa.
## ptrace_block
Link chall: https://dreamhack.io/wargame/challenges/1197

Đầu tiên mình check 7749 thứ, tới khi check strings thì được như sau:
```
> strings prob
/lib64/ld-linux-x86-64.so.2
__gmon_start__
_ITM_deregisterTMCloneTable
_ITM_registerTMCloneTable
AES_set_encrypt_key
AES_cbc_encrypt
__cxa_finalize
write
__libc_start_main
ptrace
srand
puts
close
open
__isoc99_scanf
__stack_chk_fail
printf
time
libcrypto.so.3
libc.so.6
OPENSSL_3.0.0
GLIBC_2.34
GLIBC_2.7
GLIBC_2.4
GLIBC_2.2.5
PTE1
u+UH
generate your flag!
%255s
./out.txt
:*3$"
GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
.shstrtab
.interp
.note.gnu.property
.note.gnu.build-id
.note.ABI-tag
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rela.dyn
.rela.plt
.init
.plt.got
.plt.sec
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.init_array
.fini_array
.dynamic
.data
.bss
.comment
```
Mình thấy có AES_CBC, vậy thứ mình cần tìm ở bài này là key và iv, đặc biệt chú ý đến .init_array. Hiểu đơn giản ngắn gọn là nó chứa các hàm được chạy trước hàm main. 
Mở IDA lên check hàm main. (Nhớ sửa code cho dễ đọc trước nhé)
```c
__int64 __fastcall main(int a1, char **a2, char **a3)
{
  int fd; // [rsp+Ch] [rbp-514h]
  __int64 input[32]; // [rsp+10h] [rbp-510h] BYREF
  char buf[1032]; // [rsp+110h] [rbp-410h] BYREF
  unsigned __int64 v7; // [rsp+518h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  memset(input, 0, sizeof(input));
  puts("generate your flag!");
  printf("> ");
  __isoc99_scanf("%255s", input);
  encrypt((__int64)input, (__int64)buf, 256);
  fd = open("./out.txt", 1);
  write(fd, buf, 0x100uLL);
  close(fd);
  return 0LL;
}
```
Hàm encrypt:
```c
__int64 __fastcall encrypt(__int64 input, __int64 output, int size)
{
  char key[256]; // [rsp+20h] [rbp-120h] BYREF
  __int64 iv[4]; // [rsp+120h] [rbp-20h] BYREF

  iv[3] = __readfsqword(0x28u);
  iv[0] = 0LL;
  iv[1] = 0LL;
  AES_set_encrypt_key(to_key, 128LL, key);
  AES_cbc_encrypt(input, output, size, key, iv, 1LL);
  return 0LL;
}
```
Ta thấy có một mảng được gán bằng key, và iv được gán bằng null hết. Ta check mảng gán cho key:
```
to_key          db 41h, 28h, 19h, 4Eh, 0A5h, 7Ch, 0A1h, 41h, 13h, 0CFh
.data:0000000000004010                                         ; DATA XREF: init_func1+83↑o
.data:0000000000004010                                         ; init_func1+93↑o ...
.data:000000000000401A                 db 88h, 0ACh, 2Ah, 0F0h, 0B7h, 0DAh
```
Đến đây ta được mảng như sau
```
41 28 19 4E A5 7C A1 41 13 CF 88 AC 2A F0 B7 DA
```
Thử bỏ vào python decrypt luôn thì thấy hơi sai sai...
Mình từng làm một bài trick lỏ overwrite .fini_array cho nó lặp vô tận rồi nên mình biết ngay là vấn đề nằm ở .init_array 
Có hai hàm đáng chú ý ở phần .init_array (ở đây mình đổi tên hàm rồi):
```c
void init_func1()
{
  unsigned int v0; // eax
  int v1; // ebx
  int v2; // [rsp+4h] [rbp-1Ch]
  int i; // [rsp+8h] [rbp-18h]
  int j; // [rsp+Ch] [rbp-14h]

  v0 = time(0LL);
  srand(v0);
  v2 = 1;
  for ( i = 0; i <= 4095; ++i )
  {
    v1 = ptrace(PTRACE_TRACEME, 0LL, 0LL, 0LL);
    v2 *= v1 * rand();
  }
  for ( j = 0; j <= 14; ++j )
    to_key[j + 1] += to_key[j] + v2;
}
```
```c++
int init_func2()
{
  int v0; // eax
  int result; // eax
  int i; // [rsp+8h] [rbp-8h]
  char v3; // [rsp+Ch] [rbp-4h]

  v0 = rand();
  srand(v0);
  result = rand();
  v3 = result;
  for ( i = 0; i <= 15; ++i )
  {
    result = i;
    to_key[i] ^= v3;
  }
  return result;
}
```
Hướng đầu tiên mọi người tiếp cận có vẻ là chạy debug để lấy ra key đúng, nhưng khi có ptrace thì mọi thứ có vẻ không như vậy. Mình hiểu là nếu như 1 process đang bị debug bởi debugger, thì debugger sẽ gọi ptrace tới process đó => process bị trace và ptrace(PTRACE_TRACEME, 0, 0, 0)  sẽ return khác 0 (gửi lời cảm ơn đến anh d4rkn19ht vì đoạn này em từng đọc được trên github của anh) . Vậy mình rev chay đoạn này hết thôi, có thêm vấn đề nữa là hàm rand(). Ở đây vì ta không chạy debug nên ta biết luôn giá trị ptrace = 0, hàm init_func1() rev khá dễ.
Đến hàm thứ 2 thì có rand() giống như hàm trên, nhưng giá trị nó chỉ nằm trong khoảng char nên cách hiệu quả nhất là Brute Force chứ còn gì nữa :D 
```python
#!/usr/bin/python3 
from Crypto.Cipher import AES

key = bytearray(bytes.fromhex('41 28 19 4E A5 7C A1 41 13 CF 88 AC 2A F0 B7 DA'))
for i in range(0, len(key) - 1):
    key[i + 1] = (key[i + 1] + key[i]) % 256
iv = b'\x00' * 16
out = open("ans.txt", "rb").read()
for i in range(256):
    new_key = key 
    for j in range(len(new_key)):
        new_key[j] ^= i 
    cipher = AES.new(new_key, AES.MODE_CBC, iv)
    x = cipher.decrypt(out)
    if b'DH' in x:
        print(x)
```
Đến đây thì ra flag rồi đó, bài đơn giản đúng không nào :D

## My_First_Game_v0.1
Link chall: https://dreamhack.io/wargame/challenges/1237
- Mình rev windows khá yếu (vì mình mới chơi rev gần đây :D), bài này thuộc dạng game hacking. Hướng đầu tiên của mình là mở IDA lên đọc code và sẽ cố patch sao cho nó di chuyển cái trục kia sang flag, nhưng mò đủ kiểu vẫn không được (phần này khá ngốn time của mình). 
- Trùng hợp là hôm nay toàn làm mấy bài game hacking. Nên mình sẽ tìm tool nào đó để hack game, ở Windows thì có Cheat Engine. Trước giờ mình chưa xài bao giờ, nhưng mình thích học những thứ mới :D. 
- Hướng đi bài này sẽ là search tìm giá trị của mấy cái vị trí đó trên memory, rồi dùng Cheat Engine thay đổi nó để đọc được flag. 
- Sau một khoảng thời gian tìm thì mình tìm được giá trị nó ở đây: 
![Screenshot 2024-11-15 124511](https://hackmd.io/_uploads/Syz2XPEMyx.png)
- Rồi chỉnh giá trị để ta đọc được flag thôi, ta biết 4 bytes đầu là X còn 4 bytes sau là giá trị Y. 
![image](https://hackmd.io/_uploads/rJVnEP4Gyx.png)
![image](https://hackmd.io/_uploads/HkSGHvEGJl.png)
![image](https://hackmd.io/_uploads/rkKUHv4fye.png)
Ghép lại là có flag thui :D Bài này làm t đau mắt vcl hic =)))) 

## Casino-777 
Link chal: https://dreamhack.io/wargame/challenges/143
- Bài này mới đọc vào mình thấy random thì thấy hơi rén nhẹ rồi, nhưng phải bình tĩnh phân tích thôi :))) 
- Mở IDA lên thì ta chú ý 2 hàm này, như mọi bài thì đổi tên biến cho dễ đọc.
``` c
int choice_1()
{
  __int64 v0; // rbx
  __int64 v1; // rbx
  char v3; // [rsp+Fh] [rbp-21h]
  int i; // [rsp+10h] [rbp-20h]
  int v5; // [rsp+14h] [rbp-1Ch]
  int j; // [rsp+18h] [rbp-18h]
  int size; // [rsp+1Ch] [rbp-14h]

  for ( i = 0; i <= 9; ++i )
  {
    *((_QWORD *)&gen_arr + i) = malloc(0x10uLL);
    size = *(_DWORD *)&some_arr[4 * i] + 1;
    **((_DWORD **)&gen_arr + i) = size;
    v0 = *((_QWORD *)&gen_arr + i);
    *(_QWORD *)(v0 + 8) = malloc(size);
    v5 = 0;
    for ( j = 0; j < size; ++j )
    {
      v3 = alphabet[(unsigned __int8)random_byte() % 0x5Eu];
      *(_BYTE *)(j + *(_QWORD *)(*((_QWORD *)&gen_arr + i) + 8LL)) = v3;
      if ( v3 == 55 )
        v5 = 1;
    }
    if ( !v5 )
    {
      v1 = *(_QWORD *)(*((_QWORD *)&gen_arr + i) + 8LL);
      *(_BYTE *)(v1 + (unsigned __int8)random_byte() % size) = 55;
    }
  }
  slot_check = 1;
  return puts("slot generated !");
}
```
``` c
unsigned __int64 choice_2()
{
  int i; // [rsp+Ch] [rbp-134h]
  int j; // [rsp+10h] [rbp-130h]
  int v3; // [rsp+14h] [rbp-12Ch]
  int k; // [rsp+18h] [rbp-128h]
  int fd; // [rsp+1Ch] [rbp-124h]
  int size; // [rsp+20h] [rbp-120h]
  int num_rotate; // [rsp+24h] [rbp-11Ch]
  unsigned __int64 num_inp; // [rsp+28h] [rbp-118h] BYREF
  __int64 src; // [rsp+30h] [rbp-110h] BYREF
  __int64 v10; // [rsp+38h] [rbp-108h]
  __int64 v11; // [rsp+40h] [rbp-100h]
  __int64 v12; // [rsp+48h] [rbp-F8h]
  __int64 v13; // [rsp+50h] [rbp-F0h]
  __int64 v14; // [rsp+58h] [rbp-E8h]
  __int64 v15; // [rsp+60h] [rbp-E0h]
  __int64 v16; // [rsp+68h] [rbp-D8h]
  __int64 v17; // [rsp+70h] [rbp-D0h]
  __int64 v18; // [rsp+78h] [rbp-C8h]
  __int64 v19; // [rsp+80h] [rbp-C0h]
  __int64 v20; // [rsp+88h] [rbp-B8h]
  __int64 v21; // [rsp+90h] [rbp-B0h]
  __int64 v22; // [rsp+98h] [rbp-A8h]
  __int64 v23; // [rsp+A0h] [rbp-A0h]
  __int64 v24; // [rsp+A8h] [rbp-98h]
  __int64 v25; // [rsp+B0h] [rbp-90h]
  __int64 v26; // [rsp+B8h] [rbp-88h]
  __int64 v27; // [rsp+C0h] [rbp-80h]
  __int64 v28; // [rsp+C8h] [rbp-78h]
  __int64 v29; // [rsp+D0h] [rbp-70h]
  __int64 v30; // [rsp+D8h] [rbp-68h]
  __int64 v31; // [rsp+E0h] [rbp-60h]
  __int64 v32; // [rsp+E8h] [rbp-58h]
  __int64 v33; // [rsp+F0h] [rbp-50h]
  __int64 v34; // [rsp+F8h] [rbp-48h]
  __int64 v35; // [rsp+100h] [rbp-40h]
  __int64 v36; // [rsp+108h] [rbp-38h]
  __int64 v37; // [rsp+110h] [rbp-30h]
  __int64 v38; // [rsp+118h] [rbp-28h]
  __int64 v39; // [rsp+120h] [rbp-20h]
  __int64 v40; // [rsp+128h] [rbp-18h]
  unsigned __int64 v41; // [rsp+138h] [rbp-8h]

  v41 = __readfsqword(0x28u);
  num_inp = 0LL;
  if ( slot_check )
  {
    ++slot_check;
    puts("How many rotate the slots ?");
    printf("> ");
    __isoc99_scanf("%lld", &num_inp);
    for ( i = 0; i <= 9; ++i )
    {
      size = **(_DWORD **)&gen_arr[8 * i];
      num_rotate = num_inp % size;
      src = 0LL;
      v10 = 0LL;
      v11 = 0LL;
      v12 = 0LL;
      v13 = 0LL;
      v14 = 0LL;
      v15 = 0LL;
      v16 = 0LL;
      v17 = 0LL;
      v18 = 0LL;
      v19 = 0LL;
      v20 = 0LL;
      v21 = 0LL;
      v22 = 0LL;
      v23 = 0LL;
      v24 = 0LL;
      v25 = 0LL;
      v26 = 0LL;
      v27 = 0LL;
      v28 = 0LL;
      v29 = 0LL;
      v30 = 0LL;
      v31 = 0LL;
      v32 = 0LL;
      v33 = 0LL;
      v34 = 0LL;
      v35 = 0LL;
      v36 = 0LL;
      v37 = 0LL;
      v38 = 0LL;
      v39 = 0LL;
      v40 = 0LL;
      for ( j = 0; j < size; ++j )
        *((_BYTE *)&src + (j + num_rotate) % size) = *(_BYTE *)(*(_QWORD *)(*(_QWORD *)&gen_arr[8 * i] + 8LL) + j);
      memcpy(*(void **)(*(_QWORD *)&gen_arr[8 * i] + 8LL), &src, size);
    }
    v3 = 1;
    printf("Result: ");
    for ( k = 0; k <= 9; ++k )
    {
      printf("%c ", (unsigned int)**(char **)(*(_QWORD *)&gen_arr[8 * k] + 8LL));
      if ( **(_BYTE **)(*(_QWORD *)&gen_arr[8 * k] + 8LL) != '7' )
        v3 = 0;
    }
    putchar(10);
    if ( v3 )
    {
      puts("Jackpot ! Congratuations !!");
      fd = open("flag", 0);
      src = 0LL;
      v10 = 0LL;
      v11 = 0LL;
      v12 = 0LL;
      v13 = 0LL;
      v14 = 0LL;
      v15 = 0LL;
      v16 = 0LL;
      v17 = 0LL;
      v18 = 0LL;
      v19 = 0LL;
      v20 = 0LL;
      v21 = 0LL;
      v22 = 0LL;
      v23 = 0LL;
      v24 = 0LL;
      v25 = 0LL;
      v26 = 0LL;
      v27 = 0LL;
      v28 = 0LL;
      v29 = 0LL;
      v30 = 0LL;
      v31 = 0LL;
      v32 = 0LL;
      v33 = 0LL;
      v34 = 0LL;
      v35 = 0LL;
      v36 = 0LL;
      v37 = 0LL;
      v38 = 0LL;
      v39 = 0LL;
      v40 = 0LL;
      read(fd, &src, 0x100uLL);
      puts((const char *)&src);
    }
  }
  else
  {
    puts("Generate the slots first !");
  }
  return __readfsqword(0x28u) ^ v41;
}
```
- Tóm lại flow chương trình là: gen 10 dãy số giá trị random, ta cần rotate sao cho mỗi phần tử đầu tiên của 10 dãy số này là 7 hết thì nó sẽ nhả flag ra. Biết rằng mỗi dãy đều có ít nhất 1 phần tử là số 7.
- Ở đây thì ta thấy phép mod, cộng với việc biết trước size của 10 dãy số đó và phần tử được in ra sau mỗi lần rotate xong. 
- Mình có thuật toán như sau: Mình sẽ cố gắng giữ nhiều số 7 nhất, vì các tính chất ở trên nên ta hoàn toàn có thể giữ số 7 sau mỗi lần rotate bằng cách cho số lần rotate chia hết cho size của dãy số ta cần giữ lại số 7. Tóm lại là: giữ các số 7, random ra số 7 tiếp theo, rồi lại giữ nó, rồi lại tiếp tục như vậy. 
- Script bài này của mình (ủa rồi là rev dữ chưa ???): 
``` python
#!/usr/bin/python3 
from math import lcm

s = bytearray(bytes.fromhex("48 00 00 00 52 00 00 00 58 00 00 00 60 00 00 00 66 00 00 00 4E 00 00 00 64 00 00 00 7E 00 00 00 82 00 00 00 88 00 00 00"))
size_arr = []
for i in range(0, len(s), 4):
    size_arr.append(s[i] + 1)
print(size_arr)
from pwn import * 

context.log_level = "debug"
p = remote('host3.dreamhack.games', 20004)
# p = process('./casino_777')
p.sendlineafter(b'> ', b'1')
num7 = 0 
rotate = 1 
while num7 != 10:
    if num7 == 0:
        rotate += 1 
    p.sendlineafter(b'> ', b'2')
    p.sendlineafter(b'> ', str(rotate))
    arr = p.recvline()[8:-1].decode().split(" ")
    new_num7 = 0 
    new_rotate = 1 
    for i in range(10):
        if arr[i] == '7':
            new_num7 += 1 
            new_rotate *= size_arr[i]
    if new_num7 > num7:
        num7 = new_num7
        rotate = new_rotate
    log.info(arr)
p.interactive()
```
## baseball
Sau khi dành hơn 1 tuần bỏ CTF để ôn giữa kì thì mình đã trở lại :D mai thi nhập môn mạch số mà thôi kệ nhớ CTF quá làm luôn =)))
Link chall: https://dreamhack.io/wargame/challenges/105 
Bài này khá đơn giản, bỏ tí thời gian ra đọc là hiểu. Tốn thời gian implement hơn là đọc code. 
Ta chỉ cần quan tâm duy nhất hàm này trong chương trình (mình đã đổi tên biến cho dễ đọc)
``` c
_BYTE *__fastcall sub_1289(_BYTE *input, int input_size)
{
  _BYTE *v3; // rax
  _BYTE *idx_0; // rax
  _BYTE *v5; // rax
  _BYTE *v6; // rax
  _BYTE *v7; // rax
  _BYTE *output_idx; // [rsp+18h] [rbp-28h]
  _BYTE *idx_1; // [rsp+18h] [rbp-28h]
  _BYTE *v10; // [rsp+18h] [rbp-28h]
  _BYTE *inp_idx; // [rsp+20h] [rbp-20h]
  unsigned __int64 size; // [rsp+28h] [rbp-18h]
  _BYTE *v13; // [rsp+30h] [rbp-10h]
  _BYTE *v14; // [rsp+38h] [rbp-8h]

  size = (4 * input_size / 3 + 4) / 0x48uLL + 4 * input_size / 3 + 4 + 1;
  if ( size < input_size )
    return 0LL;
  v13 = malloc(size);
  if ( !v13 )
    return 0LL;
  v14 = &input[input_size];
  inp_idx = input;
  output_idx = v13;
  while ( v14 - inp_idx > 2 )
  {
    *output_idx = table_file[*inp_idx >> 2];
    output_idx[1] = table_file[(inp_idx[1] >> 4) | (16 * *inp_idx) & 0x30];
    output_idx[2] = table_file[(inp_idx[2] >> 6) | (4 * inp_idx[1]) & 0x3C];
    v3 = output_idx + 3;
    output_idx += 4;
    *v3 = table_file[inp_idx[2] & 0x3F];
    inp_idx += 3;
  }
  if ( v14 != inp_idx )
  {
    idx_0 = output_idx;
    idx_1 = output_idx + 1;
    *idx_0 = table_file[*inp_idx >> 2];
    if ( v14 - inp_idx == 1 )
    {
      *idx_1 = table_file[(16 * *inp_idx) & 0x30];
      v5 = idx_1 + 1;
      v10 = idx_1 + 2;
      *v5 = '=';
    }
    else
    {
      *idx_1 = table_file[(inp_idx[1] >> 4) | (16 * *inp_idx) & 0x30];
      v6 = idx_1 + 1;
      v10 = idx_1 + 2;
      *v6 = table_file[(4 * inp_idx[1]) & 0x3C];
    }
    v7 = v10;
    output_idx = v10 + 1;
    *v7 = '=';
  }
  *output_idx = 0;
  return v13;
}
```
Tóm tắt một tí về bài này
* Nó cho chúng ta text_in.txt, text_out.txt và flag_out.txt 
* Từ text_in.txt và text_out.txt ta khôi phục được bảng table 
* Từ bảng table ta reverse để lấy flag 

Bài này mình dùng z3 để giải, script của mình:
``` python
#!/usr/bin/python3 
from z3 import * 

table = [BitVec(f'table_{i}', 8) for i in range(64)]
solver = Solver()
inp = open("text_in.txt", "rb").read()
out = open("text_out.txt", "rb").read()
v14 = len(inp) 
inp_idx = 0
output_idx = 0

# *output_idx = table_file[*inp_idx >> 2];
# output_idx[1] = table_file[(inp_idx[1] >> 4) | (16 * *inp_idx) & 0x30];
# output_idx[2] = table_file[(inp_idx[2] >> 6) | (4 * inp_idx[1]) & 0x3C];
# v3 = output_idx + 3;
# output_idx += 4;
# *v3 = table_file[inp_idx[2] & 0x3F];
# inp_idx += 3;

while v14 - inp_idx > 2:
    solver.add(out[output_idx] == table[inp[inp_idx] >> 2])
    solver.add(out[output_idx + 1] == table[(inp[inp_idx + 1] >> 4) | (16 * inp[inp_idx]) & 0x30])
    solver.add(out[output_idx + 2] == table[(inp[inp_idx + 2] >> 6) | (4 * inp[inp_idx + 1]) & 0x3c]) 
    solver.add(out[output_idx + 3] == table[inp[inp_idx + 2] & 0x3f])
    output_idx += 4 
    inp_idx += 3 

# if ( v14 != inp_idx )
# {
# idx_0 = output_idx;
# idx_1 = output_idx + 1;
# *idx_0 = table_file[*inp_idx >> 2];
# if ( v14 - inp_idx == 1 )
# {
#     *idx_1 = table_file[(16 * *inp_idx) & 0x30];
#     idx_2 = idx_1 + 1;
#     idx_3 = idx_1 + 2;
#     *idx_2 = '=';
# }
# else
# {
#     *idx_1 = table_file[(inp_idx[1] >> 4) | (16 * *inp_idx) & 0x30];
#     v6 = idx_1 + 1;
#     idx_3 = idx_1 + 2;
#     *v6 = table_file[(4 * inp_idx[1]) & 0x3C];
# }
# v7 = idx_3;
# output_idx = idx_3 + 1;
# *v7 = '=';
# }
# *output_idx = 0;  

if v14 != inp_idx: 
    idx_0 = output_idx 
    idx_1 = output_idx + 1 
    solver.add(out[idx_0] == table[inp[inp_idx] >> 2]) 
    if v14 - inp_idx == 1:
        solver.add(out[idx_1] == table[(16 * inp[inp_idx]) & 0x30])
        idx_2 = idx_1 + 1 
        idx_3 = idx_1 + 2 
    else:
        solver.add(out[idx_1] == table[(inp[inp_idx + 1] >> 4) | (16 * inp[inp_idx]) & 0x30]) 
        v6 = idx_1 + 1 
        solver.add(out[v6] == table[(4 * inp[inp_idx + 1]) & 0x3c]) 
s = b''
from pwn import * 
if solver.check() == sat:
    m = solver.model() 
    arr = []
    for i in range(64):
        if m[table[i]] == None:
            s += b'\x00'
            arr.append(0)
        else:
            s += p8(int(str(m[table[i]])))
            arr.append(m[table[i]])
print(s)
open("table", "wb").write(s)
table = s 
mp = [0] * 256 
for i in range(len(arr)):
    if s[i] == 0:
        continue
    assert(mp[s[i]] == 0)
    mp[s[i]] = i

flag = [BitVec(f'flag_{i}', 8) for i in range(30)]
solver = Solver()
out = open("flag_out.txt", "rb").read()
v14 = 30
inp_idx = 0
output_idx = 0 

while v14 - inp_idx > 2:
    x = mp[out[output_idx]]
    solver.add(flag[inp_idx] >> 2 == x)
    x = mp[out[output_idx + 1]]
    solver.add((flag[inp_idx + 1] >> 4) | (16 * flag[inp_idx]) & 0x30 == x)
    x = mp[out[output_idx + 2]]
    solver.add((flag[inp_idx + 2] >> 6) | (4 * flag[inp_idx + 1]) & 0x3c == x)
    x = mp[out[output_idx + 3]]
    solver.add(flag[inp_idx + 2] & 0x3f == x)
    output_idx += 4 
    inp_idx += 3 
    
if v14 != inp_idx: 
    idx_0 = output_idx 
    idx_1 = output_idx + 1 
    x = mp[out[idx_0]]
    solver.add(flag[inp_idx] >> 2 == x)
    # solver.add(out[idx_0] == table[inp[inp_idx] >> 2]) 
    if v14 - inp_idx == 1:
        # solver.add(out[idx_1] == table[(16 * inp[inp_idx]) & 0x30])
        x = mp[out[idx_1]]
        solver.add((16 * flag[inp_idx]) & 0x30 == x)
        idx_2 = idx_1 + 1 
        idx_3 = idx_1 + 2 
    else:
        # solver.add(out[idx_1] == table[(inp[inp_idx + 1] >> 4) | (16 * inp[inp_idx]) & 0x30]) 
        x = mp[out[idx_1]]
        solver.add((flag[inp_idx + 1] >> 4) | (16 * flag[inp_idx]) & 0x30 == x)
        v6 = idx_1 + 1 
        # solver.add(out[v6] == table[(4 * inp[inp_idx + 1]) & 0x3c]) 
        x = mp[out[v6]]
        solver.add((4 * flag[inp_idx + 1]) & 0x3c == x)

if solver.check() == sat:
    m = solver.model()
    arr = [m[flag[i]].as_long() for i in range(30)]
    print(bytes(arr))
```
## Times 
Link chall: https://dreamhack.io/wargame/challenges/247
Mở IDA lên coi hàm main:
``` c
__int64 __fastcall main(int a1, char **a2, char **a3)
{
  int v4; // ebx
  int v5; // ebx
  unsigned int *v6; // rbx
  int v7; // [rsp+1Ch] [rbp-54h] BYREF
  int i; // [rsp+20h] [rbp-50h]
  int j; // [rsp+24h] [rbp-4Ch]
  int k; // [rsp+28h] [rbp-48h]
  int m; // [rsp+2Ch] [rbp-44h]
  int input_size; // [rsp+30h] [rbp-40h]
  unsigned int seed; // [rsp+34h] [rbp-3Ch]
  char *input; // [rsp+38h] [rbp-38h]
  __int64 v15[6]; // [rsp+40h] [rbp-30h] BYREF

  v15[3] = __readfsqword(0x28u);
  if ( a1 == 2 )
  {
    input = strdup(a2[1]);
    input_size = strlen(input);
    puts("Welcome to registration center");
    seed = time(0LL);
    srand(seed);
    v4 = rand();
    v7 = v4 + rand();
    v15[0] = 0LL;
    v15[1] = 0LL;
    do_sth(&v7, 4uLL, (__int64)v15);
    for ( i = 0; i < input_size; ++i )
    {
      input[i] ^= *((_BYTE *)v15 + ((4 * (_BYTE)i) & 0xF));
      input[i] ^= *((_BYTE *)v15 + ((4 * (_BYTE)i + 1) & 0xF));
      input[i] ^= *((_BYTE *)v15 + ((4 * (_BYTE)i + 2) & 0xF));
      input[i] ^= *((_BYTE *)v15 + ((4 * (_BYTE)i + 3) & 0xF));
    }
    for ( j = 0; j < input_size / 2; ++j )
      *(_WORD *)&input[2 * j] ^= xor_value;
    seed = time(0LL);
    srand(seed);
    v5 = rand();
    v7 = v5 + rand();
    memset(v15, 0, 0x10uLL);
    do_sth(&v7, 4uLL, (__int64)v15);
    for ( k = 0; k < input_size; ++k )
    {
      input[k] ^= *((_BYTE *)v15 + ((4 * (_BYTE)k) & 0xF));
      input[k] ^= *((_BYTE *)v15 + ((4 * (_BYTE)k + 1) & 0xF));
      input[k] ^= *((_BYTE *)v15 + ((4 * (_BYTE)k + 2) & 0xF));
      input[k] ^= *((_BYTE *)v15 + ((4 * (_BYTE)k + 3) & 0xF));
    }
    for ( m = 0; m < input_size / 4; ++m )
    {
      v6 = (unsigned int *)&input[4 * m];
      *v6 = enc(*v6);
    }
    if ( !memcmp(input, &cmp_arr, 0x29uLL) )
      puts("Registration done !");
    else
      puts("Registration failed..");
    return 0LL;
  }
  else
  {
    printf("%s [registration code]\n", *a2);
    return 1LL;
  }
}
```
Nhưng khi mình chạy thử thì nó hiện như này xong exit luôn.
```
❯ ./times
Not yet !!! Please wait more time.
```
Bài này y chang bài ptrace_block trên kia mình làm :))) Vấn đề ở hàm init thôi 
``` c
__int64 init_func()
{
  int v0; // edx
  __int64 result; // rax

  if ( time(0LL) <= 1909094399 )
  {
    puts("Not yet !!! Please wait more time.");
    exit(0);
  }
  v0 = 1234 * (ptrace(PTRACE_TRACEME, 29808LL, 24946LL, 25955LL) + 1);
  result = v0 ^ (unsigned int)(unsigned __int16)word_4048;
  word_4048 ^= v0;
  return result;
}
```
Ở đây ta patch call _exit bằng nop hết là được. Xong là chạy được bth rùi nhé 
```
❯ ./times
Not yet !!! Please wait more time.
./times [registration code]
```
Sau khi patch 
``` c 
__int64 init_func()
{
  int v0; // edx
  __int64 result; // rax

  if ( time(0LL) <= 1909094399 )
    puts("Not yet !!! Please wait more time.");
  v0 = 1234 * (ptrace(PTRACE_TRACEME, 29808LL, 24946LL, 25955LL) + 1);
  result = v0 ^ (unsigned int)(unsigned __int16)xor_value;
  xor_value ^= v0;
  return result;
}
```
![image](https://hackmd.io/_uploads/HJWPuqImJx.png)
Ta biết ptrace khi không debug = 0, giá trị của xor_value khi xor với cái kia là ra 0. Nên đoạn xor với giá trị này ở hàm main ta có thể bỏ qua.
Check hàm enc:
``` c
__int64 __fastcall enc(unsigned int a1)
{
  return (unsigned int)__ROL4__(
                         (((16
                          * ((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333)) & 0xF0F0F0F0 | (((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333) >> 4) & 0xF0F0F0F) << 8) & 0xFF00FF00 | (((16 * ((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333)) & 0xF0F0F0F0 | (((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333) >> 4) & 0xF0F0F0F) >> 8) & 0xFF00FF,
                         16);
}
```
Để rev ROL4 thì ta thay bằng ROR4 là được. Từ chuỗi cmp_arr ta rev được chuỗi trước khi thực hiện hàm enc. 
Script của mình:
``` python
#!/usr/bin/python3
from ctypes import * 
from pwn import * 

def _rol(val, bits, bit_size):
    return (val << bits % bit_size) & (2 ** bit_size - 1) | \
           ((val & (2 ** bit_size - 1)) >> (bit_size - (bits % bit_size)))

def _ror(val, bits, bit_size):
    return ((val & (2 ** bit_size - 1)) >> bits % bit_size) | \
           (val << (bit_size - (bits % bit_size)) & (2 ** bit_size - 1))

__ROR4__ = lambda val, bits: _ror(val, bits, 32)
__ROR8__ = lambda val, bits: _ror(val, bits, 64)
__ROL4__ = lambda val, bits: _rol(val, bits, 32)
__ROL8__ = lambda val, bits: _rol(val, bits, 64)

def f(a1):
    return __ROL4__((((16 * ((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333)) & 0xF0F0F0F0 | (((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333) >> 4) & 0xF0F0F0F) << 8) & 0xFF00FF00 | (((16 * ((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333)) & 0xF0F0F0F0 | (((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333) >> 4) & 0xF0F0F0F) >> 8) & 0xFF00FF, 16)

def revF(a1):
    return __ROR4__((((16 * ((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333)) & 0xF0F0F0F0 | (((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333) >> 4) & 0xF0F0F0F) << 8) & 0xFF00FF00 | (((16 * ((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333)) & 0xF0F0F0F0 | (((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333) >> 4) & 0xF0F0F0F) >> 8) & 0xFF00FF, 16)

cmp = bytes.fromhex("66 0C 4C 86 A6 2C 1C 9C 1C 66 1C 2C 9C 6C A6 CC A6 6C 6C AC A6 A6 86 4C 2C 46 EC 8C EC 46 8C 9C 4C EC C6 66 4C 46 86 4C")
input_final = []
ans = b''
for i in range(0, 40, 4):
    x = (cmp[i + 3] << 24) + (cmp[i + 2] << 16) + (cmp[i + 1] << 8) + cmp[i]
    x = revF(x)
    ans += p32(x)
print(ans)
```
Ờm mình nhập thử thì nó là flag luôn ??? :D Rev có 1 hàm đã ra flag đúng là việc nhẹ lương cao =)))
## bitvm
Link chall: https://dreamhack.io/wargame/challenges/1019
Check hàm main:
``` c
__int64 __fastcall main(int a1, char **a2, char **a3)
{
  if ( a1 == 2 )
  {
    if ( (unsigned int)check_arg(a2[1]) == 1 )
    {
      return 0LL;
    }
    else
    {
      if ( (unsigned __int8)check() )
        puts("Wrong.");
      else
        puts("Correct!!!");
      return 0LL;
    }
  }
  else
  {
    puts("Usage: ./chall command_file");
    return 0LL;
  }
}
```
Hàm check:
``` c 
__int64 check()
{
  __int64 v1; // rdi
  unsigned __int8 v2; // [rsp+Fh] [rbp-11h]
  __int64 v3[2]; // [rsp+10h] [rbp-10h]

  v3[1] = __readfsqword(0x28u);
  v3[0] = 0LL;
  while ( 1 )
  {
    v2 = txt[i_val++];
    if ( v2 == 32 )
      break;
    switch ( v2 )
    {
      case 0x21u:
        putchar(txt[i_val]);
        ++i_val;
        break;
      case 0x22u:
        v1 = (unsigned __int8)getchar();
        get_input(v1);
        break;
      case 0x23u:
        *((_BYTE *)v3 + txt[i_val]) = txt[i_val + 1];
        i_val += 2;
        break;
      case 0x24u:
        *((_BYTE *)v3 + txt[i_val]) = *((_BYTE *)v3 + txt[i_val + 1]);
        i_val += 2;
        break;
      case 0x25u:
        *((_BYTE *)v3 + txt[i_val]) = *((_BYTE *)v3 + txt[i_val + 2]) & *((_BYTE *)v3 + txt[i_val + 1]);
        i_val += 3;
        break;
      case 0x26u:
        *((_BYTE *)v3 + txt[i_val]) = *((_BYTE *)v3 + txt[i_val + 2]) | *((_BYTE *)v3 + txt[i_val + 1]);
        i_val += 3;
        break;
      case 0x27u:
        *((_BYTE *)v3 + txt[i_val]) = *((_BYTE *)v3 + txt[i_val + 2]) ^ *((_BYTE *)v3 + txt[i_val + 1]);
        i_val += 3;
        break;
      case 0x28u:
        LOBYTE(v3[0]) = sub_55E1EC6C1315();
        break;
      case 0x29u:
        get_input(*((_BYTE *)v3 + txt[i_val]));
        ++i_val;
        break;
    }
  }
  return LOBYTE(v3[0]);
}
```
Mình viết script để xem nó làm gì:
``` python
#!/usr/bin/python3

command = open("command", "rb").read()
flagSize = 68
inp = [ord('a')] * 68 
flag = [0] * 68
i = 0 
inp_idx = 0
v3 = [0] * 16 
while True:
    v2 = command[i]
    i += 1 
    if v2 == 0x20:
        break 
    if v2 == 0x21:
        i += 1 
    elif v2 == 0x22:
        v1 = flag[inp_idx] 
        inp_idx += 1 
    elif v2 == 0x23:
        v3[command[i]] = command[i + 1]
        print(f'v3[{command[i]}] = {command[i + 1]}')
        i += 2 
    elif v2 == 0x24:
        v3[command[i]] = v3[command[i + 1]]
        print(f'v3[{command[i]}] = v3[{command[i + 1]}]')
        i += 2 
    elif v2 == 0x25:
        v3[command[i]] = v3[command[i + 2]] & v3[command[i + 1]]
        print(f'v3[{command[i]}] = v3[{command[i + 1]}] & v3[{command[i + 2]}]')
        i += 3 
    elif v2 == 0x26:
        v3[command[i]] = v3[command[i + 2]] | v3[command[i + 1]]
        print(f'v3[{command[i]}] = v3[{command[i + 1]}] | v3[{command[i + 2]}]')
        i += 3
    elif v2 == 0x27:
        v3[command[i]] = v3[command[i + 2]] ^ v3[command[i + 1]]
        print(f'v3[{command[i]}] = v3[{command[i + 1]}] ^ v3[{command[i + 2]}]')
        i += 3
    elif v2 == 0x28:
        inp_idx -= 1
        v3[0] = flag[inp_idx] 
        print(f'v3[0] = flag[{inp_idx}]')
    elif v2 == 0x29:
        flag[inp_idx] = v3[command[i]]
        print(f'flag[{inp_idx}] = v3[{command[i]}]')
        i += 1 
        inp_idx += 1 
```
``` python
v3[1] = 243
v3[2] = 113
v3[3] = 255
v3[0] = flag[67]
v3[4] = v3[0] & v3[1]
v3[5] = v3[2] ^ v3[4]
v3[7] = v3[5] | v3[7]
v3[4] = v3[0] | v3[1]
v3[5] = v3[3] ^ v3[4]
v3[7] = v3[5] | v3[7]
v3[1] = 193
v3[2] = 1
v3[3] = 245
v3[0] = flag[66]
v3[4] = v3[0] & v3[1]
v3[5] = v3[2] ^ v3[4]
v3[7] = v3[5] | v3[7]
v3[4] = v3[0] | v3[1]
v3[5] = v3[3] ^ v3[4]
v3[7] = v3[5] | v3[7]
v3[1] = 38
v3[2] = 32
v3[3] = 63
v3[0] = flag[65]
v3[4] = v3[0] & v3[1]
v3[5] = v3[2] ^ v3[4]
v3[7] = v3[5] | v3[7]
v3[4] = v3[0] | v3[1]
v3[5] = v3[3] ^ v3[4]
v3[7] = v3[5] | v3[7]
v3[1] = 128
v3[2] = 0
v3[3] = 229
v3[0] = flag[64]
v3[4] = v3[0] & v3[1]
v3[5] = v3[2] ^ v3[4]
v3[7] = v3[5] | v3[7]
v3[4] = v3[0] | v3[1]
v3[5] = v3[3] ^ v3[4]
v3[7] = v3[5] | v3[7]
...
v3[1] = 20
v3[2] = 4
v3[3] = 84
v3[0] = flag[0]
v3[4] = v3[0] & v3[1]
v3[5] = v3[2] ^ v3[4]
v3[7] = v3[5] | v3[7]
v3[4] = v3[0] | v3[1]
v3[5] = v3[3] ^ v3[4]
v3[7] = v3[5] | v3[7]
v3[0] = v3[7]
Ta cần v3[0] = v[7] = 0
```
Hmm nhìn phép bit này hơi khó rev, brute force được nên làm luôn nhé.
``` python
#!/usr/bin/python3 

arr = [243, 113, 255, 193, 1, 245, 38, 32, 63, 128, 0, 229, 151, 16, 183, 248, 96, 252, 123, 48, 123, 37, 36, 101, 42, 34, 58, 248, 96, 249, 224, 96, 227, 88, 24, 121, 219, 17, 255, 152, 16, 191, 62, 34, 126, 30, 4, 127, 236, 36, 255, 250, 56, 251, 53, 53, 55, 224, 96, 226, 72, 64, 109, 0, 0, 102, 93, 21, 127, 223, 24, 255, 199, 0, 255, 100, 100, 100, 188, 36, 254, 117, 49, 117, 13, 0, 63, 161, 32, 181, 204, 4, 252, 76, 68, 108, 155, 0, 255, 61, 53, 63, 192, 64, 228, 63, 56, 63, 116, 52, 119, 68, 0, 119, 251, 97, 251, 230, 36, 247, 43, 35, 107, 157, 0, 255, 251, 49, 251, 39, 33, 63, 213, 17, 253, 69, 0, 125, 231, 98, 231, 13, 8, 61, 127, 100, 127, 33, 32, 103, 153, 16, 189, 217, 65, 249, 64, 64, 102, 242, 50, 247, 168, 32, 185, 190, 50, 190, 174, 34, 238, 96, 96, 98, 236, 96, 237, 196, 0, 247, 87, 69, 119, 152, 16, 186, 184, 48, 188, 48, 32, 115, 57, 32, 123, 81, 81, 123, 4, 0, 76, 20, 4, 84]
flag = [] 
v3 = [0] * 8 
for i in range(0, len(arr), 3):
    v3[1] = arr[i] 
    v3[2] = arr[i + 1] 
    v3[3] = arr[i + 2] 
    for c in range(33, 127):
        v3[0] = c
        v3[4] = v3[0] & v3[1]
        v3[5] = v3[2] ^ v3[4]
        v3[7] = v3[5] | v3[7]
        v3[4] = v3[0] | v3[1]
        v3[5] = v3[3] ^ v3[4]
        v3[7] = v3[5] | v3[7]
        if v3[7] == 0:
            flag.append(c)
            break 
        else:
            v3[4] = v3[5] = v3[6] = v3[7] = 0
print(bytes(flag[::-1]))
```

## public 
Link chall: https://dreamhack.io/wargame/challenges/91
Hàm main:
``` c 
__int64 __fastcall main(int a1, char **a2, char **a3)
{
  int i; // [rsp+Ch] [rbp-174h]
  __int64 v5; // [rsp+18h] [rbp-168h] BYREF
  unsigned __int64 v6; // [rsp+20h] [rbp-160h] BYREF
  unsigned __int64 ptr; // [rsp+28h] [rbp-158h] BYREF
  __int64 v8; // [rsp+30h] [rbp-150h]
  __int64 v9; // [rsp+38h] [rbp-148h]
  unsigned __int64 n1; // [rsp+40h] [rbp-140h]
  unsigned __int64 n2; // [rsp+48h] [rbp-138h]
  FILE *stream; // [rsp+50h] [rbp-130h]
  unsigned __int64 v13; // [rsp+58h] [rbp-128h]
  char s[8]; // [rsp+60h] [rbp-120h] BYREF
  __int64 v15; // [rsp+68h] [rbp-118h]
  __int64 v16; // [rsp+70h] [rbp-110h]
  __int64 v17; // [rsp+78h] [rbp-108h]
  __int64 v18; // [rsp+80h] [rbp-100h]
  __int64 v19; // [rsp+88h] [rbp-F8h]
  __int64 v20; // [rsp+90h] [rbp-F0h]
  __int64 v21; // [rsp+98h] [rbp-E8h]
  __int64 v22; // [rsp+A0h] [rbp-E0h]
  __int64 v23; // [rsp+A8h] [rbp-D8h]
  __int64 v24; // [rsp+B0h] [rbp-D0h]
  __int64 v25; // [rsp+B8h] [rbp-C8h]
  __int64 v26; // [rsp+C0h] [rbp-C0h]
  __int64 v27; // [rsp+C8h] [rbp-B8h]
  __int64 v28; // [rsp+D0h] [rbp-B0h]
  __int64 v29; // [rsp+D8h] [rbp-A8h]
  __int64 v30; // [rsp+E0h] [rbp-A0h]
  __int64 v31; // [rsp+E8h] [rbp-98h]
  __int64 v32; // [rsp+F0h] [rbp-90h]
  __int64 v33; // [rsp+F8h] [rbp-88h]
  __int64 v34; // [rsp+100h] [rbp-80h]
  __int64 v35; // [rsp+108h] [rbp-78h]
  __int64 v36; // [rsp+110h] [rbp-70h]
  __int64 v37; // [rsp+118h] [rbp-68h]
  __int64 v38; // [rsp+120h] [rbp-60h]
  __int64 v39; // [rsp+128h] [rbp-58h]
  __int64 v40; // [rsp+130h] [rbp-50h]
  __int64 v41; // [rsp+138h] [rbp-48h]
  __int64 v42; // [rsp+140h] [rbp-40h]
  __int64 v43; // [rsp+148h] [rbp-38h]
  __int64 v44; // [rsp+150h] [rbp-30h]
  __int64 v45; // [rsp+158h] [rbp-28h]
  unsigned __int64 v46; // [rsp+168h] [rbp-18h]

  v46 = __readfsqword(0x28u);
  n1 = 0LL;
  stream = fopen("n.txt", "r");
  __isoc99_fscanf(stream, "%llu %llu", &v5, &v6);
  fclose(stream);
  while ( n1 <= 0xFCFCFCFC )
  {
    v8 = sub_12FE(v5);
    v9 = sub_12FE(v8 + 128);
    n1 = v9 * v8;
  }
  printf("n1 = %llu\n", n1);
  n2 = v6;
  v13 = (v8 - 1) * (v9 - 1);
  while ( n2 < v13 && sub_1249(n2, v13) != 1 )
    ++n2;
  printf("n2 = %llu\n", n2);
  *(_QWORD *)s = 0LL;
  v15 = 0LL;
  v16 = 0LL;
  v17 = 0LL;
  v18 = 0LL;
  v19 = 0LL;
  v20 = 0LL;
  v21 = 0LL;
  v22 = 0LL;
  v23 = 0LL;
  v24 = 0LL;
  v25 = 0LL;
  v26 = 0LL;
  v27 = 0LL;
  v28 = 0LL;
  v29 = 0LL;
  v30 = 0LL;
  v31 = 0LL;
  v32 = 0LL;
  v33 = 0LL;
  v34 = 0LL;
  v35 = 0LL;
  v36 = 0LL;
  v37 = 0LL;
  v38 = 0LL;
  v39 = 0LL;
  v40 = 0LL;
  v41 = 0LL;
  v42 = 0LL;
  v43 = 0LL;
  v44 = 0LL;
  v45 = 0LL;
  stream = fopen("flag.txt", "rb");
  __isoc99_fscanf(stream, "%255s", s);
  fclose(stream);
  if ( (strlen(s) & 3) != 0 )
  {
    puts("invalid length!");
    exit(-1);
  }
  stream = fopen("out.bin", "wb");
  for ( i = 0; i < strlen(s) >> 2; ++i )
  {
    ptr = encrypt(*(unsigned int *)&s[4 * i], n2, n1);
    fwrite(&ptr, 8uLL, 1uLL, stream);
  }
  fclose(stream);
  return 0LL;
}
```
``` c 
unsigned __int64 __fastcall sub_1289(__int64 a1, unsigned __int64 n2, unsigned __int64 n1)
{
  unsigned __int64 v4; // [rsp+20h] [rbp-10h]
  unsigned __int64 i; // [rsp+28h] [rbp-8h]

  if ( !n1 )
    exit(-1);
  v4 = 1LL;
  for ( i = 0LL; i < n2; ++i )                  // v4 = a1 ^ n2 % n1 
    v4 = a1 * v4 % n1;
  return v4;
}
```
Ok bài rất rõ ràng, dễ hiểu. Nhìn kiểu quái gì nó cũng là crypto đội lốt reverse (lol mình mới chơi CTF nên chưa đụng bài crypto nào). Từ ref đề bài cho ta đoán nó là RSA. 
Ta được cho 2 file out.txt và out.bin
![image](https://hackmd.io/_uploads/BJQbFGcXJe.png)
Phân tích thừa số nguyên tố thì:
* n1 = 65287 · 65419
* Vậy φ(n1) = (p-1)(q-1)
* Vừa hay gcd(φ(n1), n2) = 1 luôn nên ta xem e = n2
* Để tiện cho việc tính toán ta xem n = n1, e = n2, p = 65287, q = 65419

v4 = a1 ^ n2 % n1 ->  c = m ^ e (mod n)
Ở đây ta có sẵn c = v4, tìm m:
d = e ^ (-1) (mod φ(n))
m = c ^ d (mod n)
``` python
#!/usr/bin/python3 
from Crypto.Util.number import * 
from pwn import * 

n = 4271010253
e = 201326609
p = 65287
q = 65419
phi_n = (p - 1) * (q - 1)
d = pow(e, -1, phi_n)
out = open("out.bin", "rb").read()
ans = b''
for i in range(0, len(out), 8):
    x = out[i] | (out[i + 1] << 8) | (out[i + 2] << 16) | (out[i + 3] << 24)
    m = pow(x, d, n)
    ans += p32(m)
print(ans)
```
Bài này dễ nhưng nó là crypto nên mình xin để lại trên blog =))) (mong mấy bạn crypto đừng vào pressing mình hjx hjx)
## very-easy-wasm 
Mấy chall trên khá dễ, nên mình nghĩ mình làm những thứ mới mẻ hơn chút.
Link chall: https://dreamhack.io/wargame/challenges/762
Script js nằm trong index.html:
``` javascript
const btn = document.getElementById("btn")
        const input = document.getElementById("input")
        const out = document.getElementById("out")

        const memory = new WebAssembly.Memory({ initial: 1 });
        const u8Arr = new Uint8Array(memory.buffer)


        WebAssembly.instantiateStreaming(fetch('simple.wasm'), { "imports": { memory } })
            .then(obj => {
                btn.addEventListener("click", function () {
                    const flag = input.value

                    const res = new TextEncoder().encodeInto(flag, u8Arr)
                    const len = res.written

                    for (let i = 0; i < 9; i++) {
                        obj.instance.exports.something(len)
                        for (let j = 0; j < len / 2 | 0; j++) {
                            let t = u8Arr[len - j - 1]
                            u8Arr[len - j - 1] = u8Arr[j]
                            u8Arr[j] = t
                        }
                    }

                    out.innerText = Array.from(u8Arr.slice(0, len), function (byte) {
                        return ('0' + (byte & 0xFF).toString(16)).slice(-2);
                    }).join('')
                })
            })
```
Dùng wasm2wat hoặc wasm-decompile để đọc simple.wasm.

wasm2wat: 
``` wat
(module
  (type (;0;) (func (param i32) (result i32)))
  (type (;1;) (func (param i32)))
  (import "imports" "memory" (memory (;0;) 1))
  (func (;0;) (type 0) (param i32) (result i32)
    local.get 0
    i32.const 192
    i32.xor
    i32.const 175
    i32.add
    i32.const 255
    i32.and)
  (func (;1;) (type 0) (param i32) (result i32)
    local.get 0
    i32.const 191
    i32.xor
    i32.const 236
    i32.add
    i32.const 255
    i32.and)
  (func (;2;) (type 0) (param i32) (result i32)
    local.get 0
    i32.const 196
    i32.xor
    i32.const 171
    i32.add
    i32.const 255
    i32.and)
  (func (;3;) (type 1) (param i32)
    (local i32 i32 i32)
    i32.const 0
    local.tee 3
    local.set 1
    loop  ;; label = @1
      local.get 1
      i32.load8_u
      local.set 2
      local.get 1
      local.get 2
      local.get 3
      i32.const 3
      i32.rem_u
      call_indirect (type 0)
      i32.store8
      local.get 2
      local.set 3
      local.get 1
      i32.const 1
      i32.add
      local.tee 1
      local.get 0
      i32.lt_s
      br_if 0 (;@1;)
    end)
  (table (;0;) 3 funcref)
  (export "something" (func 3))
  (elem (;0;) (i32.const 0) func 0 1 2))
```
wasm-decompile:
``` wasm
import memory imports_memory;

table T_a:funcref(min: 3, max: 0);

function f_a(a:int):int {
  return (a ^ 192) + 175 & 255
}

function f_b(a:int):int {
  return (a ^ 191) + 236 & 255
}

function f_c(a:int):int {
  return (a ^ 196) + 171 & 255
}

export function something(a:int) {
  var d:int = 0;
  var b:int = d;
  loop L_a {
    var c:int = b[0]:ubyte;
    b[0]:byte = call_indirect(c, d % 3);
    d = c;
    b = b + 1;
    if (b < a) continue L_a;
  }
}
```
Có vẻ cái wasm-decompile này đọc thuận mắt hơn, dễ hiểu hơn cái wasm2wat kia ? Vì mình thấy code không phức tạp lắm nên không cần giải thích nhiều đâu nhỉ, code lại thôi:
``` python3
#!/usr/bin/python3
from pwn import * 

def f1(a):
    return (a ^ 192) + 175 & 255

def f2(a):
    return (a ^ 191) + 236 & 255 

def f3(a):
    return (a ^ 196) + 171 & 255

arr = bytearray(bytes.fromhex('aea9ee71d339c19b0e9c504213b105ebd8c78b9205d46ef347fe70ce4df8dcf4cd7c9f79'))
n = len(arr)
for k in range(9):
    for i in range(0, n // 2, 1):
        t = arr[n - i - 1]
        arr[n - i - 1] = arr[i]
        arr[i] = t
    d = 0
    ans = []
    for i in range(len(arr)):
        for c in range(256):
            if d % 3 == 0:
                if f1(c) == arr[i]:
                    d = c
                    ans.append(c)
                    break
            if d % 3 == 1:
                if f2(c) == arr[i]:
                    d = c 
                    ans.append(c)
                    break 
            if d % 3 == 2:
                if f3(c) == arr[i]:
                    d = c
                    ans.append(c)
                    break 
    arr = ans
            
print(bytes(arr))
```
Mình thấy script còn cải tiến được:
``` python3
#!/usr/bin/python3
from pwn import * 

def f1(a):
    return (a - 175 & 255) ^ 192

def f2(a):
    return (a - 236 & 255) ^ 191 

def f3(a):
    return (a - 171 & 255) ^ 196

arr = bytearray(bytes.fromhex('aea9ee71d339c19b0e9c504213b105ebd8c78b9205d46ef347fe70ce4df8dcf4cd7c9f79'))
n = len(arr)
for k in range(9):
    for i in range(0, n // 2, 1):
        t = arr[n - i - 1]
        arr[n - i - 1] = arr[i]
        arr[i] = t
    d = 0
    ans = []
    for i in range(len(arr)):
        if d % 3 == 0:
            d = f1(arr[i])
            ans.append(d)
        elif d % 3 == 1:
            d = f2(arr[i])
            ans.append(d)
        elif d % 3 == 2:
            d = f3(arr[i])
            ans.append(d) 
    arr = ans
            
print(bytes(arr))
```
Không hiểu bài này cho cái server làm gì nhỉ :v đâu cần lắm đâu ta. 
## passcode
Link chall: https://dreamhack.io/wargame/challenges/228
Và lại là DG house... à nhầm WebAssembly.
Check file main.js thì mình thấy mảng code chính là code WebAssembly, ta viết lại file:
``` python
#!/usr/bin/python3
from pwn import * 

code = [...] # Phần code dài quá nên mình xin lược bỏ

file = b''
for x in code:
    file += p8(x)
open("main.wasm", "wb").write(file)
```
Bài trước tại đơn giản nên mới làm được vậy thôi :)) Mình tìm được cái này cũng khá xịn xò https://github.com/nneonneo/ghidra-wasm-plugin. Mở ghidra lên và đọc thôi:
``` c

uint export::f(undefined *arr,int len)

{
  undefined uVar1;
  byte bVar2;
  uint uVar3;
  byte *idx;
  uint uVar4;
  
  uVar3 = 0xff;
  if (len == 0x19) {
    uVar3 = 0;
    do {
      idx = arr + (uVar3 & 0xff) % 0x19;
      bVar2 = *idx ^ *(byte *)(uVar3 + 0x420);
      *idx = bVar2;
      if ((uVar3 & 1) == 0) {
        uVar4 = (uint)(bVar2 >> (uVar3 & 7)) | (uint)bVar2 << (-uVar3 & 7);
      }
      else {
        uVar4 = (uint)bVar2 << (uVar3 & 7) | (uint)(bVar2 >> (-uVar3 & 7));
      }
      *idx = (byte)uVar4;
      *idx = *(byte *)((uVar4 & 0xff) + 0x520);
      uVar3 = uVar3 + 1;
    } while (uVar3 != 0x100);
    uVar1 = *arr;
    *arr = arr[0x18];
    arr[0x18] = uVar1;
    uVar1 = arr[1];
    arr[1] = arr[0x17];
    arr[0x17] = uVar1;
    uVar1 = arr[2];
    arr[2] = arr[0x16];
    arr[0x16] = uVar1;
    uVar1 = arr[3];
    arr[3] = arr[0x15];
    arr[0x15] = uVar1;
    uVar1 = arr[4];
    arr[4] = arr[0x14];
    arr[0x14] = uVar1;
    uVar1 = arr[5];
    arr[5] = arr[0x13];
    arr[0x13] = uVar1;
    uVar1 = arr[6];
    arr[6] = arr[0x12];
    arr[0x12] = uVar1;
    uVar1 = arr[7];
    arr[7] = arr[0x11];
    arr[0x11] = uVar1;
    uVar1 = arr[8];
    arr[8] = arr[0x10];
    arr[0x10] = uVar1;
    uVar1 = arr[9];
    arr[9] = arr[0xf];
    arr[0xf] = uVar1;
    uVar1 = arr[10];
    arr[10] = arr[0xe];
    arr[0xe] = uVar1;
    uVar1 = arr[0xb];
    arr[0xb] = arr[0xd];
    arr[0xd] = uVar1;
    uVar3 = memcmp(arr,(void *)0x400,0x19);
  }
  return uVar3 & 0xff;
}
```
Đọc file main.js thì biết được đây là hàm duy nhất chúng ta cần quan tâm. Thấy có vẻ rev chay được nên rev chay hết nhé (~~thật ra vì mình thấy dùng z3 không được~~). Đoạn dịch bit mình mò thì thấy làm ngược lại là được, mấy đoạn còn lại thì dễ rồi.
``` python
#!/usr/bin/python3 

bVar2 = 110
uVar3 = 2
uVar4 = (bVar2 >> (uVar3 & 7)) | bVar2 << (-uVar3 & 7)
print(uVar4) 
uVar4 &= 0xff 
x = (uVar4 << (uVar3 & 7)) | uVar4 >> (-uVar3 & 7)
x &= 0xff
print(x) 
```
Script của mình:
``` python
#!/usr/bin/python3

arr = bytearray(bytes.fromhex('2d f8 01 81 6d 94 3d 86 f1 80 a9 1f ca 33 08 56 81 c5 2f 53 e9 5f 33 be 6e 00'))
xor_arr = bytes.fromhex("8d 8b de d0 34 1e 55 2f ca 92 62 fd 6b 4e 24 89 6e 0b ef 8a 42 9a 1a 50 9d ff d1 b4 b5 cc a1 70 e6 b0 69 b8 22 5b 7b 41 ae e3 82 4f dc af 05 ee fe 47 71 4c 96 4a fa 21 53 85 cf c8 a2 c0 03 73 30 80 be 72 f9 c5 d3 fb 7d 25 14 b3 ba f1 12 11 c1 19 a0 98 7f 5d 90 bc aa 5a 09 e5 06 2d 6f ac 39 0e da 23 2a 27 01 84 91 29 0c b6 3d e8 ce c7 ab f3 59 66 28 1f 78 51 07 32 00 88 f7 60 d8 ec d4 4d 8e 9b 1d 1c 35 7c 97 61 3e 46 d9 eb e2 a8 36 3c 94 86 44 99 52 dd 8c 37 7a f5 f8 5e db 75 ea fc 4b df d2 45 3b 17 c2 a3 2c 13 40 7e 56 08 87 33 81 93 9f 31 bd 2b 48 0a 10 65 9e 64 1b 04 f0 c6 ed a4 79 a9 e7 9c a7 5f f4 3a 26 20 16 38 6a cd 76 c3 43 e4 49 f2 cb d7 6d 68 e0 0f a6 e9 58 77 b9 74 02 c9 d5 2e b1 d6 e1 95 18 67 3f 0d 57 ad 15 8f 83 63 bf bb f6 b7 54 5c c4 b2 6c a5 45 67 ec be c5 de 6d 23 e6 db f9 e3 a0 7e 7a 5e 41 e9 1f 17 29 c7 84 f3 1c d1 f2 3f ae 65 0c 1d e0 e8 6b 00 ed 18 31 55 5d 95 a5 5a e1 51 d5 b5 0e 50 b9 d9 9f f7 32 c3 d4 ef a9 62 98 9c c1 72 bb ea 60 38 d7 3e 01 2b 27 a2 99 86 bd 61 b7 7b a7 cc f4 aa c8 36 9a 4e 3b 52 0f 16 d8 9e dc 48 c0 2a 39 44 c9 c4 14 6f 64 02 cf 57 f5 c6 85 2e 75 e4 08 63 2c af 59 cb 47 7d 79 78 c2 8c ce 10 49 87 26 4b 8e 77 94 03 f6 97 4d 35 83 0a 1e 71 f0 3a 53 5f e7 15 69 90 cd 68 04 81 9d fb 42 22 24 a8 2f 82 b4 ab 1b 19 28 07 ad 74 30 8d b2 e5 ca fc a6 8a f1 09 0d 89 91 66 76 92 eb 88 46 fe d3 6e ee 58 40 6c b6 13 ac a4 0b b1 a1 7f 3c 8b 54 33 34 5b b0 bf d0 93 73 4a 70 bc 43 df 4c dd 37 ba d2 ff f8 e2 a3 3d 80 20 2d 1a 05 11 5c 06 b8 21 96 8f fa b3 12 7c d6 da 4f fd 25 9b 6a 56 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00")

for i in range(0, 0x19 // 2, 1):
    arr[i], arr[0x19 - i - 1] = arr[0x19 - i - 1], arr[i]
    
mp = {}
for i in range(0x100, 0x100 + 0x100):
    assert(xor_arr[i] not in mp)
    mp[xor_arr[i]] = i
    
for uVar3 in range(0xff, -1, -1):
    idx = (uVar3 & 0xff) % 0x19
    # arr[idx] = xor_arr[0x100 + (uVar4 & 0xff)]
    uVar4 = mp[arr[idx]] - 0x100 
    if (uVar3 & 1) == 0:
        bVar2 = (uVar4 << (uVar3 & 7)) | uVar4 >> (-uVar3 & 7)
    else:
        bVar2 = (uVar4 >> (uVar3 & 7)) | uVar4 << (-uVar3 & 7) 
    bVar2 &= 0xff
    arr[idx] = bVar2 ^ xor_arr[uVar3]
print(arr)
```
## pikachu_volleyball
Link chall: https://dreamhack.io/wargame/challenges/1052
Điều đầu tiên làm mình ấn tượng là cái game này thật sự quá đáng............. yêu =))) 
Chủ yếu logic nằm hết ở file main.bundle.js (khi mình làm xong rồi thì biết nó gọi là webpack obfuscation ?), mình deobfuscate trước đã. Cũng may vì code khá đẹp, tên biến rất dễ đọc. Vì đây là game nên trước tiên mình Ctrl + F tìm các keyword mà ta quen thuộc khi chơi game. Tìm tới chữ "score" thì ra được hàm này có vẻ khá quan trọng trong chương trình, nó tác động tới số điểm người chơi:
``` javascript
['round']() {
const _0x4b6216 = 0x1 === this.keyboardArray[0x0].powerHit || 0x1 === this.keyboardArray[0x1].powerHit;
if (true === this.physics.player1.isComputer && true === this.physics.player2.isComputer && _0x4b6216) {
  this.frameCounter = 0x0;
  this.view.game.visible = false;
  return void (this.state = this.intro);
}
const _0x277fa9 = this.physics.runEngineForNextFrame(this.keyboardArray);
this.playSoundEffect();
this.view.game.drawPlayersAndBall(this.physics);
this.view.game.drawCloudsAndWave();
if (true === this.gameEnded) {
  if (true === this.gameFinished) {
    var _0x2c4c8c = new TextDecoder().decode(this.message);
    this.view.game.drawGameEndMessage(this.frameCounter, _0x2c4c8c);
  } else {
    this.view.game.drawGameEndMessage(this.frameCounter, '');
  }
  this.frameCounter++;
  return void ((this.frameCounter >= this.frameTotal.gameEnd || this.frameCounter >= 0x46 && _0x4b6216) && (this.frameCounter = 0x0, this.view.game.visible = false, this.state = this.intro));
}
if (_0x277fa9 && false === this._isPracticeMode && false === this.roundEnded && false === this.gameEnded) {
  if (this.physics.ball.punchEffectX < 216) {
    this.isPlayer2Serve = true;
    this.scores[0x1] += 0x1;
    this.calcMessage();
    if (this.scores[0x1] > this.winningScore) {
      this.gameEnded = true;
      this.physics.player1.isWinner = false;
      this.physics.player2.isWinner = true;
      this.physics.player1.gameEnded = true;
      this.physics.player2.gameEnded = true;
    }
    if (this.scores[0x1] == this.winningScore) {
      this.gameEnded = true;
      if (0x0 == this.scores[0x0]) {
        this.gameFinished = true;
      }
      this.physics.player1.isWinner = false;
      this.physics.player2.isWinner = true;
      this.physics.player1.gameEnded = true;
      this.physics.player2.gameEnded = true;
    }
  } else {
    this.isPlayer2Serve = false;
    this.scores[0x0] += 0x1;
    if (this.scores[0x0] >= this.winningScore) {
      this.gameEnded = true;
      this.physics.player1.isWinner = true;
      this.physics.player2.isWinner = false;
      this.physics.player1.gameEnded = true;
      this.physics.player2.gameEnded = true;
    }
  }
  this.view.game.drawScoresToScoreBoards(this.scores);
  if (false === this.roundEnded && false === this.gameEnded) {
    this.slowMotionFramesLeft = this.SLOW_MOTION_FRAMES_NUM;
  }
  this.roundEnded = true;
}
if (true === this.roundEnded && false === this.gameEnded && 0x0 === this.slowMotionFramesLeft) {
  this.view.fadeInOut.changeBlackAlphaBy(0.0625);
  this.state = this.afterEndOfRound;
}
}
```
``` javascript
["drawGameEndMessage"](_0x2097bc, _0x133a1a) {
const _0xebf299 = this.messages.gameEnd;
const _0xc813d0 = _0xebf299.texture.width;
const _0x422e94 = _0xebf299.texture.height;
if (0x0 === _0x2097bc) {
  _0xebf299.visible = true;
  this.score.text = _0x133a1a;
  if ('' != _0x133a1a) {
    this.score.visible = true;
  }
}
if (_0x2097bc < 0x32) {
  const _0x400011 = 0x2 * Math.floor((0x32 - _0x2097bc) * _0xc813d0 / 0x32);
  const _0x396c59 = 0x2 * Math.floor((0x32 - _0x2097bc) * _0x422e94 / 0x32);
  _0xebf299.x = 0xd8 - _0xc813d0 / 0x2 - _0x400011;
  _0xebf299.y = 0x32 - _0x396c59;
  _0xebf299.width = _0xc813d0 + 0x2 * _0x400011;
  _0xebf299.height = _0x422e94 + 0x2 * _0x396c59;
} else {
  _0xebf299.x = 0xd8 - _0xc813d0 / 0x2;
  _0xebf299.y = 0x32;
  _0xebf299.width = _0xc813d0;
  _0xebf299.height = _0x422e94;
}
}
```
Khi score >= winningScore thì nghĩa là endGame, nó in ra cái message gì đó, mà message khi chúng ta win game thì khác gì flag ? 
Mảng message được khởi tạo trong chương trình:
``` javascript
this.message = new Uint8Array([0x44, 0x49, 0x7b, 0x60, 0x63, 0x46, 0x73, 0x2, 0x22, 0x21, 0x50, 0x1a, 0xdb, 0xf6, 0xab, 0xd9, 0x146, 0x154, 0x171, 0x10a, 0x1f1, 0x1cd, 0x1c5, 0x27e, 0x22e, 0x22e, 0x2c3, 0x2e9, 0x37f, 0x30d, 0x3bb, 0x7d]);
```
Ok ta in ra thử thì thấy nó không có ý nghĩa gì cả, để ý một đoạn nhỏ ở code đầu tiên, đây cũng là đoạn gây thay đổi cho mảng message:
``` javascript
this.scores[0x1] += 0x1;
this.calcMessage();
```
``` javascript
["calcMessage"]() {
        var _0x5e4053 = this.scores[0x1];
        if (0x2 != _0x5e4053 && 0x1f != _0x5e4053 && _0x5e4053 < 0x21) {
          this.message[_0x5e4053] ^= _0x5e4053 * _0x5e4053;
        }
      }
```
Vậy cái this.scores[0x1] là điểm bên phía người chơi, mỗi khi ta tăng từng điểm thì flag cũng sẽ thay đổi theo. Ta code giả lập lại quá trình tăng điểm. Script của mình: 
``` python
#!/usr/bin/python3

message = [0x44, 0x49, 0x7b, 0x60, 0x63, 0x46, 0x73, 0x2, 0x22, 0x21, 0x50, 0x1a, 0xdb, 0xf6, 0xab, 0xd9, 0x146, 0x154, 0x171, 0x10a, 0x1f1, 0x1cd, 0x1c5, 0x27e, 0x22e, 0x22e, 0x2c3, 0x2e9, 0x37f, 0x30d, 0x3bb, 0x7d]
for i in range(0x20):
    if i == 2 or i == 0x1f:
        continue
    message[i] ^= i * i
print(''.join(chr(x & 0xff) for x in message))
```
## Slooooow
Link chall: https://dreamhack.io/wargame/challenges/1150
Chạy file:
``` linux
❯ ./main
   _____ __
  / ___// /___  ____  ____  ____  ____ _      __
  \__ \/ / __ \/ __ \/ __ \/ __ \/ __ \ | /| / /
 ___/ / / /_/ / /_/ / /_/ / /_/ / /_/ / |/ |/ /
/____/_/\____/\____/\____/\____/\____/|__/|__/
Yes, it's actually slow!
...According to the master theorem
from 'Introduction to Algorithms'.
```
Chạy xong nó đứng 1 hồi lâu, chắc phải tốn mấy năm mới xong nhỉ ? Nên mình đi phân tích chương trình vậy. Check hàm main:
``` c++
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  sub_1249(a1, a2, a3);
  sub_1290();
  sub_138B(0LL, &unk_1869F);
  sub_14A9();
  return 0LL;
}
```
Hàm sub_1290 chỉ in mấy cái chữ ra màn hình thôi, check 2 hàm kia:
``` c 
__int64 __fastcall sub_138B(unsigned int a1, unsigned int a2)
{
  __int64 result; // rax
  unsigned int v3; // [rsp+18h] [rbp-8h]
  int v4; // [rsp+1Ch] [rbp-4h]

  if ( (unsigned int)sub_1313(dword_4020[a1], dword_4020[a2]) )
  {
    v3 = dword_4020[a1];
    dword_4020[a1] = dword_4020[a2];
    dword_4020[a2] = v3;
  }
  result = a2 - a1;
  if ( (int)result > 1 )
  {
    v4 = (int)(a2 - a1 + 1) / 3;
    sub_138B(a1, a2 - v4);
    sub_138B(v4 + a1, a2);
    return sub_138B(a1, a2 - v4);
  }
  return result;
}
```
``` c++
unsigned __int64 sub_14A9()
{
  unsigned int v1; // [rsp+8h] [rbp-68h] BYREF
  unsigned int i; // [rsp+Ch] [rbp-64h]
  __int64 v3; // [rsp+10h] [rbp-60h]
  __int64 v4; // [rsp+18h] [rbp-58h]
  char v5[72]; // [rsp+20h] [rbp-50h] BYREF
  unsigned __int64 v6; // [rsp+68h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  v3 = EVP_sha256();
  v4 = EVP_MD_CTX_new();
  EVP_DigestInit_ex(v4, v3, 0LL);
  EVP_DigestUpdate(v4, dword_4020, &unk_61A80);
  EVP_DigestFinal_ex(v4, v5, &v1);
  printf("Flag is: DH{");
  for ( i = 0; i < v1; ++i )
    printf("%02x", (unsigned __int8)v5[i]);
  puts("}");
  return v6 - __readfsqword(0x28u);
}
```
Ok thì mình thấy vấn đề chính của ta nằm ở hàm sub_138B, mình thấy nó chia để trị rất cồng kềnh nên độ phức tạp khá là cao. Hàm đó nó thực hiện các thao tác gì đó với mảng dword_4020, rồi mảng dword_4020 lại dùng để sha256 để ra flag. Hướng của mình là đọc lấy giá trị mảng dword_4020 trong chương trình, rồi tối ưu lại thuật toán sao cho đủ nhanh, rồi viết lại mảng dword_4020 trong chương trình chính của mình. Lúc này mảng dword_4020 đã được áp dụng thuật toán chia để trị trên rồi nên khúc gọi hàm sub_138B ta patch lại thành nop hết.
Code của mình:
``` c++
#include <bits/stdc++.h>
using namespace std;
unsigned int dword_4020[0x1869f + 5];
bool sub_1313(unsigned int a1, unsigned int a2)
{
  return ((a1 >> 25) | (a1 >> 9) & 0xFF80 | (a1 << 6) & 0x3F0000 | (a1 << 22)) <= ((a2 >> 25) | (a2 >> 9) & 0xFF80 | (a2 << 6) & 0x3F0000 | (a2 << 22));
}
void sub_138B(unsigned int a1, unsigned int a2)
{
    long long result; // rax
  unsigned int v3; // [rsp+18h] [rbp-8h]
  int v4; // [rsp+1Ch] [rbp-4h]

  if ( (unsigned int)sub_1313(dword_4020[a1], dword_4020[a2]) )
  {
    v3 = dword_4020[a1];
    dword_4020[a1] = dword_4020[a2];
    dword_4020[a2] = v3;
  }
  result = a2 - a1;
  if ( (int)result > 1 )
  {
    v4 = (int)(a2 - a1 + 1) / 3;
    sub_138B(a1, a2 - v4);
    sub_138B(v4 + a1, a2);
    sub_138B(a1, a2 - v4);
  }
//   return;
}
int main() {
    int size = 0x1869f + 1;
    FILE *file;
    file = fopen("main", "rb");
    fseek(file, 0x3020, SEEK_SET);
    size_t dwordRead = fread(dword_4020, 4, size, file);
    fclose(file);
    sort(dword_4020, dword_4020 + size, sub_1313);
    file = fopen("main.cp", "r+b");
    fseek(file, 0x3020, SEEK_SET);
    fwrite(dword_4020, 4, size, file);
    return 0;
}
```
Thuật toán chia để trị trên mình thấy nó giống như một hàm sort, nên mình tối ưu theo hướng đó. Rồi viết lại mảng dword_4020 ở chương trình thôi. 
Fun fact: trong quá trình làm mình quên mất cộng size thêm 1, làm mình mất mấy ngày để nhận ra ???
## instrs
Link chall: https://dreamhack.io/wargame/challenges/1275
Hàm main:
``` c 
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  int v4; // [rsp+1Ch] [rbp-24h]
  char *lineptr; // [rsp+20h] [rbp-20h] BYREF
  size_t n; // [rsp+28h] [rbp-18h] BYREF
  FILE *stream; // [rsp+30h] [rbp-10h]
  unsigned __int64 v8; // [rsp+38h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  sub_12EA(a1, a2, a3);
  memset(input, 0, sizeof(input));
  memset(byte_4060, 0, 8uLL);
  idx_2 = 0;
  idx_1 = 0;
  puts("Enter Your Program");
  read(0, input, 8uLL);
  byte_4058 = 0;
  v4 = sub_136D();
  printf("Result: %d\n", (unsigned int)v4);
  if ( v4 > 99999 )
  {
    lineptr = 0LL;
    n = 0LL;
    stream = fopen("./flag", "r");
    getline(&lineptr, &n, stream);
    printf("Good, get the flag: %s", lineptr);
    free(lineptr);
    fclose(stream);
  }
  return 0LL;
}
```
``` c
__int64 sub_136D()
{
  unsigned int v0; // eax
  unsigned int v2; // [rsp+8h] [rbp-8h]
  int v3; // [rsp+Ch] [rbp-4h]

  v2 = 0;
  while ( 1 )
  {                                             // open
    while ( 1 )
    {
      v3 = 0;
      ++v2;
      v0 = input[idx_1];
      if ( v0 > 0x72 )
      {
LABEL_17:
        puts("No Hack!");
        exit(-1);
      }
      if ( input[idx_1] < 0x61u )
        break;
      switch ( input[idx_1] )
      {
        case 'a':
          if ( !byte_4060[idx_2] )
            goto LABEL_18;
          idx_1 = 0;
          break;
        case 'e':
          v3 = 1;
          goto LABEL_18;
        case 'l':
          if ( --idx_2 < 0 )
            idx_2 = 0;
          goto LABEL_18;
        case 'n':
          goto LABEL_18;
        case 'r':
          if ( ++idx_2 > 7 )
            idx_2 = 7;
          goto LABEL_18;
        default:
          goto LABEL_17;
      }
    }                                           // close
    if ( v0 == '+' )
    {
      ++byte_4060[idx_2];
    }
    else
    {
      if ( v0 != '-' )
        goto LABEL_17;
      --byte_4060[idx_2];
    }
LABEL_18:
    if ( v3 )
      return v2;
    if ( ++idx_1 > 7 )
      idx_1 = 7;
  }
}
```
Chương trình bắt nhập một chuỗi chỉ gồm các kí tự 'nalr+e-' và chuỗi này có độ dài tối đa là 8. Từ các kí tự này nó sẽ thực hiện thao tác gì đó, mỗi lần loop nó tăng biến đếm v4, mục tiêu của chúng ta là làm cho v4 > 99999 thì nó nhả flag. Sau khi đọc hàm trên thì ta thấy kí tự cuối cùng của chuỗi chắc chắn là chữ 'e'. Giờ mình có 2 hướng: 
- Brute force 7 kí tự còn lại, đpt O(6 ^ 7), nhưng thực tế là ta phải tương tác với chương trình nên độ phức tạp này tăng lên rất nhiều lần :D 
- Tự craft chuỗi sao cho nó thỏa mãn là được, cách này khá khó vì chương trình có kiểm tra giới hạn thời gian.

Bằng một phép màu nào đó mình đã tìm ra được chuỗi thỏa mãn là +r++alae...
## ROVM
Link chall: https://dreamhack.io/wargame/challenges/113
Bài này debug + đọc assembly khá là nhọc nhằn nên mình sẽ không nói quá chi tiết. Mình có viết script để dịch file chain sang assembly.
``` python
#!/usr/bin/python3 
from capstone import Cs, CS_ARCH_X86, CS_MODE_64
from pwn import * 

mp = {}
def disassemble_code(binary_code) -> None:
    disassembler = Cs(CS_ARCH_X86, CS_MODE_64)
    
    for instruction in disassembler.disasm(binary_code, 0x1224000):
    	mp[f'0x{instruction.address:07x}'] = f'{instruction.mnemonic} {instruction.op_str}'
        # print(f"0x{instruction.address:08x}: {instruction.mnemonic} {instruction.op_str}")
    return None


opcode = open("opcode", "rb").read()
disassemble_code(opcode)
chain = open("chain", "rb").read()
for i in range(len(chain) // 8):
	x = hex(u64(chain[8 * i: 8 * i + 8]))
	print(hex(0x1225000 + 8 * i), end = ": ")
	print(x, end = "")
	if x in mp:
		print(':', end = "")
		idx = u64(chain[8 * i: 8 * i + 8])
		while True:
			if hex(idx) in mp:
				asm_code = mp[hex(idx)]
				print(f' {asm_code};', end = "")
				if "ret" in asm_code:
					break
			idx += 1 
		print()
	else:
		value = u64(chain[8 * i: 8 * i + 8])
		print()
```
Giờ có code assembly của file chain rồi, kết hợp cả rev chay và debug thì nó dễ hơn mỗi debug đúng không. Hướng tiếp theo của mình đã quá rõ ràng rồi nên mình cũng lười nói tiếp hehe =))))
## Theori of Relativity
Link chall: https://dreamhack.io/wargame/challenges/638
Bài này thuộc dạng flag checker cơ bản thôi.
Hàm main:
``` c
__int64 __fastcall main(int a1, char **a2, char **a3)
{
  char s[104]; // [rsp+0h] [rbp-70h] BYREF
  unsigned __int64 v5; // [rsp+68h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  printf("Key: ");
  fgets(s, 100, stdin);
  s[strcspn(s, "\n")] = 0;
  if ( (unsigned __int8)check(&byte_6020, s) )
    printf("Congrats! Flag is DH{%s} ...if you didn't cheat!\n", s);
  else
    puts("Hmm... :thinking_face:");
  return 0LL;
}
```
``` c 
bool __fastcall check(_BYTE *a1, unsigned __int8 *a2)
{
  unsigned __int8 v5; // [rsp+12h] [rbp-2Eh]
  unsigned __int8 v6; // [rsp+13h] [rbp-2Dh]
  char i; // [rsp+18h] [rbp-28h]
  __int128 v8; // [rsp+20h] [rbp-20h]

  v8 = 0uLL;
  for ( i = 0; ; ++i )
  {
    v5 = *a1 + i * i;
    v6 = *a2;
    if ( !__popcnt(v5) )
      break;
    if ( (i & 1) != 0 )
      v8 += (unsigned __int8)(v6 ^ v5);
    else
      v8 += (unsigned __int8)(v5 - v6);
    ++a1;
    ++a2;
  }
  return (int)__popcnt(v6) + v8 == 0;
}
```
Ta biết rằng popcnt >= 0 và giá trị unsigned luôn >= 0. Nên để biểu thức thỏa mãn thì chỉ có v8 = 0 và popcnt(v6) = 0 (uhm vì v5 lúc đó = 0 nên giá trị cuối v6 = 0 thì cũng là đương nhiên). Nghĩa là ở mọi vòng lặp v6 = v5, ta viết script đơn giản như sau:
``` python
a1 = bytes.fromhex('...')

for i in range(len(a1)):
    v5 = (a1[i] + i * i) & 0xff 
    if v5 == 0:
        break
    print(chr(v5), end = "")
```
Nhưng để ý kĩ thì có 2 hàm init được gọi trước main:
``` c++
__int64 init_func1()
{
  if ( ptrace(PTRACE_TRACEME, 0LL, 0LL, 0LL) >= 0 )
    return (unsigned int)(unsigned __int8)byte_6020-- - 1;
  else
    return (unsigned int)(unsigned __int8)byte_6020++ + 1;
}
```
Nếu không debug thì nó trả về nhánh trên, nếu có debug thì trả về nhánh dưới.
``` c++
unsigned __int64 init_func2()
{
  _BYTE *v0; // rax
  int i; // [rsp+4h] [rbp-2Ch]
  _BYTE *v3; // [rsp+8h] [rbp-28h]
  __int64 v4[3]; // [rsp+10h] [rbp-20h]
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  v4[0] = (__int64)init_func1;
  v4[1] = (__int64)init_func2;
  v4[2] = (__int64)check;
  for ( i = 0; i <= 2; ++i )
  {
    v3 = (_BYTE *)v4[i];
    while ( *v3 != 0xC3 )
    {
      v0 = v3++;
      if ( *v0 == 0xCC )
      {
        byte_6020 += 2;
        return v5 - __readfsqword(0x28u);
      }
    }
  }
  byte_6020 -= 2;
  return v5 - __readfsqword(0x28u);
}
```
0xc3 chính là opcode của ret, vậy là vòng lặp while đó có ý nghĩa duyệt từ đầu tới cuối hàm. Còn 0xcc thì nếu ta đặt breakpoint ở hàm đó rồi debug nó thì sẽ có thêm int3 (0xcc) ở hàm đó, nếu chúng ta debug mà không để ý hàm này thì sẽ bị sai. Vậy giờ mình rev hết thì chỉ cần a1[0] -= 3 (a1 = byte_6020) là giải quyết được vấn đề... Ok nhưng nó vẫn sai là sao ???
Tới khúc này mình đặt breakpoint trước khi thực hiện cả 2 hàm init đó. Mình nhận thấy mảng byte_6020 có giá trị hoàn toàn khác với lúc chúng ta không debug...
![image](https://hackmd.io/_uploads/SkI93OF4Jx.png)
Ok vì mình đặt breakpoint trước khi 2 hàm init kịp làm gì, nên mảng của mình sẽ còn nguyên vẹn chưa bị tác động gì. Script của mình:
```python
a1 = bytearray(bytes.fromhex('...'))
a1[0] -= 3
for i in range(len(a1)):
    v5 = (a1[i] + i * i) & 0xff 
    if v5 == 0:
        break
    print(chr(v5), end = "")
```
Sau khi làm xong, mình đọc writeup của người khác thì biết vấn đề nằm ở ELF RELA Relocation Table, nếu mình chỉ dựa vào static analysis thôi thì sẽ không giải được bài này, mình cũng học được cách giấu giá trị ở ELF RELA Relocation Table =)))











