无纸化会议-禁止进程启动
需求
实现禁止进程启动的功能
可选方案
- 轮询。
每隔固定时间检查一次进程列表,比较出新出现的进程,然后将其kill。
这种方法会消耗相对较多的计算资源,并且有响应延迟,且实现难度不低,故不采用 - dll注入
向全局注入一个dll,当有新进程启动时,dll进行attach时会向无纸化会议系统发回启动进程的进程id,系统收到id后据此来实现kill
这里选择这一种方案,相比于第一种方案,dll注入可以在进程启动时立即响应,没有延迟,不需要循环查找和比较,资源消耗相对较低,由于这使用了c++编写的dll以及跨进程,调试难度会轻微上升,但是跨进程执行的操作并不复杂,总体来讲实现难度与第一种相近
实现
新建dll项目HookProc,实现两部分功能:向主程序提供向全局注入此dll和取消该注入的控制函数,以及在远程进程上的实际执行的逻辑
在主程序中新建类ProcessManager,作为主程序和dll之间的接口,通过动态调用dll的方式控制注入
在主程序中添加处理windows 消息队列的函数,来处理dll从其他进程发来的进程id,执行相关操作。
相关代码:
获得启动的进程的id
HookProc.dll
dllmain.cpp
extern "C"
{
__declspec(dllexport)
void StartupHook(void);
__declspec(dllexport)
void CloseHook(void);
void ProcessHookUp();
void ProcessHookDelete();
}
int count = 0;
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
ProcessHookUp();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
ProcessHookDelete();
break;
}
return TRUE;
}
HHOOK hThis = NULL;
LRESULT CALLBACK ProcessClose(int nCode, WPARAM wParam, LPARAM lParam)
{
return CallNextHookEx(hThis, nCode, wParam, lParam);
}
wchar_t ws[203];
void StartupHook(void)
{
DWORD dwPID;
HMODULE h = GetModuleHandleW(L"ProcHook.dll");
HINSTANCE hi= LoadLibrary(TEXT(".\\HookProc.dll"));
hThis = SetWindowsHookExW(WH_GETMESSAGE, ProcessClose,hi ,0 /*GetWindowThreadProcessId(FindWindow(NULL, L"pm_client"), &dwPID)*/);
long long n = (long long) hThis;
int i = 0;
if (n == 0) {
ws[i++] = L'e';
if (hi == NULL)ws[i++] = L'x';
n = GetLastError();
}
for (;n != 0;n /= 10) {
ws[i++] = '0' + n % 10;
}
ws[i++] = '\0';
MessageBoxW(FindWindow(NULL, L"pm_client"), ws, L"WORLD", MB_OK);
}
void CloseHook(void)
{
UnhookWindowsHookEx(hThis);
}
#include <fstream>
void ProcessHookUp()
{
DWORD proId = GetCurrentProcessId();
HWND hWnd = FindWindow(NULL, L"pm_client");
std::ofstream x("C:\\mylog.txt",std::ios::app);
x << proId << "\n" << hWnd << "\n" << std::endl;
x.flush();
x.close();
if (hWnd)
{
PostMessage(hWnd, WM_USER + 7, 0, proId);
}
}
void ProcessHookDelete()
{
DWORD proId = GetCurrentProcessId();
}
主程序
ProcessManager
public class ProcessManager
{
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
private delegate void Func();
Func startupHook;
Func closeHook;
static IntPtr pDll;
public void StartupHook()
{
startupHook();
}
public void CloseHook()
{
closeHook();
}
public ProcessManager()
{
IntPtr pAddressOfFunctionToCall = GetProcAddress(pDll, "StartupHook");
startupHook = (Func)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(Func));
pAddressOfFunctionToCall = GetProcAddress(pDll, "CloseHook");
closeHook = (Func)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(Func));
}
static ProcessManager()
{
pDll = LoadLibrary(@".\HookProc.dll");
Console.WriteLine(pDll);
}
}
消息队列处理
IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
if (msg == 0x0400 + 7) {
int proId = lParam.ToInt32();
Log.i("hook", $"pid:{proId}");
//do sth
}
return IntPtr.Zero;
}
private void Window_Loaded(object sender, RoutedEventArgs e) {
p = new ProcessManager();
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
if (source != null) source.AddHook(WndProc); else Log.i("wndproc", "failed");
p.StartupHook();
}
用pid kill进程
Process p = Process.GetProcessById(pid);
p.Kill();
实现效果
一些细节
1.在vs中需要使用def文件来指示dll文件可以导出的函数,如在这里,需要创建一个.def文件:
LIBRARY HookProc
EXPORTS
StartupHook
CloseHook
然后在项目-属性-链接器-输入-模块定义文件中指定这个.def文件
否则生成的dll中这些函数将不会被导出,但是vs不会给出任何提示
2.dll项目不方便使用vs的调试器进行调试,也没有控制台可以输出,可以使用win32的MessageBoxW函数来显示输出
函数签名如下
int MessageBoxW(
HWND hWnd,
LPCWSTR lpText,
LPCWSTR lpCaption,
UINT uType
);
hWnd表示归属的窗口,可以为NULL
lpText是对话框中的文本
lpCaption是对话框的标题
uType是窗口类型,MB_OK将显示一个OK按钮
LPCWSTR 是wchar_t *
注意不可以用这种方法调试hook的实际逻辑,因为messagebox本身也是一个新的窗口,这会递归地调用这个过程,破坏windows桌面,替代方法是把输出写到特定的文件上。
3.根据win32 文档,32位dll的全局hook只能hook到32位的程序,64位同理,所以如果要hook所有程序,应该同时准备32位dll和64位dll,并且它们的名字应该不同