Skip to content

Instantly share code, notes, and snippets.

@Porges
Last active August 29, 2015 14:10
Show Gist options
  • Save Porges/868916d1a39a6b93960b to your computer and use it in GitHub Desktop.
Save Porges/868916d1a39a6b93960b to your computer and use it in GitHub Desktop.
smuggling data in C#
using System;
using System.CodeDom;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Serialization;
using System.Threading;
// The situation:
// There is an untouchable class that wraps some instance that we can provide.
// In order for our instance to do its work, it needs some more data to be
// passed to it (but not at construction time). (To make this less easy,
// the data we need to pass is an object with behaviour, it is not just some value.)
// However, we can't modify the 'untouchable' class to allow us to pass this data.
// Here is the 'untouchable' class.
sealed class Untouchable<TArg>
{
// Pretend TArg is fixed, it's just generic so I only have to write one instance.
private readonly IDoIt<TArg> _inner;
public Untouchable(IDoIt<TArg> inner) { _inner = inner; }
public void DoIt(TArg arg) => _inner.DoIt(arg);
}
// The interface we're implementing:
interface IDoIt<in TArg>
{
void DoIt(TArg arg);
}
// And the data we want to be able to access :
class ThingIWantToSmuggle
{
private int _counter;
public ThingIWantToSmuggle(int i) { _counter = i; }
public void Hello() => Console.WriteLine("Hello! " + _counter++);
}
public static class Smuggler
{
// 1)
// If we can pass through a subclass, it's easy!
class Base { }
class DerivedSmuggler : Base
{
public ThingIWantToSmuggle Instance;
}
class UseSubclass : IDoIt<Base>
{
public void DoIt(Base arg) =>
((DerivedSmuggler)arg).Instance.Hello();
}
private static void Smuggle1(ThingIWantToSmuggle budgie)
{
new Untouchable<Base>(new UseSubclass())
.DoIt(new DerivedSmuggler { Instance = budgie });
}
// 2)
// What if the data class is sealed? Let's pass it in a global (static) variable...
sealed class SealedBase { }
class UseStaticVariable : IDoIt<SealedBase>
{
public static ThingIWantToSmuggle Instance;
public void DoIt(SealedBase arg) =>
Instance.Hello();
}
private static void Smuggle2(ThingIWantToSmuggle budgie)
{
UseStaticVariable.Instance = budgie;
new Untouchable<SealedBase>(new UseStaticVariable()).DoIt(new SealedBase());
}
// 3)
// But this is going to be chaos if we have multiple threads...
// Let's magically attach the data to what we're passing:
class UseConditionalWeakTable : IDoIt<SealedBase>
{
public static readonly ConditionalWeakTable<SealedBase, ThingIWantToSmuggle> Mapper
= new ConditionalWeakTable<SealedBase, ThingIWantToSmuggle>();
public void DoIt(SealedBase arg)
{
ThingIWantToSmuggle value;
if (Mapper.TryGetValue(arg, out value))
{
value.Hello();
}
}
}
static void Smuggle3(ThingIWantToSmuggle budgie)
{
var sb = new SealedBase();
UseConditionalWeakTable.Mapper.Add(sb, budgie);
new Untouchable<SealedBase>(new UseConditionalWeakTable()).DoIt(sb);
}
// 4)
// But what if the value is a struct...
struct Structy { }
// ... or the untouchable generates a new instance of SealedBase?
// We can pass a named argument with CallContext.
class UseCallContext : IDoIt<Structy>
{
public void DoIt(Structy arg) =>
((ThingIWantToSmuggle)CallContext.GetData("budgie")).Hello();
}
static void Smuggle4(ThingIWantToSmuggle budgie)
{
CallContext.SetData("budgie", budgie);
try
{
new Untouchable<Structy>(new UseCallContext()).DoIt(new Structy());
}
finally
{
CallContext.FreeNamedDataSlot("budgie"); // clean up after ourselves
}
}
// 5)
// What if this is not perverse enough?
// Imagine that the data we're passing has an integer field:
class WithAField
{
public long Data;
}
// we can store a GCHandle in an integer and fetch the object from that:
class UseGCHandle : IDoIt<WithAField>
{
public void DoIt(WithAField arg)
{
var handle = GCHandle.FromIntPtr(new IntPtr(arg.Data));
var budgie = (ThingIWantToSmuggleWithData)handle.Target;
budgie.Hello();
// and we can get the actual data for that field from our struct:
var actualData = budgie.RealData;
Console.WriteLine("was passed " + actualData);
}
}
struct ThingIWantToSmuggleWithData
{
public ThingIWantToSmuggleWithData(long data) : this() { RealData = data; }
public long RealData;
public void Hello() { Console.WriteLine("Hello from " + GetType()); }
}
static void Smuggle5()
{
{
var handle = GCHandle.Alloc(new ThingIWantToSmuggleWithData(12345));
try
{
new Untouchable<WithAField>(new UseGCHandle())
.DoIt(new WithAField { Data = GCHandle.ToIntPtr(handle).ToInt64() });
// note that this is actually what you do when you need to supply
// a void* to a callback from native code
}
finally
{
handle.Free();
}
}
}
// 6)
// What if we want to smuggle the GCHandle inside something that has only a string field...
class WithAString
{
public string Data;
}
// .. *and* the string field is validated so we can't put weird stuff into it? [NB: we don't validate it here, but print it instead]
class UseGCHandleHiddenInString : IDoIt<WithAString>
{
public unsafe void DoIt(WithAString arg)
{
fixed (char* smuggling = arg.Data)
{
var len = arg.Data.Length;
var chars = new[] { smuggling[len + 1], smuggling[len + 2], smuggling[len + 3], smuggling[len + 4] };
fixed (char* cs = chars)
{
var bs = (byte*)cs;
var handleValue = BitConverter.ToInt64(new[] { bs[0], bs[1], bs[2], bs[3], bs[4], bs[5], bs[6], bs[7] }, 0);
var budgie = (ThingIWantToSmuggleWithData)GCHandle.FromIntPtr(new IntPtr(handleValue)).Target;
budgie.Hello();
var actualData = budgie.RealData;
Console.WriteLine("was passed " + actualData);
}
}
}
}
private unsafe static void Smuggle6()
{
var handle = GCHandle.Alloc(new ThingIWantToSmuggleWithData(54321));
try
{
var x = "<the real string>";
Console.WriteLine("The string is: " + x + ".");
fixed (byte* chars = BitConverter.GetBytes(GCHandle.ToIntPtr(handle).ToInt64()))
{
x += "\0" + new string((char*)chars, 0, 4); // 4 chars = 1 long
}
fixed (char* c = x)
{
// Tell the string it's not as long as it thinks it is:
var length = ((int*)c) - 1;
*length -= 5;
try
{
Console.WriteLine("The string is (still): " + x + ".");
new Untouchable<WithAString>(new UseGCHandleHiddenInString())
.DoIt(new WithAString { Data = x });
}
finally
{
// better restore this, or else.
*length += 5;
}
}
}
finally
{
handle.Free();
}
}
// 7)
// Here's an alternate solution to the "sealed base class" problem, when
// the base class isn't sealed but has a private/internal constructor:
class PrivateBase
{
private PrivateBase()
{
}
}
class UseZombieObject : IDoIt<PrivateBase>
{
public void DoIt(PrivateBase arg)
{
var zombie = (EvilDerived)arg;
zombie.Smuggling.Hello();
}
}
class EvilDerived : PrivateBase
{
public EvilDerived() : this(Asplode()) { }
private EvilDerived(int i) : this() { }
public ThingIWantToSmuggle Smuggling;
private static int Asplode()
{
throw new StackOverflowException();
}
public static EvilDerived Instance;
~EvilDerived() { Instance = this; }
}
private static void Smuggle7(ThingIWantToSmuggle budgie)
{
try
{
new EvilDerived();
}
catch (StackOverflowException)
{
// ^ we know we threw this or we wouldn't be able to catch it
while (EvilDerived.Instance == null)
{
// NB: don't try this with the debugger attached
GC.Collect();
GC.WaitForPendingFinalizers();
}
var derived = EvilDerived.Instance;
derived.Smuggling = budgie;
new Untouchable<PrivateBase>(new UseZombieObject()).DoIt(derived);
}
}
// 8)
// Or, if you have the right security permissions, you can simply:
class LessEvilButStillPrettyEvil : PrivateBase
{
private LessEvilButStillPrettyEvil() : this(1) { }
private LessEvilButStillPrettyEvil(int i) : this() { }
public ThingIWantToSmuggle Instance;
}
class UseLessEvilObject : IDoIt<PrivateBase>
{
public void DoIt(PrivateBase arg)
{
var zombie = (LessEvilButStillPrettyEvil)arg;
zombie.Instance.Hello();
}
}
private static void Smuggle8(ThingIWantToSmuggle budgie)
{
var easyDerived = (LessEvilButStillPrettyEvil)FormatterServices.GetUninitializedObject(typeof(LessEvilButStillPrettyEvil));
easyDerived.Instance = budgie;
new Untouchable<PrivateBase>(new UseLessEvilObject()).DoIt(easyDerived);
}
// 9)
// Another fun way to pass data is using Thread.Abort:
class UseThreadAbort : IDoIt<Structy>
{
public void DoIt(Structy arg)
{
try
{
Thread.Sleep(Timeout.Infinite);
}
catch (ThreadAbortException ex)
{
var budgie = (ThingIWantToSmuggle)ex.ExceptionState;
budgie.Hello();
Thread.ResetAbort(); // NB: this must happen after you access ExceptionState
}
}
}
static void Smuggle9(ThingIWantToSmuggle budgie)
{
var outer = new Untouchable<Structy>(new UseThreadAbort());
var t = new Thread(() => outer.DoIt(new Structy()));
t.Start();
Thread.Sleep(1);
t.Abort(budgie);
t.Join();
}
// 10)
// Alternately, if we're allowed free reign with the string,
// and we have a struct to pass... we can fudge the runtime type:
class JustDoIt : IDoIt<WithAString>
{
public static int StructIWantToSmuggleTypeHandle;
public unsafe void DoIt(WithAString arg)
{
// overwrite the type handle
// this is just about the most dangerous thing I can think of doing in .NET
var budgie = (object)arg.Data;
var handle = GCHandle.Alloc(budgie, GCHandleType.Pinned);
try
{
Console.WriteLine("Before: " + budgie.GetType());
// typehandle is the 4 bytes before the start of the object
var typeHandle = ((int*)handle.AddrOfPinnedObject()) - 2;
// save the old one
var oldTypeHandle = *typeHandle;
// overwrite the typehandle
*typeHandle = StructIWantToSmuggleTypeHandle;
Console.WriteLine("After: " + budgie.GetType());
// now we can unbox it to the type we want
var unboxed = (StructIWantToSmuggle)handle.Target;
unboxed.Hello();
// restore old type handle
*typeHandle = oldTypeHandle;
}
finally
{
handle.Free();
}
}
}
struct StructIWantToSmuggle
{
public int Value;
public void Hello()
{
Console.WriteLine("Hello, " + Value);
}
}
unsafe static void Smuggle10()
{
var s = new StructIWantToSmuggle { Value = 6789 };
var size = Marshal.SizeOf(s);
var str = new string('x', size);
fixed (char *c = str)
{
var handle = GCHandle.Alloc(s, GCHandleType.Pinned);
try
{
var source = (byte*)handle.AddrOfPinnedObject();
var dest = (byte*)c;
JustDoIt.StructIWantToSmuggleTypeHandle = *(((int*)source) - 1);
for (int i = 0; i < size; ++i)
{
// string data starts 4 bytes earlier than where the pointer is
dest[i-4] = source[i];
}
new Untouchable<WithAString>(new JustDoIt())
.DoIt(new WithAString { Data = str });
// restore length just in case
*(((int*)dest) - 1) = size;
}
finally
{
handle.Free();
}
}
}
// Run them all:
public static void Main()
{
var budgie = new ThingIWantToSmuggle(0);
Smuggle1(budgie);
Smuggle2(budgie);
Smuggle3(budgie);
Smuggle4(budgie);
Smuggle5();
Smuggle6();
Smuggle7(budgie);
Smuggle8(budgie);
Smuggle9(budgie);
Smuggle10();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment