앙 규무링~ 

 

 

DLL 인젝션

오늘은 DLL 인젝션의 동작 원리(?)에 대해서 좀 집고 넘어면 좋을 것 같아서 써본다

 

DLL 인젝션이란 ?

실행 중인 다른 프로세스에 특정 DLL 파일을 강제로 삽입하는 기법.

 KeyHook.dll을 인젝션!

어제 살펴봤던 것처럼 다른 프로세서의 메모리 공간으로 DLL을 강제 삽입 시키는 행위 입니다.

DLL에 관해서는 먼저 읽어봐야할 자료가 있습니다.

- 블랙펄 시큐리티 자료입니다.

 

[정상인이 쓴 DLL 인젝션을 사용한 API 후킹]

[PLT와 GOT 자세히 알기 1]

[PLT와 GOT 자세히 알기 2]

 

위의 글들을 읽었다는 가정 아래 DLL 인젝션을 할 때 절차를 확인해봅시다.

 

DLL injection 순서!

#1. 대상 프로세스의 핸들 구하기

#2. 대상 프로세스 메모리에 인젝션할 DLL 경로를 쓴다.

#3. LoadLibraryW() API 주소를 구한다.

#4. 대상 프로세스에 원격 스레드(Remote Thread)를 실행한다.

 

- 위의 방법이 가장 무난한 dll 인젝션 방법이라고 합니다. 그럼 하나씩 알아봅시다!

 

#1. 대상 프로세스의 핸들 구하기

 

'hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID))'

 

프로세스에 DLL을 인젝션 하기 위해서는 대상 프로세스의 PID값이 필요합니다. 이 PID값을 OpenProcess() API를 통해서 알아오게 됩니다. 

3번째 파라미터인 dwPID 값을 사용해 알아내서 프로세스의 핸들을 획득할 수 있습니다.

 

#2. 대상 프로세스 메모리에 인젝션할 DLL 경로를 쓰기

 

'pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE) ;'

 

위의 방식으로 프로세스의 핸들을 얻었다고 한다면, 프로세스에 로딩할 DLL 파일의 경로를 알려줘야 합니다.

어따가 로딩할 껀지 알려주기 위해서 VirtualAllocEx() API를 이용해 상대방 프로세스의 메모리 공간에 버퍼를 할당합니다.

 

'WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName, dwBufSize, NULL)'

 

앞에서 할당받은 버퍼주소 (pRemoteBuf)에다가 WriteProcessMemory() API를 이용하여 DLL 경로 문자열을 써줍니다.

이 WriteProcessMemory() 여깃 hProcess 핸들이 가리키는 상대 프로세스에 메모리 공간에 쓰는 것입니다.

 

정리 : VirtualAllocEx()로 메모리 공간을 확보하고, WriteProcessMemory()로 로딩될 dll 경로를 적어줌.

 

#3. LoadLibraryW() API 주소를 구하기

 

hMod = GetModuleHandle("kernel32.dll");

pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");

 

DLL 인젝션을 하기 위해서는 'kernel32.dll' 안의 LoadLibrary() 함수를 이용해야 합니다. 그러므로 GetModuleHandle()을 이용하여 커널.dll의 핸들을 획득한 후에 GetProcAddress()를 사용하여 LoadLibrary()의 주소를 알아오게 됩니다.

 

#4. 대상 프로세스에 원격 스레드 실행

 

'hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);'

 

모든 준비가 끄탄고 마지막으로 대상 프로세스(ex:notepad.exe)로 하여금 LoadLibraryW() API를 호출하도록 명령을 내리면 되는데... Window OS는 그러한 API를 제공하지 않습니다. 따라서 CreateRemoteThread() API를 사용해서 프로세스 내부에 스레드를 생성하고 그 스레드의 시작주소를 LoadLibraryW()의 주소로 채워넣어 실행시키는 방법을 사용합니다.

 

