作者: churui 2005-11-05 12:22
某日,遇到一个奇怪的程序(你也许并不关心它的名字,我们就姑且称它为程序A)。这个程序是十分霸道的,在与我的程序(你也肯定不会关心它的名字,所以我们称为程序B)同时执行的时候,总能从程序B(这里也就是进程B了)的数据段中读取到一些内容。这一点让我非常不爽,于是我决定给B加入自我保护的功能,让A不能轻易的读取。听起来有点象“磁心大战”?呵呵,总之交锋就是这样开始的,初衷也非常简单,而争夺的过程倒是几经波折。
在战斗开始之前,还需要说明的是,这里的A和B都是GUI程序,而且B总是先于A执行。
考虑一下A读取B的数据段,我能想到的也就是三种方法:
1.读取映象文件
2.使用ReadProcessMemory
3.注入进程B,然后直接读取。
对于第一种方法,只要B加一个简单的壳,A就无计可施。所以比较实用的还是后两种方法。经过观察,发现A在执行过程中给进程B注入了一个DLL,所以我判断A可能使用的是第三种方法。
A给B注入DLL,一般来说也就是三种方法。但是不管哪种方法,我认为最终总要调用ntdll!LdrLoadDll。于是,最原始的办法就是在进程B中hook LdrLoadDll这个API,拦截可疑的DLL。实现的过程并不复杂,可惜没有效果。也就是说,A在注入DLL的过程中,根本没有被B拦截到。
我使用IceSword,监视A的启动过程。发现A在启动时会在每个进程(当然,除了一些特殊的系统进程如Idle,csrss等等)中创建一个远线程。因此,我希望能把创建远线程拦截下来。考虑到一般创建远线程之前总要先用kernel32!OpenProcess得到进程句柄,我就试图在ring3使用hook api拦截所有的OpenProcess。可惜我的努力又失败了。本来hook OpenProcess工作的挺好,但是只要A一启动,对OpenProcess的hook马上失效。考虑到前面hook ntdll!LdrLoadDll也没有奏效,我认为A一定采取了某些防止hook api的手段。
在这种情况下,我打算到ring0去解决问题。由于之前我只有win32的开发经验,而driver几乎没有做过,拿着罗云彬老大翻译的Kmd教程中文版恶补两天,又在driverdevelop找了一些源码和资料,终于拼凑出了一个勉强能运行的driver。功能倒也简单,就是通过修改SSDT(System Service Dispatch Table)。使得ntdll!NtOpenProcess执行到核心态的时候,能够被我拦截下来。
这次还是失败了,而且失败的情况也和ring3如出一辙:本来ntdll!NtOpenProcess能够被拦截下来的,一旦A启动后,所有的拦截立刻失效。让我啼笑皆非的是,我做了另外一个简单的程序C,唯一的作用就是使用kernel32!OpenProcess去打开B,结果当A启动后,连这个C也能成功的打开B了。使用IceSword一看,原来A启动后,SSDT会被自动改回最初的内容,真让我无语。
在driverdevelop上向等bmyyyud,zhaock等几位大虾请教了好几次,得出了以下的结论:ntdll中导出了NtOpenProcess和ZwOpenProcess,而两者实际上是一回事。ntosknrl中也导出了NtOpenProcess和ZwOpenProces,而两者完全不同:ntosknrl!NtOpenProcess实际上就是“正常的”SSDT入口,而ntosknrl!ZwOpenProcess,不知道它是干什么的。如果修改SSDT,其实很容易被别人发现并且破掉(把入口换回NtOpenProcess就可以了)。那么有没有其他方法?看有的资料上说可以换int 2 e的中断处理函数,而xp中既然用sysenter/syscall了,看来这一招也不灵。最终,bmyyyud大虾提示我,可以改正常ntosknrl!NtOpenProcess的入口代码。(非入口~~~~)
修改ntosknrl!NtOpenProcess的入口代码比修改SSDT难度要大一些。首先ntosknrl!NtOpenProcess的代码所在页面具有只读属性,在修改页面前需要先修改CR0寄存器的第16个bit,否则一定是蓝屏没商量,对于我这样的新手来说,这还真有点让人无所适从。其次,为了修改入口代码,最方便的方法是使用微软的detours,可惜detours中引用了很多ring3的API,无法直接使用。只好拿来detours的源码,把不必要的细枝末节都剪裁掉,把所有使用到ring3 API的部分都尽量去掉或者用ring0的API代替。只留下最关键的部分,试着run了一把,没想到居然能运行。
这样做完之后,发现A程序调用ntosknrl!NtOpenProcess并且被我拦截了,颇感欣慰……没想到见鬼的是,在A程序调用ntosknrl!NtOpenProcess失败的情况下,它仍然给B创建了一个远线程。这个时候zhaock大虾告诉我,即使不调用NtOpenProcess,也可以调用PsLookupProcessByProcessId,ObOpenObjectByPointer来达到同样的目的。看来还需要拦截ntosknrl!NtCreateThread,保险起见,把ntosknrl!NtReadVirtualMemory也拦截下来。一切做完后,试运行,终于把A彻底拦住了,它再也没能用B中读出一个字节….. 我忽然想到了huyg师兄曾经说的“资料不是最重要的,蛮干才是最重要的”……(不敢苟同~~~~)
posted on 2008-07-21 15:37
free2000fly 阅读(1760)
评论(1) 编辑 收藏 引用