计算 C 中两个标签之间的代码大小(操作码字节数)

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

基于这个 MSVC Windows 反汇编,有没有一种方法可以确定标签之间的字节数 jmp_code_startjmp_code_finish?手动计算操作码字节 B8 D0 10 36 00 2D C3 00 00 00 89 45 FC FF 55 FC 显示有 16 个字节,但这很乏味。也许有更好的技术?

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  
}

我试过这段代码,但出现错误:

int jmp_size = jmp_code_finish - jmp_code_start;

printf("jmp_size %d", jmp_size);

原源码为

#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;
}

根据提供的答案,下面的代码现在可以使用了!

#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) {}

警告

确定代码大小为 16 字节后,在其他地方使用该确切代码的大小可能会有所不同,即 17 字节。编译器选择使用哪些寄存器并直接影响生成的操作码字节数。

    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]  

在标准 C 中无法做到这一点。

但在实践中 - 您通常可以使用如下代码完成此操作:

__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;
}

__declspec(naked) 属性(在 GCC 中可用作 __attribute__((naked)))对于确保 shellcode 函数的定义没有函数序言或尾声是必要的 - 从而保持其原始大小(以字节为单位)。

GCC 和 Clang 有一个扩展来获取标签地址:

(char *)&&jmp_code_finish - (char *)&&jmp_code_start