Skip to main content

Decompiling .NET software on Windows via SSH

If you are anything like me, you may find Windows “challenging” to use. Its GUI tends to get in the way of just about everything. Running Windows in a hypervisor only accentuates this. The CLI tools appear to follow the same pattern. Microsoft is actively improving this, but I still prefer using git bash. So, what does this mean if we are stuck on Windows, and we need to automate some reverse engineering tasks?

Table of contents

Less is more

It turns out that we can mitigate these issues thanks to Microsoft of all things. Windows now supports OpenSSH server – as an installable Windows feature. A surprise to be sure, but a welcome one.

I need a text-based interface to a Windows machine so that I can programmatically decompile Windows-specific .NET applications using ILSpy. Doing this by hand with ILSpy’s GUI is not very enjoyable. Luckily, a command line version of the tool named ilspycmd is made available separately as a NuGet package. Running it via SSH will make my workflow a lot better.

If you are only interested in running OpenSSH server, you can skim the post and follow the PowerShell steps. Better yet, you can find an OpenSSH service operations guide in the References at the end of the post.

Setting up OpenSSH

First we need to install chocolatey, which is an open source package manager for Windows. I will not cover the installation steps here, as the maintainers may change the installation process in the future. The instructions on their website are easy to follow, I promise.

Once you have installed chocolatey, you will need to open PowerShell as an Administrator. You can do this by right clicking on the Windows menu. From an elevated PowerShell, you must run the following commands:

choco install git -y
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~
Start-Service sshd
Set-Service -Name sshd -StartupType 'Automatic'
New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol Any -Action Allow
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Program Files\Git\bin\bash.exe" -PropertyType String -Force

Phew! Those should be the last commands we need to type directly into the Windows machine.

After successfully running those steps, you should be able to SSH into the Windows machine. If you are satisfied with password based authentication (you should not be, and you should feel bad if you are), then you can skip to the next section.

Otherwise, you will need to run the following commands in an SSH session to add your public key as an authorized key:

echo '<public-key-data>' > /c/ProgramData/ssh/administrators_authorized_keys
icacls C:\ProgramData\ssh\administrators_authorized_keys /remove "NT AUTHORITY\Authenticated Users"
icacls C:\ProgramData\ssh\administrators_authorized_keys /inheritance:r
get-acl C:\ProgramData\ssh\ssh_host_dsa_key | set-acl C:\ProgramData\ssh\administrators_authorized_keys
Restart-Service sshd

The icacls commands are very important. They prevent non-administrators from modifying the administrators_authorized_keys file. Speaking of that file, administrator accounts must place their public SSH keys in there. Administrators cannot use the .ssh/ directory in HOME out of the box. You can modify the sshd config file if you really want to change this… But that just feels like a bad idea (there is a link in the References, but all bets are off if you do this).

At this point you should exit your SSH session and try to login again. If you are prompted for your Windows account password, then something broke. Otherwise, you should be able to login without receiving a password prompt.

Now, we can turn off password authentication and restart the service:

sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/g' /c/ProgramData/ssh/sshd_config
powershell -c Restart-Service sshd

Woohoo! We can now remotely access the Windows machine using only public key based authentication.

Installing and using ilspycmd

With the SSH server configured, you will need to install the .NET Core SDK, restart the SSH server, and your SSH session to refresh your PATH (yes, you really need to do all of this, including restarting the SSH server):

choco install dotnetcore-sdk -y
Restart-Service sshd

After restarting the SSH server and reconnecting, you can install ilspycmd using the NuGet package manager via the dotnet CLI. Note, the version number of ilspycmd is explicitly provided. This is because, at the time of writing this post, the current non-preview version of ilspycmd does not support the shipping version of .NET Core. The -g parameter tells the NuGet package manager to install the tool for all users:

dotnet tool install -g ilspycmd --version

Yah! Now we can decompile .NET applications and libraries. The ilspycmd tool is very flexible (unix philosophy ftw!). By default the tool will generate a single source file when given no arguments and write it to stdout. Very handy if you have output redirection in mind.

For my use case, I would like to extract a C# .NET dynamic link library into separate source files so that I can easily grep them. The -p parameter invokes this functionality. When using this feature, you must specify where to save the files using the -o parameter:

mkdir ~/Desktop/some-dll-output
ilspycmd.exe some.dll -p -o ~/Desktop/some-dll-output

This will also save a .csproj so you can open the decompiled files as if they were a real Visual Studio .NET solution. Keep in mind that opening them in VS is a little maneuver that is gonna cost us 51 years.

Now we can write a simple bash script or a program to automate decompiling .NET software on Windows. All while keeping the Windows GUI at a safe distance :)


Switching to Command Prompt or PowerShell

If you need to use the Command Prompt or PowerShell, you can do so by running either cmd or powershell. You can also pass commands to these shells using the -c parameter. Keep in mind that this may not work when you specify a file path.

Updates and corrections

  • May 12, 2022 - Move sections around to be consistent with new post structure
  • October 6, 2020 - Added missing steps required after install the .NET core
  • September 30, 2020 - Updated OpenSSH Windows Firewall rule to allow any transport and not restrict listening connections to port 22. This allows for SSH port forwarding
  • September 13, 2020 - Style references more closely to APA format. General changes for new blog theme
  • May 12, 2020 - Fixed OpenSSH server Add-WindowsCapability command - it was installing the client, not the server. Removed unnecessary Get-NetFirewallRule Windows command
  • April 27, 2020 - Added a table of contents