HANDLE CreateRemoteThread(
  HANDLE                 hProcess,				// 프로세스 핸들
  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  SIZE_T                 dwStackSize,
  LPTHREAD_START_ROUTINE lpStartAddress,	// 스레드 함수 주소
  LPVOID                 lpParameter,			// 스레드 파라미터 주소
  DWORD                  dwCreationFlags,
  LPDWORD                lpThreadId
);

myhack.dll 실습!

다음의 파일을 이용하여 dll 인젝션을 해보자!

 

InjectDll.exe
0.04MB
myhack.dll
0.03MB

첫 번째로 메모장을 이용해 실습할 것이므로 메모장의 PID를 구합니다.

PID = 5920!

그 다음 cmd를 오픈한뒤에 다음과 같이 실행 파라미터를 입력해서 InjectDll.exe를 실행합니다.

 

success!!

 

dll 인젝션이 성공하게 되면 하나의 index.html파일이 생성되게 되며, 페이지를 열게 되면 다음과 같습니다!

어디서 본 페이지죠 ㅎㅎ?

실습 끝!

 

은 아니고 이제 소스코드를 분석할 시간이다...

InjectDll.cpp
0.00MB
myhack.cpp
0.00MB

 

먼저 Myhack.cpp를 보자!

// Myhack.cpp

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

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

#define DEF_URL     	(L"http://www.naver.com/index.html")
#define DEF_FILE_NAME   (L"index.html")

HMODULE g_hMod = NULL;

DWORD WINAPI ThreadProc(LPVOID lParam)
{
    TCHAR szPath[_MAX_PATH] = {0,};

    if( !GetModuleFileName( g_hMod, szPath, MAX_PATH ) )
        return FALSE;
	
    TCHAR *p = _tcsrchr( szPath, '\\' );
    if( !p )
        return FALSE;

    _tcscpy_s(p+1, _MAX_PATH, DEF_FILE_NAME);

    URLDownloadToFile(NULL, DEF_URL, szPath, 0, NULL);

    return 0;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    HANDLE hThread = NULL;

    g_hMod = (HMODULE)hinstDLL;

    switch( fdwReason )
    {
    case DLL_PROCESS_ATTACH : 
        OutputDebugString(L"<myhack.dll> Injection!!!");
        hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
        CloseHandle(hThread);
        break;
    }

    return TRUE;
}

먼저 Dllmain()을 봅시다! DLL이 로딩되어지면서(DLL_PROCESS_ATTACH)될 때 디버그 문자열을 출력합니다.

("myhack.dll injection!!!") 그 후 스레드를 실행합니다. ThreadProc()의 내용은 URLDownloadToFile() API를 이용해 사이트의 index.html 파일을 다운받게 합니다. 프로세스에 DLL 인젝션이 발생한 경우 해당 DLL의 DllMain() 함수가 호출되고 결국 URLDownloadToFile() API가 호출되게 되는 원리! - 잘읽어보십쇼

 

다음은 InjectDll.cpp의 소스코드를 보겠습니다.

 

// InjectDll.cpp

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

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) 
{
    TOKEN_PRIVILEGES tp;
    HANDLE hToken;
    LUID luid;

    if( !OpenProcessToken(GetCurrentProcess(),
                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 
			              &hToken) )
    {
        _tprintf(L"OpenProcessToken error: %u\n", GetLastError());
        return FALSE;
    }

    if( !LookupPrivilegeValue(NULL,           // lookup privilege on local system
                              lpszPrivilege,  // privilege to lookup 
                              &luid) )        // receives LUID of privilege
    {
        _tprintf(L"LookupPrivilegeValue error: %u\n", GetLastError() ); 
        return FALSE; 
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if( bEnablePrivilege )
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.
    if( !AdjustTokenPrivileges(hToken, 
                               FALSE, 
                               &tp, 
                               sizeof(TOKEN_PRIVILEGES), 
                               (PTOKEN_PRIVILEGES) NULL, 
                               (PDWORD) NULL) )
    { 
        _tprintf(L"AdjustTokenPrivileges error: %u\n", GetLastError() ); 
        return FALSE; 
    } 

    if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
    {
        _tprintf(L"The token does not have the specified privilege. \n");
        return FALSE;
    } 

    return TRUE;
}

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;

    // #1. dwPID 를 이용하여 대상 프로세스(notepad.exe)의 HANDLE을 구한다.
    if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
    {
        _tprintf(L"OpenProcess(%d) failed!!! [%d]\n", dwPID, GetLastError());
        return FALSE;
    }

    // #2. 대상 프로세스(notepad.exe) 메모리에 szDllName 크기만큼 메모리를 할당한다.
    pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);

    // #3. 할당 받은 메모리에 myhack.dll 경로("c:\\myhack.dll")를 쓴다.
    WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllPath, dwBufSize, NULL);

    // #4. LoadLibraryA() API 주소를 구한다.
    hMod = GetModuleHandle(L"kernel32.dll");
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");
	
    // #5. notepad.exe 프로세스에 스레드를 실행
    hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);	

    CloseHandle(hThread);
    CloseHandle(hProcess);

    return TRUE;
}

