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

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"和"PE"之间的距离 , 是否有
DOS prompt等), 我们可以很容易的确定, 并通过引用很快找到入口(在头部插空的病毒体积一般不大).
2.节与节之间找空隙, 通过Fi或Winhex看一下节与节之间零的个数, 如果没有零的 就比较可疑
了,进行其他判断.


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


一个例子:
调用main函数之前会调用 实现写好的一个函数 函数没有参数 只是输出一句话
#include <stdio.h>
#include <math.h>
void hijack_cinit()
{
	printf("first running,hijack __cinit function\n");
}


 
int g_a;
int main(int argc, char* argv[])
{
	g_a = 100;
	//double f = sin(g_a);
	printf("Hello World! %f\n",g_a);
	
	hijack_cinit();
	getchar();
	return 0;
}



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


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




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


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


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


我们想办法得到这个地址,然后减去vftable的固定偏移 就得到imagebase(RVA)
例子:
char globalArrayA[5] = { 'A', 'A', 'A', 'A', 'A'};
char globalArrayB[5] = { 'B', 'B', 'B', 'B', 'B'};


//模拟信息泄露
void InfoLeak_Address()
{
	printf("I'm Calling to InfoLeak Functions\n");


	char *pointer = (char *)globalArrayA;
	CExploit *exp = new CExploit;




	printf("string:%s len is %d\n",pointer,strlen(pointer));
	//模拟任意地址写漏洞,修改终止符,可以越界访问数组后面的数据
	*(pointer + 5) = 0x1;
	*(pointer + 6) = 0x1;
	*(pointer + 7) = 0x1;


	printf("string:%s len is %d\n",pointer,strlen(pointer));


	//构造对象,放置在数组globalArrayA的后方
	memcpy(pointer + 5 + 1, (void *)exp, sizeof(CExploit));




	//越界读取
	DWORD dwModuleoffset = *(DWORD *)&globalArrayA[6];
	/*
		.textbss:00401000 ; Input MD5   : 1626AC6E19A9D1D153E3980CE98838F2
		.textbss:00401000 ; Input CRC32 : 7237D060
		.textbss:00401000
		.textbss:00401000 ; File Name   : F:\work\code\TestCode\Exploit-study\Debug\Exploit-study.exe
		.textbss:00401000 ; Format      : Portable executable for 80386 (PE)
		.textbss:00401000 ; Imagebase   : 400000
		.textbss:00401000 ; Section 1. (virtual address 00001000)
		.textbss:00401000 ; Virtual size                  : 00010000 (  65536.)
		.textbss:00401000 ; Section size in file          : 00000000 (      0.)
		.textbss:00401000 ; Offset to raw data for section: 00000000
		.textbss:00401000 ; Flags E00000A0: Text Bss Executable Readable Writable
		.textbss:00401000 ; Alignment     : default
		.textbss:00401000 ; PDB File Name : F:\work\code\TestCode\Exploit-study\Debug\Exploit-study.pdb


		相对模块起始地址的偏移
		rdata:00423B1C ; const CExploit::`vftable'
		.rdata:00423B1C ??_7CExploit@@6B@ dd offset j_CExploit__GetPointer
		.rdata:00423B1C                                         ; DATA XREF: CExploit__CExploit+2Eo
		.rdata:00423B1C                                         ; CExploit___CExploit+26o
		.rdata:00423B20                 dd offset j_CInterface__SetPointer
		.rdata:00423B24                 dd offset j_CExploit__SetPointer
		.rdata:00423B28                 db    0
		.rdata:00423B29                 db    0
		.rdata:00423B2A                 db    0
		.rdata:00423B2B                 db    0
	
	*/
	//比较magic number ,如果一直说明是我们放置的对象,读取虚函数表,计算出模块的具体偏移
	if (0x99978456 == *(DWORD *)&globalArrayA[6 + 4])
	{
		//每个编译器编译出来的偏移可能都不一样,所以需要自己选择一个偏移
		// VS2012 V120 + WIN7 X86 => 0x23b1c
		// VS2012 V110 + WIN7 X64 => 0x1d98c
		dwModuleoffset -= 0x1d98c;  // ===> (const CExploit::`vftable')00423B1C - 400000 = 0x23b1c


		printf("info leak of address is 0x%x\n", dwModuleoffset);


		printf("GetModuleHandle:Getting address as current module is 0x%x\n\n", (DWORD)GetModuleHandle(NULL));
	}	
	delete exp;


}




CWinApp theApp;
using namespace std;




class CInterface
{
public:
	int m_flag;
	CInterface::CInterface(){ m_flag = 0x99978456;/*it's my magic number*/ }
	virtual int GetPointer(){ return m_flag; };
	virtual int SetPointer(){ return m_flag; };
	int TellMe(){ printf("Tell me ,What is flag ? %d\n", m_flag); return m_flag; }
};


class CExploit : public CInterface
{
public:
	int m_pointer;
	int m_calc;
	char *pszArray;
	CExploit::CExploit() :m_pointer(0x7fffffff),pszArray(0){}
	CExploit::~CExploit() { if (pszArray) delete pszArray; }
	virtual int GetPointer() { return m_pointer; }
	virtual int SetPointer(int pointer){ m_pointer = pointer; return m_pointer; }
	int Calc(int x) { m_pointer = x * 0x79865 + 0x10111 - 0x7d63d; return m_pointer; }
	int CExpAllocBuff(int x){ m_pointer = Calc(x); pszArray = (char *)malloc(m_pointer); }


};





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


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


最后使用NULL调用MoveFile移动文件到c:\recycle\12234.tmp,从而删除了c:\recycle\12234.tmp。

你可能感兴趣的:(epo,ASLR,病毒原理)