我们在实际编程中,需要保存许多私有数据,例如:密码、密钥等等。所以,我们需要经常在使用完这些私有数据后,清除内存使用踪迹,以防止被潜在的入侵者获得这些数据。这篇文章中,我们讨论使用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()使用分析的更多相关文章

  1. 从ofo牵手理财平台看,用户隐私数据的使用有底线吗?

    智慧生活的到来既是社会变迁的拐点,又不可避免地带来一种挥之不去的焦虑.这种焦虑的由来,是因个人隐私数据在智慧生活下变成一种"黑暗财富".随着相关数据挖掘.收集.分析技术的成熟,人们 ...

  2. Springboot 配置文件、隐私数据脱敏的最佳实践(原理+源码)

    大家好!我是小富- 这几天公司在排查内部数据账号泄漏,原因是发现某些实习生小可爱居然连带着账号.密码将源码私传到GitHub上,导致核心数据外漏,孩子还是没挨过社会毒打,这种事的后果可大可小. 说起这 ...

  3. iOS10-配置获取隐私数据权限声明

    iOS10中,苹果加强了对用户隐私数据的保护,在访问以下数据的时候都需要在info.list重配置privacy,进行声明,否则程序无法正常运行. Contacts, Calendar, Remind ...

  4. Fabric v2.0中的隐私数据

    文章来源于https://hyperledger-fabric.readthedocs.io/en/release-2.0/ 私有数据集在v1.4中提出,一直使用的是隐私数据集方式,即建立一个隐私数据 ...

  5. Hadoop基础-HDFS数据清理过程之校验过程代码分析

    Hadoop基础-HDFS数据清理过程之校验过程代码分析 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 想称为一名高级大数据开发工程师,不但需要了解hadoop内部的运行机制,还需 ...

  6. Wireshark数据抓包教程之认识捕获分析数据包

    Wireshark数据抓包教程之认识捕获分析数据包 认识Wireshark捕获数据包 当我们对Wireshark主窗口各部分作用了解了,学会捕获数据了,接下来就该去认识这些捕获的数据包了.Wiresh ...

  7. 65、Spark Streaming:数据接收原理剖析与源码分析

    一.数据接收原理 二.源码分析 入口包org.apache.spark.streaming.receiver下ReceiverSupervisorImpl类的onStart()方法 ### overr ...

  8. 兼容iOS 10:配置获取隐私数据权限声明

    原文链接 iOS 10的一大变化是更强的隐私数据保护.在文档中是这么描述的: You must statically declare your app’s intended use of protec ...

  9. iOS 用户的隐私数据-privacy-sensitive data

    1  Xcode 报错:This app has crashed because it attempted to access privacy-sensitive data without a usa ...

随机推荐

  1. 蒲公英 · JELLY技术周刊 Vol.36: 你好 Hooks,再见 2020

    蒲公英 · JELLY技术周刊 Vol.36 不知不觉,蒲公英已经伴随我们走过了一年时光,在这一年我们从基础技术.前端框架.图形编程.人工智能等诸多领域为大家推介了三百余篇文章,尽管这一年来风雨不断, ...

  2. pandas取前K大的数,sort_values()和nlargest()速度比较

    排序量比较大时: 数据量比较小时: 所以结论就是: 数据量大时选用nlargest,数据量小时选用sort_values() 具体数据量怎么算大:10000条时两个方法的时间差不多,所以可以按1000 ...

  3. Mac电脑 Android Studio连接小米手机

    1.设置>关于本机>点击5下MIUI版本>激活开发者模式 2.设置>更多设置>开发者选项>开启开发者选项>开启USB调试>开启USB安装>开启显示 ...

  4. 关于STM32的CAN的过滤器

    关于STM32的CAN的过滤器STM32普通型芯片的CAN有14组过滤器组,互联型有28组过滤器组.一般我们用的都是普通型的,所以在本文中可以说STM32有14组过滤器组.根据配置,每1组过滤器组可以 ...

  5. DNS主从服务器配置实现

    主服务器-centos7-IP:192.168.184.201 从服务器-centos7-IP:192.168.184.202 客户端-ubuntu1804-IP:192.168.184.150 ①客 ...

  6. 轻松理解UML用例图时序图类图的教程

    摘自https://zhuanlan.zhihu.com/p/29874146 写在前面 当你老大扔给你这样的图,或者你需要完成某些功能而去看文档的时候发现以下类似这样的图会不会不(一)知(脸)所(懵 ...

  7. 浅析 MVC Pattern

    一.前言 最近做CAD插件相关的工作,用到了一些模式,解决对应场景的问题. 比如插件的运行实例上使用Singleton.实例内部使用了MVC(Strategy and Observer ). 针对CA ...

  8. js 鼠标点击页面出现文字

    <script type="text/javascript"> var a_idx = 0; jQuery(document).ready(function($) { ...

  9. js如何替换字符串中匹配到多处中某一指定节点?

    抛出一个问题,如图,搜索关键字,匹配到四处,那我鼠标放在第二处,我想把它变个颜色,该怎么实现呢?回到文章的标题,js如何替换字符串中匹配到多处中某一指定节点? 字符串的替换,我们首先想到的一个属性是r ...

  10. SQL查找连续出现的数字

    基于Oracle: 题:编写一个 SQL 查询,查找所有至少连续出现三次的数字. +----+-----+ | Id | Num | +----+-----+ | 1 | 1 | | 2 | 1 | ...