1. 程式人生 > >程式碼注入——彙編編寫注程式碼

程式碼注入——彙編編寫注程式碼

程式碼注入——彙編編寫注程式碼

0x00 思路

  在準備些彙編程式碼之前,我們先要理清楚整個呼叫過程的思路,以及過程中的引數傳遞過程。我的的目標是在指定的目標程式中注入一個messagebox執行緒,首先要呼叫messageBox就需要用到LoadLibrary裝入user32庫函式。我們還需要GetprocessAddress來取得messagebox的函式地址,取得了messagebox的地址,我們需要向messagebox傳遞四個引數。Messagebox的原型如下:

MessageBox(hWnd: HWND; Text, Caption: PChar; Type: Word): Integer;

四個引數中TextPChar是一定要傳遞進去的。結合上篇中利用c++編寫的注入程式碼我們可以整理出這樣一個方案:

1)使用c++編寫獲取LoadLibraryGetprocessAddress函式地址的程式。

2)使用匯編編寫獲取messagebox地址的函式,以及儲存messagebox的各個引數。

3)使用c++呼叫createRemotetThread函式開啟一個子執行緒執行彙編程式碼。

0x01 彙編部分的程式碼編寫

1)od載入一個空的exe檔案,我們將程式碼寫入開始地址位401000h的地方。如下圖:

 

這裡需要注意的是,之所以要先開闢兩個堆疊空間,是因為我們需要呼叫

LoadLibrary以及GetProcessAddress。在堆疊開闢兩個四位元組的空間就是為了存放傳過來的兩個函式的地址。

2)接下來編寫需要傳入的字串,我們將游標移至401033處。使用快捷鍵ctrl+e輸入字串ReverseCore,記得以00結尾。如下圖:

 

下一字串就放在起始地址為401044處。輸入www.reversecore.com。如下圖。

 

401058輸入返回函式,如下圖:

 

至此,彙編程式碼輸入完畢。我們接下來儲存一下檔案。

0x01 彙編指令說明

被注入的彙編指令從地址401000開始執行,執行到40102e處有一個call 40103f,進入地址

40103f,又是一個call指令,call 401058轉向地址401058401058後面的指令就是退出指令。

00401000         55                  push ebp

00401001         8BEC                mov ebp,esp

00401003         8B75 08             mov esi,dword ptr ss:[ebp+0x8]

上面這三條指令就是儲存現場,讓後為傳入的兩引數開闢堆疊空間。ebp+0——ebp+3存放著函式LoadLibrary的起始地址,ebp+4——ebp+7存放著函式GetProcAddress的函式地址。

 

00401006         68 6C6C0000     push 0x6C6C

0040100B         68 32322E64     push 0x642E3232

00401010         68 75736572     push 0x72657375

00401015         54              push esp

00401016         FF16            call dword ptr ds:[esi]

 

上面的五條指令就是先將字串user32.dll壓棧,然後使用esp指向字串,最後一句呼叫API函式LoadLibrary,其返回值存放在eax中。()

00401018         68 6F784100         push 0x41786F

0040101D         68 61676542         push 0x42656761

00401022         68 4D657373         push 0x7373654D

00401027         54                  push esp

00401028         50                  push eax

00401029         FF56 04             call dword ptr ds:[esi+0x4]

上面的六條指令先將字串MessageBox壓棧,再讓esp指向字串,eax存放著上個函式LoadLibrary的返回值,這裡作為GetProccessAddress的引數。最後一句呼叫GetProcessAddress函式取得函式MessageBox的地址。

0040102C         6A 00               push 0x0

0040102E         E8 0C000000         call asmtest1.0040103F

00401033          52                 push edx                               

00401034         65:76 65             jbe short 0040109c

00401037         72 73                jb short asmtest1.004010AC

00401039         65:43                inc ebx

0040103B         6f                   outs dx,dword ptr ds:[esi]

0040103C         72 65                jb short asmtest1.004010A3

0040103E         00E8                 add al,ch

 

0040103F         E8 14000000         call asmtest1.00401058

00401044        /77 77               ja short asmtest1.004010BD

00401046        |77 2E               ja short asmtest1.00401076

00401048        |72 65               jb short asmtest1.004010AF

0040104A        |76 65               jbe short asmtest1.004010B1

0040104C        |72 73               jb short asmtest1.004010C1

 

 

00401058         6A 00                push 0x0

0040105A         FFD0                call eax

0040105C         33C0                xor eax,eax

0040105E         8BE5                mov esp,ebp

00401060         5D                  pop ebp  

