CreateRemoteThread 直接注入代码执行一文拾遗

    Three Ways to Inject Your Code into Another Process 一文中提到成功注入进程后,子类化窗口过程的一些技巧。作者分配了一块堆内存,然后将线程参数和线程函数前后紧挨着拷贝到这片堆内存中,最后CreateRemoteThread运行线程函数。作者的这种方式要获得线程参数只能通过远程线程自定位的方式。我这么懒惰可能直接把需要的参数当做CreateRemoteThread的参数一起传递给远程线程了,当然,虽然没验证过,但是应该可行。

    作者的文章被转载这么多次,他提到的技巧不尝试一下有点暴殄天物的感觉,于是就有了这篇文章。

    本文依然一步步走,一下子写出正确的代码感觉少了点挫败感,同时也比较符合人类进步的规律;再说这同时记录了自己犯下的错误。

    注意,工程设置中需要去掉增量连接和 /Gz选项

1)先上直观的代码,当然是运行出错的!

列表1:

#include <stdafx.h>
#include <windows.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
typedef HANDLE (*open)(LPCTSTR,DWORD,DWORD,LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE);
typedef BOOL (*read)(HANDLE,LPVOID,DWORD,LPDWORD,LPOVERLAPPED);
typedef BOOL (*write)(HANDLE,LPVOID,DWORD,LPDWORD,LPOVERLAPPED);
typedef DWORD (*func)(void*);
#define ERRERS 1
#define PATHLEN 64
#define PROLOGUE 0x23
#define FUNCOFFSET 0x44
#define STRUCTLEN sizeof(INJCODE)
#define OPENPROCESSPROITY PROCESS_CREATE_THREAD|PROCESS_QUERY_INFORMATION|PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ
struct INJCODE
{
	char filePath[PATHLEN];
	func func1; //writeFunc
};

void MinorFunc()
{
	__asm
	{
		int 0x03;
	}
}

void AfterMinorFunc()
{}

DWORD FuncV1(void* args)
{
	INJCODE* injCode;
	injCode = (INJCODE*)MinorFunc;
	injCode--;
        (injCode->func1)();
        return 0;
}

DWORD AfterMajorFunc(void* args)
{
	return 0;
}

INJCODE* injCode;

int main()
{
	printf("FuncV1:%p\n",FuncV1);
	DWORD pid,dwThreadId;
	DWORD writtenNum,readNum;
	INJCODE* injCode;
	FILE* fp = fopen("c:\\pid.txt","r+");
	assert(fp);
	fscanf(fp,"%d",&pid);
	fclose(fp);
	HANDLE remoteProgHd = OpenProcess(OPENPROCESSPROITY,FALSE,pid);

	DWORD funcLen = ((DWORD)AfterMajorFunc-(DWORD)FuncV1) + ((DWORD)AfterMinorFunc-(DWORD)MinorFunc);
	injCode = (INJCODE*)VirtualAllocEx(remoteProgHd,NULL,sizeof(INJCODE)+funcLen,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
        //本意是想给injCode->filePath赋值的结果:
        //读injCode变量,引起异常的访问方式
	strcpy(injCode->filePath,"abc");
        //同样会引起异常  
<pre name="code" class="cpp">        injCode->func1=FuncV1;
return 0;}

 

代码初看没问题吧,调试运行,程序运行到

strcpy(injCode->filePath,"abc");

必然出错,看windbg输出,C0000005,同时dt injCode分配的内存也有错误的提示:


排查了很久,才找到根结所在:

injCode = (INJCODE*)VirtualAllocEx(remoteProgHd,NULL,sizeof(INJCODE)+funcLen,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
VirtualAllocEx成功返回,injCode指向的内存区(0x00000ac0)在目标进程中,同样的地址在本进程中可能未分配或者没有相应访问权限,如果访问会导致出错。在这段代码中,程序企图读本进程0ac0处的内存值,从windbg输出来看这片内存应该还未分配,因此出现异常。

记得injCode指向的是远程线程的内存区!因此对任何由VirtualAllocEx(RemoteHandle);分配的内存进行的读写操作只能借助Read/WriteProcessMemory来访问远程结构体变量中的各个成员

姑且称用VirtualAllocEx创建远程进程变量吧,便于理解~

2)上更正main函数版的,用正确的方法给injCode结构各变量赋值:

列表2)

