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

- 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.



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;
}
```

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

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...

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