博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#中机密文本的保存方案
阅读量:5788 次
发布时间:2019-06-18

本文共 4513 字,大约阅读时间需要 15 分钟。

托管代码中的字符串是一类特殊的对象,它不可被改变的,每次使用 System.String 类中的方法之一或进行运算时(如赋值、拼接等)时,都要在内存中创建一个新的字符串对象,也就是为该新对象分配新的空间。这就带来两个问题:

1:原来的字符串是不是还在内存当中?

2:如果在内存当中,那么机密数据(如密码)该如何保存才足够安全?

 

先来看第一个问题:

ExpandedBlockStart.gif
代码
 
public
class
Program
{
static
void
Main(
string
[] args)
{
Method1();
//
在此处打上断点
Console.ReadKey();
}
static
void
Method1()
{
string
str
=
"
luminji
"
;
Console.WriteLine(str);
}
}

 

在Method1处打上断点,让VS执行到此处,在即时窗口中运行命令:.load sos.dll 和 !dso,如下:

打开调试中的内存查看窗口,定位到019db820(由!dso得到)。由于此时还没有进入到Method1,所以内存当中不存在字符串“luminji”。接着让程序运行到方法内部,我们看到内存当中已经存在了“luminji”了。

接着让程序继续运行,退出方法Method1,发现“luminji”依然留在内存当中。

这就带来一个问题,如果有恶意人员扫描你的内存,你的程序所保存的机密信息将无处可逃。幸好FCL中提供了System.Security.SecureString,SecureString表示一个应保密的文本,在初始化时就已经被加密。

ExpandedBlockStart.gif
代码
 
public
class
Program
{
static
System.Security.SecureString secureString
=
new
System.Security.SecureString();
static
void
Main(
string
[] args)
{
Method2();
//
在此处打上断点
Console.ReadKey();
}
static
void
Method2()
{
secureString.AppendChar(
'
l
'
);
secureString.AppendChar(
'
u
'
);
secureString.AppendChar(
'
m
'
);
secureString.AppendChar(
'
i
'
);
secureString.AppendChar(
'
n
'
);
secureString.AppendChar(
'
j
'
);
secureString.AppendChar(
'
i
'
);
}
}

 

相同的方法,可以发现在进入Method2后,已经找不到对应的字符串了。但是,问题随之而来,核心数据的保存问题已经解决了,可是文本总是要取出来用的,只要取出来不是就会被发现吗。没错,这个问题没法避免,但是我们可以做到文本一使用完毕,就释放掉。

见如下代码:

ExpandedBlockStart.gif
代码
 
static
void
Method3()
{
secureString.AppendChar(
'
l
'
);
secureString.AppendChar(
'
u
'
);
secureString.AppendChar(
'
m
'
);
secureString.AppendChar(
'
i
'
);
secureString.AppendChar(
'
n
'
);
secureString.AppendChar(
'
j
'
);
secureString.AppendChar(
'
i
'
);
IntPtr addr
=
Marshal.SecureStringToBSTR(secureString);
string
temp
=
Marshal.PtrToStringBSTR(addr);
//
使用该机密文本do something
///
=======开始清理内存
//
清理掉非托管代码中对应的内存的值
Marshal.ZeroFreeBSTR(addr);
//
清理托管代码对应的内存的值(采用重写的方法)
int
id
=
GetProcessID();
byte
[] writeBytes
=
Encoding.Unicode.GetBytes(
"
xxxxxx
"
);
IntPtr intPtr
=
Open(id);
unsafe
{
fixed
(
char
*
c
=
temp)
{
WriteMemory((IntPtr)c, writeBytes, writeBytes.Length);
}
}
///
=======清理完毕
}

 

注意查看上文代码:

    IntPtr addr = Marshal.SecureStringToBSTR(secureString);

    string temp = Marshal.PtrToStringBSTR(addr);

这两行代码表示的就是将机密文本从secureString取出来,临时赋值给字符串temp。这就存在两个问题,第一行实际调用的是非托管代码,它在内存中也会存储一个“luminji”,第二行代码是在托管内存中存储一个“luminji”。这两段文本的释放方式是不一样的。前者,可以通过使用:

Marshal.ZeroFreeBSTR(addr);

进行释放。而托管内存中的文本,只能通过重写来完成(如上文中,就是重写成为无意义的“xxxxxx”)。

