When analyzing some logical code of unity games, we could use dnspy to view Assembly-CSharp.dll. However, there's something really inconvenient. If you aren't familiar with a new game, it is difficult to grasp the general execution process of the code. Fortunately, dnspy has supported debugging for unity games on mono VM. Just follow my step, and you'll find it easy to debug a unity game with dnspy!

Download Dnspy and Dependence

If you don't know what dnspy is, you can get some information here. dnspy Page

In short, it is a tool that can be used to decompile binary .Net framework program code, with powerful analysis, decompilation, debugging functions.

You can download the latest version here on the release page. Releases · dnSpy

In addition to downloading the dnspy main binary, we also need to download the mono replacement file needed for debugging unity, which is under the unity branch of the release page.


Here you need to download it according to your game version. As for the method to view the game version, you can find the main executable program of the unity game. Right-click the program properties. The file version found in the details column is your unity version.


After downloading the corresponding replacement file, open the folder corresponding to the unity version, and you will find that there are two folders: Win32 / win64. It depends on the platform your game runs on. If you are not sure about the number of bits in the game, you can first try to replace the 32-bit file. If the game does not work properly, try to replace the 64-bit file.

Replace mono.dll for Debugging

Here you just need to find the root directory of your game, that is, the directory containing the game executable files and game data files. Its structure is usually as follows:


Open the folder GameName_Data, and replace the following file (ignored if it doesn't exist):


Exceptional Case

Given that some games may verify mono.dll to confirm that it has not been replaced, we need to do some operation to load the debug version mono.dll. The solution I came up with is to inject DLL and hook LoadLibraryExW. When the game loads mono module, we can directly load our own debugging version module. Of course, this method requires that your injection method time must be very early, which is earlier than the loading time of mono module. As for how to implement it, registry injection and hijacking injection can be implemented. Here, I will not talk about that.

My implementation is like this

                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
    char ProcessPath[MAX_PATH] = { 0 };
    if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
        DWORD curPID = GetCurrentProcessId();
        GetModuleFileNameExA(OpenProcess(PROCESS_ALL_ACCESS, false, curPID), 0, ProcessPath, MAX_PATH);
        string myPath = ProcessPath;
        if (myPath.find("SSJJ_BattleClient_Unity") != string::npos) {
            if (MH_Initialize() != MH_OK) {
                Log("MH_Initialize Load Failed!");
                return FALSE;
            return TRUE;
        else {
            return FALSE;
    else if (ul_reason_for_call == DLL_PROCESS_DETACH) { 
    return FALSE;
BOOL HookLoadLibraryW() {
    PVOID LoadLibraryWAddr = GetProcAddress(GetModuleHandleA("KernelBase.dll"), "LoadLibraryExW");
    if (!LoadLibraryWAddr) {
        Log("GET LoadLibraryWAddr failed!");
        return false;
    if (MH_CreateHook(LoadLibraryWAddr, MyLoadLibraryExW, reinterpret_cast<LPVOID*>(&oriLoadLibraryExW)) != MH_OK) {
        Log("MH_CreateHook LoadLibraryWAddr failed");
        return false;
    if (MH_EnableHook(LoadLibraryWAddr) != MH_OK) {
        Log("MH_EnableHook LoadLibraryWAddr failed");
        return false;
    Log("Hook LoadlibraryW success");
    return true;
ptrLoadLibraryExW oriLoadLibraryExW;
    _In_ LPCWSTR lpLibFileName,
    _Reserved_ HANDLE hFile,
    _In_ DWORD dwFlags
) {
    char buf[1000];
    memset(buf, 0, sizeof(buf));
    sprintf_s(buf, "%ws", lpLibFileName);
    string buf_str = buf;
    if (buf_str.find("\\mono.dll") != string::npos) {
        HMODULE res = oriLoadLibraryExW(L"C:\\Users\\15516\\AppData\\Roaming\\Wooduan\\SSJJ-4399\\battle\\6\\SSJJ_BattleClient_Unity_Data\\Mono\\monoDebug.dll", NULL, dwFlags); //replace our own debug module
        Log("Patched Load Monodebug.dll");
        return res;
    return oriLoadLibraryExW(lpLibFileName, hFile, dwFlags);

In this way, we have run the game through the debug version of mono, and the next step is to debug it with dnspy

Debug the Game With DnSpy

First, let's configure the environment variables according to the instructions on the dnspy official website

dnSpy's mono.dll will look for an environment variable called DNSPY_UNITY_DBG (Unity with .NET 2.0-3.5 assemblies) or DNSPY_UNITY_DBG2 (Unity with .NET 4.x assemblies)


--debugger-agent=transport=dt_socket,server=y,address=,defer=y or

--debugger-agent=transport=dt_socket,server=y,address=,defer=y,no-hide-debugger to enable detection of the debugger.


--debugger-agent=transport=dt_socket,server=y,address=,suspend=n or

--debugger-agent=transport=dt_socket,server=y,address=,suspend=n,no-hide-debugger to enable detection of the debugger.

According to the version of our unity game, we can configure environment variables to customize the debugging port. By the way, the debugging port of dnspy is 55555 by default, if no environment variable is set.

Here we take win10 as an example to teach you how to set environment variables


Advanced system settings


Advanced-Environment Variables


Click New and input the Variables name and Variables value


click OK and the environment variable is set

Then start the game and keep it running

Launch dnspy,and you need to note that it must be opened with administrator authority

Then according to the method of static analysis, open the main DLL of the game

Then go to Debug-Start Debugging

and select Unity(Connect),you don't need to input IP as it will be set as localhost by default


If there is no problem with the settings, the game should be already connected. You can try the add a breakpoint to see if it can be hitted



We can see that the breakpoint is triggered successfully, and the functions such as stack backtracking and variable monitoring work normally

In this way, it is very convenient to view the process of the game through single step, which is more convenient for us to analyze the game.


Debugging Unity Games · dnSpy/dnSpy Wiki (

Reprint must be marked with copyright