int _tmain(int argc, TCHAR *argv[])
{
    if( argc != 3)
    {
        _tprintf(L"USAGE : %s <pid> <dll_path>\n", argv[0]);
        return 1;
    }

    // change privilege
    if( !SetPrivilege(SE_DEBUG_NAME, TRUE) )
        return 1;

    // inject dll
    if( InjectDll((DWORD)_tstol(argv[1]), argv[2]) )
        _tprintf(L"InjectDll(\"%s\") success!!!\n", argv[2]);
    else
        _tprintf(L"InjectDll(\"%s\") failed!!!\n", argv[2]);

    return 0;
}

main() 부터 살펴봅시다. main()의 주요기능은 프로그램의 파라미터들에 대해서 체크한 후에 InjectDll()함수를 호출하는 것입니다. InjectDll() 함수가 바로 DLL 인젝션을 하는 주요 함수입니다!

그럼 호출되어지는 InjectDll() 함수는 대상 프로세스로 하여금 LoadLibrary("myhack.dll") API를 호출하도록 명령하는 기능을 가지고 있습니다.

 

-> 이 InjectDll()를 위의 주석과 절차에 따라서 한 줄씩 확인해보면 신상에 이로울것 같습니다!

'Reversing' 카테고리의 다른 글

리버싱 10  (0) 2019.04.11
리버싱 9  (0) 2019.04.10
리버싱 8  (0) 2019.04.09
리버싱 7  (0) 2019.04.05
리버싱 6  (2) 2019.04.04

 앙 귬귬띠 

 

 

DLL 인젝션

- 훅(Hook) 이란 ?

중간에서 오고가는 정보를 엿보거나 가로채기 위해 초소를 설치함. (갈고리를 건다)

이중에서 메세지 훅에 대해서 알아봅시다.

 

- 메세지 훅 ?

Window는 GUI를 제공하고 Event Driven으로 동작한다고 핟나. 키보드/마우스 등 어떠한 행위를 할 때 이벤트가 발생하고 Window는 미리 정의된 메세지를 해당 응용 프로그램으로 통보합니다. 아래 그림을 살펴봅시다.

 

https://yokang90.tistory.com/58

만약에 키보드에 입력 이벤트가 발생하면 WM_KEYDOWN 메세지가 OS queue에 추가됩니다. OS는 어느 응용 프로그램에서 이벤트가 발생한지 확인 후에 큐에서 꺼내서 Application message queue에 추가합니다. 응용 프로그램(메모장)은 자신의 app 큐를 모니터링 하다가 WM_KEYDOWN 메세지를 확인한 다음에 해당 event handler를 호출하게 됩니다.

 

위 그림과 같이 훅이 설치되어있으면 OS와 APP 큐 사잉 ㅔ설치된 훅체인을 통해 키보드 메세지 훅들을 응용 프로그램보다 먼저볼 수 있습니다. 

-> 저부분에서 가로채는것 뿐만 아니라 메세지 자체의 변경도 가능하며, 아래로 안내려 보낼수도 있다!

 

메세지 훅같은 경우에는 SetWindowsHookEx() API를 사용하면 간단하게 구현할 수 있다고 합니다.

