본문 바로가기

카테고리 없음

2024.8.16 리버싱 공부(나뭇잎 책)

API 후킹

리버싱의 핵심, 리버싱의 꽃

Wind32 API를 후킹하는 기술을 API 후킹이라고 함.

API는 하드웨어를 사용하고 싶을때 OS에서 제공하는 방법

Win32 API호출을 중간에 가로채서 제어권을 얻어내는 것.

 

정상적인 흐름 : 프로세스 -> createFile()

후킹 흐름 : 프로세스 -> MyCreateFile() -> CreateFile()

 

방법은 static ,dynamic이 있음

static은 파일 수정

dynamic은 메모리 수정

 

Location(공략 위치)

IAT - 프로세스의 IAT조작

Code - 코드를 수정(시작 코드 JMP 패치, 함수 덮어쓰기, 필요한 부분 변경)

EAT -DLL의 EAT조작

 

방법

Debug

Injection(Dll injection, Code injection)

 

디버거를 이용해 dynamic, Code 수정으로 메모장 WriteFile 후킹해서 영어 소문자인 것들을 대문자로 바꾸어 저장하기

 

 

#include "windows.h"
#include "stdio.h"
#include "tlhelp32.h"
#include "tchar.h"


#define DEF_PROC_NAME (L"notepad.exe")

LPVOID g_pfWriteFile = NULL;
CREATE_PROCESS_DEBUG_INFO g_cpdi;
BYTE g_chINT3 = 0xCC, g_chOrgByte = 0;
DWORD dwPID;


DWORD FindProcessID(LPCTSTR szProcessName)
{
    DWORD dwPID = 0xFFFFFFFF;
    HANDLE hSnapShot = INVALID_HANDLE_VALUE;
    PROCESSENTRY32 pe;

    // Get the snapshot of the system
    pe.dwSize = sizeof(PROCESSENTRY32);
    hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, NULL);

    // find process
    Process32First(hSnapShot, &pe);
    do
    {
        if (!_tcsicmp(szProcessName, (LPCTSTR)pe.szExeFile))
        {
            dwPID = pe.th32ProcessID;
            break;
        }
    } while (Process32Next(hSnapShot, &pe));

    CloseHandle(hSnapShot);

    return dwPID;
}

//writeFile의 첫 바이트를 저장하고 0xcc로 바꿈
BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde)
{
    g_pfWriteFile = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile");//00007FF9967E5310
    printf("%p\n", g_pfWriteFile);

    memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO));
    ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
        &g_chOrgByte, sizeof(BYTE), NULL);
    WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
        &g_chINT3, sizeof(BYTE), NULL);

    return TRUE;
}
// breakPoint 일떄 즉 0xcc 만날때 처리
BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pde)
{
    CONTEXT ctx;
    PBYTE lpBuffer = NULL;
    DWORD dwNumOfBytesToWrite,  i;
    long long dwAddrOfBuffer;
    PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord;

    if (EXCEPTION_BREAKPOINT == per->ExceptionCode)
    {
        if (g_pfWriteFile == per->ExceptionAddress)
        {
            WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
                &g_chOrgByte, sizeof(BYTE), NULL);

            // #2. 레지스터 정보 얻기
            ctx.ContextFlags = CONTEXT_CONTROL;
            GetThreadContext(g_cpdi.hThread, &ctx);
            GetThreadContext(g_cpdi.hProcess, &ctx);

            
          //  dwNumOfBytesToWrite = 5;//ctx.R8;
        //    dwAddrOfBuffer = ctx.Rdx;
            printf("R8 = %p\n", ctx.R8);
            
            printf("Rdi = %p\n", ctx.Rdi);
            printf("Rax = %p\n", ctx.Rax);
            printf("Rdx = %p\n", ctx.Rdx);
            printf("Rbx = %p\n", ctx.Rbx);
            printf("R11 = %p\n", ctx.R11);
            printf("Rip = %p\n", ctx.Rip);
      
            ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Rsp - 0x08),
                &dwAddrOfBuffer, sizeof(long long), NULL);
            printf("%p\n", dwAddrOfBuffer);

            ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Rsp + 0x08),
                &dwNumOfBytesToWrite, sizeof(DWORD), NULL);
            printf("%d\n", dwNumOfBytesToWrite);
            lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite + 1);
            memset(lpBuffer, 0, dwNumOfBytesToWrite + 1);
            ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(dwAddrOfBuffer),
                lpBuffer, dwNumOfBytesToWrite, NULL);
            //lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite + 1);
            //memset(lpBuffer, 0, dwNumOfBytesToWrite + 1);
            //printf("rsp = %p\n", ctx.Rsp);
