Remotesoft's Salamander 1.1.6.0 (Native Compiling)

The version (taken from the rscoree.dll) of the Salamander is some months old: I didn't bother to try a new version. However, the things I'm going to say are still valid.

This is no reversing article (you do know reversing is illegal, right?). I'm not going to debug, disassemble or do any other thing to other people's code. The only code I'm going to show is my own code. That's legal, alright?

Let's spend two words about this protection. It's quite famous because on the homepage it says explicitly that the protection compiles MSIL into native x86 code. Also, on a lot of forums it's being advised because of this feature. To obtain a demo of this protection isn't so easy: you have to do it through mail exchange. The protected exe I have (a simple hello world .NET windows application) is provided with the protection's dll: rscoree.dll. I haven't touched the dll, because it would violate the rights of Remotesoft.

If I open my protected executable with ildasm I can see that there's no way to disassemble the original methods, just as Remotesoft's homepage says. In fact, the result of this operation is something like this:

.method private hidebysig static void  Main() cil managed noinlining
{
  .entrypoint
  .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 )
  // Code size       1 (0x1)
  .maxstack  8
  IL_0000:  ret
} // end of method Form1::Main

When I looked at the exe with the CFF Explorer I noticed that it contained a section more than regular .NET assemblies. Moreover, the #US (Unicode Strings used in the application) stream was empty.

I executed the application, opened the process with WinHex, went to the additional section of data and saw this:

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

004080C0   00 0F 62 00 75 00 74 00  74 00 6F 00 6E 00 31 00   ..b.u.t.t.o.n.1.
004080D0   00 09 54 00 65 00 73 00  74 00 00 0B 46 00 6F 00   ..T.e.s.t...F.o.
004080E0   72 00 6D 00 31 00 00 05  4F 00 6B 00 00 00 00 00   r.m.1...O.k.....

That's my original #US stream... Strange enough.  A few bytes after the original #US stream I saw this:

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

004080F0   13 30 01 00 0B 00 00 00  00 00 00 00 73 06 00 00   .0..........s...
00408100   06 28 24 00 00 0A 2A 00  00 00 00 00 00 00 00 00   .($...*.........

Uhm 13 30 01... Seems to be the first dword of a fat header method, the next dword is the size of the code and the signature dword is null. The code ends with the return opcode 2A. I linked the RVA of a method of my exe to this code and opened ildasm. The disassembled code is:

.method private hidebysig static void  Main() cil managed noinlining
// SIG: 00 00 01
{
  .entrypoint
  .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 )
  // Method begins at RVA 0x80f0
  // Code size       11 (0xb)
  .maxstack  1
  IL_0000:  /* 73   | (06)000006       */ newobj     instance void HelloWorld.Form1::.ctor()
  IL_0005:  /* 28   | (0A)000024       */ call       void [System.Windows.Forms]System.Windows.Forms.Application::Run(class [System.Windows.Forms]System.Windows.Forms.Form)
  IL_000a:  /* 2A   |                  */ ret
} // end of method Form1::Main

Ok this is the code of my original Main. That's VERY strange. I thought: maybe I find the other methods as well... But the section didn't contain other code... I had something in mind and decided (of course) to give it a try: I was already very skeptical about the native code. I took the biggest method of my executable:

