Skip to content

Instantly share code, notes, and snippets.

@leberechtreinhold
Created October 28, 2018 22:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leberechtreinhold/a77aa3e7f516a17521edd05c6c4bc5e4 to your computer and use it in GitHub Desktop.
Save leberechtreinhold/a77aa3e7f516a17521edd05c6c4bc5e4 to your computer and use it in GitHub Desktop.
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security;
namespace TestSecureStringExtensions
{
public static class SecureStringExtensions
{
public static TemporalString GetString(this SecureString source)
{
if (source == null)
throw new ArgumentNullException("Can't create string from null securestring");
IntPtr unmanagedString = IntPtr.Zero;
try
{
unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(source);
return new TemporalString(Marshal.PtrToStringUni(unmanagedString));
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
}
}
public static void AppendStr(this SecureString source, string append)
{
foreach (var c in append)
{
source.AppendChar(c);
}
}
public static void AppendSecureStr(this SecureString source, SecureString append)
{
using (var toappend = append.GetString())
{
source.AppendStr(toappend.str);
}
}
public static SecureString Substring(this SecureString source, int start, int len)
{
var substr = new SecureString();
using (var original = source.GetString())
{
int end = start + len;
for (int i = start; i < end; i++)
{
substr.AppendChar(original.str[i]);
}
}
return substr;
}
public static SecureString Substring(this SecureString source, int start)
{
return source.Substring(start, source.Length - start);
}
// Beware, this creates a securestring but does not guarantee the original is safe...
public static SecureString ToSecureString(this string source)
{
var secStr = new SecureString();
if (source == null) return secStr;
// Do not use foreach(var c in source) because it creates a temporal
// char array! Although not always...
for (int i = 0; i < source.Length; i++)
{
secStr.AppendChar(source[i]);
}
return secStr;
}
// Even more beware... This accesses the string in raw and modifies the
// internal representation.
public static unsafe void ClearString(this string source, string substitue = "")
{
if (source == null) return;
GCHandle gcHandle = GCHandle.Alloc(source, GCHandleType.Pinned);
unsafe
{
char* c_test = (char*)gcHandle.AddrOfPinnedObject();
for (int i = 0; i < source.Length; i++)
{
if (i < substitue.Length)
c_test[i] = substitue[i];
else
c_test[i] = '\0';
}
}
gcHandle.Free();
}
// Technically not unsafe in the proper sense of the word, but it is
// VERY VERY unsafe since it can change any field internally
public static unsafe void ResetStringValue<T>(T obj, string fieldname)
{
var res = GetField<T>(obj, fieldname);
if (res == null)
{
throw new ArgumentException("The type given does not have the field " + fieldname);
}
var res_str = res as string;
if (res == null)
{
throw new ArgumentException("The type given has the field " + fieldname + " but is not a string");
}
res_str.ClearString();
}
private static unsafe object GetField<T>(T obj, string fieldname, Type type)
{
var fields = type.GetFields(
BindingFlags.Instance
| BindingFlags.NonPublic
| BindingFlags.Public
| BindingFlags.Static);
foreach (var field in fields)
{
if (field.Name == fieldname)
{
return field.GetValue(obj);
}
}
if (type.BaseType != null)
{
return GetField<T>(obj, fieldname, type.BaseType);
}
return null;
}
public static unsafe object GetField<T>(T obj, string fieldname)
{
return GetField<T>(obj, fieldname, obj.GetType());
}
public static unsafe object GetProperty<T>(T obj, string propertyname, Type type)
{
var properties = type.GetProperties(
BindingFlags.Instance
| BindingFlags.NonPublic
| BindingFlags.Public
| BindingFlags.Static);
foreach (var property in properties)
{
if (property.Name == propertyname)
{
return property.GetValue(obj, null);
}
}
if (type.BaseType != null)
{
return GetField<T>(obj, propertyname, type.BaseType);
}
return null;
}
public static unsafe object GetProperty<T>(T obj, string propertyname)
{
return GetProperty<T>(obj, propertyname, obj.GetType());
}
public static unsafe void ChangeMember<T>(T input, string fieldname, Object new_value)
{
var fields = input.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (var field in fields)
{
if (field.Name == fieldname)
{
field.SetValue(input, new_value);
}
}
}
}
public class TemporalString : IDisposable
{
public string str { get; private set; }
public TemporalString(string _str) { str = _str; }
public void Dispose()
{
str.ClearString();
}
}
class Program
{
static void CountNumberOfChars(string str, char char_to_count)
{
int count = 0;
foreach(var c in str)
{
if (c == char_to_count) count++;
}
Console.WriteLine("The string given has the character " + count + " times");
}
static void Main(string[] args)
{
var secret = new SecureString();
secret.AppendChar('t');secret.AppendChar('h');secret.AppendChar('i');secret.AppendChar('s');
secret.AppendChar('_');
secret.AppendChar('i');secret.AppendChar('s');
secret.AppendChar('_');
secret.AppendChar('s');secret.AppendChar('e');secret.AppendChar('c');
secret.AppendChar('r');secret.AppendChar('e');secret.AppendChar('t');
secret.MakeReadOnly();
Console.WriteLine("String in memory now, but encrypted using Windows Encryption API.");
Console.WriteLine("You can create a dump now and test using strings.exe");
Console.ReadLine();
using (var tmp = secret.GetString())
{
CountNumberOfChars(tmp.str, 's');
}
Console.WriteLine("Operated with a string and cleared it from memory.");
Console.WriteLine("You can create a dump now and test using strings.exe");
Console.ReadLine();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment