- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
在讨论后here ,如果你想有一个安全的类来在内存中存储敏感信息(例如密码),你必须:
所以这听起来不错,我创建了一个测试类来看看它是否有效。所以我做了一个简单的测试用例,我不断添加单词“LOL”和“WUT”,然后在这个安全缓冲区类中添加一个数字大约一千次,销毁该对象,然后最终执行导致核心转储的操作。
由于该类应该在销毁之前安全地清除内存,因此我不应该能够在 coredump 上找到“LOLWUT”。但是,我还是设法找到了它们,并想知道我的实现是否只是错误。但是,我使用 CryptoPP 库的 SecByteBlock 尝试了同样的事情:
#include <cryptopp/osrng.h>
#include <cryptopp/dh.h>
#include <cryptopp/sha.h>
#include <cryptopp/aes.h>
#include <cryptopp/modes.h>
#include <cryptopp/filters.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
using namespace std;
int main(){
{
CryptoPP::SecByteBlock moo;
int i;
for(i = 0; i < 234; i++){
moo += (CryptoPP::SecByteBlock((byte*)"LOL", 3));
moo += (CryptoPP::SecByteBlock((byte*)"WUT", 3));
char buffer[33];
sprintf(buffer, "%d", i);
string thenumber (buffer);
moo += (CryptoPP::SecByteBlock((byte*)thenumber.c_str(), thenumber.size()));
}
moo.CleanNew(0);
}
sleep(1);
*((int*)NULL) = 1;
return 0;
}
然后编译使用:
g++ clearer.cpp -lcryptopp -O0
然后启用核心转储
ulimit -c 99999999
然后,启用核心转储并运行它
./a.out ; grep LOLWUT core ; echo hello
给出以下输出
Segmentation fault (core dumped)
Binary file core matches
hello
这是什么原因造成的?由于 SecByteBlock 的 append 引起的重新分配,应用程序的整个内存区域是否重新分配?
另外,This is SecByteBlock's Documentation
edit:使用 vim 检查核心转储后,我得到了这个: http://imgur.com/owkaw
edit2:更新了代码,使其更易于编译,以及编译说明
final edit3:看起来 memcpy 是罪魁祸首。请参阅 Rasmus 在下面的答案中的 mymemcpy
实现。
最佳答案
尽管出现在核心转储中,但密码实际上并不在内存中清除缓冲区后不再。问题是 memcpy
ing足够长的字符串会将密码泄漏到 SSE 寄存器中,那些是核心转储中显示的内容。
当 memcpy
的 size
参数大于某个阈值— 80 bytes on the mac — 然后使用 SSE 指令执行内存复制。这些指令更快,因为它们可以复制 16一次并行处理字节,而不是逐个字符,一个字节一个字节,或者一个字一个字。这是源代码的关键部分 Libc on the mac :
LAlignedLoop: // loop over 64-byte chunks
movdqa (%rsi,%rcx),%xmm0
movdqa 16(%rsi,%rcx),%xmm1
movdqa 32(%rsi,%rcx),%xmm2
movdqa 48(%rsi,%rcx),%xmm3
movdqa %xmm0,(%rdi,%rcx)
movdqa %xmm1,16(%rdi,%rcx)
movdqa %xmm2,32(%rdi,%rcx)
movdqa %xmm3,48(%rdi,%rcx)
addq $64,%rcx
jnz LAlignedLoop
jmp LShort // copy remaining 0..63 bytes and done
%rcx
是循环索引寄存器,%rsi
是s源地址寄存器,%rdi
是 destination 地址寄存器。每次绕圈跑,64 字节从源缓冲区复制到 4 个 16 字节 SSE 寄存器xmm{0,1,2,3}
;然后将这些寄存器中的值复制到目标缓冲区。
该源文件中还有很多东西可以确保出现拷贝仅在对齐的地址上,以填写剩余的拷贝部分在完成 64 字节 block 之后,并处理源和目的地重叠。
但是——SSE 寄存器在使用后不会被清除!这意味着 64 字节xmm{0,1,2,3}
寄存器中仍然存在被复制的缓冲区。
这是对 Rasmus 程序的修改,显示了这一点:
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <emmintrin.h>
inline void SecureWipeBuffer(char* buf, size_t n){
volatile char* p = buf;
asm volatile("rep stosb" : "+c"(n), "+D"(p) : "a"(0) : "memory");
}
int main(){
const size_t size1 = 200;
const size_t size2 = 400;
char* b = new char[size1];
for(int j=0;j<size1-10;j+=10){
memcpy(b+j, "LOL", 3);
memcpy(b+j+3, "WUT", 3);
sprintf((char*) (b+j+6), "%d", j);
}
char* nb = new char[size2];
memcpy(nb, b, size1);
SecureWipeBuffer(b,size1);
SecureWipeBuffer(nb,size2);
/* Password is now in SSE registers used by memcpy() */
union {
__m128i a[4];
char c;
};
asm ("MOVDQA %%xmm0, %0": "=x"(a[0]));
asm ("MOVDQA %%xmm1, %0": "=x"(a[1]));
asm ("MOVDQA %%xmm2, %0": "=x"(a[2]));
asm ("MOVDQA %%xmm3, %0": "=x"(a[3]));
for (int i = 0; i < 64; i++) {
char p = *(&c + i);
if (isprint(p)) {
putchar(p);
} else {
printf("\\%x", p);
}
}
putchar('\n');
return 0;
}
在我的 Mac 上,打印出来的是:
0\0LOLWUT130\0LOLWUT140\0LOLWUT150\0LOLWUT160\0LOLWUT170\0LOLWUT180\0\0\0
现在,检查核心转储,密码只出现一次,并作为确切的 0\0LOLWUT130\0...180\0\0\0
字符串。核心转储必须包含所有寄存器的拷贝,这就是该字符串存在的原因——它是xmm{0,1,2,4}
寄存器的值。
所以 调用后密码实际上不再在 RAM 中了SecureWipeBuffer
,它只是看起来是,因为它实际上是在一些仅出现在核心转储中的寄存器。如果你担心memcpy
存在可被 RAM 卡住利用的漏洞,不用担心了。如果在寄存器中保存密码拷贝让您感到困扰,使用不使用 SSE2 寄存器的修改后的 memcpy
,或清除它们完成后。如果您真的对此感到偏执,请继续测试您的coredumps 以确保编译器不会优化您的密码清除代码。
关于c++ - 安全清除内存并重新分配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10683941/
我在具有 2CPU 和 3.75GB 内存 (https://aws.amazon.com/ec2/instance-types/) 的 c3.large Amazon EC2 ubuntu 机器上运
我想通过用户空间中的mmap-ing并将地址发送到内核空间从用户空间写入VGA内存(视频内存,而不是缓冲区),我将使用pfn remap将这些mmap-ed地址映射到vga内存(我将通过 lspci
在 Mathematica 中,如果你想让一个函数记住它的值,它在语法上是很轻松的。例如,这是标准示例 - 斐波那契: fib[1] = 1 fib[2] = 1 fib[n_]:= fib[n] =
我读到动态内存是在运行时在堆上分配的,而静态内存是在编译时在堆栈上分配的,因为编译器知道在编译时必须分配多少内存。 考虑以下代码: int n; cin>>n; int a[n]; 如果仅在运行期间读
我是 Python 的新手,但我之前还不知道这一点。我在 for 循环中有一个基本程序,它从站点请求数据并将其保存到文本文件但是当我检查我的任务管理器时,我发现内存使用量只增加了?长时间运行时,这对我
我正在设计一组数学函数并在 CPU 和 GPU(使用 CUDA)版本中实现它们。 其中一些函数基于查找表。大多数表占用 4KB,其中一些占用更多。基于查找表的函数接受一个输入,选择查找表的一两个条目,
读入一个文件,内存被动态分配给一个字符串,文件内容将被放置在这里。这是在函数内部完成的,字符串作为 char **str 传递。 使用 gdb 我发现在行 **(str+i) = fgetc(aFil
我需要证实一个理论。我正在学习 JSP/Java。 在查看了一个现有的应用程序(我没有写)之后,我注意到一些我认为导致我们的性能问题的东西。或者至少是其中的一部分。 它是这样工作的: 1)用户打开搜索
n我想使用memoization缓存某些昂贵操作的结果,这样就不会一遍又一遍地计算它们。 两个memoise和 R.cache适合我的需要。但是,我发现缓存在调用之间并不可靠。 这是一个演示我看到的问
我目前正在分析一些 javascript shell 代码。这是该脚本中的一行: function having() { memory = memory; setTimeout("F0
我有一种情况,我想一次查询数据库,然后再将整个数据缓存在内存中。 我得到了内存中 Elasticsearch 的建议,我用谷歌搜索了它是什么,以及如何在自己的 spring boot 应用程序中实现它
我正在研究 Project Euler (http://projecteuler.net/problem=14) 的第 14 题。我正在尝试使用内存功能,以便将给定数字的序列长度保存为部分结果。我正在
所以,我一直在做 Java 内存/注意力游戏作业。我还没有达到我想要的程度,它只完成了一半,但我确实让 GUI 大部分工作了......直到我尝试向我的框架添加单选按钮。我认为问题可能是因为我将 JF
我一直在尝试使用 Flask-Cache 的 memoize 功能来仅返回 statusTS() 的缓存结果,除非在另一个请求中满足特定条件,然后删除缓存。 但它并没有被删除,并且 Jinja 模板仍
我对如何使用 & 运算符来减少内存感到非常困惑。 我可以回答下面的问题吗? clase C{ function B(&$a){ $this->a = &$a; $thi
在编写代码时,我遇到了一个有趣的问题。 我有一个 PersonPOJO,其 name 作为其 String 成员之一及其 getter 和 setter class PersonPOJO { priv
在此代码中 public class Base { int length, breadth, height; Base(int l, int b, int h) { l
Definition Structure padding is the process of aligning data members of the structure in accordance
在 JavaScript Ninja 的 secret 中,作者提出了以下方案,用于在没有闭包的情况下内存函数结果。他们通过利用函数是对象这一事实并在函数上定义一个属性来存储过去调用函数的结果来实现这
我正在尝试找出 map 消耗的 RAM 量。所以,我做了以下事情;- Map cr = crPair.collectAsMap(); // 200+ entries System.out.printl
我是一名优秀的程序员,十分优秀!