.method private hidebysig instance void  InitializeComponent() cil managed
// SIG: 20 00 01
{
  // Method begins at RVA 0x209c
  // Code size       186 (0xba)
  .maxstack  4
  IL_0000:  /* 02   |                  */ ldarg.0
  IL_0001:  /* 73   | (0A)000010       */ newobj     instance void [System.Windows.Forms]System.Windows.Forms.Button::.ctor()
  IL_0006:  /* 7D   | (04)000001       */ stfld      class [System.Windows.Forms]System.Windows.Forms.Button HelloWorld.Form1::button1
  IL_000b:  /* 02   |                  */ ldarg.0
  IL_000c:  /* 28   | (0A)000011       */ call       instance void [System.Windows.Forms]System.Windows.Forms.Control::SuspendLayout()
  IL_0011:  /* 02   |                  */ ldarg.0
  IL_0012:  /* 7B   | (04)000001       */ ldfld      class [System.Windows.Forms]System.Windows.Forms.Button HelloWorld.Form1::button1
  IL_0017:  /* 1F   | 0C               */ ldc.i4.s   12
  IL_0019:  /* 1F   | 10               */ ldc.i4.s   16
  IL_001b:  /* 73   | (0A)000012       */ newobj     instance void [System.Drawing]System.Drawing.Point::.ctor(int32,
                                                                                                               int32)
  IL_0020:  /* 6F   | (0A)000013       */ callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Location(valuetype [System.Drawing]System.Drawing.Point)
  IL_0025:  /* 02   |                  */ ldarg.0
  IL_0026:  /* 7B   | (04)000001       */ ldfld      class [System.Windows.Forms]System.Windows.Forms.Button HelloWorld.Form1::button1
  IL_002b:  /* 72   | (70)000001       */ ldstr      "button1"
  IL_0030:  /* 6F   | (0A)000014       */ callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Name(string)
  IL_0035:  /* 02   |                  */ ldarg.0
  IL_0036:  /* 7B   | (04)000001       */ ldfld      class [System.Windows.Forms]System.Windows.Forms.Button HelloWorld.Form1::button1
  IL_003b:  /* 16   |                  */ ldc.i4.0
  IL_003c:  /* 6F   | (0A)000015       */ callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Control::set_TabIndex(int32)
  IL_0041:  /* 02   |                  */ ldarg.0
  IL_0042:  /* 7B   | (04)000001       */ ldfld      class [System.Windows.Forms]System.Windows.Forms.Button HelloWorld.Form1::button1
  IL_0047:  /* 72   | (70)000011       */ ldstr      "Test"
  IL_004c:  /* 6F   | (0A)000016       */ callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Text(string)
  IL_0051:  /* 02   |                  */ ldarg.0
  IL_0052:  /* 7B   | (04)000001       */ ldfld      class [System.Windows.Forms]System.Windows.Forms.Button HelloWorld.Form1::button1
  IL_0057:  /* 02   |                  */ ldarg.0
  IL_0058:  /* FE06 | (06)000005       */ ldftn      instance void HelloWorld.Form1::button1_Click(object,
                                                                                                   class [mscorlib]System.EventArgs)
  IL_005e:  /* 73   | (0A)000017       */ newobj     instance void [mscorlib]System.EventHandler::.ctor(object,
                                                                                                        native int)
  IL_0063:  /* 6F   | (0A)000018       */ callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Control::add_Click(class [mscorlib]System.EventHandler)
  IL_0068:  /* 02   |                  */ ldarg.0
  IL_0069:  /* 1B   |                  */ ldc.i4.5
  IL_006a:  /* 1F   | 0D               */ ldc.i4.s   13
  IL_006c:  /* 73   | (0A)000019       */ newobj     instance void [System.Drawing]System.Drawing.Size::.ctor(int32,
                                                                                                              int32)
  IL_0071:  /* 6F   | (0A)00001A       */ callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Form::set_AutoScaleBaseSize(valuetype [System.Drawing]System.Drawing.Size)
  IL_0076:  /* 02   |                  */ ldarg.0
  IL_0077:  /* 20   | 24010000         */ ldc.i4     0x124
  IL_007c:  /* 20   | 11010000         */ ldc.i4     0x111
  IL_0081:  /* 73   | (0A)000019       */ newobj     instance void [System.Drawing]System.Drawing.Size::.ctor(int32,
                                                                                                              int32)
  IL_0086:  /* 28   | (0A)00001B       */ call       instance void [System.Windows.Forms]System.Windows.Forms.Form::set_ClientSize(valuetype [System.Drawing]System.Drawing.Size)
  IL_008b:  /* 02   |                  */ ldarg.0
  IL_008c:  /* 28   | (0A)00001C       */ call       instance class [System.Windows.Forms]System.Windows.Forms.Control/ControlCollection [System.Windows.Forms]System.Windows.Forms.Control::get_Controls()
  IL_0091:  /* 02   |                  */ ldarg.0
  IL_0092:  /* 7B   | (04)000001       */ ldfld      class [System.Windows.Forms]System.Windows.Forms.Button HelloWorld.Form1::button1
  IL_0097:  /* 6F   | (0A)00001D       */ callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Control/ControlCollection::Add(class [System.Windows.Forms]System.Windows.Forms.Control)
  IL_009c:  /* 02   |                  */ ldarg.0
  IL_009d:  /* 72   | (70)00001B       */ ldstr      "Form1"
  IL_00a2:  /* 28   | (0A)000014       */ call       instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Name(string)
  IL_00a7:  /* 02   |                  */ ldarg.0
  IL_00a8:  /* 72   | (70)00001B       */ ldstr      "Form1"
  IL_00ad:  /* 6F   | (0A)000016       */ callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Text(string)
  IL_00b2:  /* 02   |                  */ ldarg.0
  IL_00b3:  /* 16   |                  */ ldc.i4.0
  IL_00b4:  /* 28   | (0A)00001E       */ call       instance void [System.Windows.Forms]System.Windows.Forms.Control::ResumeLayout(bool)
  IL_00b9:  /* 2A   |                  */ ret
} // end of method Form1::InitializeComponent

