iT邦幫忙

2022 iThome 鐵人賽

DAY 23
0
Security

我逆向你逆向我的逆向工程膩系列 第 23

Dx23 - 直接代碼注入

  • 分享至 

  • xImage
  •  

在前面幾篇學過了不少注入技巧,但是他們都有一個缺點 : 痕跡過大,容易被發現。所以今天來介紹代碼注入 ( Code Injection ) 這個技術。

它的優點有 :

  • 痕跡小
  • 使用記憶體小
  • 方便快速

所以說,如過要注入的代碼比較小,就非常推薦使用這方法。題外話,大部分的病毒、shellcode 都是使用這種方法進行注入操作來規避防毒軟體的查殺。

甚麼是代碼注入?

說道代碼注入最常想到的就是 SQL injectPHP inject 等,他們是因為處理了錯誤的數據導致風險發生。而今天的代碼注入可以說完全不一樣,他是透過創建 Thread 來執行要注入的代碼,不講武德的直接讓目標程序執行你想做的事。做個比喻 : 就像是台北車站塞愛心筆給你的那些人,會直接把筆塞到你身上而不是透過話術使你買筆。

https://ithelp.ithome.com.tw/upload/images/20221007/20135675ibLtVIq8xG.png

注射器製作

本篇使用源碼來自 :
https://github.com/reversecore/book/blob/master/소스코드/03_DLL_Injection/27_Code_Injection/src/CodeInjection/CodeInjection.cpp
個人改編與成果位於 :
https://github.com/Dinlon5566/IT_Reverse_Engineering/tree/main/Dx23

首先我們先看看要做甚麼東西 :

  1. 打開我們的好朋友: notepad.exe ( 這裡是用 64位元的 ),查看 PID

https://ithelp.ithome.com.tw/upload/images/20221007/20135675fomx0oLwur.png

  1. 執行注入器,目標是記事本的 PID。可以看到目錄下只有注入器而沒有注入的 DLL。因為已經把要注入的程式放在注入器中了。

https://ithelp.ithome.com.tw/upload/images/20221007/20135675QMOT0Cmuhb.png

  1. 可以看到順利運行,並有新的 Thread 產生。

https://ithelp.ithome.com.tw/upload/images/20221007/20135675uNbc3yEsGq.png

製作

開始學習如何製作注入器吧,我們的任務是在目標程序上執行這一行代碼( 實際上通常不止一行 ) :

MessageBoxA(0,"Hello \\OwO/","IT-help",0);
  1. 首先寫上 main 的大致上的流程要甚麼做,InjectCode( ) 是用來注入的函式。
int main(int argc,char* argv[])
{
	if (argc != 2) {
		printf("USEAGE : Injecter.exe <PID>\n");
		return 1;
	}
	DWORD pid = atol(argv[1]);
  InjectCode(pid);

	return 0;
}
  1. 接下來寫上要注入的代碼( Thread 的代碼 ),這邊稍有複雜。
  • 定義引用函數與使用資料的儲存結構體
// 定義引用函數與使用資料的儲存結構體
typedef struct _THREAD_PARAM
{
    FARPROC pFunc[2];               // LoadLibraryA(), GetProcAddress()
    char    szBuf[4][128];          // "user32.dll", "MessageBoxA", "www.reversecore.com", "ReverseCore"
} THREAD_PARAM, * PTHREAD_PARAM;

  • 定義函式的行為,方便之後呼叫時直接使用引數。

//定義函式行為

//LoadLibrary
typedef HMODULE(WINAPI* PFLOADLIBRARYA)
(
    LPCSTR lpLibFileName
    );

//GetProcAddress
typedef FARPROC(WINAPI* PFGETPROCADDRESS)
(
    HMODULE hModule,
    LPCSTR lpProcName
    );

//MessageBoxA
typedef int (WINAPI* PFMESSAGEBOXA)
(
    HWND hWnd,
    LPCSTR lpText,
    LPCSTR lpCaption,
    UINT uType
    );

  • 利用上面的結構體來啟動注入程序的主函式
DWORD WINAPI ThreadProc(LPVOID lParam)
{
    PTHREAD_PARAM   pParam = (PTHREAD_PARAM)lParam;
    HMODULE         hMod = NULL;
    FARPROC         pFunc = NULL;

    // LoadLibrary("user32.dll");
    hMod = ((PFLOADLIBRARYA)pParam->pFunc[0])(pParam->szBuf[0]); 
    if (!hMod)
        return 1;

    // GetProcAddress("MessageBoxA");
    pFunc = (FARPROC)((PFGETPROCADDRESS)pParam->pFunc[1])(hMod, pParam->szBuf[1]); 
    if (!pFunc)
        return 1;

    // MessageBoxA(NULL,"hello","IT",NULL);
    ((PFMESSAGEBOXA)pFunc)(NULL, pParam->szBuf[2], pParam->szBuf[3], MB_OK);

    return 0;
}
  1. 接下來要寫上 InjectCode( ) ,把函數寫入結構中並執行注入的功能。
  • 首先設定會用到的變數與數值,尤其是函式的名稱與引數字串等。
