扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
在一个处于“可报警等待状态(Alertable Wait State)”的用户线程中使用APC,必须立即执行该函数。处于“可报警等待状态”的线程可能是由于调用了 SleepEx, WaitForSingleObjectEx, SignalObjectAndWait和MsgWaitForMultipleObjectsEx等函数把Alertableflag设置为TRUE。这种方法需要的API调用数目是最少的,而且相对而言比较可靠。
我们将要使用的所有函数都是由NOOSKRNL导出的。第一步要做的就是手动取得NTOSKRNL的基地址,为了完成这一步,我们使用被称为“mid-delta”的技术:先取得一个指向NTOSKRNL地址空间的指针,然后一直递减直到指针指向可执行文件标志“MZ”为止。要想得到一个指向NTOSKRNL地址空间的指针,我们可以先取得中断描述符表(IDT)的第一项入口地址,因为通常情况下这个地址是指向NTOSKRNL地址空间中的某一位置。
接下来的代码是访问在IDT中取得一个内存指针,然后通过递减该指针来寻找基地址。
mov esi, dword ptr ds:[0ffdff038h] ; 取得IDT地址
lodsd
cdq
lodsd ; get pointer into NTOSKRNL
@base_loop:
dec eax
cmp dword ptr [eax], 00905a4dh ; 检测“MZ”标志
jnz @base_loop
取得IDT基地址的一般方法是使用SIDT指令。由于IDT也是由0xFFDFF038地址的指针所指向的,我可以直接访问IDT地址,这样也可以减少一些字节数。也许你会注意到上面的代码并没有得到正确的IDT入口地址,我们只是取得入口地址的高字部分,这是因为低字部分的区域范围是在0—0xFFFF,忽略后仍旧在NTOSKRNL的内存地址空间里。
hash_table:
dw 063dfh; "PsLookupProcessByProcessId" _pslookupprocessbyprocessid equ [ebx]
dw 0df10h; "KeDelayExecutionThread" _kedelayexecutionthread equ [ebx+4]
dw 0f807h; "ExAllocatePool" _exallocatepool equ [ebx+8]
dw 057d2h; "ZwYieldExecution" _keyieldexecution equ [ebx+12]
dw 07b23h; "KeInitializeApc" _keinitializeapc equ [ebx+16]
dw 09dd1h; "KeInsertQueueApc" _keinsertqueueapc equ [ebx+20]
hash_table_end:
接下来我们可以建立一张哈希表,每一个所需要的函数都在其中有一个字长的哈希表项。函数名字符串在Win32 Shell Code中往往会占据大量的空间,所以使用散列机制更加合理。每个函数的指针都存放在一个表项中,而且可以由Shell Code通过EBX寄存器来访问。
接下来就执行标准的“GetProcAddress”,它会分析NTOSKRNL的导出表并且取得对应函数的入口地址。这里的哈希表有点特别,只是对导出函数名的每一字节进行XOR/ROR运算。我使用字长哈希表而不是双字长哈希表就是为尽量缩减Shell Code的长度。
一旦取得所有将要使用的函数的入口地址,接下来的的任务就是分配一个新的内存块用于存储shell code。因为代码还驻留在堆栈上,必须把代码拷贝到新的内存块。否则接下来的内核函数会覆盖掉大块区域,尤其是当我们请求降低IRQL (Interrupt Request Level)时。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者