随笔-60  评论-262  文章-1  trackbacks-0

作者: churui 2005-11-05 12:22

某日,遇到一个奇怪的程序(你也许并不关心它的名字,我们就姑且称它为程序A)。这个程序是十分霸道的,在与我的程序(你也肯定不会关心它的名字,所以我们称为程序B)同时执行的时候,总能从程序B(这里也就是进程B了)的数据段中读取到一些内容。这一点让我非常不爽,于是我决定给B加入自我保护的功能,让A不能轻易的读取。听起来有点象“磁心大战”?呵呵,总之交锋就是这样开始的,初衷也非常简单,而争夺的过程倒是几经波折。

       在战斗开始之前,还需要说明的是,这里的AB都是GUI程序,而且B总是先于A执行。

       考虑一下A读取B的数据段,我能想到的也就是三种方法:
1.
读取映象文件  
2.
使用ReadProcessMemory   
3.
注入进程B,然后直接读取。

对于第一种方法,只要B加一个简单的壳,A就无计可施。所以比较实用的还是后两种方法。经过观察,发现A在执行过程中给进程B注入了一个DLL所以我判断A可能使用的是第三种方法。

       AB注入DLL,一般来说也就是三种方法。但是不管哪种方法,我认为最终总要调用ntdll!LdrLoadDll。于是,最原始的办法就是在进程Bhook LdrLoadDll这个API,拦截可疑的DLL。实现的过程并不复杂,可惜没有效果。也就是说,A在注入DLL的过程中,根本没有被B拦截到。

       我使用IceSword,监视A的启动过程。发现A在启动时会在每个进程(当然,除了一些特殊的系统进程如Idlecsrss等等)中创建一个远线程。因此,我希望能把创建远线程拦截下来。考虑到一般创建远线程之前总要先用kernel32!OpenProcess得到进程句柄,我就试图在ring3使用hook api拦截所有的OpenProcess。可惜我的努力又失败了。本来hook OpenProcess工作的挺好,但是只要A一启动,对OpenProcesshook马上失效。考虑到前面hook ntdll!LdrLoadDll也没有奏效,我认为A一定采取了某些防止hook api的手段。

       在这种情况下,我打算到ring0去解决问题。由于之前我只有win32的开发经验,而driver几乎没有做过,拿着罗云彬老大翻译的Kmd教程中文版恶补两天,又在driverdevelop找了一些源码和资料,终于拼凑出了一个勉强能运行的driver。功能倒也简单,就是通过修改SSDTSystem Service Dispatch Table)。使得ntdll!NtOpenProcess执行到核心态的时候,能够被我拦截下来。

       这次还是失败了,而且失败的情况也和ring3如出一辙:本来ntdll!NtOpenProcess能够被拦截下来的,一旦A启动后,所有的拦截立刻失效。让我啼笑皆非的是,我做了另外一个简单的程序C,唯一的作用就是使用kernel32!OpenProcess去打开B,结果当A启动后,连这个C也能成功的打开B了。使用IceSword一看,原来A启动后,SSDT会被自动改回最初的内容,真让我无语。

       driverdevelop上向等bmyyyudzhaock等几位大虾请教了好几次,得出了以下的结论:ntdll中导出了NtOpenProcessZwOpenProcess,而两者实际上是一回事。ntosknrl中也导出了NtOpenProcessZwOpenProces,而两者完全不同:ntosknrl!NtOpenProcess实际上就是“正常的”SSDT入口,而ntosknrl!ZwOpenProcess,不知道它是干什么的。如果修改SSDT,其实很容易被别人发现并且破掉(把入口换回NtOpenProcess就可以了)。那么有没有其他方法?看有的资料上说可以换int 2 e的中断处理函数,xp中既然用sysenter/syscall,看来这一招也不灵。最终,bmyyyud大虾提示我,可以改正常ntosknrl!NtOpenProcess的入口代码。(非入口~~~~)

       修改ntosknrl!NtOpenProcess的入口代码比修改SSDT难度要大一些。首先ntosknrl!NtOpenProcess的代码所在页面具有只读属性,在修改页面前需要先修改CR0寄存器的第16bit,否则一定是蓝屏没商量,对于我这样的新手来说,这还真有点让人无所适从。其次,为了修改入口代码,最方便的方法是使用微软的detours,可惜detours中引用了很多ring3API,无法直接使用。只好拿来detours的源码,把不必要的细枝末节都剪裁掉,把所有使用到ring3 API的部分都尽量去掉或者ring0API代替。只留下最关键的部分,试着run了一把,没想到居然能运行。

       这样做完之后,发现A程序调用ntosknrl!NtOpenProcess并且被我拦截了,颇感欣慰……没想到见鬼的是,在A程序调用ntosknrl!NtOpenProcess失败的情况下,它仍然给B创建了一个远线程。这个时候zhaock大虾告诉我,即使不调用NtOpenProcess也可以调用PsLookupProcessByProcessIdObOpenObjectByPointer来达到同样的目的。看来还需要拦截ntosknrl!NtCreateThread,保险起见,把ntosknrl!NtReadVirtualMemory也拦截下来。一切做完后,试运行,终于把A彻底拦住了,它再也没能用B中读出一个字节….. 忽然想到huyg师兄曾经说的“资料不是最重要的,蛮干才是最重要的”……(不敢苟同~~~~)
posted on 2008-07-21 15:37 free2000fly 阅读(1705) 评论(1)  编辑 收藏 引用

评论:
# re: 反"远程线程注入"的思路(转载)[未登录] 2010-07-20 09:27 | zz
好厉害。。。  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理