# DEADLINE 5: ANTI_DEBUG CHALLENGE
## Anti_debug 1
* Run thử file .exe thì ta nhận thấy như sau:

* Chương trình yêu cầu user nhập input là `flag` vào sao đó sẽ check xem là flag đúng hoặc sai. Tiến hành nhập thử thì hiện ra text box báo sai

* Qua một số kinh nghiệm chơi các bài tương tự thì em nghĩ `flag` bên trong đa phần sẽ được xor hoặc được encode bằng algo encode và cmp với chuỗi mặc định nào đó, ta phải xor hoặc encode ngược lại để lấy `flag input`. Nói chung phải disas xem mới biết được
* Quăng chall vào IDA, ta sẽ thấy được hàm nhận `Input`
* Lướt xuống dưới ta sẽ thầy hai khối `try` và `catch` như sau:
**Try:**
```
; try {
mov [ebp+var_4], 0
mov [ebp+var_BC], 0Fh
push 64h ; 'd' ; Size
push 0 ; Val
lea eax, [ebp+var_78]
push eax ; void *
call _memset
add esp, 0Ch
lea ecx, [ebp+var_78]
push ecx
push offset szs ; "%s"
call sub_401050
add esp, 8
test eax, eax
jnz short loc_4012DC
```
**Catch:**
```
loc_401307:
; catch(int) // owned by 40129F
push 64h ; 'd'
lea edx, [ebp+var_78]
mov ecx, offset szBKSEECCCC ; "BKSEECCCC!!!"
call sub_401220
add esp, 4
nop
mov eax, offset loc_401AD5
loc_40131F:
jmp short near ptr loc_40131F+1
```
* Khối try:
* Gọi hàm memset để đặt toàn bộ vùng nhớ bắt đầu từ địa chỉ var_78 với kích thước 100 (0x64) byte thành giá trị 0.
* Hàm sub_401050 được gọi với tham số là con trỏ đến chuỗi var_78. Nội dung hàm sub_401050 như sau:
```
int sub_401050(int a1, ...)
{
_DWORD *v1; // eax
FILE *v3; // [esp-10h] [ebp-14h]
va_list va; // [esp+10h] [ebp+Ch] BYREF
va_start(va, a1);
v3 = __acrt_iob_func(0);
v1 = (_DWORD *)sub_401010();
return sub_40B5C1(*v1, v1[1], v3, a1, 0, va);
}
```
1. Nhận các đối số kiểu int là a1 và các đối số biến đổi.
2. Khởi tạo một danh sách đối số biến đổi bằng va_start.
3. Gán giá trị trả về của __acrt_iob_func(0) vào biến v3.
4. Gọi hàm sub_401010 và gán giá trị trả về cho con trỏ v1.
5. Gọi hàm sub_40B5C1 với các tham số tương ứng(`hàm sub_40B5C1 kiểm tra và xử lý các đối số đầu vào a3 và a4. Nếu cả hai đối số này khác 0, hàm thực hiện một cuộc gọi hàm khác thông qua __crt_seh_guarded_call. Nếu một trong hai đối số là 0, hàm đặt giá trị 0x16 cho errno và gọi _invalid_parameter_noinfo.`).
6. Trả về giá trị trả về của hàm sub_40B5C1.
* Kiểm tra giá trị trả về của sub_401050. Nếu giá trị này không bằng 0 (được kiểm tra bằng cách so sánh với 0), chương trình sẽ nhảy đến địa chỉ loc_4012D
* Lướt tiêp xuống dưới ta có thể thấy một đoạn lệnh nữa
```
mov edx, [ebp+var_BC]
mov [ebp+pExceptionObject], edx
push offset __TI1H ; pThrowInfo
lea eax, [ebp+pExceptionObject]
push eax ; pExceptionObject
call _CxxThrowException(x,x)
```
* Này dùng để gọi hàm _CxxThrowException có tác dụng như là Handler Exception
* Em thử đặt breakpoint ở trước lệnh call để debug thử

* Khi `ni` đến lệnh call thì process đã bị out
* Thật ra em vẫn chưa hiểu hết được các hàm trong chall này có tác dụng rõ ràng như thế nào nhưng xem mã pseudo ban đầu thì có thể hình dung như sau
```
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [esp+0h] [ebp-114h] BYREF
int v5; // [esp+54h] [ebp-C0h] BYREF
int v6; // [esp+58h] [ebp-BCh]
char v7[100]; // [esp+9Ch] [ebp-78h] BYREF
char *v8; // [esp+104h] [ebp-10h]
int v9; // [esp+110h] [ebp-4h]
v8 = &v4;
sub_401020("Input the flag: ", v4);
v9 = 0;
v6 = 0xF;
sub_403130(v7, 0, 0x64);
if ( !sub_401050("%s", v7) )
{
return 0;
}
if ( v6 < 0x12 )
{
v5 = v6;
sub_40468D(&v5, &_TI1H);
}
return 0;
}
```
* Chương trình sẽ nhận Input từ người dùng vào và sao đó thực hiện phép xor ở hàm `sub_401020` với chuỗi 'BKSEECCCC!!!' sau đó cmp với các kí tự ở `loc_401AD5:`


