程序开始会动态获取函数地址
获取资源文件,通过RC4算法解密配置,获取 C2 服务器信息,注册表中的子键名称,木马功能的配置 licence内容等信息,互斥体名称 中间以 "丨" 分隔
内嵌了样本的版本号 此版本为remcos的最新版本 发布自2022/8/21
创建互斥体,名为Rmc-ALXH27
判断是否是64位程序,打开注册表,查询产品名称,最后返回 计算机信息(windows7 Ultimate 32bit)
通过RC4算法加密原样本的所在路径
加密后
将加密后的样本路径存放到注册表中 创建注册表键 键名为exepath
从资源段的解密的配置中返回916F25041F4C7DA3515196E496E1F826 创建注册表键,键名为licence
创建线程,记录键盘消息,线程回调,创建文件C:\ProgramData\remcos\logs.dat, 并写入当前时间,并使用 API SetWindowsHookExA 创建线程设置消息钩子,记录键盘鼠标的操作
记录键盘操作的文件
获取用户的信息
禁用DEP 数据执行保护
检查当前运行的权限
加载ws2_32.dll
循环分别获取 getaddrinfo getnameinfo freeaddrinfo
调用getaddrinfo函数根据ip地址和端口号获取sockaddr结构
利用sockaddr结构初始化socket和创建事件对象
发送加密后的数据
读取服务器发来的数据
创建线程 处理接收到的数据
远控指令如下:
指令 | 功能 |
---|---|
0x92 | 设置受感染主机桌面图片风格 |
0x94 | 修改指定窗口的标题 |
0x95 | 获得当前可用的物理和虚拟内存信息 |
0x97 | 通过dixdiag诊断工具获取系统信息 |
0x98 | 向C&C服务器上传文件或从C&C服务器下载文件 |
0x9E 0xA2 | 在%temp%下创建alarm.wav文件 并播放声音 |
0xAC | 在受感染的机器上弹出窗口 |
0x1 | 获取受感染主机最顶端程序标题 |
0x3 | 收集受感染主机所有已安装软件的相关信息 包括其软件供应商信息、版本信息、安装的路径信息、安装的日期、卸载字符串等 |
0x6 | 收集受感染主机所有正在运行的进程信息 |
0x7 | 结束指定进程 |
0x8 | 枚举窗口 |
0x9 | 关闭指定窗口 |
0xA 0xB | 显示/隐藏指定窗口 |
0xC | 获取指定窗口的PID |
0xD | 执行指定命令行命令 |
0xF | 可打开 文件,文件夹,网址等等 |
0x10 | 获取屏幕截图 |
0x11 | 关闭连接 |
0x12 | 获取键盘信息 |
0x13 | 启动在线键盘记录器 |
0x14 | 停止在线键盘记录器 |
0x15 0x16 | 读取指定文件并发送到C2 |
0x17 | 删除指定文件 |
0x18 | 清除IE、Firefox、Chrome等浏览器的登陆信息和cookie信息 |
0x1B | 控制受感染设备摄像头 |
0x1D | 开始录音 |
0x1E | 停止录音 |
0x20 | 删除指定文件 |
0x21 | 退出进程 |
0x22 | 清理自身在受感染机器上留下的痕迹 |
0x23 | 执行vbs脚本“caonvgbpgsuqbxegcnjfxv.vbs”来重启自身 |
0x24 0x25 | 下载数据并执行还会清除自身在用户机的痕迹 |
0x26 | 通过MessageBoxW函数弹窗显示信息 |
0x27 | 关闭系统,重启,注销用户 |
0x28 | 获取用户剪切板数据 |
0x29 0x2A | 清除剪切板内容 |
0x2B | 创建一个共享内存来共享数据 |
0x2C | 从指定的URL下载数据并将数据共享到创建的共享内存中 |
0x31 | 在注册表中保存用户名 |
YARA规则:
rule Remcos
{
strings:
$my_hex_string = {46 8B 44 B4 10 03 F8 81 E7 FF 00 00 80 79 08 4F 81 CF 00 FF FF FF 47}
$my_hex_string1 = {25 30 32 69 3A 25 30 32 69 3A 25 30 32 69 3A 25 30 33 69}
$my_string1 = "SETTINGS"
condition:
$my_hex_string and $my_hex_string1 and $my_string1
}
配置提取工具:
// ConfigExtract.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include
#include
#include
using namespace std;
void rc4_init(unsigned int* s, unsigned char* key, unsigned long Len) //初始化函数
{
int i = 0, j = 0;
char k[256] = { 0 };
unsigned char tmp = 0;
for (i = 0;i < 256;i++)
{
s[i] = i;
k[i] = key[i % Len];
}
for (i = 0; i < 256; i++)
{
j = (j + s[i] + k[i]) % 256;
tmp = s[i];
s[i] = s[j]; //交换s[i]和s[j]
s[j] = tmp;
}
}
std::vector split(const string& str, char pattern)
{
char* strc = new char[strlen(str.c_str()) + 1];
strcpy(strc, str.c_str());
std::vector resultVec;
char* tmpStr = strtok(strc, &pattern);
while (tmpStr != NULL)
{
resultVec.push_back(string(tmpStr));
tmpStr = strtok(NULL, &pattern);
}
delete[] strc;
return resultVec;
}
void print(const char* key, const char* value)
{
if (value[0] == 0x1e)
{
return;
}
printf("%s: ", key);
for (int i = 0; i < strlen(value); i++)
{
if (value[i] == 0x1e)
{
continue;
}
printf("%c", value[i]);
}
printf("\r\n\r\n");
}
void DataDispose(char* Data, unsigned long Len)
{
int temp = 0;
int temp1 = 0;
for (int i = 0;i < Len - 1;i++)
{
if (Data[i] == '\0')
{
Data[i] = 0x1e;
}
if (Data[i] == 0x7c)
{
temp++;
}
else if (Data[i] == 0x40)
{
temp1++;
}
}
char c;
temp > temp1 ? c = '|' : c = '@';
vector config;
config = split(Data,c);
//判断是哪种分割符号分割的
if (temp> temp1)
{
print("C2", config[0].c_str());
print("Botnet", config[2].c_str());
print("copy_file", config[0x13].c_str());
print("copy_file", config[0x14].c_str());
print("startup_value", config[0x15].c_str());
print("startup_value", config[0x16].c_str());
print("mutex", config[0x1b].c_str());
print("mutex", config[0x1c].c_str());
print("keylog_file", config[0x21].c_str());
print("keylog_file", config[0x22].c_str());
print("take_screenshot_title", config[0x2d].c_str());
print("take_screenshot_title", config[0x2e].c_str());
print("screenshot_folder", config[0x33].c_str());
print("screenshot_folder", config[0x34].c_str());
print("audio_folder", config[0x4b].c_str());
print("audio_folder", config[0x4c].c_str());
print("copy_folder", config[0x5f].c_str());
print("copy_folder", config[0x60].c_str());
print("keylog_folder", config[0x61].c_str());
print("keylog_folder", config[0x62].c_str());
}
else if (temp < temp1)
{
print("C2", config[0].c_str());
print("Botnet", config[1].c_str());
print("copy_file", config[0xa].c_str());
print("startup_value", config[0xb].c_str());
print("mutex", config[0xe].c_str());
print("keylog_file", config[0x11].c_str());
print("take_screenshot_title", config[0x19].c_str());
print("screenshot_folder", config[0x25].c_str());
print("copy_folder", config[0x2e].c_str());
print("keylog_folder", config[0x2f].c_str());
}
}
void rc4_crypt(unsigned int* s, unsigned char* Data, unsigned long Len) //加解密
{
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for (k = 0;k < Len;k++)
{
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j]; //交换s[x]和s[y]
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k] ^= s[t];
}
DataDispose((char*)Data, Len);
}
void decode(char* ResourceData,int resourceSize)
{
unsigned int s[256] = { 0 };
char* key = new char[(BYTE)ResourceData[0]];
memcpy(key, ResourceData + 1, (BYTE)ResourceData[0]);
char* c = ResourceData + 0x4e;
rc4_init(s, (unsigned char*)key, (BYTE)ResourceData[0]); //已经完成了初始化
rc4_crypt(s, (unsigned char*)(ResourceData+1+ (BYTE)ResourceData[0]), (resourceSize- (1 + (BYTE)ResourceData[0])));//解密
}
int main()
{
char filePath[256] = { 0 };
printf("文件路径:");
scanf_s("%s", filePath, 256);
int size = strlen(filePath);
filePath[size] = '.';
filePath[size + 1 ] = '\0';
HMODULE hMod = LoadLibraryA(filePath);
if (hMod == NULL)
{
return 0;
}
HRSRC hRes = FindResourceA(hMod, "SETTINGS", (LPCSTR)RT_RCDATA);
if (hRes == NULL)
{
return 0;
}
HGLOBAL ResData = LoadResource(hMod, hRes);
int resSize = SizeofResource(hMod, hRes);
char* pResData = new char[resSize+1];
memcpy(pResData, ResData, resSize);
FreeLibrary(hMod);
decode(pResData, resSize);
delete[] pResData;
system("pause");
return 0;
}