上段代码涉及到的几个方法如下:

ExpandedBlockStart.gif
代码
 
public
static
int
GetProcessID()
{
Process p
=
Process.GetCurrentProcess();
return
p.Id;
}
public
static
IntPtr Open(
int
processId)
{
IntPtr hProcess
=
IntPtr.Zero;
hProcess
=
ProcessAPIHelper.OpenProcess(ProcessAccessFlags.All,
false
, processId);
if
(hProcess
==
IntPtr.Zero)
throw
new
Exception(
"
OpenProcess失败
"
);
processInfo.hProcess
=
hProcess;
processInfo.dwProcessId
=
processId;
return
hProcess;
}
public
static
int
WriteMemory(IntPtr addressBase,
byte
[] writeBytes,
int
writeLength)
{
int
reallyWriteLength
=
0
;
if
(
!
ProcessAPIHelper.WriteProcessMemory(processInfo.hProcess, addressBase, writeBytes, writeLength,
out
reallyWriteLength))
{
//
throw new Exception();
}
return
reallyWriteLength;
}
[StructLayout(LayoutKind.Sequential)]
internal
struct
PROCESS_INFORMATION
{
public
IntPtr hProcess;
public
IntPtr hThread;
public
int
dwProcessId;
public
int
dwThreadId;
}
[Flags]
enum
ProcessAccessFlags :
uint
{
All
=
0x001F0FFF
,
Terminate
=
0x00000001
,
CreateThread
=
0x00000002
,
VMOperation
=
0x00000008
,
VMRead
=
0x00000010
,
VMWrite
=
0x00000020
,
DupHandle
=
0x00000040
,
SetInformation
=
0x00000200
,
QueryInformation
=
0x00000400
,
Synchronize
=
0x00100000
}
static
class
ProcessAPIHelper
{
[DllImport(
"
kernel32.dll
"
)]
public
static
extern
IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)]
bool
bInheritHandle,
int
dwProcessId);
[DllImport(
"
kernel32.dll
"
, SetLastError
=
true
)]
public
static
extern
bool
WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress,
byte
[] lpBuffer,
int
nSize,
out
int
lpNumberOfBytesWritten);
[DllImport(
"
kernel32.dll
"
, SetLastError
=
true
)]
public
static
extern
bool
ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
[Out]
byte
[] lpBuffer,
int
dwSize,
out
uint
lpNumberOfBytesRead
);
[DllImport(
"
kernel32.dll
"
, SetLastError
=
true
)]
[
return
: MarshalAs(UnmanagedType.Bool)]
public
static
extern
bool
CloseHandle(IntPtr hObject);
}

 

 

总结:

1:机密文本使用System.Security.SecureString保存;

2:System.Security.SecureString被释放后使用Marshal.ZeroFreeBSTR清除在内存中的痕迹;

3:托管字符串只能使用重写内存进行清除;

本文转自最课程陆敏技博客园博客,原文链接:http://www.cnblogs.com/luminji/archive/2011/01/28/1946635.html,如需转载请自行联系原作者

你可能感兴趣的文章
阅读Spring源代码(1)
查看>>
nagios一键安装脚本,nagios监控被监控主机上的应用服务mysql数据库
查看>>
grep 命令
查看>>
JS二维数组的声明和使用
查看>>
v$archive_gap dg dataguard 断档处理 scn恢复
查看>>
问责IT风险管理:CIO需关注两个重点
查看>>
Winform打包发布图解
查看>>
PDF文件怎么编辑,超简单的方法
查看>>
EasyUI基础入门之Easyloader(载入器)
查看>>
Uva 839 Not so Mobile
查看>>
30款超酷的HTTP 404页面未找到错误设计
查看>>
程序猿必备 MyEclipse2013-2014系列
查看>>
java中ArrayList 、LinkList区别
查看>>
Spring ’14 Wave Update: Installing Dynamics CRM on Tablets for Windows 8.1
查看>>
利用rand7()构造rand10()
查看>>
MySQL 备份与恢复
查看>>
吃午饭前,按书上的代码写会儿--Hunt the Wumpus第一个版本
查看>>
easyui中combobox的值改变onchang事件
查看>>
Eclipse魔法堂:任务管理器
查看>>
一周自学动态站点设计
查看>>