前言

假设说我们的计算机中安装有杀毒软件,那么当我们有意或无意地下载了一个恶意程序后。杀软一般都会弹出一个对话框提示我们,下载的程序非常可能是恶意程序,建议删除之类的。或者杀软就不提示。直接删除了;或者当我们运行了某一个程序,包括有可疑操作,比方创建开机启动项,那么杀软一般也会对此进行提醒;或者当我们在计算机中插入U盘。杀软往往也会第一时间对U盘进行扫描,确认没有问题后,再打开U盘……上述这些,事实上都属于杀软的“主动防御”功能。

“主动防御”简单介绍

杀毒软件通常集成监控识别、病毒扫描和清除、自己主动升级病毒库、主动防御等功能,有的杀毒软件还带有数据恢复等功能。是计算机防御系统的重要组成部分。关于当中的“病毒扫描和清除”,我们之前在专杀工具的编写以及特征码查杀的课程中。已经解说过主要的原理。

而杀毒软件发展至今。各大安全厂商也越来越将目光放在“主动防御”技术的研发上。之所以会如此重视这项技术,是由于优秀的“主动防御”系统即便不升级病毒库,依然能够预防各种未知木马和新病毒。由于不论什么恶意程序,它假设想要实现各种各样的功能,终于都是须要调用各种API函数的。那么我们就能够对敏感API函数进行监控,在API函数运行之前,先解析API函数各个參数的内容。从而判定目标程序到底是想实现什么样的功能,假设是恶意操作,就进行拦截,让该API函数无法运行。

假设是正常操作。则直接放行。

比方恶意程序都喜欢将自己加入进系统的启动项中,那么我们就能够开启对注冊表的相关函数的监控。假设检測到目标程序会加入注冊表启动项。那么在加入之前,先进行拦截,询问用户是否允许该程序加入启动项,假设用户允许。则取消拦截。将程序加入启动项。假设用户不允许,或者“主动防御”系统就认定目标程序是恶意程序,那么就会阻止恶意程序的这一功能,以保证我们计算机的安全。

通过我们之前的课程对于病毒的分析能够知道,病毒程序往往会有多种行为,那么也就须要我们对于计算机系统的方方面面进行监控,甚至还须要一定的算法来分析各个API函数调用的相互关系。从而对目标程序进行判定。正是由于这样,“主动防御”系统才不是那么倚重病毒库的支持。因此非常多高手的计算机中往往不安装不论什么杀毒软件,可是却一定要安装“主动防御”系统。

成熟的杀毒软件或者专门的“主动防御”系统。它们的编程实现是基于 Ring0层的,由于也仅仅有在内核级别,才干够实现良好的监控效果。

而我们的课程为了简单起见,也便于大家理解,讨论的是最简单的基于Ring3层的“主动防御”系统的实现。虽说是R3级的,可是也涉及了不少的知识。希望大家一定要先将这次所讲的理论弄清楚。这样在接下来的编程实现中,才会游刃有余。

我们须要的知识有下面几点:

1、Inline Hook的基本原理

2、DLL注入的基本方法

3、对系统进行全局监控

Inline Hook的基本原理

API函数都保存在操作系统提供的DLL文件里。

当我们在程序中调用某个API函数并运行程序后,程序会隐式地将API函数所在的DLL文件载入到进程中,这样,程序就会像调用自己的函数一样调用API函数。我们能够看一下这个实验程序:

