你的隐私数据真的安全吗之memset()使用分析
我们在实际编程中,需要保存许多私有数据,例如:密码、密钥等等。所以,我们需要经常在使用完这些私有数据后,清除内存使用踪迹,以防止被潜在的入侵者获得这些数据。这篇文章中,我们讨论使用memset()函数来清除私有数据是,可能发生的一系列问题。
1.在stack上分配的隐私数据
首先,我们给出一个代码片段示例,关于如何处理栈上分配变量:
#include <string>
#include <functional>
#include <oistream>
// 隐私数据类型
struct PrivateData
{
size_t m_hash;
char m_pswd[100];
};
// 操作在password上的函数
void doSmth(PrivateData& data)
{
std::string s(data.m_pswd);
std::hash<std::string> hash_fn;
data.m_hash = hash_fn(s);
}
// 输入和处理password的函数
int funcPswd()
{
PrivateData data;
std::cin >> data.m_pswd;
doSmth(data);
memset(&data, 0, sizeof(PrivateData));
return 1;
}
int main()
{
funcPswd();
return 0;
}
上面示例完全是一个虚假例子。如果我们使用编译器(这里以Visual Studio 2015)编译一个调试版本的代码,那么这个代码可以很完善的运行,包括\(memset()\)函数的操作,也就是隐私数据用完之后,会得到清除。
但是让我们直接编译成一个运行版本,并且反编译出来结果如下:
......
doSmth(data);
000000013f3072BF lea rcx, [data]
000000013F3072C3 call doSmth (013F30153Ch)
memset(&data, 0, sizeof(PrivateData));
000000013F3072C8 mov r8d, 70h
000000013F3072CE xor edx, edx
000000013F3072D0 lea rcx, [data]
000000013F3072D4 call memset (013F301352h)
return 1;
000000013F3072D9 mov eax, 1
......
上面反编译代码可见当我们调用\(memset()\)函数,这就在使用隐私数据后清除。
我们进一步编译一个优化版本的发布代码,并且反编译之后,如下:
......
000000013F7A1035 call
std::operator>><><char> > (013F7A18B0h)
000000013F7A103A lea rcx,[rsp+20h]
000000013F7A103F call doSmth (013F7A1170h)
return 0;
000000013F7A1044 xor eax,eax
......
可知,所有关系到函数\(memset()\)代码都被删除了。从编译器优化角度来看,不再使用的数据没必要被清除掉,这对于编译器来说是合法的。从语言角度来看,函数内使用后的隐私数据不会被其他函数调用,所以不用\(memset()\)进行清除也不会影响程序的操作。但是,从安全角度来看,我们的隐私数据没有被清除是非常危险的。
2.在heap上分配的隐私数据
现在,让我们进一步研究,假设我们在堆上使用\(malloc\)函数或者\(new\)操作符分配隐私数据,下面是使用\(malloc\)函数的代码:
#include <string>
#include <functional>
#include <iostream>
// 隐私数据类型
struct PrivateData
{
size_t m_hash;
char m_pswd[100];
};
// 操作在password上的函数
void doSmth(PrivateData& data)
{
std::string s(data.m_pswd);
std::hash<std::string> hash_fn;
data.m_hash = hash_fn(s);
}
// 输入和处理password的函数
int funcPswd()
{
PrivateData data = (PrivateData*)malloc(size0f(PrivateData));
std::cin >> data.m_pswd;
doSmth(data);
memset(&data, 0, sizeof(PrivateData));
free(data);
return 1;
}
int main()
{
funcPswd();
return 0;
}
对于上述代码,我们使用Visual Studio 2015编译一个发行版本,然后反编译出结果如下:
......
000000013FBB1021 mov rcx,
qword ptr [__imp_std::cin (013FBB30D8h)]
000000013FBB1028 mov rbx,rax
000000013FBB102B lea rdx,[rax+8]
000000013FBB102F call
std::operator>><><char> > (013FBB18B0h)
000000013FBB1034 mov rcx,rbx
000000013FBB1037 call doSmth (013FBB1170h)
000000013FBB103C xor edx,edx
000000013FBB103E mov rcx,rbx
000000013FBB1041 lea r8d,[rdx+70h]
000000013FBB1045 call memset (013FBB2A2Eh)
000000013FBB104A mov rcx,rbx
000000013FBB104D call qword ptr [__imp_free (013FBB3170h)]
return 0;
000000013FBB1053 xor eax,eax
......
可见Visual Studio的编译器没有优化掉相关的\(memset()\)函数代码,我们进一步使用5.2.1版本的gcc和3.7.0版本的clang编译看看结果。
这里需要提出,我们在gcc和clang版本的代码中添加了一些额外的代码,也就是读取被清除后的隐私数据地址上,通过读取被清除的指针,虽然这样的操作在实际编程中是不合理的,但是我们这边为了方便展示,代码如下:
....
#include "string.h"
....
size_t len = strlen(data->m_pswd);
for (int i = 0; i < len;="" ++i)="" printf("%c",="" data-="">m_pswd[i]);
printf("| %zu \n", data->m_hash);
memset(data, 0, sizeof(PrivateData));
free(data);
for (int i = 0; i < len;="" ++i)="" printf("%c",="" data-="">m_pswd[i]);
printf("| %zu \n", data->m_hash);
....
现在,这里给出使用gcc编译器反汇编出来的代码片段:
movq (%r12), %rsi
movl $.LC2, %edi
xorl %eax, %eax
call printf
movq %r12, %rdi
call free
可见,\(printf()\)函数后面直接跟着\(free()\)函数,\(memset()\)函数直接被优化掉。这时,如果我们运行恶意代码,读取隐私数据地址上的信息,依然可以读取到相关数据。
现在让我们查看clang编译器:
movq (%r14), %rsi
movl $.L.str.1, %edi
xorl %eax, %eax
callq printf
movq %r14, %rdi
callq free
同样,\(memset()\)函数直接被优化掉,这样也会导致隐私数据泄露。
通过上述的一系列实验可知,\(memset()\)函数直接被优化掉,不论是栈上数据还是堆上数据。最后,我们进一步探讨使用\(new\)操作的情况,调整代码如下:
#include <string>
#include <functional>
#include <iostream>
#include "string.h"
struct PrivateData
{
size_t m_hash;
char m_pswd[100];
};
void doSmth(PrivateData& data)
{
std::string s(data.m_pswd);
std::hash<std::string> hash_fn;
data.m_hash = hash_fn(s);
}
int funcPswd()
{
PrivateData* data = new PrivateData();
std::cin >> data->m_pswd;
doSmth(*data);
memset(data, 0, sizeof(PrivateData));
delete data;
return 1;
}
int main()
{
funcPswd();
return 0;
}
使用Visual Studio编译后反编译的代码如下:
000000013FEB1044 call doSmth (013FEB1180h)
000000013FEB1049 xor edx,edx
000000013FEB104B mov rcx,rbx
000000013FEB104E lea r8d,[rdx+70h]
000000013FEB1052 call memset (013FEB2A3Eh)
000000013FEB1057 mov edx,70h
000000013FEB105C mov rcx,rbx
000000013FEB105F call operator delete (013FEB1BA8h)
return 0;
000000013FEB1064 xor eax,eax
使用gcc编译后反编译的代码如下:
call printf
movq %r13, %rdi
movq %rbp, %rcx
xorl %eax, %eax
andq $-8, %rdi
movq $0, 0(%rbp)
movq $0, 104(%rbp)
subq %rdi, %rcx
addl $112, %ecx
shrl $3, %ecx
rep stosq
movq %rbp, %rdi
call _ZdlPv
上面,Visual Studio和gcc编译后的代码显示,之前的隐私数据都得到了清除,最后使用clang编译如下:
movq (%r14), %rsi
movl $.L.str.1, %edi
xorl %eax, %eax
callq printf
movq %r14, %rdi
callq _ZdlPv
可知,clang对我们代码做了优化,隐私数据依然存在。
所以,我们应该如何更好的清除掉我们的隐私数据,从而保存我们的安全?
我们应该使用特殊的内存清除函数,它指定编译器不回删除这些函数。例如在Visual Studio中,可以使用\(RtlSecureZeroMemory\)函数。从C++11标准开始,我们可以使用\(memset\_s\)函数。此外,我们也可以实现我们自己需要的安全版本的内存清楚函数,示例1代码如下:
errno_t memset_s(void *v, rsize_t smax, int c, rsize_t n) {
if (v == NULL) return EINVAL;
if (smax > RSIZE_MAX) return EINVAL;
if (n > smax) return EINVAL;
volatile unsigned char *p = v;
while (smax-- && n--) {
*p++ = c;
}
return 0;
}
示例2代码:
void secure_zero(void *s, size_t n)
{
volatile char *p = s;
while (n--) *p++ = 0;
}
你的隐私数据真的安全吗之memset()使用分析的更多相关文章
- 从ofo牵手理财平台看,用户隐私数据的使用有底线吗?
智慧生活的到来既是社会变迁的拐点,又不可避免地带来一种挥之不去的焦虑.这种焦虑的由来,是因个人隐私数据在智慧生活下变成一种"黑暗财富".随着相关数据挖掘.收集.分析技术的成熟,人们 ...
- Springboot 配置文件、隐私数据脱敏的最佳实践(原理+源码)
大家好!我是小富- 这几天公司在排查内部数据账号泄漏,原因是发现某些实习生小可爱居然连带着账号.密码将源码私传到GitHub上,导致核心数据外漏,孩子还是没挨过社会毒打,这种事的后果可大可小. 说起这 ...
- iOS10-配置获取隐私数据权限声明
iOS10中,苹果加强了对用户隐私数据的保护,在访问以下数据的时候都需要在info.list重配置privacy,进行声明,否则程序无法正常运行. Contacts, Calendar, Remind ...
- Fabric v2.0中的隐私数据
文章来源于https://hyperledger-fabric.readthedocs.io/en/release-2.0/ 私有数据集在v1.4中提出,一直使用的是隐私数据集方式,即建立一个隐私数据 ...
- Hadoop基础-HDFS数据清理过程之校验过程代码分析
Hadoop基础-HDFS数据清理过程之校验过程代码分析 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 想称为一名高级大数据开发工程师,不但需要了解hadoop内部的运行机制,还需 ...
- Wireshark数据抓包教程之认识捕获分析数据包
Wireshark数据抓包教程之认识捕获分析数据包 认识Wireshark捕获数据包 当我们对Wireshark主窗口各部分作用了解了,学会捕获数据了,接下来就该去认识这些捕获的数据包了.Wiresh ...
- 65、Spark Streaming:数据接收原理剖析与源码分析
一.数据接收原理 二.源码分析 入口包org.apache.spark.streaming.receiver下ReceiverSupervisorImpl类的onStart()方法 ### overr ...
- 兼容iOS 10:配置获取隐私数据权限声明
原文链接 iOS 10的一大变化是更强的隐私数据保护.在文档中是这么描述的: You must statically declare your app’s intended use of protec ...
- iOS 用户的隐私数据-privacy-sensitive data
1 Xcode 报错:This app has crashed because it attempted to access privacy-sensitive data without a usa ...
随机推荐
- 简单的xml数据库设计
我的需求 有时候系统需要存储一些简单的关系型属性,不考虑并发,不考虑性能(一次操作在几ms),数据库没有其他依赖引用,拿过来就可以用 为什么选xml作为数据库? 可读性好 实体的对应关系不严格 , 二 ...
- [leetcode]103. Binary Tree Zigzag Level Order Traversal二叉树Z字形层序遍历
相对于102题,稍微改变下方法就行 迭代方法: 在102题的基础上,加上一个变量来判断是不是需要反转 反转的话,当前list在for循环结束后用collection的反转方法就可以实现反转 递归方法: ...
- 二本非科班,秋招,实习,面试,offer之路
不知不觉已经工作一年多的,我是2019年7月毕业的,但是如果算上实习就工作差不多两年了的吧. 最近不是刚刚过了圣诞节吗?然后又准备到元旦了,迎来2021年!在微信公众号上看到小部分公众号在总结2020 ...
- java中string、stringBuild、stringBuffer的区别
(1)string 1,Stirng是对象不是基本数据类型 2,String是final类,不能被继承.是不可变对象,一旦创建,就不能修改它的值. 3,对于已经存在的Sti ...
- mysql词法分析和语法分析
如果没有命中查询缓存,就要开始真正执行语句了.首先,MySQL 需要知道你要做什么,因此需要对 SQL 语句做解析.分析器先会做"词法分析".你输入的是由多个字符串和空格组成的一条 ...
- 原生js的一些盲点
1.document.readyState 有三个值loading interactive //loading 仍在加载 interactive 文档已被解析 正在加载状态结束 但是样式表和框架之 ...
- slice,splice,split,unshift的用法
工作了很久始终对这4个用法处于混淆状态,今天写个帖子来警示下自己 // slice(start,end),从start值开始截取到end前的元素组成新的数组,不改变原数组 // slice(index ...
- python -c 妙用
前言 python -c 命令还是有用的哈 正文 python的 -c 可以在命令行中调用 python 代码, 实际上 -c 就是 command 的意思 官方文档中解释为(节选自: python ...
- 浅谈Go中的time.After
go的一条哲学是 不要通过共享来实现通信,而是通信来实现共享 多协程之间通过 channel 来实现通信,而普遍会遇到的问题是,如何进行超时控制,资料一查询,需要配置select和time.After ...
- Python pip install 默认路径修改。
pip动不动就下载数百M的文件.这些文件默认在C:盘,那么为了节省空间需要修改这些路径: 打开cmd命令窗口.输入: python -m site C:\Users\hewei>python - ...