Hello and welcome.
My name is Paul.
And today I'm going to talk to you a little bit about tracing memory allocations using the x64dbg debugger,
and also explain a little bit about the differences and relationships between three APIs: VirtualAlloc
versus HeapAllocEx versus NtAllocateVirtualMemory.
OK, memory.
This diagram here shows a very good relationship between the three APIs. At the top is the VirtualAlloc
Also known as VirtualAllocEx
and they both refer to the user APIs.
Yes, they both belong to the user APIs.
Ultimately, all of them lead to the kernel API
call NtAllocateVirtualMemory. When you are trying to trace the base address which is being allocated
by these three APIs,
you need to look at different parameters.
So, for example, let's take the first one: VirtualAlloc.
Head over to your MSDN reference for VirtualAlloc, and you will see that
this explanation of the various parameters.
The important thing is the return value for VirtualAlloc. If you scroll down, you will see VirtualAlloc
return's value.
If the function succeeds, the return value is the base address of the allocated region of
memory.
So even if you are using a debugger, you should look for the EAX register. Function calls always return
the values in EAX. So VirtualAlloc returns the base address of the allocated memory region in
the EAX register. The same thing is true for HeapAlloc. You can look it up, look up the HeapAlloc
MSDN reference,
and if you scroll down to the return value, you will see that if the function succeeds, the return
value is the base address of the allocated region.
So again, if you want to look for the base address being allocated via this API, then look in the
EAX register and you will see the base address being allocated.
Now let's get to the last one, the kernel API NtAllocateVirtualMemory. This one is different.
This one does not return the base address in the EAX register.
Instead, it returns it in the second parameter of this API. So the second parameter is explained here.
BaseAddress: a pointer to a variable that will receive the base address of the allocated region
of pages.
And what does this API return?
If you scroll down and look at the return, NtAllocateVirtualMemory returns STATUS SUCCESS
on error, Status below it
If you look at the NTSTATUS value, STATUS_SUCCESS means all zeros.
That is the meaning of STATUS_SUCCESS.
So now enough of theory.
Let's open up our x64dbg and try to look at some practical applications,
how we can make use of this knowledge.
So when you're trying to do this, obviously use your virtual machine.
So in this example, here this is a malware sample .bin, and I'm going to analyze this memory allocation
by this malware.
So I'm going to run my x64dbg as administrator.
And then the settings I'm using: December 22, 2021 version of x64dbg, and I'm running
Windows 10 virtual machine. My options are as follows.
Under Options > Preferences, I set only Entry Breakpoint checked.
So now let's open our malware sample. This sample has .bin extension.
You select All Files.
Click on the .bin, open, and now we hit our breakpoint.
So now if you want to put a breakpoint on the APIs, you use the breakpoint BP command.
You can set a breakpoint out here.
So I happen to know that this malware has anti-debugger capability, so we have to defeat the
anti-debugger. To do that,
you must first put a breakpoint on IsDebuggerPresent. IsDebuggerPresent.
So type IsDebuggerPresent.
Hit Enter and you will see the breakpoint set.
If you go back to the Breakpoints tab, you can see that the breakpoint has been set to IsDebuggerPresent.
All right, so now how do we defeat the anti-debugger? Just run.
Click on Run.
And now it has hit the breakpoint.
The API IsDebuggerPresent.
So now we are going to run to user code.
Click on this so that we will
let it execute.
We are now in the system space.
As you can see, kernelbase.dll is where this API is found.
So we want to execute this until it goes back to the user
mode, the user space.
So we hit this button here: Run to User Code.
So it has already executed IsDebuggerPresent, and now we'll come back to the user space.
As you can see up here, sample.bin, we are in user space now.
If you step back one step, you can see that you just came back from this function, this function
call, and the result of this function
call is in the EAX register, and it shows one, which means that it has already detected that a debugger
is present, which is our debugger.
So how do we defeat this anti-debugger?
We can just right-click on this and then Modify Value and set it to zero here. OK?
So yes, we are now modifying the result of this API call to force it by setting it to zero.
Setting it to zero means false. False means debugger is not detected, and you can see now the next test here is to test
whether it equals zero or not.
So if you step over now, you will see that the test has failed.
That means it is
zero.
So that means debugger is not detected,
and therefore this jump, which means jump if not zero, will not occur.
It will go straight.
We have successfully defeated the anti-debugger detection by this malware. And now the next thing
we want to do is to detect the memory allocation.
So to do that, you come back to the command here and put a breakpoint: VirtualAlloc.
Then hit Enter, and you see it says Breakpoint set. So to confirm that, go back to the Breakpoints
tab and you will see the VirtualAlloc breakpoint has been set. Now go back to the CPU tab, click Run until it
hits the VirtualAlloc breakpoint.
So click on Run.
And now it is at VirtualAlloc.
So we step over it
until we come to this function call: NtAllocateVirtualMemory.
And then if you look over here, the second parameter,
it is still
referring to this.
The second parameter is the base address where you will find the allocated region of memory.
So the second parameter is coming from here.
So the parameters to a function call are pushed in reverse order from bottom to the top.
So pushed from bottom to the top.
So all of this eventually will be residing on the stack in readiness for this function call.
So that's why this is the second parameter we should be watching out for.
You can right-click on the second parameter and Follow in Dump.
And this will be the address of your allocated region of memory after this function runs.
So let's step over this to run the function.
And now it has run this function.
And if you look at the return value, it is zero.
So the return value, according to the MSDN reference, tells you
if you scroll down and look at the return value: if the function succeeds, it returns STATUS_SUCCESS.
STATUS_SUCCESS.
And as you have seen earlier, STATUS_SUCCESS means 0x0.
So if you see zero here, it means that this function call was successful,
and this is the address which has been allocated.
This is in the little-endian format: 0x00
1 2 E 0 0 0 0 in little-endian order. So you can actually
go to now
this region of memory.
0 1 2 E 0 0 0 0
Go to Memory Map: 0 1 2
E 0 0 0 0. And if you scroll to the right, you see it says Private, meaning that it has been allocated
using VirtualAlloc functions, and the memory protection is ERW, which means executable, readable,
and writable.
So this confirms that this memory has been successfully allocated.
So this is how you can use the knowledge of these API calls to trace memory allocations.
And in this video, I explained to you the three types, and we are looking at all of them now.
So thank you for watching.
I hope this helps you guys.
Bye for now.