hasherezade/tiny_tracer

Antidebug detection implementation

cecio opened this issue · 12 comments

cecio commented

Hey @hasherezade

I'm opening this as an Issue even if it's not a real one, but it's more to start a conversation :-).

First of all let me thank you for the great tools you are creating for all of us, they are awesome!

Then, coming to my "Issue". I used TinyTracer for a while and I found it very useful. I had a use case which was not entirely covered, which was the following: sometimes, in order to carry on dynamic analysis on malware samples, I'd like to identify where antidebug tricks are placed, so that I can patch them out and continue with my work.

So, I started to implement this kind of "flagging" by using TinyTracer as a starting point. Right now I'm just at the beginning of this, but I already have something functional. You can have a look to the fork if you want (https://github.com/cecio/tiny_tracer).

What I did so far is to start to implement various techniques (mainly from the well known https://anti-debug.checkpoint.com, but then also from few others) and add a line in the TinyTracer output when one if found ([ANTIDEBUG] is the tag I used for these messages).
I tried to keep my code as much as possible isolated from the main core (files AntiDebug.cpp and AntiDebug.h) and I put it under a specific option of the INI file (because is going for sure to impact performance)

I'm currently focused on two aspect:

  • antidebug API usage
  • memory access monitoring for antidebug tricks

Some sample output looks like this:

[ANTIDEBUG] --> PEB!BeingDebugged accessed at 0x135d
1038;ucrtbase.__acrt_iob_func
1057;ucrtbase.__stdio_common_vfprintf
[ANTIDEBUG] --> PEB!NtGlobalFlag  accessed at 0x1384
1038;ucrtbase.__acrt_iob_func
1057;ucrtbase.__stdio_common_vfprintf
13a8;kernel32.GetCurrentProcess
13b5;kernel32.IsWow64Process
2302;vcruntime140.memset
10c5;ntdll.VerSetConditionMask
10d4;ntdll.VerSetConditionMask
10e3;ntdll.VerSetConditionMask
1105;kernel32.VerifyVersionInfoW
2302;vcruntime140.memset
10c5;ntdll.VerSetConditionMask
10d4;ntdll.VerSetConditionMask
10e3;ntdll.VerSetConditionMask
1105;kernel32.VerifyVersionInfoW
[ANTIDEBUG] --> Heap Flags accessed at 0x13f3 https://anti-debug.checkpoint.com/techniques/debug-flags.html#manual-checks-heap-flags
[ANTIDEBUG] --> Heap Flags accessed at 0x13fc https://anti-debug.checkpoint.com/techniques/debug-flags.html#manual-checks-heap-flags
1038;ucrtbase.__acrt_iob_func
1496;kernel32.GetModuleFileNameA
14bc;kernel32.CreateFileA
[ANTIDEBUG] --> https://anti-debug.checkpoint.com/techniques/object-handles.html#createfile 
CreateFileA:
    Arg[0] = ptr 0x000000f9300ff9c0 -> "C:\Temp\Antidebug2.exe"
    Arg[1] = 0x0000000080000000 = 2147483648
    Arg[2] = 0
    Arg[3] = 0
    Arg[4] = 0x000000f900000003 = 1069446856707
    Arg[5] = 0x00007ff800000000 = 140703128616960
    Arg[6] = 0

1038;ucrtbase.__acrt_iob_func

For the time being, I fully implemented (for both 32 and 64 bit) the "Debug Flags" portion of the Checkpoint site and started to work on the "Object Handles". I'm at the beginning, but it's starting to take shape.

If you find this in some way usfeful, I'm more than happy to do a pull request. Obviously if you think it does not fit, or if you just don't want my crappy code in your repo, I totally understand you ;-) and I can keep this as a separated fork. And if you have any suggestion or advice on how to improve it, it's welcome!

Thanks a lot.

Hi @cecio !
Thank you for your kind words, and I am happy that you enjoy my tools.
I just tested it, and I really like the idea! Actually, I was planning to implement bypasses of common techniques somewhere in the future - but this may be another option. Tagging is also very useful, and I like the way you started implementing it. Keep it up, and I will also contribute if you want.
A small change that just comes into my mind after a quick look, is, that I would like the line start with the offset of where the technique was called. Like:

1635;[ANTIDEBUG] --> PEB!BeingDebugged accessed

This is the convention that I use across the .tag file, and it helps to load those tags into various tools.

debug_tag

Just a small change, but it will increase the readability significantly.
Anyways, I will welcome your pull request! I may just rework few things here and there if you don't mind.

cecio commented

Thanks a lot @hasherezade!

I started to implement the change you suggested, which make totally sense (thanks for the tip).
It's pretty easy to do it in the AntidebugMemoryAccess, but I didn't found a super-clean way to do it in the tracking of calls. This because in the call tracking I usually have the return address (which is the instruction after).

I found a way, by saving the last transition in a global. I don't like very much this, but it works and it' probably the simplest way. What do you think (you can have a look at the last commit on the fork)? Any other idea?

It results in something like this in tag file:

132a;ntdll.NtQuerySystemInformation
132a;[ANTIDEBUG] --> https://anti-debug.checkpoint.com/techniques/debug-flags.html#using-win32-api-checks-ntquerysysteminformation

image

I marked everything I modified in the core with a comment ANTIDEBUG: so that mods will be easily identified.

Let me know what you think. If it's ok, I'll test a bit more than I can make a first pull request. Then you can obviously rework anything you think need to be reworked.
My plan is then continue to implement all the other checks.

Thanks!

@cecio - to be honest, I also don't like the solution with saving the last transition in a global. I worry it may give wrong results if the application is multithreaded.
I think for now it is better to just use return addresses, or give up tagging of those particular cases.

cecio commented

