首页 理论教育 恶意代码设计实现计算机信息安全技术研究

恶意代码设计实现计算机信息安全技术研究

时间:2023-10-26 理论教育 版权反馈
【摘要】:恶意代码的破坏效果是实现和核心,恶意代码是通过原子级功能实现的。本章节着重阐述的是笔者设计实现的部分恶意代码。课题通过Shellcode引擎自动对恶意代码执行Shellcode特定的约束检查。相对于堆栈缓冲区溢出攻击技术而言,新一代的堆溢出攻击技术更具威胁性。

恶意代码设计实现计算机信息安全技术研究

恶意代码的破坏效果是实现和核心,恶意代码是通过原子级功能实现的。每一种恶意代码实现某种特定的破坏效果,通过原子级攻击的组合可以达到综合的破坏效果,并且分级可控。本章节着重阐述的是笔者设计实现的部分恶意代码。

(一)Shellcode的设计

在编写Shellcode的过程中,由于Shellcode本身的特性会存在诸多约束,如坏字符(全零字节数据)等。在恶意代码的实际编写过程中,通过动态获得函数地址来调用系统函数,要求恶意代码具有很多的功能和特性,这就使得恶意代码会很长,程序流程会很复杂。课题通过Shellcode引擎自动对恶意代码执行Shellcode特定的约束检查。

1.引擎结构实现

通过对程序机器码的直接操作来实现Shellcode引擎。基于异或的原理:两个数同为真或两者同为假,异或后为假;两个数一个为真,另一个为假,异或后为真。逻辑异或的真值见表3-6。

表3-6 逻辑异或的真值

假设 A=10101010,B=10100101, 则 A⊕B=10101010⊕10100101=00001111。这种逻辑异或有一个很好的特性,也就是当两次异或同一操作数次后,就还原到数据本身,即(A⊕B)⊕B=00001111⊕10100101=10101010=A。

Shellcode引擎功能模块布局见表3-7。

表3-7 Shellcode引擎功能模块布局

2.引擎代码结构

在Shellcode中寻找加密密钥,密钥满足不产生坏字符,关键实现流程如图3-5所示。在流程图中,变量i用于记录密钥,变量j用于记录Shellcode数组下标,变量l用于记录寻找结果,变量sh_Len为Shellcode长度值,Enc_key保存密钥。

图3-5 寻找密钥流程

(二)基于堆的溢出攻击技术

1.缓冲区溢出攻击的原理及其发展阶段

缓冲区溢出攻击的目的在于,扰乱具有特权运行程序的功能,这样可以使得攻击者取得程序的控制权,如果该程序具有足够的权限,那么整个主机就被控制了。为了达到这个目的,攻击者必须实现两个目标:一是在程序的地址空间里安排适当的代码;二是适当初始化寄存器和存储器,让程序跳转到攻击者安排的地址空间中执行。

缓冲区溢出攻击经历了两代的发展:第一代是基于堆栈的缓冲区溢出攻击,第二代是基于堆的缓冲区溢出攻击。相对于堆栈缓冲区溢出攻击技术而言,新一代的堆溢出攻击技术更具威胁性。

最常见的一种缓冲区溢出攻击方式是基于堆栈的缓冲区溢出。函数的实现是在栈帧里进行局部变量内存分配的,所以函数中定义的缓冲区变量占用的内存空间是在该函数被调用时的栈帧里。在操作系统中,对缓冲区的操作是从内存的低地址往高地址的方向进行的,在缓冲区的高地址处保存的是函数调用返回地址。如果向缓冲区填充大于目标缓冲区大小的内容,保存在函数栈帧中的返回地址就有可能被覆写,导致程序的执行流程按照攻击者的意图转移。目前,针对这种缓冲区溢出的预防措施较多,而且检测溢出隐患也相对比较容易。