#include <stdafx.h>
#include <windows.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
typedef HANDLE (*open)(LPCTSTR,DWORD,DWORD,LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE);
typedef BOOL (*read)(HANDLE,LPVOID,DWORD,LPDWORD,LPOVERLAPPED);
typedef BOOL (*write)(HANDLE,LPVOID,DWORD,LPDWORD,LPOVERLAPPED);
typedef DWORD (*func)(void*);
#define ERRERS 1
#define PATHLEN 64
#define PROLOGUE 0x23
#define FUNCOFFSET 0x44
#define STRUCTLEN sizeof(INJCODE)
#define OPENPROCESSPROITY PROCESS_CREATE_THREAD|PROCESS_QUERY_INFORMATION|PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ
struct INJCODE
{
	char filePath[PATHLEN];//+0x00
	func func1;//+0x40
	func func2;//+0x44
};

void MinorFunc()
{
	__asm
	{
		int 0x03;
	}
}

void AfterMinorFunc()
{}

DWORD MajorFunc(void* args)
{
	INJCODE* injCode;
	injCode = (INJCODE*)MajorFunc;
	injCode--;
	(injCode->func2)(NULL);
	return 0;
}

#if 0
DWORD FuncV2(void* args)
{
	INJCODE* injCode;
	__asm
	{
		call CurEip;
CurEip:
		pop eax;
		//指向writeFunc的起始
		sub eax,PROLOGUE;
		//指向堆分配的INJCODE起始
		sub eax,FUNCOFFSET;
		mov injCode,eax;
	}
	return 0;
}
#endif

DWORD AfterMajorFunc(void* args)
{
	return 0;
}

INJCODE* injCode;

int main()
{
	DWORD pid,dwThreadId;
	DWORD writtenNum,readNum;
	INJCODE* injCode;
	FILE* fp = fopen("c:\\pid.txt","r+");
	assert(fp);
	fscanf(fp,"%d",&pid);
	fclose(fp);
	HANDLE remoteProgHd = OpenProcess(OPENPROCESSPROITY,FALSE,pid);
	//主函数和被调用函数的长度,首先存放MajorFunc,后面紧跟着MinorFunc
	DWORD funcLen = ((DWORD)AfterMajorFunc-(DWORD)MajorFunc) + ((DWORD)AfterMinorFunc-(DWORD)MinorFunc);
	injCode = (INJCODE*)VirtualAllocEx(remoteProgHd,NULL,sizeof(INJCODE)+funcLen,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
	//INJCODE!+0x40 func1所在,存放主函数指针 
	DWORD Func1Addr = ((DWORD)((char*)injCode)+0x40);
	INJCODE!+0x44 func2所在,存放被调函数指针 
	DWORD Func2Addr = ((DWORD)((char*)injCode)+0x44);
	DWORD MajorFuncBase = ((DWORD)((char*)injCode)+sizeof(INJCODE));
	DWORD MinorFuncBase = ((DWORD)((char*)injCode)+sizeof(INJCODE)+((DWORD)AfterMajorFunc-(DWORD)MajorFunc));
	WriteProcessMemory(remoteProgHd,((char*)injCode)+0x00,"c:\\1.txt",strlen("c:\\1.txt"),&writtenNum);
	//injCode!func1=MajorFunc
	WriteProcessMemory(remoteProgHd,(char*)Func1Addr,&MajorFuncBase,sizeof(DWORD),&writtenNum);
	//injCode!func2=MinorFunc
	WriteProcessMemory(remoteProgHd,(char*)Func2Addr,&MinorFuncBase,sizeof(DWORD),&writtenNum);
	//复制函数到远程进程
	WriteProcessMemory(remoteProgHd,(char*)MajorFuncBase,MajorFunc,((DWORD)AfterMajorFunc-(DWORD)MajorFunc),&writtenNum);
	WriteProcessMemory(remoteProgHd,(char*)MinorFuncBase,MinorFunc,((DWORD)AfterMinorFunc-(DWORD)MinorFunc),&writtenNum);
	HANDLE remoteThread = CreateRemoteThread(remoteProgHd,NULL,0,(LPTHREAD_START_ROUTINE)((char*)MajorFuncBase),NULL,0,&dwThreadId);
	WaitForSingleObject(remoteThread, INFINITE);
	return 0;	
}

编译运行,injCode变量中各个成员的值已经成功更新到目标进程了:


可以看到b50000 +40 +44处分别是两个函数的地址值,再看下这两个地址上的函数:


可是运行CreateRemoteThread后,被注入的进程运行到call ecx后还是出错,why?

回到代码MajorFunc中:

<pre name="code" class="cpp">	INJCODE* injCode;
	injCode = (INJCODE*)MajorFunc;
	injCode--;

 

对应的Opcode为:

列表3)

