映射dll到r0的高维注入
背景
之前写过一篇把r3代码放r0并且跑起来的例子,当时win10会出现1a错误,原因是我没关KPTI,导致页面异常,最近又看到了类似的注入,于是写了点代码映射dll在r0,并注入到目标进程,代码:GitHub。
相关知识介绍
首先呢,大家都知道不同进程之间的虚拟地址是分开的,你改你的地址数据不会影响另外的进程。但是对于系统dll而已,不会经常修改,所以一般情况下只会映射一份,系统的ntdll和kernel32这类的dll的物理内存都是同一份。顺便说一下什么是COW,CORY-ON-WRITE写时拷贝,当用户尝试对ntdll或者kernel32的函数进行修改的时候,会拷贝到另外一块物理内存进行修改,这里分别附加到不同进程,观察他们的物理地址。不同进程之间的Kernel32!TlsGetValue对应的pte是相同的一份,但是没有写权限只有读和执行,如果修改会重新映射一个pte。
好了了解完这个之后,我们怎么突破这个限制了,让每一个进程的TlsGetValue都被修改。
1、自己把TlsGetValue的物理页挂到其他地址去,并且修改它的页属性
2、使用windows的mdl函数
这里选择第二种,因为自己挂比较麻烦,而且页不安全,稍微写成就容易蓝,mdl会把对应的pte映射到内核的虚拟地址,然后就可以进行修改,强调一下mdl需要try起来。
NTSTATUS MapSC(PUCHAR shellcode, PVOID address, ULONG len)
{
BOOLEAN isLock = FALSE;
NTSTATUS status = STATUS_UNSUCCESSFUL;
PMDL mdl = IoAllocateMdl(address, PAGE_SIZE, FALSE, FALSE, NULL);
__try
{
MmProbeAndLockPages(mdl, UserMode, IoReadAccess);
isLock = TRUE;
PVOID virtualAddress = MmMapLockedPagesSpecifyCache(mdl, KernelMode, MmCached, NULL, FALSE, NormalPagePriority);
if (virtualAddress)
{
memcpy(virtualAddress, shellcode, len);
status = STATUS_SUCCESS;
MmUnmapLockedPages(virtualAddress, mdl);
if (isLock)
{
MmUnlockPages(mdl);
}
IoFreeMdl(mdl);
mdl = NULL;
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
if (isLock)
{
MmUnlockPages(mdl);
}
IoFreeMdl(mdl);
mdl = NULL;
}
return status;
}
接着现在能够修改所有进程的TlsGetValue,然后让他跳到我们内核申请的地址
UCHAR checkPid[] =
{
0x65, 0x48, 0x8B, 0x04, 0x25, 0x30, 0x00, 0x00, 0x00, // mov rax, gs:[0x30]
0x8B, 0x40, 0x40, // mov eax,[rax+0x40] ; pid
0x3D, 0x00, 0x00, 0x00, 0x00, // cmp eax, TargetPid
0x0F, 0x85, 0x00, 0x00, 0x00, 0x00, // jne 0xAABBCC
0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rax, KernelMemory
0xFF, 0xE0 // jmp rax
};
但是正常人都知道用户层无法执行内核代码,其实也很简单,只需要 给对应的页面加上用户层可访问就行了,这里或上4就是对user置位,最新代码简单处理了2M和1GB的大页。
VOID Modify2UserMem()
{
if (g_shellCode && g_allocateSize)
{
PULONG64 PteBase = (PULONG64)GetPTEBase();
if (PteBase)
{
for (ULONG i = 0; i < g_allocateSize / PAGE_SIZE; i++)
{
PULONG64 Pte = (PULONG64)MiGetXXXAddress((ULONG64)g_shellCode + (ULONG64)i * PAGE_SIZE, PteBase);
PULONG64 Pde = (PULONG64)MiGetXXXAddress((ULONG64)Pte, PteBase);
PULONG64 Ppe = (PULONG64)MiGetXXXAddress((ULONG64)Pde, PteBase);
PULONG64 Pxe = (PULONG64)MiGetXXXAddress((ULONG64)Ppe, PteBase);
if (MmIsAddressValid(Pte) && MmIsAddressValid(Pde) && MmIsAddressValid(Ppe) && MmIsAddressValid(Pxe))
{
*Pte |= 4;
*Pde |= 4;
*Ppe |= 4;
*Pxe |= 4;
}
}
}
}
}
好了,代码写好了测试一下,发现1a蓝屏,其实是因为KPTI,简单说一下怎么判断是否开启KPTI
cr4的第17位表示是否支持pci,当KPTI开启时,这个位会被置位。
关闭方式,管理员
reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management" /v FeatureSettingsOverride /t REG_DWORD /d 3 /f
reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management" /v FeatureSettingsOverrideMask /t REG_DWORD /d 3 /f
下面是没有开启的,cr4第17位为0,并且用户层地址的pxe ppe pde pte都是有执行权限的
开启了的,pxe没有执行权限就不会继续往下了,并且cr4的第17位是1
最后,映射dll到内存,修改页属性,指向oep,最后修复cow hook就完成了。
当然这里映射还有一个坑
正常的修复IAT是需要dll的导出函数,但是像kernel32这种是有坑的,这个前面带a的,不是自己实现,指向的是字符串
if ((pImageThunkData->u1.Ordinal & IMAGE_ORDINAL_FLAG64) == 0)
{
PIMAGE_IMPORT_BY_NAME pImageImportName = (PIMAGE_IMPORT_BY_NAME)(pImageThunkData->u1.AddressOfData + (PUCHAR)g_shellCode);
PVOID func = GetModuleExport(pModuleBase, pImageImportName->Name);
if (func && func < pModuleBase)
{
if (memcmp((PUCHAR)pModuleBase + (ULONG64)func, "NTDLL", strlen("NTDLL")) == 0)//NTDLL.RtlEncodePointer
{
UNICODE_STRING Kernel32String = RTL_CONSTANT_STRING(L"Ntdll.dll");
PVOID NtdllAddress = GetUserModule(EProcess, &Kernel32String, FALSE);
func = GetModuleExport(NtdllAddress, (PCCHAR)pModuleBase + (ULONG64)func + strlen("NTDLL."));
}
}
if (func)
pImageThunkData->u1.Function = (ULONG64)func;
}
pImageThunkData++;
还有编译的时候选择mt不要整md,md编译出来的IAT长这样,拿什么去给你修复这个dll的IAT
这里dll的起始不是oep而是第一个导出函数。
在编译的时候全选release,不要整debug,如果你选debug导致进程崩溃或者蓝屏的问题需要你自己去修复bug了,代码只是一个Demo,出问题自己改。
相关文章
- Spring IOC - 控制反转(依赖注入) - 创建对象的方式
- Spring 依赖注入(注解方式)
- 【UVM新型IP核验证法】通过GA遗传算法优化UVM测试激励,使得优化的激励注入平台接近达到100%测试覆盖率
- 渗透测试-SQL注入之Fuzz绕过WAF
- 如何在Java Filter 中注入 Service
- mysql基础语法及拓展到web中的sql注入
- Spring quartz Job不能依赖注入,Spring整合quartz Job任务不能注入
- sqlmap跑宽字节注入(渗透测试之SQL注入之宽字节注入)
- PHP通过反射实现自动注入参数(依赖注入)
- 联想:模块化智能手机将为物联网行业注入新动力
- 整合Mybatis-Plus高级,Oracle 主键Sequence,Sql 注入器实现自定义全局操作
- DI,依赖注入,给对象赋值 ,get,set