Created
October 28, 2018 22:58
-
-
Save leberechtreinhold/a77aa3e7f516a17521edd05c6c4bc5e4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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