HHOOK SetWindowsHookExA(
  int       idHook,		// hook type
  HOOKPROC  lpfn,		// hook procedure
  HINSTANCE hmod,		// 프로시저가 속해있는 DLL 핸들
  DWORD     dwThreadId		// hook을 걸고 싶은 thread ID
);

msdn - api 정의

훅 프로시저는 OS가 호출해주는 콜백 함수이며 훅을 걸때 DLL 내부에 존재해야 한다.

4번째 변수 dwThreadID를 0으로 줄경우 전역(Global) 훅이 설치되며 실행중인 모든 프로세스에 영향이 간다.

 

키보드 메세지 후킹 실습

- chrome.exe 프로세스의 키보드 메세지를 가로채서 입력을 받지 못하도록 하는 실습

HookMain.exe
0.04MB
KeyHook.dll
0.03MB

HookMain.exe를 실행하면 다음과 같은 창이 뜬다.

후킹중

이 상태로 chrome.exe를 실행하여 키보드 입력을 해봅니다.

-> 되지 않음

 

Keyhook.dll 인젝션 되어있는 모습
PID 매칭됌

chrome.exe의 메모리에 keyhook.dll이 인젝션 된것이 보인다. 키보드 이벤트 발생시 chrome.exe는 아무것도 받지 못하기 때문에 검색창에 입력이 되지않는다.

 

Hookmain.exe 디버깅

EP에서 시작!

먼저 우리가 봤었던 press 'q'를 찾아봅시다. (문자열 검색기능 이용)

찾음

40104D에서 찾을 수 있습니다. 여기가 main()의 안이겠죠 ?

 

main()

 

401000에 BP를 설치한 후에 디버깅을 위부터 차례대로 시작합니다.

401006의 주소에서 Loadlibrary("keyhook.dll")을 호출합니다.

이후에 40104B의 call ebx에서 keyhook.hookstart() 함수가 호출됨을 알 수 있습니다.

call 안으로 들어가봅시다.

지금 보이는 그림은 HookMain.exe 프로세스에 로딩된 Keyhook.dll의 HookStart() 함수 입니다. 여기서 SetWindowsHookEx의 첫 번째와 두 번째 인자값들을 알 수 있습니다.

 

먼저 API의 첫 번째 파라미터 값은 WH_KEYBOARD(2)입니다. 두 번째 파라미터(lpfn)의 값은 10001020 이며, 이 값이 바로 훅 프로시저의 주소라고 합니다. main()의 나머지 부분들은 'q'를 입력시 종료받는 부분입니다. (디버깅해보기)

 

이제 notepad.exe를 실행 시킨다음에 HookMain.exe를 실행시키고 키보드 입력을 받습니다.

(디버그 dll 로드 on)

다음과 같이 10000000의 주소에 keyhook.dll이 로드되는 걸 볼 수 있습니다. 해당 EP로 가봅시다.

아까 봤던 훅 프로시저의 주소 (10001020)에 BP를 걸면 키보드 입력 이벤트가 발생할 때마다 이곳에서 멈춰집니다.

Hookmain.cpp과 keyhook.cpp 소스코드를 첨부합니다.

출처 - reversecore

HookMain.cpp
0.00MB
KeyHook.cpp
0.00MB

// HookMain.cpp	출처 - reversecore

/dll 로더
#include "stdio.h"
#include "conio.h"
#include "windows.h"
 
#define DEF_DLL_NAME  "KeyHook.dll"
#define DEF_HOOKSTART  "HookStart"
#define DEF_HOOKSTOP  "HookStop"
 
typedef void(*PFN_HOOKSTART)();
typedef void(*PFN_HOOKSTOP)();
 
void main()
{
    HMODULE   hDll = NULL;
    PFN_HOOKSTART HookStart = NULL;
    PFN_HOOKSTOP HookStop = NULL;
    char   ch = 0;
 
    // KeyHook.dll 로드
    hDll = LoadLibrary("KeyHook.dll");
 
    // HookStrat, HookStop 함수의 주소를 얻어온다.
    HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);
    HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);
 
    // 후킹 시작
    HookStart();
 
    // q가 입력될때 까지 로더는 아무런 기능을 하지않고 대기
    printf("press 'q' to quit!\n");
    while (getch() != 'q');
 
    // 후킹 종료
    HookStop();
 
    // KeyHook.dll 언로드
    FreeLibrary(hDll);
}
//keyhook.cpp

