思路
SetWindowDisplayAffinity
函数,可以设定窗口的显示属性,只能用于调用进程拥有的窗口——否则调用会失败但不报告特定错误- 将
SetWindowDisplayAffinity
封装为DLL - 利用注入器将DLL注入目标进程实现反截屏
基础函数
DLL部分
SetWindowDisplayAffinity
- 语法
bool setWindowDisPlayAffility(
[in] HWND hWnd,
[in] DWORD dwAffinity
);
- 参数
hWnd
:顶级窗口的句柄。 窗口必须属于当前进程dwAffinity
:指定窗口内容的显示位置的显示相关性设置。
值 | 含义 |
---|---|
WDA_NONE | 对窗口的显示位置没有限制 |
WDA_MONITOR | 窗口内容仅显示在监视器上。 在其他任何位置,窗口都会显示,其中不显示任何内容 |
WDA_EXCLUDEFROMCAPTURE | 窗口仅显示在监视器上。 在其他任何位置,窗口根本不显示 |
- 返回值
如果函数成功,则返回TRUE
;例如,在非顶级窗口上进行函数调用时,它将返回FALSE
。 要获得更多的错误信息,请调用 GetLastError
DllMain
- 语法
BOOL WINAPI DllMain(
_In_ HINSTANCE hinstDLL,
_In_ DWORD fdwReason,
_In_ LPVOID lpvReserved
);
-
参数
hinstDLL
: DLL 模块的句柄,值是 DLL 的基址,DLL 的 HINSTANCE 与 DLL 的 HMODULE 相同,因此可以在调用需要模块句柄的函数时使用 hinstDLLfdwReason
: 指示调用 DLL 入口点函数的原因代码, 可能的值为:DLL_PROCESS_ATTACH
:进程正在加载 DLL,使用这个DLL_THREAD_ATTACH
:线程正在附加到 DLL,每个新线程都触发,导致高频重复调用DLL_THREAD_DETACH
:线程正在分离 DLLDLL_PROCESS_DETACH
:进程正在卸载 DLL
lpvReserved
: 如果DLL_PROCESS_ATTACH fdwReason,则 lpvReserved 对于动态加载为 NULL,对于静态加载为非 NULL,一般不关心这个
-
框架
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // handle to DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpvReserved ) // reserved
{
// Perform actions based on the reason for calling.
switch( fdwReason )
{
case DLL_PROCESS_ATTACH:
// Initialize once for each new process.
// Return FALSE to fail DLL load.
// 程序要执行的功能
break;
case DLL_THREAD_ATTACH:
// Do thread-specific initialization.
break;
case DLL_THREAD_DETACH:
// Do thread-specific cleanup.
break;
case DLL_PROCESS_DETACH:
if (lpvReserved != nullptr)
{
break; // do not do cleanup if process termination scenario
}
// Perform any necessary cleanup.
break;
}
return TRUE; // Successful DLL_PROCESS_ATTACH.
}
GetCurrentProcessId()
- 语法
DWORD GetCurrentProcessId();
获取当前进程的唯一标识符(PID)
EnumWindows与EnumWindowsProc回调函数
通过将句柄传递到每个窗口,进而将传递给应用程序定义的回调函数,枚举屏幕上的所有顶级窗口。 枚举窗口 将一直持续到最后一个顶级窗口被枚举或回调函数返回FALSE
- EnumWindows语法
BOOL EnumWindows(
[in] WNDENUMPROC lpEnumFunc,
[in] LPARAM lParam
);
-
参数
lpEnumFunc
:回调函数的地址。 回调函数的原型必须为BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam);
lParam
:用户定义的回调函数参数。 回调函数将在每个窗口上调用,并将此参数作为 lParam 传递。
-
EnumWindowsProc回调函数语法
BOOL CALLBACK EnumWindowsProc(
_In_ HWND hwnd, //顶级窗口的句柄
_In_ LPARAM lParam //用户定义的EnumWindows的(LPARAM)lParam
);
DisableThreadLibraryCalls()
DisableThreadLibraryCalls
函数允许一个 DLL
禁用对该 DLL
的 DLL_THREAD_ATTACH
和 DLL_THREAD_DETACH
回调通知,从而避免 DllMain
在每次线程创建与终止时被调用,这样不仅可减少上下文切换开销,还能降低工作集大小,提升多线程应用的性能
- 语法
BOOL DisableThreadLibraryCalls(
[in] HMODULE hLibModule
);
- 参数
hLibModule
:DLL 模块的句柄。
注入部分
LoadLibraryA
将指定的模块加载到调用进程的地址空间中,指定的模块可能会导致加载其他模块
- 语法
HMODULE LoadLibraryA(
[in] LPCSTR lpLibFileName
);
-
参数
lpLibFileName
:模块的名称,这可以是库模块(.dll 文件)或可执行模块(.exe 文件)。 如果指定的模块是可执行模块,则不会加载静态导入;而是使用 DONT_RESOLVE_DLL_REFERENCES 标志 LoadLibraryEx 加载模块。
-
返回值
- 如果成功,则返回模块的句柄;如果失败,则返回 NULL。
OpenProcess
VirtualAllocEx
WriteProcessMemory
GetModuleHandleA
GetProcAddress
CreateRemoteThread
实现
AntiCapture.dll
#include "pch.h"
#include <windows.h>
#include <thread>
static const DWORD affinityMode = WDA_MONITOR;
void AntiCapture() {
DWORD pid = GetCurrentProcessId();
//枚举所有所有顶层窗口句柄
EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL{
DWORD wnpid = 0;
GetWindowThreadProcessId(hwnd, &wnpid);
//如果当前窗口的进程ID和DLL所在进程ID一致,并且窗口可见,则设置窗口的显示模式
if (wnpid == (DWORD)lParam && IsWindowVisible(hwnd))
{
SetWindowDisplayAffinity(hwnd, affinityMode);
}
return TRUE;
}, (LPARAM)pid);
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hModule);
std::thread(AntiCapture).detach();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
g++ -shared -O2 -Wall -Wl,--out-implib,libAntiCapture.a -o AntiCapture.dll AntiCapture.cpp -static-libgcc -static-libstdc++
远程线程注入
#include <iostream>
#include <windows.h>
#include <tlhelp32.h>
#include <string>
#define PROCESS_NAME L"TargetApp.exe"
#define DLL_NAME "AntiCapture.dll"
VOID ShowError(const char* err)
{
printf("%s 失败:%u", err, GetLastError());
}
DWORD GetPid(const WCHAR* pProName)
{
//创建系统进程快照
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
if (hSnap == INVALID_HANDLE_VALUE)
{
ShowError("CreateToolhelp32Snapshot");
return 0;
}
PROCESSENTRY32 pe;
pe.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hSnap, &pe))
{
do
{
//忽略大小写比较
if (_wcsicmp(pe.szExeFile,pProName) == 0)
{
printf("PID为%d\n", pe.th32ProcessID);
return pe.th32ProcessID;
}
} while (Process32Next(hSnap, &pe));
}
return 0;
}
BOOL inject(DWORD dwPid,const CHAR dllName[])
{
HANDLE hProcess = NULL;
HMODULE hKernel32 = NULL;
SIZE_T dwSize = NULL;
LPVOID DLL_address = NULL;
FARPROC hLoadLibraryA = NULL;
HANDLE hRemoteThread = NULL;
//打开注入进程,获取进程句柄
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (hProcess == NULL)
{
ShowError("OpenProcess");
return 0;
}
//在注入进程中申请空间
dwSize = strlen(dllName) + 1;
DLL_address = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
if (DLL_address == NULL)
{
ShowError("VirtualAllocEx");
return 0;
}
//将DLL路径写入进程
if (!WriteProcessMemory(hProcess, DLL_address, dllName, dwSize, NULL))
{
ShowError("WriteProcessMemory");
return 0;
}
//获取模块的地址
hKernel32 = GetModuleHandleA("kernel32.dll");
if (hKernel32 == NULL)
{n
ShowError("GetModuleHandleA");
return 0;
}
//获取LoadLibraryA函数地址
hLoadLibraryA = GetProcAddress(hKernel32,"LoadLibraryA");
if (hLoadLibraryA == NULL)
{
ShowError("GetProcAddress");
return 0;
}
//创建远程线程进行DLL注入
hRemoteThread = CreateRemoteThread(
hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE)hLoadLibraryA,
DLL_address,
0,NULL
);
if (hRemoteThread == NULL)
{
ShowError("CreateRemoteThread");
return 0;
}
if (hKernel32) FreeLibrary(hKernel32);
if (hProcess) CloseHandle(hProcess);
if (hRemoteThread) CloseHandle(hRemoteThread);
return 1;
}
int main()
{
DWORD pid = GetPid(PROCESS_NAME);
if (pid == 0)
{
printf("获取PID失败\n");
return 0;
}
if (inject(pid, DLL_NAME))
{
printf("注入成功\n");
}
return 0;
}
g++ -O2 -Wall -o Injector.exe Injector.cpp