Jump to content
Tuts 4 You

Less Simple .NET Virtualization


DefCon42
Go to solution Solved by kao,

Recommended Posts

Language : .NET
Platform : .NET/Mono
OS Version : All
Packer / Protector : Custom

Description :

This is something I've stopped working on over the last few months, but if someone's interested in taking up the project with me I'll gladly accept. The original password is hashed to prevent string equality hooking, so the goal here is just to make it respond correctly.

Cracking :

If you do crack this, please post in the thread (or DM me) about how you did it. It doesn't have to be step-by-step; a simple "after doing X all you need to do is Y" is fine. If you have any suggestions for additional obfuscation, please include those as well. Any method is acceptable (besides printing the correct string yourself):^)

Screenshots are attached.

Out.exe

powershell_wqcqNXvjYb.png

powershell_I5XLPbXvq3.png

  • Like 3
  • Thanks 2
Link to comment
Share on other sites

  • Solution

It's a really nice challenge, thank you! :)

 

Pseudo-solution:

Step 1: make type/function/variable names readable. De4dot to the rescue.
Step 2: get some idea how the VM works. In this case, we have P-Code stored in MemoryStream and stream.Position tells us which instruction we're currently executing (aka. EIP).

Spoiler

DgCeOUI.png


Step 3: put some smart breakpoints and trace execution of the VM. We're looking for good boy/bad boy jumps, so focus on changes in stream.Position. I put a breakpoint in UnmanagedMemoryStream.Seek:

Spoiler

3RHhvHy.png


Step 4: look at the log data and identify good boy/bad boy jump. In my case, logged data with some comments looked like this.

Spoiler

 


....
Seek 0x0000000000000ECB
Seek 0x0000000000000ECB
Seek 0x0000000000000ECB
Seek 0x0000000000000ECB
Seek 0x0000000000001039
Seek 0x000000000000107F
Seek 0x000000000000116E
Seek 0x00000000000012AB
Seek 0x000000000000126C
Seek 0x000000000000126C
Seek 0x000000000000140A
Seek 0x000000000000144F
Seek 0x000000000000146F
Seek 0x0000000000001479

asks for SN

Seek 0x0000000000001644

loop each char

Seek 0x0000000000001543
Seek 0x0000000000001543
Seek 0x0000000000001543
Seek 0x0000000000001543
Seek 0x0000000000001543
Seek 0x0000000000001543
Seek 0x0000000000001543
Seek 0x0000000000001543

loop each char ends

Seek 0x00000000000016DB
Seek 0x00000000000016F4

prints bad boy, so good boy jump should be somewhere before

Seek 0x0000000000001787  

 

 

So, we need to trace few instructions starting from EIP=16F4. Turns out that comparison instruction is at EIP=172B and good boy jump is EIP=173D.

 

Step 5: patch P-Code or VM engine. I decided to patch P-Code directly, as integrity checks for the P-Code were not enabled. I changed comparison instruction to compare 2 identical values, so the check always succeeds and good boy jump is always taken.

 

Mission accomplished. :)

 

EDIT: attached file should not be in the middle of sentence.

Out-patched-by-kao.zip

Edited by kao
  • Like 7
  • Thanks 2
Link to comment
Share on other sites

Awesome! Good job! Directly patching the VM bytecode wasn't something I thought about at all. I'll have to take a look at how to fix that ;)

Link to comment
Share on other sites

4 hours ago, DefCon42 said:

I'll have to take a look at how to fix that ;)

Then I'll just patch VM engine. :) 

 

Currently, the conditional jump in your engine looks like this:

if (Program.ᐪ\u1B5F\u1AD4ᖪᕠኯᘃᎻ)
{
	Program.\u181Aზមᢔᖋ\u103A᧓ᤗᕫᶖ\u181A\u1072ᥱ.Seek((long)Program.ᝋᴕ\u1DFBᄂᦨ\u0FDBፌጏᬬửᖜ, SeekOrigin.Begin);
}

I can change it to this:

if (Program.ᄹớቜᄽ៦ᖸᆎᘮἘ.Position == 0x174B || Program.ᐪ\u1B5F\u1AD4ᖪᕠኯᘃᎻ)
{
    Program.\u181Aზមᢔᖋ\u103A᧓ᤗᕫᶖ\u181A\u1072ᥱ.Seek((long)Program.ᝋᴕ\u1DFBᄂᦨ\u0FDBፌጏᬬửᖜ, SeekOrigin.Begin);
}

It says "if we are the the goodboy/badboy then always jump; otherwise check the condition.."

This approach is slightly more unreliable than patching bytecode, because you are relying on dnSpy/dnLib not to f*ck up the assembly - but still definitely doable.

Out-engine-patch-by-kao.zip

Link to comment
Share on other sites

7 hours ago, kao said:

Then I'll just patch VM engine. :) 

 

Currently, the conditional jump in your engine looks like this:


if (Program.ᐪ\u1B5F\u1AD4ᖪᕠኯᘃᎻ)
{
	Program.\u181Aზមᢔᖋ\u103A᧓ᤗᕫᶖ\u181A\u1072ᥱ.Seek((long)Program.ᝋᴕ\u1DFBᄂᦨ\u0FDBፌጏᬬửᖜ, SeekOrigin.Begin);
}

I can change it to this:


if (Program.ᄹớቜᄽ៦ᖸᆎᘮἘ.Position == 0x174B || Program.ᐪ\u1B5F\u1AD4ᖪᕠኯᘃᎻ)
{
    Program.\u181Aზមᢔᖋ\u103A᧓ᤗᕫᶖ\u181A\u1072ᥱ.Seek((long)Program.ᝋᴕ\u1DFBᄂᦨ\u0FDBፌጏᬬửᖜ, SeekOrigin.Begin);
}

It says "if we are the the goodboy/badboy then always jump; otherwise check the condition.."

This approach is slightly more unreliable than patching bytecode, because you are relying on dnSpy/dnLib not to f*ck up the assembly - but still definitely doable.

Out-engine-patch-by-kao.zip 57.25 kB · 0 downloads

Fair point /shrug

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...