带while循环的PIC程序仅在x86跨进程注入时触发栈溢出
问题原因分析
核心原因
x86 Windows进程的主线程默认带有系统内置的SEH(结构化异常处理)链,当栈空间不足时,系统会自动捕获EXCEPTION_STACK_OVERFLOW异常,触发栈扩展(提交更多预留的栈内存),因此本地执行时80k数据的栈操作不会崩溃。但远程注入的PIC代码运行时:
- 若使用
QueueAPCThread注入,代码会在目标进程的现有线程(如刚创建的挂起主线程)中执行,该线程的SEH链未包含栈扩展的处理逻辑,且PIC代码无法依赖CRT的异常处理机制; - 若使用
CreateRemoteThread注入,默认创建的线程栈虽可扩展,但目标进程的栈预留空间可能已被占用部分,剩余空间不足以容纳80k数据的栈操作,直接触发崩溃。
直接诱因
你的代码在栈上分配了用于存储去空格后Base64字符串的大缓冲区,x86栈的初始提交空间通常仅几十KB到1MB,处理80k数据时剩余栈空间被耗尽,触发溢出。
替代实现方案
1. 改用堆内存存储临时数据(推荐)
PIC代码中通过系统API动态分配堆内存,彻底避免占用栈空间:
步骤示例:
- 手动遍历PE导出表或通过
GetModuleHandleA("kernel32.dll")获取kernel32.dll基址(符合PIC无依赖要求); - 通过
GetProcAddress获取VirtualAlloc、VirtualFree的函数地址; - 分配足够的内存(如预留
80*1024*2的冗余空间); - 在堆内存中完成去空格操作,处理完成后释放内存。
简化代码片段(PIC兼容):
// 伪代码,需适配PIC的动态API获取逻辑 char* raw_base64 = ...; // 原始带空格的Base64字符串 size_t raw_len = strlen(raw_base64); // 分配可读写的堆内存 LPVOID heap_buf = VirtualAlloc(NULL, raw_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (!heap_buf) { /* 处理分配失败逻辑 */ } // 去空格处理 char* dst = (char*)heap_buf; char* src = raw_base64; while (*src) { if (*src != ' ') { *dst++ = *src; } src++; } *dst = '\0'; // 使用处理后的字符串... // 释放内存 VirtualFree(heap_buf, 0, MEM_RELEASE);
2. 原地修改字符串,零额外缓冲区开销
如果原始Base64字符串所在内存是可写的(比如注入时已写入目标进程的可写内存区域),可以用双指针法原地去除空格,完全不需要额外缓冲区:
char* str = ...; // 可写的原始Base64字符串 char* dst = str; char* src = str; while (*src) { if (*src != ' ') { *dst++ = *src; } src++; } *dst = '\0';
该方法效率最高,无内存分配开销,前提是原始字符串内存可写且允许修改。
3. 调整远程线程栈大小(仅适用于CreateRemoteThread注入)
如果必须使用栈内存,调用CreateRemoteThread时指定足够大的栈大小,并设置STACK_SIZE_PARAM_IS_A_RESERVATION标志:
HANDLE hThread = CreateRemoteThread( hTargetProcess, NULL, 2 * 1024 * 1024, // 预留2MB栈空间 (LPTHREAD_START_ROUTINE)pPicCode, pPicParam, 0, NULL );
但此方法兼容性差,不同目标进程的栈限制可能不同,不推荐作为首选方案。
内容的提问来源于stack exchange,提问作者Octoberfest7
相关产品推荐
相关产品推荐

