软件防火墙有什么防不了?
但外出连接监测真的能让系统安然地运行在互联网中吗?笔者发现,软件防火墙其实很脆弱,很容易失效,甚至可能助纣为虐。
目前,黑客至少可以有两种方法来证明这些理论设想,第一是DLL注入法,第二是运行时进程代码注入法。
软件防火墙工作原理
几乎所有的软件防火墙都提供了类似硬件防火墙的功能,可以让用户限制对特定服务器端口的访问,这不是本篇文章的重点。软件防火墙的另外一个重要的特点就是可以赋予不同进程不同的权限和信任级别,这个就是通过外出连接监测来实现的。
当一个可执行程序创建进程企图访问网络的时候,软件防火墙要计算这个可执行文件的校验码,然后提示用户是否将这个进程作为可信的进程来允许其访问网络。防火墙一般通过安装核心模式驱动程序,或者钩子来监视和获取进程对操作系统底层的网络函数的调用。用户可以信任一个进程连接Internet,或者信任一个进程建立监听端口等。
关键的一点就是,只要用户将一个可执行程序加入信任列表,那么这个可执行程序创建的所有进程都是被信任的。如果该可执行程序被修改了,它的校验码也就会改变,防火墙可以对此发出警报。只要可执程序行不变,防火墙就会信任其创建的进程。
无附加DLL的运行时进程注入技术
软件防火墙只在创建进程时检查可执行程序的校验码。一旦进程成功加载到内存以后,防火墙就会假定其合法性,直至该进程终止。所以,我们只要在合法的受信任的进程的内存中动态注入代码并运行,就可以成功地穿透防火墙!
黑客首先 要得到对进程内存的访问权限,然后把恶意的代码拷贝到进程的内存空间,最后在该进程中远程执行代码。听起来很深奥,其实不然。只要您对Win32 API熟悉,就会发现其实微软提供了黑客需要的一切。
Cre ateRemoteThread函数可以创建一个在其它进程的地址空间里运行的线程。VirtualAllocEx()和WriteProcessMemory()这两个函数可以将黑客需要用到的代码注入到另外一个进程的地址空间,然后通过执行它们来创建一个远程线程。这些函数在MSDN中都有详细的说明。
黑客要做的就是创建一个二进制可执行文件,将需要注入的代码(用来穿透防火墙)包含在里面。换句话说,就是可执行文件里面应该有这样两个函数,一个用来将注入代码注入到受信任的进程中,另外一个就是被注入的函数。
黑客也有技术难题
听起来比较容易,但要具体实施,还需要解决几个难题。比如,黑客不可能用C语言写一个程序用来将另外一个静态的C语言编写的函数插入到一个远程进程里面,这样做会引起远程进程崩溃。虽然可能成功地调用有关的API函数,但是使用高级语言来做这样的操作还是有许多潜在的问题。
问题的本质就是:编译器用硬编码的偏移量生成汇编代码。比如,当使用C常量字符串时,这个串是被存放在最终的可执行文件的某个特定的内存地址处的。也就是说,当您的进程需要传递这个串的地址给某个函数的时候,这个地址是完全硬编码到可执行二进制代码中的。
我们可以用一个简单的例子来说明问题:
void main() {
printf(“Hello World”);
return 0;
}
这段C语言程序可以说众所周知。假设“Hello World”这个字符串存放在0x28000偏移处,我们事先知道应用程序加载的基地址是0x00400000,那么对“Hello World”的引用直接指向地址0x00428000(基地址+偏移地址)。
下面的返回汇编代码可以证明这一点
00401597 ...
00401598 push 0x00428000 ; the hello world string
0040159D call 0x004051e0 ; address of printf
0040159E ...
硬编码会造成什么问题呢?如果您处在进程自己的空间内,没有任何问题。如果您将代码移动到另外一个地址空间,所有这些硬编码的地址会指向完全不同的内容。比如上面的例子,如果0x00428000指向的地方恰巧存在一个合法的C字符串,也许程序不会崩溃,否则肯定导致程序崩溃!
另外,所有的函数调用的地址也是硬编码的,而在其它进程的地址空间内,这些函数也许在完全不同的地方,函数会丢失!
这些看似困难的问题不能成为黑客的禁锢,要保证所有地址的正确,黑客有三种方法:
1、 采用DLL注入。
2、 将所有类似的C串作为参数传递给CreateRemoteProcess。
3、 使用汇编语言,来避免这种硬编码。
对于第一种采用DLL注入的方法,使用起来相对简单,因为不必考虑地址合法有效的问题,Windows在导入DLL的时候会为其定址,可以保证地址的正确性。
对于第二种采用参数传递的方法,可以定义一个结构来包含这些C串:
typedef struct remoteparamter{
TCHAR testchar;
};
StrCpy(testchar,TEXT(“Hello World”));
而后将需要插入的函数代码和结构用WriteProcessMemory函数写入到远程进程的地址空间,接下来用CreateRemoteThread创建远程线程时,将上面的结构地址句柄作为参数传递给远程线程函数,从而正确地寻址。
对于第三种方法,使用汇编语言就可以避免C编译器的地址硬编码的问题。
黑客的一些技巧
首先,黑客要尽可能多地避免硬编码内容。第一个是用户缺省浏览器的文件路径。他们一般通过查询注册表的“HKEY_CLASSES_ROOThtmlfileshell opencommand”来获取该路径。
接下来调用CreateProcess()函数,通过给STARTUP_INFO结构的wShowWindow传递SW_HIDE,可以使创建的浏览器窗口不可见。
为了确保浏览器进程已经装载完毕并且达到了稳定状态,黑客可以调用WaitForInoutIdle()函数给进程一些时间来完成初始化。一切就绪后,黑客调用VirtualAllocEx()函数在创建的进程内部分配内存,然后调用WriteProcess Memory()函数将恶意代码拷贝到分配的内存里,最后调用CreateRemoteProcess()运行这些代码。接下来,他们只需要等待远程线程终止。
要插入的函数可以接受一个DWORD参数。黑客将被插入的函数的基地址传递给远程线程函数,被插入的代码就知道自己在远程进程中的位置。因为无法直接读取EIP寄存器,并且插入的代码有时又必须引用自身内部数据结构的内存地址,所以这一点对于黑客来说很重要。
代码被插入远程进程后,首先必须定位kernel32.dll在远程进程中的基地址,否则什么都干不了。然后黑客利用GetProcAddress来装载其它内容。简而言之,我们从PEB(进程环境块)中得到kernel32的基地址,PEB可以在TEB(线程环境块)中找到。TEB的偏移量存放在FS寄存器中。有了kernel32的基地址,我们就可以遍历其导出节部分找到GetProcAddress()的地址。动态链接库包含一个导出块,所有的被导出(输出)的函数的偏移都被以一种可读的名字(字符串)排列在里面。其中有两列内容是我们关心的,一列是函数名称,一列是函数实体指针。黑客可以模仿GetProcAddress函数来做一件事情:在函数名称列找到“GetProcAddress”串,然后得到其偏移地址。
总结上面的内容,我们发现黑客的基本思路就是,用CreateProcess()打开可信的进程,然后注入代码并用CreateRemoteThread()启动代码。有些防火墙挂钩CreateRemote Thread()和CreateProcess()函数,所以可以发现这种操作。不过,大多数防火墙没这样。不过不要心存侥幸,黑客还有办法完全穿透软件防火墙。
下面继续看黑客的新方法。这里他们要用到两个重要的API函数GetThread Context()和SetThreadContext ()。这两个函数可以修改线程的上下文。上下文是一个重要的结构,它包含当前线程上的所有的CPU寄存器值。所以,通过上面两个函数调用,就可以获取和修改这些值,并让CPU使用新的值。黑客最有兴趣的就是EIP寄存器(包含线程的指令指针)。
首先,他们先找到一个正在运行的可信的进程。然后,还是按照前面的方法将代码注入到这个进程的内存空间。接下来,黑客不需要创建一个新的远程线程,他们要劫持主线程,作法就是修改主线程的指令指针使其指向被插入的代码。当被插入代码完成了操作后再将主线程的上下文恢复。 为了隐蔽性,黑客可以用一个事件对象来通知被插入代码操作已经完成,然后他们就可以立即释放远程进程中的内存分配并进行其它清理工作。不过有的进程可能不允许劫持主线程,但是黑客总可以找到可以利用的线程。
设想,这样的代码如果修改成shellcode用来攻击Windows的远程溢出,那么结果是什么。
【结语】通过上面的分析,我们可以看到在防火墙外出检测表面的安全的掩饰下,黑客已经在行动了。更可怕的是,我们自以为它是如此安全而没有任何的防范和戒备。各位网管要时刻保持戒备,不要让表面的安全蒙住您的眼睛。
文章转载地址:http://www.365master.com/kt_article_show.php?article_id=1748&categ_code=10041003