공부해요/리버싱_핵심원리

43. DLL Injection in Kernel 6

yenas0 2024. 12. 26. 08:10
반응형

CreateRemoteThread() API사용하는 방법은 xp에서는 잘 되는데 7에서는 다르게 동작함 그래서 DLL 인젝션이 안된다..

왜냐면 세션관리 정책이 변경되었기때문

https://github.com/reversecore/book/tree/master/%EC%8B%A4%EC%8A%B5%EC%98%88%EC%A0%9C/05_64%EB%B9%84%ED%8A%B8_%26_Windows_Kernel_6/43_DLL_Injection_in_Kernel_6/bin

 

book/실습예제/05_64비트_&_Windows_Kernel_6/43_DLL_Injection_in_Kernel_6/bin at master · reversecore/book

리버싱 핵심원리 - 소스 코드 및 실습 예제. Contribute to reversecore/book development by creating an account on GitHub.

github.com

 

위에 InjectDll.exe로 DLL 인젝션 실패를 재현해볼 수 있다.

먼저 소스 코드를 보면

BOOL InjectDLL(DWORD dwPID, LPCTSTR szDLLPath) {
    HANDLE hProcess = NULL, hThread = NULL;
    HMODULE hMod = NULL;
    LPVOID pRemoteBuf = NULL;
    DWORD dwBufSize = (DWORD)(_tcslen(szDLLPath) + 1) * sizeof(TCHAR);
    LPTHREAD_START_ROUTINE pThreadProc;
    BOOL bRet = TRUE;

    if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID))) {
        _tprintf(L"OpenProcess(%d) failed!!! [%d]\n", dwPID, GetLastError());
        return FALSE;
    }

    pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);
    WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDLLPath, dwBufSize, NULL);

    hMod = GetModuleHandle(L"kernel32.dll");
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");

    hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
    if (hThread == NULL) {
        _tprintf(L"[ERROR] CreateRemoteThread() failed!!! [%d]\n", GetLastError());
        bRet = FALSE;
        goto _ERROR;
    }

    WaitForSingleObject(hThread, INFINITE);

_ERROR:
    if (pRemoteBuf) 
        VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);
    
    if (hThread) 
        CloseHandle(hThread);
    
    if (hProcess) 
        CloseHandle(hProcess);

    return bRet;
}

여긴 23장에 나온 DLL 인젝션 소스코드랑 같음

 

아래는 Dummpy.cpp

#include "windows.h"
#include "tchar.h"

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
    TCHAR szPath[MAX_PATH] = {0};
    TCHAR szMsg[1024] = {0};
    TCHAR *p = NULL;

    switch (fdwReason) {
        case DLL_PROCESS_ATTACH:
            GetModuleFileName(NULL, szPath, MAX_PATH);
            p = _tcsrchr(szPath, L'\\');
            if (p != NULL) {
                _stprintf_s(szMsg, 1024 - sizeof(TCHAR),
                            L"Injected in %s(%d)", 
                            p + 1, // Process Name
                            GetCurrentProcessId()); // PID
                OutputDebugString(szMsg);
            }
            break;
    }

    return TRUE;
}

얘는 dummy.dll파일이 프로세스에 정상적으로 인젝션되면 디버그 메시지를 출력함

 

Process Explorer를 실행해서 세션 0의 svchost.exe프로세스와 세션 1의 notepad.exe프로세스에 각각 dummy.dll파일을 인젝션하고 InjectDll.exe와 dummy.cll파일을작업폴더에 복사한다음 실행해보면 세션 1의 프로레스에는 인젝션되고 0에는 인젝션이 실패하는것을 확인할 수있다. 

 

왤까..

세션0의 svchost.exe프로세스에 인젝션 시에 CreateRemoteThread() API함수 호출에 실패했으니까..

 

올리디버거를 실행시켜서  

API호출하는데까지 와서 파라미터를 확인해본다..

중요파라미터는 아래와 같다

① svchost.exe(PID: 2712)의 프로세스 핸들
② kernel32!LoadLibraryA() API 주소
③ svchost.exe의 프로세스 메모리에 할당한 버퍼 주소

 

해당 API로 들어가보면 내부적으로 kenelbase!CreateRemoteThreadEx()를 호출하는걸 알 수 있음

이때 파라미터 하나 늘어나고 ntdll!ZwCreateThreadEx() API호출하고나서도 파라미터개수가 늘어난다. 위에 123파라미터는 그대로 전달됨

여기서 계속 들어가다보면 SYSENTER명령어로 커널 모드로 진입하니 유저모드 디버깅으로는 더 이상 진행할 수 없다.

windows xp windows 7
Kernel32!CreateRemoteThread()
-> ntdll!ZwCreateThread()
Kernel32!CreateRemoteThread()
-> kernelbse!CreateRemoteThreadEx()
-> ntdll!ZwCreateThreadEx()

API호출흐름을 보면 위와 같다.

이렇게 7에서 새로 추가된 API가 있으니까 DLL인젝션이 실패했다고 가정할 수 있다.

 

그럼 어케 해야될까.. CreateRemoteThread()대신 ZwCreateThreadEx()를 직접호출하면 된다.

구체적으로는 ZwCreateThreadEx()를 직접 호출해 성공한 경우에는 CreateSuspended 파라미터가 0이고 CreateRemoteThread() API내부에서 호출되는 ZwCreateThreadEx()호출에서는 이 파라미터 값이 1이다. 그래서 인젝션이 실패한것..

 

즉 파라미터에서 0으로 되어있던거를 1로 바꿔주면 인젝션이 된다.!!

오 진짜됨

 

어랍쇼 캡쳐한거 왜 일케 남았지

 

아무튼 2번째 방법도 있는데 이건 조건 분기를 조작하는 방법이라고 한다.

ZwCreateThreadEx()를 그대로 실행하면 pThread Handle에 값이 세팅되는데 이거는 스레드는 정상적으로 생성이 되었다는 말이다. 근데 이게 정상적으로 동작하지 않았다면 호출되지 못했거나 ntdll!ZwResumeThread() API가 실패했거나인것

 

근데 코드 확인해보면 조건분기 명령어로 이 API 호출없이 넘어가는걸 볼 수 있다. 그래서 이 조건분기문을 조작해서 호출되도록 하면 DLL인젝션이 성공하는 것이다.

 

윈도우 7에서는 kernel32!CreateRemoteThread()를 호출하는것보다 ntdll!ZwCreateThreadEx() API를 직접호출하는게 더 낫다 -> 새로운 InjectDll_new.exe프로그램

 

InjectDll_new.exe

kernel32!CreateRemoteThread()를 직접 호출하지 않고, 사용자 정의 함수인 MyCreateRemoteThread()를 호출

MyCreateRemoteThread()는 운영 체제(OS) 버전을 확인해서

Vista 이상에서는 ntdll!NtCreateThreadEx()를 호출

XP 이하에서는 kernel32!CreateRemoteThread()를 호출하

한다..

반응형

'공부해요 > 리버싱_핵심원리' 카테고리의 다른 글

45. TLS 콜백 함수  (0) 2025.01.02
44. InjDll.exe-DLL 인젝션 전용 도구  (0) 2024.12.26
42. Session in Kernel 6  (0) 2024.12.26
41. ASLR  (0) 2024.12.26
40. 64비트 디버깅  (0) 2024.12.26