一个感染U盘木马分析
本帖最后由 安静的小酒吧 于 2016-4-12 18:28 编辑一个感染U盘木马分析——IDAPython 让生活更美好样本名称:jusched.exe样本类型:Win32;木马;感染U盘样本大小:210KbMD5:F24F0FBDB38CF52E18EC9C899990B53ESHA-1:1C0CC3C1255E295958D310C269267D66169BAF36
0x00 前言前两天在一台内网机器上插U盘,发现U盘多出了很多用U盘里面的文件夹名称命名的exe文件,很显然是被感染了,然后就着手分析,发现样本并不复杂,但是所有的API函数地址都是动态获取然后保存在全局变量,同时所有的字符串都是加密的,这让我想起来了在Freebuf看到的一篇文章《IDAPython:让你的生活更美好》,使用IDAPython脚本批量解密并重命名函数名称,会让工作简单很多。0x01 加密算法分析(1)样本中共有两种字符串加密方式,一种是针对API函数名称和动态链接库名称的,这种是一个字符串内部字符顺序调整的加密算法,我也不知道该归为哪一类。
加密字符串和动态获取API地址IDA F5后的代码如下 int __cdecl sub_401180(int a1, char *a2, char a3){
char v4; // @1
int j; // @9
int k; // @1
int v7; // @9
int i; // @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;
}
*(_BYTE *)(v7 + a1) = 0;
}
else
{
v7 = 0;
for ( j = 3; j >= 0; --j )
{
for ( k = j; k < i; k += 4 )
*(_BYTE *)(k + a1) = a2;
}
*(_BYTE *)(v7 + a1) = 0;
}
return sub_401046();
}
Python改写后如下(代码略丑,勿喷):def decstr(decs,flag):
k=len(decs)
slen=k
tmp=0
plain=
if flag==1:
j=3
while j>=0:
k=j
while k<slen:
plain=decs
tmp+=1
k+=4
j-=1
else:
j=3
while j>=0:
k=j
while k<slen:
plain=decs
tmp+=1
k+=4
j-=1
return ''.join(plain)
看一下这个代码调用关系:.text:0040227E push 0 ;区分加密还是解密,0加密,1解密
.text:00402280 push offset aMlnatuaeedhlgo ; "MlnAtuaeedHlGoed"
.text:00402285 lea eax,
.text:00402288 push eax ; int
.text:00402289 call sub_401041;解密函数
.text:0040228E add esp, 0Ch
.text:00402291 mov esi, esp
.text:00402293 lea ecx,
.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那篇文章写得)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)
dec=decstr(s,ref)
print dec
MakeComm(x.frm, dec)
rename_func(x.frm,dec+'_r')
解密前:
解密后: 重命名前:
重命名后:
0x02 加密算法分析(2)除了前一种针对API函数名称加密的算法外,另外一种是针对其他普通字符串加密的,该加密算法是一个类似rot13的加密算法,只是这里扩展到了94个可见字符。IDA F5的算法如下:char *__cdecl sub_401580(int a1, char *a2)
{
char *result; // eax@1
char v3; // @1
int j; // @3
char *i; // @1
char *v6; // @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 )
{
result = &i;
i = byte_4270A0[(j + 47) % 94];
break;
}
result = (char *)(j + 1);
}
if ( j == 94 )
{
result = &a2[(_DWORD)i];
i = a2[(_DWORD)i];
}
}
i = 0;
return result;
}
使用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:
out+=table[(j+47)%94]
break
return out由于这些字符串是全局使用的字符串,每次都经过解密,为了方便阅读反编译代码直接在与字符串上面patch即可,分析时忽略解密函数,该过程于上面类似也需要以下几步:(1) 通过交叉引用定位解密函数的参数(2) 通过字符串参数地址获取字符串并解密(3) Patch被加密的字符串完整代码如下: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:
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)
print s,
dec=decstr(s)
print dec
pathstr(ref,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):FreeBuf关于IDAPython:http://www.freebuf.com/sectool/92107.html lpplite 发表于 2016-4-18 14:44
不错。但是最可恨的U盘病毒,是导致U盘无法使用的那种。格式化也不行,量产也没用的。那种病毒才是太气人了 ...
这种我倒是没见过,有样本的话能不能给一个我尝试看看? 安静的小酒吧 发表于 2016-4-18 22:50
这种我倒是没见过,有样本的话能不能给一个我尝试看看?
我不懂技术。。。现在有的只有一两个已经不能使用的U盘了。病毒我是抓不到的。{:1_908:} 留名~ 感谢分享~ 学习下,支持楼主 记得以前学校机房都是这种木马,不过在它发作时我就截下它的.ini文件。好像就是一个脚本! 学习学习,谢谢分享! 吾爱破解论坛 www.52pojie.cn
这玩意看不懂啊 谢谢分享,过程写的很详细。 IDA Python写的66哒,求带! dingk 发表于 2016-4-15 10:20
IDA Python写的66哒,求带!
freebuf那个系列不错,还有那个IDAPythonbook 有查杀这样的病毒的软件就好了,经常碰到这个病毒