http://mirror.sweon.net/madchat/vxdevl/vxmags/vxtasy1/VXTASY%231.20B
__________________________________________________________________________
(__________________________________________________________________________)
{ __ } { __ } | | { __ } { __ }
|\\| |\\| .----O----------------------------O----. |//| |//|
|\\| |\\| | Anti-debugging in Win32 | |//| |//|
|\\| |\\| `--------------------------------------' |//| |//|
{____} {____} {____} {____}
I am almost ashamed to open this subject here, but it has to be done.
I am ashamed not actually about writing it, but I am ashamed of the
anti-virus companies' shame. Because it *IS* a shame not to have after such
a long time something which you could call a real Win32 emulator. And don't
jump on me because it is true... Each and every win32 virus I wrote and you
see in this issue was not discovered at first sight by any AV. After a
little work on them, some smart AVs like AVP and DrWeb started to discover
them... It was only a matter of adding more laywers of encryption and all
was hidden completely. However, even if the fond of the article doesn't
really exist (there is *NO* av that would act like good old TBAV in Dos), we
must start talking about this, because there is not so long until the AVers
will start taking this seriously and programm some real code emulators.
So, I guess you understood that this article will also be about anti
emulation, not only anti-debugging.
First, let us note that the Win32 viruses need the Apis. They search
the Apis using specific searching methods and save their addresses in
different places in the memory. A call to an api, therefore, will look like
this when unassembled:
jmp dword ptr [xxxxxxxx]
And at address [xxxxxxxx] you have the saved address of the api
inside the kernel body.
From the beginning this causes the code to be pretty uncomprehsible
at first sight. A diassembler should locate all calls of the type above and
then retrieve the addresses of the apis, and then scan the kernel, or other
DLL's export section to locate the name of the api. Only after doing so, the
person who disassembles your code could understand what it does and take
action depending on the returned values.
Of course, we are not here to speak about fighting the human enemy.
After all, any virus can be finally unassembled and understood. I will speak
more on the automatical emulation and scanning.
Think of that... The AV is trying to 'see' what is your code going to
do. So, one of the very first methods that comes to mind, and which has a
lot of strength under Win32 would be the debugging. The Win32 systems allows
one process to debug another process. However, this automatically sets a
flag and the escape is quite obvious. All one needs to do is run the
following api like this:
call [ebp+_IsDebuggerPresent]
or eax, eax
jz exit
...
The name of the api is more than obvious. It returns 0 if the process
is not debugged and non-zero if it is debugged. If a non-zero value is
returned, it means that the running process is debugged.
Anyhow, as well as the human, the machine could look up each call
somewhere inside a DLL body, scan it's export table and compare with some
known names. For example, the AV might notice that a call is made to the api
IsDebuggerPresent, and it might override the answer, or fake it. This is why
we need to hide better the calls to the Apis. One idea that I had was to
change them into some sort of redirector, which would simulate an import
table. Something like this, for example:
call isdebuggerpresent
...
isdebuggerpresent:
jmp dword ptr [_isdebuggerpresent]
...
_isdebuggerpresent dd 0
When you retrieve the apis addresses, you put the address of the
IsDebuggerPresent api address in the _isdebuggerpresent place. But, remember
that in viruses we use the delta handle. The jump will not point in the
right direction unless you do a little trick... Assumming that your jumps
are into an array like this:
jmp dword ptr [api1]
jmp dword ptr [api2]
...
,each jmp gets compiled like this:
0E9 xx xx xx xx
So, all you need to do is go down the array and increment each
address with the value of the delta. In this way, the jumps will point the
right data.
Anyhow, the entire IsDebuggerPresent example gets compiled like this:
call xxxxxxxx
xxxxxxxx: jmp [yyyyyyyy]
yyyyyyyy dd aaaaaaaa
So, you see, this looks exactly like a real import table addressing
type. This is a much trickier way of calling the Apis and this could still
fool many AVs.
Another very important thing is to avoid the usual. This is kind of
redundant to say, as any original thing has a lower rate of disclosure, but
I feel like I would at least emphasize it a little.
Let's see what became usual in the Win32 viruses:
1) Checking for 'MZ' and 'PE'
The usual checks are:
cmp word ptr [...], 'ZM'
cmp word ptr [...], 'EP' (dword ptr [...], 00004554)
Please, avoid this! It flags most AV's. This kind of check is used
whenever the virus tries to locate the kernel32 or check for a PE file
validity. Use strange methods like these:
mov ax, word ptr [...]
xor ax, 1234h
cmp ax, 'ZM' xor 1234h
2) Api names list
A list with Api names could be suspicious, if not inside the import
or export section. Guard the api names like this, for example:
_CreateFileA db 'C'+1,'r'+1,'e'+1,'a'+1,'t'+1,'e'+1,\
'F'+1,'i'+1,'l'+1,'e'+1,'A+1
Before searching for api names, be sure to decrease each byte in the
api name to obtain the real name. But on disk the above definition will look
like this:
"DsfbufGjmfB"
...Pretty annoying, huh?
Another trick, but which could be useless on good debuggers could be
the next one. This is a little piece of code which sets to 0 all debugger
registers by generating the Mov DRx, eax instruction and calling it:
lea esi, drs ; point Debug Registers opcodes
mov ecx, 7 ; 7 registers
lea edi, bait ; point the opcode place
;
repp: ;
lodsb ; take the opcode
mov byte ptr [edi], al ; generate instruction
call zapp ; call it!
loop repp ; do it again
jmp finish ;
;
zapp: ;
xor eax, eax ; eax = 0
dw 230fh ; This turns to mov DRx, eax
bait label ;
db 0 ;
ret ;
;
drs db 0c0h, 0c8h, 0d0h, 0d8h, 0e8h, 0f0h, 0f8h ; debug registers opcodes
Yet, another interesting trick is using the SEH handler to make
jumps. The idea is to set a quick set handler and then generate an
instruction that causes a protection fault. The default SEH handler is the
one of the running process. If the running process is trying to step by step
our virus, the temporary SEH frame of the virus will not be actualy
activated, so the protection fault will not make the code fall in the virus,
where it should, but instead the AV will freeze after meeting the exception
error and abort. Here is an example:
lea eax, ContinueCode ; Set up a the
push eax ; SEH handler
push fs:[0] ;
mov fs:[0], esp ;
;
xor eax, eax ; This causes a page write fault
mov [eax], 100h ; error
;
jmp Quit ;
;
;
ContinueCode: ; we come here if the error
mov esp, [esp+8] ; happened
pop fs:[0] ; Restore SEH frame
add esp, 4 ; restore stack
...
Quit: ...-> return to host
So, using the SEH handler we jump to the ContinueCode label. If the
error doesn't occure for some reason, the code quits.
This brings us to hiding another thing: the SEH frame setup. If the
AV understands that your code sets up a SEH frame, it might set up a
breakpoint there and still trap your code. So, choose alternate ways of
setting up the SEH frame. Here are some:
Normal way:
lea eax, ContinueCode ; Set up a the
push eax ; SEH handler
push fs:[0] ;
mov fs:[0], esp ;
Altenate way example:
mov ecx, -100h
push fs
pop es
mov eax, 12345678h
push eax
lea eax, ContinueCode
mov dword ptr [esp-4], eax
mov ebx, es:[100h+ecx]
push ebx
mov eax, [esp]
mov ebx, [eax]
mov es:[100h+ecx], eax
Even if it looks ugly, unoptimized and all, it achives it's goal: it
sets a SEH frame to point to ContinueCode and still, it doesn't look a bit
like the normal way. You could even consider making it polymorphic.
And now, a really neat and cool idea would be the next one. I have to
be honest with you, I didn't even have time to try it for the moment, but at
least if you think about it, it's half done...
The idea goes like this... All win32 viruses keep their data close to
the code area and it is normal to be so. Therefore, the flags on the section
that the code runs in, should have the READ set (to be able to execute) and
WRITE set (to be able to alter the variables' values). What if we would try
to change all that? Here is how:
First of all, define your virus very well, like this:
code section
code_start:
...
code_end
data_start:
...
data_end
end virus
Then think of two delta handles. Whenever you are accessing data from
the data part use the first delta handle, and whenever you need to access a
value from the code part, use the second delta handle (this situation will
occur usualy only when you need the address of some routines, or when
generating a poly decryptor). Note that data that only requires reading can
also be put in the code section.
Then, when your virus infects its victim, it should add two sections
one after the other, like this:
.mycode
(code)
.mydata
(data)
The size of the first section is simple to compute. It's the size of
the section padded to memory alignment. How to calculate the first delta
handle, needed to access the data? Look:
start_of_code:
call get_delta
get_delta:
pop ebp
sub ebp, offset get_delta
add ebp, alignment_difference
To get the alignment difference, simply locate the last section's raw
data start and then substract the before last section's raw data start and
also the size of code:
Alignment_difference = data_raw_start - code_raw_start - code_size
To compute the raw data addresses is too simple and I will not
explain again (check the pe file layout article).
Now, everytime you want to access a data you simply use this:
mov register, [ebp+address]
For the code addressing you calculate another delta handle which you
put, let's say in the ESI register. When you want to use the address of
some procedure you do this:
lea register, [esi+address]
So, what do we have here? We have a virus lying in two sections!! And
most important, the code section doesn't need to have the Write flag set.
You can trust me this will pose a LOT of problems to the AVs...
Well, these would some small ideas on the Win32 guard up against AV
attack, but I am sure that in the future we shall have to have better new
methods as the AV industry will grow more and more.
Hope you enjoyed this, and if the two sections stuff works, please
tell me ;-)
�������������������������
� Lord Julus - 1999 �
��������������������������