看流星社区

 找回密码
 注册账号
查看: 1841|回复: 0

病毒分析,病毒原理,ASLR,DEP,EPO

[复制链接]

该用户从未签到

发表于 2017-6-1 12:52:21 | 显示全部楼层 |阅读模式
1.病毒分析的基本工具方法
整体的思维:利用一些工具来监控程序调用了哪些API或行为
~1.对于监控API
~~1.apilog18pub http://www.softpedia.com/get/Programming/Other-Programming-Files/API-Logger.shtml
~~2.Api Monitor x86 x64都有 (强力推荐!!!)
~2.使用行为监控工具
~~1.SysTracer(已有x64版 但不推荐)

~~2.ProcMon
~~3.网络的可以使用Smsniff x86 x64 或 SockMon
知道大体的行为,就可以下断 + 静态慢慢分析了


1 先整体,后局部,从文件对比入手(文件大小,ultral Edit 简单对比,IDA bindiff 流程观察)。


2 观察PE文件各个“段结构”,“大小”,“属性”(PEinfo 等对比工具)。


3 明确待分析文件是由什么编程工具编写,查明EOP异同点。


4 当简单分析后,无法发现明显代码流程劫持的情况下,可以特别关一
下这几个API的调用的变化,exit, _exit, ExitProcess。


5 碰到混淆的的循环功能代码时,要多从大的文件感染特性上判断,找
思路。


6 分析时,主要观察内存变化(段的属性,是否被重新填充),寄存器的变化,eax ,ecx,esi,edi 。




EPO:
http://dudeaap.bokee.com/5535366.html
1. EPO存在的意义
EPO 是为了对付杀毒软件的启发式查毒而诞生的。原先病毒只是简单地修改PE的入口,他很容易被杀
毒软件的启发式扫描识别,然后被杀。但杀毒软件不可能对PE文件的所有部分进行启发式扫描,大部分只
是对PE入口处的代码进行启发式检查。EPO正是利用了这一点,通过把入口置于PE中某个不不显眼的位置
,来减少被查杀的可能性。
除了对抗反病毒产品的检测外,由于其执行时的随机性,对反静态分析和反动态跟踪都有影响。


2. 如果病毒的代码不从入口处开始, 那它一定是入口模糊技术(EntryPoint Obscuring 简写EPO)吗?


这要看对EPO进行怎样的精确定义。
通过引入表感染的病毒就比较边缘化,把它归到EPO中或常规的感染中都不太合适,但都有理由。但
是,实际上,它确实也不修改被感染入口以及入口处代码。
3. 病毒的入口在什么地方?
首先总结一下病毒入口可能隐藏的地方:
1. JMP ( JuMP ) 无条件转移指令。
2. 条件转移指令:JZ , JS , JO , JP , JC , JA ......。
3. LOOP(LOOP)循环指令。
4. CALL (CALL) 过程调用指令。
5. RET(RETurn)子程序返回指令 ,入口存放在堆栈中。
6. 直接修改EIP寄存器 , 没见过这样的直接修改的。
暂时还没有发现其他的可用于隐藏入口的指令。集思广益,看看还有其他的吗?


这5类的范围比较大, 对于隐藏比较浅的病毒 可以在入口附近对上面的指令进行检查,找到入口。但
对于隐藏的比较深的,需要其他的办法。
4. 病毒的代码(body)在什么地方?通过代码反推入口.
如果我们能找到病毒的代码, 通过反推 , 我们可以通过对这些代码的引用找到入口。
1. 通过节的属性确定


PE结构中数据是分节存放的,每一个节都有其属性,我们只关心具有可执行属性的节。节的属性在
节表的0x16h处, 他的高位可能是IMAGE_SCN_MEN_EXECUTE (0x20000000h) , IMAGE_SCN_MEM_READ
(0x40000000h) 或IMAGE_SCN_MEM_WRITE(0x80000000h) 的组合。我们只关心组合为20000000(理论上不
存在),60000000,A0000000(理论上不存在),E0000000(重点关注。。。)。 其实只有两种可能性
。如果是E0000000我们基本上可以所定病毒的所在节了。 s
资源节中的代码和overlay中的代码是否有执行权限还需要证明。有兴趣的话可以去做实验。
2. 确定代码的位置。


病毒代码的位置又分为连续和不连续两种。
1. 连续代码 位置无外乎开头(Prepending)、结尾(Appending)、中间(Overwriting or
Shifting), 我们重点关注的是开头和结尾。病毒代码的特点是大段代码中你找不到API函数的直接调用
,找不到绝对的跳转都是相对的转移,引用也只是小范围的。如果找到长距离的引用 ,那基本上就是病
毒入口附近了。还有没有其他方法去很快确定代码块呢?
2.不连续代码 分为两类在文件头找空隙和在节与节之间找空隙,但他们英文名相同:Cavity.


