使用匯編語言編寫注入代碼

這裏主要藉助OllyDbg的彙編功能,使用匯編語言編寫注入代碼即ThreadProc()函數。與上一篇文章的CodeInject.cpp代碼類似,但區別在於THREAD_PARAM結構體不包含字符串成員,且使用指令字節數組替代了ThreadProc()函數(因爲代碼本身同時包含所需的字符串數據)。

彙編語言常用的開發工具(Assembler)有MASM、TASM和FASM等。

編寫簡單的待修改程序

編寫一個並無啥功能的asmtest.exe程序以用於通過OllyDbg使用匯編編寫注入代碼。

asmtest.cpp

#include "stdio.h"

int test(){
    return 0;
}

int main(int argc, char* argv[]){
    return test();
}

編譯生成asmtest.exe。

使用OllyDbg的Assemble功能修改彙編代碼

使用OllyDbg打開asmtest.exe,劃到代碼區域的頂端位置,右鍵New origin here,將EIP更改爲改地址401000:

注意,New origin here命令僅用來修改EIP寄存器值,與直接通過調用方式轉到指定地址是不一樣的,因爲寄存器與棧中內容並未改變。

空格鍵即可彈出輸入彙編命令的窗口,將其中的複選“Fill with NOP's”取消掉(若處於複選狀態,輸入代碼長度短於已有代碼時,剩餘長度會填充爲NOP指令,以整體對齊代碼):

接下來使用匯編編寫ThreadProc()函數。與C語言編寫的不同在於,需要的Data(字符串)已包含在Code中。

自EP處輸入代碼至40102E地址的call指令處(後續再說明爲啥call地址爲40103E):

簡單地說一下,就是先建立棧幀,401001地址處的MOV指令將ESI指向LoadLibraryA(),接下來3條PUSH指令爲“User32.dll”的ASCII碼(這種將字符串壓入棧的方法僅適用於彙編編寫的程序),接着PUSH ESP將上述字符串壓入棧中,再CALL指令調用LoadLibraryA(‘User32.dll’);接着3條PUSH爲“MessageBoxA”,壓入棧後PUSH EAX將use32.dll地址壓入棧,再CALL指令調用GetProcAddress();PUSH 0指令壓入MessageBoxA()第四個參數uType值0,最後的CALL指令相當於PUSH+JMP指令,即將下列的字符串壓入棧後再跳轉到下一個位置執行。

接着在緊跟着的地址401033中輸入字符串,Ctrl+E打開Edit窗口,在ASCII欄輸入字符串“SKI12”,再到HEX欄最後輸入00即NULL結束字符串,其爲MessageBoxA()第三個參數lpCaption:

接着在40103E地址處填寫call指令,可以看到之前的call指向現在的call指令,中間保存的是輸入的‘SKI12’字符串,之所以顯示奇怪的命令在於OllyDbg將字符串誤認爲IA-32指令,是由於輸入者在Code位置輸入字符串引起的:

接着在該call指令下面打開Edit窗口輸入字符串‘Assemble Code Inject By SKI12’再輸入NULL如紫框所示,其爲MessageBoxA()第二個參數lpText,可以看到指令輸入到了401060地址處,修改該處Hex,前面的00爲之前字符串的NULL、後面修改爲6A00即PUSH 0指令(壓入MessageBoxA()第一個參數hWord)如綠框所示,接着通過Edit框輸入Hex值的方式分別往下輸入命令,call eax指令調用MessageBoxA(),XOR指令將線程函數的返回值設置爲0,最後刪除棧幀再return。完整的彙編代碼如下圖:

保存文件,右鍵》Copy to executable》All modifications》Copy all》Save file:

打開新保存的文件asmtest_ski12.exe,在Dump窗口中選中之前寫入的代碼部分,即從401000至40106A地址處的內容:

右鍵選中的內容》Copy》To file,保存爲本地文件ski12.txt:

