首页
社区
课程
招聘
一些关于R0读写进程内存的问题(APC?附加?换出?)欢迎拍砖
2013-5-8 18:16 28121

一些关于R0读写进程内存的问题(APC?附加?换出?)欢迎拍砖

2013-5-8 18:16
28121
近日在研究直接读写内存,遇到了诸多问题
参考WRK中NtReadVirtualMemory/NtWriteVirtualMemory->MmCopyVirtualMemory->MiDoPoolCopy/MiDoMappedCoy的过程,读写进程内存的大致思路大概如下
解析进程句柄得到目标进程的PEPROCESS,然后检查一番,之后附加到目标进程(同时处理下APC)并切换CR3,最后复制内存,然后……

顺便看了下网上的实现方向,下面是第一种:

直接从目标进程的EPROCESS中读取页目录指针,然后切换CR3,并进行内存读写。
这种粗暴地方法看上去可以,但根本没考虑APC的问题,如果切换完CR3后线程被APC打断,执行APC时很可能因为地址空间不一致而……

于是就有了第二种,切换CR3前先处理下APC。处理的方法有很多,有的像MS一样保存APC这里先不讨论,但另外两种方法能有效么
A.通过KTHREAD禁用内核APC/特殊APC
看上去可以,交付APC前的确会检查是否禁用。

B.提升IRQL到APC_LEVEL
首先提升IRQL会造成不稳定么,其次似乎APC似乎也可以在进程切换时被交付……

然后是第三种,老老实实保存APC状态的,然后切换CR3再memcpy,这样似乎正确,当前线程的地址空间已经是目标进程的了。然而这里还有一个问题,目标进程的虚拟内存很可能不在物理内存中

这种情况下产生页错误内核还能正确处理么?还是得像MS那样先判断目标进程是否在内存中,如果不在的话等待其被换入内存


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞0
打赏
分享
最新回复 (47)
雪    币: 110
活跃值: (34)
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 1 2013-5-8 18:48
2
0
第一种情况的参考:
http://bbs.pediy.com/showthread.php?t=64938
雪    币: 110
活跃值: (34)
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 1 2013-5-9 20:16
3
0

木有人能给点参考么
雪    币: 293
活跃值: (232)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
瀚海云烟 1 2013-5-9 20:52
4
0
偶们一般用 KeStackAttachProcess 附加进程, 这样系统会自己去切换CR3, 这样不管线程调度还是怎么的(切换CR3我感觉也没什么问题的,这样用你出现过蓝屏吗?), 都已经是对方的领空了, 至于APC算不了什么, DISPATCH_LEVEL才是线程调度,换页内存的IRQL, 在APC下还是可以读取分页内存的 ( 你提升IRQL到DISPATCH_LEVEL就严格不能读取分页内存了 ).
APC也是在线程上下文的, 每个线程有自己的线程队列, 在KTHREAD的 kernelapcdisable 结构中, 他也有上下文, 在切换的时候会查看是否切换线程的呀, 在切换的时候肯定会把当前的上下文保存起来, 然后再用新的线程上下文付值寄存器. 系统又不是说切换APC还用原来的CR3.
只要附加了进程 修改CR0读写保护位  就可以读写内存了,其他不用管, 反正不会蓝屏的哈
但是在读写之前最好还是先, MmIsAddressValid \ ProbeForRead \ ProbeForWrite 判断下,内存是否存在可读可写
雪    币: 110
活跃值: (34)
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 1 2013-5-9 21:03
5
0
不用KeStackAttachProcess ,自己实现躲HOOK……
雪    币: 293
活跃值: (232)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
瀚海云烟 1 2013-5-9 21:11
6
0
重载内核调用
雪    币: 110
活跃值: (34)
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 1 2013-5-9 22:11
7
0
这是最后的选择,解析PE文件还得费不少功夫……
雪    币: 228
活跃值: (115)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
exediy 1 2013-5-10 18:33
8
0
躲???自己LOAD个内核 不怕HOOK  爱咋咋滴!!!然后重载内核的代码早就满天飞了,XP-WIN8全系列
雪    币: 110
活跃值: (34)
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 1 2013-5-10 18:59
9
0
内核重载能躲掉大部分的浅层和中层HOOK,但是依然牺牲了隐蔽性……
就是重载了,KeStackAttachProcess还是会留下不少痕迹
雪    币: 228
活跃值: (115)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
exediy 1 2013-5-10 19:21
10
0
痕迹???重载KeStackAttachProcess就不经过原本被HOOK的部分了.再说更深的地方一般应用软件也不敢HOOK,
雪    币: 371
活跃值: (72)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
学雄 1 2013-5-10 19:33
11
0
  有什么痕迹?
