栈溢出笔记1.8 字符串问题

本文探讨了在编写Shellcode时如何通过Hash算法减少字符串占用的空间。介绍了ROR13Hash算法的应用,该算法可以将函数名转换为Hash值,从而在查找函数时提高代码的紧凑性和隐蔽性。

在前面编写Shellcode的过程中,我们用到的字符串要么直接使用DB定义,要么通过PUSH直接压入栈上(相当于定义局部变量),这样的话,如果一个Shellcode中的字符串多一点的话,仅字符串就要占用不少空间。而且,在1.7节中,我们在kernel32.dll的导出表中查找函数名称的时候,有一个字符串比较的过程,要将我们指定的函数名(如LoadLibraryA)与导出表中的函数名比较,这样一方面我们要保存需要查找的函数名,另一方面比较字符串占用了比较多的代码。还有,字符串问题会有前面的NULL字节的问题。

我们能不能把字符串去掉一些呢?常用的方法是Hash,即将一个字符串算出一个Hash值,然后进行比较,这样,我们就不需要保存待查找的函数名,而只保存一个Hash值,同样,比较的代码也会少一些。当然,效率可能会降低,因为每个字符串都需要算完整的Hash值,而直接比较的话,差别比较大的字符串马上就可以判断出来。但在Shellcode中,效率一般不是优先考虑的,优先考虑的是代码体积,如果能用十几个字节能解决问题,就不要用几十个字节,因为,有漏洞的程序留给我们可用的空间是有限的,每一个字节都是宝贵的。而且,代码量越大,隐蔽性越差,被人发现的概率越大。

那么需要找一个字符串Hash算法,这个算法至少要保证在一个dll中,所有的函数名算出来的Hash值是唯一的。最常用的是ROR13 Hash算法,这也是Metasploit中使用的算法,它的思想如下:

/*********************************************************/
acc := 0;
for c in input_string do    // 对于字符串中的每个字符
acc := ROR(acc, 13)   // 先将acc循环右移13位
acc := acc+c         // 与当前字符求和
end
/*********************************************************/

算法来自:https://www.fireeye.com/blog/threat-research/2012/11/precalculated-string-hashes-reverse-engineering-shellcode.html

这里只记录这样一个方法,具体实现与上一节大同小异,不过是换掉函数compare_string而已,就不写代码了。
实现请看这篇文章:http://www.tophertimzen.com/blog/shellcodeTechniquesCPP/