1.在文件头插空, 如果发现文件头很乱 而且没有压缩的痕迹("MZ"和&quotE"之间的距离 , 是否有
DOS prompt等), 我们可以很容易的确定, 并通过引用很快找到入口(在头部插空的病毒体积一般不大).
2.节与节之间找空隙, 通过Fi或Winhex看一下节与节之间零的个数, 如果没有零的 就比较可疑
了,进行其他判断.


正推和反推两种办法去帮助我们确定EPO病毒代码的入口.
5. 但EPO技术并不是完美的, 他有它的局限性. 数据和代码的区分, 指令被执行的可能性等等 .
需要克服这些问题的话病毒就需要带反汇编引擎, 引擎的功能越强大, 体积也相应的变大 , IDA的反
汇编引擎是最好的, 也是最大的. 平时没有见过EPO部分代码超过50行的, EPO的判断也是比较简单的 ,
最多就是判断一下前后的指令是否合法(本人才疏学浅 ). 隐藏比较深的 一般是搜索一些功能函数的入
口, 像大家比较熟悉的55 8B EC, 确定功能函数后再搜索一些转移指令等等. 遇到过的隐藏最深的是搜索
ExitProcess或ExitThread API函数的调用并进行修改 , 这就比较变态了 , 但是实现代码并不长.
EPO的对杀毒引擎有很大的影响,要想检测到并查全病毒是很难的, 没有很好的办法 (因为模式匹
配的过滤串的位置不定,必然会带来一定的搜索,从而 降低了引擎的效率)。


一个例子:
调用main函数之前会调用 实现写好的一个函数 函数没有参数 只是输出一句话
  1. #include <stdio.h>
  2. #include <math.h>
  3. void hijack_cinit()
  4. {
  5.         printf("first running,hijack __cinit function\n");
  6. }
  7. int g_a;
  8. int main(int argc, char* argv[])
  9. {
  10.         g_a = 100;
  11.         //double f = sin(g_a);
  12.         printf("Hello World! %f\n",g_a);
  13.        
  14.         hijack_cinit();
  15.         getchar();
  16.         return 0;
  17. }
复制代码


我们来修改一个地方的值
找到程序入口地址 向下拉 找到call __cinit 调试吧应该是j__cinit
找到call _initterm 上一句push offset xxxx
记录xxxxx


然后使用VA转FOA 到Hex定位到xxxx的文件偏移
修改为hijack_cinit函数的地址
然后保存 此时运行修改后的程序 发现先于main函数调用了hijack_cinit
原因:
  1. void __cdecl initterm(void (__cdecl **pfbegin)(), void (__cdecl **pfend)())
  2. {
  3.   while ( pfbegin < pfend )
  4.   {
  5.     if ( *pfbegin )
  6.       (*pfbegin)();                             // 传入一个边界,传入保存地址的指针  然后每次+4 去调用
  7.     ++pfbegin;
  8.   }
  9. }
复制代码



如果在xp下运行,这没有问题,因为XP EXE基地址都是固定的


Win7加入了地址随机化ASLR(准确来说应该是VC加入了地址随机化ASLR)
这就涉及信息泄露漏洞
思路:
比如一个数组有5个元素
数组有一个或多个终止符 一般是1或者3
如果我们将终止符破坏,就可以读到更多的信息


这是第一步,第二步我们需要一个记录了模块地址的地址
这是可以通过虚函数表来得到 vftable
在c++中 只要类中有虚函数,那么在某一个地方就会存放这个类的地址,这个地址就是我们需要的地址,它包含了imagebase


