---
# System prepended metadata

title: 'DEADLINE 5: ANTI_DEBUG CHALLENGE'

---

# DEADLINE 5: ANTI_DEBUG CHALLENGE 

## Anti_debug 1

* Run thử file .exe thì ta nhận thấy như sau: 

![Screenshot 2024-02-28 080524](https://hackmd.io/_uploads/rkGld-2nT.png)

* 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

![Screenshot 2024-02-28 081104](https://hackmd.io/_uploads/S1CHuZhna.png)

* 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ử 

![Screenshot 2024-02-28 145507](https://hackmd.io/_uploads/SkRJvP22T.png)
* 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:` 

  ![Screenshot 2024-02-28 151106](https://hackmd.io/_uploads/SyHp5DnnT.png)


 ![Screenshot 2024-02-28 151112](https://hackmd.io/_uploads/By_a5vnnT.png)
 
 * Ngoaì ra trong quá trình debug, nếu đặt breakpoint ở `loc_401307` sẽ thấy được một hàm anti_debug 

![Screenshot 2024-02-28 160139](https://hackmd.io/_uploads/S1eSFT326.png)

* Đâ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

![Screenshot from 2024-02-28 11-05-08](https://hackmd.io/_uploads/S1xuc0hn6.png)

* 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ử 

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

* 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`

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

* Ở đ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`

![Screenshot 2024-03-01 204340](https://hackmd.io/_uploads/r1ChoL1Ta.png)

![Screenshot 2024-03-01 204350](https://hackmd.io/_uploads/rkHTsIJT6.png)


* 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 

![Screenshot from 2024-03-01 10-54-33](https://hackmd.io/_uploads/HyUhcdkaT.png)

```
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)]))
```

![Screenshot from 2024-03-01 10-54-42](https://hackmd.io/_uploads/Sk8rsdya6.png)


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