/*
            lpBuffer = (PBYTE)malloc(0x201);
            memset(lpBuffer, 0, 0x201);


            ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Rsp),
                lpBuffer, 0x8, NULL);
            for (i = 0; i < 0x8; i++)
            {

                printf("%02x", lpBuffer[i]);

            }
            printf("i \n");

            lpBuffer = (PBYTE)malloc(0x201);
            memset(lpBuffer, 0, 0x201);

            ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Rsp-0x100),
                lpBuffer, 0x200, NULL);

            printf("\n### original string ###\n%s\n", lpBuffer);

            for (i = 0; i < 0x200; i++)
            {
                if ((i % 8) == 0) {
                    printf("i=%d/// \n",i/8);
                }
             
                printf("%02x", lpBuffer[i]);
              
            }*/



            printf("\n### org string ###\n%s\n", lpBuffer);

            for (i = 0; i < dwNumOfBytesToWrite; i++)
            {
                if (0x61 <= lpBuffer[i] && lpBuffer[i] <= 0x7A)
                    lpBuffer[i] -= 0x20;
            }

            printf("\n### converted string ###\n%s\n", lpBuffer);

            WriteProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,
                lpBuffer, dwNumOfBytesToWrite, NULL);

            free(lpBuffer);

            ctx.Rip = (DWORD)g_pfWriteFile;
            SetThreadContext(g_cpdi.hProcess, &ctx);

            ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId, DBG_CONTINUE);
            Sleep(1);
            // #11. API Hook
          WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,&g_chINT3, sizeof(BYTE), NULL);

          //  DebugActiveProcessStop(dwPID);
           
        //    printf("\n### org string ###\n%s\n", lpBuffer);
         //   OnCreateProcessDebugEvent(pde);

            return TRUE;
        }
    }
    printf(
        "Error Code = %d\n", GetLastError());
    printf("ee = %p\n", per->ExceptionCode);
   // ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
    return FALSE;
}

void DebugLoop()
{
    DEBUG_EVENT de;
    DWORD dwContinueStatus;

    while (WaitForDebugEvent(&de, INFINITE))
    {
        dwContinueStatus = DBG_CONTINUE;

        if (CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode)
        {
            OnCreateProcessDebugEvent(&de);
        }
        else if (EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode)
        {
            if (!OnExceptionDebugEvent(&de))
            {
               // dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
            }
            else
                continue;

        }
        else if (EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode)
        {
            printf(
                "Error Code = %d\n", GetLastError());
            printf("break\n");
            break;
        }
        
        ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);
    }
}

int main(int argc, char* argv[])
{
   

    // Attach Process
    dwPID = (DWORD)FindProcessID(DEF_PROC_NAME);
    if (!DebugActiveProcess(dwPID))
    {
        printf("DebugActiveProcess(%d) failed!!!\n"
            "Error Code = %d\n", dwPID, GetLastError());
        return 1;
    }
    printf("debugging..\n");

    DebugLoop();

    return 0;
}

 

코드를 살펴보면 

 

main

 1.프로세스 이름으로 PID를 찾고 PID의 프로세스에 디버거를 붙인다.

 2.DebugLoop함수에 들어간다

 

 

DebugLoop

1. WaitForDebugEvent함수로 이벤트를 기다린다.

2.발생이벤트에 따라 처리한다

디버거가 붙으면 - OnCreateProcessDebugEvent

디버거가 뗴지면 - break

디버거에 예외 이벤트가 발생하면  - OnExceptionDebugEvent

 

OnCreateProcessDebugEvent

1.writefile의 주소를 찾는다

2.g_cpdi에 create process info를 복사한다.
3.프로세스의 writefile 함수 첫 바이트를 읽고 저장한다.
3.프로세스의 writefile 함수 첫 바이트를 INT3명령어(0xcc)로 변경한다.

 

OnExceptionDebugEvent

1. BP 이벤트인지 확인하고 , writefile함수의 첫번째에서 발생한 것인지 확인한다.

2.레지스터의 정보를 얻는다.

3.writefile의 인자를 저장한다.(길이, 문자열 시작 주소)

4.문자열을 대문자로 바꾸어 적는다.

5.Rip 값을 writefile로 바꾸고( 현재 rip는 writefile +1임 INT 3(1바이트를 실행하고 넘어와서) 프로세스에 반영한다.

6.프로세스를 진행시킨다. (제대로 진행 시키기 위해 sleep(1))

7.다시 훅을 걸기 위해 writefile의 맨 앞을 0xcc로 바꾼다.

 

 

여기서 내가 x64dbg로 메모장을 분석해보았는데

 

rcx에는 뭔지 모를게 들어가고

rdx에 문자열 주소, r8에는 길이 r9에도 모를게 들어갔다.

 

그래서 x64dbg상에서 r8을 늘리고 rdx의 주소를 다른걸로 바꾸고 그 주소에 문자열을 길게 써봤는데 작동을 했다.

 

하지만 (위 코드)디버거로 코드를 수정할때 reg를 얻어왔을때 다른 값들이 들어있었다. 이유를 모르겠다.

 

그래서 (위 코드)디버거로 문자의 길이와 주소를 알아오기 위해 stack을 위 아래 0x100 바이트씩 출력해서 RSP 기준 값들을 보면서 찾았다. 그게 rsp+8 ,rsp-8 이었다.

 

 

되긴 함 소문자 글자를 다 대문자로 바꾸기

왜 레지스터 값이 다를까