Search code examples
cshellcode

Calculate code size (number of opcode bytes) between two labels in C


Based upon this MSVC Windows disassembly, is there a method to determine the number of bytes between the labels jmp_code_start and jmp_code_finish? Manually counting the opcode bytes B8 D0 10 36 00 2D C3 00 00 00 89 45 FC FF 55 FC shows there are 16 bytes but that's tedious. Perhaps there's a better technique?

int main(int argc, char* argv[])
{
003610D0 66 90                xchg        ax,ax  
003610D2 55                   push        ebp  
003610D3 8B EC                mov         ebp,esp  
003610D5 51                   push        ecx  
jmp_code_start:;

    char (*jmp)() = (char*)&main - sizeof(shellcode);
003610D6 B8 D0 10 36 00       mov         eax,offset main (03610D0h)  
003610DB 2D C3 00 00 00       sub         eax,0C3h  
003610E0 89 45 FC             mov         dword ptr [jmp],eax  
    jmp();
003610E3 FF 55 FC             call        dword ptr [jmp]  

jmp_code_finish:;
    
    return 0;
003610E6 33 C0                xor         eax,eax  
}

I tried this code but it gave errors:

int jmp_size = jmp_code_finish - jmp_code_start;

printf("jmp_size %d", jmp_size);

error

The original source code is

#include <windows.h>
#include <stdio.h>


// 195 bytes small Windows/x86 null-free WinExec Calc.exe shellcode.

char shellcode[195] = 
"\x89\xe5\x83\xec\x20\x31\xdb\x64\x8b\x5b\x30\x8b\x5b\x0c\x8b\x5b"
"\x1c\x8b\x1b\x8b\x1b\x8b\x43\x08\x89\x45\xfc\x8b\x58\x3c\x01\xc3"
"\x8b\x5b\x78\x01\xc3\x8b\x7b\x20\x01\xc7\x89\x7d\xf8\x8b\x4b\x24"
"\x01\xc1\x89\x4d\xf4\x8b\x53\x1c\x01\xc2\x89\x55\xf0\x8b\x53\x14"
"\x89\x55\xec\xeb\x32\x31\xc0\x8b\x55\xec\x8b\x7d\xf8\x8b\x75\x18"
"\x31\xc9\xfc\x8b\x3c\x87\x03\x7d\xfc\x66\x83\xc1\x08\xf3\xa6\x74"
"\x05\x40\x39\xd0\x72\xe4\x8b\x4d\xf4\x8b\x55\xf0\x66\x8b\x04\x41"
"\x8b\x04\x82\x03\x45\xfc\xc3\xba\x78\x78\x65\x63\xc1\xea\x08\x52"
"\x68\x57\x69\x6e\x45\x89\x65\x18\xe8\xb8\xff\xff\xff\x31\xc9\x51"
"\x68\x2e\x65\x78\x65\x68\x63\x61\x6c\x63\x89\xe3\x41\x51\x53\xff"
"\xd0\x31\xc9\xb9\x01\x65\x73\x73\xc1\xe9\x08\x51\x68\x50\x72\x6f"
"\x63\x68\x45\x78\x69\x74\x89\x65\x18\xe8\x87\xff\xff\xff\x31\xd2"
"\x52\xff\xd0";


int main(int argc, char* argv[])
{
jmp_code_start:;

    char (*jmp)() = (char*)&main - sizeof(shellcode);
    jmp();

jmp_code_finish:;
    
    return 0;
}

Based upon the supplied answer, this code below now works!

#include <windows.h>
#include <stdio.h>


// 195 bytes small Windows/x86 null-free WinExec Calc.exe shellcode.

char shellcode[195] = 
"\x89\xe5\x83\xec\x20\x31\xdb\x64\x8b\x5b\x30\x8b\x5b\x0c\x8b\x5b"
"\x1c\x8b\x1b\x8b\x1b\x8b\x43\x08\x89\x45\xfc\x8b\x58\x3c\x01\xc3"
"\x8b\x5b\x78\x01\xc3\x8b\x7b\x20\x01\xc7\x89\x7d\xf8\x8b\x4b\x24"
"\x01\xc1\x89\x4d\xf4\x8b\x53\x1c\x01\xc2\x89\x55\xf0\x8b\x53\x14"
"\x89\x55\xec\xeb\x32\x31\xc0\x8b\x55\xec\x8b\x7d\xf8\x8b\x75\x18"
"\x31\xc9\xfc\x8b\x3c\x87\x03\x7d\xfc\x66\x83\xc1\x08\xf3\xa6\x74"
"\x05\x40\x39\xd0\x72\xe4\x8b\x4d\xf4\x8b\x55\xf0\x66\x8b\x04\x41"
"\x8b\x04\x82\x03\x45\xfc\xc3\xba\x78\x78\x65\x63\xc1\xea\x08\x52"
"\x68\x57\x69\x6e\x45\x89\x65\x18\xe8\xb8\xff\xff\xff\x31\xc9\x51"
"\x68\x2e\x65\x78\x65\x68\x63\x61\x6c\x63\x89\xe3\x41\x51\x53\xff"
"\xd0\x31\xc9\xb9\x01\x65\x73\x73\xc1\xe9\x08\x51\x68\x50\x72\x6f"
"\x63\x68\x45\x78\x69\x74\x89\x65\x18\xe8\x87\xff\xff\xff\x31\xd2"
"\x52\xff\xd0";

void shellcode_func(void);
void dummy_function(void);

int main(int argc, char* argv[])
{
    size_t jmp_size = (size_t)dummy_function - (size_t)shellcode_func;
    
    return 0;
}

__declspec(naked) void shellcode_func(void) {
    char (*jmp)();
    jmp = (char*)&main - sizeof(shellcode);
    jmp();
}

void dummy_function(void) {}

WARNING

After determining the code size is 16 bytes, using that exact code elsewhere can differ in size i.e., 17 bytes. The compiler chooses which registers to use and directly affects the number of opcode bytes generated.

    char (*jmp)();
    jmp = (char*)&main - sizeof(shellcode);
005E10D5 B9 C0 10 5E 00       mov         ecx,offset main (05E10C0h)  
005E10DA 81 E9 C0 00 00 00    sub         ecx,0C0h  
005E10E0 89 4D FC             mov         dword ptr [jmp],ecx  
    jmp();
005E10E3 FF 55 FC             call        dword ptr [jmp]  




    char (*jmp)();
    jmp = (char*)&main - sizeof(shellcode);
005E11B0 B8 C0 10 5E 00       mov         eax,offset main (05E10C0h)  
005E11B5 2D C0 00 00 00       sub         eax,0C0h  
005E11BA 89 45 FC             mov         dword ptr [jmp],eax  
    jmp();
005E11BD FF 55 FC             call        dword ptr [jmp]  

Solution

  • There is no way to do this in standard C.

    But in practice - you can usually accomplish this with code like:

    __declspec(naked) void shellcode_func(void) {
        char (*jmp)();
        jmp = (char*)&main - sizeof(shellcode);
        jmp();
    }
    
    void dummy_function(void) {}
    
    int main() {
        size_t jmp_size = (size_t)dummy_function - (size_t)shellcode_func;
    }
    

    the __declspec(naked) attribute (available as __attribute__((naked)) in GCC) is necessary for ensuring that the shellcode function is defined without a function prologue or epilogue - thus maintaining its original size in bytes.