雪    币: 110
活跃值: (34)
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 1 2013-5-10 20:16
12
0
目标进程的就绪线程列表会有变化,可能会把本线程加入该链表。如果被抓到的话……
雪    币: 110
活跃值: (34)
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 1 2013-5-10 20:17
13
0
躲过HOOK可以绕开大部分检查,但是动了系统中的数据的话还是会有痕迹,参考上一个回复
雪    币: 228
活跃值: (115)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
exediy 1 2013-5-10 21:54
14
0
nt!KeStackAttachProcess:
804f8802 8bff             mov     edi,edi
804f8804 55               push    ebp
804f8805 8bec             mov     ebp,esp
804f8807 56               push    esi
804f8808 57               push    edi
804f8809 64a124010000     mov     eax,fs:[00000124]//currentThread
804f880f 8bf0             mov     esi,eax
804f8811 64a194090000     mov     eax,fs:[00000994]//_FX_SAVE_AREA unknown param
;不过可以通过下面KeBugCheckEx 分析出来 应该是地址空间, 而且bugCode是5....
804f8817 85c0             test    eax,eax
804f8819 741c             jz      nt!KeStackAttachProcess+0x35 (804f8837)
nt!KeStackAttachProcess+0x19://BSOD
804f881b 64a194090000     mov     eax,fs:[00000994]
804f8821 50               push    eax
804f8822 0fb68665010000   movzx   eax,byte ptr [esi+0x165]
804f8829 50               push    eax
804f882a ff7644           push    dword ptr [esi+0x44]
804f882d ff7508           push    dword ptr [ebp+0x8]
804f8830 6a05             push    0x5
804f8832 e89d120000       call    nt!KeBugCheckEx (804f9ad4)
nt!KeStackAttachProcess+0x35:
804f8837 8b7d08           mov     edi,[ebp+0x8]//参数 目标进程KPEB
804f883a 397e44           cmp     [esi+0x44],edi//目标进程与当前进程比较
804f883d 750c             jnz     nt!KeStackAttachProcess+0x49 (804f884b)
nt!KeStackAttachProcess+0x3d://相等
804f883f 8b450c           mov     eax,[ebp+0xc]//输出参数
804f8842 c7401001000000   mov     dword ptr [eax+0x10],0x1//将APCstate->KPEB设为1
804f8849 eb39             jmp     nt!KeStackAttachProcess+0x82 (804f8884)
nt!KeStackAttachProcess+0x49://不相等
804f884b ff1514874d80 call dword ptr [nt!_imp__KeRaiseIrqlToDpcLevel (804d8714)]//提高到DPC
804f8851 80be6501000000   cmp     byte ptr [esi+0x165],0x0//ApcStateIndex?=OriginalApcEnvironment
当前线程是否处于挂接状态
804f8858 884508           mov     [ebp+0x8],al//...竟然用函数参数空间来放IRQL
804f885b 740f             jz      nt!KeStackAttachProcess+0x6a (804f886c)
nt!KeStackAttachProcess+0x5b://不等
804f885d ff750c           push    dword ptr [ebp+0xc]
804f8860 ff7508           push    dword ptr [ebp+0x8]
804f8863 57               push    edi
804f8864 56               push    esi
804f8865 e898fdffff       call    nt!KiAttachProcess (804f8602)
804f886a eb18             jmp     nt!KeStackAttachProcess+0x82 (804f8884)
nt!KeStackAttachProcess+0x6a://相等
804f886c 8d864c010000     lea     eax,[esi+0x14c]//用curthread->saveApcstate进程不处于挂接状态,
需要将存下来以便恢复时候还原,你可能会问为什么上面不用saveState,原因是windows内核中线程只有两个ApcState来使用,
一个存储当前的,一个用来存储原来的,当当前进程已经是挂接状态时,其saveApc已经被使用 所以需要提供一个apcstate来存储,
至于这个apcstate是否被用来存储当前的apcstate还是存储saveapc中的就要看
KiAttachProcess 怎么处理了.
804f8872 50               push    eax
804f8873 ff7508           push    dword ptr [ebp+0x8]
804f8876 57               push    edi
804f8877 56               push    esi
804f8878 e885fdffff       call    nt!KiAttachProcess (804f8602)
804f887d 8b450c           mov     eax,[ebp+0xc]
804f8880 83601000         and     dword ptr [eax+0x10],0x0//返回值里设为0
nt!KeStackAttachProcess+0x82:
804f8884 5f               pop     edi
804f8885 5e               pop     esi
804f8886 5d               pop     ebp
804f8887 c20800           ret     0x8

