After reading the principle of detour, I thought about whether I could simulate one.
Some details were found during the comparison. Detour replaces the first few bytes of the function (five bytes when reading the document) with jmp __
. But does machine code truncation take effect? You can see the machine code of each Assembly instruction in the Disassembly window of vs. The length of the machine code of each instruction is not fixed, I think detour is not as simple as a fixed copy of five bytes (the source code of detour has not been studied yet ). Therefore, the second step (this is a learning experiment) is to change the design.
I chose to change the modified Code back to the original function after the defined function is executed. It looks like this:
In addition, the user-defined function does not know that it returns traceBack.
That is, the original function is always executed. This becomes a hook and can only be hooked once, because I restored the modified area.
However, I will not care about this for the moment (this is a learning experiment ).
Some interesting details are discovered only when it is actually implemented by itself. For example, the jmp should have multiple machine codes. Which one should be selected? The code page is not allowed to be written and must be modified by the VirtualProtect function, traceBack cannot return but jmp. To ensure stack balance, it must be written as _ declspec (naked.
I also modified 15 bytes and three sentences:
Push __
Push __
Jmp __
The first push information is provided to traceBack so that he can find the code before modification and copy it back.
The second push is pushed to the traceBack address, so that the jump to traceBack is transparent to the User-Defined Function (hook.
The last hop is a recent hop, so the relative displacement must be calculated.
Effect:
1: #include "detour.h"
2: #include <stdio.h>
3: #include <windows.h>
4:
5: #pragma optimize("",off)
6:
7: void hook()
8: {
9: printf("detoured\n");
10: }
11:
12: int main()
13: {
14: detourInit();
15: //---------------------------------------------------------
16:
17: detour( Sleep,hook );
18:
19: Sleep(1);
20:
21: system("pause");
22: return 0;
23: }
PS: detour. h is my own source code. (The code is below)
Result:
Sleep is hooked up. However, it is normal to call Sleep again.
Code:
Detour. h (actually, it should be named hook)
1: #ifndef __DETOUR_H__
2: #define __DETOUR_H__
3:
4: #pragma once
5:
6: #include <windows.h>
7: #include <assert.h>
8: #include <stdlib.h>
9:
10: typedef unsigned char u8;
11:
12: // push (subscript) + push (traceBack address) + jmp (hook) cover 15 bytes
13: #define TBSIZE 15
14: #define INFOSIZE 5
15: #define REPSIZE 15
16:
17: //machine code
18: #define MC_FARJMP 0xE9
19: #define MC_PUSH 0x68
20:
21: struct s_detour
22: {
23: void *oldFunc;
24: void *hook;
25: u8 backUp[TBSIZE];
26: };
27:
28: s_detour gInfo[INFOSIZE];
29:
30: void detourInit()
31: {
32: assert( sizeof(gInfo) > sizeof(s_detour) );
33: memset( gInfo,0,sizeof(gInfo) );
34: }
35:
36: LPDWORD ftb_old = new DWORD;
37: BOOL ftb_res;
38: void* ftb_addr;
39: __declspec(naked) void traceBack( int tn )
40: {
41: //restore
42: // ps: Only jmp can return
43: __asm
44: {
45: push 0x1 // pretend to fill the address so that tn can be accessed correctly
46: push ebp
47: mov ebp,esp
48: }
49:
50: assert( tn >= 1 && tn < INFOSIZE );
51:
52: //#the return would work?
53: ftb_res = VirtualProtect( gInfo[tn].oldFunc,REPSIZE,PAGE_EXECUTE_READWRITE,ftb_old );
54: assert( ftb_res );
55:
56: memcpy( gInfo[tn].oldFunc,gInfo[tn].backUp,REPSIZE );
57:
58: ftb_res = VirtualProtect( gInfo[tn].oldFunc,REPSIZE,*ftb_old,ftb_old );
59: assert( ftb_res );
60:
61: //ready to back
62: ftb_addr = (void*)gInfo[tn].oldFunc;
63: gInfo[tn].oldFunc = NULL;
64: __asm
65: {
66: mov esp,ebp
67: pop ebp
68: pop eax //pop 0x1
69: pop eax //balance stack pop tn
70:
71: mov eax,ftb_addr
72: jmp eax
73: }
74: }
75:
76: //register
77: bool detour( void *oldFunc,void *hook )
78: {
79: if( oldFunc == NULL || hook == NULL )
80: return false;
81:
82: int tn;
83: for( tn = 1;tn < INFOSIZE;++tn )
84: {
85: if( gInfo[tn].oldFunc == NULL ) //could be used
86: break;
87: }
88: if( tn == INFOSIZE )
89: return false;
90:
91: gInfo[tn].oldFunc = oldFunc;
92: gInfo[tn].hook = hook;
93:
94: LPDWORD lpOld = new DWORD;
95: BOOL res = VirtualProtect( oldFunc,REPSIZE,PAGE_EXECUTE_READWRITE,lpOld );
96: assert( res );
97:
98: memcpy( gInfo[tn].backUp,oldFunc,REPSIZE ); //back up
99:
100: //replace
101: u8 *pf = (u8*)oldFunc;
102: pf[0] = MC_PUSH;
103: * (int *) (int) pf + 1) = tn; // press the tag
104:
105: pf = (u8*)( (int)pf + 5 );
106: pf[0] = MC_PUSH;
107: * (int *) (int) pf + 1) = (int) traceBack; // press the traceBack address
108:
109: pf = (u8*)( (int)pf + 5 );
110: pf[0] = MC_FARJMP;
111: * (int *) (int) pf + 1) = (int) hook-(int) pf)-5 ); // relative address of near jmp
112:
113: res = VirtualProtect( oldFunc,REPSIZE,*lpOld,lpOld );
114: assert( res );
115: free( lpOld );
116:
117: return true;
118: }
119:
120: #endif