---
# System prepended metadata

title: Anti_debug_1

---

# Anti_debug_1

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

* Đây là một file PE 32bit viết bằng C/C++, tiền hành chạy thử file thực thi 

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

* Chương trình bắt ta nhập input và check, có lẽ chuỗi cần nhập sẽ là flag cần tìm

* Tiến hành phân tích tĩnh bằng IDA

```
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    HACCEL v4; // esi
    HANDLE CurrentProcess; // eax
    HWND Window; // eax
    HWND v7; // esi
    struct tagMSG Msg; // [esp+8h] [ebp-2Ch] BYREF
    HANDLE TokenHandle; // [esp+24h] [ebp-10h] BYREF
    DWORD ReturnLength; // [esp+28h] [ebp-Ch] BYREF
    HACCEL TokenInformation; // [esp+2Ch] [ebp-8h] BYREF

    v4 = 0;
    TokenHandle = 0;
    CurrentProcess = GetCurrentProcess();
    if ( OpenProcessToken(CurrentProcess, 8u, &TokenHandle) )
    {
        ReturnLength = 4;
        if ( GetTokenInformation(TokenHandle, TokenElevation, &TokenInformation, 4u, &ReturnLength) )
        {
            v4 = TokenInformation;
        }
    }

    if ( TokenHandle )
    {
        CloseHandle(TokenHandle);
    }

    if ( !v4 )
    {
        MessageBoxA(0, "Please run the program with administrator right", "Warning", 0);
        exit(1);
    }

    LoadStringW(hInstance, 0x67u, &WindowName, 0x64);
    LoadStringW(hInstance, 0x6Du, &ClassName, 0x64);
    sub_621260(hInstance);
    ::hInstance = hInstance;
    Window = CreateWindowExW(0, &ClassName, &WindowName, 0xCF0000u, 0x80000000, 0, 0x80000000, 0, 0, 0, hInstance, 0);
    v7 = Window;
    if ( !Window )
    {
        return 0;
    }

    ShowWindow(Window, nShowCmd);
    UpdateWindow(v7);
    TokenInformation = LoadAcceleratorsW(hInstance, (LPCWSTR)0x6D);
    while ( GetMessageW(&Msg, 0, 0, 0) )
    {
        if ( !TranslateAcceleratorW(Msg.hwnd, TokenInformation, &Msg) )
        {
            TranslateMessage(&Msg);
            DispatchMessageW(&Msg);
        }
    }

    return Msg.wParam;
}
```

* Đây là Entry point của chương trình, trước khi phân tích đến điểm đầu thì ta phân tích hàm TLSCallback_0 trước

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

* Hàm khởi tạo biến `v4` bằng giá trị trả về từ hàm `sub_401DF0`.
* Khởi tạo `v7` bằng giá trị trả về từ hàm `sub_401F10`.
* Gọi hàm thông qua con trỏ `v7` với các tham số được chỉ định.Nếu `v7` khác NULL, gán `0x74` vào một byte trong địa chỉ của `unk_405018 + 0xA` sau đó ret `result`. Rốt cục thì sau khi gọi hai hàm `sub_401DF0` và `sub_401F10` thì giá trị return sẽ là một addr nào đó 
* Tiến hành debug xem thử ta đặt breakpoint ở `line 9` 

