你的隐私数据真的安全吗之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 ...
随机推荐
- [leetcode]236. Lowest Common Ancestor of a Binary Tree树的最小公共祖先
如果一个节点的左右子树上分别有两个节点,那么这棵树是祖先,但是不一定是最小的,但是从下边开始判断,找到后一直返回到上边就是最小的. 如果一个节点的左右子树上只有一个子树上遍历到了节点,那么那个子树可能 ...
- mysql8.0.19压缩版安装
1.官网下载.zip格式的MySQL Server的压缩包,选择x86或x64版,并解压. 2. 创建 data文件夹 及 my.ini文件,并编辑 [mysqld] # 设置为自己MYSQL的安装目 ...
- Bata冲刺——第一天
这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzzcxy/2018SE1 这个作业要求在哪里 https://edu.cnblogs.com/campus/fz ...
- 四、hive安装
一.安装方式(内嵌模式,本地模式远程模式) 安装环境以及前提说明: Hive是依赖于hadoop系统的,因此在运行Hive之前需要保证已经搭建好hadoop集群环境. 本例中使用的hadoop版本为2 ...
- livy提交spark应用
spark-submit的使用shell时时灵活性较低,livy作为spark提交的一种工具,是使用接口或者java客户端的方式提交,可以集成到web应用中 1.客户端提交的方式 http://l ...
- java集合并发操作异常解决方法:CopyOnWriteArrayList
一.我们知道ArrayList是线程不安全的,请编写一个不安全的案例并给出解决方案 HashSet与ArrayList一致 HashMap HashSet底层是一个HashMap,存储的值放在Hash ...
- i5 11300H 怎么样 相当于什么水平
i5-11300H 为 4 核 8 线程,主频 3.1GHz,睿频 4.4GHz,三级缓存 8MBi5-11300H 怎么样看完你就知道了 https://list.jd.com/list.html?
- 《Spring Boot 实战纪实》之前言
目录 前言 (思维篇)人人都是产品经理 1.需求文档 1.1 需求管理 1.2 如何攥写需求文档 1.3 需求关键点文档 2 原型设计 2.1 缺失的逻辑 2.2 让想法跃然纸上 3 开发设计文档 3 ...
- [leetcode] 周赛 223
比赛题目:https://leetcode-cn.com/contest/weekly-contest-223/. 解码异或后的数组 题目:1720. 解码异或后的数组. 还记得数列求和的「累加法」? ...
- WPF APP 启动时增加特殊逻辑
public partial class App : Application { public App() { this.Startup += (o1, e1)=>{ string comman ...