API hooks are not a new technology, but I am ashamed that I have mastered them recently and have come up with some stupid ways to achieve my goal. There may be many errors or vulnerabilities in this article, and you may want to correct them in time.
This article describes in detail how to implement API hook programming in Visual Studio (hereinafter referred to as VS). Read the basics of this article: Basic knowledge of the Operating System (process management, memory management ), the Win32 application and dynamic link library (DLL) will be compiled and debugged in ).
API hooks are an advanced programming technique that is often used to complete some special functions, such as screen-based word acquisition for dictionary software and data modification for game modification software. Of course, this technology is mostly used by hackers or viruses to attack other programs, intercept the required data or change the behavior of the target program. This article does not discuss the application of this technology, but focuses only on implementation. At the same time, people who want to master this technology can apply it legally, and do not do dangerous or illegal things to harm themselves.
I. Principles
Every program running in the operating system must call the functions provided by the operating system-that is, the API (Application Programming Interface)-to implement various functions of the program. In Windows, APIs are thousands of system functions. Some programs do not directly call the API code, such as the following program:
#include <iostream>using namespace std;int main(void){cout << "Hello World!" << endl;return 0;}
In fact, the cout object's internal Handler has called the API for you. Even if your main function is empty, no code is written in it. As long as the program is started by the operating system, some basic APIs, such as LoadLibrary, will be called. This function is used to load the DLL, that is, to read the code and data in the DLL into the current process and execute the startup code. This function will be used later.
If you can try to replace the target API function called by the host process with a custom function, you can intercept the parameters passed by the host process to the target API and change the behavior of the host process. However, to modify the target API function, you must first find and open the host process and run the custom code in the host process. Therefore, API hooks are divided into four steps: 1. find and open the host process, 2. load the injection body into the host process and run it. replace the target API with a disguised function. execute disguised functions. The entire program is divided into two parts: one is the application responsible for finding and opening the host process and injecting code, and the other is the injection body containing the modified Code and disguised functions.
2. Search for a specified process
There are many methods to search for a specified process. The following describes three methods:
1. Find the process handle of the form pointed by the mouse
DWORD GetProcIDFromCursor(void){//Get current mouse cursor positionPOINT ptCursor;if (!GetCursorPos(&ptCursor)){cout << "GetCursorPos Error: " << GetLastError() << endl;return 0;}//Get window handle from cursor postionHWND hWnd = WindowFromPoint(&ptCursor);if (NULL == hWnd){cout << "No window exists at the given point!" << endl;return 0;}//Get the process ID belong to the window.DWORD dwProcId;GetWindowThreadProcessId(hWnd, &dwProcId);return dwProcId;}
2. Find the process with the specified file name
# Include <Psapi. h> # pragma comment (lib, "Psapi. lib ") DWORD GetProcIDFromName (LPCTSTR lpName) {DWORD aProcId [1024], dwProcCnt, dwModCnt; HMODULE hMod; TCHAR szPath [MAX_PATH]; // list all processes IDif (! EnumProcesses (aProcId, sizeof (aProcId), & dwProcCnt) {cout <"EnumProcesses error:" <GetLastError () <endl; return 0 ;} // all the processes in the example for (DWORD I = 0; I <dwProcCnt; ++ I) {// open the process. Skip HANDLE hProc = OpenProcess (PROCESS_ALL_ACCESS, FALSE, aProcId [I]); if (NULL! = HProc) {// open the 1st modules of the process and check whether their names match the target if (EnumProcessModules (hProc, & hMod, sizeof (hMod), & dwModCnt )) {GetModuleBaseName (hProc, hMod, szPath, MAX_PATH); if (0 = lstrcmpi (szPath, lpName) {CloseHandle (hProc); return aProcId [I];} closeHandle (hProc) ;}} return 0 ;}
3. Find other processes with specified information
Use createconlhelp32snapshot to enumerate all processes running in the system and obtain the process information through the relevant data structure. For detailed usage, see:
Http://msdn.microsoft.com/en-us/library/windows/desktop/ms686701.aspx
Iii. Code Injection
As mentioned above, LoadLibrary can inject the specified DLL code into the current process. If the host process can execute this function and input the file name of our own DLL, then our code can be run in the target process.
HMODULE WINAPI LoadLibrary( __in LPCTSTR lpFileName);
Let's look at another function: CreateRemoteThread, which allows the host process to open a new thread, but the processing function (LPTHREAD_START_ROUTINE) of the new thread must be the function address in the host process.
Handle winapi CreateRemoteThread (_ in HANDLE hProcess, _ in getting started, _ in SIZE_T dwStackSize, _ in getting lpStartAddress, _ in LPVOID lpParameter, _ in DWORD dwCreationFlags, _ out LPDWORD lpThreadId); // The LPTHREAD_START_ROUTINE defines typedef DWORD (WINAPI * PTHREAD_START_ROUTINE) (LPVOID lpThreadParameter );
Consider that the assembly instruction of function call is based on the return value and parameter type. The return value is transmitted only through the eax register. All parameters are pushed in the form of pointers or numerical constants, that is to say, only the number of parameters affects the stack balance of function calls. We can see that the number of LoadLibrary parameters is exactly the same as that of LPTHREAD_START_ROUTINE. LoadLibrary is a system function and can be called directly by every process. If you can get the address of the LoadLibrary function in the host process, pass in CreateRemoteThread as lpStartAddress, and pass in the DLL file name as lpParameter, then the host process can execute the injection body code.
To pass the DLL file name to the host process, we also need to introduce the following four APIs: VirtualAllocEx and VirtualFreeEx can allocate and release a piece of memory space in the host process; readProcessMemory and WriteProcessMemory can read or write data at the specified memory address in the host process.
Of course, the cleaning should be completed after the injected code is executed. First, uninstall the loaded DLL and use another system function: FreeLibrary. Like the code injection above, CreateRemoteThread is used to pass in the FreeLibrary address as the lpStartAddress parameter. Note that the FreeLibrary parameter is an HMODULE. The handle is actually the global ID of a Module, which is generally given by the return value of LoadLibrary. Therefore, you can call GetExitCodeThread to obtain the returned value of the previously executed LoadLibrary thread and pass it in as the lpParameter parameter of CreateRemoteThread. This completes the DLL Uninstall. Remember to use VirtualFreeEx to release the memory applied by VirtualAllocEx, close all opened handles, and complete the final cleaning.
Now the steps for injecting code are clear:
- Call OpenProcess to obtain the host process handle;
- Call GetProcAddress to find the address of the LoadLibrary function in the host process;
- Call VirtualAllocEx and WriteProcessMemory to write the DLL file name string to the memory of the host process;
- Call CreateRemoteThread to execute LoadLibrary and run DLL in the host process;
- Call VirtualFreeEx to release the newly applied memory;
- Call WaitForSingleObject to wait until the injection thread ends;
- Call GetExitCodeThread to obtain the handle of the previously loaded DLL;
- Call CreateRemoveThead to run FreeLibrary to uninstall the DLL;
- Call CloseHandle to close all opened handles.
All the code injected by code is organized as follows. (Note: This program needs to compile and generate an exe file in win32 Console mode. When running in the console, two parameters are required: 1st parameters are the image name of the host process, which can be viewed in the task manager; 2nd parameters are the complete path File Name of the injection DLL. After the program runs, it loads the specified DLL into the host process with the specified name)
# Include <tchar. h> # include <Windows. h> # include <atlstr. h> # include <Psapi. h> # pragma comment (lib, "Psapi. lib ") # include <iostream> # include <string> using namespace std; DWORD FindProc (LPCSTR lpName) {DWORD aProcId [1024], dwProcCnt, dwModCnt; char szPath [MAX_PATH]; HMODULE hMod; // list all processes IDif (! EnumProcesses (aProcId, sizeof (aProcId), & dwProcCnt) {// cout <"EnumProcesses error:" <GetLastError () <endl; return 0 ;} // all the processes in the example for (DWORD I = 0; I <dwProcCnt; ++ I) {// open the process. Skip HANDLE hProc = OpenProcess (PROCESS_ALL_ACCESS, FALSE, aProcId [I]); if (NULL! = HProc) {// open the 1st modules of the process and check whether their names match the target if (EnumProcessModules (hProc, & hMod, sizeof (hMod), & dwModCnt )) {GetModuleBaseNameA (hProc, hMod, szPath, MAX_PATH); if (0 = _ stricmp (szPath, lpName) {CloseHandle (hProc); return aProcId [I];} closeHandle (hProc) ;}} return 0 ;}// the first parameter is the host process image name, you can view the // second parameter in the task manager as the complete file name of the DLL to be injected, int main (int argc, char * argv []) {if (argc! = 3) {cout <"Invalid parameters! "<Endl; return-1;} // find the target process and open the handle DWORD dwProcID = FindProc (argv [1]); if (dwProcID = 0) {cout <"Target process not found! "<Endl; return-1;} HANDLE hTarget = OpenProcess (PROCESS_ALL_ACCESS, FALSE, dwProcID); if (NULL = hTarget) {cout <"Can't Open target process! "<Endl; return-1 ;}// obtain the entry point address of LoadLibraryW and FreeLibrary in the host process. HMODULE hKernel32 = GetModuleHandle (_ T (" Kernel32 ")); export pLoadLib = (callback) GetProcAddress (hKernel32, "LoadLibraryW"); LPTHREAD_START_ROUTINE pFreeLib = (LPTHREAD_START_ROUTINE) GetProcAddress (hKernel32, "FreeLibrary "); if (NULL = pLoadLib | NULL = pFreeLib) {cout <"Library procedure not found:" <GetL AstError () <endl; CloseHandle (hTarget); return-1;} WCHAR szPath [MAX_PATH]; MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, argv [2],-1, szPath, sizeof (szPath)/sizeof (szPath [0]); // allocate space for the LoadLibraryW parameter in the host process, and write the parameter value to LPVOID lpMem = VirtualAllocEx (hTarget, NULL, sizeof (szPath), MEM_COMMIT, PAGE_READWRITE); if (NULL = lpMem) {cout <"Can't alloc memory block:" <GetLastError () <endl; closeHandle (hTarget); Return-1;} // The parameter is the file path of the DLL to be injected if (! WriteProcessMemory (hTarget, lpMem, (void *) szPath, sizeof (szPath), NULL) {cout <"Can't write parameter to memory:" <GetLastError () <endl; VirtualFreeEx (hTarget, lpMem, sizeof (szPath), MEM_RELEASE); CloseHandle (hTarget); return-1;} // create a semaphore, DLL code can use ReleaseSemaphore to notify the main program to clear HANDLE hSema = CreateSemaphore (NULL, 0, 1, _ T ("Global \ InjHack ")); // inject the DLL into the host process HANDLE hThread = CreateRemoteThread (hTarget, NUL L, 0, pLoadLib, lpMem, 0, NULL); // release the parameter VirtualFreeEx (hTarget, lpMem, sizeof (szPath), MEM_RELEASE) in the host process ); if (NULL = hThread) {cout <"Can't create remote thread:" <GetLastError () <endl; CloseHandle (hTarget); return-1 ;} // wait for the DLL semaphore or host process to exit WaitForSingleObject (hThread, INFINITE); HANDLE hObj [2] = {hTarget, hSema}; if (WAIT_OBJECT_0 = WaitForMultipleObjects (2, hObj, FALSE, INFINITE) {cout <"Target Process exit. "<endl; CloseHandle (hTarget); return 0;} CloseHandle (hSema); // obtain the Module IDDWORD dwLibMod of the DLL Based on the thread exit code; if (! GetExitCodeThread (hThread, & dwLibMod) {cout <"Can't get return code of LoadLibrary:" <GetLastError () <endl; CloseHandle (hThread ); closeHandle (hTarget); return-1;} // close the thread handle CloseHandle (hThread); // inject FreeLibrary code again to release the injection body loaded by the host process. DLLhThread = CreateRemoteThread (hTarget, NULL, 0, pFreeLib, (void *) dwLibMod, 0, NULL); if (NULL = hThread) {cout <"Can't call FreeLibrary: "<GetLastError () <endl; CloseHandle (hTarget); return-1;} WaitForSingleObject (hThread, INFINITE); CloseHandle (hThread); CloseHandle (hTarget ); return 0 ;}
Iv. Hooks
The above program can inject self-compiled code into the host process. Next we will further discuss how to write the injection body (Dynamic Link Library) to intercept the target API. This part of the content is deeper than above and requires a little basic compilation knowledge.
1. Perform assembly-level debugging in
VS provides powerful debugging functions for users to conveniently view the running status of injection code and host code. Now we need to create another project as the host process. Well, the simple dialog box program of MFC is a good choice. The following describes GetTickCount as the target API. In the response dialog box, click the left mouse button to press the event and add the GetTickCount code:
void CMyTargetDlg::OnLButtonDown(UINT nFlags, CPoint point){GetTickCount();CDialog::OnLButtonDown(nFlags, point);}
Set a breakpoint before GetTickCount. After running the program, click the left button to stop the program here. Then, open the disassembly (debugging menu-> window) and you will see the following disassembly code:
There are four lines of Assembly commands, 1st columns are the memory address of the command, 2nd columns are Assembly commands, and 3rd columns are operands. The compilation results on different machines are also different, so the memory address is different, but the subsequent commands and operands are similar. Click F10 (process by process), run to the row 0063615, and then press F11 (Statement by statement) to enter the code of GetTickCount. See:
The following command is executed:
mov edx, 7FFE0000h
Note that the address of the Code is 7C80934A, And the next code is 7C80934F, which indicates that the mov command line is 5 in length. Now open the memory display window (debug> WINDOW> memory), and enter 0x7C80934A in the address, as shown below:
The machine code corresponding to this mov command is: ba 00 00 fe 7f. To understand the relationship between x86 architecture downlink encoding and machine code, we need to refer to a very important document "Intel 64 and IA-32 ubuntures Software Developer's Manual" (hereinafter referred to as IA32SDM ), this is free for developers provided by Intel, which can be found at the following URL:
Http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html
We recommend that you download the three-volume subscription. You can find the machine code corresponding to the above mov command in Vol. 2B-4.2 (total page 1,257th) of IA32SDM:
Imm32 indicates a 32-bit instant number, which is the "00 00 fe 7f" displayed in the memory viewer. Because Intel's CPU system is Little Ending, the byte order is backward, that is, 0x7FFE0000. The Opcode (operation code) corresponding to the mov command is "B8 + rd". For the Opcode format, see Vol. 2A-3.1.1.1 of IA32SDM (606th pages in total). The excerpt is as follows:
The "Opcode" column in the table above shows the object code produced for each form of the instruction. when possible, codes are given as hexadecimal bytes in the same order in which they appear in memory. definitions of entries other than hexadecimal bytes are as follows:
- REX. w-Indicates the use of a REX prefix that affects operand size or instruction semantics. the ordering of the REX prefix and other optional/mandatory instruction prefixes are discussed Chapter 2. note that REX prefixes that promote legacy instructions to 64-bit behavior are not listed explicitly in the opcode column.
- /Digit-A digit between 0 and 7 indicates that the ModR/M byte of the instruction uses only the r/m (register or memory) operand. the reg field contains the digit that provides an extension to the instruction's opcode. this is a number ranging from 0 to 7, indicating that the ModR/M bytes of the command only use the r/m operands. The reg bits of ModR/M are the numbers used as an additional code of the operation code)
- /R-Indicates that the ModR/M byte of the instruction contains a register operand an r/m operand.
- Cb, cw, cd, cp, co, ct-A 1-byte (cb), 2-byte (cw), 4-byte (cd ), 6-byte (cp), 8-byte (co) or 10-byte (ct) value following the opcode. this value is used to specify a code offset and possibly a new value for the code segment register.
- Ib, iw, id, io-A 1-byte (ib), 2-byte (iw), 4-byte (id) or 8-byte (io) immediate operand to the instruction that follows the opcode, ModR/M bytes or scaleindexing bytes. the opcode determines if the operand is a signed value. all words, doublewords and quadwords are given with the low-order byte first.
- + Rb, + rw, + rd, + ro-A register code, from 0 through 7, added to the hexadecimal byte given at the left of the plus sign to form a single opcode byte. see Table 3-1 for the codes. It is added with the hexadecimal number algebra on the left of the plus sign to form a complete bitcode byte. For specific code, see Table 3-1). The + ro columns in the table are applicable only in 64-bit mode.
- + I-A number used in floating-point instructions when one of the operands is ST (I) from the FPU register stack. the number I (which can range from 0 to 7) is added to the hexadecimal byte given at the left of the plus sign to form a single opcode byte.
From this we can see that 0xB8 is the base code, rd is the 32-bit Register Code, and the complete Opcode is obtained by adding algebra. For the register code Table, see Vol. 2A-Table 3-1 of IA32SDM (total page 607th), for example:
From the section marked in the red box in the preceding table, we can see that the additional code corresponding to EAX is 0, so the Opcode of this mov command is 0xB8 + 0x00 = 0xB8. Open the register window (debug> WINDOW> Register) to view the values of each register. Press F10 to execute a single step, and you can also see the changes of each register (the changed value is marked in red), such:
2. Prepare JMP
The above briefly introduces the basic method of assembly-level debugging in VS, and explains how to analyze the machine code using the mov command as an example. With these tools and materials, you can clearly understand the details of the code to be executed within the system. From the preceding section, we can see that the First Command executed by the GetTickCount API is mov. If you can change the Opcode of mov to jmp, you can jump to the custom function address to execute arbitrary code. Check the machine code of the jmp command from IA32SDM (Vol. 2A-3.2:
Due to the random location of the custom function and the protection of the win32 operating system, the segment address of each process is fixed, and the program can be accessed through the CS register, but cannot be changed. Therefore, we have two options: one is to use absolute jump within the JMP r/m32 Command Execution segment, and the other is to use relative jump within the JMP rel32 Command Execution segment. First, explain how to use JMP r/m32 to execute absolute redirection. For the machine code format, see Vol. 2A-2.1 of IA32SDM, for example:
It can be seen that the machine code consists of six parts, and the machine code corresponding to the JMP r/m32 command is "FF/4", three of which are used: one-byte Opcode (0xFF), one-byte ModR/M, and four-byte Displacement operations. ModR/M specifies the addressing method of the CPU and the additional code of the operation code. It is divided into three sections: Mod, Reg/Opcode, and R/M. For details, see Vol of IA32SDM. 2A-2.1.3 and the following Table 2-2, for example:
Let's take a look at the header first. Line 4 "/digit (Opcode)" is 4 in the machine code FF/4, so check the column marked in the red box (the binary value of 4 is 6th) you can. "Valid Address" specifies the finger-seeking method. To avoid operations on registers, use one command to complete the jump. We select the simplest "disp32" line, it indicates that only part 3rd of the instruction machine code is used to indicate the destination address of the jump. In this way, the Mod bit used is 00, the Opcode bit is 100, and the R/M bit is 101. For calculation, 00 100 101 (Binary) = 0x25.
Displacement points to a 4-byte memory segment that stores the final destination address. Therefore, you must first apply for a 4-byte space with VirtualAllocEx, store the address of the User-Defined Function, and then fill in the requested address with Displacement. To sum up, the complete machine code should be FF 25 XX. The last four bytes are a memory address containing the target function entry address (which is indeed a bit round ).
Using the JMP r/m32 command to complete the jump is complicated. Not only does the request for and release the memory, but the entire machine command has 6 bytes. The simpler method is to use the JMP rel32 command to execute the relative jump, and the machine code has only five bytes. The machine code corresponding to JMP rel32 is E9 cd, where cd is the relative address. The calculation method is: target address-Current Instruction address-5.
After preparing the machine code of the JMP command, you can replace it with the entry address of the target API to spoof the host process to execute disguised functions.
3. Modify the entry point
After reading the above introduction, I believe you can't wait to try to hook up the target API. Although there are still many problems that have not been solved, such as how to return, how to execute the original API function, and how to return the whole body, these problems can be put first, first, let's see if the above method can be used to successfully hook up.
First, you need to create a DLL project to generate the injection body and customize a DllMain function, as shown below:
#include <windows.h>BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved){switch (fdwReason){case DLL_PROCESS_ATTACH:DisableThreadLibraryCalls(hInstDll);InstallMonitor();break;case DLL_PROCESS_DETACH:break;}return TRUE;}
Then write the hook function InstallMonitor:
void InstallMonitor(void){HANDLE hProc = GetCurrentProcess();BYTE aOpcode[5] = {0xE9}; //JMP Procudure*(DWORD*)(&g_aOpcode[1]) = DWORD(MonFunc) - DWORD(GetTickCount) - 5;WriteProcessMemory(hProc, LPVOID(GetTickCount), LPVOID(aOpcode), 5, NULL);CloseHandle(hProc);}
The above code is easy to understand. aOpcode is a jmp Instruction constructed according to the method described above, specifying to jump to the custom camouflage function MonFunc, and then entering the jmp Instruction into the Code of GetTickCount using WriteProcessMemory. The disguised function MonFunc is very easy to write:
Void WINAPI MonFunc (void) {MessageBox (NULL, _ T ("injection Code"), _ T ("example"), 0 );}
So far, you can compile an injection DLL according to the above Code, and then use the injection program in the third part of this article to inject the DLL into the host process for execution.
5. Perfect deception
If you successfully execute the result as described above, you may find that the host process crashes after confirmation in the dialog box. There are several reasons:
- The disguised function does not properly balance the stack, leading to an error in clearing the stack of the host when the returned result is returned;
- The disguised function does not execute the result as an API, and the host cannot call the system API normally, resulting in an error;
- Disguised functions are not thread-safe, leading to errors in concurrent calls by the host;
- The host has security protection measures. After the attack is detected, the host automatically recovers or destroys itself.
This article only discusses the solution for the first three reasons, without considering the 4th reasons. The following are explained one by one.
1. Maintain the stack balance
Most APIs have parameters, and the parameters are pushed to the stack by the host before the call command is executed. The call Convention for Win32API is _ stdcall, which indicates that the API is responsible for Stack cleaning. If the disguised function does not properly clear the stack when it is returned, errors will occur. Therefore, the parameter table of the disguised function must be the same as that of the original API to ensure that the code generated by the compiler can be correctly returned to the host code.
2. Execute the functions of the original API
In order to be able to execute the original API function, you must restore its original code before calling it, otherwise it will be stuck in an endless loop. Of course, the original machine code should be retained when the machine code is rewritten, so that it can be restored to its original state using WriteProcessMemory. The ReadProcessMemory API function is opposite to the WriteProcessMemory function and can read the machine code at the specified position. Remember to modify the entry point of the original API after it is called. Otherwise, you will not be able to cheat it next time. The structure of the entire camouflage function is as follows:
//Monitor FunctionDWORD WINAPI MonFunc(DWORD dwErr){//Restore the original API before calling itReleaseBase();//Calling the original APIDWORD dw = GetTickCount();//Monitor the original API againMonitorBase();//You can do anything herereturn dw;}
3. Thread Security
Using EnterCriticalSection and LeaveCriticalSection is the best choice to ensure thread security. Using this function to pack disguised functions can solve the problem of concurrent access. The current Code should look like this:
//Monitor FunctionDWORD WINAPI MonFunc(DWORD dwErr){//Thread safetyEnterCriticalSection(&g_cs);//Restore the original API before calling itReleaseBase();DWORD dw = GetTickCount();MonitorBase();//You can do anything here//Thread safetyLeaveCriticalSection(&g_cs);return dw;}
4. Complete example
The complete code of the injection body DLL is provided below for your reference. This DLL hangs a hook on GetTickCount. You can add any custom code to the disguised function MonFunc and call UninstallMonitor to end the hook program when exiting.
#include <tchar.h>#include <Windows.h>//Handle of current processHANDLE g_hProc;//Backup of orignal code of target apiBYTE g_aBackup[6];BYTE g_aOpcode[6];//Critical section, prevent concurrency of calling the monitorCRITICAL_SECTION g_cs;//Base address of target API in DWORDDWORD g_dwApiFunc = (DWORD)GetTickCount;//Hook the target API__inline BOOL MonitorBase(void){// Modify the heading 6 bytes opcode in target API to jmp instruction,// the jmp instruction will lead the EIP to our fake functionreturn WriteProcessMemory(g_hProc, LPVOID(g_dwApiFunc),LPVOID(g_aOpcode), sizeof(g_aOpcode) / sizeof(g_aOpcode[0]), NULL);}//Unhook the target API__inline BOOL ReleaseBase(void){// Restore the heading 6 bytes opcode of target API.return WriteProcessMemory(g_hProc, LPVOID(g_dwApiFunc),LPVOID(g_aBackup), sizeof(g_aOpcode) / sizeof(g_aOpcode[0]), NULL);}//Pre-declareBOOL UninstallMonitor(void);//Monitor FunctionDWORD WINAPI MonFunc(DWORD dwErr){//Thread safetyEnterCriticalSection(&g_cs);//Restore the original API before calling itReleaseBase();DWORD dw = GetTickCount();MonitorBase();//You can do anything here, and you can call the UninstallMonitor//when you want to leave.//Thread safetyLeaveCriticalSection(&g_cs);return dw;}//Install MonitorBOOL InstallMonitor(void){//Get handle of current processg_hProc = GetCurrentProcess();g_aOpcode[0] = 0xE9; //JMP Procudure*(DWORD*)(&g_aOpcode[1]) = (DWORD)MonFunc - g_dwApiFunc - 5;InitializeCriticalSection(&g_cs);//Start monitorreturn MonitorBase();}BOOL UninstallMonitor(void){//Release monitorif (!ReleaseBase())return FALSE;DeleteCriticalSection(&g_cs);CloseHandle(g_hProc);//Synchronize to main application, release semaphore to free injectorHANDLE hSema = OpenSemaphore(EVENT_ALL_ACCESS, FALSE, _T("Global\\InjHack"));if (hSema == NULL)return FALSE;return ReleaseSemaphore(hSema, 1, (LPLONG)g_hProc);}BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved){switch (fdwReason){case DLL_PROCESS_ATTACH:DisableThreadLibraryCalls(hInstDll);InstallMonitor();break;case DLL_PROCESS_DETACH:break;}return TRUE;}