#include<stdio.h>
#include<windows.h> int main()
{
char szCommandLine[] = "notepad.exe"; // 所要启动的程序
STARTUPINFO si = {sizeof(si)};
PROCESS_INFORMATION pi;
si.dwFlags = STARTF_USESHOWWINDOW; // 指定wShowWindow成员有效
si.wShowWindow = TRUE; // 此成员设为TRUE的话则显示新建进程的主窗体
BOOL bRet = CreateProcess(
NULL, // 不在此指定可运行文件的文件名称
szCommandLine, // 命令行參数
NULL, // 默认进程安全性
NULL, // 默认进程安全性
FALSE, // 指定当前进程内句柄不能够被子进程继承
CREATE_NEW_CONSOLE,// 为新进程创建一个新的控制台窗体
NULL, // 使用本进程的环境变量
NULL, // 使用本进程的驱动器和文件夹
&si,
&pi );
if(bRet)
{
// 关掉不使用的句柄
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
getchar();
return 0;
}

上述程序运行后,会打开“记事本”程序。由于我们整个“主动防御”程序的设计就是环绕着CreateProcess()这个函数展开的。所以我们的样例也是以这个函数来解说的。

我们能够使用OD载入这个程序来看一下函数调用位置处的语句:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />

图1

能够看到,当一系列的push让參数入栈后,程序调用了call语句来调用kernel32.dll中的CreateProcess()函数。事实上call语句要实现两个功能。首先是向栈中压入当前指令在内存中的位置,即利用push语句保存返回地址。之后是利用jmp语句跳转到所调用函数的入口处。能够先单步运行到上图中的call语句处,然后按下F7步入这个call:

图2

此时程序跳到了0x7C80236B的位置,也就是CreateProcess()真正实现的代码位置。通过观察当前内存中的载入状态,可知该地址位于kernel32.dll中:

图3

能够总结一下,当我们所编写的exe文件须要调用CreateProcess()函数的时候,会将kernel32.dll载入内存(事实上无论运行什么程序,kernel32.dll一般都会自己主动载入内存),然后调用该动态链接库中的CreateProcess()函数(事实上真正调用的是CreateProcessA或CreateProcessW)。由于真正的CreateProcess()函数的实如今kernel32.dll模块中。这里所说的调用,事实上能够理解为直接跳到该函数的地址去运行。

基于这些知识,我们全然能够改动API函数在内存中的映像,从而实现对API函数的钩取。

详细来说就是直接使用汇编指令jmp来改变代码的运行流程,先不让它跳到0x7C80236B的位置,而是跳到我们自己编写的代码处运行,在运行完我们自己的代码后,再决定是否让它再跳到0x7C80236B继续运行。

那么如今的问题是应该怎样构造jmp语句。

事实上关于这个问题,我在《缓冲区溢出分析》系列的课程中,以前讲过一个“jmp back”的方法,这里的方式与它是一样的。最好还是先来看一下。我们的程序中的jmp语句的特点。进入main函数后,正好就有一个jmp:

图4

程序通过这个jmp。就来到的main函数的真正位置。这里分析一下位于0x00401005处的反汇编代码,是“E9 06000000”。

当中的“E9”就是jmp对应的机器码,后面的“06000000”事实上是一个跳转偏移,偏移的计算公式例如以下:

jmp后的偏移值 = 目的地址 – 当前地址 - 5

公式中减去5,是由于jmp指令进行跳转。须要五个字节实现。那么针对于我们当前的这个程序。结合上图,目的地址为0x00401010。当前地址为0x00401005。即:

jmp后的偏移值 = 0x00401010 - 0x00401005 – 5 = 6

也就是“E9”后面的“06”的由来。经过上述分析可知。我们仅仅要在欲钩取的函数位置处,改动前五个字节为我们的jmp语句。使其跳向我们自己的函数位置就能够了。

由于这样的方法是在程序的流程中直接进行嵌入jmp指令来改变流程的,所以就把它叫做Inline Hook。

DLL注入的基本方法

我们希望被钩取的函数所在的程序,在每次调用CreateProcess()的时候,都能够主动运行我们的jmp语句,这就须要我们对目标程序的功能进行扩展。这能够通过让目标程序载入我们编写的DLL来完毕。那么为了让目标程序能够载入DLL。就须要利用DLL注入的方法来实现。

详细到我们所要编写的主动防御程序,事实上函数钩取,也就是Hook CreateProcess()功能,是通过一个DLL程序来实现的,而我们的主函数的作用,就是将DLL注入到对应的进程中,当停止监控时,再将DLL卸载。

DLL的注入与卸载是一系列严格的流程,注入的流程例如以下:

1、OpenProcess获得要注入进程的句柄

2、VirtualAllocEx在远程进程中开辟出一段内存,长度为strlen(dllname)+1;

3、WriteProcessMemory将Dll的名字写入第二步开辟出的内存中。

4、CreateRemoteThread将LoadLibraryA作为线程函数。參数为Dll的名称,创建新线程

5、CloseHandle关闭线程句柄

卸载的流程例如以下:

1、CreateRemoteThread将GetModuleHandle注入到远程进程中。參数为被注入的Dll名

2、GetExitCodeThread将线程退出的退出码作为Dll模块的句柄值。

3、CloseHandle关闭线程句柄

3、CreateRemoteThread将FreeLibraryA注入到远程进程中,參数为第二步获得的句柄值。

4、WaitForSingleObject等待对象句柄返回

5、CloseHandle关闭线程及进程句柄。

事实上上述流程的道理并不难,而我们下次课程中的程序的编写,就根据上述流程来进行。

对系统进行全局监控

在Ring3层。我们经常使用的对系统进行全局监控的方式是使用Windows的全局钩子。在操作系统中安装全局钩子以后,仅仅要目标进程符合我们所设定的条件,全局钩子的DLL文件会被操作系统自己主动或强行地载入到该进程中。而DLL文件存放的正是钩子函数的代码,也即我们想要钩取实现的功能。

可见,这样的钩子须要使用DLL注入的方式来实现。假设使用这样的方式,我们须要使用SetWindowsHookEx()函数来进行钩子的设置。并将该函数的第一个參数设置为WH_GETMESSAGE,即监视被投递到消息队列中的消息。

那么关于这样的方式的详细实现方法,由于并非我们讨论的重点,因此大家能够參考对应的资料。

我们这次所採用的是一种比較简单的办法,并不须要进行全局监控。而是监控explorer.exe进程。

事实上绝大多数的进程都是由explorer.exe进程创建的。比方我们打开ProcessExplorer。然后运行一下我们上面所编写的程序:

图5

可见在explorer.exe进程下有非常多的子进程,而我们的CreateProcessTest.exe正是其子进程的一员,包括由该程序所启动的notepad.exe程序。因此仅仅要我们对该进程进行监控,钩取其CreateProcess()函数。就能够达到我们想要实现的监控目的。当然,使用这样的方式并不见得能够起到绝对的监控效果。毕竟方法还是太简单了,可是基本能够达到我们希望的效果。

小结

这次我们讨论了基于Ring3层的主动防御技术的基本原理。我们讨论的都是最简单的方式。希望大家能够弄清楚这次我们所解说的每个知识。这样在下次的编程实现中,才不会有困惑。

病毒木马查杀实战第020篇:Ring3层主动防御之基本原理的更多相关文章

  1. 病毒木马查杀实战第025篇:JS下载者脚本木马的分析与防御

    前言 这次我与大家分享的是我所总结的关于JS下载者脚本木马的分析与防御技术.之所以要选择这样的一个题目,是因为在日常的病毒分析工作中,每天都会遇到这类病毒样本,少则几个,多则几十个(当然了,更多的样本 ...

  2. 病毒木马查杀实战第011篇:QQ盗号木马之专杀工具的编写

    前言 由于我已经在<病毒木马查杀第004篇:熊猫烧香之专杀工具的编写>中编写了一个比较通用的专杀工具的框架,而这个框架对于本病毒来说,经过简单修改也是基本适用的,所以本文就不讨论那些重叠的 ...

  3. 病毒木马查杀实战第010篇:QQ盗号木马之十六进制代码分析

    前言 按照我的个人习惯,在运用诸如IDA Pro与OllyDBG对病毒进行逆向分析之前,我都会利用一些自动化的工具,通过静态或动态的分析方法(参见<病毒木马查杀第008篇:熊猫烧香之病毒查杀总结 ...

  4. 病毒木马查杀实战第009篇:QQ盗号木马之手动查杀

    前言 之前在<病毒木马查杀第002篇:熊猫烧香之手动查杀>中,我在不借助任何工具的情况下,基本实现了对于"熊猫烧香"病毒的查杀.但是毕竟"熊猫烧香" ...

  5. 病毒木马查杀实战第015篇:U盘病毒之脱壳研究

    前言 因为我们的终于目标是编写出针对于这次的U盘病毒的专杀工具.而通过上次的分析我们知道,病毒有可能在不同的计算机中会以不同的名称进行显示.假设真是如此,那么就有必要在此分析出病毒的命名规律等特征,然 ...

  6. 病毒木马查杀实战第021篇:Ring3层主动防御之编程实现

    前言 我们这次会依据上次的内容,编程实现一个Ring3层的简单的主动防御软件.整个程序使用MFC实现,程序开始监控时,会将DLL程序注入到explorer.exe进程中,这样每当有新的进程创建,程序首 ...

  7. 病毒木马查杀实战第017篇:U盘病毒之专杀工具的编写

    前言 经过前几次的讨论,我们对于这次的U盘病毒已经有了一定的了解,那么这次我们就依据病毒的行为特征,来编写针对于这次U盘病毒的专杀工具. 专杀工具功能说明 因为这次是一个U盘病毒,所以我打算把这次的专 ...

  8. 病毒木马查杀实战第022篇:txt病毒研究

    前言 反病毒爱好者们非常喜欢讨论的一个问题就是,现在什么样的病毒才算得上是主流,或者说什么样的病毒才是厉害的病毒呢?我们之前的课程所解说的都是Ring3层的病毒.所以有些朋友可能会觉得.那么Ring0 ...

  9. 病毒木马查杀实战第013篇:一个基于.NET的“敲竹杠”病毒研究

    前言 恶意程序发展至今,其功能已经从最初的单纯破坏,不断发展为隐私的窥探,信息的盗取,乃至如今非常流行的"敲竹杠"病毒,用于勒索.可见随着时代的发展,病毒的作者们往往也是想利用自己 ...

随机推荐

  1. hdu6153 poj3336强化版kmp+线性dp

    发现很早以前用exkmp做过一次,但是对这题来说只要将两个串翻转一下即可转换成s2的所有前缀出现的问题 /* 给出s1,s2,求s2的每个后缀在s1中出现的次数 ans = sum{后缀长度*出现次数 ...

  2. 使用共同函数,将PNotify弹出提示框公用

    PNotify(http://sciactive.github.io/pnotify/)是个不错的jquery库(好像最新版的pnotify已不需要jquery了). 使用它来实现网站常用的提示框,很 ...

  3. jenkins X实践系列(2) —— 基于jx的DevOps实践

    jx是云原生CICD,devops的一个最佳实践之一,目前在快速的发展成熟中.最近调研了JX,这里为第2篇,使用已经安装好的jx来实践CICD,旨在让大家了解基于jx的DevOps是如何运转的,感兴趣 ...

  4. 用HTML+CSS画出一个同心圆

    参加web前端校招的同学们经常会遇到这样的面试题:用HTML+CSS画出一个同心圆. 例如: 这道题主要考验的是基础盒模型布局能力和倒圆角属性的巧用. 1.html代码 <body> &l ...

  5. 【Java】 剑指offer(44) 连续子数组的最大和

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 数字以0123456789101112131415…的格式序列化到 ...

  6. 【Java】 剑指offer(68) 树中两个结点的最低公共祖先

      本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 输入两个树结点,求它们的最低公共祖先. 思路 该题首先要和面试 ...

  7. 我的Java自学之路

    其实在转正之后我就想抽个时间好好的梳理一下我的 Java 上车之路 ,但是一直拖到现在 ,因为有学弟问到 ,所以也就给了我动力 .毕竟答应了人家的事要做到 . 首先要有相应的背景介绍 ,不然说个毛线啊 ...

  8. js算法初窥06(算法模式03-函数式编程)

    在解释什么是函数式编程之前,我们先要说下什么是命令式编程,它们都属于编程范式的一种.命令式编程其实就是一块一块的代码,其中包括了我们要执行的逻辑或者判断或者一些运算.也就是按部就班的一步一步完成我们所 ...

  9. xss总结--2018自我整理

    0x00前言 因为ctf中xss的题目偏少(因为需要机器人在后台点选手的连接2333),所有写的比较少 这里推荐个环境http://test.xss.tv/ 0x01xss作用 常见的输出函数:pri ...

  10. jQuery Ajax -附示例

    jQuery其实就是一个JavaScript的类库,其将复杂的功能做了上层封装,使得开发者可以在其基础上写更少的代码实现更多的功能. jQuery 不是生产者,而是大自然搬运工. jQuery Aja ...