Android系统安全 — 2.1-隐私数据用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,sizeof(PrivateData));
	return 1;
}
int main()
{
	funcpswd();
	return 0;
}

上面示例完全是一个虚假例子。如果我们使用编译器(这里以Visual Studio 2015)编译一个调试版本的代码,那么这个代码可以很完善的运行,包括\(memset()\)函数的操作,也就是隐私数据用完之后,会得到清除。
但是让我们直接编译成一个运行版本,并且反编译出来结果如下:

......
000000013F7A1035  call        std::operator>><><char> > (013F7A18B0h)  
000000013F7A103A  lea         rcx,[rsp+20h]  
000000013F7A103F  call        doSmth (013F7A1170h)  
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,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,sizeof(PrivateData));
free(data);
for (int i = 0; i < len;="" ++i)="" printf("%c",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,%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,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),%rdi
callq _ZdlPv

可知,clang对我们代码做了优化,隐私数据依然存在。
所以,我们应该如何更好的清除掉我们的隐私数据,从而保存我们的安全?
我们应该使用特殊的内存清除函数,它指定编译器不回删除这些函数。例如在Visual Studio中,可以使用\(RtlSecureZeroMemory\)函数。从C++11标准开始,我们可以使用\(memset\_s\)函数。此外,我们也可以实现我们自己需要的安全版本的内存清楚函数,示例1代码如下:

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


总结
memset()使用分析全部内容,遇到的程序开发问题希望够帮你解决你的隐私数据。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值