WindowsAPI查缺补漏-动态链接库
静态链接库
静态链接库头文件:
1 2 3
| #pragma once int funAdd(int a, int b); int funMul(int a, int b);
|
静态链接源文件:
1 2 3 4 5 6 7
| #include "StaticLinkLibrary.h" int funAdd(int a, int b) { return a + b; }; int funMul(int a, int b) { return a * b; };
|
调用方:
1 2 3 4 5 6 7 8
| #include <Windows.h> #include "StaticLinkLibrary.h" #pragma comment(lib, "StaticLinkLibrary.lib") int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { TCHAR szBuf[256] = { 0 }; wsprintf(szBuf, TEXT("funAdd(5, 6) = %d\nfunMul(5, 6) = %d"), funAdd(5, 6), funMul(5, 6)); MessageBox(NULL, szBuf, TEXT("提示"), MB_OK); };
|
动态链接库
DLL头文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #pragma once
#ifdef DLL_EXPORT #define DLL_VARABLE extern "C" __declspec(dllexport) #define DLL_CLASS __declspec(dllexport) #define DLL_API extern "C" __declspec(dllexport) #else #define DLL_VARABLE extern "C" __declspec(dllimport) #define DLL_CLASS __declspec(dllimport) #define DLL_API extern "C" __declspec(dllimport) #endif
typedef struct _POSITION { int x; int y; }POSITION, * PPOSITION;
DLL_VARABLE int nValue; DLL_VARABLE POSITION ps;
class DLL_CLASS CStudent { public: CStudent(LPTSTR lpName, int nAge); ~CStudent(); public: LPTSTR GetName(); int GetAge(); private: TCHAR m_szName[64]; int m_nAge; };
DLL_API int WINAPI funAdd(int a, int b); DLL_API int WINAPI funMul(int a, int b);
|
DLL源文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| #include <Windows.h> #include <strsafe.h> #include <tchar.h> #define DLL_EXPORT #include "DllSample.h"
int nValue; POSITION ps; BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { nValue = 5; ps.x = 6; ps.y = 7; break; }; case DLL_THREAD_ATTACH: {}; case DLL_THREAD_DETACH: {}; case DLL_PROCESS_DETACH: { break; }; }; return TRUE; };
CStudent::CStudent(LPTSTR lpName, int nAge) { if (m_szName) StringCchCopy(m_szName, _countof(m_szName), lpName); m_nAge = nAge; }; CStudent::~CStudent() {}; LPTSTR CStudent::GetName() { return m_szName; }; int CStudent::GetAge() { return m_nAge; };
int WINAPI funAdd(int a, int b) { return a + b; }; int WINAPI funMul(int a, int b) { return a * b; };
|
但不建议从DLL中导出变量和类,好孩子不要这么干。调用时需要.h、.lib和.dll。静态链接库.lib为对象库,包含函数实现代码,而DLL的.lib为导入库,里面只有连接到.dll的符号列表。
C++编译器对函数导出名有命名规则,函数名以“?”开头,后面是函数名,在“@@YA”后面表示参数列表,用代号表示如X为void、D为char、E为unsigned char、F为short、H为int、I为unsigned int、N为double等,最后以“@Z”结尾。如上述funAdd
的导出名为?funAdd@@YAHHH@Z
。这样命名规则导致C++编译的DLL没法被其他语言调用,解决方法是用关键字extern "C"
强制使用标准C函数名称,调用约定默认为__cdecl
,当强制改为__stdcall
调用约定时,函数导出名前加下划线,结尾“@”后加参数传递给函数的字节数,如_funAdd@8
。如果仍然要导出funAdd
函数时,新建模块定义.def文件:
然后对DLL的隐式链接:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <Windows.h> #include <strsafe.h> #include <tchar.h> #include "DllSample.h" #pragma comment(lib, "DllSample.lib") int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { TCHAR szBuf[256] = { 0 }; TCHAR szBuf2[512] = { 0 }; wsprintf(szBuf, TEXT("nValue = %d\nps.x = %d, ps.y = %d\n"), nValue, ps.x, ps.y); StringCchCopy(szBuf2, _countof(szBuf2), szBuf); CStudent student((LPTSTR)TEXT("老王"), 40); wsprintf(szBuf, TEXT("姓名:%s, 年龄:%d\n"), student.GetName(), student.GetAge()); StringCchCat(szBuf2, _countof(szBuf2), szBuf); wsprintf(szBuf, TEXT("funAdd(5, 6) = %d\nfunMul(5, 6) = %d"), funAdd(5, 6), funMul(5, 6)); StringCchCat(szBuf2, _countof(szBuf2), szBuf); MessageBox(NULL, szBuf2, TEXT("提示"), MB_OK); };
|
发行时应带上.dll文件,定位DLL文件的顺序为:可执行文件目录、Windows系统目录、Windows目录、进程当前目录、PATH环境变量列出的所有目录。
VC++编译使用/MT或/MTd选项时多线程静态链接C运行时库,程序体积增大但可在其他机器上正常运行;使用/MD或/MDd选项时多线程动态链接C运行时库,程序体积减小但可能出现缺少相关动态链接库的错误。
入口点函数DllMain
代码如上,处理一些调用DLL入口点函数的原因码,如果不需要空着就行。常见的有:
枚举值 |
含义 |
DLL_PROCESS_ATTACH |
DLL第一次被映射到进程地址空间中时引发,后续如果再有线程用LoadLibrary(Ex) 映射时不会引发,返回TRUE初始化成功,返回FALSE则整个进程直接退出。隐式链接时lpvReserved非NULL,显式链接为NULL。 |
DLL_PROCESS_DETACH |
DLL被从进程内存空间撤销映射时引发,用TerminateProcess 终止进程时不会引发,DLL加载失败、进程终止或FreeLibrary 卸载DLL时引发,忽略返回值。用FreeLibrary 或DLL加载失败而卸载DLL时lpvReserved为NULL,进程终止导致DLL卸载时lpvReserved为非NULL。 |
DLL_THREAD_ATTACH |
进程中创建一个新线程时引发,一般用于新线程初始化。地址空间所有DLL都会被通知,所有DLL该通知都执行结束后才创建,主线程的创建不引发该通知,忽略返回值。 |
DLL_THREAD_DETACH |
进程中有一个线程正在退出时引发,一般用于相关善后。所有DLL都处理完该通知时该线程才退出,用TerminateProcess 终止进程时不会引发,DLL加载失败、进程终止或FreeLibrary 卸载DLL时不引发。 |
延迟加载DLL
即显式加载DLL。编译器在可执行模块中嵌入__delayLoadHelper2
函数,延迟加载DLL函数时跳转该函数,该函数解析延迟加载导入表,并用LoadLibrary(Ex)
和GetProcAddress
等函数完成加载和函数地址解析。当延迟加载的DLL不存在时,或当DLL中一个函数不存在时,该函数抛出一个异常,可用结构化异常处理SEH捕捉异常来继续运行,否则进程终止。
__FUnloadDelayLoadedDLL2
当希望一个延迟加载的DLL在进程结束或DLL卸载时不会被立即释放内存,下次加载时速度更快时,可对链接器开启/DELAY:UNLOAD选项。当确实要把某个延迟加载的DLL从内存中释放时使用该函数:
1 2 3
| BOOL WINAPI __FUnloadDelayLoadedDLL2( LPCSTR szDll );
|
DragQueryFile
查询所拖放文件的名称:
1 2 3 4 5 6
| UINT DragQueryFile( _In_ HDROP hDrop, _In_ UINT iFile, _Out_ LPTSTR lpszFile, UINT cch );
|
对于iFile,文件索引从0开始,设置为0xFFFFFFFF时函数返回所拖放文件总数。该值介于0和所拖放文件总数之间时将指定索引文件名复制到lpszFile缓冲区。 当lpszFile为NULL则函数返回所需缓冲区大小,单位字符,不含终止空字符。
要创建一个支持拖放的窗口时,CreateWindowEx
的dwExStyle指定WS_EX_ACCEPTFILES,程序接收WM_DROPFILES消息,wParam为所拖放文件信息的HDROP类型结构句柄,lParam缺省,处理完该消息应返回0。
例如获取所有拖动文件的文件名:
1 2 3 4 5 6 7 8 9 10 11
| HDROP hDrop; UINT uDragCount; TCHAR szFileName[MAX_PATH] = { 0 }; case WM_DROPFILES: { hDrop = (HDROP)wParam; uDragCount = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); for (UINT i = 0; i < uDragCount; i++) { DragQueryFile(hDrop, i, szFileName, _countof(szFileName)); }; };
|
DragQueryPoint
查询拖动操作期间鼠标指针位置:
1 2 3 4
| BOOL DragQueryPoint( _In_ HDROP hDrop, _Out_ PPOINT lppt );
|
DragFinish
释放系统为拖动操作分配的内存:
1 2 3
| BOOL DragFinish( HDROP hDrop );
|
例子
GetMd5.dll源文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| #include <Windows.h> #define DLL_EXPORT #include "GetMd5.h"
BOOL GetMd5(LPCTSTR lpFileName, LPTSTR lpMd5) { HANDLE hFile, hFileMap; HCRYPTPROV hProv, hHash; TCHAR szContainer[] = TEXT("MyKeyContainer"); LPVOID lpMemory; hFile = CreateFile(lpFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return FALSE; if (!CryptAcquireContext(&hProv, szContainer, NULL, PROV_RSA_FULL, 0)) if (!CryptAcquireContext(&hProv, szContainer, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) return FALSE; if (!CryptCreateHash(hProv, CALG_MD5, NULL, 0, &hHash)) return FALSE; hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); if (!hFileMap) return FALSE; __int64 qwFileSize; DWORD dwFileSizeHigh; SYSTEM_INFO si; qwFileSize = GetFileSize(hFile, &dwFileSizeHigh); qwFileSize += (((__int64)dwFileSizeHigh) << 32); GetSystemInfo(&si); __int64 qwFileOffset = 0; DWORD dwBytesInBlock; while (qwFileSize > 0) { dwBytesInBlock = si.dwAllocationGranularity; if (qwFileSize < dwBytesInBlock) dwBytesInBlock = (DWORD)qwFileSize; lpMemory = MapViewOfFile(hFileMap, FILE_MAP_READ, (DWORD)(qwFileOffset >> 32), (DWORD)(qwFileOffset & 0xFFFFFFFF), dwBytesInBlock); if (!lpMemory) return FALSE; if (!CryptHashData(hHash, (LPBYTE)lpMemory, dwBytesInBlock, 0)) return FALSE; UnmapViewOfFile(lpMemory); qwFileOffset += dwBytesInBlock; qwFileSize -= dwBytesInBlock; }; DWORD dwHashLen = 0; if (!CryptGetHashParam(hHash, HP_HASHVAL, NULL, &dwHashLen, 0)) return FALSE; LPBYTE lpHash = new BYTE[dwHashLen]; if (!CryptGetHashParam(hHash, HP_HASHVAL, lpHash, &dwHashLen, 0)) return FALSE; for (DWORD i = 0; i < dwHashLen; i++) wsprintf(lpMd5 + i * 2, TEXT("%02X"), lpHash[i]); delete[]lpHash; CloseHandle(hFileMap); CloseHandle(hFile); CryptDestroyHash(hHash); CryptReleaseContext(hProv, 0); return TRUE; };
|
GetMd5.dll头文件:
1 2 3 4 5 6 7 8 9
| #pragma once
#ifdef DLL_EXPORT #define DLL_API extern "C" __declspec(dllexport) #else #define DLL_API extern "C" __declspec(dllimport) #endif
DLL_API BOOL GetMd5(LPCTSTR lpFileName, LPTSTR lpMd5);
|
调用者:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| #include <windows.h> #include <Shlwapi.h> #include <delayimp.h> #include "resource.h" #include "GetMd5.h" #pragma comment(lib, "Shlwapi.lib") #pragma comment(lib, "GetMd5.lib")
HINSTANCE g_hInstance;
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { g_hInstance = hInstance; DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { HRSRC hResBlock; HANDLE hRes; LPVOID lpDll; DWORD dwDllSize; HANDLE hFile; TCHAR szFileName[MAX_PATH] = { 0 }; TCHAR szMd5[64] = { 0 }; HDROP hDrop; switch (uMsg) { case WM_INITDIALOG: { ChangeWindowMessageFilterEx(hwndDlg, WM_DROPFILES, MSGFLT_ALLOW, NULL); ChangeWindowMessageFilterEx(hwndDlg, 0x49, MSGFLT_ALLOW, NULL); if (!PathFileExists(TEXT("GetMd5.dll"))) { hResBlock = FindResource(g_hInstance, MAKEINTRESOURCE(IDR_MYDLL), TEXT("MyDll")); if (!hResBlock) return FALSE; hRes = LoadResource(g_hInstance, hResBlock); if (!hRes) return FALSE; lpDll = LockResource(hRes); dwDllSize = SizeofResource(g_hInstance, hResBlock); hFile = CreateFile(TEXT("GetMd5.dll"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return FALSE; WriteFile(hFile, lpDll, dwDllSize, NULL, NULL); CloseHandle(hFile); }; return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_GETMD5: { if (GetDlgItemText(hwndDlg, IDC_EDIT_FILENAME, szFileName, _countof(szFileName))) if (GetMd5(szFileName, szMd5)) { SetDlgItemText(hwndDlg, IDC_EDIT_MD5, szMd5); __FUnloadDelayLoadedDLL2("GetMd5.dll"); }; break; }; case IDCANCEL: { EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; case WM_DROPFILES: { hDrop = (HDROP)wParam; DragQueryFile(hDrop, 0, szFileName, _countof(szFileName)); SetDlgItemText(hwndDlg, IDC_EDIT_FILENAME, szFileName); DragFinish(hDrop); return FALSE; }; }; return FALSE; };
|
线程局部存储
原理
线程局部存储TLS结构包含索引数组(或进程位数组)和存储槽。索引数组为4字,一共64位,每一位对应存储槽数组中的一个元素。存储槽为一个64个LPVOID类型元素的数组,每个LPVOID元素为一个指向具体内容的指针。每一个线程都有一个存储槽,但线程之间共用同一个索引数组。索引数组的每一位为1则代表所有线程的存储槽该数组索引位置均有地址指向数据,为0则没有。索引数组位数最少为TLS_MINIMUM_AVAILABLE即64个,需要时系统分配更多,最高记录1088个。用NtCurrentTeb
获取当前线程的线程环境块TEB结构指针。
1 2 3 4 5 6 7 8 9 10 11 12
| typedef struct _TEB { PVOID Reserved1[12]; PPEB ProcessEnvironmentBlock; PVOID Reserved2[399]; BYTE Reserved3[1952]; PVOID TlsSlots[64]; BYTE Reserved4[8]; PVOID Reserved5[26]; PVOID ReservedForOle; PVOID Reserved6[4]; PVOID TlsExpansionSlots; } TEB, * PTEB;
|
TlsAlloc
从进程中分配一个TLS索引,把索引数组某位设为1,且每个线程存储槽中该索引位置初始化为NULL,进程中每个线程可使用该索引操作自己的数据。分配到的TLS索引为了每个线程都要访问,一般要设为全局变量。
1
| DWORD WINAPI TlsAlloc(VOID);
|
TlsSetValue
在存储槽指定索引处写入数据:
1 2 3 4
| BOOL WINAPI TlsSetValue( _In_ DWORD dwTlsIndex, _In_opt_ LPVOID lpTlsValue );
|
TlsGetValue
获取存储槽中指定索引处的数据:
1 2 3
| LPVOID WINAPI TlsGetValue( _In_ DWORD dwTlsIndex );
|
TlsFree
释放TLS索引,把索引数组中对应位置0,并把每个线程存储槽中该索引位置数据化NULL。注意它不释放内容指针指向内存,自己搞的自己手动释放。
1 2 3
| BOOL WINAPI TlsFree( _In_ DWORD dwTlsIndex );
|
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| #include <windows.h> #include "resource.h"
#define THREADCOUNT 5
DWORD g_dwTlsIndex; HWND g_hwndDlg;
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
DWORD WINAPI ThreadProc(LPVOID lpParameter); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { HANDLE hThread[THREADCOUNT] = { 0 }; switch (uMsg) { case WM_INITDIALOG: { g_hwndDlg = hwndDlg; return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_OK: { g_dwTlsIndex = TlsAlloc(); if (g_dwTlsIndex == TLS_OUT_OF_INDEXES) { MessageBox(hwndDlg, TEXT("TlsAlloc函数调用失败!"), TEXT("错误提示"), MB_OK); return FALSE; }; SetDlgItemText(g_hwndDlg, IDC_EDIT_TLSSLOTS, TEXT("")); for (int i = 0; i < THREADCOUNT; i++) if ((hThread[i] = CreateThread(NULL, 0, ThreadProc, (LPVOID)i, 0, NULL)) != NULL) CloseHandle(hThread[i]); WaitForMultipleObjects(THREADCOUNT, hThread, TRUE, INFINITE); TlsFree(g_dwTlsIndex); break; }; case IDCANCEL: { EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; }; return FALSE; }; DWORD WINAPI ThreadProc(LPVOID lpParameter) { LPVOID lpData = NULL; TCHAR szBuf[64] = { 0 }; lpData = new BYTE[256]; ZeroMemory(lpData, 256); if (!TlsSetValue(g_dwTlsIndex, lpData)) { wsprintf(szBuf, TEXT("线程%d调用TlsSetValue失败"), (INT)lpParameter); MessageBox(g_hwndDlg, szBuf, TEXT("错误提示"), MB_OK); delete[]lpData; return 0; }; lpData = TlsGetValue(g_dwTlsIndex); if (!lpData && GetLastError() != ERROR_SUCCESS) { wsprintf(szBuf, TEXT("线程%d调用TlsGetValue失败"), (INT)lpParameter); MessageBox(g_hwndDlg, szBuf, TEXT("错误提示"), MB_OK); }; wsprintf(szBuf, TEXT("线程%d的索引%d处的值:0x%p\r\n"), (INT)lpParameter, g_dwTlsIndex, lpData); SendMessage(GetDlgItem(g_hwndDlg, IDC_EDIT_TLSSLOTS), EM_SETSEL, -1, -1); SendMessage(GetDlgItem(g_hwndDlg, IDC_EDIT_TLSSLOTS), EM_REPLACESEL, TRUE, (LPARAM)szBuf); delete[]lpData; return 0; };
|
静态TLS
使用这种方式声明为全局变量或静态变量:
1
| __declspec(thread) LPVOID gt_lpData;
|
该前缀在编译链接时把变量放到.tls区段,如果是Release版本可能会被优化到别的区段。一般用gt_前缀表示全局TLS变量,用st_表示静态TLS变量。当然每个线程的该变量都是独立的,上面那个例子可以改写成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #include <windows.h> #include "resource.h"
#define THREADCOUNT 5
__declspec(thread) LPVOID gt_lpData = NULL; HWND g_hwndDlg;
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
DWORD WINAPI ThreadProc(LPVOID lpParameter); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { HANDLE hThread[THREADCOUNT]; switch (uMsg) { case WM_INITDIALOG: { g_hwndDlg = hwndDlg; return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_OK: { SetDlgItemText(g_hwndDlg, IDC_EDIT_TLSSLOTS, TEXT("")); for (int i = 0; i < THREADCOUNT; i++) if ((hThread[i] = CreateThread(NULL, 0, ThreadProc, (LPVOID)i, 0, NULL)) != NULL) CloseHandle(hThread[i]); break; }; case IDCANCEL: { EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; }; return FALSE; }; DWORD WINAPI ThreadProc(LPVOID lpParameter) { TCHAR szBuf[64] = { 0 }; gt_lpData = new BYTE[256]; ZeroMemory(gt_lpData, 256); wsprintf(szBuf, TEXT("线程%d的gt_lpData值:0x%p\r\n"), (INT)lpParameter, gt_lpData); SendMessage(GetDlgItem(g_hwndDlg, IDC_EDIT_TLSSLOTS), EM_SETSEL, -1, -1); SendMessage(GetDlgItem(g_hwndDlg, IDC_EDIT_TLSSLOTS), EM_REPLACESEL, TRUE, (LPARAM)szBuf); delete[]gt_lpData; return 0; };
|
Windows钩子
SetWindowsHookEx
将应用程序定义的钩子函数安装到钩子链中,钩子函数用于监视系统某些类型事件:
1 2 3 4 5 6
| HHOOK WINAPI SetWindowsHookEx( _In_ INT idHook, _In_ HOOKPROC lpfn, _In_ HINSTANCE hMod, _In_ DWORD dwThreadId );
|
idHook钩子函数类型常用的有:
枚举值 |
含义 |
WH_GETMESSAGE |
用GetMessage 或PeekMessage 获取消息时调用钩子函数。 |
WH_KEYBOARD |
用GetMessage 或PeekMessage 获取键盘消息WM_KEYUP或WM_KEYDOWN时调用钩子函数。 |
WH_MOUSE |
用GetMessage 或PeekMessage 获取鼠标消息时调用钩子函数。 |
WH_CALLWNDPROC |
系统消息发送到目标从窗口过程前调用钩子函数。 |
WH_CALLWNDPROCRET |
目标窗口过程处理完消息后调用钩子函数。 |
WH_DEBUG |
/调用其他钩子函数前调用本钩子函数。 |
WH_CBT |
激活/创建/销毁/最小化/最大化/移动/调整窗口前、完成系统命令前、从系统消息队列中删除鼠标/键盘事件前、设置键盘焦点前、与系统消息队列同步前调用钩子函数。 |
WH_FOREGROUNDIDLE |
前台线程即将变为空闲时调用钩子函数。 |
WH_JOURNALRECORD |
记录发送给系统消息队列所有信息,钩子函数不需要在DLL中。 |
WH_JOURNALPLAYBACK |
回放日志记录钩子记录的系统事件,钩子函数不需要在DLL中。 |
WH_MSGFILTER |
用户对对话框/消息框/菜单/滚动条操作后,系统发送对应消息前调用钩子函数,局部钩子。 |
WH_SYSMSGFILTER |
同WH_MSGFILTER,系统钩子。 |
WH_SHELL |
Windows Shell接收通知事件前调用钩子函数,如Shell被激活/重绘。 |
当监视当前进程时,lpfn指向的钩子函数位于当前进程程序代码中,hMod为NULL,dwThreadId设置为当前进程的线程ID。当监视其他进程时,lpfn指向的钩子函数必须位于DLL中,hMod为包含钩子函数的DLL模块句柄,dwThreadId设置为其他进程创建的线程ID。当设置全局系统钩子时,lpfn指向的钩子函数必须位于DLL中,hMod为包含钩子函数的DLL模块句柄,dwThreadId为0。
钩子回调函数定义如下:
1 2 3 4 5
| LRESULT CALLBACK HookProc( INT nCode, WPARAM wParam, LPARAM lParam );
|
CallNextHookEx
把消息事件传递给钩子链中下一个钩子函数。系统中可安装多个不同类型或相同类型的钩子,每种钩子有维护一个钩子链,是同种类型钩子的钩子函数指针列表,最近加入的钩子放在链表头部,每个钩子函数有义务把消息事件传递下去。
1 2 3 4 5 6
| LRESULT WINAPI CallNextHookEx( _In_opt_ HHOOK hhk, _In_ INT nCode, _In_ WPARAM wParam, _In_ LPARAM lParam );
|
nCode、wParam、lParam用钩子函数同名参数即可。
UnhookWindowsHookEx
卸载安装在钩子链中的钩子函数:
1 2 3
| BOOL WINAPI UnhookWindowsHookEx( _In_ HHOOK hhk );
|
ToUnicode
把虚拟键码转为Unicode字符:
1 2 3 4 5 6 7 8
| INT WINAPI ToUnicode( _In_ UINT wVirtKey, _In_ UINT wScanCode, _In_opt_ CONST PBYTE lpKeyState, _Out_ LPWSTR pwszBuff, _In_ INT cchBuff, _In_ UINT wFlags );
|
就钩子函数应用场景来说,wVirtKey用wParam,wScanCode用lParam高16位即可,lpKeyState不讲了看下面例子用法。
例子
定义一个WH_KEYBOARD键盘钩子的钩子函数。对于回调函数的nCode,当小于0时钩子函数必须将消息传递给CallNextHookEx
并返回其返回值,当为HC_ACTION时表示wParam和lParam中含有有关击键消息,处理完用CallNextHookEx
传递下去,也可以直接返回TRUE丢弃信息并组织信息向下传播。wParam包含虚拟键码,lParam包含消息重复计数、扫描码、扩展键标志、状态描述吗、先前键状态标志、转换状态标志等。
DLL头文件为:
1 2 3 4 5 6 7 8 9 10 11 12
| #pragma once
#ifdef DLL_EXPORT #define DLL_API extern "C" __declspec(dllexport) #else #define DLL_API extern "C" __declspec(dllimport) #endif
DLL_API BOOL InstallHook(int idHook, DWORD dwThreadId, HWND hwnd); DLL_API BOOL UninstallHook();
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
|
DLL源文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #include <Windows.h> #include <tchar.h> #define DLL_EXPORT #include "HookDll.h"
HINSTANCE g_hMod; HHOOK g_hHookKeyboard; TCHAR g_szBuf[256] = { 0 }; #pragma data_seg("Shared") HWND g_hwnd = NULL; #pragma data_seg() #pragma comment(linker, "/SECTION:Shared,RWS") BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { g_hMod = hModule; break; }; case DLL_THREAD_ATTACH: {}; case DLL_THREAD_DETACH: {}; case DLL_PROCESS_DETACH: { break; }; }; return TRUE; };
BOOL InstallHook(int idHook, DWORD dwThreadId, HWND hwnd) { if (!g_hHookKeyboard) { g_hwnd = hwnd; g_hHookKeyboard = SetWindowsHookEx(idHook, KeyboardProc, g_hMod, dwThreadId); if (!g_hHookKeyboard) return FALSE; }; return TRUE; }; BOOL UninstallHook() { if (g_hHookKeyboard) if (!UnhookWindowsHookEx(g_hHookKeyboard)) return FALSE; g_hHookKeyboard = NULL; return TRUE; };
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { BYTE bKeyState[256]; COPYDATASTRUCT copyDataStruct = { 0 }; if (nCode < 0) return CallNextHookEx(NULL, nCode, wParam, lParam); if (nCode == HC_ACTION) { GetKeyboardState(bKeyState); bKeyState[VK_SHIFT] = HIBYTE(GetKeyState(VK_SHIFT)); ZeroMemory(g_szBuf, sizeof(g_szBuf)); ToUnicode(wParam, lParam >> 16, bKeyState, g_szBuf, _countof(g_szBuf), 0); copyDataStruct.cbData = sizeof(g_szBuf); copyDataStruct.lpData = g_szBuf; SendMessage(g_hwnd, WM_COPYDATA, (WPARAM)g_hwnd, (LPARAM)©DataStruct); }; return CallNextHookEx(NULL, nCode, wParam, lParam); };
|
监视程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <windows.h> #include "HookDll.h" #include "resource.h" #pragma comment(lib, "HookDll.lib")
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_INSTALLHOOK: { InstallHook(WH_KEYBOARD, 0, hwndDlg); break; }; case IDC_BTN_UNINSTALLHOOK: { UninstallHook(); break; }; case IDCANCEL: { UninstallHook(); EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; case WM_COPYDATA: { SendMessage(GetDlgItem(hwndDlg, IDC_EDIT_KEYBOARD), EM_SETSEL, -1, -1); SendMessage(GetDlgItem(hwndDlg, IDC_EDIT_KEYBOARD), EM_REPLACESEL, TRUE, (LPARAM)(LPTSTR)(((PCOPYDATASTRUCT)lParam)->lpData)); return TRUE; }; }; return FALSE; };
|
获取其他窗口信息时最好用PostMessage
而不是SendMessage
,要不处理事件可能过长。
变量共享
创建一个叫“Shared”的区段,里面放个变量。注意只能将已初始化的变量保存到自定义段中,没初始化就跑别的段去了。结尾表示自定义段结束,回到默认段。
1 2 3
| #pragma data_seg("Shared") HWND g_hwnd = NULL; #pragma data_seg()
|
为“Shared”段指定相关属性,R为READ,W为WRITE,E为EXECUTE,S为SHARED。
1
| #pragma comment(linker,"/SECTION:Shared,RWS")
|
DLL注入
前提芝士
下面小节有杂七杂八的新芝士,这里一口气介绍完。
对于桌面上图标,每个图标都为一个列表项。发送LVM_GETITEMTEXT获取列表项文本,wParam为列表项索引,lParam为指向LVITEM结构的指针。 发送LVM_GETITEMPOSITION获取列表项位置,wParam指定为列表项索引,lParam为指向POINT结构指针,返回列表项左上角坐标。发送LVM_FINDITEM查找桌面上具有指定列表项文本的列表项,wParam指定开始搜索的列表项索引,不包括指定项,-1从头开始,lParam是指向LVFINDINFO结构指针,包含有关要搜索内容信息。发送LVM_SETITEMPOSITION设置该列表项位置,wParam为列表项索引,LOWORD(lParam)和HIWORD(lParam)为左上角的X、Y坐标。
系统中程序管理器Program Manager窗口类名为Progman(Win10为WorkerW?),其下有一个窗口类名SHELLDLL_DefView的子窗口,旗下有一个窗口类名为SysListView32的子窗口,即桌面列表视图控件。
GetTopWindow
查找指定父窗口关联的第一个子窗口句柄:
1 2 3
| HWND WINAPI GetTopWindow( _In_opt_ HWND hWnd );
|
GetNativeSystemInfo
之前GetSystemInfo
返回的SYSTEM_INFO结构的wProcessorArchitecture如果为PROCESSOR_ARCHITECTURE_INTEL则32位,为PROCESSOR_ARCHITECTURE_AMD64或PROCESSOR_ARCHITECTURE_IA64则64位,但这返回的是系统架构。如果想要获取当前进程为64位还是在WoW64下运行的32位应用程序,如:
1 2 3 4 5 6 7
| BOOL Is64bitSystem(VOID) { SYSTEM_INFO si = { 0 }; GetNativeSystemInfo(&si); if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 || si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) return TRUE; return FALSE; };
|
IsWow64Process
判断一个进程是否运行在WoW64环境中:
1 2 3 4
| BOOL WINAPI IsWow64Process( _In_ HANDLE hProcess, _Out_ PBOOL Wow64Process );
|
hProcess需要PROCESS_QUERY_INFORMATION或PROCESS_QUERY_LIMITED_INFORMATION。
CreateRemoteThread
在其他进程中创建一个远程线程:
1 2 3 4 5 6 7 8 9
| HANDLE WINAPI CreateRemoteThread( _In_ HANDLE hProcess, _In_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ SIZE_T dwStackSize, _In_ LPTHREAD_START_ROUTINE lpStartAddress, _In_ LPVOID lpParameter, _In_ DWORD dwCreationFlags, _Out_ LPDWORD lpThreadId );
|
SystemParameterInfo
用于获取或设置系统参数,包括桌面、图标、输入(键盘、鼠标、输入语言等)、菜单、电源、屏保、超时、UI效果、窗口和辅助功能等。
1 2 3 4 5 6
| BOOL WINAPI SystemParameterInfo( _In_ UINT uiAction, _In_ UINT uiParam, _Inout_ PVOID pvParam, _In_ UINT fWinIni );
|
uiAction系统参数很多自己去查MSDN,其决定uiParam和pvParam的具体含义。当fWinIni为SPIF_UPDATEINIFILE表示将新的系统参数写入用户配置文件,为SPIF_SENDCHANGE表示既写入用户配置文件也广播WM_SETTINGCHANGE消息到所有顶级窗口,为0则不需要。
Wow64DisableWow64FsRedirection/Wow64RevertWow64FsRedirection
禁用/恢复调用线程的文件系统重定向:
1 2 3 4 5 6
| BOOL WINAPI Wow64DisableWow64FsRedirection( _Out_ PVOID* ppOldValue ); BOOL WINAPI Wow64RevertWow64FsRedirection( _In_ PVOID pOldValue );
|
这俩玩意儿一般这么配合着用:
1 2 3
| LPVOID pOldValue = NULL; Wow64DisableWow64FsRedirection(&pOldValue); Wow64RevertWow64FsRedirection(&pOldValue);
|
Windows钩子注入
被注入的DLL头文件:
1 2 3 4 5 6 7 8 9 10
| #pragma once
#ifdef DLL_EXPORT #define DLL_API extern "C" __declspec(dllexport) #else #define DLL_API extern "C" __declspec(dllimport) #endif
DLL_API BOOL InstallHook(int idHook, DWORD dwThreadId); DLL_API BOOL UninstallHook();
|
被注入的DLL源文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
| #include <Windows.h> #include <Commctrl.h> #include "resource.h" #define DLL_EXPORT #include "DIPSHookDll.h"
HINSTANCE g_hMod; HHOOK g_hHook; TCHAR g_szRegSubKey[] = TEXT("Software\\Desktop Item Position Saver");
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam); INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); VOID SaveListViewItemPositions(HWND hwndLV); VOID RestoreListViewItemPositions(HWND hwndLV); BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { g_hMod = hModule; break; }; case DLL_THREAD_ATTACH: {}; case DLL_THREAD_DETACH: {}; case DLL_PROCESS_DETACH: { break; }; }; return TRUE; };
BOOL InstallHook(int idHook, DWORD dwThreadId) { if (!g_hHook) { g_hHook = SetWindowsHookEx(idHook, GetMsgProc, g_hMod, dwThreadId); if (!g_hHook) return FALSE; }; PostThreadMessage(dwThreadId, WM_NULL, 0, 0); return TRUE; }; BOOL UninstallHook() { if (g_hHook) if (!UnhookWindowsHookEx(g_hHook)) return FALSE; g_hHook = NULL; return TRUE; };
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) { static BOOL bFirst = TRUE; if (nCode < 0) return CallNextHookEx(NULL, nCode, wParam, lParam); if (nCode == HC_ACTION) if (bFirst) { bFirst = FALSE; CreateDialogParam(g_hMod, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); }; return CallNextHookEx(NULL, nCode, wParam, lParam); }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_APP: { if (lParam) SaveListViewItemPositions((HWND)wParam); else RestoreListViewItemPositions((HWND)wParam); return TRUE; }; case WM_CLOSE: { DestroyWindow(hwndDlg); return TRUE; }; }; return FALSE; }; VOID SaveListViewItemPositions(HWND hwndLV) { int nCount; HKEY hKey; LVITEM lvi = { 0 }; TCHAR szName[MAX_PATH] = { 0 }; POINT pt; RegDeleteKey(HKEY_CURRENT_USER, g_szRegSubKey); nCount = SendMessage(hwndLV, LVM_GETITEMCOUNT, 0, 0); RegCreateKeyEx(HKEY_CURRENT_USER, g_szRegSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, NULL); lvi.mask = LVIF_TEXT; lvi.pszText = szName; lvi.cchTextMax = _countof(szName); for (int i = 0; i < nCount; i++) { ZeroMemory(szName, _countof(szName) * sizeof(TCHAR)); SendMessage(hwndLV, LVM_GETITEMTEXT, i, (LPARAM)&lvi); SendMessage(hwndLV, LVM_GETITEMPOSITION, i, (LPARAM)&pt); RegSetValueEx(hKey, szName, 0, REG_BINARY, (LPBYTE)&pt, sizeof(pt)); }; RegCloseKey(hKey); }; VOID RestoreListViewItemPositions(HWND hwndLV) { HKEY hKey; TCHAR szName[MAX_PATH] = { 0 }; POINT pt; DWORD dwType; LONG_PTR lStyle; LONG lResult; LVFINDINFO lvfi = { 0 }; int nItem; RegOpenKeyEx(HKEY_CURRENT_USER, g_szRegSubKey, 0, KEY_QUERY_VALUE, &hKey); lStyle = GetWindowLongPtr(hwndLV, GWL_STYLE); if (lStyle & LVS_AUTOARRANGE) SetWindowLongPtr(hwndLV, GWL_STYLE, lStyle & ~LVS_AUTOARRANGE); lResult = ERROR_SUCCESS; for (int i = 0; lResult != ERROR_NO_MORE_ITEMS; i++) { DWORD dwchName = _countof(szName); DWORD dwcbDaata = sizeof(pt); lResult = RegEnumValue(hKey, i, szName, &dwchName, NULL, &dwType, (LPBYTE)&pt, &dwcbDaata); if (lResult == ERROR_NO_MORE_ITEMS) continue; lvfi.flags = LVFI_STRING; lvfi.psz = szName; if ((dwType == REG_BINARY) && (dwcbDaata == sizeof(pt))) { nItem = SendMessage(hwndLV, LVM_FINDITEM, -1, (LPARAM)&lvfi); if (nItem != -1) SendMessage(hwndLV, LVM_SETITEMPOSITION, nItem, MAKELPARAM(pt.x, pt.y)); }; }; SetWindowLongPtr(hwndLV, GWL_STYLE, lStyle); RegCloseKey(hKey); };
|
控制程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| #include <windows.h> #include "resource.h" #include "DIPSHookDll.h" #pragma comment(lib, "DIPSHookDll.lib")
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { static HWND hwndLV; HWND hwndDIPSServer; switch (uMsg) { case WM_INITDIALOG: { hwndLV = GetTopWindow(GetTopWindow(FindWindow(TEXT("ProgMan"), NULL))); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_SAVE), FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_RESTORE), FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_UNINSTALLHOOK), FALSE); return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_INSTALLHOOK: { InstallHook(WH_GETMESSAGE, GetWindowThreadProcessId(hwndLV, NULL)); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_SAVE), TRUE); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_RESTORE), TRUE); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_UNINSTALLHOOK), TRUE); MessageBox(hwndDlg, TEXT("安装消息钩子成功"), TEXT("成功"), MB_OK); break; }; case IDC_BTN_UNINSTALLHOOK: { hwndDIPSServer = FindWindow(NULL, TEXT("DIPSServer")); SendMessage(hwndDIPSServer, WM_CLOSE, 0, 0); UninstallHook(); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_SAVE), FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_RESTORE), FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_UNINSTALLHOOK), FALSE); MessageBox(hwndDlg, TEXT("卸载消息钩子成功"), TEXT("成功"), MB_OK); break; }; case IDC_BTN_SAVE: { hwndDIPSServer = FindWindow(NULL, TEXT("DIPSServer")); SendMessage(hwndDIPSServer, WM_APP, (WPARAM)hwndLV, TRUE); MessageBox(hwndDlg, TEXT("保存桌面图标成功"), TEXT("成功"), MB_OK); break; }; case IDC_BTN_RESTORE: { hwndDIPSServer = FindWindow(NULL, TEXT("DIPSServer")); SendMessage(hwndDIPSServer, WM_APP, (WPARAM)hwndLV, FALSE); MessageBox(hwndDlg, TEXT("恢复桌面图标成功"), TEXT("成功"), MB_OK); break; }; case IDCANCEL: { if (FindWindow(NULL, TEXT("DIPSServer"))) SendMessage(hwndDlg, WM_COMMAND, IDC_BTN_UNINSTALLHOOK, 0); EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; }; return FALSE; };
|
远程线程注入
用CreateRemoteThread
在目标进程中创建远程线程,但麻烦的是lpStartAddress线程函数也必须在目标进程地址空间中。我们无法将可执行代码等直接写入目标进程地址空间,获取API函数因ASLR也异常麻烦。但Kernel32.dll、User32.dll和Gdi32.dll等常用DLL在不同进程中被载入相同内存地址,但每次开机加载地址会变一次。
方法就是直接拿CreateRemoteThread
启动LoadLibraryA
/LoadLibraryW
,字符串参数用VirtualAllocEx
在目标进程分配内存地址,用WriteProcessMemory
写入DLL文件名称。
DLL源文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| #include <Windows.h> #include <tchar.h> BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { TCHAR szBuf[MAX_PATH] = { 0 }; LPBYTE lpAddress = NULL; MEMORY_BASIC_INFORMATION mbi = { 0 }; int nLen; TCHAR szModName[MAX_PATH] = { 0 }; HWND hwndRemoteApp; switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { hwndRemoteApp = FindWindow(TEXT("#32770"), TEXT("RemoteApp")); while (VirtualQuery(lpAddress, &mbi, sizeof(mbi)) == sizeof(mbi)) { if (mbi.State == MEM_FREE) mbi.AllocationBase = mbi.BaseAddress; if ((mbi.AllocationBase == NULL) || (mbi.AllocationBase == hModule) || (mbi.BaseAddress != mbi.AllocationBase)) nLen = 0; else nLen = GetModuleFileName(HMODULE(mbi.AllocationBase), szModName, _countof(szModName)); if (nLen > 0) { wsprintf(szBuf, TEXT("%p\t%s\r\n"), mbi.AllocationBase, szModName); SendDlgItemMessage(hwndRemoteApp, 1005, EM_SETSEL, -1, -1); SendDlgItemMessage(hwndRemoteApp, 1005, EM_REPLACESEL, TRUE, (LPARAM)szBuf); }; lpAddress += mbi.RegionSize; }; break; }; case DLL_THREAD_ATTACH: {}; case DLL_THREAD_DETACH: {}; case DLL_PROCESS_DETACH: { break; }; }; return TRUE; };
|
控制程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
| #include <windows.h> #include <tchar.h> #include <TlHelp32.h> #include "resource.h"
HWND g_hwndDlg;
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); DWORD WINAPI ThreadProc(LPVOID lpParameter); BOOL InjectDll(DWORD dwProcessId, LPTSTR lpDllPath); BOOL EjectDll(DWORD dwProcessId, LPTSTR lpDllPath); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { HANDLE hThread = NULL; DWORD dwProcessId; TCHAR szDllPath[MAX_PATH] = { 0 }; switch (uMsg) { case WM_INITDIALOG: { g_hwndDlg = hwndDlg; SetDlgItemText(hwndDlg, IDC_EDIT_PROCESSID, TEXT("请输入进程ID")); SetDlgItemText(hwndDlg, IDC_EDIT_DLLPATH, TEXT("F:\\Source\\Windows\\Chapter16\\RemoteDll\\Debug\\RemoteDll.dll")); return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_INJECT: { hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL); if (hThread) CloseHandle(hThread); break; }; case IDC_BTN_EJECT: { dwProcessId = GetDlgItemInt(hwndDlg, IDC_EDIT_PROCESSID, NULL, FALSE); GetDlgItemText(hwndDlg, IDC_EDIT_DLLPATH, szDllPath, _countof(szDllPath)); EjectDll(dwProcessId, szDllPath); break; }; case IDCANCEL: { SendMessage(hwndDlg, WM_COMMAND, IDC_BTN_EJECT, 0); EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; }; return FALSE; }; DWORD WINAPI ThreadProc(LPVOID lpParameter) { DWORD dwProcessId; TCHAR szDllPath[MAX_PATH] = { 0 }; dwProcessId = GetDlgItemInt(g_hwndDlg, IDC_EDIT_PROCESSID, NULL, FALSE); GetDlgItemText(g_hwndDlg, IDC_EDIT_DLLPATH, szDllPath, _countof(szDllPath)); return InjectDll(dwProcessId, szDllPath); }; BOOL InjectDll(DWORD dwProcessId, LPTSTR lpDllPath) { HANDLE hProcess = NULL; LPTSTR lpDllPathRemote = NULL; HANDLE hThread = NULL; hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, dwProcessId); if (!hProcess) return FALSE; int cbDllPath = (_tcslen(lpDllPath) + 1) * sizeof(TCHAR); lpDllPathRemote = (LPTSTR)VirtualAllocEx(hProcess, NULL, cbDllPath, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (!lpDllPathRemote) return FALSE; if (!WriteProcessMemory(hProcess, lpDllPathRemote, lpDllPath, cbDllPath, NULL)) return FALSE; PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW"); if (!pfnThreadRtn) return FALSE; hThread = CreateRemoteThread(hProcess, NULL, 0, pfnThreadRtn, lpDllPathRemote, 0, NULL); if (!hThread) return FALSE; WaitForSingleObject(hThread, INFINITE); if (!lpDllPathRemote) VirtualFreeEx(hProcess, lpDllPathRemote, 0, MEM_RELEASE); if (hThread) CloseHandle(hThread); if (hProcess) CloseHandle(hProcess); return TRUE; }; BOOL EjectDll(DWORD dwProcessId, LPTSTR lpDllPath) { HANDLE hSnapshot; MODULEENTRY32 me = { sizeof(MODULEENTRY32) }; BOOL bRet; BOOL bFound = FALSE; HANDLE hProcess = NULL; HANDLE hThread = NULL; hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId); if (hSnapshot == INVALID_HANDLE_VALUE) return FALSE; bRet = Module32First(hSnapshot, &me); while (bRet) { if (_tcsicmp(TEXT("RemoteDll.dll"), me.szModule) == 0 || _tcsicmp(lpDllPath, me.szExePath) == 0) { bFound = TRUE; break; }; bRet = Module32Next(hSnapshot, &me); }; if (!bFound) return FALSE; hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION, FALSE, dwProcessId); if (!hProcess) return FALSE; PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "FreeLibrary"); if (!pfnThreadRtn) return FALSE; hThread = CreateRemoteThread(hProcess, NULL, 0, pfnThreadRtn, me.modBaseAddr, 0, NULL); if (!hThread) return FALSE; WaitForSingleObject(hThread, INFINITE); if (hSnapshot != INVALID_HANDLE_VALUE) CloseHandle(hSnapshot); if (hThread) CloseHandle(hThread); if (hProcess) CloseHandle(hProcess); return TRUE; };
|
函数转发机制注入
如kernel32.dll有许多转发函数,用VS Developer Command Prompt:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| dumpbin -exports kernel32.dll Microsoft (R) COFF/PE Dumper Version 14.39.33523.0 Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file kernel32.dll
File Type: DLL
Section contains the following exports for KERNEL32.dll
00000000 characteristics 2EE8ABD0 time date stamp 0.00 version 1 ordinal base 1671 number of functions 1671 number of names
ordinal hint RVA name
1 0 AcquireSRWLockExclusive (forwarded to NTDLL.RtlAcquireSRWLockExclusive) 2 1 AcquireSRWLockShared (forwarded to NTDLL.RtlAcquireSRWLockShared) 3 2 000187B0 ActivateActCtx 4 3 00014620 ActivateActCtxWorker 5 4 00020FA0 ActivatePackageVirtualizationContext
|
如前2个函数没有RVA,因为kernel32.dll中根本没有转发函数的实现,调用时直接转发到NTDLL.DLL对应函数中。实现具有函数转发器功能的DLL可以:
1
| #pragma comment(linker, "/export:被转发函数=某模块.目标函数")
|
函数转发代码用AheadLib批量生成。例如一个单纯的函数转发器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <Windows.h> BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { break; }; case DLL_THREAD_ATTACH: {}; case DLL_THREAD_DETACH: {}; case DLL_PROCESS_DETACH: { break; }; }; return TRUE; }; #pragma comment(linker, "/export:MyMessageBox=User32.MessageBoxW")
|
调用程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <windows.h> #include "resource.h"
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { HMODULE hFunctionForwarder = NULL; typedef BOOL(WINAPI* pfnMyMessageBox)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType); pfnMyMessageBox pMyMessageBox = NULL; switch (uMsg) { case WM_INITDIALOG: { return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_MYMESSAGEBOX: { hFunctionForwarder = LoadLibrary(TEXT("FunctionForwarderDll.dll")); if (hFunctionForwarder) { pMyMessageBox = (pfnMyMessageBox)GetProcAddress(hFunctionForwarder, "MyMessageBox"); if (pMyMessageBox) pMyMessageBox(hwndDlg, TEXT("MyMessageBox"), TEXT("提示"), MB_OK); FreeLibrary(hFunctionForwarder); }; break; }; case IDCANCEL: { EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; }; return FALSE; };
|
假如某个程序调用SomeDLL.dll,那么我们利用函数转发搞一个新的SomeDLL.dll,旧的更名为SomeDLLReplace.dll,然后不需要拦截的直接从SomeDLL.dll转发到SomeDLLReplace.dll中。
实例:DrawDll.dll中导出一个绘制矩形和绘制椭圆的函数,然后DrawDll2.dll更名为DrawDll.dll并替换,实际链接新的DrawDllReplace.dll,实施DLL劫持,可执行程序DrawApp对DrawDll.dll调用时矩形函数画椭圆,椭圆函数画矩形。
DrawDll.h:
1 2 3 4 5 6 7 8 9 10
| #pragma once
#ifdef DLL_EXPORT #define DLL_API extern "C" __declspec(dllexport) #else #define DLL_API extern "C" __declspec(dllimport) #endif
DLL_API VOID DrawRectangle(HWND hwnd); DLL_API VOID DrawEllipse(HWND hwnd);
|
DrawDll.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <Windows.h> #include <tchar.h> #define DLL_EXPORT #include "DrawDll.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; };
VOID DrawRectangle(HWND hwnd) { HDC hdc; hdc = GetDC(hwnd); Rectangle(hdc, 10, 10, 110, 110); ReleaseDC(hwnd, hdc); }; VOID DrawEllipse(HWND hwnd) { HDC hdc; hdc = GetDC(hwnd); Ellipse(hdc, 10, 10, 110, 110); ReleaseDC(hwnd, hdc); };
|
DrawDll2.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include <Windows.h> BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { break; }; case DLL_THREAD_ATTACH: {}; case DLL_THREAD_DETACH: {}; case DLL_PROCESS_DETACH: { break; }; }; return TRUE; }; #pragma comment(linker, "/export:DrawRectangle=DrawDllReplace.DrawRectangle") #pragma comment(linker, "/export:DrawEllipse=DrawDllReplace.DrawEllipse")
|
DrawDllReplace.h:
1 2 3 4 5 6 7 8 9 10
| #pragma once
#ifdef DLL_EXPORT #define DLL_API extern "C" __declspec(dllexport) #else #define DLL_API extern "C" __declspec(dllimport) #endif
DLL_API VOID DrawRectangle(HWND hwnd); DLL_API VOID DrawEllipse(HWND hwnd);
|
DrawDllReplace.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <Windows.h> #include <tchar.h> #define DLL_EXPORT #include "DrawDllReplace.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; };
VOID DrawRectangle(HWND hwnd) { HDC hdc; hdc = GetDC(hwnd); Ellipse(hdc, 10, 10, 110, 110); ReleaseDC(hwnd, hdc); }; VOID DrawEllipse(HWND hwnd) { HDC hdc; hdc = GetDC(hwnd); Rectangle(hdc, 10, 10, 110, 110); ReleaseDC(hwnd, hdc); };
|
DrawApp.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| #include <windows.h> #include "resource.h" #include "DrawDll.h" #pragma comment(lib, "DrawDll.lib")
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { static BOOL bFirst = TRUE; static BOOL bRect; HDC hdc; PAINTSTRUCT ps; switch (uMsg) { case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_DRAWRECT: { bFirst = FALSE; bRect = TRUE; InvalidateRect(hwndDlg, NULL, TRUE); break; }; case IDC_BTN_DRAWELLIPSE: { bFirst = FALSE; bRect = FALSE; InvalidateRect(hwndDlg, NULL, TRUE); break; }; case IDCANCEL: { EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; case WM_PAINT: { hdc = BeginPaint(hwndDlg, &ps); if (!bFirst) { if (bRect) DrawRectangle(hwndDlg); else DrawEllipse(hwndDlg); }; EndPaint(hwndDlg, &ps); return TRUE; }; }; return FALSE; };
|
CreateProcess写ShellCode注入
用CreateProcess
以挂起模式创建一个子进程,在子进程主线程运行前可项子进程地址空间注入ShellCode并率先获得执行权,ShellCode执行完成再转去执行主线程原代码。
以下代码ShellCode中最后部分需要根据实际更改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| #include <windows.h> #include "resource.h"
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); BOOL CreateProcessAndInjectDll(); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_CREATE: { CreateProcessAndInjectDll(); break; }; case IDCANCEL: { EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; }; return FALSE; }; BOOL CreateProcessAndInjectDll() { STARTUPINFO si = { sizeof(STARTUPINFO) }; PROCESS_INFORMATION pi = { 0 }; TCHAR szExePath[MAX_PATH] = TEXT("ThreeThousandYears.exe"); TCHAR szDllPath[MAX_PATH] = TEXT("MessageBoxDll.dll"); BOOL bRet; BYTE ShellCode[29 + MAX_PATH * sizeof(TCHAR)] = { 0x60, 0x9C, 0x68,0xAA,0xBB,0xCC,0xDD, 0xFF,0x15,0xDD,0xCC,0xBB,0xAA, 0x9D, 0x61, 0xFF,0x25,0xAA,0xBB,0xCC,0xDD, 0xAA,0xAA,0xAA,0xAA, 0xAA,0xAA,0xAA,0xAA, 0, }; bRet = CreateProcess(szExePath, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi); if (!bRet) return FALSE; CONTEXT context; context.ContextFlags = CONTEXT_FULL; if (!GetThreadContext(pi.hThread, &context)) return FALSE; DWORD dwLoadLibraryWAddr = (DWORD)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "LoadLibraryW"); if (!dwLoadLibraryWAddr) return FALSE; LPVOID lpMemoryRemote = VirtualAllocEx(pi.hProcess, NULL, 29 + MAX_PATH * sizeof(TCHAR), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (!lpMemoryRemote) return FALSE; *(PDWORD)(ShellCode + 3) = (DWORD)lpMemoryRemote + 29; *(PDWORD)(ShellCode + 9) = (DWORD)lpMemoryRemote + 21; *(PDWORD)(ShellCode + 17) = (DWORD)lpMemoryRemote + 25; *(PDWORD)(ShellCode + 21) = dwLoadLibraryWAddr; *(PDWORD)(ShellCode + 25) = context.Eip; memcpy_s(ShellCode + 29, MAX_PATH * sizeof(TCHAR), szDllPath, sizeof(szDllPath)); if (!WriteProcessMemory(pi.hProcess, lpMemoryRemote, ShellCode, 29 + MAX_PATH * sizeof(TCHAR), NULL)) return FALSE; context.Eip = (DWORD)lpMemoryRemote; if (!SetThreadContext(pi.hThread, &context)) return FALSE; ResumeThread(pi.hThread); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return TRUE; };
|
APC机制注入
Windows为每个线程维护一个APC队列,类似于远程线程注入,用QueueUserAPC
把APC回调函数设为LoadLibraryA
/LoadLibraryW
地址。因为不知道目标进程中哪些线程处于可通知等待状态,为确保成功需要向目标进程每个线程都注入APC。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
| #include <windows.h> #include <tchar.h> #include <TlHelp32.h> #include "resource.h"
HWND g_hwndDlg;
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); DWORD WINAPI ThreadProc(LPVOID lpParameter); BOOL InjectDll(DWORD dwProcessId, LPTSTR lpDllPath); BOOL EjectDll(DWORD dwProcessId, LPTSTR lpDllPath); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { HANDLE hThread = NULL; DWORD dwProcessId; TCHAR szDllPath[MAX_PATH] = { 0 }; switch (uMsg) { case WM_INITDIALOG: { g_hwndDlg = hwndDlg; SetDlgItemText(hwndDlg, IDC_EDIT_PROCESSID, TEXT("请输入进程ID")); SetDlgItemText(hwndDlg, IDC_EDIT_DLLPATH, TEXT("F:\\Source\\Windows\\Chapter16\\RemoteDll2\\Debug\\RemoteDll.dll")); return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_INJECT: { hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL); if (hThread) CloseHandle(hThread); break; }; case IDC_BTN_EJECT: { dwProcessId = GetDlgItemInt(hwndDlg, IDC_EDIT_PROCESSID, NULL, FALSE); GetDlgItemText(hwndDlg, IDC_EDIT_DLLPATH, szDllPath, _countof(szDllPath)); EjectDll(dwProcessId, szDllPath); break; }; case IDCANCEL: { SendMessage(hwndDlg, WM_COMMAND, IDC_BTN_EJECT, 0); EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; }; return FALSE; }; DWORD WINAPI ThreadProc(LPVOID lpParameter) { DWORD dwProcessId; TCHAR szDllPath[MAX_PATH] = { 0 }; dwProcessId = GetDlgItemInt(g_hwndDlg, IDC_EDIT_PROCESSID, NULL, FALSE); GetDlgItemText(g_hwndDlg, IDC_EDIT_DLLPATH, szDllPath, _countof(szDllPath)); return InjectDll(dwProcessId, szDllPath); }; BOOL InjectDll(DWORD dwProcessId, LPTSTR lpDllPath) { HANDLE hProcess = NULL; LPTSTR lpDllPathRemote = NULL; hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, dwProcessId); if (!hProcess) return FALSE; SIZE_T cbDllPath = (_tcslen(lpDllPath) + 1) * sizeof(TCHAR); lpDllPathRemote = (LPTSTR)VirtualAllocEx(hProcess, NULL, cbDllPath, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (!lpDllPathRemote) return FALSE; if (!WriteProcessMemory(hProcess, lpDllPathRemote, lpDllPath, cbDllPath, NULL)) return FALSE; HMODULE hModule = GetModuleHandle(TEXT("Kernel32")); PAPCFUNC pfnAPC = NULL; if (hModule != NULL) { pfnAPC = (PAPCFUNC)GetProcAddress(hModule, "LoadLibraryW"); if (!pfnAPC) return FALSE; }; HANDLE hSnapshot = INVALID_HANDLE_VALUE; THREADENTRY32 te = { sizeof(THREADENTRY32) }; BOOL bRet = FALSE; HANDLE hThread = NULL; hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (hSnapshot == INVALID_HANDLE_VALUE) return FALSE; bRet = Thread32First(hSnapshot, &te); while (bRet) { if (te.th32OwnerProcessID == dwProcessId) { hThread = OpenThread(THREAD_SET_CONTEXT, FALSE, te.th32ThreadID); if (hThread) { QueueUserAPC(pfnAPC, hThread, (ULONG_PTR)lpDllPathRemote); CloseHandle(hThread); hThread = NULL; }; }; bRet = Thread32Next(hSnapshot, &te); }; CloseHandle(hSnapshot); if (hProcess) CloseHandle(hProcess); return TRUE; };
BOOL EjectDll(DWORD dwProcessId, LPTSTR lpDllPath) { HANDLE hSnapshot = INVALID_HANDLE_VALUE; MODULEENTRY32 me = { sizeof(MODULEENTRY32) }; THREADENTRY32 te = { sizeof(THREADENTRY32) }; BOOL bRet = FALSE; BOOL bFound = FALSE; HMODULE hModule = GetModuleHandle(TEXT("Kernel32")); PAPCFUNC pfnAPC = NULL; if (hModule != NULL) { pfnAPC = (PAPCFUNC)GetProcAddress(hModule, "FreeLibrary"); if (!pfnAPC) return FALSE; }; hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId); if (hSnapshot == INVALID_HANDLE_VALUE) return FALSE; bRet = Module32First(hSnapshot, &me); while (bRet) { if (_tcsicmp(TEXT("RemoteDll.dll"), me.szModule) == 0 || _tcsicmp(lpDllPath, me.szExePath) == 0) { bFound = TRUE; break; }; bRet = Module32Next(hSnapshot, &me); }; CloseHandle(hSnapshot); if (!bFound) return FALSE; hSnapshot = INVALID_HANDLE_VALUE; bRet = FALSE; HANDLE hThread = NULL; hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (hSnapshot == INVALID_HANDLE_VALUE) return FALSE; bRet = Thread32First(hSnapshot, &te); while (bRet) { if (te.th32OwnerProcessID == dwProcessId) { hThread = OpenThread(THREAD_SET_CONTEXT, FALSE, te.th32ThreadID); if (hThread) { QueueUserAPC(pfnAPC, hThread, (ULONG_PTR)(me.modBaseAddr)); CloseHandle(hThread); hThread = NULL; }; }; bRet = Thread32Next(hSnapshot, &te); }; CloseHandle(hSnapshot); return TRUE; };
|
输入法机制注入
用户切换输入法时,输入法管理器加载所选择输入法对应的.ime文件到当前活动进程中,这实际上是个DLL。只需在该.ime文件DllMain
函数的DLL_PROCESS_ATTACH中调用LoadLibrary
加载被注入的DLL即可。
输入法.ime工程必须添加一个版本信息资源Version,其中FILETYPE为VFT_DRV,FILESUBTYPE为VFT2_DRV_INPUTMETHOD,其他信息无关紧要。还要在项目属性的目标文件扩展名改为.ime。
如果编译成32位在64位机器下跑时则要注意文件系统重定向问题,这时需要更改代码中系统目录。
输入法.ime文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #include <Windows.h> #include <tchar.h> #include <strsafe.h> BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { static HANDLE hFileMap; static LPVOID lpMemory; static TCHAR szInjectDllName[MAX_PATH] = { 0 }; static HMODULE hModuleInject; switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 4096, TEXT("DEAE59A6-F81B-4DC4-B375-68437206A1A4")); if (!hFileMap) return FALSE; lpMemory = MapViewOfFile(hFileMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); if (!lpMemory) return FALSE; StringCchCopy(szInjectDllName, _countof(szInjectDllName), (LPTSTR)lpMemory); hModuleInject = LoadLibrary(szInjectDllName); break; }; case DLL_THREAD_ATTACH: { break; }; case DLL_THREAD_DETACH: { break; }; case DLL_PROCESS_DETACH: { UnmapViewOfFile(lpMemory); CloseHandle(hFileMap); if (hModuleInject) FreeLibrary(hModuleInject); break; }; }; return TRUE; };
|
要被注入的.dll:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <Windows.h> BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { MessageBox(NULL, TEXT("我是通过输入法注入的dll"), TEXT("提示"), MB_OK); break; }; case DLL_THREAD_ATTACH: {}; case DLL_THREAD_DETACH: {}; case DLL_PROCESS_DETACH: { break; }; }; return TRUE; };
|
安装、卸载和清理输入法文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
| #include <windows.h> #include <tchar.h> #include <strsafe.h> #include "resource.h" #pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") #pragma comment(lib, "Imm32.lib")
HWND g_hwndDlg; HKL g_hklDefault; HKL g_hklMy; TCHAR g_szKLID[16];
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); VOID InstallMyIME(); VOID UninstallMyIME(); VOID ClearMyIME(); int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { static HANDLE hFileMap; static LPVOID lpMemory; TCHAR szInjectDllName[MAX_PATH] = { 0 }; LPTSTR lpStr = NULL; switch (uMsg) { case WM_INITDIALOG: { g_hwndDlg = hwndDlg; GetModuleFileName(NULL, szInjectDllName, _countof(szInjectDllName)); if (lpStr = _tcsrchr(szInjectDllName, TEXT('\\'))) StringCchCopy(lpStr + 1, _tcslen(TEXT("MyIMETestDll.dll")) + 1, TEXT("MyIMETestDll.dll")); hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 4096, TEXT("DEAE59A6-F81B-4DC4-B375-68437206A1A4")); if (!hFileMap) { MessageBox(hwndDlg, TEXT("CreateFileMapping调用失败"), TEXT("提示"), MB_OK); return TRUE; }; lpMemory = MapViewOfFile(hFileMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); if (!lpMemory) { MessageBox(hwndDlg, TEXT("MapViewOfFile调用失败"), TEXT("提示"), MB_OK); return TRUE; }; StringCchCopy((LPTSTR)lpMemory, MAX_PATH, szInjectDllName); return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_INJECT: { InstallMyIME(); break; }; case IDC_BTN_EJECT: { UninstallMyIME(); break; }; case IDC_BTN_CLEAR: { ClearMyIME(); break; }; }; return TRUE; }; case WM_CLOSE: { UnmapViewOfFile(lpMemory); CloseHandle(hFileMap); UninstallMyIME(); ClearMyIME(); EndDialog(hwndDlg, 0); return TRUE; }; }; return FALSE; }; VOID InstallMyIME() { CopyFile(TEXT("MyIME.ime"), TEXT("C:\\WINDOWS\\system32\\MyIME.ime"), FALSE); SystemParametersInfo(SPI_GETDEFAULTINPUTLANG, 0, &g_hklDefault, FALSE); g_hklMy = ImmInstallIME(TEXT("MyIME.ime"), TEXT("我的输入法")); StringCchPrintf(g_szKLID, _countof(g_szKLID), TEXT("%08X"), (DWORD)g_hklMy); if (ImmIsIME(g_hklMy)) { LoadKeyboardLayout(g_szKLID, KLF_ACTIVATE); PostMessage(GetForegroundWindow(), WM_INPUTLANGCHANGEREQUEST, INPUTLANGCHANGE_SYSCHARSET, (LPARAM)g_hklMy); SystemParametersInfo(SPI_SETDEFAULTINPUTLANG, 0, &g_hklMy, SPIF_SENDCHANGE); MessageBox(g_hwndDlg, TEXT("我的输入法已经设置为默认输入法"), TEXT("提示"), MB_OK); }; return; }; VOID UninstallMyIME() { SystemParametersInfo(SPI_SETDEFAULTINPUTLANG, 0, &g_hklDefault, SPIF_SENDCHANGE); if (UnloadKeyboardLayout(g_hklMy)) MessageBox(g_hwndDlg, TEXT("我的输入法已经卸载成功"), TEXT("提示"), MB_OK); return; }; VOID ClearMyIME() { TCHAR szSubKey[MAX_PATH] = TEXT("SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\"); StringCchCat(szSubKey, _countof(szSubKey), g_szKLID); RegDeleteKey(HKEY_LOCAL_MACHINE, szSubKey); HKEY hKey; LPCTSTR lpSubKey = TEXT("Keyboard Layout\\Preload"); DWORD dwIndex = 0; TCHAR szValueName[16] = { 0 }; DWORD dwchValueName; TCHAR szValueData[MAX_PATH] = { 0 }; DWORD dwcbValueData; LONG lRet; RegOpenKeyEx(HKEY_CURRENT_USER, lpSubKey, 0, KEY_READ | KEY_WRITE, &hKey); while (TRUE) { dwchValueName = _countof(szValueName); dwcbValueData = sizeof(szValueData); lRet = RegEnumValue(hKey, dwIndex, szValueName, &dwchValueName, NULL, NULL, (LPBYTE)szValueData, &dwcbValueData); if (lRet == ERROR_NO_MORE_ITEMS) break; if (_tcsicmp(g_szKLID, szValueData) == 0) RegDeleteValue(hKey, szValueName); dwIndex++; }; if (!DeleteFile(TEXT("C:\\WINDOWS\\system32\\MyIME.ime"))) { MoveFileEx(TEXT("C:\\WINDOWS\\system32\\MyIME.ime"), NULL, MOVEFILE_DELAY_UNTIL_REBOOT); MessageBox(g_hwndDlg, TEXT("我的输入法已清理完毕,重启后删除.ime文件"), TEXT("提示"), MB_OK); } else MessageBox(g_hwndDlg, TEXT("我的输入法已清理完毕"), TEXT("提示"), MB_OK); return; };
|
Shadow API
Shadow API能过调试器对API下的断点。例如要过MessageBox
时,Win10中该API的实现在USER32.DLL中,内容为直接调用user32!MessageBoxTimeoutW
。选择用VirtualAlloc
申请一块内存空间,把MessageBoxW
的代码复制到其中,然后调用时直接调用该内存空间。但是其中汇编call
指令二进制码需要进行修改,二进制下4字节为MessageBoxTimeoutW
函数地址减call MessageBoxTimeoutW
下一条指令地址,这里要修改的偏移为22。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| #include <windows.h> #include "resource.h"
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { typedef int (WINAPI* pfnMessageBoxW)(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType); pfnMessageBoxW pMessageBoxW = NULL; static pfnMessageBoxW pMessageBoxWNew = NULL; BYTE bArr[30] = { 0 }; LPBYTE pMessageBoxTimeoutW = NULL; DWORD dwReplace; switch (uMsg) { case WM_INITDIALOG: { pMessageBoxW = (pfnMessageBoxW)GetProcAddress(GetModuleHandle(TEXT("User32.dll")), "MessageBoxW"); ReadProcessMemory(GetCurrentProcess(), pMessageBoxW, bArr, sizeof(bArr), NULL); pMessageBoxWNew = (pfnMessageBoxW)VirtualAlloc(NULL, sizeof(bArr), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); WriteProcessMemory(GetCurrentProcess(), pMessageBoxWNew, bArr, sizeof(bArr), NULL); pMessageBoxTimeoutW = (LPBYTE)GetProcAddress(GetModuleHandle(TEXT("User32.dll")), "MessageBoxTimeoutW"); dwReplace = pMessageBoxTimeoutW - (LPBYTE)pMessageBoxWNew - 26; WriteProcessMemory(GetCurrentProcess(), (LPBYTE)pMessageBoxWNew + 22, &dwReplace, 4, NULL); return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_OK: { if (*(LPBYTE)pMessageBoxWNew == 0xCC) *(LPBYTE)pMessageBoxWNew = 0x8B; pMessageBoxWNew(hwndDlg, TEXT("内容"), TEXT("标题"), MB_OK); break; }; case IDCANCEL: { EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; }; return FALSE; };
|
综合实战:API HOOK
下面讲的只适用于32位下,
例如一个模仿视频移动水印的程序,有一个图像静态控件,每隔2秒用ExtTextOut
把蓝色水印位置变一下,每隔5秒用DrawText
把红色水印位置变一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| #include <windows.h> #include <tchar.h> #include <time.h> #include "resource.h" #pragma comment(lib, "Winmm.lib")
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { HDC hdc; HFONT hFont, hFontOld; TCHAR szTextBlue[] = TEXT("用户名:老王"); TCHAR szTextRed[] = TEXT("Powered By 老王"); static SIZE sizeBlue, sizeRed; static RECT rcBlue, rcRed; static RECT rcExtTextOut = { 0 }; static RECT rcDrawText = { 0 }; RECT rcClient; int x, y; switch (uMsg) { case WM_INITDIALOG: { PlaySound(TEXT("三生石上刻相思.wav"), NULL, SND_FILENAME | SND_ASYNC | SND_LOOP); hdc = GetDC(hwndDlg); hFont = CreateFont(12, 0, 0, 0, 0, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, TEXT("宋体")); hFontOld = (HFONT)SelectObject(hdc, hFont); GetTextExtentPoint32(hdc, szTextBlue, _tcslen(szTextBlue), &sizeBlue); GetTextExtentPoint32(hdc, szTextRed, _tcslen(szTextRed), &sizeRed); SelectObject(hdc, hFontOld); ReleaseDC(hwndDlg, hdc); GetClientRect(hwndDlg, &rcClient); SetRect(&rcBlue, 0, 0, rcClient.right - sizeBlue.cx, rcClient.bottom - sizeBlue.cy); SetRect(&rcRed, 0, 0, rcClient.right - sizeRed.cx, rcClient.bottom - sizeRed.cy); SetTimer(hwndDlg, 1, 2000, NULL); SetTimer(hwndDlg, 2, 5000, NULL); return TRUE; }; case WM_TIMER: { hdc = GetDC(hwndDlg); SetBkMode(hdc, TRANSPARENT); hFont = CreateFont(12, 0, 0, 0, 0, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, TEXT("宋体")); hFontOld = (HFONT)SelectObject(hdc, hFont); switch (wParam) { case 1: { InvalidateRect(hwndDlg, &rcExtTextOut, TRUE); SetTextColor(hdc, RGB(0, 0, 255)); srand((UINT)time(NULL)); x = rand() % (rcBlue.right + 1); y = rand() % (rcBlue.bottom + 1); SetRect(&rcExtTextOut, x, y, x + sizeBlue.cx, y + sizeBlue.cy); ExtTextOut(hdc, x, y, 0, NULL, szTextBlue, _tcslen(szTextBlue), NULL); break; }; case 2: { InvalidateRect(hwndDlg, &rcDrawText, TRUE); SetTextColor(hdc, RGB(255, 0, 0)); srand(GetTickCount()); x = rand() % (rcRed.right + 1); y = rand() % (rcRed.bottom + 1); SetRect(&rcDrawText, x, y, x + sizeRed.cx, y + sizeRed.cy); DrawText(hdc, szTextRed, _tcslen(szTextRed), &rcDrawText, DT_SINGLELINE); break; }; }; SelectObject(hdc, hFontOld); DeleteObject(hFont); ReleaseDC(hwndDlg, hdc); return TRUE; }; case WM_SIZE: { SetRect(&rcBlue, 0, 0, LOWORD(lParam) - sizeBlue.cx, HIWORD(lParam) - sizeBlue.cy); SetRect(&rcRed, 0, 0, LOWORD(lParam) - sizeRed.cx, HIWORD(lParam) - sizeRed.cy); return TRUE; }; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDCANCEL: { EndDialog(hwndDlg, 0); break; }; }; return TRUE; }; }; return FALSE; };
|
选择用远程线程注入方式注入一个DLL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| #include <windows.h> #include <tchar.h> #include "resource.h"
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); BOOL InjectDll(); BOOL EjectDll(); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL); return 0; }; INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_BTN_INJECT: { InjectDll(); break; }; case IDC_BTN_EJECT: { break; }; case IDCANCEL: { EndDialog(hwndDlg, 0); break; }; } return TRUE; }; }; return FALSE; }; BOOL InjectDll() { TCHAR szCommandLine[MAX_PATH] = TEXT("FloatingWaterMark.exe"); STARTUPINFO si = { sizeof(STARTUPINFO) }; PROCESS_INFORMATION pi = { 0 }; LPCTSTR lpDllPath = TEXT("..\\..\\FWMDll\\Release\\FWMDll_修改.dll"); LPTSTR lpDllPathRemote = NULL; HANDLE hThreadRemote = NULL; GetStartupInfo(&si); CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi); int cbDllPath = (_tcslen(lpDllPath) + 1) * sizeof(TCHAR); lpDllPathRemote = (LPTSTR)VirtualAllocEx(pi.hProcess, NULL, cbDllPath, MEM_COMMIT, PAGE_READWRITE); if (!lpDllPathRemote) return FALSE; if (!WriteProcessMemory(pi.hProcess, lpDllPathRemote, lpDllPath, cbDllPath, NULL)) return FALSE; PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW"); if (!pfnThreadRtn) return FALSE; hThreadRemote = CreateRemoteThread(pi.hProcess, NULL, 0, pfnThreadRtn, lpDllPathRemote, 0, NULL); if (!hThreadRemote) return FALSE; WaitForSingleObject(hThreadRemote, INFINITE); ResumeThread(pi.hThread); if (!lpDllPathRemote) VirtualFreeEx(pi.hProcess, lpDllPathRemote, 0, MEM_RELEASE); if (!pi.hThread) CloseHandle(pi.hThread); if (!pi.hProcess) CloseHandle(pi.hProcess); return TRUE; }; BOOL EjectDll() { return TRUE; };
|
被注入的DLL,选择修改该API开头为call
,跳转到自定义函数中,这里不需要导出函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| #include <Windows.h> #include <tchar.h>
LPBYTE pExtTextOutW; TCHAR szText1[] = TEXT("屏幕"); TCHAR szText2[] = TEXT("用户名"); TCHAR szText3[] = TEXT("购买者"); TCHAR szTextReplace[] = TEXT(" "); LPTSTR lpStr; VOID InterceptExtTextOutW(LPTSTR lpText); BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { BYTE bExtTextOutWCall[] = { 0xFF, 0x74, 0x24, 0x18, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90, 0x90 }; DWORD dwOldProtect; MEMORY_BASIC_INFORMATION mbi = { 0 }; switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { pExtTextOutW = (LPBYTE)GetProcAddress(GetModuleHandle(TEXT("Gdi32.dll")), "ExtTextOutW"); *(LPINT)(bExtTextOutWCall + 5) = (INT)InterceptExtTextOutW - (INT)pExtTextOutW - 0x9; VirtualQuery(pExtTextOutW, &mbi, sizeof(mbi)); VirtualProtect(pExtTextOutW, 512, PAGE_EXECUTE_READWRITE, &dwOldProtect); WriteProcessMemory(GetCurrentProcess(), pExtTextOutW, bExtTextOutWCall, sizeof(bExtTextOutWCall), NULL); VirtualProtect(pExtTextOutW, 512, mbi.Protect, &dwOldProtect); break; }; case DLL_THREAD_ATTACH: {}; case DLL_THREAD_DETACH: {}; case DLL_PROCESS_DETACH: { break; }; }; return TRUE; };
_declspec(naked) VOID InterceptExtTextOutW(LPTSTR lpText) { _asm { push ebp mov ebp, esp sub esp, 0x10 push ebx push esi push edi push ecx push edx }; if ((lpStr = _tcsstr(lpText, szText1)) || (lpStr = _tcsstr(lpText, szText2)) || (lpStr = _tcsstr(lpText, szText3))) memcpy(lpStr, szTextReplace, _tcslen(lpStr) * sizeof(TCHAR)); _asm { pop edx pop ecx pop edi pop esi pop ebx mov esp, ebp pop ebp add esp, 8 mov edi, edi push ebp mov ebp, esp push ecx mov dword ptr[ebp - 0x4], 0x49414E mov eax, pExtTextOutW add eax, 0xD jmp eax }; };
|