If you’ve used dnSpy to make a simple modification to a binary you’ll know that it’s often as simple as finding the section of code requiring a small change, right click, “modify method”, then compile.
Often though, the code you are looking at in dnSpy cannot be easily edited and re-compiled. You know the small change you’d like to make, but frustratingly it cannot be easily modified.
Some example threads:
Editing the IL directly is a potential solution, but edits are more complex than they would be in C# and without a good understanding of IL it’s easy to make a mess of it.
The approach we used this week was simple, but easy to forget, which is why i’m jotting it down.
“In computer science, a NOP, no-op, or NOOP (pronounced “no op”; short for no operation) is a machine language instruction and its assembly language mnemonic, programming language statement, or computer protocol command that does nothing.” wiki
The tl;dr is that dnSpy makes it easy to NOP out sections that when removed achieve our desired outcome. And since we are editing the binary rather than re-compiling the code, we bypass the issues that can pop up in more complex applications that have dependencies.
In this example, we are trying to bypass hypervisor protections**:
We’re really lucky, the application in question has a debug log that is enabled by default. The log gives us clues about the functions to review once we open the application in dnSpy. It also shows the hypervisor detection clearly and will make it easier to ‘confirm’ our change is successful.
Once we open the application in dnSpy we can see that the debug log function name lines up, and that the code enabling us to bypass the hypervisor check is right there. We’ll focus on the Hyper-V detection.
We can also see that there is a structure which has a valid detection type of “None”. In most cases, we’d simply change the value to “None” and recompile.
Here’s a quick screenshot of what we’d love to do (it wont work like this):
We can see that if we do attempt to compile there are multiple errors:
So re-compiling, even after a small change is going to be painful. But the alternative, mentioned above is to edit the binary rather than recompiling the code. It’s not as straightforward if you’re only familiar with C#, but under the right conditions it can be pain free. This particular app provides such a condition.
The constructor for the class that assess the hypervisor already sets the value to “None” when it is initialized.
That means that (most likely) all we need to do is ensure that code to modify (“Set”) that value never runs. So we wont need to understand a whole lot of IL, we can simply NOP out the sections that make a change.
We do that by finding the line that needs to disappear, right click, “edit IL instructions”.
The edit dialog will take you to the IL in question and put a rectangle around it, you can see in this example that the mapping between IL and C# can be difficult to follow, but when it is a simple change, like NOP’s, you’re going to be in good shape. We right click the line and choose “replace with NOPs”.
When you return to the main viewing window, you’ll notice that the code is now missing, it has been replaced with an empty set of curly braces
All we do now is save the binary file (not recompile):
Once we copy the binary back into place and restart the application we can confirm that the hypervisor detection is now inactive.
We can observe it in the behavior we were looking to bypass (usually an error saying something like “running on Hyper-V is unsupported”).
In this particular case we can also confirm in the debug log provided by the application:
Nothing groundbreaking, but hopefully it’ll save you some time.