I accidentally discovered some ways to track the API, especially the use of debugging functions!
I didn't find it before!
Tracking Monitoring Program Overview When we perform a tracking monitoring analysis of a target program, according to the target of tracking monitoring, there is basically the following ways to implement tracking monitoring of the API function:
Write Log Record Analysis If you have the source code for the target program, you can record the parameters and run results of the API on the entry point and exit point of the critical API function. This method can often be seen in the extension program. The disadvantage of this method is that you must have source code, and you must recompile the source code each time you modify the log. Because the program is different from the goals we have to discuss, it will not be discussed here. The principle of injection of the monitoring code Injecting the target program is the process space of the monitoring code into the target program when the target program is running. Monitoring code Through the previous preparation, modify the running code of the target program so that the target program calls the specified API function, the running command will jump into the monitor code, so that the monitoring code can record the operation parameters of the API function, then Monitor code in running API function code. After running through the API function code, return to the monitor code, run the result, and return to the target program. Its operational schematic is:
On the 32-bit Windows platform, each process space is independent. To run monitoring code in the target program, you must inject the monitoring code into the target process. A common method is to compile the monitor code into a DLL and then inject the DLL into the target process. There are a lot of articles in which DLL injection target processes, such as in "Windows Core Programming", in detail, in detail, how to inject DLL is not the focus of this article, here is not introduced, please refer to other related Article. Using the solution to the monitoring code to achieve monitoring in the target program, the following methods are common: 1. Write the jump instruction (JMP) in the target function, jump to the monitor code implementation monitoring 2. As in the first way, the jump instruction is written in the target function entry, but the mechanism to achieve monitoring in the monitor code is different. 3. With the API Hook function, modify the import table (Import Address Table) of the EXE and DLL, write the function portal address in the monitor code, and when the EXE or DLL calls other DLLs, you can jump to the monitoring code. Track monitoring. The 1 to 3 programs introduced here have been introduced in many information. They are no longer repeated. They have a biggest shortcomings: Monitor a function must know the prototype of the function (ie, the number of parameters and call mode --WinaPi Call or other?). When writing monitoring code, you must ensure that the prototype of the monitoring code function is consistent with the prototype of the monitored function. I want to add a monitoring code when adding a monitoring function. For details, please refer to DETOTOURS and API HOOK. Using a proxy DLL monitoring API function monitoring agent DLL implementation API function monitoring is the original DLL rename, replacing the original DLL with a new DLL. This new DLL export function is the same as the export function used to DLL, and the order of exporting functions is the same as the original DLL. The new DLL name is the same as the name of the DLL. In each function in the new DLL, you are responsible for logging the running parameters and running results, and the original DLL function is called. Its operation is as shown in the figure:
For example: a.exe calls B.dll, when you want to monitor the export function of B.DLL, the B.DLL is named C. DLL, generate a new monitoring module B.dll, export function name and order and original B. DLL is exactly the same, so A.EXE enters the monitoring module when calling B. DLL to implement the target of tracking monitoring. The new B.dll here is the original B.dll agent, and all functions that call B.dll will be monitored by new B.dll. If B.EXE is also called B.dll, so that more than a.exe's call is monitored, B.exe's call is also monitored. Monitoring records have more useless data, and the analysis has increased difficulty. In this way, this article does not have more introductions. The Tracking Monitor Tracking Monitor is used as the debugger to debug the target process as the debugger to set breakpoints in the entry of the API function of the target process. Thus, when the target process calls the monitored API function, the target program will generate a debug interrupt, and the system will notify the interrupt information notified the tracking monitor, and the target process of the debug is hang. Thus, the tracking monitor can access the memory of the target process to get the parameters of the API function. Then notify the system to let the pending target process continues to run. Similarly, set breakpoints at the exit of the API function, you can get the processing result of the API function. Program that implements tracking monitoring by debugging a function is actually the most basic usage is the debugger. In the debugger, you can easily get the input and output parameters of the API function, or the value of the variable can be obtained. But use the debugger as a tracking monitor, it is very inconvenient. When generating breakpoint debug information, we must first record the input and output parameters of the API function, followed by continuing the target process to intervene. Due to excessive needs of artificial intervention, there is no great role in tools as tracking analysis. As a relatively practical tracking monitoring tool, you should automatically record the input and output parameters, and you can automatically run the target process without user intervention. Then, if we can write a similar debugger function, this debugger only needs to implement our requirements for tracking monitoring tools, that is, automatically record the input and output parameters, and automatically let the target process continues to run. The purpose of tracking monitoring tools can be reached. In the next article, we will make a detailed description of how to use the trial function. References: 1. "Windows Core Programming", Jeffrey Richter, Machinery Press 2. Microsoft's MSDN 3. Detours can get the source code on http://research.microsoft.com/sn/detours/. The DETOURS function is valid under WinNT and W2K and does not support 9X. Tracking the API with the Trial Function We know that when a target program is running, more or less will call an API function. When we debug this target program, it is very important to know the input output parameters of certain APIs and the operation results, tracking monitoring of the call path and parameters of the API, is very helpful in analyzing the internal call mechanism of the research target program. of. The API referred to herein includes not only a narrow Windows system function, but also includes a third party (and itself) providing DLL output functions. If you travel from tracking monitoring, the API of tracking monitors not only includes a broad API, more likelihood, but more likelihood, including the internal unidentified functions in Exe and DLL. When the API function tracking monitoring analysis is performed, it is generally no source code and debug version, more cases only exe and dll release.
The tracking goal is to obtain the parameters and run results that call the API function by running the target program, and do not want to change the running path of the target program. If we can write a similar debugger function, this debugger needs to implement our requirements for tracking monitoring tools, that is, automatically record the input and output parameters, and automatically let the target process run. Below we will introduce a solution that can simply output monitoring results in the case of a function prototype - use the debug function to monitor the monitor of API functions. Use the debug function to achieve monitoring the API function
Everyone knows that VC can be used to debug procedures, in addition to debugging the Debug program, of course, you can also debug the Release program (debug the Release program is assembly code). If you know the entry address of the function, simply set the breakpoint on the function portal. When the program calls the function of setting the breakpoint, the VC will suspend the operation of the target program, you can get all the you want to get the target program memory. Thing. In general, as long as you have enough patience and perseverance, as well as some assembly knowledge, it can be done to monitor the input and output parameters of the API function. However, since the VC debugger will suspend the operation of the target program during each breakpoint, too many pauses for the target program cannot endure for monitoring tasks. Therefore, there will be no too many people really use the VC debugger as a good API function monitor. If the VC debugger can automatically output the stack value when you automatically output breakpoints (the function's input parameters), the stack value is automatically output at the end of the function, which is the output parameters of the function. ) And the value of the CPU register (that is, the function return value) and does not pause the target program. Everything is automatic without us to intervene. Will you use it as a monitor? I will. I don't know how to make VC like this (maybe VC can be like this, but I don't know. Please let me know if anyone knows, thank you), but I know that it is obvious that the VC is also the task of completing the debugger by calling the Windows API function, and These functions can clearly realize my requirements. What I need to do is to use these API functions, write a simple debugger, automatically output monitoring results when the target program breakpoint occurs and automatically restores the operation of the target program. Obviously, if you use a VC debugger as a monitor, you can get a simple input output parameter and function run results without knowing the target function, and because the monitoring code does not inject the target program, there will be no monitoring target function and monitoring code. Conflict. The VC debugger obviously tracks the recursive function, or tracks the DLL module to call the DLL itself function, and the EXE calls its own functions. As long as you know the entrance address of the target function, you can track (monitor EXE own functions) You can refer to the MAP file to get the address of the EXE internal function by selecting the output MAP file when generating an EXE module. Without I heard that VC can't debug multi-threaded, up to me to say that debug multi-thread is more troublesome - Certificate multi-thread is to debug. Obviously, the VC can also debug code in dllmain. These, it is already possible to prove that our goals can be achieved by debugging functions.
How to write a program that realize our goals? What debug functions are needed?
First, let the target program enters the debugged state: For a start-up process, use the debugactiveProcess function to capture the target process, and enter the target process into the debugged state.
Bool DebugActiveProcess (DWORD DWPROCESSID);
The parameter dWProcessID is the process ID of the target process. How to get a running process ID through the TOOLHELP series function or PSAPI library function introduced in many articles, this is no longer repeated. For server programs, since there is no permission that cannot capture the target process, you can get debug privileges by lifting the privileges of the monitor, capture the target process (the user must have debug privileges). For starting a new program, set the necessary parameters to enter the debugged state by setting the necessary parameters through the CreateProcess function. Bool CreateProcess (LPCTSTR LPApplicationName,
LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation);
For details, please refer to MSDN, here I only introduce the parameters we are interested in. Here and the general usage are different as the debugging program DWCREATIONFLAGS must be set to debug_process or debug_only_this_process. This start-up target program will enter the debugged state. Here, DEBUG_PROCESS and DEBUG_ONLY_THIS_PROCESS are explained. Debug_only_this_process is only debugging the target process, while the debug_process parameter not only debugs the target process, but debugs all the sub-process initiated by the target process. For example: Start B.exe in A.EXE, if you start with debug_only_this_process, monitoring processes only debug a.exe does not debug B.exe, if it is debug_process, it will debug a.exe and b.exe. For the sake of simplicity, this article only discusses the case where the start-up parameter is debug_only_this_process. Instructions:
STARTUPINFO ST = {0};
Process_information pro = {0}; st.cb = sizeof (st); CreateProcess (null, pszcmd, null, null, false, debug_only_this_process, null, szpath, & st, & pro)); // Close handle - these handles No longer used in the debugger, so you can turn off CloseHandle (Pro.hthread); CloseHandle (Pro.hprocess);
Second, monitoring procedures entering the debugged state: The target process enters the debugged state, debugging program (here the debugger is our monitoring program, will no longer explain) will be responsible for debugging the debugged operation of the debugged program. The debugger obtains debug messages from the debugged program via the waitfordeBugevent function, the debugger is processed according to the resulting debug message, and the debug process will be paused until the debugger notified the debugger to continue running through the ContinueDebugevent function.
BOOL WAITFORDEBUGEVENT (LPDebug_Event LPDebugEvent, // Debug EVENT INFORMATION
DWORD DWMILLISECONDS // Time-Out value);
The debug message can be obtained in the parameter lpDebugevent, which is required to note that the function must be the same thread that the function must enter the debug status of the target program. That is to say, the thread called through the debugactiveProcess or CreateProcess is a thread. In addition, I like to set dwmilliseconds to -1 (unlimited waiting). So I usually use the CreateProcess and WaitFordeBugevent functions in a new thread. Typedef strunt _debug_event {
DWORD dwDebugEventCode; DWORD dwProcessId; DWORD dwThreadId; union {EXCEPTION_DEBUG_INFO Exception; CREATE_THREAD_DEBUG_INFO CreateThread; CREATE_PROCESS_DEBUG_INFO CreateProcessInfo; EXIT_THREAD_DEBUG_INFO ExitThread; EXIT_PROCESS_DEBUG_INFO ExitProcess; LOAD_DLL_DEBUG_INFO LoadDll; UNLOAD_DLL_DEBUG_INFO UnloadDll; OUTPUT_DEBUG_STRING_INFO DebugString; RIP_INFO RipInfo;} u;} DEBUG_EVENT, * LPDEBUG_EVENT;
In this debug message structure, DwdeBuGeventCode records the message code that generates debug interrupt. Detailed description of the message code can be referred to MSDN. Where we are interested in the message code is:
EXCEPTION_DEBUG_EVENT: Generate debug exceptions
CRATE_THREAD_DEBUG_EVENT: The new thread generates create_process_debug_event: new process is generated. Note: Only once when Debug_only_this_process, there may be many times if the program starts a child process when Debug_Process. EXIT_THREAD_DEBUG_EVENT: A thread runs abuse EXIT_PROCESS_DEBUG_EVENT: A process abort. Note: Only once when Debug_only_this_process, there may be many times in Debug_Process. LOAD_DLL_DEBUG_EVENT: A DLL module is loaded. Unload_dll_debug_event: A DLL module is uninstalled.
After obtaining the debug message of the target program, the debugger performs different processing according to these message code, and finally notifies the debugger to continue to run.
Bool ContinueDebugevent (DWORD DWPROCESSID, // Process To Continue
DWORD DWTHREADID, // Thread to Continue DWORD DWCONTINUESTATUS / / //CINOTION STATUS
This function notifies the debugger to continue to run. Use example:
Debug_event dbe;
Bool Rc; CreateProcess (NULL, PSZCMD, NULL, NULL, FALSE, DEBUG_ONLY_THIS_PROCESS, NULL, SZPATH, & St, & Pro); WaitFordeBugevent (& DBE, Infinite)) {// If it is an exit message, debug monitoring end IF (DBE . dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break; // enter debug monitor process rc = OnDebugEvent (& dbe); if (rc) ContinueDebugEvent (dbe.dwProcessId, dbe.dwThreadId, DBG_CONTINUE); else ContinueDebugEvent (dbe.dwProcessId, dbe.dwThreadId, DBG_ DBG_EXCEPTION_NOT_HANDED);} // Debug message handler BOOL WINAPI OONDEBUGEVENT (Debug_Event * pevent) {// We have not operated the target process, so go back to true. Return True;}
These programs are the simplest debugger. However, it basically doesn't use it. You haven't set a breakpoint in the target process, you can't complete the task for the API function monitored. Set breakpoints for the target process: Our goal is to monitor the input output of the API function, then you should first provide which API letters in the DLL module and the entry address of these APIs. In the previous, the broad API also includes an unidentified internal function. If you have a DLL module debug version and debug connection file (PDB file), you can also get information about the internal function according to debug information. · Get the entry address of the function name and function inlet address to obtain the function of the function. For DLLs compiled with VC, if it is a debug version, you can get debugging information through the ImageHLP library function, and analyze the entry address of the function. If there is no DEBUG version, you can also get the entry address of the function by analyzing the export function table. 1. Use the ImageHLP library function to get the DEBUG version of the function name and function entry address. You can use the ImageHLP library function to analyze Debug information, the associated function is Syminitialize, SymeNumerateSymbols, and undecorateSymbolname. For details, please refer to the instructions and usages of these functions in MSDN. However, using imageHLP can only analyze the programs compiled with VC, and the programs compiled by C Builder cannot be analyzed in this way. 2. DLL Export Table Gets the function export function name and the entry address of the function. In most cases, we still want to monitor the Release version of the input and output parameters. After all, the Debug version is not the product we finally supplied to the user. Debug and Release have different compilation conditions that result in different results, and discuss in many BBSs. So, I think tracking monitoring Release version is more practical. The export function name is active on the MSDN by analyzing the DLL export table. Note About derived tables You can refer to articles about the PE structure. 3. Getting a COM interface through the OLE function You can also analyze the interface functions provided by the OLE function. The interface function is not exported through a DLL export table. You can analyze the COM interface through the loadTypelib function, get the entrance address of the COM record interface, so you can monitor the call of the COM interface. This is what the API Hook cannot be implemented. Here I don't intend to analyze the way the COM interface is. On the MSDN, you can find the relevant source code to modify your goal by searching the LoadTypelib Sample Keyword. Here is the program that automatically analyzes the target module to get the DLL export function, as the purpose of our monitoring, these work is just to get a series of function names and function addresses. The function name is just a name that makes us easy to identify the function. The function entry address is our truly concern. In other words, if you can make sure that an address must be a function (including internal function) entry address, you can define your own name, add it to your function management table, you can also achieve monitoring The function of the input and output parameters of this function. This is why the monitoring function of implementing the EXE internal function. If you have an MAP file generated when EXE compile (you can choose to generate a map file when compile), you can get the MAP file to get the inlet address of the internal function, add the internal function to your function management table. (The name of a function is not meaningful for Funa or funb for monitoring functions, but the name is Funa or the name of FUNB is meaningful for monitoring monitoring results. You can complete the MessageBox function to monitor results. It is output with the name of FUNA, so when you monitor some internal no name, you can define your own name).
· Setting the breakpoint setting breakpoint at the function entry address is very simple, as long as 0xCC (INT 3) is written to the specified address. When this program runs to the specified address, the debug interrupt information notification debugger is generated. Modifying the memory data of the specified process can be done by the WriteProcessMemory function. Since the general case is protected as a program code segment, there is still a function to be used. VirtualProtectex. In actual conditions, when the trial break occurs, the debugger should write the original code back to the debugged program. Unsigned char setbreakpoint (DWORD PADD, Unsigned Char Code)
{Unsigned Char B; BOOL RC; DWORD DWREAD, DWOLDFLG; // 0x800000 or more address is a system common area, can not modify if (padd> = 0x800000 || PADD == 0) Return code; // Get the original code RC = ReadProcessMemory (_GHDebug, Padd, & b, sizeof (byte), & dwread); // The original code is the same, there is no need to modify if (rc == 0 || b == code) Return Code; / / modify page protection attribute VirtualProtectEx (_ghDebug, pAdd, sizeof (unsigned char), PAGE_READWRITE, & dwOldFlg); // modified object code WriteProcessMemory (_ghDebug, pAdd, & code, sizeof (unsigned char), & dwRead); // restore the page protection attributes VirtualProtectex (_GHDebug, padd, sizeof (unsigned char); return b;} You must save the original code when setting breakpoints, so you can restore the code when you recover breakpoints. General usage is: set breakpoint m_code = setbreakpoint (pFunadd, 0xcc); recovery breakpoint: setbreakpoint (pfunadd, m_code); remember, each function entry address code can be different, you should save each breakpoint address A original code, there will be no errors when recovering. Ok, now the breakpoint has been set in the target program. When the target program is called, a debug interrupt information notification debugger will be generated. We have to write our debug interrupt programs in the debugger.
Write debug interrupt handler
When the debugger is interrupted, an Exception_Debug_event information notification debugger is generated. The Exception_Debug_info structure will be populated at the same time.
Typedef struct _exception_debug_info {
EXCEPTION_RECORD ExceptionRecord; DWORD dwFirstChance;} EXCEPTION_DEBUG_INFO, * LPEXCEPTION_DEBUG_INFO; typedef struct _EXCEPTION_RECORD {DWORD ExceptionCode; DWORD ExceptionFlags; struct _EXCEPTION_RECORD * ExceptionRecord; PVOID ExceptionAddress; DWORD NumberParameters; ULONG_PTR ExceptionInformation [EXCEPTION_MAXIMUM_PARAMETERS];} EXCEPTION_RECORD, * PEXCEPTION_RECORD; In this configuration, We are more interested in generating interrupted addresses ExceptionAddress and generating interrupt information code ExceptionCode. Information code related to our task in the information code is:
EXCEPTION_BREAKPOINT: Breakpoint Interrupt Information Code
EXCEPTION_SINGLE_STEP: Single-step Interrupt Information Code
The breakpoint interruption is due to we produced when we set the breakpoint 0xcc code running. Since the interrupt is generated, we must write the original code back to the debugged program to continue running. However, once the code is written back to the target program so that when the target program calls the function again, we will only achieve a monitoring. Therefore, after we must write the original code back to the debugged procedure, we should let the debugged program have been single-step, and then generate a single-step interrupt debugging information. In single-step interrupt processing, we will write the 0xcc code to the entry address of the function, so that it can guarantee that an interrupt is generated again. First, we must prepare for the starting line ID and thread handles before interrupting processing. To manage single-step interrupt processing, we must also maintain a process based on thread-based single-step addresses, which allows the debugged program to have multi-threaded features. - We cannot guarantee that the single-step runtime is not interrupted by other threads of the process.
/ / We use a MAP to manage the relationship between thread ID and threaded handles.
/ / Also uses a MAP management function address and breakpoint TYPEDEF MAP
> Thread_map;
Typedef Map
> Thread_singlestep_map;
Thread_map _gthreads; fun_break_map _gfunbreaks; // and assume that the following scheme is used to manage the original code for the original code byte code = setbreakpoint (pfunadd, 0xcc); if (code! = 0xcc) _Gfunbreaks [pfunadd] = code; ... // debugging program BOOL WINAPI OnDebugEvent (DEBUG_EVENT * pEvent) {BOOL rc = TRUE; switch (pEvent-> dwDebugEventCode) {case CREATE_PROCESS_DEBUG_EVENT: // record the relationship between the thread ID and thread handle _gthreads [pEvent-> dwThreadId] = pEvent-> u.CreateProcessInfo.hThread; ... break; case CREATE_THREAD_DEBUG_EVENT: record thread relations // ID and thread handle _gthreads [pEvent-> dwThreadId] = pEvent-> u.CreateThread.hThread; ... break; case EXIT_THREAD_DEBUG_EVENT: // thread exit Clear thread id _GTHReads.rase (pevent-> dwthreadid); ... Break; Case Exception_debug_event: // Interrupt handler RC = ONDEBUGEXCEPTION (PEVENT); Break; ...} Return RC;} The interrupt handler is performed. Similarly, we only consider the interrupt information code we care. When interrupt occurs, we get the context information of the interrupt thread through GetThReadContext (& Context). At this point, context.esp is the return address of the function. The value of the Context.esp 4 position is the first parameter of the function. Context.esp 8 is the second parameter, and you can get any parameters you want. It should be noted that because the parameters are content in the debugged process, so you must get through the ReadProcessMemory function:
DWORD BUF [4];
// Take 4 parameters readProcessMemory (_GHDebug, (void *) (Context.esp 4), & buf, sizeof (buf), & dwread);
Then BUF [0] is the first parameter, BUF [1] is the second parameter. . . Note, when the function is called in Funa (int A, char * p, openfilename * pof) function, BUF [0] = a, buf [1] = P Here BUF [1] is the pointer of P instead of P, if you I hope to access the contents of P, you must obtain the contents of P again through the ReadProcessMemory function. This must also be the same for structural pointers:
// Get the contents of P:
Char PBUF [256]; ReadProcessMemory (_GHDebug, (Void *) (BUF [1]), & PBUF, SIZEOF (PBUF), & dwread); // Get POF content: OpenFileName of ReadProcessMemory (_GHDebug, (void *) (BUF [2]), & of, sizeof (s), & dwread);
If there is a pointer in the structure, the contents of the debugged program must be read in the same manner as the content of the P-based content. In general, you must realize that all content of the monitoring target program is a memory read operation of the target process, which is the memory address of the target process, not the address of the debug process. Obviously, when the debugging process generates an interrupt information in the function entry, the debugger can only get the function's input parameters, and cannot get the output parameters we want and return value! In order to achieve our goal, we must generate interrupts, acquire the output parameters and return values of the function when the function call is completed. When processing a function inlet interrupt, a breakpoint of the return address of the function must be set. Thus, when the function returns, the output parameter of the function and the return value are returned. Refer to the source code of the appendix for the implementation of the appendix. You can write your own simple debug monitoring programs through the source code of Appendix. Of course, there are several problems because I have not explained here. One is the process of the function returns a breakpoint. For example, Try, Catch's processing must be redesigned with the structure of return_fun_stack, considering that some defect processing can still solve this problem. Another problem is that there is no relationship with the entrance breakpoint of the function and the return breakpoint. This problem is better resolved, just redesign RETURN_FUN, FUN_BREAK_MAP and other structures can associate them. Since I am here as long as it is to analyze how to implement interrupt debugging processing, these perfect procedures work by readers to track.
About Win9x system
The careful readers can find a problem in the setbreakpoint function, that is, the entry address of the function cannot be greater than 0x80000000. In this way, we know that the space of 0x80000000 or more is a systematic space, we generally cannot modify the procedures for these spaces, otherwise it will affect the system's work. In the NT environment, all DLLs are loaded under 0x80000000, and the code to modify the 0x80000000 will not have an impact on other processes. Therefore, all DLL functions can be monitored in NT. However, under Win9X, kernel32.dll, user32.dll, gdi32.dll, etc., is loaded into spaces of 0x80000000 or more, modifying the code for these spaces to destroy the system. So, do you not monitor the function of these DLL modules under 9x? Indeed, monitoring of setting breakpoints at the entry at the Win9X platform cannot be utilized. We must implement this function with additional methods. In the previous discussion, the method of modifying the module import table via the API Hook can implement the entry of the API to modify the inlet of your monitoring program, or monitor functionality. If the API Hook is used, it must be known that the function prototype must be written for each function, and the flexibility is restricted. And our goal is whether there is how many DLLs, no matter how many export functions in DLL, we can achieve our monitoring function without modifying our procedure. So, the API Hook cannot complete our goal, but we can use the modified solution to implement the target. First, modify the import table, point the function's call address to our monitoring code, in the monitor code, we don't have to program the function, just simply call JMP XXXX. Then, when you set a breakpoint, it is not set in the entry point of the function, but is set on our monitor code. Thus, when our module calls the system API function, monitoring is achieved. The principle of modification is like a picture:
As shown, assuming that our monitoring code is 0x20000000 space of the target process, we will export the address of the table function while analyzing the DLL export table, and set to JMP XXXX code in the monitor code. This way we are writing to the address of the monitor code when modifying the Import Table of the EXE module. When the target program calls the MessageBox function, the program will first jump to the monitor code to perform the JMP instruction to the USER32.DLL MessageBox entry address. After doing so, we want to monitor the call of the Messagebox function, just set the breakpoint at the 0x200000000 of the monitoring code, it has reached the purpose of monitoring. Limited to the reason, it will not be discussed here. Extended application
You can easily expand your monitoring tracking function on this basis. Just modify the program that records the input and output function results, you get a new feature: 1. The function of the monitoring function call performance is achieved in the logging input and output parameters. (The Truetime function equivalent to NUMEGA) Due to the DEBUG technology, the resulting time will include the debug function results in the switching time of the process. The time is just a reference value, but it is generally sufficient for analysis performance. 2. Add a function called by the input and output parameters to add a function that is called, the Numega's TRUECOVERAGE function is implemented. 3. Monitor the input and output values of Malloc, Free, Realloc functions, and make a simple memory leak check function. The key is that you can get the address of the RELEASE version of the RELEASE version by the MAP file to implement the tracking of the Release version. 4. Add a StackWalk function in the record input parameter processing to implement a Call Stack function, and the analysis is called by which function calls yourself. This function can also be implemented in the JMP scheme, but you must make sure that the function of StackWalk is not called the function you monitor. In the Hook API (IAT) scenario, it is not guaranteed, but it is possible to contain your monitoring code in the call list. One thing to note is that our goal is to monitor the running path of the program, not changing the parameters and modification results, so the modification parameters and run paths can be implemented here in JMP and Hook API (IAT). . Others: The code TESTDEBUG.ZIP Appendix this article is a simple debug monitor that automatically outputs the address content and function call return value of the 4 input parameters of the monitor function. The code only indicates that the API can be implemented by the monitoring function, so the monitoring of the system DLL is not implemented. Debugapi.zip is an application DEBUGAPISPY.exe written by this solution that implements the input and output parameter function of the most basic tracking monitor in this scenario, which also implements the monitoring support for system DLL under 9X. The program supports the use of Win9X / NT / W2K / XP. Source code download: Testdebug.zip, Debugapi.zip Reference: 1. "Windows Core Programming", Jeffrey Richter, Machinery Press 2. Microsoft's MSDN 3. Detours can get the source code on http://research.microsoft.com/sn/detours/. The DETOURS function is valid under WinNT and W2K and does not support 9X.