Yeah, I totally agree.
Let me try in this way: I'll use the return address and try to make as clear as possible that it is referring to previous instruction, something like:

132a;ntdll.NtQuerySystemInformation
132f;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [ANTIDEBUG] --> https://anti-debug.checkpoint.com/techniques/debug-flags.html#using-win32-api-checks-ntquerysysteminformation

or

132a;ntdll.NtQuerySystemInformation
132f;[ANTIDEBUG] -->^ Previous call refers to https://anti-debug.checkpoint.com/techniques/debug-flags.html#using-win32-api-checks-ntquerysysteminformation

I'll try this out and check how it looks

cecio commented

hey.

I did some changes.

  • Now the [ANTIDEBUG] comment is placed in the line after, like this:

image

  • I implemented a new check (LoadLibrary and LOAD_DLL_DEBUG_INFO)
  • Since the Antidebug requires specific API to be watched, I moved them out of params.txt and it looks for a antidebug_params.txt, in order to keep it separated from the "normal" activity

If this is ok, I'll make a pull request, then I'll continue to work on the further implementations

Thanks for the updates! I am a quite busy today, so I just took a quick look. I like how the tagging looks now.
I just have some questions:

  • Is there any particular reason to enable: LOG_INDIRECT_CALLS?
  • If Antidebug detection is enabled - is the antidebug_params.txt used instead of the normal params.txt? It shouldn't be that one of them affects the other...
  • I think instread of writing this "Previous call refers to..." we can be specific which call (as it was before)
  • In case if the function was watched for detection of the debugging technique only, its parameters should not be logged - only the "[ANTIDEBUG]" line should be added. But I saw what it currently produces is both, i.e.
314f;kernel32.IsDebuggerPresent
3151;[ANTIDEBUG] -->^ Previous call refers to https://anti-debug.checkpoint.com/techniques/debug-flags.html#using-win32-api-isdebuggerpresent
IsDebuggerPresent:

2320;kernel32.GetStdHandle

While params.txt don't include tracing IsDebuggerPresent parameters. So it should be:

314f;kernel32.IsDebuggerPresent
3151;[ANTIDEBUG] -->^ Previous call refers to https://anti-debug.checkpoint.com/techniques/debug-flags.html#using-win32-api-isdebuggerpresent
2320;kernel32.GetStdHandle

In general I see no point in adding the file antidebug_params.txt while the list of the functions that are being checked for the antidebug techniques is hardcoded anyways: https://github.com/cecio/tiny_tracer/blob/2ecd5d9f31e886a1c4a63f667fbda06f37f45699/AntiDebug.cpp#L125
We already know which functions we will be watching, and which parameters - so, why do we need the user to define this list again in a file?
The params.txt is there because we want to give to the user flexibility which functions' parameters are they going to dump. But in case of antidebug techniques, the list is predefined, and any new check needs to be additionally implemented. So all what user should be able to do is to enable/disable it (as it is done in the .ini file).
Again, I just took a quick look, so maybe I am missing something.

cecio commented

Thanks for the comments!

Let me go through them:

  • Is there any particular reason to enable: LOG_INDIRECT_CALLS?
    No, it was a test. Switched back to original :-)
  • If Antidebug detection is enabled...
    Yes, true, I'll come to this later to explain
  • I think instead of writing this "Previous call refers to..." ...
    Done.
  • In case if the function was watched for detection of the debugging technique only, its parameters should not be logged...
    Good point. I was thinking about this, but I find some value in having this logging enabled, for example
14bc;kernel32.CreateFileA
14c2;[ANTIDEBUG] -->^ kernel32!CreateFile on module https://anti-debug.checkpoint.com/techniques/object-handles.html#createfile
CreateFileA:
	Arg[0] = ptr 0x000000796f18fb50 -> "C:\Temp\Antidebug2.exe"
	Arg[1] = 0x0000000080000000 = 2147483648
	Arg[2] = 0
	Arg[3] = 0
	Arg[4] = 0x0000007900000003 = 519691042819
	Arg[5] = 0
	Arg[6] = 0

This antidebug trick tries to open the executable itself in exclusive mode. Having the parameters displayed make immediately clear what is happening, having the file name and the mode printed just below. Anyway, I was in doubt as well. If you prefer not to having them, I can remove this.

  • In general I see no point in adding the file antidebug_params.txt while the list of the functions that are being checked for the antidebug techniques is hardcoded...

True. You are right saying that the list is in some way already hardcoded, at least for checking the results. But I need to hardcode also the list of watched functions (because I need to check the parameters). I thought to hardcode this in a separate file (that's why I separated it from the params.txt ) instead of doing this into the code by calling manually the MonitorFunctionArgs somewhere. But you are right, I can get rid of the antidebug_params.txt and do this entirely in the code. And the more i think about it, the more i think that getting rid of the file is a better solution.

When you have time, let me know what you think about the last two points and I'll modify the code accordingly.
In the meantime thanks a lot! :-)

cecio commented

Quick update: I did some additional mods

  • removed the antidebug_params.txt file, now this is hardcoded as you suggested
  • changed the ANTIDEBUG option to int so now you can specify a level, in order to disable more invasive checks
  • added a new detection, now the "Object Handles" section of the checkpoint site is complete

thank you @cecio ! I checked it and I like it, feel free to send a pull request :)
I may rework some details later.

cecio commented

@hasherezade great, I'll do it.
Do you prefer a squashed merge or should I do a "standard" pull with all my commit history?
Thanks!

@cecio - squashed is better.
one more thing - can you apply this patch before?
https://gist.github.com/hasherezade/a8db9c849f529c611b7b0b07fd70a57f
It is for .vcxproj files - ensuring their backward compatibility.

cecio commented

thanks @hasherezade! I'm closing this after the merge.