mov     dword ptr [ebp-4],401030h
mov     eax,dword ptr [ebp-4]
MajorFunc被翻译为401030,绝对地址,更重要的,401030是在注入进程的地址空间中,目标进程的地址空间中未必有,因此会导致访问出错。代码运行在其他进程空间中,而且地址不确定,怎么才能获得injCode的地址?这就用到线程自定位了,远程线程获得当前指令的内存地址,减去偏移量,最终定位到injCode的起始地址。因为线程运行的函数位于injCode分配的内存空间中,且紧挨着injCode变量,修改修改偏移找个起始地址还不简单。

列表4) 终结版:

// redirect.cpp : Defines the entry point for the console application.
//
#include <stdafx.h>
#include <windows.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
typedef HANDLE (*open)(LPCTSTR,DWORD,DWORD,LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE);
typedef BOOL (*read)(HANDLE,LPVOID,DWORD,LPDWORD,LPOVERLAPPED);
typedef BOOL (*write)(HANDLE,LPVOID,DWORD,LPDWORD,LPOVERLAPPED);
typedef DWORD (*func)(void*);
#define ERRERS 1
#define PATHLEN 64
#define PROLOGUE 0x0e
#define FUNCOFFSET 0x48
#define STRUCTLEN sizeof(INJCODE)
#define OPENPROCESSPROITY PROCESS_CREATE_THREAD|PROCESS_QUERY_INFORMATION|PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ
struct INJCODE
{
	char filePath[PATHLEN];//+0x00
	func func1;//+0x40
	func func2;//+0x44
};

void MinorFunc()
{
	__asm
	{
		int 0x03;
	}
}

void AfterMinorFunc()
{}

DWORD MajorFunc(void* args)
{
	INJCODE* injCode;
	__asm
	{
		call CurEip;
CurEip:
		pop eax;
		//指向writeFunc的起始
		sub eax,PROLOGUE;
		//指向堆分配的INJCODE起始
		sub eax,FUNCOFFSET;
		mov injCode,eax;
	}
	(injCode->func2)(NULL);
	return 0;
}

DWORD AfterMajorFunc(void* args)
{
	return 0;
}

INJCODE* injCode;

int main()
{
	DWORD pid,dwThreadId;
	DWORD writtenNum,readNum;
	INJCODE* injCode;
	FILE* fp = fopen("c:\\pid.txt","r+");
	assert(fp);
	fscanf(fp,"%d",&pid);
	fclose(fp);
	HANDLE remoteProgHd = OpenProcess(OPENPROCESSPROITY,FALSE,pid);
	//主函数和被调用函数的长度,首先存放MajorFunc,后面紧跟着MinorFunc
	DWORD funcLen = ((DWORD)AfterMajorFunc-(DWORD)MajorFunc) + ((DWORD)AfterMinorFunc-(DWORD)MinorFunc);
	injCode = (INJCODE*)VirtualAllocEx(remoteProgHd,NULL,sizeof(INJCODE)+funcLen,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
	//INJCODE!+0x40 func1所在,存放主函数指针 
	DWORD Func1Addr = ((DWORD)((char*)injCode)+0x40);
	INJCODE!+0x44 func2所在,存放被调函数指针 
	DWORD Func2Addr = ((DWORD)((char*)injCode)+0x44);
	DWORD MajorFuncBase = ((DWORD)((char*)injCode)+sizeof(INJCODE));
	DWORD MinorFuncBase = ((DWORD)((char*)injCode)+sizeof(INJCODE)+((DWORD)AfterMajorFunc-(DWORD)MajorFunc));
	WriteProcessMemory(remoteProgHd,((char*)injCode)+0x00,"c:\\1.txt",strlen("c:\\1.txt"),&writtenNum);
	//injCode!func1=MajorFunc
	WriteProcessMemory(remoteProgHd,(char*)Func1Addr,&MajorFuncBase,sizeof(DWORD),&writtenNum);
	//injCode!func2=MinorFunc
	WriteProcessMemory(remoteProgHd,(char*)Func2Addr,&MinorFuncBase,sizeof(DWORD),&writtenNum);
	//复制函数到远程进程
	WriteProcessMemory(remoteProgHd,(char*)MajorFuncBase,MajorFunc,((DWORD)AfterMajorFunc-(DWORD)MajorFunc),&writtenNum);
	WriteProcessMemory(remoteProgHd,(char*)MinorFuncBase,MinorFunc,((DWORD)AfterMinorFunc-(DWORD)MinorFunc),&writtenNum);
	HANDLE remoteThread = CreateRemoteThread(remoteProgHd,NULL,0,(LPTHREAD_START_ROUTINE)((char*)MajorFuncBase),NULL,0,&dwThreadId);
	WaitForSingleObject(remoteThread, INFINITE);
	return 0;	
}
注入后运行MinorFunc结果:

跳转到0xb50078后:

顺利的运行到MinorFunc中。

至此,全文完!



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值