將該文件內容去掉多餘的部分,保存中間的ASCII字節部分,在每個字節前面加上0x前綴,各個字節之間以逗號分隔,這就是用匯編語言編寫的代碼注入彈框payload:

編寫代碼注入程序

CodeInject_Assemble.cpp

// CodeInject_Assemble.cpp

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

//不像之前那樣包含字符串成員,因爲注入代碼中包含所需字符串數據
typedef struct _THREAD_PARAM {
    FARPROC pFunc[2];               // LoadLibraryA(), GetProcAddress()
} THREAD_PARAM, *PTHREAD_PARAM;

//彙編語言編寫的彈框payload
BYTE g_InjectionCode[] = {
        0x55, 0x8B, 0xEC, 0x8B, 0x75, 0x08, 0x68, 0x6C, 0x6C, 0x00, 0x00, 0x68, 0x33, 0x32, 0x2E, 0x64, 
	0x68, 0x75, 0x73, 0x65, 0x72, 0x54, 0xFF, 0x16, 0x68, 0x6F, 0x78, 0x41, 0x00, 0x68, 0x61, 0x67, 
	0x65, 0x42, 0x68, 0x4D, 0x65, 0x73, 0x73, 0x54, 0x50, 0xFF, 0x56, 0x04, 0x6A, 0x00, 0xE8, 0x0B, 
	0x00, 0x00, 0x00, 0x53, 0x4B, 0x49, 0x31, 0x32, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0xE8, 0x1E, 
	0x00, 0x00, 0x00, 0x41, 0x73, 0x73, 0x65, 0x6D, 0x62, 0x6C, 0x65, 0x20, 0x43, 0x6F, 0x64, 0x65, 
	0x20, 0x49, 0x6E, 0x6A, 0x65, 0x63, 0x74, 0x20, 0x42, 0x79, 0x20, 0x53, 0x4B, 0x49, 0x31, 0x32, 
	0x00, 0x6A, 0x00, 0xFF, 0xD0, 0x33, 0xC0, 0x8B, 0xE5, 0x5D, 0xC3
};

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

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

    if( !LookupPrivilegeValue(NULL,           // lookup privilege on local system
                              lpszPrivilege,  // privilege to lookup 
                              &luid) )        // receives LUID of privilege
    {
        printf("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) )
    {
        printf("AdjustTokenPrivileges error: %u\n", GetLastError() ); 
        return FALSE; 
    } 

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

    return TRUE;
}

BOOL InjectCode(DWORD dwPID){
    HMODULE         hMod            = NULL;
    THREAD_PARAM    param           = {0,};
    HANDLE          hProcess        = NULL;
    HANDLE          hThread         = NULL;
    LPVOID          pRemoteBuf[2]   = {0,};

    hMod = GetModuleHandleA("kernel32.dll");

    // set THREAD_PARAM
    param.pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");
    param.pFunc[1] = GetProcAddress(hMod, "GetProcAddress");

    // Open Process
    if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS,               // dwDesiredAccess
                                  FALSE,                            // bInheritHandle
                                  dwPID)) )                         // dwProcessId
    {
        printf("OpenProcess() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    // Allocation for THREAD_PARAM
    if( !(pRemoteBuf[0] = VirtualAllocEx(hProcess,                  // hProcess
                                         NULL,                      // lpAddress
                                         sizeof(THREAD_PARAM),      // dwSize
                                         MEM_COMMIT,                // flAllocationType
                                         PAGE_READWRITE)) )         // flProtect
    {
        printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    if( !WriteProcessMemory(hProcess,                               // hProcess
                            pRemoteBuf[0],                          // lpBaseAddress
                            (LPVOID)&param,                         // lpBuffer
                            sizeof(THREAD_PARAM),                   // nSize
                            NULL) )                                 // [out] lpNumberOfBytesWritten
    {
        printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    // Allocation for ThreadProc()
    if( !(pRemoteBuf[1] = VirtualAllocEx(hProcess,                  // hProcess
                                         NULL,                      // lpAddress
                                         sizeof(g_InjectionCode),   // dwSize
                                         MEM_COMMIT,                // flAllocationType
                                         PAGE_EXECUTE_READWRITE)) ) // flProtect
    {
        printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }
	//g_InjectionCode替換了之前的ThreadProc()
    if( !WriteProcessMemory(hProcess,                               // hProcess
                            pRemoteBuf[1],                          // lpBaseAddress
                            (LPVOID)&g_InjectionCode,               // lpBuffer
                            sizeof(g_InjectionCode),                // nSize
                            NULL) )                                 // [out] lpNumberOfBytesWritten
    {
        printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    if( !(hThread = CreateRemoteThread(hProcess,                    // hProcess
                                       NULL,                        // lpThreadAttributes
                                       0,                           // dwStackSize
                                       (LPTHREAD_START_ROUTINE)pRemoteBuf[1],
                                       pRemoteBuf[0],               // lpParameter
                                       0,                           // dwCreationFlags
                                       NULL)) )                     // lpThreadId
    {
        printf("CreateRemoteThread() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    WaitForSingleObject(hThread, INFINITE);	

    CloseHandle(hThread);
    CloseHandle(hProcess);

    return TRUE;
}

int main(int argc, char *argv[]){
    DWORD dwPID     = 0;

	if( argc != 2 ){
	    printf("\n USAGE  : %s <pid>\n", argv[0]);
		return 1;
	}

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

    // code injection
    dwPID = (DWORD)atol(argv[1]);
    InjectCode(dwPID);

	return 0;
}

隨意注入的cmd進程:

跟蹤調試分析

調試分析,使用OllyDbg打開notepad.exe運行,設置生成新進程時進行斷點,查看進程PID並進行代碼注入,可看到調試器暫停在線程代碼起始點,先是生成棧幀,[EBP+8]是傳入函數的第一個參數,這裏指THREAD_PARAM結構體指針,執行完該MOV指令後ESI存儲的地址是CodeInject_Assemble.exe進程爲THREAD_PARAM結構體在notepad.exe進程內存空間中分配的內存緩衝區地址;可以看到該地址存儲着2個4字節的值,即LoadLibraryA()和GetProcAddress()的起始地址:

接着將“user32.dll”字符串以ASCII碼逆序壓入棧中,PUSH ESP中爲該字符串的起始地址,最後CALL指令調用LoadLibraryA(‘user32.dll’),函數的返回地址保存在EAX中:

使用ALT+E查看加載到進程內存中的所有DLL:

同樣,將“MessageBoxA”字符串壓入棧作爲GetProcAddress()函數的第二個參數值,接着PUSH EAX指令將上述的user32.dll返回地址壓入棧作爲GetProcAddress()函數的第一個參數hMod的值,再CALL指令調用GetProcAddress(hMod, ‘MessageBoxA’):

接着是依次壓入MessageBoxA()函數的第四個參數uType值0、第三個參數lpCaption值“SKI12”、第二個參數lpText值“Assemble Code Inject By SKI12”和第一個參數hWnd值0,最後調用MessageBoxA():

使用CALL指令將包含在代碼間的字符串數據地址壓入棧:

僅適用於使用匯編語言編寫的程序。簡單地說,CALL相當於PUSH+JMP。執行1A002E地址處的CALL指令後,函數(1A003E)終止並將返回地址(1A0033)壓入棧中,再JMP到相應的函數地址(1A003E)。這裏1A003E實際並不是函數,不具有RETN指令返回的形態。此處的CALL指令只是用來將緊接其後的“SKI12”字符串地址壓入棧,然後轉到下一條代碼指令。

執行完call指令後便彈框顯示注入內容。

之後便是設置ThreadProc()函數的返回值爲0(使用XOR EAX,EAX指令比使用MOV EAX,0指令更快捷),再刪除棧幀及函數返回:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章