用以下步骤编译出纯二进制代码段 (参数含义可以上 explainshell 查看)

gcc -w -m32 -c -O -fno-ident a.c
objcopy -O binary -j .text a.o a

如果有需要动态确定的数据(如外部函数地址、字符串),用 objdump 找到对应的 0 0 0 0 串,记下偏移

举例:

int f(int a, int b) {
    char *s = malloc(16);
    sprintf(s, "%d + %d = %d", a, b, a + b);
    return (int)s;
}
f:
   0:   57                      push   edi
   1:   56                      push   esi
   2:   53                      push   ebx
   3:   83 ec 20                sub    esp,0x20
   6:   8b 74 24 30             mov    esi,DWORD PTR [esp+0x30]
   a:   8b 7c 24 34             mov    edi,DWORD PTR [esp+0x34]
   e:   c7 04 24 10 00 00 00    mov    DWORD PTR [esp],0x10
  15:   e8 00 00 00 00          call   1a <_f+0x1a>
  1a:   89 c3                   mov    ebx,eax
  1c:   8d 04 3e                lea    eax,[esi+edi*1]
  1f:   89 44 24 10             mov    DWORD PTR [esp+0x10],eax
  23:   89 7c 24 0c             mov    DWORD PTR [esp+0xc],edi
  27:   89 74 24 08             mov    DWORD PTR [esp+0x8],esi
  2b:   c7 44 24 04 00 00 00    mov    DWORD PTR [esp+0x4],0x0
  32:   00
  33:   89 1c 24                mov    DWORD PTR [esp],ebx
  36:   e8 00 00 00 00          call   3b <_f+0x3b>
  3b:   89 d8                   mov    eax,ebx
  3d:   83 c4 20                add    esp,0x20
  40:   5b                      pop    ebx
  41:   5e                      pop    esi
  42:   5f                      pop    edi
  43:   c3                      ret

那么只要在运行时在以下位置填入对应参数即可

0x16malloc 的地址
0x2f"%d + %d = %d" 的地址
0x37sprintf 的地址

那么就有小朋友问了: 上面的步骤能不能全自动执行呢?有

通过 gcc -w -m32 -S -fverbose-asm -masm=intel a.c 可以获得由 gcc 输出的中间汇编码,里面保留了所有名字 (malloc, sprintf),和 objdump 的对应位置进行比较就可以获得这些填空的偏移和名称

.file "a.c"
  .intel_syntax noprefix
  .section .rdata,"dr"
LC0:
  .ascii "%d + %d = %d\0"
  .text
  .globl  _f
  .def  _f; .scl  2;  .type 32; .endef
_f:
  push  edi  #
  push  esi  #
  push  ebx  #
  sub esp, 32  #,
  mov esi, DWORD PTR [esp+48]  # a, a
  mov edi, DWORD PTR [esp+52]  # b, b
  mov DWORD PTR [esp], 16  #,
  call  _malloc  #
  mov ebx, eax   # tmp93,
  lea eax, [esi+edi]   # D.1818,
  mov DWORD PTR [esp+16], eax  #, D.1818
  mov DWORD PTR [esp+12], edi  #, b
  mov DWORD PTR [esp+8], esi   #, a
  mov DWORD PTR [esp+4], OFFSET FLAT:LC0   #,
  mov DWORD PTR [esp], ebx   #, tmp93
  call  _sprintf   #
  mov eax, ebx   #, tmp93
  add esp, 32  #,
  pop ebx  #
  pop esi  #
  pop edi  #
  ret
  .ident  "GCC: (tdm64-1) 5.1.0"
  .def  _malloc;  .scl  2;  .type 32; .endef
  .def  _sprintf; .scl  2;  .type 32; .endef

得知了 (位置, 名称) 后,在 常见 dll 列表 中寻找 (GetProcAddress) 即可

再结合 glotgodbolt (我还没有找到 windows 环境的线上编译器) 后面不用我说了吧