#include "stdio.h"
#include "windows.h"

#define DEF_PROCESS_NAME		"notepad.exe"

HINSTANCE g_hInstance = NULL;
HHOOK g_hHook = NULL;
HWND g_hWnd = NULL;

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
{
	switch( dwReason )
	{
        case DLL_PROCESS_ATTACH:
			g_hInstance = hinstDLL;
			break;

        case DLL_PROCESS_DETACH:
			break;	
	}

	return TRUE;
}

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	char szPath[MAX_PATH] = {0,};
	char *p = NULL;

	if( nCode >= 0 )
	{
		// bit 31 : 0 => press, 1 => release
		if( !(lParam & 0x80000000) )
		{
			GetModuleFileNameA(NULL, szPath, MAX_PATH);
			p = strrchr(szPath, '\\');

            // 현재 프로세스 이름을 비교해서 만약 notepad.exe 라면 0 아닌 값을 리턴함
            // => 0 아닌 값을 리턴하면 메시지는 다음으로 전달되지 않음
			if( !_stricmp(p + 1, DEF_PROCESS_NAME) )
				return 1;
		}
	}

    // 일반적인 경우에는 CallNextHookEx() 를 호출하여
    //   응용프로그램 (혹은 다음 훅) 으로 메시지를 전달함
	return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

#ifdef __cplusplus
extern "C" {
#endif
	__declspec(dllexport) void HookStart()
	{
		g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);
	}

	__declspec(dllexport) void HookStop()
	{
		if( g_hHook )
		{
			UnhookWindowsHookEx(g_hHook);
			g_hHook = NULL;
		}
	}
#ifdef __cplusplus
}
#endif

'Reversing' 카테고리의 다른 글

DLL 인젝션  (2) 2019.04.12
리버싱 9  (0) 2019.04.10
리버싱 8  (0) 2019.04.09
리버싱 7  (0) 2019.04.05
리버싱 6  (2) 2019.04.04

인라인 패치

이번 시간에는 인라인 코드 패치를 실습해보자!

 

간단히 말해서 '코드 케이브'를 이용해서 패치를 하는 기법입니다. 대상 프로그램이 압축 또는 암호화 되어있어서 직접 파일을 수정하기 어려울 때 '케이브'를 이용해서 패치를 진행합니다.

 

출처 : http://blog.naver.com/PostView.nhn?blogId=ktw1332&logNo=220407203753&parentCategoryNo=&categoryNo=26&viewDate=&isShowPopularPosts=false&from=section

우리가 패치하고 싶은 코드가 OEP영역에 암호화 되어있는 경우 복호화시 어떻게 될지 모르기 때문에 인라인 코드 패치를 진행하는 것이다.

 

실습을 통해서 인라인 패치에 대해서 알아보자!

unpackme#1.aC.exe
0.00MB

먼저 프로그램을 실행 시켜봤더니 다음과 같았다.

저 문구들을 패치하는게 이번 문제인 것 같다.

디버거로 오픈 시에 다음과 같이 암호화 되어있는걸 볼 수 있다.

올리 디버거

 

x32dbg

 

둘이좀 다르게 보여주는데 잘 모르겠다; 아무튼 올리말고 xdbg로 해보자!

일단 내가 찾던 메세지는 검색해본 결과 없고, 좀더 디버깅을 진행해보자!

 

디버깅을 진행하다보면 다음과 같은 코드를 볼 수 있다. 이 형태를 잘 기억해두자

-> 복호화 루프라고 한다.

복호화 루프

xor byte ptr ds:[ebx], 44 에서 특정영역(4010F5 ~ 401248)을 복호화합니다.

그리고 4010B0의 첫 번째 call로 들어가보면 또다른 2개의 복호화 루프를 볼 수 있습니다.