基于堆的缓冲区溢出攻击是近年来逐渐受到关注的缓冲区溢出攻击方式。malloc经常被用于分配内存,并向用户返回一个指向所分配堆块开始位置的内存指针。这个指针前面有8个字节的内部结构,块使用情况、分配的块长度等管理信息保存在这个内部结构中。攻击者如果修改管理信息,可能会引起将恶意数据写到一个任意内存地址中去的问题,导致程序转而执行恶意代码。这种溢出攻击更具威胁,即使禁止一个堆的执行权限,也并不能解决溢出的问题。因为虽然禁止了在堆中执行指令的可能,但攻击者仍然有机会覆写在堆中的数据。两种溢出攻击技术的比较见表3-8。

表3-8 两种缓冲区溢出攻击技术的比较

2.堆管理机制及其潜在弱点

在Linux操作系统中,GNU计划(GNU's Not Unix)C的malloc称空闲内存为“块”(chunk)。内存块的大小和状态信息都保存在块的内部。空闲块的释放过程执行如下四个步骤:

(1)检查邻居内存块,判断其是处于空闲状态还是处于已使用状态。

(2)如果邻居内存块已使用,则直接将被释放的内存块添加到“空闲块双向链表”中。还要增加两个内存指针到块中未使用的数据部分,“fd”指针指向链表中下一个空闲块的起始位置,“bk”指针指向链表上一个空闲块的起始位置。

(3)如果上一个或者下一个内存块没有使用(即已经是在空闲块双向链表中了),则分别对每个邻居空闲块调用unlink操作,实际上,就是执行了一个双向链表节点的删除工作,将邻居空闲内存块从“空闲块双向链表”中删除。空闲块删除过程如图3-6、图3-7所示。若删除chunk结点,则chunk1->fd<=chunk->fd;chunk0->bk<=chunk->bk。

(4)将邻居空闲内存块与即将释放的内存块合并,新合并内存块按“步骤(2)”加到空闲块链表中,会有如下等式成立,即chunk1->fd=chunk->bk+8;chunk0->bk=chunk->fd+12。

图3-6 邻居空闲块删除前的堆内存

图3-7 邻居空闲块与释放的内存块合并后的内存

需要注意的是,在“步骤(3)”中,“一个内存位置指针被指向另一个内存位置的指针所覆写”,这种内存管理方式存在安全隐患。如果攻击者可以控制覆写的内存位置,并且可以把恶意代码插入指针所指向的内存位置,那么攻击者就可以在被覆写的内存位置被引用时执行恶意代码。

3.堆溢出攻击入侵实现

利用Wu-Ftpd的glob扩展弱点进行堆溢出攻击,需要执行以下七个步骤:

(1)攻击者首先要在堆中插入指向某一内存位置的指针。

(2)在“步骤(1)”中指向的内存位置创建一个空闲内存块,然后在该内存块的相邻位置再创建一个一样的内存块。这两个由攻击者创建的空闲内存块中包含假的内存管理信息。比如,“fd”和“bk”指针假定指向下一个和上一个空闲内存列表中的内存块,实际上,“fd”指向了攻击者插入的恶意代码内存起始位置,而“bk”指向了攻击者希望用指向恶意代码的指针覆写的内存位置。在本章节中,“bk”存放的是指向系统日志指针的地址。此外,这两个内存块的“本块大小”指示邻居内存块空闲。

(3)攻击者发送包含“~{”字符串的命令到FTP服务器上。

(4)由于glob在处理“~{”字符串的时候导致内存错误,不初始化内存位置,其结果FTP服务器试图释放由“步骤(1)”提供的指针所指向的内存块,这个内存块实际上就是“步骤(2)”中由攻击者创建的第一个空闲内存块。

(5)系统调用free函数进行内存释放。检查即将释放的内存块的相邻内存块,也就是“步骤(2)”中由攻击者创建的第二个空闲内存块。如果发现该块未使用,应当与即将释放的内存块合并,“bk+8”指针所指向的内存块位置被“fd”指向的内存地址所覆写。其结果是攻击者成功地用恶意代码起始位置地址覆写了指向系统日志指针。该过程如图3-8所示。

(6)此后,FTP服务器试图立即写系统日志。由于程序执行流向已经被攻击者改变,实际上并没有运行写系统日志的代码,因此FTP服务器转而执行由攻击者插入的恶意代码。

(7)至此,攻击者获得了目标FTP服务器的根用户权限。

图3-8 Wu-Ftpd堆溢出攻击过程

(三)格式化字符串攻击技术

格式化字符串攻击是缓冲区溢出攻击的一种,普通的缓冲区溢出就是利用了堆栈生长方向和数据存储方向相反的特点,用后存入的数据覆盖先前压栈的数据,一般是覆盖返回地址,从而改变程序的流程。这样,函数在返回时就跳转到了攻击者指定的地址,便可以按照攻击者的意愿做任何事情了。

格式化字符串漏洞和普通的缓冲溢出有相似之处,但又有所不同,它们都是利用了程序员的疏忽大意来改变程序的正常运行流程。

格式化字符串漏洞的历史要比缓冲区溢出短得多,而且一般被认为是程序员的编程错误。但是,格式化字符串漏洞攻击可以往任意地址写任意内容,其危害不可小视。

1.*printf系列函数的特殊性质

所谓格式化字符串,就是在*printf系列函数中,按照一定的格式对数据进行输出,可以输出到标准输出,即printf;也可以输出到文件句柄、字符串等。 对应的函数有 fprintf,sprintf,snprintf,vprintf,vfprintf,vsprintf等,能被攻击者利用的地方也就出在这一系列的*printf函数中。在正常情况下,这些函数不会造成什么危害,但是*printf系列函数有三个特殊的性质,这些特殊性质如果被攻击者结合起来利用,就会形成漏洞。*printf系列函数的三个特性如下:

(1)*printf系列函数参数个数不固定,造成访问越界数据

因为*printf系列函数的参数个数是不固定的,如果其第一个参数,即格式化字符串是由用户来提供的话,那么用户就可以访问格式化字符串前面的堆栈里的任何内容了。之所以会出现格式化字符串漏洞,就是因为程序员把printf的第一个参数,即格式化字符串交给用户来提供,如果用户提供特定数量的%x,就可以访问到特定地址的堆栈内容。

(2)利用%n格式符写入跳转地址

在上一步中,只是显示内存的内容而没有改变它,利用*printf的一个特殊格式符%n,就可以向内存中写入内容。%n是一个在编程中不经常用到的格式符,它的作用是把前面已经打印的长度写入某个内存地址中。在实际利用某个漏洞时,并不是直接把跳转地址写入函数的返回地址,而是通过地址来间接地改写返回地址。现在已经可以利用提交格式化字符串来访问格式化字符串前面堆栈里的内容了,并且利用%n可以向一个内存单元中的地址写入一个值。如果可以访问到提交的字符串,就可以在提交的字符串中放上某个函数的返回地址的指针,这样就可以利用%n来改写这个返回地址。当然,%n向内存中写入的值并不是随意的,它只能写入前面打印过的字符数量,而攻击者需要的是写入存放Shellcode的地址,就如同普通的缓冲区溢出攻击那样。

(3)利用附加格式符控制跳转地址的值

*printf系列函数的其中一个性质是,程序员可以定义打印字符的宽度,即在格式符中间加上一个整数,*printf就会把这个数值作为输出宽度。如果输出的实际宽度大于指定宽度,则仍按实际宽度输出;如果小于指定宽度,则按指定宽度输出。程序员可以利用这个特性,用很少的格式符来输出一个很大的数值到%n,而且这个数值可以由攻击者来指定。通过附加格式符来控制向函数返回地址中写入的值,一般是利用%n前面的最后一个格式符来控制这个数值的,通常需要一些计算,计算方法一般是用Shellcode的地址减去最后一个格式化字符串前的所有格式化字符串的打印长度,这样%n写入的数值就恰好是Shellcode的地址了。

2.格式化字符串攻击的常用方法

对于格式化字符串攻击的常用方法,笔者现归纳总结如下:

(1)覆盖函数返回地址

覆盖当前执行函数的返回地址,当这个函数执行完毕并返回的时候,就可以按照攻击者的意愿改变程序的运行流程了。在使用这种技术时需要知道两个信息:一是堆栈中存储函数返回地址的存储单元的地址,二是Shellcode首地址。

格式化字符串攻击覆盖函数返回地址通常有两种选择:一是覆盖邻近的一个函数(调用该函数的函数)的返回地址,这是与普通缓冲区溢出攻击相类似的方法;二是覆盖*printf系列函数自身的返回地址,较前者而言,该方法具有更高的精确度,即使在条件较为苛刻的情况下也可以使用。

(2)覆盖.dtors列表(析构函数指针

可执行连接格式(Executable and Linkable Format,简称ELF)文件格式的.dtors列表,即析构函数指针,.dtors的作用是该表中的内容将在main函数返回的时候被执行。利用格式化字符串漏洞进行攻击覆写的是内存映像的.dtors,默认编译的ELF文件都是有该字段的。这种攻击方式的思路是:利用格式化字符串漏洞“往任意地址写任意内容”的特点,直接把Shellcode的地址写入.dtors中,Shellcode的地址可以是一个有效的范围,使用的Shellcode位于其中,然后用NOP填充满,这样可以提高精确度;或者可以考虑把Shellcode放入环境变量中,这样精度更高,而且限制更少。

这种格式化字符串攻击技术具有两个特点:①如果攻击者可以看到目标的进制代码,那么很容易确定覆写的地址,只要分析ELF文件镜像和.dtors的位置就可以了。②其缺点是目标程序应当被编译,并且与GNU工具链接;在程序执行exit函数之前,很难找到可以存储Shellcode的地方。

(3)覆盖GOT表(全局偏移表)

在ELF文件的进程空间中,全局偏移表(Global Offset Table,简称GOT)能够把与位置无关的地址定位到绝对地址。程序使用到的每个库函数在GOT中都有一个函数地址的入口,在程序第一次使用函数之前,入口包含运行连接器(run-time-linker,简称rtl)的地址;当程序调用函数时,控制权将传递给rtl,函数实地址被解析并插入GOT中。此后,每次调用此函数就直接传递控制权给该函数,不再调用rtl了。

在利用格式化字符串漏洞之后,通过覆写程序,即将使用的函数GOT入口,攻击者可以夺取程序的控制权,并且跳转到任意可执行的地址。执行返回地址检查的堆栈保护策略对这种格式化字符串攻击方式不起作用。

通过覆写GOT的入口方法获得程序的控制权具有两方面优点:①独立于环境变量(如堆栈)和动态内存分配(如堆)。GOT入口地址由二进制代码确定,如果两个系统运行相同的二进制代码,则其GOT的入口总是一样的。②易于操作,运行objdump命令就可以获取需要覆写的地址。

(4)Return into LibC(返回库函数)

Return into LibC的原理阐述:这里的function_in_lib本来应该是函数的返回地址,现在变成系统库中的地址,或者是过程链接表(Process Link Table,简称PLT)中的地址。程序用到扩展基址指针寄存器(Extended Base Pointer,简称EBP),buffer fill-up(*)应该正确地处理这个function_in_lib函数。arg_1,arg_2,……是function_in_lib函数的参数。当function_in_lib函数返回时,会把dummy_int32作为返回地址EIP,继续执行,如图3-9所示。

图3-9 Return into LibC的情况

攻击者的利用程序在调用system函数之前必须调用一系列的函数来获得特权。在进行前需要解决两个问题:①把一系列的调用都串联起来,同时要保证使用的是正确的参数。目前,要想把一系列函数调用串联起来,有两种方法:一是“esp lifting”方法,该方法适用于-fomit-frame-pointer编译的程序;二是“frame faking”方法,该方法是为编译时没有带上-fomitframe-pointer编译选项而设计准备的。②函数和参数所有的数据都不能包含“\0”。

利用Return into LibC技术进行格式化字符串攻击的特点:一是需要一个固定地址的缓冲区,也就是说,缓冲区要么是静态的,要么是malloc产生的,并且该缓冲区的数据是可由用户控制的;二是必须得到一些重要、精确的数据,如需要在利用程序开头定义的一些常量数值。

(5)利用atexit结构

这种格式化字符串攻击方式主要针对静态链接二进制代码,利用的是atexit结构的处理器,atexit函数用于注册一个给定的函数,该函数在程序exit时被调用。注册的函数是反序被调用的,至少可以注册32个函数。当然,只要有足够分配的内存,更多的函数也是允许的。这种机制允许程序设置多个处理器,在退出时用于释放资源。(www.xing528.com)

在静态编译的二进制中,libc被保存在程序的堆区域,因此atexit的位置在程序中的静态数组附近。在静态的字符缓冲区后面构造攻击者的atexit结构,覆盖atexit变量,可以使exit函数执行在内存中的任何地方,如执行攻击者设置好的Shellcode。

(6)覆盖jmpbuf's

覆写jmpbuf的技术最初是用于堆溢出漏洞利用的。由于格式化字符串jmpbuf的特性与函数指针类似,而且格式化字符串可以往内存的任意地址写入任何内容,不受缓冲区中jmpbuf相对位置的限制。

格式化字符串攻击可以使用jmpbuf's(setjmp/longjmp)进行漏洞利用。jmpbuf中保存的是栈帧,在后面执行时跳转到这里。如果攻击者有机会覆盖在setjmp与longjmp函数之间的缓冲区,就可以进行漏洞利用了。

(四)内核入侵隐藏技术

1.内核支持可加载模块

入侵隐藏技术演进的发展趋势:从最开始简单的后门,到内核级RootKit,新的入侵隐藏技术越来越复杂,越来越隐蔽;开始的入侵隐藏技术大多数都围绕着后门和木马进行,在最近几年,主要集中在内核级的系统开发上。在不同的内核下,内核入侵的难易程度也不一样。下面,笔者对不同情形下内核入侵的方法进行简要说明:

(1)内核支持可加载模块

在这种情形下,用户编写的代码在编译成目标文件并使用insmod命令插入内核后,代码运行在内核空间,可访问内核空间的内存,在这种情形下,程序的装入由系统提供。而且,在内核支持可加载模块的系统中,一般还会导出符号表。如果自己要用的某个系统函数或变量正好在导出的符号表中,在编写的代码中就可直接使用此函数或变量。此时,系统实际上完成了类似编译器的链接器功能。

(2)内核不支持可加载模块

内核不支持可加载模块,说明系统不能将自己编写的程序装入内核空间执行,这意味着将编写的代码装入内核空间这个步骤必须编程实现。在将可执行代码装入内存的过程中,必不可少的步骤是对代码的重定位。而对一般的用户程序而言,代码的装入由系统完成,代码编写者根本不用关心重定位的问题。

不难看出,在内核支持可加载模块的情况下,进行内核入侵的难度较小。正因如此,系统管理员可能会基于安全性的考虑而使用不支持可加载模块的内核。本章节的重点放在后一种情况,该攻击方法对支持可加载模块的内核同样适用。

2.各步骤的实现原理

笔者在本节详细分析了入侵流程中各步骤的实现原理。在某些步骤中会涉及读写内存的操作,这些操作是通过读写设备文件/dev/kmem来实现的,文件/dev/kmem是系统虚拟内存的镜像,通过修改这个文件,能够达到修改系统内存的目的。一般情况下,这个文件的权限是root用户可读写。

(1)求出系统调用表的地址

那那瓦提(Nanavati)和科塔里(Kothari)在Hidden Processes Detection using the PspCidTable中,以及西尔维奥·塞萨尔(Silvio Cesare)在Runtime kernel kmem patching中,都提出了通过模式匹配的方法在内存中搜索系统调用表地址的方案。其思想是搜索Linux系统system_call函数的前100个字节,找到call指令,call指令后的四个字节就是系统调用表地址。system_call函数的地址可通过如下操作得到:首先,通过汇编指令“sidt”得到中断描述符寄存器的值,中断描述符寄存器中保存有中断描述符表的首地址;其次,根据中断描述符首地址得到0x80号中断描述符的地址;最后,读取内存0x80号中断描述符,这个中断描述符中就保存有system_call函数的地址。

(2)求出kmalloc函数的地址

Silvio提出了求kmalloc函数地址的方法,但它不能保证100%成功。本章节提出了另外一种实现方案,在多种操作系统的内核上均可取得正确的结果。

在内核中调用kmalloc需要两个参数,kmalloc函数原型为

void*kmalloc(size_t size,int flags);

要在整个内核中进行模式匹配搜索无疑是不现实的,既然已经知道了系统调用表的地址,便知道了所有系统调用处理函数的地址。要想在系统调用处理函数的二进制代码中寻找kmalloc函数地址,首先必须选择一个系统调用处理函数,可选择源代码目录中,在fs/select.c文件中定义的sys_poll函数。实现流程如下:

第一步,根据系统调用表,求出sys_poll函数的地址(即syscall_table_NR_poll)。

第二步,读出内存中从sys_poll函数地址开始的800个字节。

第三步,在读出的800个字节中搜索0xc7******f0010000,其中*标识任意16进制数字。

第四步,在接下来的32个字节中查找0xe8。

第五步,0xe8后的四个字节就是kmalloc函数的相对偏移,对这四个字节进行验证,若计算得到的绝对地址大于0xc0000000,则地址为kmalloc函数的地址;否则跳转到“第四步”。

(3)编写函数get_kmm在用户态程序中分配内核空间内存

找到kmalloc函数的地址后,接下来就可实现在内核中申请一块内存。需要注意的是,在用户态程序中是不能直接调用kmalloc函数的。

首先,编写函数get_kmm,这个函数通过调用kmalloc来分配内核空间的内存,在执行这个编写的函数的二进制代码时不需要重定位。

其次,将这个函数编译成目标代码,并用这段目标代码中的代码段部分来覆盖某个不常用的系统调用,如olduname,覆盖操作通过写/dev/kmem文件实现。

再次,在用户空间进行olduname系统调用,此时,执行的就是内核空间中完成分配内存任务的那段代码了。

最后,得到内核空间的内存后,恢复原有系统调用olduname代码。

(4)编写入侵代码

这个步骤需要用到syscall_table值。编写的入侵代码主要是系统调用替换代码,为了让这些函数能替代系统调用,即用户空间的程序进行系统调用时,执行的是本程序的替代函数,需要在系统进入系统调用函数过程中的某个步骤进行修改。下面,笔者分析系统如何由用户态切换到系统态,并执行系统调用代码。

Linux在实现系统调用时使用中断向量(0x80),通过system_call进入内核中。当用户态进程执行一条int 0x80汇编指令时,CPU就切换到内核态,并开始执行system_call函数,再通过系统调用表来取得相应系统调用的地址执行。通过分析上述过程,可得出以下三种方案:

第一,新建一个系统调用表,在保留原有系统调用表大部分内容的前提下,以被替换系统调用的编号为索引,加入自己编写的替换函数地址;然后修改system_call函数的二进制代码,使其使用新的系统调用表。

第二,修改系统调用表,修改要替换的系统调用对应的表项,指向自己编写的替换函数。

第三,修改原有系统调用函数的二进制代码,在代码开头加上跳转指令,跳转地址为自己编写的替换函数的地址。

上述三种方案的性能比较见表3-9。

表3-9 三种方案的比较

(5)对汇编代码进行处理,并将处理后的代码汇编

编译后的汇编代码往往由好几个段构成,必须将代码中的几个段合并为一个代码段。为了保存入侵代码的相关信息,以供接下来的步骤使用,还要对汇编代码进行一定的修改。修改完成后,加入一定辅助信息,并进行汇编,从而得到目标代码。

(6)对目标代码进行分析,提取出其中的代码段及重定位信息

要想提取出目标代码中的代码段,需要知道代码段的偏移和大小,有两种方案:一是自己编程,对目标代码文件进行解析,这要求编程者熟悉ELF文件格式;二是使用相应参数的objdump命令来读取段信息。

编译后的目标代码中存在需要重定位的地址,对重定位的解决一般有两种方法。方法及其原理阐述如下:

①入侵代码自身完成重定位

入侵代码中有一段初始化代码,初始化代码首先执行一个call指令,将下一条指令地址压栈,这时栈顶保存的就是当前代码执行时的一个内存地址了。因此,只需先将入侵代码编译得到汇编代码,在汇编代码中对重定位变量的访问方式进行修改,便可使入侵代码自身具有重定位能力。

②载入程序完成重定位

这种方法是直接对目标代码进行操作。在Linux系统中,目标代码的文件格式为ELF,目标代码中保存有重定位表,载入程序可以根据目标代码中保存的重定位表对代码段进行重定位。与获取目标代码中代码段信息的方法类似,获取重定位表既可以编程对目标代码文件进行解析,也可以使用带相应参数的objdump命令来读取重定位表。

(7)调用get_kmm分配内核空间的内存和代码重定位

调用函数get_kmm本质上是执行函数get_kmm的代码,这是通过先替换一个系统调用sys_olduname执行代码,再在用户空间调用系统调用olduname来实现的。

计算出目标代码中代码段的长度,根据这个长度信息决定分配内存空间的大小。调用函数get_kmm分配内存,得到内存的首地址(大于0xc0000000),再载入程序,根据内存首地址以及上一步骤得到的重定位信息对代码段中的二进制代码进行重定位。

(8)将重定位后的代码写入分配的内存

通过写设备文件/dev/kmem实现编写单独程序,需要配合对应的make文件来实现。

3.入侵代码隐藏技术的实现

通过分析,可得出入侵代码实际需要隐藏三个方面的内容,即攻击过程的文件信息、进程信息和网络连接。

(1)隐藏文件和进程信息的实现

重新定义两个系统调用new_getdents和new_getdents64,用来替换原有的系统调用sys_getdents和sys_getdents64,两个函数的实现步骤几乎一样,给出new_getdents的流程。其实现流程如图3-10所示。

图3-10 自定义系统调用new_getdents流程

(2)隐藏网络连接的实现

在程序中必须重新编写open和read两个系统调用,为了进行相关资源的释放工作,还需重新编写close,修改的系统调用命名为new_open,new_read和new_close。以new_open和new_read为例,其执行流程如图3-11和图3-12所示。

图3-11 new_open的执行流程

图3-12 new_read的执行流程

(五)杀毒软件反制的技术

杀毒软件反制,即使杀毒软件失效的方法,主要研究对象为卡巴斯基、诺顿、趋势科技等杀毒软件。主要研究思路是分析杀毒软件的保护方法,找到其中存在的缺陷,以寻求一种通用的能够反制上述杀毒软件的方法。笔者通过分析四种解决思路,最后找到了运用进程结束与文件篡改技术相结合的方法,使杀毒软件失效。这四种解决思路为:一是将杀毒软件的核心进程结束;二是将杀毒软件的核心文件删除;三是篡改杀毒软件的病毒库日期,使病毒库失效;四是将杀毒软件的内核服务驱动卸载。

杀毒软件的保护机制分析见表3-10。

表3-10 杀毒软件的保护机制分析

续 表

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