미친해커

[Windows] Instrumentation Callback 응용하기 - syscall detect 본문

Windows/Instrumentation Callback

[Windows] Instrumentation Callback 응용하기 - syscall detect

미친해커 2022. 8. 15. 06:50
반응형
 

GitHub - jungjin0003/Instrumentation-Callback

Contribute to jungjin0003/Instrumentation-Callback development by creating an account on GitHub.

github.com

Instrumentation Callback을 통해 알 수 있는 것

지금까지의 포스팅만 봤을 때 Instrumentation Callback은 그저 syscall 어셈블리가 실행되었다는 정보 밖에 알 수 없었다. 하지만 Instrumentation Callback은 그 외에도 아래와 같은 정보들을 구할 수 있다.

  • Original Return Address (sysret 가 되었을 때 원래 돌아가야 할 주소)
  • Syscall Number
  • Function Name

How Do I Get The Above Information?

Instrumentation Callback으로 등록된 함수가 호출되었을 때 r10 레지스터에는 원래 돌아가야 할 리턴 주소가 기록되어 있다. 이를 보고 어떠한 커널 함수를 호출했는지 알아낼 수 있다.

What is The Syscall Detect?

결국 tracking와 크게 다를게 없다. 하지만 이번 응용하기에서 다른점은 syscall instruction을 호출한 주소가 실제 ntdll.dll 또는 win32u.dll가 아니라면 프로그램 실행을 중지하는 원리이다.

Sample

#include <stdio.h>
#include <windows.h>

#pragma comment (lib, "ntdll.lib")

#define PROCESS_INFO_CLASS_INTRUMENTATION 40
#define RIP_SANITY_CHECK(Rip,BaseAddress,ModuleSize) (Rip > BaseAddress) && (Rip < (BaseAddress + ModuleSize))

NTSYSAPI NTSTATUS NTAPI NtSetInformationProcess(HANDLE ProcessHandle, PROCESS_INFORMATION_CLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength);

#ifdef __GUNC__
__declspec(naked) NTSTATUS SystemCall(ULONG number)
{
    __asm__ __volatile__ ("mov rax, r10");
    __asm__ __volatile__ ("syscall");
    __asm__ __volatile__ ("ret");
}
#elif _MSC_VER
extern PVOID GetSyscallReturnAddress();
extern NTSTATUS SystemCall(ULONG number);
#endif

typedef struct _PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION
{
    ULONG Version;
    ULONG Reserved;
    PVOID Callback;
} PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION, *PPROCESS_INSTRUMENTATION_CALLBACK_INFORMATION;

VOID CALLBACK InstrumentationCallback()
{
    static HANDLE NtdllBase;
    static HANDLE Win32uBase;
    
    PVOID ReturnAddress = NULL;

#ifdef __GNUC__
    __asm__ __volatile__ (
        "mov %[ReturnAddress], r10\n\t"
        :
        : [ReturnAddress] "m" (ReturnAddress)
    );
#elif _MSC_VER
    ReturnAddress = GetSyscallReturnAddress();
#else
#error This compiler is not supported. Please checck the compiler or target OS
#endif

    if (NtdllBase == NULL)
        NtdllBase = GetModuleHandleA("ntdll.dll");

    if (Win32uBase == NULL)
        Win32uBase = GetModuleHandleA("win32u.dll");

    DWORD NtdllSize = ((IMAGE_NT_HEADERS *)((ULONG_PTR)NtdllBase + ((IMAGE_DOS_HEADER *)NtdllBase)->e_lfanew))->OptionalHeader.SizeOfImage;
    DWORD Win32uSize = Win32uBase == NULL ? 0 : ((IMAGE_NT_HEADERS *)((ULONG_PTR)Win32uBase + ((IMAGE_DOS_HEADER *)Win32uBase)->e_lfanew))->OptionalHeader.SizeOfImage;

    if (!(RIP_SANITY_CHECK(ReturnAddress, (ULONG_PTR)NtdllBase, NtdllSize)) || (Win32uBase != NULL && !(RIP_SANITY_CHECK(ReturnAddress, (ULONG_PTR)Win32uBase, Win32uSize))))
    {
        printf("[SYSCALL-DETECT] Kernel returns to unverified module, preventing further execution!\n");
        __debugbreak();
    }

    return;
}

int main(int argc, char *argv[])
{
    PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION nirvana;
    nirvana.Version = 0;
    nirvana.Reserved = 0;
    nirvana.Callback = InstrumentationCallback;

    NtSetInformationProcess(GetCurrentProcess(), PROCESS_INFO_CLASS_INTRUMENTATION, &nirvana, sizeof(PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION));
    SystemCall(0x46); // NtYieldExecution

    printf("[SYSTEM] Successfully executed the syscall instruction\n");

    return 0;
}

MSVC 컴파일용 어셈블리

더보기
; File : extern.asm
_TEXT SEGMENT

PUBLIC GetSyscallReturnAddress
PUBLIC SystemCall

GetSyscallReturnAddress PROC
    mov rax, r10
    ret
GetSyscallReturnAddress ENDP

SystemCall PROC
    mov rax, rcx
    syscall
    ret
SystemCall ENDP

_TEXT ENDS

END
=====GCC=====
CommandLine : gcc.exe main.c -o main.exe -lntdll -masm=intel
=====MSVC====
CommandLine : ml64.exe /c extern.asm => result : extern.obj
CommandLink : cl.exe main.c extern.obj /Od /Z7 /link /MANIFEST:NO /DEBUG:FULL /OPT:REF /OPT:ICF /OPT:LBR

실행 결과

$ main.exe
[SYSCALL-DETECT] Kernel returns to unverified module, preventing further execution!

Reference

 

Detecting Manual Syscalls from User Mode - Winternl

By now direct system calls are ubiquitous in offensive tooling. Manual system calls remain effective for evading userland based EDRs. From within userland, there has been little answer to this powerful technique. Such syscalls can be effectively mitigated

winternl.com

 

반응형
Comments