我们想办法得到这个地址,然后减去vftable的固定偏移 就得到imagebase(RVA)
例子:
  1. char globalArrayA[5] = { 'A', 'A', 'A', 'A', 'A'};
  2. char globalArrayB[5] = { 'B', 'B', 'B', 'B', 'B'};
  3. //模拟信息泄露
  4. void InfoLeak_Address()
  5. {
  6.         printf("I'm Calling to InfoLeak Functions\n");
  7.         char *pointer = (char *)globalArrayA;
  8.         CExploit *exp = new CExploit;
  9.         printf("string:%s len is %d\n",pointer,strlen(pointer));
  10.         //模拟任意地址写漏洞,修改终止符,可以越界访问数组后面的数据
  11.         *(pointer + 5) = 0x1;
  12.         *(pointer + 6) = 0x1;
  13.         *(pointer + 7) = 0x1;
  14.         printf("string:%s len is %d\n",pointer,strlen(pointer));
  15.         //构造对象,放置在数组globalArrayA的后方
  16.         memcpy(pointer + 5 + 1, (void *)exp, sizeof(CExploit));
  17.         //越界读取
  18.         DWORD dwModuleoffset = *(DWORD *)&globalArrayA[6];
  19.         /*
  20.                 .textbss:00401000 ; Input MD5   : 1626AC6E19A9D1D153E3980CE98838F2
  21.                 .textbss:00401000 ; Input CRC32 : 7237D060
  22.                 .textbss:00401000
  23.                 .textbss:00401000 ; File Name   : F:\work\code\TestCode\Exploit-study\Debug\Exploit-study.exe
  24.                 .textbss:00401000 ; Format      : Portable executable for 80386 (PE)
  25.                 .textbss:00401000 ; Imagebase   : 400000
  26.                 .textbss:00401000 ; Section 1. (virtual address 00001000)
  27.                 .textbss:00401000 ; Virtual size                  : 00010000 (  65536.)
  28.                 .textbss:00401000 ; Section size in file          : 00000000 (      0.)
  29.                 .textbss:00401000 ; Offset to raw data for section: 00000000
  30.                 .textbss:00401000 ; Flags E00000A0: Text Bss Executable Readable Writable
  31.                 .textbss:00401000 ; Alignment     : default
  32.                 .textbss:00401000 ; PDB File Name : F:\work\code\TestCode\Exploit-study\Debug\Exploit-study.pdb
  33.                 相对模块起始地址的偏移
  34.                 rdata:00423B1C ; const CExploit::`vftable'
  35.                 .rdata:00423B1C ??_7CExploit@@6B@ dd offset j_CExploit__GetPointer
  36.                 .rdata:00423B1C                                         ; DATA XREF: CExploit__CExploit+2Eo
  37.                 .rdata:00423B1C                                         ; CExploit___CExploit+26o
  38.                 .rdata:00423B20                 dd offset j_CInterface__SetPointer
  39.                 .rdata:00423B24                 dd offset j_CExploit__SetPointer
  40.                 .rdata:00423B28                 db    0
  41.                 .rdata:00423B29                 db    0
  42.                 .rdata:00423B2A                 db    0
  43.                 .rdata:00423B2B                 db    0
  44.        
  45.         */
  46.         //比较magic number ,如果一直说明是我们放置的对象,读取虚函数表,计算出模块的具体偏移
  47.         if (0x99978456 == *(DWORD *)&globalArrayA[6 + 4])
  48.         {
  49.                 //每个编译器编译出来的偏移可能都不一样,所以需要自己选择一个偏移
  50.                 // VS2012 V120 + WIN7 X86 => 0x23b1c
  51.                 // VS2012 V110 + WIN7 X64 => 0x1d98c
  52.                 dwModuleoffset -= 0x1d98c;  // ===> (const CExploit::`vftable')00423B1C - 400000 = 0x23b1c
  53.                 printf("info leak of address is 0x%x\n", dwModuleoffset);
  54.                 printf("GetModuleHandle:Getting address as current module is 0x%x\n\n", (DWORD)GetModuleHandle(NULL));
  55.         }       
  56.         delete exp;
  57. }
复制代码




  1. CWinApp theApp;
  2. using namespace std;
复制代码


  1. class CInterface
  2. {
  3. public:
  4.         int m_flag;
  5.         CInterface::CInterface(){ m_flag = 0x99978456;/*it's my magic number*/ }
  6.         virtual int GetPointer(){ return m_flag; };
  7.         virtual int SetPointer(){ return m_flag; };
  8.         int TellMe(){ printf("Tell me ,What is flag ? %d\n", m_flag); return m_flag; }
  9. };
  10. class CExploit : public CInterface
  11. {
  12. public:
  13.         int m_pointer;
  14.         int m_calc;
  15.         char *pszArray;
  16.         CExploit::CExploit() :m_pointer(0x7fffffff),pszArray(0){}
  17.         CExploit::~CExploit() { if (pszArray) delete pszArray; }
  18.         virtual int GetPointer() { return m_pointer; }
  19.         virtual int SetPointer(int pointer){ m_pointer = pointer; return m_pointer; }
  20.         int Calc(int x) { m_pointer = x * 0x79865 + 0x10111 - 0x7d63d; return m_pointer; }
  21.         int CExpAllocBuff(int x){ m_pointer = Calc(x); pszArray = (char *)malloc(m_pointer); }
  22. };
复制代码




木马另类删除文件的方法
如果直接拦截DeleteFile发现并没有断下来,它用的方法是:


创建一个回收站文件,例如:c:\recycle\12234.tmp,然后使用MoveFile将文件改为c:\recycle\12234.tmp,


最后使用NULL调用MoveFile移动文件到c:\recycle\12234.tmp,从而删除了c:\recycle\12234.tmp。
点击按钮快速添加回复内容: 支持 高兴 激动 给力 加油 苦寻 生气 回帖 路过 感恩
您需要登录后才可以回帖 登录 | 注册账号

本版积分规则

小黑屋|手机版|Archiver|看流星社区 |网站地图

GMT+8, 2024-3-19 18:36

Powered by Kanliuxing X3.4

© 2010-2019 kanliuxing.com

快速回复 返回顶部 返回列表