C语言局部字符数组初始化存储差异及栈初始化优化方法问询
关于PIC Shellcode中字符数组初始化的问题解答
1. 为何两种局部数组初始化方式的存储位置截然不同?
这本质是编译器对字符串字面量的处理逻辑差异,结合PIC(位置无关代码)的编译特性导致的:
- 当使用
char winexec[] = "WinExec";时,字符串字面量"WinExec"会被编译器存入程序的只读数据段(.rodata)。函数执行时,生成的代码会从这个全局数据段拷贝字节到栈上的数组。但在shellcode注入场景中,原进程的只读数据段地址在目标进程中完全无效,一旦代码尝试访问这个不存在的地址,就会执行失败。 - 而
char winexec[] = { 'W','i','n','E','x','e','c','\0' };的初始化方式,编译器会直接生成栈帧内的字节赋值指令,把每个字符逐个写入栈上的数组空间。整个字符串完全存储在当前函数的局部栈段中,不依赖任何全局数据段,因此注入到目标进程后能正常访问。
2. 有没有更简洁的栈上初始化C字符数组的方法?
针对PIC shellcode场景,推荐以下几种简洁方案:
- 小端数值赋值法:将字符串转换为对应小端序的整数,直接通过指针赋值,代码最简洁且无全局依赖:
#include <stdint.h> char winexec[8]; *(uint64_t*)winexec = 0x636578654E6E6957; // 对应"WinExec"的小端字节序列 - 复合字面量+memcpy:利用C的复合字面量特性,让编译器在栈上生成临时字符串,再拷贝到目标数组(需确保编译时启用PIC选项,避免字面量进入全局段):
#include <string.h> char winexec[8]; memcpy(winexec, (char[]){"WinExec"}, sizeof(winexec)); - 编译期宏拆解字符串:自定义宏将字符串字面量拆解为字符列表,兼顾可读性和栈初始化特性:
#define STR_STACK_INIT(s) { s[0], s[1], s[2], s[3], s[4], s[5], s[6], '\0' } char winexec[] = STR_STACK_INIT("WinExec"); - 内联汇编栈构造:直接通过汇编指令将字符压栈(注意小端序),适合纯shellcode场景:
char *winexec; __asm__ ( "push 0x00636578\n" "push 0x654E6E69\n" "push 0x57\n" "mov %0, rsp\n" : "=r"(winexec) : : "rsp" );
内容的提问来源于stack exchange,提问作者vengy
相关产品推荐
相关产品推荐

