Python2.7中使用ctypes.memset设置子串内存失败问题求助
首先,你的核心问题出在直接操作Python不可变字符串的内存以及内存地址计算依赖未公开的内部实现上,咱们一步步拆解:
为什么你的代码没生效还乱改内存?
地址计算不可靠
id(string)拿到的是Python字符串对象(PyStringObject)的起始地址,而不是字符串字符数据的起始地址。你用sys.getsizeof(string) - (len(string)+1)来算头部偏移,这种方式完全依赖Python的内部结构体布局——不同版本、不同平台(32/64位)的PyStringObject结构都可能不一样,偏移量计算自然不准,导致你写到了错误的内存区域。Python字符串内存可能是只读的
Python2.7的字符串是不可变对象,很多小字符串会被interned(字符串驻留),存放在只读内存区域。这也是你调用ctypes.CDLL('libc.so.6').memset时触发段错误的原因——你试图往只读内存里写数据,操作系统直接阻止了。
正确的处理方式:用可变缓冲区操作敏感数据
既然Python字符串不可变且内存访问受限,咱们应该先把字符串复制到可变的内存缓冲区里操作,完成后再清零缓冲区彻底清除敏感数据。示例代码如下:
import ctypes def overwrite_sensitive_substring(original_str): # 创建可变的C风格字符串缓冲区,复制原字符串内容 buff = ctypes.create_string_buffer(original_str) # 定位敏感子串的位置 start_idx = original_str.find('{') end_idx = original_str.find('}') + 1 if start_idx != -1 and end_idx != 0: # 用memset覆盖敏感区域,从start_idx位置开始,写入(end_idx - start_idx)个0 ctypes.memset(ctypes.byref(buff, start_idx), 0, end_idx - start_idx) # 这里可以使用处理后的缓冲区内容,比如打印或业务逻辑处理 print("处理后内容:", buff.value) # 最后清零整个缓冲区,彻底清除敏感数据痕迹 ctypes.memset(buff, 0, len(buff))
额外说明:为什么不能依赖Python内部结构?
如果你好奇之前的地址计算到底错在哪,这里提一下Python2.7的PyStringObject内部结构(注意:这是未公开的实现细节,绝对不要在生产代码里依赖):
typedef struct { PyObject_VAR_HEAD // 可变对象头部,包含引用计数、类型指针等 long ob_shash; // 字符串哈希值 int ob_sstate; // 驻留状态标记 char ob_sval[1]; // 字符数据起始位置(柔性数组) } PyStringObject;
字符数据的起始地址应该是id(string) + sizeof(PyVarObject) + sizeof(long) + sizeof(int),但sizeof的值在32位和64位平台上完全不同,而且Python的编译选项也可能改变结构体布局——就算你算对了地址,内存只读的问题依然存在,所以这种方法从根源上就不具备可行性。
内容的提问来源于stack exchange,提问作者truth_seeker

