Goal:
The goal of this snippet is to create a modified version, at runtime, of an executable to prevent it from blocking the thread.
Problem:
I am using Mono.Cecil to perform the patch; however, something about the way it's recompiled is causing ANY modified method to throw an InvalidProgramException
. To prove that the program is not invalid, I went ahead and rewrote it using the modification I want in C# and recompiled it. Manually compiling the program with the changes worked perfectly; however, the IL of the patched methods are the EXACT same except for the file alignment.
Code:
private static void PatchStartup(TypeDefinition definition)
{
ILProcessor il = definition.Methods.First(x => x.Name == "Start").Body.GetILProcessor();
int index = default(int);
for (int i = il.Body.Instructions.Count - 1; i != 0; i--)
if (il.Body.Instructions[i].OpCode == OpCodes.Newobj)
{
index = i + 2;
break;
}
List<Instruction> removedInstructions = new List<Instruction>();
for (int i = index; i < il.Body.Instructions.Count; i++)
removedInstructions.Add(il.Body.Instructions[i]);
foreach (var i in removedInstructions) il.Remove(i);
il.InsertAfter(il.Body.Instructions[il.Body.Instructions.Count - 1], il.Create(OpCodes.Ret));
il.Body.Variables.Clear();
il.Body.InitLocals = false;
il.Body.Optimize();
il.Body.OptimizeMacros();
}
Original IL:
// Token: 0x06000029 RID: 41 RVA: 0x00002DE0 File Offset: 0x00000FE0
.method public hidebysig static
void Start (
string[] args
) cil managed
{
// Header Size: 12 bytes
// Code Size: 258 (0x102) bytes
// LocalVarSig Token: 0x1100000C RID: 12
.maxstack 3
.locals init (
[0] string,
[1] class [mscorlib]System.Exception
)
/* 0x00000FEC 726E020070 */ IL_0000: ldstr "Eco Server {0}"
/* 0x00000FF1 286300000A */ IL_0005: call string [Eco.Shared]Eco.Shared.Authentication.EcoVersion::get_Version()
/* 0x00000FF6 284700000A */ IL_000A: call string [mscorlib]System.String::Format(string, object)
/* 0x00000FFB 286400000A */ IL_000F: call void [Eco.Shared]Eco.Shared.Utils.Log::WriteLine(string)
/* 0x00001000 02 */ IL_0014: ldarg.0
/* 0x00001001 286500000A */ IL_0015: call void [Eco.Core]Eco.Core.Utils.CommandLine::SetCmdLine(string[])
/* 0x00001006 14 */ IL_001A: ldnull
/* 0x00001007 FE062D00002B */ IL_001B: ldftn !!0 [Eco.Core]Eco.Core.Serialization.SerializationUtils::DeserializeJson<class [Eco.Shared]Eco.Shared.Authentication.LoginSession>(string)
/* 0x0000100D 736700000A */ IL_0021: newobj instance void class [mscorlib]System.Func`2<string, class [Eco.Shared]Eco.Shared.Authentication.LoginSession>::.ctor(object, native int)
/* 0x00001012 286800000A */ IL_0026: call void class [Eco.Shared]Eco.Shared.Serialization.JsonUtil`1<class [Eco.Shared]Eco.Shared.Authentication.LoginSession>::set_Deserialize(class [mscorlib]System.Func`2<string, !0>)
/* 0x00001017 14 */ IL_002B: ldnull
/* 0x00001018 FE062E00002B */ IL_002C: ldftn !!0 [Eco.Core]Eco.Core.Serialization.SerializationUtils::DeserializeJson<class [Eco.Shared]Eco.Shared.Authentication.VerifyResult>(string)
/* 0x0000101E 736900000A */ IL_0032: newobj instance void class [mscorlib]System.Func`2<string, class [Eco.Shared]Eco.Shared.Authentication.VerifyResult>::.ctor(object, native int)
/* 0x00001023 286A00000A */ IL_0037: call void class [Eco.Shared]Eco.Shared.Serialization.JsonUtil`1<class [Eco.Shared]Eco.Shared.Authentication.VerifyResult>::set_Deserialize(class [mscorlib]System.Func`2<string, !0>)
/* 0x00001028 14 */ IL_003C: ldnull
/* 0x00001029 FE062F00002B */ IL_003D: ldftn !!0 [Eco.Core]Eco.Core.Serialization.SerializationUtils::DeserializeJson<class [Eco.Shared]Eco.Shared.Services.ServerListing[]>(string)
/* 0x0000102F 736B00000A */ IL_0043: newobj instance void class [mscorlib]System.Func`2<string, class [Eco.Shared]Eco.Shared.Services.ServerListing[]>::.ctor(object, native int)
/* 0x00001034 286C00000A */ IL_0048: call void class [Eco.Shared]Eco.Shared.Serialization.JsonUtil`1<class [Eco.Shared]Eco.Shared.Services.ServerListing[]>::set_Deserialize(class [mscorlib]System.Func`2<string, !0>)
/* 0x00001039 14 */ IL_004D: ldnull
/* 0x0000103A FE063000002B */ IL_004E: ldftn !!0 [Eco.Core]Eco.Core.Serialization.SerializationUtils::DeserializeJson<class [Eco.Shared]Eco.Shared.Services.ServerInfo>(string)
/* 0x00001040 736D00000A */ IL_0054: newobj instance void class [mscorlib]System.Func`2<string, class [Eco.Shared]Eco.Shared.Services.ServerInfo>::.ctor(object, native int)
/* 0x00001045 286E00000A */ IL_0059: call void class [Eco.Shared]Eco.Shared.Serialization.JsonUtil`1<class [Eco.Shared]Eco.Shared.Services.ServerInfo>::set_Deserialize(class [mscorlib]System.Func`2<string, !0>)
/* 0x0000104A 16 */ IL_005E: ldc.i4.0
/* 0x0000104B 286F00000A */ IL_005F: call void [Eco.Shared]Eco.Shared.Utils.StreamPool::Initialize(bool)
/* 0x00001050 D080000001 */ IL_0064: ldtoken [Eco.Core]Eco.Core.Utils.ThreadSafeDictionary`2
/* 0x00001055 280E00000A */ IL_0069: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
/* 0x0000105A 287000000A */ IL_006E: call void [Eco.Shared]Eco.Shared.Networking.RPCManager::Initialize(class [mscorlib]System.Type)
/* 0x0000105F 14 */ IL_0073: ldnull
/* 0x00001060 FE062B000006 */ IL_0074: ldftn void Eco.Server.Startup::ApplicationThreadException(object, class [System]System.Threading.ThreadExceptionEventArgs)
/* 0x00001066 737100000A */ IL_007A: newobj instance void [System]System.Threading.ThreadExceptionEventHandler::.ctor(object, native int)
/* 0x0000106B 287200000A */ IL_007F: call void [System.Windows.Forms]System.Windows.Forms.Application::add_ThreadException(class [System]System.Threading.ThreadExceptionEventHandler)
/* 0x00001070 285600000A */ IL_0084: call class [mscorlib]System.AppDomain [mscorlib]System.AppDomain::get_CurrentDomain()
/* 0x00001075 14 */ IL_0089: ldnull
/* 0x00001076 FE062A000006 */ IL_008A: ldftn void Eco.Server.Startup::CurrentDomainUnhandledException(object, class [mscorlib]System.UnhandledExceptionEventArgs)
/* 0x0000107C 737300000A */ IL_0090: newobj instance void [mscorlib]System.UnhandledExceptionEventHandler::.ctor(object, native int)
/* 0x00001081 6F7400000A */ IL_0095: callvirt instance void [mscorlib]System.AppDomain::add_UnhandledException(class [mscorlib]System.UnhandledExceptionEventHandler)
/* 0x00001086 2828000006 */ IL_009A: call bool Eco.Server.Startup::CheckWorkingDirectory()
/* 0x0000108B 2D01 */ IL_009F: brtrue.s IL_00A2
/* 0x0000108D 2A */ IL_00A1: ret
/* 0x0000108E 2827000006 */ IL_00A2: call void Eco.Server.Startup::RegisterCustomTypes()
/* 0x00001093 7304000006 */ IL_00A7: newobj instance void Eco.Server.PluginManager::.ctor()
/* 0x00001098 26 */ IL_00AC: pop
.try
{
/* 0x00001099 7E7500000A */ IL_00AD: ldsfld string [mscorlib]System.String::Empty
/* 0x0000109E 0A */ IL_00B2: stloc.0
/* 0x0000109F 2B06 */ IL_00B3: br.s IL_00BB
// loop start (head: IL_00BB)
/* 0x000010A1 287600000A */ IL_00B5: call string [mscorlib]System.Console::ReadLine()
/* 0x000010A6 0A */ IL_00BA: stloc.0
/* 0x000010A7 06 */ IL_00BB: ldloc.0
/* 0x000010A8 728C020070 */ IL_00BC: ldstr "exit"
/* 0x000010AD 6F7700000A */ IL_00C1: callvirt instance bool [mscorlib]System.String::Contains(string)
/* 0x000010B2 2CED */ IL_00C6: brfalse.s IL_00B5
// end loop
/* 0x000010B4 7296020070 */ IL_00C8: ldstr "Saving..."
/* 0x000010B9 287800000A */ IL_00CD: call void [mscorlib]System.Console::WriteLine(string)
/* 0x000010BE 287900000A */ IL_00D2: call void [Eco.Core]Eco.Core.Plugins.StorageManager::SaveAndFlush()
/* 0x000010C3 72AA020070 */ IL_00D7: ldstr "Done"
/* 0x000010C8 287800000A */ IL_00DC: call void [mscorlib]System.Console::WriteLine(string)
/* 0x000010CD 16 */ IL_00E1: ldc.i4.0
/* 0x000010CE 287A00000A */ IL_00E2: call void [mscorlib]System.Environment::Exit(int32)
/* 0x000010D3 DE18 */ IL_00E7: leave.s IL_0101
} // end .try
catch [mscorlib]System.Exception
{
/* 0x000010D5 0B */ IL_00E9: stloc.1
/* 0x000010D6 72B4020070 */ IL_00EA: ldstr "Caught an exception checking for console input, console input disabled. (probably safe to ignore)"
/* 0x000010DB 287800000A */ IL_00EF: call void [mscorlib]System.Console::WriteLine(string)
/* 0x000010E0 07 */ IL_00F4: ldloc.1
/* 0x000010E1 6F7B00000A */ IL_00F5: callvirt instance string [mscorlib]System.Exception::get_Message()
/* 0x000010E6 287800000A */ IL_00FA: call void [mscorlib]System.Console::WriteLine(string)
/* 0x000010EB DE00 */ IL_00FF: leave.s IL_0101
} // end handler
/* 0x000010ED 2A */ IL_0101: ret
} // end of method Startup::Start
Runtime-Modified IL:
// Token: 0x06000029 RID: 41 RVA: 0x00002DE0 File Offset: 0x00000FE0
.method public hidebysig static
void Start (
string[] args
) cil managed
{
// Header Size: 12 bytes
// Code Size: 174 (0xAE) bytes
.maxstack 3
/* 0x00000FEC 726E020070 */ IL_0000: ldstr "Eco Server {0}"
/* 0x00000FF1 286300000A */ IL_0005: call string [Eco.Shared]Eco.Shared.Authentication.EcoVersion::get_Version()
/* 0x00000FF6 284700000A */ IL_000A: call string [mscorlib]System.String::Format(string, object)
/* 0x00000FFB 286400000A */ IL_000F: call void [Eco.Shared]Eco.Shared.Utils.Log::WriteLine(string)
/* 0x00001000 02 */ IL_0014: ldarg.0
/* 0x00001001 286500000A */ IL_0015: call void [Eco.Core]Eco.Core.Utils.CommandLine::SetCmdLine(string[])
/* 0x00001006 14 */ IL_001A: ldnull
/* 0x00001007 FE062D00002B */ IL_001B: ldftn !!0 [Eco.Core]Eco.Core.Serialization.SerializationUtils::DeserializeJson<class [Eco.Shared]Eco.Shared.Authentication.LoginSession>(string)
/* 0x0000100D 736700000A */ IL_0021: newobj instance void class [mscorlib]System.Func`2<string, class [Eco.Shared]Eco.Shared.Authentication.LoginSession>::.ctor(object, native int)
/* 0x00001012 286800000A */ IL_0026: call void class [Eco.Shared]Eco.Shared.Serialization.JsonUtil`1<class [Eco.Shared]Eco.Shared.Authentication.LoginSession>::set_Deserialize(class [mscorlib]System.Func`2<string, !0>)
/* 0x00001017 14 */ IL_002B: ldnull
/* 0x00001018 FE062E00002B */ IL_002C: ldftn !!0 [Eco.Core]Eco.Core.Serialization.SerializationUtils::DeserializeJson<class [Eco.Shared]Eco.Shared.Authentication.VerifyResult>(string)
/* 0x0000101E 736900000A */ IL_0032: newobj instance void class [mscorlib]System.Func`2<string, class [Eco.Shared]Eco.Shared.Authentication.VerifyResult>::.ctor(object, native int)
/* 0x00001023 286A00000A */ IL_0037: call void class [Eco.Shared]Eco.Shared.Serialization.JsonUtil`1<class [Eco.Shared]Eco.Shared.Authentication.VerifyResult>::set_Deserialize(class [mscorlib]System.Func`2<string, !0>)
/* 0x00001028 14 */ IL_003C: ldnull
/* 0x00001029 FE062F00002B */ IL_003D: ldftn !!0 [Eco.Core]Eco.Core.Serialization.SerializationUtils::DeserializeJson<class [Eco.Shared]Eco.Shared.Services.ServerListing[]>(string)
/* 0x0000102F 736B00000A */ IL_0043: newobj instance void class [mscorlib]System.Func`2<string, class [Eco.Shared]Eco.Shared.Services.ServerListing[]>::.ctor(object, native int)
/* 0x00001034 286C00000A */ IL_0048: call void class [Eco.Shared]Eco.Shared.Serialization.JsonUtil`1<class [Eco.Shared]Eco.Shared.Services.ServerListing[]>::set_Deserialize(class [mscorlib]System.Func`2<string, !0>)
/* 0x00001039 14 */ IL_004D: ldnull
/* 0x0000103A FE063000002B */ IL_004E: ldftn !!0 [Eco.Core]Eco.Core.Serialization.SerializationUtils::DeserializeJson<class [Eco.Shared]Eco.Shared.Services.ServerInfo>(string)
/* 0x00001040 736D00000A */ IL_0054: newobj instance void class [mscorlib]System.Func`2<string, class [Eco.Shared]Eco.Shared.Services.ServerInfo>::.ctor(object, native int)
/* 0x00001045 286E00000A */ IL_0059: call void class [Eco.Shared]Eco.Shared.Serialization.JsonUtil`1<class [Eco.Shared]Eco.Shared.Services.ServerInfo>::set_Deserialize(class [mscorlib]System.Func`2<string, !0>)
/* 0x0000104A 16 */ IL_005E: ldc.i4.0
/* 0x0000104B 286F00000A */ IL_005F: call void [Eco.Shared]Eco.Shared.Utils.StreamPool::Initialize(bool)
/* 0x00001050 D07F000001 */ IL_0064: ldtoken [Eco.Core]Eco.Core.Utils.ThreadSafeDictionary`2
/* 0x00001055 280E00000A */ IL_0069: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
/* 0x0000105A 287000000A */ IL_006E: call void [Eco.Shared]Eco.Shared.Networking.RPCManager::Initialize(class [mscorlib]System.Type)
/* 0x0000105F 14 */ IL_0073: ldnull
/* 0x00001060 FE062B000006 */ IL_0074: ldftn void Eco.Server.Startup::ApplicationThreadException(object, class [System]System.Threading.ThreadExceptionEventArgs)
/* 0x00001066 737100000A */ IL_007A: newobj instance void [System]System.Threading.ThreadExceptionEventHandler::.ctor(object, native int)
/* 0x0000106B 287200000A */ IL_007F: call void [System.Windows.Forms]System.Windows.Forms.Application::add_ThreadException(class [System]System.Threading.ThreadExceptionEventHandler)
/* 0x00001070 285600000A */ IL_0084: call class [mscorlib]System.AppDomain [mscorlib]System.AppDomain::get_CurrentDomain()
/* 0x00001075 14 */ IL_0089: ldnull
/* 0x00001076 FE062A000006 */ IL_008A: ldftn void Eco.Server.Startup::CurrentDomainUnhandledException(object, class [mscorlib]System.UnhandledExceptionEventArgs)
/* 0x0000107C 737300000A */ IL_0090: newobj instance void [mscorlib]System.UnhandledExceptionEventHandler::.ctor(object, native int)
/* 0x00001081 6F7400000A */ IL_0095: callvirt instance void [mscorlib]System.AppDomain::add_UnhandledException(class [mscorlib]System.UnhandledExceptionEventHandler)
/* 0x00001086 2828000006 */ IL_009A: call bool Eco.Server.Startup::CheckWorkingDirectory()
/* 0x0000108B 2D01 */ IL_009F: brtrue.s IL_00A2
/* 0x0000108D 2A */ IL_00A1: ret
/* 0x0000108E 2827000006 */ IL_00A2: call void Eco.Server.Startup::RegisterCustomTypes()
/* 0x00001093 7304000006 */ IL_00A7: newobj instance void Eco.Server.PluginManager::.ctor()
/* 0x00001098 26 */ IL_00AC: pop
/* 0x00001099 2A */ IL_00AD: ret
} // end of method Startup::Start
Manually Modified IL:
// Token: 0x06000029 RID: 41 RVA: 0x000030A8 File Offset: 0x000012A8
.method public hidebysig static
void Start (
string[] args
) cil managed
{
// Header Size: 12 bytes
// Code Size: 174 (0xAE) bytes
.maxstack 3
/* 0x000012B4 726E020070 */ IL_0000: ldstr "Eco Server {0}"
/* 0x000012B9 287D00000A */ IL_0005: call string [Eco.Shared]Eco.Shared.Authentication.EcoVersion::get_Version()
/* 0x000012BE 286200000A */ IL_000A: call string [mscorlib]System.String::Format(string, object)
/* 0x000012C3 287E00000A */ IL_000F: call void [Eco.Shared]Eco.Shared.Utils.Log::WriteLine(string)
/* 0x000012C8 02 */ IL_0014: ldarg.0
/* 0x000012C9 287F00000A */ IL_0015: call void [Eco.Core]Eco.Core.Utils.CommandLine::SetCmdLine(string[])
/* 0x000012CE 14 */ IL_001A: ldnull
/* 0x000012CF FE062D00002B */ IL_001B: ldftn !!0 [Eco.Core]Eco.Core.Serialization.SerializationUtils::DeserializeJson<class [Eco.Shared]Eco.Shared.Authentication.LoginSession>(string)
/* 0x000012D5 738100000A */ IL_0021: newobj instance void class [mscorlib]System.Func`2<string, class [Eco.Shared]Eco.Shared.Authentication.LoginSession>::.ctor(object, native int)
/* 0x000012DA 288200000A */ IL_0026: call void class [Eco.Shared]Eco.Shared.Serialization.JsonUtil`1<class [Eco.Shared]Eco.Shared.Authentication.LoginSession>::set_Deserialize(class [mscorlib]System.Func`2<string, !0>)
/* 0x000012DF 14 */ IL_002B: ldnull
/* 0x000012E0 FE062E00002B */ IL_002C: ldftn !!0 [Eco.Core]Eco.Core.Serialization.SerializationUtils::DeserializeJson<class [Eco.Shared]Eco.Shared.Authentication.VerifyResult>(string)
/* 0x000012E6 738300000A */ IL_0032: newobj instance void class [mscorlib]System.Func`2<string, class [Eco.Shared]Eco.Shared.Authentication.VerifyResult>::.ctor(object, native int)
/* 0x000012EB 288400000A */ IL_0037: call void class [Eco.Shared]Eco.Shared.Serialization.JsonUtil`1<class [Eco.Shared]Eco.Shared.Authentication.VerifyResult>::set_Deserialize(class [mscorlib]System.Func`2<string, !0>)
/* 0x000012F0 14 */ IL_003C: ldnull
/* 0x000012F1 FE062F00002B */ IL_003D: ldftn !!0 [Eco.Core]Eco.Core.Serialization.SerializationUtils::DeserializeJson<class [Eco.Shared]Eco.Shared.Services.ServerListing[]>(string)
/* 0x000012F7 738500000A */ IL_0043: newobj instance void class [mscorlib]System.Func`2<string, class [Eco.Shared]Eco.Shared.Services.ServerListing[]>::.ctor(object, native int)
/* 0x000012FC 288600000A */ IL_0048: call void class [Eco.Shared]Eco.Shared.Serialization.JsonUtil`1<class [Eco.Shared]Eco.Shared.Services.ServerListing[]>::set_Deserialize(class [mscorlib]System.Func`2<string, !0>)
/* 0x00001301 14 */ IL_004D: ldnull
/* 0x00001302 FE063000002B */ IL_004E: ldftn !!0 [Eco.Core]Eco.Core.Serialization.SerializationUtils::DeserializeJson<class [Eco.Shared]Eco.Shared.Services.ServerInfo>(string)
/* 0x00001308 738700000A */ IL_0054: newobj instance void class [mscorlib]System.Func`2<string, class [Eco.Shared]Eco.Shared.Services.ServerInfo>::.ctor(object, native int)
/* 0x0000130D 288800000A */ IL_0059: call void class [Eco.Shared]Eco.Shared.Serialization.JsonUtil`1<class [Eco.Shared]Eco.Shared.Services.ServerInfo>::set_Deserialize(class [mscorlib]System.Func`2<string, !0>)
/* 0x00001312 16 */ IL_005E: ldc.i4.0
/* 0x00001313 288900000A */ IL_005F: call void [Eco.Shared]Eco.Shared.Utils.StreamPool::Initialize(bool)
/* 0x00001318 D0BC000001 */ IL_0064: ldtoken [Eco.Core]Eco.Core.Utils.ThreadSafeDictionary`2
/* 0x0000131D 282C00000A */ IL_0069: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
/* 0x00001322 288A00000A */ IL_006E: call void [Eco.Shared]Eco.Shared.Networking.RPCManager::Initialize(class [mscorlib]System.Type)
/* 0x00001327 14 */ IL_0073: ldnull
/* 0x00001328 FE062B000006 */ IL_0074: ldftn void Eco.Server.Startup::ApplicationThreadException(object, class [System]System.Threading.ThreadExceptionEventArgs)
/* 0x0000132E 738B00000A */ IL_007A: newobj instance void [System]System.Threading.ThreadExceptionEventHandler::.ctor(object, native int)
/* 0x00001333 288C00000A */ IL_007F: call void [System.Windows.Forms]System.Windows.Forms.Application::add_ThreadException(class [System]System.Threading.ThreadExceptionEventHandler)
/* 0x00001338 287000000A */ IL_0084: call class [mscorlib]System.AppDomain [mscorlib]System.AppDomain::get_CurrentDomain()
/* 0x0000133D 14 */ IL_0089: ldnull
/* 0x0000133E FE062A000006 */ IL_008A: ldftn void Eco.Server.Startup::CurrentDomainUnhandledException(object, class [mscorlib]System.UnhandledExceptionEventArgs)
/* 0x00001344 738D00000A */ IL_0090: newobj instance void [mscorlib]System.UnhandledExceptionEventHandler::.ctor(object, native int)
/* 0x00001349 6F8E00000A */ IL_0095: callvirt instance void [mscorlib]System.AppDomain::add_UnhandledException(class [mscorlib]System.UnhandledExceptionEventHandler)
/* 0x0000134E 2828000006 */ IL_009A: call bool Eco.Server.Startup::CheckWorkingDirectory()
/* 0x00001353 2D01 */ IL_009F: brtrue.s IL_00A2
/* 0x00001355 2A */ IL_00A1: ret
/* 0x00001356 2827000006 */ IL_00A2: call void Eco.Server.Startup::RegisterCustomTypes()
/* 0x0000135B 7304000006 */ IL_00A7: newobj instance void Eco.Server.PluginManager::.ctor()
/* 0x00001360 26 */ IL_00AC: pop
/* 0x00001361 2A */ IL_00AD: ret
} // end of method Startup::Start
Thanks to thehennyy for posting the comment that lead to a solution.
The problem was that I had not cleared the error-handlers after removing the try-catch block.
For anyone else having this obscure problem, add the following to your code to remove the handler before writing the assembly to the disk or a Stream
.
var il = ...;
il.Body.ExceptionHandlers.Remove(ExceptionHandler handler);
User contributions licensed under CC BY-SA 3.0