"Variable speed gear" re-study the author bbbkom Key words shifting gear call door RING0 lifted "shifting gear" (hereinafter referred to as "gears") this software, everyone should know, the software is known as the world's first program to change the game speed . I am very magical when I started, I can't think of the principle of achieving it, but I have a limited personal level. I have never been able to solve it. It has become a big question mark that I can't stay in my brain. I saw a article called "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " However, there is still a relatively vague understanding: the original gear is achieved by cutting the game program for the time-related function call and modify the return result. In order to completely understand the principle of the gear, I am going to have an export. Taking into account the "hand" author from the "gear" disassembly code, then I also start from the disassembly code. However, it is not enough to compile the content, and a few books from the library have a number of books on the underlying mechanism of Windows and 386. After almost two weeks of "practice", the self-feeling is a bit good, haha, I also have something to wait. Put the "gear" out of eight pieces! Before I started, I saw "Issue" again. This time you can clearly: Jump to the Ring0 class section by calling the door, modify the first 8 bytes of each system time-related function as JMP instructions, and turn To the "gear" mapped to the code above 2G, the purpose of intercepting the call to each system time-related function. But at the same time, my doubts are also more clear: 1. "Gear" how to establish a calling door descriptor pointing to the code that maps to 2G or more; 2. How to map your code to a linear address of 2G 3. How is the code that mapped to 2G to do this with such doubts correctly in the case of code base, I officially started an analysis of "gears" disassembly code. Tools, don't say, when it is Softice for Windows98, W32DASM, OK, departure! My "Gear" version is 0.221 for Win98 and WinMe, with two files (shifting gears. EXE and HOOK.DLL). First look at the hook.dll, use W32DASM to disassemble hook.dll, look at its output function:? Ghwnd @@ 3pauhwnd __ @@ a? GNHOTKEY1 @@ 3ka? Gnhotkey2 @@ 3ka? GNHOTKEY3 @@ 3ka? GNHOTKEY4 @@ 3ka? NHOOK @@ 3ha? set @@ yahpauhwnd __ @@? unhook @@ yahxz See a function name as if the DLL is just installing a hook capture shift hotkey, with my research purposes is not too big, jump over! Look at the change in the transmission gear. EXE import function, TimegetTim, getTickcount and other functions are in it. Hey, there is CreateFilemappingA and MapViewOffileEx, it seems that "gears" is created with these two functions.
Here are some key import function: Hook.?gnHotKey1@@3KA Hook.?gnHotKey2@@3KA Hook.?gnHotKey3@@3KA Hook.?gnHotKey4@@3KA Hook.?SetHook@@YAHPAUHWND__@@@Z KERNEL32 .CreateFileMappingA KERNEL32.GetModuleFileNameA KERNEL32.GetModuleHandleA KERNEL32.GetTickCount KERNEL32.MapViewOfFileEx KERNEL32.QueryPerformanceCounte USER32.KillTimer USER32.SendMessageA USER32.SetTimer WINMM.timeGetTime WINMM.timeSetEvent Since the "gear" intercepted timeGetTime, I'll track the implementation of timeGetTime function. I first wrote a Win32 app (hereinafter referred to as app), when the TimegetTime is called when the client is left, and the returned result is output to the client area. Run this program, open the "gear", change the current speed. Ctrl D exhaled Softice, BPX TimegetTime, exit, left the APP client area, and Softice jumped out. Ha, the first instruction of the TimegetTime function is JMP 8xxx 002a, and F8 continues to execute, and enter the "gear" map to the code above 2G linear addresses. All the way f8, found that "Gear" will then restore the TimegetTime command, and call TimeGetTime again so that the correct result of TimegetTime is obtained and saved. "Gear" will change the TimegetTime command to JMP 8xxx 002a. I guess what I have to do next! That's right, return the return value to the Return value to the program app to call TimegetTime. I carefully analyze, the formula of "gear" modification return value is as follows: multiple * (return value - the return value of the first call TimegetTime) Modified return value = ----------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- " The return value after the last revised "is the unconfirmed that I guess, for reference only. The code analysis has been part, but I have not solved my previous question, how is the "gear" shot the code map? Also get the right to modify the code? Since CREATEFILEMAPPINGA is called in the "gear", I want to install the call door, the initialization portion of the mapping code should be in the vicinity of the function code. Ok, along this idea, exhale Softice, set breakpoints at Createf Ilemappinga, turn the "gear" and then run. SoftICE jumps out, stopping at CreateFile Mappinga, F11 returns to the code of "gear".
See the "gear" calls CreateFileMappinga as follows: CreateFileMappingA (FF, 0, 4, 0, 10000, 0); Visible "Gear" creates a mapping file with a length of 0x10000, continue, "gear" then calls MapViewOffileEx, call The form is as follows: MapViewOffileEx (EDX, 2, 0, 0, EAX); // Edx returned to createFileMappingA // EAX for the base address of the application map, Eax is 0x8000 0000 when the first call is 0x8000 0000 here is The key, "Gear" is mapped to the memory space of the base address to 0x8000 0000, and it is not seen that Windows really allows him to map? Sure enough, "gear" judges whether the return value is valid after the call, if it is invalid, adds the base address of the last application to 0x1000, and then calls MapViewOffileEx again. It is looped to success until it is successful, and the returned address will be saved. Next, "Gear" copies the code lines of the original "gear" EXE to the mapping area. At this point, the "gear" has mapped the critical code to the linear address of 2G. I will f8, haha, and the familiar SGDT command has played a photo. "Gear" Save the Global Descriptive Table Linear Address, and then save the local descriptor table index with the SLDT command to calculate the LDT base address. Then, "Gear" creates a code segment of the privilege level in the local descriptor to point to the "gear" own code that needs to use Ring0 privileges, and points the address of the call gate pointing in the local descriptor table 2. Change to "gear" is mapped to code higher than 2G. Then "gear" calls each time-related API and saves its return value to leave the result of the result. The "gear" is called again to modify the first command of each API with a code that mapped to 2G. Here, the initialization section of "Gear" is over, just waiting to be hooked on the game in the drum, haha! The end code is just a matter of recovery, it is just the inverse process of the initialization code, so it will not be described again (in fact, I am too lazy to see, ^ _ ^!). So, my acceleration principle of "gears" has been Have a general understanding, deeply feeling the exquisiteness of the "gear" code, so I feel that some of the techniques used in the "gear" have been summarized: 1. The base address has nothing to do with the code to write aunt and the title is a title. ^ _ ^. Looking at the initialization code of the "gear", knowing that the base address of its mapping code is almost random, how is the "gear" guarantee that the code after the map can run properly? If the code is a complete order, it has nothing, but if you want to call the subroutine in your mapped code? Oh, just calculate the entry address of the child program and call it, but still to get the address where the map code is located. The "gear" simply uses two instructions to get the address of the currently executing instruction, the specific (address is assumed): 0: 0 Call 5 0: 5 Pop ESI now ESI's value is 5, haha! The CALL here is close-in, the whole command is E800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000-00-00 Address. 2. Modify the call door, generate JMP instructions, and modify the code is highly dependent on the CPU operation, the skill is also very strong, mainly to drill the exploitation of the operating system.
For example, "Gear" is to use SGDT, SLDT to obtain global and local descriptor table base sites to install call doors. By accessing the calling door to get RING0 permissions for some operations allowed by the system; and CIH viruses are interrupted with SIDT Description The Chart At the base address The interrupt gate is installed and then the soft interrupt is obtained, the principle is the same. These are discussing many times on the water, everyone is very familiar, so they don't dare to go to the ax, it is written. 3.64K Code Writing by calling createFileMappinga function parameters can know "gear" only maps 10000 (64K) area, so that the code and data mapped above 2G will never be greater than 64K. I think the author chooses 64K to the size of the map area, which may be related to the caller or data. After any one of the mapping code, it obtains a base address of the mapping code after obtaining the current command address, and then the offset of the subroutine inlet or data can be obtained. My Comment: One sentence: Mr. Wang Rong, who admires "gears". The code of the "gear" shows his deep understanding of the Windows operational mechanism and the deep compilation of the idea. For me, "gears" seem to be a beautiful art. Every place is worthwhile, so I have seen my analysis process after I have seen the "gear" code. impulse. But at the same time, I have to admit that the implementation of the "gear" is realized by its highly technically skill, in other words, this is the limitations of this method. Isn't it the interception of the API? Is it so troublesome? In order to confirm your own ideas, I directly found a hook API code on CodeGuru, which is the input festival of modifying the process PE image in all DLL processes by installing the WH_CBT type global hook. Methods have a detailed description in "Windows Core Programming"). Make the code a little modification, you can work (tried under the StarCraft, you can change the game speed). Despite the only 98, I feel that I can use it in 2000 because I only use one or two compilation instructions in the code, and the entire program is running under RING3, and there is no derailed move. Of course, this method also has a disadvantage that the API call does not work with loadLibrary to get the WinMM.dll and get the TimegetTime address with GetProcAddress (why the reason is in "Windows Core Programming"). I am planning to publish the source code after the test program is slightly perfect, and you will be welcome to download. My gratitude: After I completely figure out the code of "Gear", it is already the third day of the morning, helplessness is too much, and it is not like "notes", I only spend a night, I will spend it. One morning, two afternoons, two nights ended the battle, it is really embarrassing. I have been able to adhere to myself for two days, it is not open to Xiaoqiang's support with the brothers of the bedroom.
I didn't know how much smoke in these days. I didn't think of the news. I only said that I am here! Also I would like to thank Sunlie to read this article very much, pointing out the mistakes in the original text and very valuable comments! The last thing to say is that personal level is limited, and there is a mistake in the article. Welcome everyone! ^ _ ^ A: Usage Tools: Softice for Windows98, W32DASM, Visualc 6.0 Operating System: Window98 2nd Analysis Target: Variable Speed Gear for 98me Version: 0.221 Reference Books or Articles: 80x86 Compilation Language Program Design Tutorial Yang Jiwen, etc. WINDOWS analysis - initialization articles and kernel articles Tsinghua University Press Virtual Equipment Driver Development Intel 32-bit System Software Programming 80x86 Instruction Reference Manual "" Transmission Gears "Research Book" Addition B: "Gear" Key Code Complete Note First, Initialization section (start analyzing the call CreateFileMappingA function from the "gear") 0167: 00401B0E PUSH 00 0167: 00401B10 PUSH 00010000 0167: 00401B15 PUSH 00 0167: 00401B17 PUSH 04 0167: 00401B19 PUSH 00 0167: 00401B1B PUSH FF 0167: 00401B1D cALL [KERNEL32 CreateFileMappingA! ]; Call CreateFileMappingA; call form such as right: CreateFileMappingA (FF, 0, 4, 0, 10000, 0) 0167: 00401B23 MOV ECX, [EBP-30] 0167: 00401B26 MOV [ECX 00000368], EAX 0167: 00401B 2C MOV DWORD PTR [EBP-14], 80000000 0167: 00401B33 JMP 00401B41 0167: 00401B35 MOV EDX, [EBP-14] 0167: 00401B38 Add EDX, 00010000; Application Base address plus 0x10000 0167: 00401B3E MOV [EBP-14], EDX 0167: 00401B41 MOV EAX, [EBP-14] 0167: 00401B44 Push EAX; mapping file base address 0167: 00401b45 push 00;
The number of bytes of the map 0167: 00401B47 PUSH 00; file offset low 32 bits 0167: 00401B49 push 00; file offset high 32 bits 0167: 00401B4B PUSH 02; Access mode 0167: 00401B4D MOV ECX, [EBP-30] 0167: 00401B50 MOV EDX, [ECX 00000368] 0167: 00401B56 PUSH EDX; CreateFileMappingA returned map file handle 0167: 00401B57 cALL; call form at right [KERNEL32 MapViewOfFileEx!]: MapViewOfFileEx (EDX, 2,0,0,0, EAX) 0167: 00401B5D MOV ECX, [EBP-30]; [EBP-30] for the upcoming map of 2G 0167: 00401B60 MOV [ECX 0000036C], EAX; code of the code 0167: 00401B66 MOV EDX , [EBP-30] 0167: 00401B69 CMP DWORD PTR [EDX 0000036C], 00; check MapViewOfFileEx 0167: 00401B70 JZ 00401B74; return value, if 0 continues to transfer 0167: 00401B72 JMP 00401B76; call MapViewOfFileEx 0167: 00401B74 JMP 00401b35; until success is 0167: 00401B76 MOV EAX, [EBP-30] 0167: 00401B79 MOV ECX, [EAX 0000036C] 0167: 00401B7F MOV [EBP-08], ECX; mapping file start address storage [EBP-08 ] 0167: 00401B82 Call [Winmm! TimegetTime] 0167: 00401B88 MOV [EBP-14], EAX; TimegetTime 0167: 00401BA0 MOV ECX, [EBP-08] will be saved to [EBP-14] 0167: 00401BA3 MOV EDX, [EBP-14]; and mapping file base address FF30 0167: 00401BA6 MOV [ECX
0000ff30], edx ... omitted code Similar save call for the initial GetTickCount, QueryperFormanceCounTer return value 0167: 00401bed MOV DWORD PTR [EBP-14], 00000000 0167: 00401BF4 MOV EDX, [EBP-30] 0167: 00401BF7 MOV EAX [EDX 0000036C] 0167: 00401BFD MOV ECX, [EBP-14] 0167: 00401C00 MOV BYTE PTR [ECX EAX 0000F000], 9A; 9A is a long-conditioned instruction code 0167: 00401c08 MOV EDX, [EBP-14 ] 0167: 00401c0B Add EDX, 01 0167: 00401C0E MOV [EBP-14], EDX 0167: 00401C11 MOV EAX, [EBP-14] 0167: 00401C14 Add Eax, 04 0167: 00401C17 MOV [EBP-14], EAX 0167: 00401C1A MOV ECX, [EBP-30] 0167: 00401C1D MOV EDX, [ECX 0000036C] 0167: 00401C23 MOV EAX, [EBP-14] 0167: 00401C26 MOV BYTE PTR [EAX EDX 0000F000], 14; 14 is called Index of door descriptor 01 67: 00401C2E MOV ECX, [EBP-14] 0167: 00401C31 Add ECX, 01 0167: 00401C34 MOV [EBP-14], ECX 0167: 00401C37 MOV EDX, [EBP-30] 0167: 00401C3A MOV EAX, [EDX 0000036C ] 0167: 00401C40 MOV ECX, [EBP-14] 0167: 00401C43 MOV BYTE PTR [ECX EAX 0000F000], 00; Call instructions other parts 0167: 00401C4B MOV EDX, [EBP-14] 0167: 00401C4E Add EDX, 01 0167: 00401C51 MOV [EBP-14], EDX 0167: 00401C54 MOV EAX, [EBP-30] 0167: 00401C57 MOV ECX, [EAX
0000036C] 0167: 00401C5D MOV EDX, [EBP-14] 0167: 00401C60 MOV BYTE PTR [EDX ECX 0000F000], C2 0167: 00401C68 MOV EAX, [EBP-14] 0167: 00401c6b Add Eax, 01 0167: 00401C6E MOV [EBP-14], EAX 0167: 00401C71 MOV ECX, [EBP-30] 0167: 00401C74 MOV EDX, [ECX 0000036C] 0167: 00401C7A MOV EAX, [EBP-14] 0167: 00401C7D MOV BYTE PTR [EAX EDX 0000F000], 00 0167: 00401C85 MOV ECX, [EBP-14] 0167: 00401C88 Add ECX, 01 0167: 00401C8B MOV [EBP-14], ECX 0167: 00401C8E MOV EDX, [EBP-30] 0167: 00401C91 MOV EAX , [EDX 0000036C] 0167: 00401C97 MOV ECX, [EBP-14] 0167: 00401C9A MOV BYTE PTR [ECX EAX 0000F000], 00 0167: 00401CA2 MOV EDX, [EBP-14] The above code is written in the mapping code offset F000 write instruction call 0014: 0000 0167: 00401CA5 Add EDX, 01; Directive A91400C20000 Total 6 bytes 0167: 00401CA8 MOV [EBP-14], EDX; 0167: 00401CAB MOV ESI , 0040213B; start address 0167: 00401cb0 MOV EDI, [EBP-08] to be copied, [EBP-08]; the target address to copy code (mapping area) 0167: 00401cb3 MOV ECX, 00402688; 402688 is the end of the code to be copied Address 0167: 00401CB8 SUB ECX, ESI 0167: 00401CBA REPZ MOVSB; copy the code all to the mapping area 0167: 00401CBC SGDT fword PTR [EBP-1C]; This sentence is very critical 0167: 00401cc0 Lea EAX, [EBP-001C ] 0167: 00401CC6 MOV EAX, [EAX 02];
Take GDT linear base 0167: 00401cc9 xor EBX, EBX 0167: 00401ccb SLDT BX; offset from LDT in GDT 0167: 00401CCE and BX, -08 0167: 00401CD2 Add EAX, EBX 0167: 00401CD4 MOV ECX, [EAX 02] 0167: 00401CD7 SHL ECX, 08 0167: 00401CDA MOV CL, [EAX 07] 0167: 00401CDD ROR ECX, 08; The above calculates the LDT linear base 0167: 00401CE0 MOV [EBP-0C], ECX; Save 0167: 00401CE3 MOV EAX, [EBP-30] 0167: 00401CE6 MOV ECX, [EBP-0C] 0167: 00401CE9 MOV [EAX 00000370], ECX 0167: 00401CEF MOV EDX, [EBP-30] 0167: 00401CF2 MOV EAX, [EDX 0000036C] 0167: 00401CF8 MOV ECX, [EBP-0C] 0167: 00401cfb MOV [EAX 0000FE00], ECX; save the LDT linear base to map code 0167: 00401D01 MOV AX , CS; get the current code segment description symbol 0167: 00401d04 and AX, FFF8 0167: 00401D08 MOV [EBP-10], AX 0167: 00401D0C MOV EDX, [EBP-10] 0167: 00401d0f and edx, 0000fff; EDX is a code segment Descriptor in the offset of 0167: 00401d1d15 MOV EAX, [EBP-30] 0167: 00401d18 MOV ECX, [EAX 00000370]; ECX This time is LDT linear base 0167: 00401d1e MOV EAX, [EBP-30 ] 0167: 00401d21 MOV EAX, [EAX 00000370]; EAX is the LDT linear base address
0167: 00401D27 MOV ESI, [EDX ECX] 0167: 00401D2A MOV [ES 08], ESI 0167: 00401D2D MOV ECX, [EDX ECX 04]; Copy the current code segment descriptor to 0167: 00401D31 MOV [ EAX 0C], ECX; LDT 1 item 0167: 00401D34 MOV EDX, [EBP-30] 0167: 00401D37 MOV EAX, [EDX 00000370] 0167: 00401D3D MOV CL, [EAX 0D] 0167: 00401D40 and Cl, 9F 0167: 00401D43 MOV EDX, [EBP-30] 0167: 00401D46 MOV Eax, [EDX 00000370] 0167: 00401D4C MOV [EAX 0D], Cl; modify the DPL of the LDT 1 item, then the call When the door is transferred to this code, I get RING0 permissions 0167: 00401D4F MOV EAX, [EBP-0C] 0167: 00401D52 Add Eax, 10; NOTE 2-in-LDT index 2 call doors address 0167: 00401d55 MOV EBX, 0040213B 0167: 00401D5A MOV [EAX], EBX 0167: 00401D5C MOV [EAX 04], EBX 0167: 00401D5F MOV Word PTR [EAX 02], 000c 0167: 00401d65 MOV Word PTR [EAX 04], EC00; Pickup door modification 0167: 00401d6b MOV ECX, [EBP-08] 0167: 00401D6E MOV EDX, [WinMM! TimegetTime] 0167 : 00401D74 MOV [ECX 0000fee0]
; EDX; save partially omitted timeGetTime entry address ... are sequentially stored GetTickCount, GetMessageTime, timeSetEvent, SetTimer, timeGetSystemTime, QueryPerformanceCounter entry address 0167: 00401DD2 MOV ECX, [EBP-08] 0167: 00401DD5 MOV EAX, [WINMM timeGetTime!] 0167 : 00401DDA MOV EBX, [EAX] 0167: 00401DDC MOV [ECX 0000fe40], EBX 0167: 00401DE2 MOV EBX, [EAX 04] 0167: 00401DE5 MOV [ECX 0000fe44], EBX; save the TimegetTime function 8 bytes ... sequentially stored instruction portion omitted GetTickCount, GetMessageTime, timeSetEvent, timeGetSystemTime, before QueryPerformanceCounter 8 bytes of the instruction 0167: 00401E6D MOV bYTE PTR [ECX 0000FE90], E9 0167: 00401E74 MOV EAX, 00402165 0167: 00401E79 SUB EAX, 0040213B EAX is the offset of the code in the mapping code 0167: 00401e7e Add Eax, ECX; calculates the linear entrance address of the intercept code 0167: 00401E80 SUB Eax, [Winmm! TimegetTime] 0167: 00401E86 SUB EAX, 05; JMP instruction length 5 bytes 0167: 00401e89 MOV [ECX 0000fe91], EAX; calculation generated from TimegetTime jump to the JMP instruction of the intercept code to the intercept code and save ... successively calculates and generates omitted parts GetTickCount, GetMessageTime, timeSetEvent, timeGetSystemTime, QueryPerformanceCounter jump instruction JMP intercepted code and save 0167: 00401F58 CLI; interrupt disabled, to guard against accidents when modifying the code 0167 occurs: 00401F59 MOV EAX, 004021F3; 0167: 00401F5E SUB Eax, 0040213b; Calculating subroutines in mapping code 0167: 00401f63 add eax, [EBP-08]; EAX = 8xxx 00b8 0167: 00401f66 push EAX;
The incoming parameter EAX is modified; the subroutine entry address 0167: 00401F67 MOV EAX, [EBP-08]; call 8xxx 0000 0167: 00401f6a call eax; return timegettime first command is changed ... 省略 部分 部分 依 g , the first instruction GetMessageTime, timeSetEvent, timeGetSystemTime, QueryPerformanceCounter function 0167: 00401FF SETI; set interrupt initialization code two ends, intercept part a function of time (in timeGetTime as an example, the code to track the order listed) timeGetTime JMP 832A 002A; this is timeGetTime The modified first command 0167: 832a 002a CLI; at this time [ESP] = 40BF2C, that is, the next instruction of the TimegetTime function in the game program ... (6) each register is set to the stack and MOV EBP, ESP 0167: 832A 0033 CALL 832A 0038; Put the current EIP (ie the address of the next instruction) 0167: 832A 0038 POP EDI; Remove the current command address XOR DI, DI MOV ESI, EDI The 64K memory head address is assigned to the ESI; at this time, ESI = EDI = 832A 0000 Add ESI, 0040 2102 SUB ESI, 0040 213B; Ask of the mapping code first address PUSH ESI 0167: 832A 004B Call EDI; ESI is a passive parameter The TimegetTime code has been restored by TimegetTime code 0167: 832A 004D Call 832a 0052; 0167: 832a 0052 POP EDI XOR DI, DI; Slimming Technology Call [EDI 0000FEED];
Call the original TimegetTime function SUB EAX, [EDI 0000 FF30]; minus the result of the first call TimegetTime MUL DWORD PTR [EDI 0000 Fe30]; multiplied multiples specified by the user MOV EBX, 00100000 DIV EBX; divided by constant 100000 Add Eax, [EDI 0000FE20] MOV EAX, 004021F3 SUB EAX, 0040213B Add Eax, EDI; The above command returns the value of the TimegetTime function returns the value of the Push Eax; Eax is the passive parameter CALL EDI; return TimegetTime first instructions JMP ... Restores the value of each register, Eax is the modified return value return; at this time [ESP] = 40BF2C, the execution RET will return to the game; 0167: 832A 0000 Call 832a 0005 0167: 832A 0005 POP EDI XOR DI, DI; Old Set Springs _ ^ MOV ESI, [EDI 0000 Fe00]; This address saves LDT linear base MOV EAX, [ESP 04] MOV [ESI 10 ], AX SHR EAX, 10 MOV [ESI 16], AX; The above code is changed to the offset of the memory door descriptor in the LDT 2 to the incoming parameter ... MOV EAX, 0000 0F00 Call EAX;
Call subroutine modification TimegetTime code 0167: 832a 0027 RET 4; pop-up parameter, return; 0167: 832a f000 call 0014: 000000 RET 0; 000c: 832A 0097 Call 832a 009c 000c: 832a 009c Pop EDI MOV EAX, [EDI 0000 Fe40 ] MOV EBX, [EDI 0000 FeE0] MOV [EBX], EAX MOV EAX, [EDI 0000 Fe44] MOV [EBX 04], ED RETF Note: EDI 0000 Fe40 starts from 8 bytes as the original TimegetTime function The instruction EDI 0000 FeE0 saves the entry address of the TimegetTime function to restore the first 8 bytes of TimegetTime; 000c: 832A 00B8 Call 832a 00bd 000c: 832a 00bd Pop EDI XOR DI, DI ... MOV EAX, [EDI 0000 Fe90] MOV EBX, [EDI 0000 Fee0] MOV [EBX], EAX MOV EAX, [EDI 0000fe94] MOV [EBX 04], EAX RETF