00401061         C3                  retn

上面的標藍程式碼是真正的程式碼,其餘的的實際上儲存的是字串,仔細觀察就以可以發現,我們這是我們之前存放的地址。分別從地址00401033以及00401044開始。

其實我們關心的是0040102E 0040103F 地址處的call跳轉,這兩個跳轉有啥作用?答:其實這兩個跳轉是來傳遞引數的,這兩個引數分別是00401033處的字串ReverseCore以及00401044處的字串www.reversecore.com。這兩個引數都是MessageBox的引數。那為啥用call 跳轉來傳遞引數?答:其實這樣傳遞引數具有一定的安全性,而且也不用呼叫ebp,省事。那其中的原理是什麼?答:我們簡單回想一下call指令的整個過程,先將返回地址壓棧,執行完在跳轉。其實就是push jmp指令的結合。現在我們來看看這兩個call後面的首地址,是的。其實就是要傳入的字串地址!這兩個call指令把字串的首地址當作了返回值地址來壓棧,這樣也就相當於把兩個字串引數入棧!四個引數的入棧過程即:40102c push 0,傳遞第四個引數,兩個call分別傳遞三二兩個引數,最後401058處傳遞最後一行引數。地址0040105A  call指令呼叫函式MessageBox。執行到這一步就會彈出messagebox的對話方塊。

最後幾行程式碼恢復棧幀並且返回。

0x02 c++程式碼編寫

1)先將之前儲存彙編檔案用OD開啟,轉到40100處,複製40100——401061機器碼。如下圖:

 

整理得到如下機器碼陣列:

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, 0x0C, 0x00, 0x00,

0x00, 0x52, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x43, 0x6F,

0x72, 0x65, 0x00, 0xE8, 0x14, 0x00, 0x00, 0x00, 0x77, 0x77,

0x77, 0x2E, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x63,

0x6F, 0x72, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x00, 0x6A, 0x00,

0xFF, 0xD0, 0x33, 0xC0, 0x8B, 0xE5, 0x5D, 0xC3

 

2)編寫c++程式。程式碼如下:

// CodeInjection2.cpp : 此檔案包含 "main" 函式。程式執行將在此處開始並結束。

//

 

#include "pch.h"

#include <iostream>

#include<windows.h>

#include "stdio.h"

using namespace std;

 

 

typedef struct _THREAD_PARAM

{

FARPROC pFunc[2];               // LoadLibraryA(), GetProcAddress()

} THREAD_PARAM, *PTHREAD_PARAM;

 

 

//ThreadProc()

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, 0x0C, 0x00, 0x00,

0x00, 0x52, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x43, 0x6F,

0x72, 0x65, 0x00, 0xE8, 0x14, 0x00, 0x00, 0x00, 0x77, 0x77,

0x77, 0x2E, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x63,

0x6F, 0x72, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 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 Injection(DWORD dwPID)

{

HMODULE hMod = NULL;

THREAD_PARAM param = {0,};

HANDLE hProcess = NULL;

HANDLE hThread = NULL;

LPVOID pRemoteBuf[2] = { 0, };

 

 

hMod = GetModuleHandleA("kernel32.dll");

 

 

//設定執行緒引數

param.pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");

param.pFunc[1] = GetProcAddress(hMod, "GetProcessAddress");

 

 

//開啟執行緒

 // Open Process

if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS,               // dwDesiredAccess

FALSE,                            // bInheritHandle

dwPID)))                         // dwProcessId

{

printf("OpenProcess() fail : err_code = %d\n", GetLastError());

return FALSE;

}

//LoadLibraryA函式分配空間

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;

}

//LoadLibraryA寫入記憶體

if (!WriteProcessMemory(hProcess,                               // hProcess

pRemoteBuf[0],                          // lpBaseAddress

(LPVOID)¶m,                         // lpBuffer

sizeof(THREAD_PARAM),                   // nSize

NULL))                                 // [out] lpNumberOfBytesWritten

{

printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());

return FALSE;

}

//GetProcessAddress函式分配空間

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;

}

 

//GetProcessAddress寫入記憶體

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()

{

    std::cout << "Hello World!\n";

}

 

 

編譯生成名為CodeInjection2.exeReleased檔案

0x03 驗證是否成功

步驟和一篇一樣,用管理員許可權開啟cmd,將CodeInjection2.exemessageBox.dlld盤。開啟notepad.exe。開啟processexploer檢視PIDcmd輸入d:轉自d盤,輸入CodeInjection.exe 10584 。結果如下圖:

 

顯然注入成功了。