0

I'm trying to merge two methods together (one method from my project, one from a dll) and write them to a dll. The original code looks like this:

 .method public hidebysig static class Storage.DataMap 
    Create() cil managed 
  {
    .maxstack 8

    // [22 7 - 22 70]
    IL_0000: call         unmanaged stdcall native int Storage.DataMapPlugin::DataMap_CreateEmptyDataMap()
    IL_0005: newobj       instance void Storage.DataMap::.ctor(native int)
    IL_000a: ret          

  } // end of method DataMap::Create

I want to add a simple Console.WriteLine to this code, I end up with the following: (From decompiler)

  .method public hidebysig static class Storage.DataMap 
    Create() cil managed 
  {
    .maxstack 8

    // [22 7 - 22 44]
    IL_0000: ldstr        "Hello World"
    IL_0005: call         void [mscorlib]System.Console::WriteLine(string)

    // [23 7 - 23 70]
    IL_000a: call         unmanaged stdcall native int Storage.DataMapPlugin::DataMap_CreateEmptyDataMap()
    IL_000f: newobj       instance void Storage.DataMap::.ctor(native int)
    IL_0014: ret          

  } // end of method DataMap::Create

My decompiler is not able to decompile this (dotPeek) so I am assuming that the IL I generated is invalid. However, I'm not sure what's wrong with my generated IL.

I'm using dnlib with C# to get both the method's IL code, then Concat both blocks of code together (I stripped the nop and ret statements (except for that last one)). To recompile the code I use the ModuleDef#Write(String) function, I've had success with this when writing my own IL statements, but not when merging already existing statements.

(The following IL is all printed from the console, not from decompiler)
Original part 1 IL:

IL_0000: nop
IL_0001: ldstr "Hello World"
IL_0006: call System.Void System.Console::WriteLine(System.String)
IL_000B: nop
IL_000C: ret

Original part 2 IL:

IL_0000: call System.IntPtr Storage.DataMapPlugin::DataMap_CreateEmptyDataMap()
IL_0005: newobj System.Void Storage.DataMap::.ctor(System.IntPtr)
IL_000A: ret

Merged IL:

IL_0001: ldstr "Hello World"
IL_0006: call System.Void System.Console::WriteLine(System.String)
IL_0000: call System.IntPtr Storage.DataMapPlugin::DataMap_CreateEmptyDataMap()
IL_0005: newobj System.Void Storage.DataMap::.ctor(System.IntPtr)
IL_000A: ret
Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
bramhaag
  • 628
  • 1
  • 9
  • 24
  • C# too high level? :-) In any case, I'd have to wonder why that `nop` was there in the first place. It seems an easy candidate to remove, both for you *and* the compiler, so perhaps it's needed for some reason. – paxdiablo Dec 29 '17 at 00:45
  • It does not appear to make a difference, the first thing I tried when I was the decompiler error was to remove `nop`, but that did not help. @paxdiablo – bramhaag Dec 29 '17 at 00:48
  • Actually, that merged IL has the labels all messed up (1,6,0,5,a). Could that be a problem? – paxdiablo Dec 29 '17 at 00:52
  • There is not enough context in your question. But for sure, the "Merged IL" you posted doesn't look correct to me, as your byte offsets are all wrong. Those offsets should correctly reflect the actual byte offsets for the IL instructions in the code. Instead, first you start at offset `0001` instead of `0000` as any method ought to, and second you don't adjust the instructions from your second method, restarting the offsets again at `0000`. Whether those matter or not depend on what you're using to recompile the IL after you've merged it. Please fix your question so it has enough information. – Peter Duniho Dec 29 '17 at 00:53
  • @PeterDuniho I'm using dnlib with the `ModuleDef#Write(String)` function to recompile the IL after merging. I'll clarify this in the post. – bramhaag Dec 29 '17 at 00:57
  • 2
    If you are using dnlib, you need to include in your question a good [mcve] showing exactly how you use it. All you've shown above is _data_, which is next to useless when asking people to help you to debug _code_. – Peter Duniho Dec 29 '17 at 01:02
  • 2
    If the IL is invalid then peverify should be able to tell you what is wrong. Honestly you really need to show the code that you are using to do the merge. One possible issue is that you are attempting to load a string which must be defined in the module's metadata. Are you properly updating the metadata of the target assembly to include the new metadata token? You need change both the IL of the method and the target module's metadata. – Mike Zboray Dec 29 '17 at 01:28

1 Answers1

1

I fixed my own problem. As multiple individuals correctly pointed out in the comments, the IL is correct. The issue wasn't related to my code or the generated IL, but to the decompiler, dotPeek.

What happend was the following: I generated the DLL for the first time, but the IL was wrong (it had 2 return statements), decompiled it with dotPeek and saw that the IL was malformed. I did this again but with the correct IL (only 1 return statement) and deleted the old DLL, renamed the new DLL to the old, deleted, one. DotPeek however did not like this, it would show me the updated IL, but not the updated code.

After many restarts and removing/re-adding the assembly I found out this was the case. dnSpy seems to work much better than dotPeek so I'll be using that in the future.

bramhaag
  • 628
  • 1
  • 9
  • 24