Jump to content
Nytro

Anti-debugging with RDTSC

Recommended Posts

Posted

Anti-debugging with RDTSC

-------------------------------------------------

Playing with RDTSC

-------------------------------------------------

by Piotr Bania <bania.piotr @ gmail.com>

[: www.piotrbania.com :]

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 <stdio.h>
#include <conio.h>
#include <windows.h>

#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 -----------------------------------------

Sursa: http://piotrbania.com/all/articles/playing_with_rdtsc.txt

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...