BOOL InjectCode(DWORD dwPID)
{
		// 把變數都宣告在這邊
    HMODULE         hMod = NULL;
    THREAD_PARAM    param = { 0, };
    HANDLE          hProcess = NULL;
    HANDLE          hThread = NULL;
    LPVOID          pRemoteBuf[2] = { 0, };
    DWORD           dwSize = 0;

		// 取得 kernel32 
		hMod = GetModuleHandleA("kernel32.dll");
    if (hMod == NULL) {
        printf("GetModeuleHandle fail : error code [%d]\n",GetLastError());
    }

    // 設定使用到函式的位置與所引入時的值
			// 利用 GetProcAddress 得到函式位置
    param.pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");    
    param.pFunc[1] = GetProcAddress(hMod, "GetProcAddress");
			// 把值填入變數中,方便之後引用
    strcpy_s(param.szBuf[0], "user32.dll");
    strcpy_s(param.szBuf[1], "MessageBoxA");
    strcpy_s(param.szBuf[2], "Hello \\OwO/");
    strcpy_s(param.szBuf[3], "IT-help");
  • OpenProcess(PID) 取得目標程序的 Handle,取得權限後才可以注入目標。

if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS,   // dwDesiredAccess
        FALSE,                // bInheritHandle
        dwPID)))             // dwProcessId
    {
        printf("OpenProcess() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }
  • 在目標程序宣告空間,大小為一個 THREAD_PARAM 的結構。
dwSize = sizeof(THREAD_PARAM);
 if (!(pRemoteBuf[0] = VirtualAllocEx(hProcess,          // hProcess
        NULL,                 // lpAddress
        dwSize,               // dwSize
        MEM_COMMIT,           // flAllocationType
        PAGE_READWRITE)))    // flProtect
    {
        printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }
  • 把程式寫入上面宣告的記憶體( 程式在 param 中 )
if (!WriteProcessMemory(hProcess,       // hProcess
        pRemoteBuf[0],                  // lpBaseAddress
        (LPVOID)&param,                 // lpBuffer
        dwSize,                         // nSize
        NULL))                         // [out] lpNumberOfBytesWritten
    {
        printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }
  • 接下來是宣告 Thread 所需要的空間。
    大小是 InjectCode - ThreadProc 這樣的位置相減,原因為在編譯時 VisualStudio 會依照函數的順序來進行編譯,所以會以 ThreadProc、InjectCode 的順序來編寫。而相減之後就是函數的空間。

https://ithelp.ithome.com.tw/upload/images/20221007/20135675rJFRiiwnAZ.png

// Allocation for ThreadProc()
    dwSize = (DWORD)InjectCode - (DWORD)ThreadProc;
    if (!(pRemoteBuf[1] = VirtualAllocEx(hProcess,          // hProcess
        NULL,                 // lpAddress
        dwSize,               // dwSize
        MEM_COMMIT,           // flAllocationType
        PAGE_EXECUTE_READWRITE)))    // flProtect
    {
        printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }
  • GetProcAddress() 中取得的資料位置寫入上方宣告記憶體中
if (!WriteProcessMemory(hProcess,                       // hProcess
        pRemoteBuf[1],                  // lpBaseAddress
        (LPVOID)ThreadProc,             // lpBuffer
        dwSize,                         // nSize
        NULL))                         // [out] lpNumberOfBytesWritten
    {
        printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }
  • 利用 CreateRemoteThread 遠端執行線程,透過 PID 開啟
if (!(hThread = CreateRemoteThread(hProcess,            // hProcess
        NULL,                // lpThreadAttributes
        0,                   // dwStackSize
        (LPTHREAD_START_ROUTINE)pRemoteBuf[1],     // dwStackSize
        pRemoteBuf[0],       // lpParameter
        0,                   // dwCreationFlags
        NULL)))             // lpThreadId
    {
        printf("CreateRemoteThread() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }
  • 等待線程結束,就關閉 Handle
		// wait to thread close
    WaitForSingleObject(hThread, INFINITE);

    // clode the Handle
    CloseHandle(hThread);
    CloseHandle(hProcess);

    return TRUE;
}

那看看能不能執行,並以系統管理員來執行程序 ( 一定要用系統管理員 )。

如果執行後沒有發生行為,請安裝 VC++
https://ithelp.ithome.com.tw/upload/images/20221007/2013567531gDQ3Td1N.png


上一篇
Dx22 - 一些工具的介紹
下一篇
Dx24 - API Hook
系列文
我逆向你逆向我的逆向工程膩31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言