<think>嗯,用户现在遇到了在使用insmod传递字符串参数时出现栈溢出问题。首先,我需要回顾之前和用户的对话,看看之前讨论的内容。之前用户询问了如何通过insmod传递字符串给内核模块,我给出了示例代码,包括使用module_param声明字符指针参数,以及如何加载模块并传递参数。用户现在的问题是关于传递字符串时出现栈溢出的解决方案。 首先,我需要确认用户的问题具体是什么情况导致的栈溢出栈溢出通常发生在局部变量分配的空间超过栈的大小,或者递归调用过深。但在内核模块中,用户传递字符串参数时,如果模块代码中错误地将字符串参数存储在栈分配的缓冲区中,而没有进行长度检查,就可能发生栈溢出。 回想之前的示例代码,用户的内核模块中使用的是静态字符指针(char *mystring = "default";),然后通过module_param将其声明为模块参数。这种情况下,字符串参数在内核中是通过动态分配内存来存储的,不会直接导致栈溢出。所以可能用户在实际编码时,可能没有正确使用module_param,而是自己声明了一个栈上的缓冲区,比如在init函数中定义了一个局部数组,并尝试将参数复制进去,而没有检查长度,导致溢出。 比如,如果用户在内核模块的初始化函数中这样写: static int __init mymodule_init(void) { char local_buffer[64]; strcpy(local_buffer, mystring); ... } 这里如果mystring的长度超过64字节,就会导致栈溢出。这种情况下,用户需要避免在栈上使用固定大小的缓冲区,或者确保字符串长度不会超过缓冲区大小。 另外,可能用户在模块参数的处理上,没有正确使用内核提供的API,比如使用charp类型,而是自己处理字符串的拷贝,导致问题。比如,如果用户自己解析参数,并尝试将其拷贝到栈上的数组,而没有检查长度,就会溢出。 所以,解决方案应该包括以下几点: 1. 使用内核提供的module_param宏正确声明字符串参数,让内核自动管理内存,避免手动拷贝到栈缓冲区。 2. 如果必须使用栈缓冲区,确保进行长度检查,使用strncpy等安全函数,并限制拷贝的长度。 3. 在内核模块中避免在栈上分配大缓冲区,改用动态内存分配(如kmalloc)或者静态分配。 另外,用户可能需要检查内核编译时的配置,比如栈的大小,但通常内核栈较小(比如8KB),所以在内核编程中需要特别小心栈的使用。 根据用户提供的引用,之前的回答已经提到了module_param的使用,以及内核会自动分配内存。可能用户在实现时没有正确遵循这些步骤,或者有其他代码导致栈溢出。比如,可能在模块初始化函数中定义了一个大数组,或者在处理字符串时进行了不安全的操作。 因此,在回答中需要强调正确使用module_param,避免手动处理字符串拷贝到栈空间,检查模块代码中的缓冲区使用情况,使用安全函数,以及必要时调整代码结构避免栈溢出。 另外,需要提醒用户在内核编程中栈空间有限,避免在栈上分配大内存,尽量使用动态分配,或者将缓冲区声明为静态或全局变量,这样它们不会占用栈空间。 总结来说,用户的问题可能源于在模块代码中错误地将传入的字符串参数拷贝到栈上的固定大小缓冲区,导致溢出。解决方案是正确使用内核提供的参数处理机制,避免栈上的不安全操作,并进行必要的长度检查和内存分配。</think>### 解决`insmod`传递字符串导致栈溢出的分步方案 --- #### 1. **问题根源定位** 当`insmod`传递字符串参数导致栈溢出时,通常由以下两种场景引发: - **栈缓冲区溢出**:在模块初始化函数中使用局部数组接收字符串参数(例如`char buf[128]`),未进行长度检查直接拷贝 - **递归调用链过长**:模块初始化代码中存在非预期的深度函数调用(较罕见) --- #### 2. **内核安全编程实践** ##### 2.1 正确使用`module_param`机制 **错误示例**(引发栈溢出): ```c static int __init mymodule_init(void) { char local_buf[64]; // 危险:栈空间分配 strcpy(local_buf, mystring); // 无长度检查 printk(KERN_INFO "String: %s\n", local_buf); return 0; } ``` **修正方案**: ```c // 正确声明模块参数 static char *mystring = "default"; module_param(mystring, charp, 0644); // charp类型由内核自动管理内存[^1] static int __init mymodule_init(void) { printk(KERN_INFO "Safe string: %s\n", mystring); // 直接使用内核分配的内存 return 0; } ``` ##### 2.2 强制长度限制(需要自定义参数时) ```c #define MAX_STR_LEN 256 static char safe_buf[MAX_STR_LEN]; module_param_string(custom_str, safe_buf, MAX_STR_LEN, 0); // 使用内核提供的安全接口[^2] ``` --- #### 3. **调试与验证** ##### 3.1 内核栈大小检查 ```bash # 查看当前内核栈配置 $ ulimit -s 8192 # 典型值为8KB(x86架构) ``` ##### 3.2 反汇编验证 通过`objdump -dS your_module.ko`检查`mymodule_init`函数: - 确认无大尺寸局部数组(如`char buf[4096]`) - 检查`strcpy`等危险函数调用是否被替换为`strncpy` --- #### 4. **高级防护方案** ##### 4.1 编译器加固 在Makefile中添加栈保护选项: ```makefile EXTRA_CFLAGS += -fstack-protector-strong ``` ##### 4.2 运行时检测 使用`CONFIG_VMAP_STACK`配置选项(需要内核支持): ```bash # 在.config文件中启用 CONFIG_VMAP_STACK=y # 使用虚拟映射栈检测溢出[^3] ``` --- #### 5. **典型错误对照表** | 错误模式 | 修正方案 | 原理说明 | |---------------------------|---------------------------------------|------------------------------| | 局部缓冲区直接接收参数 | 改用`module_param_string` | 避免栈空间分配 | | 未过滤用户输入 | 添加`strnlen`长度检查 | 防御性编程 | | 多层嵌套函数调用 | 拆分函数逻辑,减少调用深度 | 控制栈帧消耗 | | 使用`strcpy`等危险函数 | 替换为`strncpy`+显式终止符 | 防止缓冲区越界 | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值