* Ngoaì ra trong quá trình debug, nếu đặt breakpoint ở `loc_401307` sẽ thấy được một hàm anti_debug

* Đây là PEB một `Manual Check` trong `Debug Flags`. Tại vị trí thanh ghi FS trỏ vào 30h thì trả về 4byte offset của PEB.Khi vào PEB,tại vị trí PEB+2h là cờ debug.Vì thế nếu check được cờ này bật hay không là xác định được chương trình có bị debug không. Còn nếu bằng 0 thì sẽ jump tới loc_AD1AD5 cũng là hàm chứa các kí tự bên trên.
* Tiền hành xor ngược lại giữa `loc_AD1AD5` với chuỗi `BKSEECCCC!!!` để lấy input flag
```
string_1="BKSEECCCC!!!"
lst=[0]*200
input=[0,0,0,0,6,0x38,0x26,0x77,0x30,0x58,0x7E,0x42,0x2A,0x7F,0x3F,0x29,0x1A,0x21,0x36,0x37,0x1C,0x55,0x49,0x12,0x30,0x78,0x0C,0x28,0x30,0x30,0x37,0x1C,0x21,0x12,0x7E,0x52,0x2D,0x26,0x60,0x1A,0x14,0x2D,0x37,0x72,0x1C,0x45,0x44,0x43,0x37,0x2C,0x6C,0x7A,0x38,0]
for i in range(len(input)):
lst[i]=(input[i])^ord(string_1[i%len(string_1)])
print("".join(chr(i) for i in lst))
```
* `BKSEC{e4sy_ch4ll_but_th3r3_must_b3_som3_Qnt1_debug??}`
---> Bài này em cảm thấy có anti debug cũng như k, k làm khó gì khi solve chỉ cần patch một tí và đặt breakpoint đúng chỗ sẽ đơn giản chương trình hơn đi rất nhiều
## Anti debug 2
* Quăng vào DIE để check thì đây là file ELF_64bit nên em run thử trên máy ảo xem sao

* Kết quả cũng tương tự như bài 1, đều nhận Input từ người dùng và check Input
* Quăng vào IDA disas thử

