ApourtArtt / NtHkBypass

Bypass Xigncode verification on Nostale HK server
4 stars 5 forks source link

Explanation to replicate #2

Open pacioc193 opened 2 years ago

pacioc193 commented 2 years ago

I've a game that run xigncode 3 as software protection. I need to bypass it. Could you explain me how to find the correct jmp to replace?

Grazie

ApourtArtt commented 2 years ago

It depends of the game you're wanting to bypass it. In this repository, you are not really bypassing XignCode, you are avoiding it to be added to the game (so the heartbeat is not send anymore, causing a disconnection in the following 10 minutes if you don't replicate the behaviour of XignCode). How to find the jmp is rather easy, start the game with your debugger (I use Cheat Engine), then step over until the process stops (since you are attaching a debugger, XignCode will detect it and stop your game), once found, go in the call and continue the same process, you can also use a tool like Ghidra which will help you to do that quicker thanks to the C decompilation.

If you really want to bypass XignCode, you have (at least, for NosTale), two options:

The first one will require some more advanced reverse engineer skills, while the second only require to understand how your game works. An example with NosTale is described in this issue: https://github.com/ApourtArtt/NtHkBypass/issues/1#issuecomment-1186504005

pacioc193 commented 2 years ago

I'm searching to reproduce it but is impossible to start application with debugger. Themida will stop the process. At start the application load x3.xem and this will cut the possibility to do anything else...

pacioc193 commented 2 years ago

Done, i've found the loading of dll xigncode, now executable bypass it. Now the issue is heartbeat... Do you have more information about it?

ApourtArtt commented 2 years ago

Sorry for the late response - I was planning to share my thoughts but I prefered to actually experiment and try to simulate the heartbeat. My client is currently running and so far, no disconnection due to heatbeat, so I guess it works.

I opened my game client (NostaleClientX.exe, in my case) in Ghidra and looked for when the client opens x3.xem. image I saw that it was starting a function named (LPCSTR)0x01 - as you can see on the screenshot above.

My first idea was to make a DLL proxy between the client and x3.xem (code later), so I am able to run my code through my DLL before x3.xem is actually used.

The final result I get is:

Here is the final code:

#include <Windows.h>
#include <stdio.h>
#include <thread>

static bool started{ false };

FARPROC OrigOrdinal1 = NULL;
FARPROC OrigOrdinal2 = NULL;

static void start()
{
    if (started)
        return;
    started = true;
    AllocConsole();
    freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);

    const HMODULE h_lib_module = LoadLibraryA("XIGNCODE\\orig_x3.xem");
    if (h_lib_module == nullptr)
    {
        fprintf(stdout, "ERROR!!!!!!!!");
        return;
    }

    OrigOrdinal1 = GetProcAddress(h_lib_module, (LPCSTR)0x1);
    if (OrigOrdinal1 == NULL)
        fprintf(stdout, "ERROR Orig1");
    OrigOrdinal2 = GetProcAddress(h_lib_module, (LPCSTR)0x2);
    if (OrigOrdinal1 == NULL)
        fprintf(stdout, "ERROR Orig2");

    std::thread* th = new std::thread([]()
    {
        while (true)
        {
            fprintf(stdout, "From Thread\n");
            Sleep(5000);
        }
    });
}

extern "C" __declspec(dllexport) void __declspec(naked) BLABLABLA()
{
    __asm JMP OrigOrdinal1;
}

extern "C" __declspec(dllexport) void __declspec(naked) X()
{
    __asm JMP OrigOrdinal2;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    start();
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        fprintf(stdout, "DLL_PROCESS_ATTACH\n");
        fflush(stdout);
    case DLL_THREAD_ATTACH:
        fprintf(stdout, "DLL_THREAD_ATTACH\n");
        fflush(stdout);
    case DLL_THREAD_DETACH:
        fprintf(stdout, "DLL_THREAD_DETACH\n");
        fflush(stdout);
    case DLL_PROCESS_DETACH:
        fprintf(stdout, "DLL_PROCESS_DETACH\n");
        fflush(stdout);
        break;
    }
    return TRUE;
}

And... Voilà! image

Obviously, the code itself does not do anything, and is not really clean, but you can just go on with this snippet.

pacioc193 commented 2 years ago

First of all thanks you very much for the detail explanation of how you create the bypass. It’s a very good approach of reverse.

I will perform some test this weekend with the suggested info.

From my fast understand :

Is this correct?

ApourtArtt commented 2 years ago

1) You are correct, but it is not really an "empty dll", it is your loader! 2) You are correct 3) I am not sure I understood this point

pacioc193 commented 2 years ago

I already released some bot for this game but I never include a loader. Always read and write memory using a special software in Java. It was never detected. Now I want to find new variable for this boy and I need to run cheat engine… I don’t need to inject, I need to run cheat engine. I will try by the way

pacioc193 commented 2 years ago

Hi,

just some update. My binary that load x3.xem use an hash check to be sure no injection happen to the main software. I won’t be able to create a proxy server between dll and game. I’m working on heartbeat . I find that software use 3 channels socket where receive some message ( 1 from login server, 1 from game server and 1 seems from xigncode ) After some minute in game it check the queue of the xigncode and disconnect the client.

I want try to bypass the 3rd queue or send empty package from my bot software. Do you think is possible?