복호화 루프 2

4010C8의 xor에 의해서 401007 ~ 401085가 복호화 되고, 다시 4010DB의 xor로 4010F5 ~ 401248 영역이 복호화 됩니다. 복호화 루프 1과 동일한 범위로 2중 복호화가 진행.

(이거 ebx에서 시작해서 ecx 1씩 줄면서 루프돌리면서 ebx 1씩 증가하는거 관찰하샘)

 

그렇게 2개의 루프를 지나고 다음의 call 401039 명령을 만나게 됩니다.

안으로 들어가보면 다음과 같습니다.

여기서 눈여겨 볼 부분은 checksum 루프 입니다.

먼저 401041의 edx 0을 통해서 edx를 초기화 합니다. 후에 401046에 ADD 명령으로 특정 주소 영역(4010F5 ~ 401248)에서 4바이트 단위로 순차적으로 값을 읽어서 edx에 덧셈 연산으로 누적시켜버립니다!

 

이렇게 루프를 다돌고 탈출하면 edx 레지스터에 특정한 값이 저장되는데, 이게바로 checksum 값 !!!!

ecx=0 되고 edx가 checksum값이 됨 

 

이 checksum 영역은 위에서 설명했다시피 2중으로 암호화되어 있는 영역임. 바로 이영역에 우리가 패치하려는 문자열이 존재할 것 같음.

 

그 후에 내가나온 checksum 값을 비교하는 401062의 CMP/JE 명령을 통해서 서로 다르면 에러 메세지 출력 후에 종료, 같다면 401083의 JMP로 OEP로 가게 됩니다.

checksum 결과에 따른 조건 분기

이러한 checksum 계산 방법은 특정 영역 코드/데이터가 변조되지 않았음을 검증하는 용도로 많이 사용된다고 합니다.

-> 해당 영역에서 한 바이트만 변경되어도 checksum 값이 달라짐

데이터 변조하고 그럼 checksum 비교 부분까지 변조하면 되겠네 ??? 

 

어찌 됐건 OEP로 가보자!

쩜프! 쩜프! 해서 여기까지 도착하게 된다.

OEP !

해당 부분은 보시다시피 다이얼로그를 실행시킵니다. 40123E주소의 다이얼로그를 실행시키면 박스가 나타나게 되죠.

DialogBoxParam() API의 정의는 MSDN에서 찾아볼 수 있습니다.

DialogBoxParam()는 총 5개의 변수를 받습니다. 

INT_PTR DialogBoxParamA(
  HINSTANCE hInstance,
  LPCSTR    lpTemplateName,
  HWND      hWndParent,
  DLGPROC   lpDialogFunc,
  LPARAM    dwInitParam
);

네 ~ 이중에서 4번째 파라미터가 프로시저의 주소라고 합니다.

5개 중 바로 이 4번째 주소!

그래서 PUSH 4010F5로 가봅니다.

패치 문자열 발견!!!

이곳에 패치해야할 문자열이 있는걸 확인할 수 있었습니다. (40110A, 401123) 위치 확인

그럼 인라인 패치를 진행해보도록 합시다.

 

여태까지 한게 이렇게 된다고함https://flack3r.tistory.com/entry/%EC%9D%B8%EB%9D%BC%EC%9D%B8-%ED%8C%A8%EC%B9%98

 

그렇다면 ... 프로그램의 흐름을 좀 위의 그림을 통해서 살펴보고 가봅시다 어려움 ㅡㅡ

401000	[EP Code]
40109B		[Decode]
4010A3			XOR [B] with 44
4010C8			XOR [C] with 7
4010DB			XOR [B] with 11
401039			[A]
401046				Checksum [B]
401090				XOR [C] with 17
401083				JMP OEP

일단.. [EP Code]는 [Decode]를 호출하며 디코딩 부분에서는 [B] - [A] - [B] 순서로 디코딩 작업이 이루어짐.

그러면면서 암호가 해제된 [A] 영역의 코드를 실행하게 되는데,,, 디코딩 과정해서 아까봤다시피 edx로 checksum 값이 떨어지게 됨. 이 checksum값을 [A]영역에서 cmp/je 하며 쳌썸 변경여부를 판별함.