* Chương trình kinh dị, rất dài và nhiều hàm
* Tiến hành debug bằng `Remote Linux`.
```
__int64 __fastcall main(int a1, char **a2, char **a3)
{
void (__noreturn *v4)();
v4 = sub_5596988E6100;
return sub_5596988FBAA0((__int64)&v4, (__int64)&off_55969892FF18, a1, (__int64)a2, 0);
}
```
* Xem xét hàm main của chương trình, có thể xác định được đây là chương trình compile từ Rust. hàm main ta thấy là giả, hàm main thật là hàm được wrap trong hàm main giả này (`sub_55845AA06100`)
```
void __noreturn sub_5596988E6100()
{
unsigned int v0; // ebx
__int64 v1; // rcx
__int64 v2; // rax
__int64 v3; // rsi
__int32 v4; // eax
unsigned __int32 v5; // ecx
__int64 v6; // rbx
__int64 v7; // r13
__int64 v8; // rbp
unsigned __int64 v9; // rcx
__int32 v10; // eax
bool v11; // zf
__int128 v12; // [rsp+8h] [rbp-210h] BYREF
char **v13; // [rsp+18h] [rbp-200h]
__int64 v14; // [rsp+20h] [rbp-1F8h]
const char *v15; // [rsp+28h] [rbp-1F0h]
__int64 v16; // [rsp+30h] [rbp-1E8h]
__int64 v17; // [rsp+38h] [rbp-1E0h] BYREF
__int64 v18; // [rsp+40h] [rbp-1D8h] BYREF
__int64 v19; // [rsp+48h] [rbp-1D0h]
__int64 v20; // [rsp+50h] [rbp-1C8h]
volatile signed __int32 *v21; // [rsp+58h] [rbp-1C0h] BYREF
__int128 v22[3]; // [rsp+60h] [rbp-1B8h]
char v23; // [rsp+90h] [rbp-188h]
__m128i v24[16]; // [rsp+A0h] [rbp-178h] BYREF
unsigned __int64 v25; // [rsp+1A0h] [rbp-78h]
char v26[104]; // [rsp+1B0h] [rbp-68h] BYREF
v0 = 0;
v1 = 0xF3LL;
v2 = 0x158LL;
do
{
--v2;
--v1;
}
while ( v1 > 0 );
v3 = 0x123LL;
if ( v2 )
{
sub_5596988E6610((unsigned int *)v24);
v4 = (v24[0].m128i_i32[2] >> 0xD) - 1;
if ( v24[0].m128i_i32[2] <= 0x1FFF )
{
v5 = (1 - (v24[0].m128i_i32[2] >> 0xD)) / 0x190u + 1;
v4 += 0x190 * v5;
v0 = 0xFFFDC54F * v5;
}
v3 = v24[0].m128i_u32[0]
+ 0x15180LL
* (int)(((unsigned int)sub_5596988E64D0(v24[0].m128i_i16[4]) >> 4)
+ ((v4 / 0x64) >> 2)
+ ((0x5B5 * v4) >> 2)
+ v0
- v4 / 0x64)
- 0xE77934880LL;
}
v22[0] = xmmword_559698920010;
v22[1] = xmmword_559698920020;
v22[2] = xmmword_559698920030;
v23 = 0xD0;
sub_5596988E5E70((__int64)v24, v3);
v18 = 0LL;
v19 = 1LL;
v20 = 0LL;
v13 = &off_55969892FF68;
v14 = 1LL;
*(_QWORD *)&v12 = 0LL;
v15 = szEntersomethin;
v16 = 0LL;
sub_5596988FE0A0(&v12);
v21 = (volatile signed __int32 *)sub_5596988FD7D0((__int64)&v12);
sub_5596988FD800((__int64)&v12, &v21, (__int64)&v18);
if ( (_QWORD)v12 )
{
v17 = *((_QWORD *)&v12 + 1);
sub_5596988E5B90(
(__int64)szFailedtoreadl,
0x13LL,
(__int64)&v17,
(__int64)&off_55969892FF48,
(__int64)&off_55969892FF78);
}
v6 = v19;
v7 = v20;
v8 = 0LL;
while ( v8 != 0x31 )
{
v9 = v25;
if ( v25 >= 0x40 )
{
sub_5596988E6730((const __m128i *)v26, 6, v24);
v25 = 0LL;
v9 = 0LL;
}
v10 = v24[0].m128i_i32[v9];
v25 = v9 + 1;
if ( v7 == v8 )
{
sub_5596988E5920(v7, v7, &off_55969892FF90);
}
v11 = (*(_BYTE *)(v6 + v8) ^ (unsigned __int8)v10) == *((_BYTE *)v22 + v8);
++v8;
if ( !v11 )
{
v13 = &off_55969892FFA8;
v14 = 1LL;
*(_QWORD *)&v12 = 0LL;
v15 = szEntersomethin;
v16 = 0LL;
sub_5596988FE0A0(&v12);
goto LABEL_x406;
}
}
v13 = &off_55969892FFB8;
v14 = 1LL;
*(_QWORD *)&v12 = 0LL;
v15 = szEntersomethin;
v16 = 0LL;
sub_5596988FE0A0(&v12);
LABEL_x406:
sub_559698900990(0xFFFFFFFFLL);
}
```
* Chương trình chủ yếu thực hiện hai vòng lặp và thực hiện call một số hàm.
* Vòng lặp đầu tiên: Vòng lặp này thực hiện giảm giá trị của v1 và v2 cho đến khi v1 không còn lớn hơn 0. Tuy nhiên, giá trị của v1 và v2 không được sử dụng trong phần còn lại của hàm.
* Vòng lặp thứ 2: Vòng lặp này thực hiện một số phép tính và so sánh giữa các giá trị trong mảng v24, v7, v8, và v22. Nếu các giá trị không khớp, chương trình chuyển đến nhãn LABEL_x406. Nếu các giá trị khớp, chương trình tiếp tục vòng lặp cho đến khi v8 bằng 0x31.
* Do có tham khảo cách giải của anh Sơn nên em đã biết được tác dụng và vai trò của `v24`. Tiếp tục xét đến các hàm nhận `v24` là tham số và check hàm `sub_5596988E6610`