![Screenshot 2024-03-04 144312](https://hackmd.io/_uploads/BJu5VW7pT.png)

* Sau khi debug ta nhận thấy giá trị EAX trả về là một hàm `NtQueryInformation` thuộc Win32 API, cụ thể đây là một hàm check debug trong tricks `Debug Flags`. 

```
 lea eax, [dwReturned]
    push eax ; ReturnLength
    push 4   ; ProcessInformationLength
    lea ecx, [dwProcessDebugPort]
    push ecx ; ProcessInformation
    push 7   ; ProcessInformationClass
    push -1  ; ProcessHandle
    call NtQueryInformationProcess
    inc dword ptr [dwProcessDebugPort]
    jz being_debugged
    ...
being_debugged:
    push -1
    call ExitProcess 
```
* Cụ thể hơn hàm được sử dụng để truy vấn thông tin về process. Check xem process có đang bị debug hay không nếu có thì jump tới label `being_debugged` và out process.
* Quay trở lại hàm `TLSCallback_0` thì từ đây ta biết được giá trị gán cho `v7` chính là `NtQueryInformationProcess` và sau khi check debug xong thì sẽ thực hiện đổi giá trị tại addr `unk_405018 + 0xA` 

* Tiếp theo phân tích hàm chính winmain của chúng ta 

```
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    HACCEL v4; // esi
    HANDLE CurrentProcess; // eax
    HWND Window; // eax
    HWND v7; // esi
    struct tagMSG Msg; // [esp+8h] [ebp-2Ch] BYREF
    HANDLE TokenHandle; // [esp+24h] [ebp-10h] BYREF
    DWORD ReturnLength; // [esp+28h] [ebp-Ch] BYREF
    HACCEL TokenInformation; // [esp+2Ch] [ebp-8h] BYREF

    v4 = 0;
    TokenHandle = 0;
    CurrentProcess = GetCurrentProcess();
    if ( OpenProcessToken(CurrentProcess, 8u, &TokenHandle) )
    {
        ReturnLength = 4;
        if ( GetTokenInformation(TokenHandle, TokenElevation, &TokenInformation, 4u, &ReturnLength) )
        {
            v4 = TokenInformation;
        }
    }

    if ( TokenHandle )
    {
        CloseHandle(TokenHandle);
    }

    if ( !v4 )
    {
        MessageBoxA(0, "Please run the program with administrator right", "Warning", 0);
        exit(1);
    }

    LoadStringW(hInstance, 0x67u, &WindowName, 0x64);
    LoadStringW(hInstance, 0x6Du, &ClassName, 0x64);
    sub_401260(hInstance);
    ::hInstance = hInstance;
    Window = CreateWindowExW(0, &ClassName, &WindowName, 0xCF0000u, 0x80000000, 0, 0x80000000, 0, 0, 0, hInstance, 0);
    v7 = Window;
    if ( !Window )
    {
        return 0;
    }

    ShowWindow(Window, nShowCmd);
    UpdateWindow(v7);
    TokenInformation = LoadAcceleratorsW(hInstance, (LPCWSTR)0x6D);
    while ( GetMessageW(&Msg, 0, 0, 0) )
    {
        if ( !TranslateAcceleratorW(Msg.hwnd, TokenInformation, &Msg) )
        {
            TranslateMessage(&Msg);
            DispatchMessageW(&Msg);
        }
    }

    return Msg.wParam;
}
```

* Ở đây ta chỉ sẽ quan tâm đến `CreateWindowExW` là một hàm trong Win32 API của Windows được sử dụng để tạo một cửa sổ mới với các thuộc tính và kiểu tùy chỉnh, xem ra main flow của process sẽ nằm ở `hInstance` do `hInstance` khởi tạo từ `sub_401260` nên ta check xem 

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

* Hàm "sub_401260" nhiệm vụ chính là đăng ký một lớp cửa sổ mới bằng cách sử dụng hàm "RegisterClassExW". Nhìn vào `v2.lpfnWndProc = sub_401350;` thì gán `sub_401350` cho `lpfnWndProc` tiền hành vào `sub_401350` để kiểm tra 

```
LRESULT __stdcall sub_401350(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    const CHAR *v5; // [esp-Ch] [ebp-258h]
    DWORD pdwDataLen; // [esp+0h] [ebp-24Ch] BYREF
    struct tagPAINTSTRUCT Paint; // [esp+4h] [ebp-248h] BYREF
    __int128 v8[2]; // [esp+44h] [ebp-208h] BYREF
    __int128 v9; // [esp+64h] [ebp-1E8h]
    char v10[208]; // [esp+74h] [ebp-1D8h] BYREF
    CHAR String[260]; // [esp+144h] [ebp-108h] BYREF

    v8[0] = xmmword_4038D0;
    v8[1] = xmmword_4038E0;
    v9 = xmmword_4038F0;
    memset(v10, 0, sizeof(v10));
    pdwDataLen = 0x30;
    if ( Msg <= 0xF )
    {
        switch ( Msg )
        {
            case 0xFu:
                BeginPaint(hWnd, &Paint);
                EndPaint(hWnd, &Paint);
                return 0;

            case 1u:
                sub_4012F0(hWnd);
                return 0;

            case 2u:
                PostQuitMessage(0);
                return 0;
        }

        return DefWindowProcW(hWnd, Msg, wParam, lParam);
    }

    if ( Msg != 0x111 )
    {
        return DefWindowProcW(hWnd, Msg, wParam, lParam);
    }

    switch ( (unsigned __int16)wParam )
    {
        case 4u:
            GetWindowTextA(::hWnd, String, 0x100);
            if ( (unsigned __int8)sub_401B40(String) )
            {
                sub_401000((BYTE *)String, &pdwDataLen);
                if ( pdwDataLen >= 0x2E )
                {
                    BYTE14(v9) = 0;
                    MessageBoxA(0, (LPCSTR)v8, "OK", 0);
                    return 0;
                }

                v5 = "Wrong";
            }
            else
            {
                v5 = "Wrong check fail";
            }

            MessageBoxA(0, "oh, no", v5, 0);
            return 0;

        case 0x68u:
            DialogBoxParamW(hInstance, (LPCWSTR)0x67, hWnd, DialogFunc, 0);
            return 0;

        case 0x69u:
            DestroyWindow(hWnd);
            return 0;

        default:
            return DefWindowProcW(hWnd, 0x111u, wParam, lParam);
    }
}
```

* Ở case 4 ta thấy một đoạn mã  Đây dường như là hàm check Input của chương trình `GetWindowTextA(::hWnd, String, 0x100);
            if ( (unsigned __int8)sub_401B40(String) )` hàm `sub_401B40` có lẽ sẽ là hàm check dữ liệu đầu vào từ người dùng 
            
```
char __thiscall sub_401B40(const char *this)
{
    char v2; // cl
    int v3; // esi
    int v4; // ecx
    char v5; // bl
    char v6; // cl
    int v7; // eax
    int v8; // eax
    char v9; // dl
    char v10; // al
    int v11; // eax
    void (__stdcall *v12)(_DWORD); // eax
    char result; // al
    int v14; // eax
    char v15; // dl
    int v16; // eax
    char v17; // dl
    int v18; // eax
    char v19; // dl
    int v20; // eax
    char v21; // dl
    char v22; // bl
    int v23; // eax
    unsigned __int8 v24; // cl
    int v25; // eax
    int v26; // eax
    void (__stdcall *v27)(_DWORD); // eax
    void (__stdcall *v28)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD); // [esp-4h] [ebp-25Ch] BYREF
    void (__stdcall *v29)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD); // [esp+10h] [ebp-248h]
    int v30; // [esp+14h] [ebp-244h]
    void (__stdcall *v31)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD); // [esp+18h] [ebp-240h]
    char v32; // [esp+1Fh] [ebp-239h]
    char v33[556]; // [esp+20h] [ebp-238h] BYREF
    int v34; // [esp+24Ch] [ebp-Ch]

    if ( strlen(this) < 0x26 )
    {
        return 0;
    }

    sub_401FD0(v33, byte_40501C[(unsigned __int8)byte_40501C[0] / 0xCu]);
    v2 = v32;
    v3 = 0;
    while ( 2 )
    {
        switch ( dword_4032C8[v3] )
        {
            case 1:
                v4 = dword_403360[v3];
                v5 = this[dword_4033F8[v3]];
                v32 = NtCurrentPeb()->NtGlobalFlag & 0x70;
                v6 = sub_402050(v4);
                v7 = v34;
                if ( v34 >= 0x100 )
                {
                    v7 = 0;
                }

                v34 = v7 + 1;
                v2 = byte_40329F[v7 + 1] == (char)(v5 ^ v6);
                goto LABEL_xC34;

            case 2:
                v8 = dword_4033F8[v3];
                v28 = (void (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD))dword_403360[v3];
                v9 = this[v8];
                v10 = sub_401600(v28);
                goto LABEL_xC2F;

            case 3:
                v14 = dword_4033F8[v3];
                v28 = (void (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD))dword_403360[v3];
                v15 = this[v14];
                v10 = sub_4016C0(v28);
                goto LABEL_xC2F;

            case 4:
                v16 = dword_4033F8[v3];
                v28 = (void (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD))dword_403360[v3];
                v17 = this[v16];
                v10 = sub_401760(v28);
                goto LABEL_xC2F;

            case 5:
                v18 = dword_4033F8[v3];
                v28 = (void (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD))dword_403360[v3];
                v19 = this[v18];
                v10 = sub_401950(v28);
                goto LABEL_xC2F;

            case 6:
                v20 = dword_4033F8[v3];
                v28 = (void (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD))dword_403360[v3];
                v21 = this[v20];
                v10 = sub_401AA0(v28);

LABEL_xC2F:
                v2 = v10;
                goto LABEL_xC34;

            case 7:
                v30 = dword_403360[v3];
                v22 = this[dword_4033F8[v3]];
                v23 = sub_401DF0(0x7B3FA1C0);
                v29 = (void (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD))sub_401F10(v23, 0x5A3BB3B0);
                v31 = 0;
                v28 = v29;
                v29(0xFFFFFFFF, 0x1F, &v28, 4, 0);
                v31 = v28;
                v24 = sub_402050(v30);
                v25 = v34;
                if ( v34 >= 0x100 )
                {
                    v25 = 0;
                }

                v34 = v25 + 1;
                if ( byte_40329F[v25 + 1] != (v24 ^ (unsigned __int8)v22) )
                {
                    goto LABEL_xD93;
                }

                v2 = 1;
                goto LABEL_xC3C;

            default:
LABEL_xC34:
                if ( !v2 )
                {
LABEL_xD93:
                    v26 = sub_401DF0(0x2489AAB);
                    v27 = (void (__stdcall *)(_DWORD))sub_401F10(v26, 0x3200C39D);
                    v27(0);
                    byte_4055B8 = 0;
                    return 0;
                }

LABEL_xC3C:
                if ( ++v3 < 0x26 )
                {
                    continue;
                }

                v11 = sub_401DF0(0x2489AAB);
                v12 = (void (__stdcall *)(_DWORD))sub_401F10(v11, 0x3200C39D);
                v12(0);
                byte_4055B8 = 0;
                result = 1;
                break;
        }

        return result;
    }
}
```
* Hàm trên tạo một loop chạy từ 0 đens 38, gồm có 7 case check. Ở mỗi loop, điều kiện `Case[i]` được kiểm tra. Từng `Case[i]` sẽ được check debug, sau đó kết quả trả về từ hàm đó được sử dụng cùng với const_data1 và const_data2[i].

```
char __thiscall check(const char *this)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  if ( strlen(this) < 0x26 )
    return 0;
  sub_91FD0((__m128i *)v25, unk_405018[(unsigned __int8)unk_405018[4] / 0xCu + 4]);
  v2 = v24;
  v3 = 0;
  ....
```

* Ta thấy giá trị `unk_405018` mà ta thấy ở `TLSCallback_0` được truyền vào ở đây tức là nếu ta debug chương trình từ ban đầu mà k patch ở hàm `TLSCallback_0` thì cũng sẽ ảnh hưởng đến kết quả.

* Mỗi case chương trình đều check xem có debug hay k nên ta sẽ phân tích kỹ từng case. 

## Case 1:

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

![Screenshot 2024-03-04 201552](https://hackmd.io/_uploads/Hy-oarXaa.png)



* `dword_4033F8[esi]` sẽ là nơi lưu giá trị input của user. Ở đây có dùng một tricks `Manual Check` tương tự như task anti debug trước nên em sẽ skip qua phần này 
* Ở phần pseudo từ phần check debug trở về sau 

```
 v6 = sub_402050(v4);
                v7 = v34;
                if ( v34 >= 0x100 )
                {
                    v7 = 0;
                }

                v34 = v7 + 1;
                v2 = byte_40329F[v7 + 1] == (char)(v5 ^ v6);
                goto LABEL_xC34;
```
* Ở hàm `sub_402050(v4)` sẽ cho đối số là `v4` và trả về cho ta 1 const data sau đó xor rồi so sánh với cipher tại `byte_40329F[v7 + 1]`. Nhìn chung để bypass hàm này ta cần set đối số `v4` = 0 để cho process nhận diện không có debugger 

## Case 2:

```
bool __fastcall sub_DB1600(int a1, char a2, int a3)
{
    struct _LIST_ENTRY *v4; // eax
    unsigned __int8 (*v5)(void); // eax
    char v6; // bl
    struct _LIST_ENTRY *v7; // eax
    void (__stdcall *v8)(_DWORD); // eax
    int v9; // eax
    unsigned int v11; // [esp+8h] [ebp-8h]

    v4 = sub_DB1DF0((void *)0x6AE69F02);
    v5 = (unsigned __int8 (*)(void))sub_DB1F10(v4, 0x4CCF1A0F);
    v11 = *(_DWORD *)((_BYTE *)NtCurrentPeb()->ProcessHeap + (v5() >= 6u ? 0x34 : 0) + 0xC) & 0xEFFEFFFF;
    v6 = sub_DB2050(a3);
    if ( *(int *)(a1 + 0x22C) >= 0x100 )
    {
        *(_DWORD *)(a1 + 0x22C) = 0;
    }

    ++*(_DWORD *)(a1 + 0x22C);
    v7 = sub_DB1DF0((void *)0x2489AAB);
    v8 = (void (__stdcall *)(_DWORD))sub_DB1F10(v7, 0x3200C39D);
    v8(0);
    v9 = *(_DWORD *)(a1 + 0x22C);
    byte_DB55B8 = 0;
    return byte_DB329F[v9] == (char)(a2 ^ v6);
}
```

* Hàm này cũng sẽ gọi `resolved_API`. Case thực hiện tính toán và gán giá trị vào biến v11 sau đó kiểm tra giá trị tại địa chỉ (a1 + 0x22C) và nếu lớn hơn hoặc bằng 0x100, gán bằng 0.
* Tăng giá trị tại địa chỉ (a1 + 0x22C) lên một đơn vị.Trả về kết quả so sánh giữa giá trị tại vị trí v9 của mảng byte_DB329F với kết quả của phép XOR giữa a2 và v6.

* Ở Case trên chương trình sẽ lấy ProcessHeap, ProcessHeap là một con trỏ đến heap chính của process sau đó chương trình dùng hàm `GetVersion` để lấy flags và forceflags. Hai cờ này nằm trong Heap cụ thể 

1. Flags: Cờ này được sử dụng để xác định các tùy chọn và cấu hình cho quản lý heap (bộ nhớ động). Một số giá trị cờ phổ biến bao gồm:
1.1. HEAP_GROWABLE: Cho phép heap mở rộng tự động khi cần thiết.
1.2. HEAP_GENERATE_EXCEPTIONS: Tạo ra ngoại lệ khi có lỗi xảy ra trong quá trình quản lý heap.
1.3. HEAP_NO_SERIALIZE: Không đồng bộ hóa truy cập đến heap giữa các luồng (threads) đồng thời.
1.4. HEAP_ZERO_MEMORY: Khởi tạo toàn bộ bộ nhớ heap được cấp phát thành giá trị 0.
2. .ForceFlags: Cờ này được sử dụng để áp đặt các hành vi cụ thể cho quản lý heap. Các giá trị cờ phổ biến bao gồm:
2.1. HEAP_DISABLE_COALESCE_ON_FREE: Không gom bộ nhớ lại (coalesce) khi giải phóng các khối bộ nhớ trong heap.
2.2. HEAP_CREATE_ENABLE_EXECUTE: Cho phép thực thi mã từ bộ nhớ heap.
2.3. HEAP_TAIL_CHECKING_ENABLED: Kích hoạt kiểm tra đuôi (tail checking) để phát hiện việc ghi quá phạm vi trong bộ nhớ.

* Để bypass Case này ta sẽ set IP flow của chương trinhf  không bằng với 0x40000062 ví ta thấy chương trình lấy Flags(0x40) ở offset trên processHeap
## Case 3

* Bypass tương tự case 2 nhưng lần này case sẽ lấy ForceFlas(0x44) ở offset trên processHeap

## Case 4:

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

*  Case trên kiểm tra tính toàn vẹn của vùng nhớ heap dựa trên các cờ heap được kích hoạt trong biến NtGlobalFlag.

* Nếu cờ HEAP_TAIL_CHECKING_ENABLED được kích hoạt, các byte 0xABABABAB được thêm vào cuối của các vùng nhớ heap được phân bổ. Đoạn mã kiểm tra các giá trị tại các địa chỉ và kiểm tra xem chúng có bằng 0xAB hay không.

* Tương tự, nếu cờ HEAP_FREE_CHECKING_ENABLED được kích hoạt, các byte 0xFEEEFEEE được thêm vào cuối của các vùng nhớ rỗng cho đến khi gặp vùng nhớ heap mới. 

## Case 5

*  Case này vẫn dùng kỹ thuật PEB kiểm tra các thuộc tính hoặc thông tin về process để xác định xem có sự hiện diện của debugger hay không. 

## Case 6:

```
bool __fastcall sub_191AA0(int a1, char a2, int a3)
{
    struct _LIST_ENTRY *v5; // eax
    int (__stdcall *v6)(int); // esi
    char v7; // bl
    char v8; // al
    char v9; // al

    v5 = sub_191DF0((void *)0x2489AAB);
    v6 = (int (__stdcall *)(int))sub_191F10(v5, 0x3200C39D);
    v7 = v6(1);
    v8 = v6(1);
    if ( byte_1955B8 )
    {
        if ( v7 == v8 )
        {
            goto LABEL_xADE;
        }
    }

    else if ( v7 != v8 )
    {
LABEL_xADE:
        v9 = sub_192050(a1, 1, a3);
        byte_1955B8 = 1;
        goto LABEL_xB09;
    }

    v9 = sub_192050(a1, 0, a3);

LABEL_xB09:
    if ( *(int *)(a1 + 0x22C) >= 0x100 )
    {
        *(_DWORD *)(a1 + 0x22C) = 0;
    }

    ++*(_DWORD *)(a1 + 0x22C);
    return byte_19329F[*(_DWORD *)(a1 + 0x22C)] == (char)(a2 ^ v9);
}
```

* Case này gán cả `v7`, `v8` == `v6` chính là hàm BlockInput() để check xem chương trình có đang bị debug hay ko, nếu phát hiện debug thì sẽ jmp dến `Label _xADE` vì thế để bypass ta sẽ đổi lại argu ở v9 là 1.

## Case 7:

* Case này tricks anti tương tự như ở hàm `TLSCallback_0` là `NtQueryInformationProcess` chỉ cần set return value -1 để bypass 



* Sau khi đã phân tích hết các case vầ nắm được các cách bypass ở mỗi case chương trình sẽ trả về cho ta 1 const data rồi xor với input của user nhập và cmp với cipher. Giờ ta chỉ cần xor với cipher là ra được key.

```

#include <bits/stdc++.h>
 
#define _DWORD unsigned __int32
#define _WORD unsigned __int16

char const_Data(char a1, unsigned __int8 *a2, int a3)
{
  int a; 
  char b; 
  int c; 
  unsigned __int16 d; 
  unsigned int e; 
  char f; 
  unsigned int g; 
  unsigned __int8 h; 
  bool i; 
  unsigned __int8 *j; 
  int k; 
  char l; 
  int m; 

  a = a3 - 1;
  m = 171;
  b = 0;

  do
  {
    if ( a <= 5 )
    {
      if ( *(_DWORD *)&a2[4 * a + 16] )
        d = *(_WORD *)&a2[4 * a + 16];
      else
        d = *(_WORD *)&a2[4 * a];
      c = (d >> 1) | (unsigned __int16)(((unsigned __int16)(32 * d) ^ (d ^ (unsigned __int16)(4 * (d ^ (2 * d)))) & 0xFFE0) << 10);
      *(_DWORD *)&a2[4 * a + 16] = c;
    }
    else
    {
      c = 0;
    }
    e = c & 0x7FF;
    f = c & 7;
    g = e >> 3;
    if ( a1 )
      h = a2[g + 44];
    else
      h = ~a2[g + 44];
    i = m-- == 1;
    a2[g + 44] = h ^ (1 << f);
  }
  while ( !i );
  j = a2 + 46;
  k = 64;
  do
  {
    l = *(j - 2);
    j += 4;
    b ^= *(j - 4) ^ *(j - 3) ^ *(j - 5) ^ l;
    --k;
  }
  while ( k );
  return b;
}
 
int main(){
 	unsigned __int32 index_table[] = {9, 18, 15, 3, 4, 23, 6, 7, 8, 22, 10, 11, 33, 13, 14, 27, 16, 37, 17, 19, 20, 21, 5, 34, 24, 25, 26, 2, 12, 29, 30, 31, 32, 28, 0, 35, 36, 1};
	unsigned __int32 g_const[] = {1, 3, 1, 1, 2, 1, 3, 1, 2, 2, 4, 4, 1, 3, 4, 4, 4, 1, 2, 1, 4, 1, 4, 3, 1, 2, 4, 4, 2, 2, 1, 3, 4, 2, 1, 2, 2, 3};
	unsigned __int8 default_const[] = {54,236,0,0,54,237,0,0,54,187,0,0,54,140,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,95,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,};
	unsigned __int8 cipher[] = {14,235,243,246,209,107,167,143,61,145,133,43,134,167,107,219,123,110,137,137,24,149,103,202,95,226,84,14,211,62,32,90,126,212,184,16,194,183,0,0};
	unsigned __int32 program[] =  {6, 0, 0, 0, 1, 0, 0, 0, 7, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0,2, 0, 0, 0, 4, 0, 0, 0, 3, 0,0, 0, 6, 0, 0, 0, 3, 0, 0, 0,7, 0, 0, 0, 6, 0, 0, 0, 1, 0,0, 0, 4, 0, 0, 0, 7, 0, 0, 0,4, 0, 0, 0, 1, 0, 0, 0, 5, 0,0, 0, 7, 0, 0, 0, 6, 0, 0, 0,7, 0, 0, 0, 5, 0, 0, 0, 6, 0,0, 0, 4, 0, 0, 0, 5, 0, 0, 0,1, 0, 0, 0, 7, 0, 0, 0, 5, 0,0, 0, 2, 0, 0, 0, 3, 0, 0, 0,1, 0, 0, 0, 2, 0, 0, 0, 3, 0,0, 0, 2, 0, 0, 0, 1, 0, 0, 0,6, 0, 0, 0, 2, 0, 0, 0, 4, 0,0, 0};
 
 	unsigned char tmp;
  	unsigned char flag[40];
 
	for(int i=0;i<38;i++){
		if(program[i * 4] == 1){
				tmp = const_Data(0,default_const,g_const[i]);
				flag[index_table[i]] = cipher[i]^tmp;
        }
        else if(program[i * 4] == 2){
			
				tmp = const_Data(1,default_const,g_const[i]);
				flag[index_table[i]] = cipher[i]^tmp;
        }
		else if(program[i * 4] == 3){
				tmp = const_Data(1,default_const,g_const[i]);
				flag[index_table[i]] = cipher[i]^tmp;
        }
        else if(program[i * 4] == 4){
				tmp = const_Data(0,default_const,g_const[i]);
				flag[index_table[i]] = cipher[i]^tmp;
        }
		else if(program[i * 4] == 5){
				tmp = const_Data(0,default_const,g_const[i]);
				flag[index_table[i]] = cipher[i]^tmp;
        }
		else if(program[i * 4] == 6){
				tmp = const_Data(1,default_const,g_const[i]);
				flag[index_table[i]] = cipher[i]^tmp;
        }
		else if(program[i * 4] == 7){
				tmp = const_Data(1,default_const,g_const[i]);
				flag[index_table[i]] = cipher[i]^tmp;
        }
				
		}
	printf("%s",flag);
	return 1;
}

```
* Chạy chương trình và lấy key intput `I_10v3-y0U__wh3n Y0u=c411..M3 Senor1t4`

* Quay lại file thực thi và nhập key vào và lấy flag

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


## Resolve_API 

* Trong quá trình debug và check case thì ta sẽ thấy chương trình call rất nhiều lần hàm này.
* Ban đầu ta sẽ bắt gặp được chương trình dùng resolved để call `NtQueryInformationProcess` ở hàm `TLSCallback_0` để check debug, thực chất hàm này dùng để call các hàm native API việc này đặc biệt cần thiết cho việc truy cập và sử dụng thông tin về PEB( để hiểu rõ hơn các bạn có thể đọc tài liệu về PEB ở đây https://ntopcode.wordpress.com/2018/02/26/anatomy-of-the-process-environment-block-peb-windows-internals/ )

* trong chương trình trên có 2 hàm Resolved là `sub_191DF0` và `sub_191DF0` mình sẽ tạm gọi là `Resolved_1` và `Resolved_2` 

```
struct _LIST_ENTRY *__thiscall sub_191DF0(void *this)
{
    int v1; // et1
    struct _PEB *v2; // eax
    struct _PEB_LDR_DATA *Ldr; // esi
    char *v4; // ebx
    struct _LIST_ENTRY *Flink; // eax
    signed int v6; // esi
    signed int i; // edi
    signed int v8; // edx
    unsigned int v9; // eax
    signed int j; // esi
    char v11; // cl
    struct _LIST_ENTRY *p_InLoadOrderModuleList; // [esp+Ch] [ebp-18h]
    struct _LIST_ENTRY **p_Flink; // [esp+18h] [ebp-Ch]
    size_t PtNumOfCharConverted; // [esp+1Ch] [ebp-8h] BYREF

    v1 = *MK_FP(__FS__, 0);
    v2 = NtCurrentPeb();
    if ( !v2 )
    {
        return 0;
    }

    Ldr = v2->Ldr;
    if ( !Ldr )
    {
        return 0;
    }

    v4 = (char *)unknown_libname_1(0x400);
    Flink = Ldr->InLoadOrderModuleList.Flink;
    p_InLoadOrderModuleList = &Ldr->InLoadOrderModuleList;
    p_Flink = &Flink->Flink;
    if ( Flink == &Ldr->InLoadOrderModuleList )
    {
        return 0;
    }

    while ( 1 )
    {
        wcstombs_s(&PtNumOfCharConverted, v4, 0x400u, (const wchar_t *)Flink[6].Flink, 0x3FFu);
        v6 = strlen(v4);
        for ( i = 0; i < v6; ++i )
        {
            v4[i] = tolower(v4[i]);
        }

        v8 = strlen(v4);
        v9 = 0xFFFFFFFF;
        for ( j = 0; j < v8; v9 = dword_1934D0[(unsigned __int8)(v9 ^ v11)] ^ (v9 >> 8) )
        {
            v11 = v4[j++];
        }

        if ( (void *)abs32(~v9) == this )
        {
            break;
        }

        Flink = *p_Flink;
        p_Flink = &Flink->Flink;
        if ( Flink == p_InLoadOrderModuleList )
        {
            return 0;
        }
    }

    return p_Flink[6];
}
```

* `API_1` Hàm bắt đầu bằng việc lấy con trỏ đến cấu trúc PEB như đã nói ở trên thông qua hàm NtCurrentPeb(). Nếu không thể lấy được con trỏ PEB thì ret 0. Sau đó, truy cập vào trường Ldr (Loader) của PEB để lấy con trỏ đến cấu trúc _PEB_LDR_DATA. Nếu không có con trỏ Ldr, hàm cũng trả về 0.
* Tiếp theo truy cập vào danh sách các module đã tải (_InLoadOrderModuleList) trong cấu trúc _PEB_LDR_DATA. Nếu danh sách này rỗng thì ret 0.
* Cuối cùng trả về con trỏ đến Flink[6] của module tìm thấy nó trỏ đến cấu trúc _LIST_ENTRY 

* Đây là `API_2` 

```
int __fastcall sub_191F10(int a1, int a2)
{
    _DWORD *v2; // eax
    int v3; // edx
    unsigned int v4; // ebx
    const char *v5; // edi
    signed int v6; // edx
    unsigned int v7; // eax
    signed int i; // esi
    char v9; // cl
    int v11; // [esp+Ch] [ebp-18h]
    int v12; // [esp+10h] [ebp-14h]
    int v13; // [esp+14h] [ebp-10h]
    unsigned int v14; // [esp+18h] [ebp-Ch]
    int v16; // [esp+20h] [ebp-4h]

    v2 = (_DWORD *)(a1 + *(_DWORD *)(*(_DWORD *)(a1 + 0x3C) + a1 + 0x78));
    v16 = a1;
    v12 = a1 + v2[9];
    v3 = a1 + v2[8];
    v11 = a1 + v2[7];
    v4 = 0;
    v13 = v3;
    v14 = v2[6];
    if ( !v14 )
    {
        return 0;
    }

    while ( 1 )
    {
        v5 = (const char *)(a1 + *(_DWORD *)(v3 + 4 * v4));
        v6 = strlen(v5);
        v7 = 0xFFFFFFFF;
        for ( i = 0; i < v6; v7 = dword_1934D0[(unsigned __int8)(v7 ^ v9)] ^ (v7 >> 8) )
        {
            v9 = v5[i++];
        }

        if ( a2 == abs32(~v7) )
        {
            break;
        }

        a1 = v16;
        ++v4;
        v3 = v13;
        if ( v4 >= v14 )
        {
            return 0;
        }
    }

    return v16 + *(_DWORD *)(v11 + 4 * *(unsigned __int16 *)(v12 + 2 * v4));
}
```
* Tóm lại hai hàm trên dùng để gọi các hàm native API. Ở chương trình này hàm đầu tiên sử dụng `LoadLibrary` để tải thư viện Ntdll.dll vào bộ nhớ. Hàm còn lại làm nhiệm vụ lấy địa chỉ hàm từ thư viện đã tải, sử dụng hàm `GetProcAddress` để lấy ra địa chỉ của nó.