hasherezade / tiny_tracer

A Pin Tool for tracing API calls etc
1.25k stars 138 forks source link

Antidebug detection implementation #32

Closed cecio closed 1 year ago

cecio commented 1 year ago

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:

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.

hasherezade commented 1 year ago

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 1 year ago

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!

hasherezade commented 1 year ago

@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 1 year ago

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 1 year ago

hey.

I did some changes.

image

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

hasherezade commented 1 year ago

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:

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 1 year ago

Thanks for the comments!

Let me go through them:

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.

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 1 year ago

Quick update: I did some additional mods

hasherezade commented 1 year ago

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 1 year ago

@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!

hasherezade commented 1 year ago

@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 1 year ago

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