그런 다음에 [C] 영역을 디코딩하고 마지막으로 OEP(40121E)로 점프함!

 

이걸 직접 디버깅하면서 확인해보라고 하시지만 어렵다...

 

인라인 패치 실습

어찌됐건 우리는 [B]영역의 문자열을 패치해야 되는데,, 이중으로 암호화되어있음, 그리고 쳌썸까지 써가면서 데티ㅓ가 변조되었는지 비교하기 때문에 이럴때 인라인 패치를 하면 좀 수월하게 패치할 수 있다고 함.

 

작업 순서는 이렇게 됨

#1. 파일의 적절한 위치에 패치시킬 문자열 삽입 (null padding 구역)

#2. [A]영역의 JMP OEP를 내가 삽입시킨 Patch Code로 점프시킴

#3. 프로그램이 실행되어 [A]의 JMP Patch Code를 만나면 패치 코드에서 문자열을 변경한후 OEP로 JMP하면 끝!

(동굴 판다고 생각하면 쉽네요)

 

패치코드를 설치하는데에 있어서 일반적으로 3가지 정도의 방법을 제시해줌

1. 널패딩

2. 마지막 섹션을 확장해 공간을 확보한 후 설치

3. 새로운 섹션을 확장해 공간을 확보한 후 설치

-> PE 구조가 중요하구나...

 

책에서하는 1번 방법으로 실행해 보겠음. PE Viewr로 열어보자.

나는 PE를 배웠다

 

살펴보면 첫 번째 섹션은 일단 400 ~ 800까지이며 680의 영역까지 데이터가 들어가 있고 이는 메모리의 401000 ~ 401280의 영역에 대비되어짐. 즉 메모리의 401280 ~ 402000의 영역은 널 패딩 영역임을 알 수 있음.

 

HxD로 그럼 파일 옵셋을 보며 널패딩 구역에 해당하는 680구역을 보자!

 

정말 널패딩 영역임을 확인할 수 있다!

 

이곳에 패치 케이브를 설치하자!

먼저 앞서 다시 디버깅을 진행하면서 OEP(40121E)로 가자

쨘!

여기서 앞서 찾은 널 패딩이 680 ~ 800 이기 때문에 이걸 메모리랑 대응 시켰을때 401280 ~ 401400 입니다.

그렇다면 이부분에 패치코드를 다음과 같이 작성해보자 !

(olly랑 달라서 헷갈렷다능...)

 

kyumpack ㅋㅋㅋ

코드작성하고 40121E 원래의 OEP로 점프시켜주면 패치코드가 실행되겠져 ??!

마지막으로 앞서만든 패치코드가 실행되도록 401083 주소의 jmp oep(40121e)를 패치코드 주소로 변경합니다!

변경!

그리고 해당 부분은 XOR 7 암호화를 하고있습니다. 여기서 주의할 것은 현재보이는 'E9 96 01'은 암호화가 풀린 상태의 모습이고, 파일에서 실제로 암호화된 모습은 다음과 같습니다.

 

파일의 'EE 91 06'이 XOR 7 복호화를 통해서 'E9 96 01'로 바뀐 것입니다. 패치 코드 주소는 401280 이므로 JMP명령은 다음과 같습니다.

위에서 확인 누르면 ㅇㅇ

이 instruction의 'E9 F8 01'을 그대로 쓰면 안 되고 복호화를 고려해서 XOR 7을 수행한 후에 씁니다.

 

'E9 F8 01' -> 'EE FF 06'

끝입니다. 이렇게 해서 완성!

kyumpack.exe
0.00MB

=> 글자 짤린다 ㅜㅜ 사내 MDS 때문인거 같다ㅠㅠ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'Reversing' 카테고리의 다른 글

DLL 인젝션  (2) 2019.04.12
리버싱 10  (0) 2019.04.11
리버싱 8  (0) 2019.04.09
리버싱 7  (0) 2019.04.05
리버싱 6  (2) 2019.04.04

+ Recent posts