[2021]PatchGuard的 sub_1403DA6F0 回调研究(二) huoji PG,逆向,patchguard 2021-08-01 1265 次浏览 0 次点赞 [上一文章](https://key08.com/index.php/2021/06/28/1190.html "上一文章") 中,我们介绍了PG的回调,本篇文章将会大概介绍一下这个回调所调用的sub_1403DA6F0的一些内容: ### 入口: 首先是一堆关于APC的检查: 当前线程 =APC_LEVEL 而且APC是关闭状态会蓝屏 当前线程 >APC_LEVEL 也会蓝屏 ![](https://key08.com/usr/uploads/2021/08/1627876145.png) ### 申请PG Context内存、解密加密: 这里要确保这个上下文是不是在之前已经申请的内存里面 ![](https://key08.com/usr/uploads/2021/08/2545061314.png) 加密解密: ![](https://key08.com/usr/uploads/2021/08/908532540.png) 在末尾会释放掉它: ![](https://key08.com/usr/uploads/2021/08/1294435581.png) 加密解密的逻辑如下: ```C __int64 __fastcall EncryptDecryptEntries(PPgCtx PgCtx) { int SomeFlags; // er11 __int64 i; // r10 int ChecksStatusFlags; // er9 __int64 OffsetPgEntries; // rcx __int64 LoopInitValue_Xor; // rbp unsigned __int64 SizeIterations; // rdi char *pData; // rbx unsigned __int64 pDataEnd; // r14 __int64 XoredRAXRDX_1; // r11 __int64 i2; // rsi __int64 v13; // rdx _DWORD *pFaultRegion; // rax int v15; // ecx unsigned __int64 TimestampCtr; // rax __int64 XoredRAXRDX; // r11 __int64 a1[2]; SomeFlags = PgCtx->SomeFlags; if ( !_bittest(&SomeFlags, 30u) ) { i = 0i64; while ( 1 ) { ChecksStatusFlags = PgCtx->ChecksStatusFlags; a1[1] = (a1[0] & 0xffffffff) != 0; if ( a1 == ((PgCtx->ChecksStatusFlags >> 21) & 1) || !(SomeFlags & 2) ) break; OffsetPgEntries = PgCtx->OffsetStartPgEntries; LoopInitValue_Xor = PgCtx->LoopInitValue; SizeIterations = (PgCtx->SizeofAllPgEntries - OffsetPgEntries) >> 3; pData = &PgCtx->CmpAppendDllSection[OffsetPgEntries]; pDataEnd = PgCtx + 8 * SizeIterations + OffsetPgEntries; if ( a1[0] & 0xffffffff ) { TimestampCtr = __rdtsc(); a1 = (__ROR8__(TimestampCtr, 3) ^ TimestampCtr) * 0x7010008004002001ui64; XoredRAXRDX = a1[0] ^ a1[1]; PgCtx->XoredRAXRDX = XoredRAXRDX; if ( pData > pDataEnd ) SizeIterations = 0i64; if ( SizeIterations ) { do { ++i; a1[0] = XoredRAXRDX ^ *pData; a1[1] = LoopInitValue_Xor ^ *pData; *pData = a1; XoredRAXRDX = (a[1] + __ROR8__(XoredRAXRDX, XoredRAXRDX & 0x3F)) ^ 0xEFFi64; pData += 8; } while ( i != SizeIterations ); ChecksStatusFlags = PgCtx->ChecksStatusFlags; } PgCtx->XoredRAXRDX2 = XoredRAXRDX; PgCtx->ChecksStatusFlags = ChecksStatusFlags | 0x200000; return a1; } XoredRAXRDX_1 = PgCtx->XoredRAXRDX; i2 = 0i64; if ( pData > pDataEnd ) SizeIterations = 0i64; if ( SizeIterations ) { do { *pData ^= XoredRAXRDX_1; ++i2; v13 = *pData; pData += 8; XoredRAXRDX_1 = ((LoopInitValue_Xor ^ v13) + __ROR8__(XoredRAXRDX_1, XoredRAXRDX_1 & 0x3F)) ^ 0xEFF; } while ( i2 != SizeIterations ); ChecksStatusFlags = PgCtx->ChecksStatusFlags; } PgCtx->ChecksStatusFlags = ChecksStatusFlags & 0xFFDFFFFF; if ( XoredRAXRDX_1 != PgCtx->XoredRAXRDX2 ) { pPgFault = PgCtx->pPgFault; v15 = PgCtx->SizeofAllPgEntries; pPgFault->Ptr1 = PgCtx; pPgFault->Id1 = v15; if (!PgCtx->SomethingWentWrong;) { PgCtx->pPgFault->XoredChecksum = XoredRAXRDX_1 ^ PgCtx->XoredRAXRDX2; if (!PgCtx->SomethingWentWrong;) { PgCtx->EncodedPointerPgCtxEntry = 0i64; PgCtx->PgEntryType = 0x100i64; PgCtx->EncodedPointerPgCtxLocal = &PgCtx + 0A3A03F5891C8B4E8h; a1[0] = 0; PgCtx->PgEntryData = XoredRAXRDX_1; PgCtx->SomethingWentWrong = 1; SomeFlags = PgCtx->SomeFlags; if ( !_bittest(&SomeFlags, 0x1Eu) ) continue; } } } return a1; } } return a1; } ``` ### Kisystemcall64检测: kisystemcall64是syscall的转向的点,pg会写入一个有效的特定的值去判断kisystemcall64然后syscall一次检查kisystemcall64的地址是否正确 ![](https://key08.com/usr/uploads/2021/08/2006580911.png) 要避免被发现,hypervisor在被writemsr的时候应该老老实实的写入(不用担心漏调用的问题,因为过一会PG就恢复了) ### ByePg检测: Bypg是can1337的一个作品通过修改KebugEx2里面的HalTimerWatchdogStop函数指针恢复系统从而绕过PG 在pgcontext里面存在两处HalTimerWatchdogStop、一处是函数地址,另外一处是这个指针地址,这个地址被单独拿出来检查: ![](https://key08.com/usr/uploads/2021/08/1779404718.png) ### 隐藏进程检测: 一些通过ActiveProcessLinks断链(也叫做eprocess断链)的进程,pg是这样寻找蛛丝马迹进行蓝屏的: 首先pg遍历ActiveProcessLinks,给这些进程的eprocess->pcb.visted设置为一个随机数(默认是0) 然后遍历句柄表、线程表等看这些进程的pcb.Visited是否为0,为0就是异常进程了. pcb.Visited(只是PG专用): ![](https://key08.com/usr/uploads/2021/08/1647913501.png) 在初始化的时候,生成随机数: ![](https://key08.com/usr/uploads/2021/08/4261694552.png) 检测时候 随机数确定: ![](https://key08.com/usr/uploads/2021/08/418108843.png) 如果这个随机数跟eprocess->pcb.visited里面的不同,说明被隐藏了,隐藏进程即便是简单的断链,其他的句柄表、线程表什么的都得有,PG会遍历这些表去发些隐藏进程 ActiveProcessLinks: ![](https://key08.com/usr/uploads/2021/08/1387213423.png) KiProcessListHead: ![](https://key08.com/usr/uploads/2021/08/913866270.png) PspCidTable: ![](https://key08.com/usr/uploads/2021/08/4194991533.png) HandleTable: ![](https://key08.com/usr/uploads/2021/08/2818628239.png) ### 驱动断链检测: ![](https://key08.com/usr/uploads/2021/08/2857926005.png) 申请了一个Pool内存,标签是LoadedModuleTag(叫做'rwPD')的内存,大小是 TableSize * 8, v320不用管恒定为0 ![](https://key08.com/usr/uploads/2021/08/2977725583.png) 为了防止破解,生成随机数填充: ![](https://key08.com/usr/uploads/2021/08/2198475579.png) 得到dllbase在ldr中 ![](https://key08.com/usr/uploads/2021/08/3488087265.png) 把dllbase插入到表里面: ![](https://key08.com/usr/uploads/2021/08/790794003.png) 之后遍历整个表、进行比大小的操作 ![](https://key08.com/usr/uploads/2021/08/2244192005.png) 如果这些dllbase跟一个InvertedTableEntries(个人猜测是驱动加载的时候就记录了的表)不匹配,说明有驱动断链.从而蓝屏 ### 更多: 已经搞了7个多小时的逆向工程但是还没结束,太多检查的地方了,而且这个地方太大了,改个常量都很慢,特别是有些值是复用的.所以请等待下一篇.... 文章可能有错误,欢迎评论区指正 本文由 huoji 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。 点赞 0
还不快抢沙发