这会留下什么痕迹啊....求解释....KeStackAttachProcess主要的核心功能只是安全的切换CR3啊
除非他把缺页给干了....那我就无奈了.真是高端的黑....
雪    币: 110
活跃值: (34)
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 1 2013-5-10 22:25
15
0
参考这篇文章和WRK:
http://www.whitecell.org/list.php?id=13
(这篇文章介绍了动态附加进程的过程,似乎那个时候还没有WRK,结论几乎全部由逆向工程得到而且和WRK中基本吻合,在此佩服一下)
中间的过程还是蛮繁琐的
雪    币: 228
活跃值: (115)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
exediy 1 2013-5-10 22:31
16
0
我在帖子里已经说了...就是一个保存SavedApcState的保存跟设置啊,关键是这个怎么会留下痕迹?再者切换进程到目标进程系统SYSTEM也有可能会发生切换切换啊.这样的话检查这个是非常不明智的.而且也不是切换以后SavedApcState就会发生改变.别忘了.SavedApcState不一样他是会恢复回去的,退1W步说,这个切换过程速度极快.如果想要能检测到.就要有心里准备把系统卡死...

个人感觉你有点杞人忧天......
雪    币: 110
活跃值: (34)
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 1 2013-5-10 22:46
17
0
不止是SavedApcState啊,APC我用别的方法处理掉了
注意这几点,在KiAttachProcess里面
“如果要切换的进程(EPROCESS)不在内存中则将当前线程(ETHREAD->WaitListEntry)等
待链插入到要切换进程(EPROCESS->ReadyListHead)的就绪链表中……”
(当目标进程不在内存中时):
Process->State = ProcessInTransition;

要保证安全就不能动任何目标进程的相关数据……
雪    币: 228
活跃值: (115)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
exediy 1 2013-5-10 22:48
18
0
我都说了...这些其实都是操作很快的事情,他进程线程本身切换都会触发这些东西,稍微有点脑子的程序员都不敢在这地方做检测,,,,再说你就不能用重载?重载下什么都OK了.重新确定下函数的位置 就直接调用了...后面拍拍屁股什么事都没.
雪    币: 110
活跃值: (34)
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 1 2013-5-10 22:54
19
0
前面说啦,那种情况下它的EPROCESS->ReadyListHead里面就多了一个线程,这就暴露了。
虽然的确概率不是特别大,但这里依然是个漏洞。如果正好检查一下EPROCESS->ReadyListHead的话就能发现读写它进程内存的线程……

重载不是万能的,恐怕100%可靠(忽略掉用VT挂HOOK的话)的实现得直接操作物理内存和页文件了……
雪    币: 228
活跃值: (115)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
exediy 1 2013-5-11 01:14
20
0
重载足够对付现有的安全保护软件了.个人感觉对ReadyListHead的担心完全是瞎操心.
http://bbs.pediy.com/showthread.php?p=413757
雪    币: 110
活跃值: (34)
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 1 2013-5-11 01:29
21
0
可能我的目标有点远:提出一种终极解决方案。。。检查ReadyListHead不失为反注入的好方案,对于特定的程序完全可以预知其ReadyListHead的变化情况。
多谢指教!
雪    币: 228
活跃值: (115)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
exediy 1 2013-5-11 01:31
22
0
不安全.为什么?因为别忘了用户的机器上有各种杀毒,然手输入法什么的,系统消息机制等等,检查这个太过危险,稍不小心就会引发误杀.
而且你切换完后直接把ReadyListHead清了不就可以了...检查这个就类似伸手去打人家屁股 再摸自己的脸一样
雪    币: 110
活跃值: (34)
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 1 2013-5-11 01:33
23
0
对于大多开发商来说这不是啥问题,排除一下即可。。。而且对方发现注入后完全可以直接放过,并追踪来源,这比被拦截更可怕
雪    币: 228
活跃值: (115)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
exediy 1 2013-5-11 01:34
24
0
根本就预知不了,除非他想系统卡死.切换过程那么快..而且你切换完后直接把ReadyListHead清了不就可以了...检查这个就类似伸手去打人家屁股 再摸自己的脸一样.

读取内存->附加到进程上->拷贝读取->抹除ReadyListHead->返回

整个过程是以毫秒计算的.他要怎么检查?
雪    币: 110
活跃值: (34)
能力值: (RANK:50 )
在线值:
发帖
回帖
粉丝
phpskycn 1 2013-5-11 01:46
25
0
不一定发现后就拦截啊,栈回溯一下追踪到来源,然后想办法获取PE文件,最后传到云端……
越底层越复杂,但搞安全的也不是傻子,知道有人靠内核重载绕过保护,肯定会寻找对抗方法。追踪ReadyListHead既不影响系统稳定性,又能有侦测到注入,误判是很好解决的,大不了先收集点信息不急着拦截嘛。

清ReadyListHead似乎不可行哦,后面DeAttach的时候应该要用到吧,而且把ReadyListHead清了目标进程可能还是不会被切换回内存,系统似乎要等一个进程有就绪的线程后才会换入……
游客
登录 | 注册 方可回帖
返回