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