------------------------------------------------- Playing with RDTSC ------------------------------------------------- by Piotr Bania http://pb.specialised.info All rights reserved! Disclaimer ---------- Author takes no responsibility for any actions with provided informations or codes. The copyright for any material created by the author is reserved. Any duplication of codes or texts provided here in electronic or printed publications is not permitted without the author's agreement. Introduction ------------ In this short "article" i will present some anti-debugging tricks done with usage of RDTSC intruction. This article is mainly bunch of my sick ideas, written in very fast tempo so sorry for lack of refferences or language mistakes. So here we start with bit of bunch of facts: Code: 0F 31 Mnemonic: RDTSC Description: Loads the current value of the processor's time-stamp counter into the EDX:EAX registers. The time-stamp counter is contained in a 64-bit MSR. The high-order 32 bits of the MSR are loaded into the EDX register, and the low-order 32 bits are loaded into the EAX register. The processor increments the time-stamp counter MSR every clock cycle and resets it to 0 whenever the processor is reset. The time stamp disable (TSD) flag in register CR4 restricts the use of the RDTSC instruction. When the TSD flag is clear, the RDTSC instruction can be executed at any privilege level; when the flag is set, the instruction can only be executed at privilege level 0. The time-stamp counter can also be read with the RDMSR instruction, when executing at privilege level 0. The RDTSC instruction is not a serializing instruction. Thus, it does not necessarily wait until all previous instructions have been executed before reading the counter. Similarly, subsequent instructions may begin execution before the read operation is performed. This instruction was introduced into the Intel Architecture in the Pentium processor. Most known example ------------------- Following codes is trying to prevent the application from single stepping. It will execute RDTSC twice and then calculate the difference between low order values and check it with cmp condition. If the difference lays below 0FFFh no debugger is found if it is above or equal then application is debugged (singlestepped etc.) ;------------------ SNIP ----------------------------------------- rdtsc mov ecx,eax rdtsc sub eax,ecx cmp eax,0FFFh jae found_debugger_action ;------------------ SNIP ----------------------------------------- Some crazy ideas ---------------- Following tests were done under my Windows XP SP1 on Intel Celeron 2,8ghz - pretty overloaded :). Check following program: ;------------------ SNIP ----------------------------------------- #include #include #include #define RDTSC(x,y) __asm rdtsc \ __asm mov x,eax \ __asm mov y,edx int main() { DWORD a1,b1,a2,b2; int i; for (i=0; i<20; i++) { RDTSC(a1,b1); _lopen("././RANDOM",OF_READ); RDTSC(a2,b2); printf("[%.02d] cycle: EAX2-EAX1 = %.08x * EDX2-EDX1 = %.08x\n",i,(a2-a1),(b2-b1)); } getch(); return 0; } ;------------------ SNIP ----------------------------------------- What does this program? It simply calculates the difference of RDTSC values between _lopen api execution. Now check the following output: Non traced (clear run): ---------------------- [00] cycle: EAX2-EAX1 = 000d9860 * EDX2-EDX1 = 00000000 [01] cycle: EAX2-EAX1 = 0009d768 * EDX2-EDX1 = 00000000 [02] cycle: EAX2-EAX1 = 00098bb8 * EDX2-EDX1 = 00000000 [03] cycle: EAX2-EAX1 = 00086d7c * EDX2-EDX1 = 00000000 [04] cycle: EAX2-EAX1 = 00086270 * EDX2-EDX1 = 00000000 [05] cycle: EAX2-EAX1 = 0008890c * EDX2-EDX1 = 00000000 [06] cycle: EAX2-EAX1 = 00085f98 * EDX2-EDX1 = 00000000 [07] cycle: EAX2-EAX1 = 00086fac * EDX2-EDX1 = 00000000 [08] cycle: EAX2-EAX1 = 0008771c * EDX2-EDX1 = 00000000 [09] cycle: EAX2-EAX1 = 000861ac * EDX2-EDX1 = 00000000 [10] cycle: EAX2-EAX1 = 00086cb8 * EDX2-EDX1 = 00000000 [11] cycle: EAX2-EAX1 = 000887a0 * EDX2-EDX1 = 00000000 [12] cycle: EAX2-EAX1 = 00088714 * EDX2-EDX1 = 00000000 [13] cycle: EAX2-EAX1 = 000873d4 * EDX2-EDX1 = 00000000 [14] cycle: EAX2-EAX1 = 000876ac * EDX2-EDX1 = 00000000 [15] cycle: EAX2-EAX1 = 00086484 * EDX2-EDX1 = 00000000 [16] cycle: EAX2-EAX1 = 00087e8c * EDX2-EDX1 = 00000000 [17] cycle: EAX2-EAX1 = 00088ff0 * EDX2-EDX1 = 00000000 [18] cycle: EAX2-EAX1 = 000868e4 * EDX2-EDX1 = 00000000 [19] cycle: EAX2-EAX1 = 00087f50 * EDX2-EDX1 = 00000000 Olly Trace into: --------------- [00] cycle: EAX2-EAX1 = 00f98b50 * EDX2-EDX1 = 00000000 [01] cycle: EAX2-EAX1 = 00f23440 * EDX2-EDX1 = 00000000 [02] cycle: EAX2-EAX1 = 010a786e * EDX2-EDX1 = 00000000 [03] cycle: EAX2-EAX1 = 012233e0 * EDX2-EDX1 = 00000000 [04] cycle: EAX2-EAX1 = 00c8ed4c * EDX2-EDX1 = 00000000 [05] cycle: EAX2-EAX1 = 01014bea * EDX2-EDX1 = 00000000 [06] cycle: EAX2-EAX1 = 00d9c25c * EDX2-EDX1 = 00000000 [07] cycle: EAX2-EAX1 = 00d9d34c * EDX2-EDX1 = 00000000 [08] cycle: EAX2-EAX1 = 01f2a304 * EDX2-EDX1 = 00000001 [09] cycle: EAX2-EAX1 = 00da6e4c * EDX2-EDX1 = 00000000 [10] cycle: EAX2-EAX1 = 01593a9e * EDX2-EDX1 = 00000000 [11] cycle: EAX2-EAX1 = 01dc7ab8 * EDX2-EDX1 = 00000000 [12] cycle: EAX2-EAX1 = 00f0d75a * EDX2-EDX1 = 00000000 [13] cycle: EAX2-EAX1 = 0113998c * EDX2-EDX1 = 00000000 [14] cycle: EAX2-EAX1 = 01c7dfc8 * EDX2-EDX1 = 00000000 [15] cycle: EAX2-EAX1 = 00ddedc0 * EDX2-EDX1 = 00000000 [16] cycle: EAX2-EAX1 = 00cc2308 * EDX2-EDX1 = 00000000 [17] cycle: EAX2-EAX1 = 02318eb8 * EDX2-EDX1 = 00000000 [18] cycle: EAX2-EAX1 = 00c83ec0 * EDX2-EDX1 = 00000000 [19] cycle: EAX2-EAX1 = 02f7e078 * EDX2-EDX1 = 00000000 Olly Trace over: --------------- [00] cycle: EAX2-EAX1 = 00683da4 * EDX2-EDX1 = 00000000 [01] cycle: EAX2-EAX1 = 0063666c * EDX2-EDX1 = 00000000 [02] cycle: EAX2-EAX1 = 006f1778 * EDX2-EDX1 = 00000000 [03] cycle: EAX2-EAX1 = 006d7618 * EDX2-EDX1 = 00000000 [04] cycle: EAX2-EAX1 = 0062c1d0 * EDX2-EDX1 = 00000000 [05] cycle: EAX2-EAX1 = 0062cca4 * EDX2-EDX1 = 00000000 [06] cycle: EAX2-EAX1 = 00787178 * EDX2-EDX1 = 00000000 [07] cycle: EAX2-EAX1 = 00628d34 * EDX2-EDX1 = 00000000 [08] cycle: EAX2-EAX1 = 00e6ab20 * EDX2-EDX1 = 00000000 [09] cycle: EAX2-EAX1 = 006daab4 * EDX2-EDX1 = 00000000 [10] cycle: EAX2-EAX1 = 00647750 * EDX2-EDX1 = 00000000 [11] cycle: EAX2-EAX1 = 008b898c * EDX2-EDX1 = 00000000 [12] cycle: EAX2-EAX1 = 006e00e4 * EDX2-EDX1 = 00000000 [13] cycle: EAX2-EAX1 = 009bc054 * EDX2-EDX1 = 00000000 [14] cycle: EAX2-EAX1 = 00634200 * EDX2-EDX1 = 00000000 [15] cycle: EAX2-EAX1 = 0074e0d8 * EDX2-EDX1 = 00000000 [16] cycle: EAX2-EAX1 = 0062f19c * EDX2-EDX1 = 00000000 [17] cycle: EAX2-EAX1 = 006404cc * EDX2-EDX1 = 00000000 [18] cycle: EAX2-EAX1 = 009db384 * EDX2-EDX1 = 00000000 [19] cycle: EAX2-EAX1 = 00629824 * EDX2-EDX1 = 00000000 Conclusions for tracing ----------------------- As you can see the EAX2-EAX1 difference is much bigger when program is traced then if it is clearly runned - well it's logical. We will use the fact for coding some examples (code below) now lets check single stepping mode: Some single stepping: -------------------- [00] cycle: EAX2-EAX1 = c387c6c0 * EDX2-EDX1 = 00000001 [01] cycle: EAX2-EAX1 = 43d8444c * EDX2-EDX1 = 00000000 [02] cycle: EAX2-EAX1 = 465f9ffc * EDX2-EDX1 = 00000000 [03] cycle: EAX2-EAX1 = 478f50d8 * EDX2-EDX1 = 00000000 [04] cycle: EAX2-EAX1 = 46068f98 * EDX2-EDX1 = 00000000 [05] cycle: EAX2-EAX1 = 46767aac * EDX2-EDX1 = 00000000 [06] cycle: EAX2-EAX1 = 4f2e79dc * EDX2-EDX1 = 00000001 [07] cycle: EAX2-EAX1 = 4b0fc400 * EDX2-EDX1 = 00000001 [08] cycle: EAX2-EAX1 = 42835c20 * EDX2-EDX1 = 00000001 [09] cycle: EAX2-EAX1 = 47285570 * EDX2-EDX1 = 00000000 [10] cycle: EAX2-EAX1 = 45cb4330 * EDX2-EDX1 = 00000000 [11] cycle: EAX2-EAX1 = 49d9c1b8 * EDX2-EDX1 = 00000000 [12] cycle: EAX2-EAX1 = 47b0c5e0 * EDX2-EDX1 = 00000000 [13] cycle: EAX2-EAX1 = 45ccf9ac * EDX2-EDX1 = 00000000 [14] cycle: EAX2-EAX1 = 3bb0d8b4 * EDX2-EDX1 = 00000000 [15] cycle: EAX2-EAX1 = 406d1abc * EDX2-EDX1 = 00000000 [16] cycle: EAX2-EAX1 = 4b1ab80c * EDX2-EDX1 = 00000001 [17] cycle: EAX2-EAX1 = 4111b198 * EDX2-EDX1 = 00000001 [18] cycle: EAX2-EAX1 = 462c9e94 * EDX2-EDX1 = 00000001 [19] cycle: EAX2-EAX1 = 48844964 * EDX2-EDX1 = 00000000 Conclusions for single stepping ------------------------------- - the EAX2-EAX1 is very high (look trace output to compare) - also notice the facts EDX2-EDX1 is sometimes 1, so this is a very good proof of single stepping player around. Some crazy examples ------------------- Try to play with debugger and with breakpoints on _lopen :) MAX_EAX_TIMING was calculated on the C program output somelines before this code + some extra range. EXAMPLE 1 ---------- ;------------------ SNIP ----------------------------------------- MAX_EAX_TIMING equ 000eeeeeh rdtsc push eax push edx push OF_READ @pushsz "\.\\RANDOM" @callx _lopen rdtsc sub edx,dword ptr [esp] test edx,edx jnz found_single_step ; or very slow processor sub eax,dword ptr [esp+4] cmp eax,MAX_EAX_TIMING jge found_debugger_action exit: push 0 @callx ExitProcess found_single_step: @debug "Single step action was found",0 jmp exit found_debugger_action: @debug "Debugger action was found",0 jmp exit ;------------------ SNIP ----------------------------------------- EXAMPLE 2 --------- And here is the second example, which calculates the clock time of first _lopen execution and then executes next _lopen and calculates the same thing. Then compares both results (including some extra range of 0aaaaaah - just to cover some speciall EAX2-EAX1 cases in clear mode - look tables above). If the final difference is larger then 0 we got some bad guy on us. ;------------------ SNIP ----------------------------------------- rdtsc push eax push edx push OF_READ @pushsz "\.\\RANDOM" @callx _lopen rdtsc sub edx,dword ptr [esp] test edx,edx jnz found_single_step ; or very slow processor sub eax,dword ptr [esp+4] xchg ebx,eax rdtsc push eax push OF_READ @pushsz "\.\\RANDOM" @callx _lopen ; ---> break on this call rdtsc sub eax,dword ptr [esp] add eax,0aaaaah ; some extra value sub ebx,eax cmp ebx,0 jle exit jmp found_debugger_action exit: push 0 @callx ExitProcess found_single_step: @debug "Single step action was found",0 jmp exit found_debugger_action: @debug "Debugger action was found",0 jmp exit ;------------------ SNIP -----------------------------------------