本帖最后由 安静的小酒吧 于 2016-4-12 18:28 编辑
一个感染U盘木马分析 样本名称:jusched.exe 样本类型:Win32;木马;感染U盘 样本大小:210Kb MD5:F24F0FBDB38CF52E18EC9C899990B53E SHA-1:1C0CC3C1255E295958D310C269267D66169BAF36
0x00 前言 前两天在一台内网机器上插U盘,发现U盘多出了很多用U盘里面的文件夹名称命名的exe文件,很显然是被感染了,然后就着手分析,发现样本并不复杂,但是所有的API函数地址都是动态获取然后保存在全局变量,同时所有的字符串都是加密的,这让我想起来了在Freebuf看到的一篇文章《IDAPython:让你的生活更美好》,使用IDAPython脚本批量解密并重命名函数名称,会让工作简单很多。 0x01 加密算法分析(1) 样本中共有两种字符串加密方式,一种是针对API函数名称和动态链接库名称的,这种是一个字符串内部字符顺序调整的加密算法,我也不知道该归为哪一类。
加密字符串和动态获取API地址 IDA F5后的代码如下 [C] 纯文本查看 复制代码 int __cdecl sub_401180(int a1, char *a2, char a3)[/font][/align][font=微软雅黑]{
char v4; // [sp+Ch] [bp-50h]@1
int j; // [sp+4Ch] [bp-10h]@9
int k; // [sp+50h] [bp-Ch]@1
int v7; // [sp+54h] [bp-8h]@9
int i; // [sp+58h] [bp-4h]@1
memset(&v4, 0xCCu, 0x50u);
k = strlen(a2);
for ( i = 0; i < k + 1 && a2 && a2 != 10 && a2 != 13; ++i )
;
if ( a3 == 1 )
{
v7 = 0;
for ( j = 3; j >= 0; --j )
{
for ( k = j; k < i; k += 4 )
*(_BYTE *)(v7++ + a1) = a2[k];
}
*(_BYTE *)(v7 + a1) = 0;
}
else
{
v7 = 0;
for ( j = 3; j >= 0; --j )
{
for ( k = j; k < i; k += 4 )
*(_BYTE *)(k + a1) = a2[v7++];
}
*(_BYTE *)(v7 + a1) = 0;
}
return sub_401046();
}
Python改写后如下(代码略丑,勿喷): [Python] 纯文本查看 复制代码 def decstr(decs,flag):
k=len(decs)
slen=k
tmp=0
plain=[0x0 for i in range(slen)]
if flag==1:
j=3
while j>=0:
k=j
while k<slen:
plain[tmp]=decs[k]
tmp+=1
k+=4
j-=1
else:
j=3
while j>=0:
k=j
while k<slen:
plain[k]=decs[tmp]
tmp+=1
k+=4
j-=1
return ''.join(plain)
看一下这个代码调用关系: [Asm] 纯文本查看 复制代码 .text:0040227E push 0 ;区分加密还是解密,0加密,1解密
.text:00402280 push offset aMlnatuaeedhlgo ; "MlnAtuaeedHlGoed"
.text:00402285 lea eax, [ebp+ModuleName]
.text:00402288 push eax ; int
.text:00402289 call sub_401041;解密函数
.text:0040228E add esp, 0Ch
.text:00402291 mov esi, esp
.text:00402293 lea ecx, [ebp+ModuleName]
.text:00402296 push ecx ; _DWORD
.text:00402297 mov edx, dword_42CC90
.text:0040229D push edx ; _DWORD
.text:0040229E call dword_43A17C;GetProcAddress()
.text:004022A4 cmp esi, esp
.text:004022A6 call __chkesp
.text:004022AB mov dword_43A188, eax;将获取到的函数地址保存
多以IDAPython的脚本就分一下几个部分: 1. 通过交叉引用关系找到所有对sub_401041函数的调用 2. 找出sub_401041函数的待解密字符串这个参数对应的字符串并解密 3. 添加注释(方便代码阅读) 4. 重命名通过上面字符串获得的API函数地址保存的全局变量名称 (下面很多代码都是模仿FreeBuf那篇文章写得) [Python] 纯文本查看 复制代码 def find_function_arg(addr):#获取解密函数的参数
while 1:
addr=idc.PrevHead(addr)
addr=idc.PrevHead(addr)
addr=idc.PrevHead(addr)
if GetMnem(addr)=="push":
opv=GetOperandValue(addr, 0)
addr=idc.PrevHead(addr)
flag=GetOperandValue(addr,0)
return (opv,flag)
return ""
def get_string(addr):#通过字符串地址获得字符串
out=""
while 1:
c=chr(Byte(addr))
if c in string.printable:
out+=c
else:
break
addr+=1
return out
def rename_func(addr,fname):#重命名
count=0
while 1:
count+=1
addr=idc.NextHead(addr)
if GetOperandValue(addr,0)==0x0043A17C:
while 1:
count+=1
addr=idc.NextHead(addr)
if GetMnem(addr)=="mov" and GetOpnd(addr,1)=="eax":
addr=GetOperandValue(addr,0)
idc.MakeName(addr,fname)
break
break
if count==20:
break
for x in XrefsTo(0x00401041,flags=0):
ref=find_function_arg(x.frm)
if ref!="":
s=get_string(ref[0])
dec=decstr(s,ref[1])
print dec
MakeComm(x.frm, dec)
rename_func(x.frm,dec+'_r')
解密前:
解密后: 重命名前:
重命名后:
0x02 加密算法分析(2) 除了前一种针对API函数名称加密的算法外,另外一种是针对其他普通字符串加密的,该加密算法是一个类似rot13的加密算法,只是这里扩展到了94个可见字符。IDA F5的算法如下: [C] 纯文本查看 复制代码 char *__cdecl sub_401580(int a1, char *a2)
{
char *result; // eax@1
char v3; // [sp+Ch] [bp-4Ch]@1
int j; // [sp+4Ch] [bp-Ch]@3
char *i; // [sp+50h] [bp-8h]@1
char *v6; // [sp+54h] [bp-4h]@1
memset(&v3, 0xCCu, 0x4Cu);
result = (char *)strlen(a2);
v6 = result;
for ( i = 0; (signed int)i < (signed int)v6; ++i )
{
for ( j = 0; j < 94; ++j )
{
if ( a2[(_DWORD)i] == byte_4270A0[j] )
{
result = &i[a1];
i[a1] = byte_4270A0[(j + 47) % 94];
break;
}
result = (char *)(j + 1);
}
if ( j == 94 )
{
result = &a2[(_DWORD)i];
i[a1] = a2[(_DWORD)i];
}
}
i[a1] = 0;
return result;
}
使用Python改写后如下: [Python] 纯文本查看 复制代码 table=' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghijklmnopqrstuvwxyz{|}~'
def decstr(decs):
k=len(decs)
out=''
for i in range(k):
for j in range(len(table)):
if decs==table[j]:
out+=table[(j+47)%94]
break
return out 由于这些字符串是全局使用的字符串,每次都经过解密,为了方便阅读反编译代码直接在与字符串上面patch即可,分析时忽略解密函数,该过程于上面类似也需要以下几步: (1) 通过交叉引用定位解密函数的参数 (2) 通过字符串参数地址获取字符串并解密 (3) Patch被加密的字符串 完整代码如下: [Python] 纯文本查看 复制代码 import idc
import idaapi
import string
table=' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghijklmnopqrstuvwxyz{|}~'
def decstr(decs):
k=len(decs)
out=''
for i in range(k):
for j in range(len(table)):
if decs==table[j]:
out+=table[(j+47)%94]
break
return out
def find_function_arg(addr):
while 1:
addr=idc.PrevHead(addr)
addr=idc.PrevHead(addr)
addr=idc.PrevHead(addr)
if GetMnem(addr)=="push":
opv=GetOperandValue(addr, 0)
addr=idc.PrevHead(addr)
flag=GetOperandValue(addr,0)
return (opv,flag)
return ""
def get_string(addr):
out=""
while 1:
c=chr(Byte(addr))
if c in table:
out+=c
else:
break
addr+=1
return out
def pathstr(addr,pstr):
for i in pstr:
idc.PatchByte(addr,ord(i))
addr+=1
for x in XrefsTo(0x40104B,flags=0):
ref=find_function_arg(x.frm)
if ref!="":
s=get_string(ref[0])
print s,
dec=decstr(s)
print dec
pathstr(ref[0],dec) Patch前:
Patch后:
0x03 样本结构分析 经过上面的处理之后样本的反编译代码可读性非常高了,样本功能大概分为以下几部分: 1. 自身环境判断 2. 驻留进系统 3. 回连 4. 感染U盘 样本不是很复杂,功能也很传统,就不一一列举每个功能了,感兴趣可以自己分析。 0x04 查杀方法 该样本没有很复杂的隐藏手段查杀的话下面两步就可以: 1. 在任务管理器里面结束jusched.exe这个进程 2. 在C:\Program Files (x86)\目录里面搜索jusched.exe,它是被保存在一个用随机的16进制数命名的目录里,删除就好了。 0x05 小结 1. 只是为了尽快清除木马,所以没有做太细致的分析,不正确之处还请指正 2. IDAPython可以让生活更美好 样本文件、idb文件、Python脚本(解压密码:52pojie):
样本.7z
(257.73 KB, 下载次数: 93)
FreeBuf关于IDAPython:http://www.freebuf.com/sectool/92107.html |