zoukankan      html  css  js  c++  java
  • .NET 中机密文本保存方案

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

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

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

    先来看第一个问题:

    代码
    publicclass Program
    {
    staticvoid Main(string[] args)
    {
    Method1();
    //在此处打上断点
    Console.ReadKey();
    }
    staticvoid Method1()
    {
    string str ="luminji";
    Console.WriteLine(str);
    }
    }

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

    image

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

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

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

    代码
    publicclass Program
    {
    static System.Security.SecureString secureString =new System.Security.SecureString();

    staticvoid Main(string[] args)
    {
    Method2();
    //在此处打上断点
    Console.ReadKey();
    }
    staticvoid Method2()
    {
    secureString.AppendChar(
    'l');
    secureString.AppendChar(
    'u');
    secureString.AppendChar(
    'm');
    secureString.AppendChar(
    'i');
    secureString.AppendChar(
    'n');
    secureString.AppendChar(
    'j');
    secureString.AppendChar(
    'i');
    }
    }

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

    见如下代码:

    代码
    staticvoid 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”)。

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

    代码
    publicstaticint GetProcessID()
    {
    Process p
    = Process.GetCurrentProcess();
    return p.Id;
    }
    publicstatic IntPtr Open(int processId)
    {
    IntPtr hProcess
    = IntPtr.Zero;
    hProcess
    = ProcessAPIHelper.OpenProcess(ProcessAccessFlags.All, false, processId);
    if (hProcess == IntPtr.Zero)
    thrownew Exception("OpenProcess失败");
    processInfo.hProcess
    = hProcess;
    processInfo.dwProcessId
    = processId;
    return hProcess;
    }
    publicstaticint 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)]
    internalstruct PROCESS_INFORMATION
    {
    public IntPtr hProcess;
    public IntPtr hThread;
    publicint dwProcessId;
    publicint 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
    }
    staticclass ProcessAPIHelper
    {
    [DllImport(
    "kernel32.dll")]
    publicstaticextern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId);
    [DllImport(
    "kernel32.dll", SetLastError =true)]
    publicstaticexternbool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, outint lpNumberOfBytesWritten);
    [DllImport(
    "kernel32.dll", SetLastError =true)]
    publicstaticexternbool ReadProcessMemory(
    IntPtr hProcess,
    IntPtr lpBaseAddress,
    [Out]
    byte[] lpBuffer,
    int dwSize,
    outuint lpNumberOfBytesRead
    );
    [DllImport(
    "kernel32.dll", SetLastError =true)]
    [
    return: MarshalAs(UnmanagedType.Bool)]
    publicstaticexternbool CloseHandle(IntPtr hObject);
    }

    总结:

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

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

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

    有关利用sos.dll调试非托管代码,查看http://www.cnblogs.com/luminji/archive/2011/01/27/1946217.html

  • 相关阅读:
    Java vs Python
    Compiled Language vs Scripting Language
    445. Add Two Numbers II
    213. House Robber II
    198. House Robber
    276. Paint Fence
    77. Combinations
    54. Spiral Matrix
    82. Remove Duplicates from Sorted List II
    80. Remove Duplicates from Sorted Array II
  • 原文地址:https://www.cnblogs.com/DebugLZQ/p/2132466.html
Copyright © 2011-2022 走看看