* Ở đoạn này của hàm nhận thấy hàm `sub_5596989009B0()` được gán cho `v14` tiến hành vào check thì hàm ret `sub_5596989046C0` vào check `sub_5596989046C0`
```
__time_t __fastcall sub_5596989046C0(__int64 a1)
{
unsigned __int64 v2; // [rsp+8h] [rbp-20h] BYREF
struct timespec tp; // [rsp+10h] [rbp-18h] BYREF
if ( (unsigned int)((__int64 (__fastcall *)(__int64, struct timespec *))clock_gettime)(a1, &tp) == 0xFFFFFFFF )
{
v2 = ((unsigned __int64)*(unsigned int *)((__int64 (*)(void))_errno_location)() << 0x20) | 2;
sub_5596988E5B90(
(__int64)szcalledResultu_0,
0x2BLL,
(__int64)&v2,
(__int64)&off_5596989302C8,
(__int64)&off_559698931128);
}
if ( tp.tv_nsec >= 0x3B9ACA00uLL )
{
sub_5596988E58D0(
"assertion failed: tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64library/std/src/sys/unix/mod.rsassertion fail"
"ed: signal(libc::SIGPIPE, handler) != libc::SIG_ERRtoo many active read locks on RwLocklibrary/std/src/sys/u"
"nix/locks/futex_rwlock.rsassertion failed: is_unlocked(state)state is never set to invalid valuesOnce instan"
"ce has previously been poisonedassertion failed: key != 0library/std/src/../../backtrace/src/symbolize/gimli"
"/stash.rsassertion failed: mmap_aux.is_none()library/std/src/../../backtrace/src/symbolize/gimli.rslibrary/s"
"td/src/../../backtrace/src/symbolize/gimli/elf.rs/usr/lib/debug/usr/lib/debug/.build-id/",
0x3FLL,
&off_559698931110);
}
return tp.tv_sec;
}
```
* Hàm này thực hiện các thao tác liên quan đến thời gian và kiểm tra giá trị của tp.tv_nsec. Nếu giá trị này lớn hơn hoặc bằng 0x3B9ACA00, hàm gọi
`sub_5596988E58D0` đây có thể là tricks Timing trong anti_debug
* check hàm `sub_5596988E6730`


* Hàm này chạy qua lần lượt các condition, các câu lệnh check giá trị tại `dword_56423391D178` nếu mắc phải 1 trong số condition được check thì chương trình sẽ k jump đến `LABEL_x7A8` vì thế ta cần patch chương trình để lấy giá trị của vùng `v24` và chương trình descript

```
key = [0x6E1249E8, 0x7AD8474E, 0x8AC52E1B, 0x3ED51519, 0xC591080B, 0xB83E79C0, 0x4D9564D8, 0x005422D4, 0x5983BD65, 0xC74CB460, 0xE8BFC578, 0xDA357C4B, 0xE481BB14, 0x40B77026, 0xD15D317A, 0x1DF08419, 0xBFC1538C, 0x608A4C61, 0x51730A16, 0x312A9F37, 0x9667D8CC, 0x36304C22, 0xF8200C9C, 0x9F4E4E08, 0xF0F3A92F, 0xE251854F, 0xDA577918, 0xBC3116B6, 0x7709A72A, 0xB8C5FB6F, 0x12FB0DCB, 0x048A4271, 0xF4D86754, 0xF40CD922, 0x48C1DBAA, 0x190E9669, 0xA3C080F6, 0xC68B007E, 0x16DDB6CF, 0x5B57CCF2, 0xB2C8864F, 0x6C5700D3, 0x44BF50C7, 0x96D00BCC, 0x96E61869, 0x66F7224D, 0x1C3DAE9D, 0x0E6FE80F, 0xD9C58EAD]
output = [0xBF, 0x7F, 0x60, 0x6B, 0x6E, 0xA1, 0xB4, 0x8B, 0x12, 0x01, 0x0A, 0x26, 0x4B, 0x53, 0x0A, 0x46, 0xB5, 0x03, 0x22, 0x02, 0xA9, 0x10, 0xAF, 0x6A, 0x16, 0x78, 0x2C, 0xD3, 0x1D, 0x09, 0xAF, 0x48, 0x32, 0x46, 0xC8, 0x5B, 0x93, 0x49, 0xA9, 0x96, 0x7B, 0xE3, 0xF2, 0xF8, 0x0C, 0x74, 0xAB, 0x6C, 0xD0]
print(''.join([chr(i^(j&0xff)) for i,j in zip(output, key)]))
```

> `W1{real_warm_up_9b45e23b974e7fd9fdb2e7fd4054e96c}`
* Ngoài ra, chương trình này còn có `ptrace`.
* Một chương trình có thể sử dụng ptrace để kiểm tra xem nó đang chạy trong một môi trường debug hay không. Nếu nó phát hiện rằng môi trường debug đang tồn tại, nó có thể chấm dứt hoặc thay đổi hành vi của chương trình để tránh việc debug.
* Nếu một process đang bị debug thì không thể bị debug thêm nữa), nên khi chạy bình thường, RAX sẽ trả về 0, nhưng khi debug thì nó sẽ trả về -1.