腾讯视频/爱奇艺/优酷/外卖 充值4折起
icesword 是通过 pspcidtable 这个表来遍历进程的, pspcidtable 是一个没有被 ntoskrnl.exe 导出的。这就涉及到如何定位
pspcidtable 的问题。icesword 是通过搜索特征串的方式定位 pspcidtalbe. pspcidtable 是一个 handle_talbe 结构.
pslookupprocessbyprocessid 函数中会引用 pspcidtalbe 变量。icesword 从 pslookupprocessbyprocessid 函数的前几十个字节
内搜索 pspcidtalbe 变量。在icesword 里面是不能调试的即使是用 windbg , softice,syser 调试器下断点调试,也是断不住的。
当然了你也不能用调试器调试,因为 icesword.exe 会在一个timer 中不停的重新设置 int 1,int 3 的中断处理函数。设置成
windows ntoskrnl.exe 中的缺省处理函数。即使你用硬件断点寄存器也是不管用的。那有的人就会说既然设置成 windows ntoskrnl.exe
中的缺省处理函数就可以使用 windbg 双机调试.icesword 也做了处理,icesword 会通过 kddebuggerenabled 变量判断是否允许内核
调试。如果允许调试的话. icesword 会调用 kddisabledebugger 函数禁止内核调试。
第一部分
(写的太细了,因为怕被 rootkit 的作者利用.所以就把第一部分给去掉了.如果需要可以单独和我联系)
写第二部分
这里顺便在说两个分析 icesword 中遇到的反调试小陷阱 这里把代码片段列出来,希望作者原谅
.text:000xxxf0 mov [ebp iocontrolcode], eax
.text:000xxxf3 mov eax, [esp 5ch-6ch] ; 反调试代码
.text:000xxxf7 push eax
.text:000xxxf8 mov eax, [esp 60h-6ch]
.text:000xxxfc pop ebx
.text:000xxxfd cmp eax, ebx
.text:000xxxff jz short loc_1240b ; 如果没有被调试则会跳转
.text:000xxx01 mov eax, 200edbh
.text:000xxx06 not eax
.text:000xxx08 push eax
.text:000xxx09 pop edi
.text:000xxx0a stosd
.text:000xxxf3 mov eax, [esp 5ch 6ch] 当单步执行到这条指令或者在这条指令上设置断点的时候,因为当调试器在这条指令上弹出的时候会
用到被调试程序的堆栈来保存 eflags,cs,eip, (如果 int 1,或 int 3 处理函数用任务门就可以解决这个问题。)例如 当代码执行到这条指令时
esp = 805e4320h 执行完这条指令是 eax 的值为 [esp 5ch-6ch]=[esp-10h]=[805e4320h-10h]=[805e4310h] 的值。
当单步执行到 .text:000xxxf8 mov eax, [esp 60h-6ch] 指令的时候 esp=805e432ch 以为其中入栈了一个 eax 所以 esp=805e432ch,
执行完 .text:000xxxf8 mov eax, [esp 60h-6ch] 条指令的时候 eax = [esp 60h-6ch]=[esp-ch]=[805e432ch-ch]=[805e4310h]
如果不调试的情况下 读的是同一个地址的值,所以两个值比较应该是相同的 也就是 .text:000xxxfd cmp eax, ebx 这条指令的比较结果
应该是相同的。这个指令 .text:000xxxff jz short loc_1240b 执行后直接跳转到。
如果是被调试器调试的情况下 .text:000xxxff jz short loc_1240b 不会跳转。 如果不跳转时下面的代码 会覆盖掉系统的当前 ethread
指针。接下来在调用很多系统函数都会导致系统崩溃,并且是崩溃到系统模块里面,这样给你定位错误带来误导。哈哈
.text:000xxx68 push 1 ; alignment
.text:000xxx6a push 40h ; length
.text:000xxx6c push currenteprocessobject ; address
.text:000xxx72 call ds:probeforread
这里是故意做个异常来实现跳转。如果你在 .text:000xxx72 call ds:probeforread 指令上单步执行的时候调试器会跑飞了,
也就是说从调试器退出了,没有继续跟踪下去。
第三部分
接下来说我们的 pspcidtable 我们找到了 pspcidtable 变量后, pspcidtable [这个 handle_table 的句柄表中,保存着所有进程和线程对象的指针。
pid(进程id)和 threadid(线程id)就是在这个句柄表中的索引。这个 handle_table 不属于任何进程,也没有链在 handle_table 链上。全局变量
pspcidtable 中是指向这个 handle_table 的指针。这个 handle_table 还有一点和别的 handle_table 都不同,就是它的 handle_table_entry 中的
第一个32bit 放着的是对象体指针(当然需要转换)而不是对象头指针(对象指针就是对象体指针)。] (特别注明 在[]的话不是俺写的是在网上抄来的
这里特别感谢 “jiurl玩玩win2k进程线程篇 handle_table” 文章的作者:jiurl )
我们之要想到办法遍历这个 pspcidtable 句柄表就可以遍历到系统的所有进程。icesword 为了遍历这个表他使用了系统为公开的 ntoskrnl.exe
的导出函数 exenumhandletable 。
icesword 定位到 ntoskrnl.exe 导出的 exenumhandletable函数。
这个函数是未公开的函数。
这个函数的函数原形可能是 void stdcall exenumhandletable (pulong handletable, pvoid callback, pvoid param, phandle handle optional);
其中的参数 pulong handletable 就可以用 pspcidtable 做参数.
pvoid callback 的类型为 bool (*exenumhandletablecallback)(handle_talbe_entry*,dword pid,pvoid param) 函数指针。
pvoid param 参数就是传送给回调函数的参数。
phandle handle optional 这个参数俺还没搞懂什么意思。在说俺也用不到他,所以也不管他了随他去吧。
当调用 exenumhandletable 函数的时候 函数在每次枚举到表中的一个句柄时都会调用一次回调函数。
当调用的 callback 回调函数返回值为 0 时继续枚举句柄表,如果返回 1 时则停止枚举。