1.Dll注入补充
在使用远程线程注入将dll注入到目标进程时,我们可以为其添加菜单,替换掉原有的过程处理函数便可以使用我们自己的菜单
LONG SetWindowLong(
HWND hWnd,
int nIndex,
LONG dwNewLong //替换值
);
//函数成功返回原有值
该函数用于修改窗口属性,其中第二个参数是一个索引值,表示要修改的属性,见下表:
如果是win64话,需要使用SetWindowLongPtr:
LONG_PTR SetWindowLongPtr( HWND hWnd,
int nIndex,
LONG_PTR dwNewLong
);
第二个参数有所不同,其他参数与SetWindowLong相同:
过程处理函数:
LRESULT CALLBACK WindowProc( HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);
由于除了我们点击自己菜单所发出的消息要处理外,还有各种消息需要处理,所以原来的消息处理函数要保存,并且在测试中将dll注入进MFC工程发现自己的回调函数会出现问题(可能是因为MFC消息处理机制与win32api不同),另外如果原来程序有菜单那么注入后会阻塞
当我们成功替换了消息处理函数后,我们可以开始处理WM_COMMAND消息了,最后卸载dll时候不能直接调用Freelibrary,因为Freelibrary释放dll后剩下的代码也消失了所以会引发崩溃,而FreeLibraryAndExitThread则会退出当前线程(主线程),所以要卸载dll我们需要创建一个线程再调用FreeLibraryAndExitThread
示例如下:
#include "pch.h"
#include<Windows.h>
#include"resource.h"
#include<WinUser.h>
WNDPROC oldfun = NULL; //原来的过程处理函数要保留
HINSTANCE diahs = NULL;
HWND diahw = NULL;
HMODULE g_module = NULL;
DWORD WINAPI ThreadProc(__in LPVOID lpParameter)
{
Sleep(1); //等待主线程处理剩下工作
FreeLibraryAndExitThread(g_module, 0);
}
INT_PTR CALLBACK DialogProc(HWND hwndDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
//对话框消息处理函数
if (uMsg == WM_CLOSE)
{
DestroyWindow(hwndDlg);
}
return FALSE;
}
LRESULT CALLBACK NewWindowProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
//消息处理函数
if (uMsg == WM_COMMAND)
{
switch (LOWORD(wParam))
{
case ID_MENU_1:
{
//MessageBox(hWnd, "this is 1", NULL, MB_OK);
diahw =CreateDialog(diahs, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, DialogProc);
ShowWindow(diahw, SW_SHOW);
break;
}
case ID_MENU_2:
{
//MessageBox(hWnd, "this is 2", NULL, MB_OK);
DestroyWindow(diahw);
break;
}
case ID_MENU_3:
{
SetWindowLongPtr(hWnd, GWLP_WNDPROC,(LONG_PTR)oldfun); //归还过程函数
SetMenu(FindWindow(NULL, "Dialog"), NULL); //卸载菜单
CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
break;
}
default:
break;
}
}
else
{
return oldfun(hWnd, uMsg, wParam, lParam);
}
}
BOOL init(HMODULE hModule)
{
//获取窗体句柄,设置菜单
HMENU hm = LoadMenu(hModule, MAKEINTRESOURCE(IDR_MENU1));
HWND hw = FindWindow(NULL, "Dialog");
diahs = hModule;
DWORD error = GetLastError();
SetMenu(hw, hm);
if (hm == NULL || hw == NULL)
{
return FALSE;
}
//替换原有过程函数(x64要用SetWindowLongPtr)
oldfun = (WNDPROC)SetWindowLongPtr(hw, GWLP_WNDPROC,(LONG_PTR)NewWindowProc);
error = GetLastError();
return TRUE;
}
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
g_module = hModule;
if (init(hModule))
{
MessageBox(FindWindow(NULL, "Dialog"), "finish!", "notice", MB_OK);
}
else
{
MessageBox(FindWindow(NULL, "Dialog"), "Fail!", "notice", MB_OK);
}
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
2.Windows Hook
Hook意为钩子,用于监视指定窗口的某种类型的消息,当操作系统发送消息时,钩子会先捕获消息然后再传递消息。钩子的种类有很多,参加MSDN:
按照作用范围划分,钩子又可以分为全局钩子和局部钩子,局部钩子是针对某个线程的,而全局钩子是作用于整个系统
安装(全局\局部)钩子:
HHOOK SetWindowsHookEx( int idHook,
HOOKPROC lpfn,
HINSTANCE hMod,
DWORD dwThreadId
);
第一个参数是钩子类型,如下:
第二个参数是钩子的消息处理函数
第三个参数如果是局部钩子,则填NULL,如果是全局钩子,则填钩子函数所在的Dll模块句柄
第四个参数是目标窗口主线程id,如果是全局钩子则填0
该函数返回值是钩子的句柄
卸载钩子:
BOOL UnhookWindowsHookEx(
HHOOK hhk //钩子句柄
);
这里我们以键盘消息为例,以下是键盘钩子的回调函数
LRESULT CALLBACK KeyboardProc(
int code,
WPARAM wParam,
LPARAM lParam
);
wParam和lParam是键盘消息的两个参数,而第一个参数code是Hook代码,钩子函数使用该参数以明确任务,所以该参数依赖于钩子类型,注意:由于系统中可能有多个钩子存在,所以我们需要调用如下函数把消息传给下一个钩子函数:
LRESULT CallNextHookEx(
HHOOK hhk,
int nCode,
WPARAM wParam,
LPARAM lParam
);
另外,msdn有说明,如果code<0,则不应处理消息并使用CallNextHookEx返回
安装局部钩子示例:
//MFC
HHOOK hook1 = NULL;
LRESULT CALLBACK KeyboardProc(int code,
WPARAM wParam,
LPARAM lParam
)
{
if (code < 0)
{
return CallNextHookEx(hook1, code, wParam, lParam);
}
//处理
CString key;
key.Format("%c", wParam);
SetDlgItemText(FindWindow(NULL, "HOOK"), IDC_EDIT1, key);
//传给下一个钩子
return CallNextHookEx(hook1, code, wParam, lParam);
}
void CTestHookDlg::OnBnClickedButton1()
{
//创建键盘钩子
hook1 = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, NULL, GetCurrentThreadId());
}
void CTestHookDlg::OnBnClickedButton2()
{
//卸载钩子
UnhookWindowsHookEx(hook1);
}
对于全局钩子来说,全局钩子的函数必须放在dll中,因为进程的地址空间是相互隔离的,所以一个进程不能调用其他进程地址空间的钩子函数,如果函数在dll里,则系统会将这个dll插入到发生指定事件的进程的地址空间,使得进程能够调用钩子函数
全局钩子示例:
//钩子函数所在dll
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
HHOOK hook1 = NULL;
__declspec(dllexport) char buffer[MAXBYTE] = {};
LRESULT CALLBACK KeyboardProc(int code,
WPARAM wParam,
LPARAM lParam
)
{
if (code < 0)
{
return CallNextHookEx(hook1, code, wParam, lParam);
}
//处理
wsprintf(buffer, "%c", wParam);
MessageBox(FindWindow(NULL, "HOOK"), buffer, NULL, MB_OK);
//传给下一个钩子
return CallNextHookEx(hook1, code, wParam, lParam);
}
__declspec(dllexport) BOOL install()
{
hook1 = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, GetModuleHandle("Dll_hook"), 0);
return TRUE;
}
__declspec(dllexport) BOOL unload()
{
UnhookWindowsHookEx(hook1);
return TRUE;
}
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;
}
//cpp(MFC)
#pragma comment(lib,"Dll_hook")
BOOL install();
BOOL unload();
extern char __declspec(dllimport) buffer[MAXBYTE];
void CTestHookDlg::OnBnClickedButton3()
{
install(); //创建全局钩子
}
void CTestHookDlg::OnBnClickedButton4()
{
unload(); //卸载全局钩子
}
3.服务
服务也是一种进程,但是服务是非用户进程,拥有比管理员更高的权限,此外,服务会长期驻留在windows系统中。Windows服务包含3个组件:服务应用程序,服务控制程序(SCP)和服务控制管理器(SCM),服务应用程序是执行服务的进程,服务控制程序用于控制该服务启动、停止等,服务控制管理器则是媒介,服务控制程序通过管理器来控制服务
首先先来编写服务程序:
每个服务都有自己的main函数:
VOID WINAPI ServiceMain(
__in DWORD dwArgc,
__in LPTSTR* lpszArgv
);
我们需要先调用以下函数将服务的main函数与SCM关联起来,才能管理我们的服务(告诉SCM服务入口在哪里):
BOOL WINAPI StartServiceCtrlDispatcher(
__in const SERVICE_TABLE_ENTRY* lpServiceTable //该参数是一个以空结尾的指针数组(因为服务可以有多个)
);
typedef struct _SERVICE_TABLE_ENTRY {
LPTSTR lpServiceName; //服务名称
LPSERVICE_MAIN_FUNCTION lpServiceProc; //服务主函数地址
} SERVICE_TABLE_ENTRY, *LPSERVICE_TABLE_ENTRY;
然后注册服务回调函数:
SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerEx(
__in LPCTSTR lpServiceName, //服务名称
__in LPHANDLER_FUNCTION_EX lpHandlerProc, //回调函数地址
__in_opt LPVOID lpContext //用户自定义参数
);
//失败返回0,成功返回服务状态句柄
第一个参数指定服务名字,第二个参数是服务回调函数的地址,如下:
DWORD WINAPI HandlerEx(
__in DWORD dwControl,
__in DWORD dwEventType,
__in LPVOID lpEventData,
__in LPVOID lpContext
);
前三个参数参加MSDN手册或Windows服务框架与服务的编写_11080813的技术博客_51CTO博客
第四个参数表示从RegisterServiceCtrlHandlerEx传递的用户定义数据。当多个服务共享一个进程时,lpContext参数可以帮助标识该服务
当回调函数注册完成后,我们需要通知SCM服务开始运行,使用如下api
BOOL WINAPI SetServiceStatus(
__in SERVICE_STATUS_HANDLE hServiceStatus, //RegisterServiceCtrlHandlerEx返回的句柄
__in LPSERVICE_STATUS lpServiceStatus //结构体指针,指向如下结构体
);
typedef struct _SERVICE_STATUS {
DWORD dwServiceType;
DWORD dwCurrentState;
DWORD dwControlsAccepted;
DWORD dwWin32ExitCode;
DWORD dwServiceSpecificExitCode;
DWORD dwCheckPoint;
DWORD dwWaitHint;
} SERVICE_STATUS, *LPSERVICE_STATUS;
结构体成员介绍参见SERVICE_STATUS结构各成员解析 - 知己而已 - 博客园
接着我们要在服务回调函数中处理服务的各种状态(暂停、停止,继续等),处理完状态后也要通知SCM服务发生改变
服务程序示例:
#include<Windows.h>
#include<iostream>
using std::cout;
using std::endl;
#define SERVICENAME "my_person_service"
SERVICE_STATUS_HANDLE ssh = NULL;
DWORD WINAPI HandlerEx(
__in DWORD dwControl,
__in DWORD dwEventType,
__in LPVOID lpEventData,
__in LPVOID lpContext
)
{ //服务回调函数
SERVICE_STATUS ss = {};
ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ss.dwCurrentState = SERVICE_RUNNING;
ss.dwControlsAccepted = SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
ss.dwWin32ExitCode = NO_ERROR;
switch (dwControl)
{
case SERVICE_CONTROL_PAUSE:
ss.dwCurrentState = SERVICE_PAUSED;
break;
case SERVICE_CONTROL_CONTINUE:
ss.dwCurrentState = SERVICE_RUNNING;
break;
case SERVICE_CONTROL_SHUTDOWN:
ss.dwCurrentState = SERVICE_STOP_PENDING;
break;
case SERVICE_CONTROL_STOP:
ss.dwCurrentState = SERVICE_STOPPED;
break;
default:
break;
}
BOOL flag = SetServiceStatus(ssh, &ss);
if (flag != 0)
{
//system("echo 服务状态改变,通知SCM成功 ");
}
else
{
//system("echo 服务回调函数通知SCM失败");
}
return NO_ERROR;
}
VOID WINAPI ServiceMain(
__in DWORD dwArgc,
__in LPTSTR* lpszArgv
)
{
//服务主线程函数
ssh= RegisterServiceCtrlHandlerEx(SERVICENAME, HandlerEx, NULL); //注册回调函数
if (ssh != 0 && ssh!=NULL)
{
//system("echo 服务回调函数创建成功");
}
//通知SCM服务开始运行
SERVICE_STATUS ss = {};
ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ss.dwCurrentState = SERVICE_RUNNING;
ss.dwControlsAccepted = SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
ss.dwWin32ExitCode = NO_ERROR;
BOOL error = SetServiceStatus(ssh, &ss);
if (error != 0)
{
//system("echo 通知SCM完毕!");
}
}
int main()
{
SERVICE_TABLE_ENTRY entry[2] = { {(LPSTR)SERVICENAME,ServiceMain},{NULL,NULL} };
StartServiceCtrlDispatcher(entry); //将主函数与SCM关联
return 0;
}
完成如上步骤,我们的服务程序就ok了,我们要把程序编译成release版本的才能让服务顺利安装
接下来开始编写服务控制程序:
首先是安装我们的服务程序,我们要先与SCM建立连接,使用如下api打开SCM数据库
//头文件Winsvc.h
SC_HANDLE WINAPI OpenSCManager(
__in_opt LPCTSTR lpMachineName, //目标计算机名,填NULL则表示连接到本地
__in_opt LPCTSTR lpDatabaseName, //要打开的SCM数据库,一般填NULL,打开SERVICES_ACTIVE_DATABASE 数据库
__in DWORD dwDesiredAccess //数据库访问权限
);
SCM数据库访问权限如下:
关闭服务句柄:
BOOL WINAPI CloseServiceHandle(
__in SC_HANDLE hSCObject
);
接着需要创建服务:
SC_HANDLE WINAPI CreateService(
__in SC_HANDLE hSCManager,
__in LPCTSTR lpServiceName,
__in_opt LPCTSTR lpDisplayName,
__in DWORD dwDesiredAccess,
__in DWORD dwServiceType,
__in DWORD dwStartType,
__in DWORD dwErrorControl,
__in_opt LPCTSTR lpBinaryPathName,
//以下五个一般填NULL
__in_opt LPCTSTR lpLoadOrderGroup,
__out_opt LPDWORD lpdwTagId,
__in_opt LPCTSTR lpDependencies,
__in_opt LPCTSTR lpServiceStartName,
__in_opt LPCTSTR lpPassword
);
参数说明见CreateServiceA function (winsvc.h) - Win32 apps | Microsoft Docs
如果驱动已经存在,则CreateService会失败,可以使用如下函数打开服务
SC_HANDLE WINAPI OpenService(
__in SC_HANDLE hSCManager,
__in LPCTSTR lpServiceName,
__in DWORD dwDesiredAccess
);
第三个参数是服务的访问权限,如下:
注意不要填成SCM数据库的访问权限
服务创建(安装)示例:
void CControlServiceDlg::OnBnClickedButton1()
{
//安装服务
SC_HANDLE hscm=OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); //连接SCM数据库
if (hscm != NULL)
{
AfxMessageBox("OpenSCManager成功!");
}
else
{
AfxMessageBox("OpenSCManager失败!");
return;
}
SC_HANDLE hc=CreateService(
hscm,
SERVICENAME,
"你好服务",
SC_MANAGER_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
"C:\\Users\\VMWARE\\Desktop\\Service\\Service_test.exe",
NULL, NULL, NULL, NULL, NULL
);
if (hc != NULL)
{
AfxMessageBox("CreateService成功!,服务创建完成!");
}
else
{
AfxMessageBox("CreateService失败!");
}
CloseServiceHandle(hc);
CloseServiceHandle(hscm);
}
这里我把服务名称设为宏SERVICENAME
服务创建完成后可以使用如下api启动服务:
BOOL WINAPI StartService(
__in SC_HANDLE hService,
__in DWORD dwNumServiceArgs, //NULL
__in_opt LPCTSTR* lpServiceArgVectors //NULL
);
启动服务示例:
void CControlServiceDlg::OnBnClickedButton5()
{
//启动服务
SC_HANDLE hscm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); //连接SCM数据库
if (hscm != NULL)
{
AfxMessageBox("OpenSCManager成功!");
}
else
{
AfxMessageBox("OpenSCManager失败!");
return;
}
SC_HANDLE sc = OpenService(hscm, SERVICENAME, SC_MANAGER_ALL_ACCESS);
if (sc != NULL)
{
AfxMessageBox("获取句柄成功!");
}
BOOL flag=StartService(sc, NULL, NULL);
if (flag != 0)
{
AfxMessageBox("服务启动成功!");
}
CloseServiceHandle(sc);
CloseServiceHandle(hscm);
}
服务的暂停、继续、停止等可以通过如下api完成
BOOL WINAPI ControlService(
__in SC_HANDLE hService,
__in DWORD dwControl,
__out LPSERVICE_STATUS lpServiceStatus //返回服务状态
);
第二个参数如下:
示例:
void CControlServiceDlg::OnBnClickedButton2()
{
//服务暂停
SC_HANDLE hscm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); //连接SCM数据库
if (hscm != NULL)
{
AfxMessageBox("OpenSCManager成功!");
}
else
{
AfxMessageBox("OpenSCManager失败!");
return;
}
SC_HANDLE sc = OpenService(hscm, SERVICENAME, SERVICE_ALL_ACCESS);
if (sc != NULL)
{
AfxMessageBox("获取句柄成功!");
}
SERVICE_STATUS ss;
BOOL flag=ControlService(sc, SERVICE_CONTROL_PAUSE, &ss);
if (flag == 0)
{
AfxMessageBox("ControlService失败!");
}
else
{
AfxMessageBox("ControlService成功!");
}
CloseServiceHandle(sc);
CloseServiceHandle(hscm);
}
void CControlServiceDlg::OnBnClickedButton3()
{
//服务继续
SC_HANDLE hscm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); //连接SCM数据库
if (hscm != NULL)
{
AfxMessageBox("OpenSCManager成功!");
}
else
{
AfxMessageBox("OpenSCManager失败!");
return;
}
SC_HANDLE sc = OpenService(hscm, SERVICENAME, SERVICE_ALL_ACCESS);
if (sc != NULL)
{
AfxMessageBox("获取句柄成功!");
}
SERVICE_STATUS ss;
BOOL flag = ControlService(sc, SERVICE_CONTROL_CONTINUE, &ss);
if (flag == 0)
{
AfxMessageBox("ControlService失败!");
}
else
{
AfxMessageBox("ControlService成功!");
}
CloseServiceHandle(sc);
CloseServiceHandle(hscm);
}
删除服务可以使用如下api,该函数将服务从SCM数据库中移除:
BOOL WINAPI DeleteService(
__in SC_HANDLE hService
);
删除服务示例:
void CControlServiceDlg::OnBnClickedButton6()
{
//删除服务
SC_HANDLE hscm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); //连接SCM数据库
if (hscm != NULL)
{
AfxMessageBox("OpenSCManager成功!");
}
else
{
AfxMessageBox("OpenSCManager失败!");
return;
}
SC_HANDLE sc = OpenService(hscm, SERVICENAME, SERVICE_ALL_ACCESS);
if (sc != NULL)
{
AfxMessageBox("获取句柄成功!");
}
DeleteService(sc); //删除服务
CloseServiceHandle(sc);
CloseServiceHandle(hscm);
}
用我们的服务控制程序控制服务和用Windows的Services.msc是一样的
至此,一个没有功能的服务已经编写完成