And extracted the first opcodes, this doesn't mean all the bytes, keep in mind that tokens change when an assembly is recompiled by the reflection. I wanted to search for the opcodes in the process memory using as wildcard for the tokens bytes 3F (the default wildcard of WinHex). So the hex string I typed in WinHex was:

02733F3F3F3F7D3F3F3F3F02283F3F3F3F027B3F3F3F3F1F0C1F10733F3F3F3F

And it took me to:

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

02F443C0               02 73 14 00  00 0A 7D 03 00 00 04 02       .s....}.....
02F443D0   28 15 00 00 0A 02 7B 03  00 00 04 1F 0C 1F 10 73   (.....{........s
02F443E0   16 00 00 0A 6F 17 00 00  0A 02 7B 03 00 00 04 72   ....o.....{....r
02F443F0   09 00 00 70 6F 18 00 00  0A 02 7B 03 00 00 04 16   ...po.....{.....
02F44400   6F 19 00 00 0A 02 7B 03  00 00 04 72 19 00 00 70   o.....{....r...p
02F44410   6F 1A 00 00 0A 02 7B 03  00 00 04 02 FE 06 0A 00   o.....{........
02F44420   00 06 73 1B 00 00 0A 6F  1C 00 00 0A 02 1B 1F 0D   ..s....o........
02F44430   73 1D 00 00 0A 6F 1E 00  00 0A 02 20 24 01 00 00   s....o..... $...
02F44440   20 11 01 00 00 73 1D 00  00 0A 28 1F 00 00 0A 02    ....s....(.....
02F44450   28 20 00 00 0A 02 7B 03  00 00 04 6F 21 00 00 0A   ( ....{....o!...
02F44460   02 72 23 00 00 70 28 18  00 00 0A 02 72 23 00 00   .r#..p(.....r#..
02F44470   70 6F 1A 00 00 0A 02 16  28 22 00 00 0A 2A         po......("...*

I copied and disassembled it (just to make absolutely sure):

.method private hidebysig instance void  InitializeComponent() cil managed noinlining
{
  // Code size       186 (0xba)
  .maxstack  4
  IL_0000:  ldarg.0
  IL_0001:  newobj     instance void [System.Windows.Forms]System.Windows.Forms.Button::.ctor()
  IL_0006:  stfld      class [System.Windows.Forms]System.Windows.Forms.Button HelloWorld.Form1::button1
  IL_000b:  ldarg.0
  IL_000c:  call       instance void [System.Windows.Forms]System.Windows.Forms.Control::SuspendLayout()
  IL_0011:  ldarg.0
  IL_0012:  ldfld      class [System.Windows.Forms]System.Windows.Forms.Button HelloWorld.Form1::button1
  IL_0017:  ldc.i4.s   12
  IL_0019:  ldc.i4.s   16
  IL_001b:  newobj     instance void [System.Drawing]System.Drawing.Point::.ctor(int32,
                                                                                 int32)
  IL_0020:  callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Location(valuetype [System.Drawing]System.Drawing.Point)
  IL_0025:  ldarg.0
  IL_0026:  ldfld      class [System.Windows.Forms]System.Windows.Forms.Button HelloWorld.Form1::button1
  IL_002b:  ldstr       [ERROR: INVALID TOKEN 0x70000009]
  IL_0030:  callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Name(string)
  IL_0035:  ldarg.0
  IL_0036:  ldfld      class [System.Windows.Forms]System.Windows.Forms.Button HelloWorld.Form1::button1
  IL_003b:  ldc.i4.0
  IL_003c:  callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Control::set_TabIndex(int32)
  IL_0041:  ldarg.0
  IL_0042:  ldfld      class [System.Windows.Forms]System.Windows.Forms.Button HelloWorld.Form1::button1
  IL_0047:  ldstr       [ERROR: INVALID TOKEN 0x70000019]
  IL_004c:  callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Text(string)
  IL_0051:  ldarg.0
  IL_0052:  ldfld      class [System.Windows.Forms]System.Windows.Forms.Button HelloWorld.Form1::button1
  IL_0057:  ldarg.0
  IL_0058:  ldftn      instance void HelloWorld.Form1::button1_Click(object,
                                                                     class [mscorlib]System.EventArgs)
  IL_005e:  newobj     instance void [mscorlib]System.EventHandler::.ctor(object,
                                                                          native int)
  IL_0063:  callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Control::add_Click(class [mscorlib]System.EventHandler)
  IL_0068:  ldarg.0
  IL_0069:  ldc.i4.5
  IL_006a:  ldc.i4.s   13
  IL_006c:  newobj     instance void [System.Drawing]System.Drawing.Size::.ctor(int32,
                                                                                int32)
  IL_0071:  callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Form::set_AutoScaleBaseSize(valuetype [System.Drawing]System.Drawing.Size)
  IL_0076:  ldarg.0
  IL_0077:  ldc.i4     0x124
  IL_007c:  ldc.i4     0x111
  IL_0081:  newobj     instance void [System.Drawing]System.Drawing.Size::.ctor(int32,
                                                                                int32)
  IL_0086:  call       instance void [System.Windows.Forms]System.Windows.Forms.Form::set_ClientSize(valuetype [System.Drawing]System.Drawing.Size)
  IL_008b:  ldarg.0
  IL_008c:  call       instance class [System.Windows.Forms]System.Windows.Forms.Control/ControlCollection [System.Windows.Forms]System.Windows.Forms.Control::get_Controls()
  IL_0091:  ldarg.0
  IL_0092:  ldfld      class [System.Windows.Forms]System.Windows.Forms.Button HelloWorld.Form1::button1
  IL_0097:  callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Control/ControlCollection::Add(class [System.Windows.Forms]System.Windows.Forms.Control)
  IL_009c:  ldarg.0
  IL_009d:  ldstr       [ERROR: INVALID TOKEN 0x70000023]
  IL_00a2:  call       instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Name(string)
  IL_00a7:  ldarg.0
  IL_00a8:  ldstr       [ERROR: INVALID TOKEN 0x70000023]
  IL_00ad:  callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Text(string)
  IL_00b2:  ldarg.0
  IL_00b3:  ldc.i4.0
  IL_00b4:  call       instance void [System.Windows.Forms]System.Windows.Forms.Control::ResumeLayout(bool)
  IL_00b9:  ret
} // end of method Form1::InitializeComponent

Ok, this is really the MSIL code of my method, no mistakes. The ldstr instructions show an invalid token because I haven't fixed the #US stream, but this is not important. To be accurate I searched even for the code of the last method left:

.method private hidebysig instance void  button1_Click(object sender,
                                                       class [mscorlib]System.EventArgs e) cil managed
// SIG: 20 02 01 1C 12 11
{
  // Method begins at RVA 0x217c
  // Code size       12 (0xc)
  .maxstack  1
  IL_0000:  /* 72   | (70)000027       */ ldstr      "Ok"
  IL_0005:  /* 28   | (0A)000021       */ call       valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string)
  IL_000a:  /* 26   |                  */ pop
  IL_000b:  /* 2A   |                  */ ret
} // end of method Form1::button1_Click

Same procedure:

723F3F3F3F283F3F3F3F262A

Takes us to:

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

02F43780               72 2F 00 00  70 28 25 00 00 0A 26 2A   ....r/..p(%...&*

And that's it. Now we have the MSIL code of all methods. Where's the Native Code? I don't want to imply that the Remotesoft's linker is a schwindle, but this assembly raises some reasonable doubts, in my opinion. I won't dig into the criteria used by the protection to displace the code in memory, this wouldn't be right. I honestly hope for the costumers of this product that I'm wrong (which is possible: maybe Remotesoft applied the wrong protection to this assembly, who knows..), otherwise they paid (much) for something that is not real. Moreover, the protected code is totally alike to the original, so every common code obfuscator would protect MSIL code better than this. Maybe Remotesoft will clarify this situation.

Epilogue:

The Remotesoft programmer (Huihong) posted this message on my blog:

Hello,

Please email your assembly to me, and I will take a look. MSIL instructions should not be found from memory. It seems the protected code is from some of my debug builds, where IL code is embedded within the assembly for debugging purpose.

I will post a sample, and you are welcome to try to get the IL code. I gurantee that you won't find any IL code, with everything compiled down to x86 code.

Huihong

He provided a new exe and the same version of the dll. And now it did make sense: the new exe was totally different from the old one, it didn't have a section more and even the imports were different. The IL code is not present, since the dll simply puts a ngen created native image in the assembly cache (no reversing, just Filemon). With the .NET Framework 1 I managed to do the same (and it did surprise me that others didn't do the same because it's quite easy), the problem is that with the version 2 of the framework native images changed: they have more sections and contain the MetaData and the MSIL code of the original assembly. If the new native images work even after removing the IL code I don't know, I have still to test this. So in a few words, the code protection offered by the Salamander on what regards native images of the first version of the framework is very good, since those images don't seem to me to contain any IL code. What happens with 2.0 images I have no idea. The license protection should be quite good as long as you don't have the decrypted native image. The string protection is quite easy to break once decrypted. Also, Huihong asked if maybe I had the instructions of the old assembly cached before in memory, but this is not possible, because the original exe was given to me (it wasn't really my exe) only after I saw the first method in memory. So I guess it was just an honest mistake on both sides. Anyway, I'd like to thank Huihong for his courtesy in clarifying this whole thing.

Daniel Pistelli