Results 1 to 2 of 2
  1. #1
    a4123278
    a4123278 is offline
    Member-in-training
    Join Date
    2010 Apr
    Posts
    68
    Thanks Thanks Given 
    2
    Thanks Thanks Received 
    15
    Thanked in
    1 Post
    Rep Power
    0

    [Tutorial] Silkroad Multiclient

    Silkroad Multiclient Tutorial

    In this tutorial you will learn to patch sro_client.exe and Silkroad.exe
    to allow starting of multiple clients.

    First you will need the following things
    - OllyDbg 1.10
    - Visual C++ 2005 or later
    - EXEInfo PE
    - StripperX
    - 32bit version of Windows
    - Winject or another DLL injector

    Part 1: Analyze the client
    If you open sro_client.exe in OllyDbg you should notice that it is packed. To find out
    what it is packed with we will use EXEInfo PE. You can find this here http://www.exeinfo.xwp.pl/

    EXEInfo PE tells us sro_client.exe is packed with ASprotect ver 2.1 / 2.^. This is quite simple to unpack
    and requires only a few clicks.


    Part 2: Unpacking the client
    Start stripper v9 and click the open button. It will give you an open file dialog. Select your client
    and click unpack. Once it does it's thing sro_client may crash (ignore this) and click on save in the
    new window that has appeared. StripperX will save the unpacked file as _sro_client.exe


    Part 3: Analyzing and patching the Silkroad launcher
    Start two Silkroad.exe's and you will get this error.


    Start OllyDbg and open Silkroad.exe. Right click -> Search for -> All referenced text strings


    Search for the word "already" until you find "%s is already executed!!".


    Double click the first one and you should see some similar code to this.


    We need to just completely jump over the MessageBoxA error to do this we will need to change
    the JNZ SHORT 00437ACB to a JMP by double clicking on the JNZ and changing it to a JMP.


    Go back to the text string window and find the other one we found. Patch it the same way by locating
    the CMP EAX,0x0B7.

    Next we need to find
    Now right click -> Copy to executable -> All modifications


    On the next window click Copy All. And on the next window right click -> Save file and save it as the
    same file name.

    Now we will be able to run Silkroad.exe while sro_client is running.

    Part 4: Patching the client
    Move the unpacked client to your Silkroad directory and start OllyDbg and open the newly unpacked
    sro_client. Let OllyDbg analyze the exe. It'll be easier to look at and save us some time.

    Now hit Shift+F9 this will run the program. You should an error message that looks like this.


    Click the << rewind button in OllyDbg (it's directly beside the open file button). sro_client has now been
    reloaded in Olly.

    We obviously need to find this error message within the program. Right click -> Search for -> All referenced text strings.


    In the new window that has appeared right click -> Search for text


    Remember the error message? Type in "Please Execute" this should be sufficient enough to find it.


    Double click that line and it will take you to the string. As you can see it's just a simple MessageBoxA call.
    This is exactly what we need to bypass.


    Double click the JNZ and change it to a JMP as such.


    Scroll up a little and you should see a text string that says something about a MapTool.
    If you have every tried to run another client while one was already running it would bring the already
    running client to the front and close the one you just ran. Well this is that code.


    If we take a close look at the code we can see that it creates a mutex called "Silkroad Client" then
    calls GetLastError() to see if it failed and if it did it will bring the other Silkroad client to the front
    and terminate (JNZ = JMP if false)

    As you can see the EAX holds the return value of GetLastError() from looking at CMP EAX,0xB7.

    So simply double click the JNZ after CMP EAX,0xB7 and change it to a JMP like we did before.

    Now lets save the file and see if it worked correctly. Right click -> Copy to executable -> All modifications
    Select copy all in the next window.


    Right click in the new window -> Save file
    Save it in the same location and same file name.


    Now click the << rewind button. Find the file and double click it twice to launch two clients.
    Eventually you should get an error like this with some Korean letters.


    We need to bypass this error too. Like we did before. Right click -> Search for -> All referenced text strings
    We need to find "Silkroad" because we cannot type the letters into OllyDbg. Once you find the string it should
    be directly below "GateWay : %s"


    Double click it and the code should look like this.


    Change the JNZ to a JMP and save the file exactly like before. Run the modified client twice and there shouldn't
    be any problems. Now try to login two characters. One will get in and oh noes C10 error :o

    The C10 error is caused by multiple logins trying to using the same MAC (Media Access Control) address it's
    basically the unique ID of your network adapter. Silkroad MUST get your MAC address from some where. The best
    way to do this would be with a call to GetAdaptersInfo()

    Right click in OllyDbg -> Search for -> All intermodular calls
    Find any calls to iphlpapi.GetAdaptersInfo


    You can either set break points here and look for your MAC address in the register's or I can tell you
    that MOV EDX,DWORD PTR DS:[EDI+0x194] will move your MAC address into the EDX register. This is what we
    need to patch.

    The easiest way to patch this is to make a DLL and code cave it.

    Part 5: Creating the DLL
    Create a new prject in VC++ and name it macChanger. Disable pre-compiled headers and change the character-set
    to multi byte. We will use the WriteJMP code from my other tutorial. Make sure this code goes above your DllMain.
    Code:
    void WriteBytesASM(DWORD destAddress, LPVOID patch, DWORD numBytes)
    {
        DWORD oldProtect = 0;
        DWORD srcAddress = PtrToUlong(patch);
        VirtualProtect((void*)(destAddress), numBytes, PAGE_EXECUTE_READWRITE, &oldProtect);
        __asm
        {
            nop                        // Filler
            nop                        // Filler
            nop                        // Filler
    
            mov esi, srcAddress        // Save the address
            mov edi, destAddress    // Save the destination address
            mov ecx, numBytes        // Save the size of the patch
    Start:
            cmp ecx, 0                // Are we done yet?
            jz Exit                    // If so, go to end of function
    
            mov al, [esi]            // Move the byte at the patch into AL
            mov [edi], al            // Move AL into the destination byte
            dec ecx                    // 1 less byte to patch
            inc esi                    // Next source byte
            inc edi                    // Next destination byte
            jmp Start                // Repeat the process
    Exit:
            nop                        // Filler
            nop                        // Filler
            nop                        // Filler
        }
        VirtualProtect((void*)(destAddress), numBytes, oldProtect, &oldProtect);
    }
    void WriteJMP(DWORD destAddress, VOID (*func)(VOID), BYTE nopCount)
    {
        DWORD offset = (PtrToUlong(func) - destAddress) - 5;
        BYTE nopPatch[0xFF] = {0};
    
        BYTE patch[5] = {0xE9, 0x00, 0x00, 0x00, 0x00};
        memcpy(patch + 1, &offset, sizeof(DWORD));
        WriteBytesASM(destAddress, patch, 5);
    
        if(nopCount == 0)
            return;
    
        memset(nopPatch, 0x90, nopCount);
        WriteBytesASM(destAddress + 5, nopPatch, nopCount);
    }
    We need to generate 5 random numbers for our MAC address. Create a 6 BYTE array at the very top of the code
    but below the includes.
    Code:
    BYTE MAC[6] = {0};
    Now create a DWORD below the byte array for our return address. It's always easier to just copy pasta the ASM
    from Silkroad into C++ and return a few addresses after it so there won't be any problems.
    Code:
    DWORD dwRet = 0x0046C791;
    In the DllMain function add. This will generate 5 random bytes for our MAC address. Write the code cave
    after the MAC address has been generated.
    Code:
    for(int x = 1; x < 5; ++x)        //Skip the first byte and generate random values
    {
        srand(GetTickCount());        //Initialize a random seed
        MAC[x] = rand() % 0xFF;        //Generate random BYTE
    }
    //Code cave
    WriteJMP(0x0046C787, MACChange, 0);
    Next we will write our naked MAC change function. Remember that EDX will contain our MAC address and
    EDI+194 holds our original MAC address.
    Code:
    __declspec(naked) void MACChange()
    {
        __asm
        {
            LEA EDX, DWORD PTR DS:[MAC]            //Move the MAC byte array into the EDX register
            MOV EAX,DWORD PTR SS:[ESP+0x18]        //Copied from Silkroad
            JMP dwRet                            //Return to original code
        }
    }
    Here's the full code if you couldn't follow that.
    Code:
    #include "stdio.h"
    #include "windows.h"
    
    BYTE MAC[6] = {0};
    DWORD dwRet = 0x0046C791;
    void WriteJMP(DWORD destAddress, VOID (*func)(VOID), BYTE nopCount);
    
    __declspec(naked) void MACChange()
    {
        __asm
        {
            MOV EDX, DWORD PTR DS:[MAC]            //Move the MAC byte array into the EDX register
            MOV EAX,DWORD PTR SS:[ESP+0x18]        //Copied from Silkroad
            JMP dwRet                            //Return to original code
        }
    }
    
    BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call,LPVOID lpReserved)
    {
        if(ul_reason_for_call == DLL_PROCESS_ATTACH)
        {
            DisableThreadLibraryCalls(hModule);
    		srand(GetTickCount());			//Initialize a random seed
    		
            for(int x = 1; x < 5; ++x)		//Skip the first byte and generate random values
            {
                MAC[x] = rand() % 0xFF;		//Generate random BYTE
            }
    
            //Code cave
            WriteJMP(0x0046C787, MACChange, 0);
        }
        return true;
    }
    void WriteBytesASM(DWORD destAddress, LPVOID patch, DWORD numBytes)
    {
        DWORD oldProtect = 0;
        DWORD srcAddress = PtrToUlong(patch);
        VirtualProtect((void*)(destAddress), numBytes, PAGE_EXECUTE_READWRITE, &oldProtect);
        __asm
        {
            nop                        // Filler
            nop                        // Filler
            nop                        // Filler
    
            mov esi, srcAddress        // Save the address
            mov edi, destAddress    // Save the destination address
            mov ecx, numBytes        // Save the size of the patch
    Start:
            cmp ecx, 0                // Are we done yet?
            jz Exit                    // If so, go to end of function
    
            mov al, [esi]            // Move the byte at the patch into AL
            mov [edi], al            // Move AL into the destination byte
            dec ecx                    // 1 less byte to patch
            inc esi                    // Next source byte
            inc edi                    // Next destination byte
            jmp Start                // Repeat the process
    Exit:
            nop                        // Filler
            nop                        // Filler
            nop                        // Filler
        }
        VirtualProtect((void*)(destAddress), numBytes, oldProtect, &oldProtect);
    }
    void WriteJMP(DWORD destAddress, VOID (*func)(VOID), BYTE nopCount)
    {
        DWORD offset = (PtrToUlong(func) - destAddress) - 5;
        BYTE nopPatch[0xFF] = {0};
    
        BYTE patch[5] = {0xE9, 0x00, 0x00, 0x00, 0x00};
        memcpy(patch + 1, &offset, sizeof(DWORD));
        WriteBytesASM(destAddress, patch, 5);
    
        if(nopCount == 0)
            return;
    
        memset(nopPatch, 0x90, nopCount);
        WriteBytesASM(destAddress + 5, nopPatch, nopCount);
    }
    void WriteCALL(DWORD destAddress, VOID (*func)(VOID), BYTE nopCount)
    {
        DWORD offset = (PtrToUlong(func) - destAddress) - 5;
        BYTE nopPatch[0xFF] = {0};
    
        BYTE patch[5] = {0xE8, 0x00, 0x00, 0x00, 0x00};
        memcpy(patch + 1, &offset, sizeof(DWORD));
        WriteBytesASM(destAddress, patch, 5);
    
        if(nopCount == 0)
            return;
    
        memset(nopPatch, 0x90, nopCount);
        WriteBytesASM(destAddress + 5, nopPatch, nopCount);
    }
    Part 6: Testing
    Rename the patched _sro_client.exe to sro_client.exe Start two clients that you have already
    patched with Silkroad.exe and inject the DLL we made into one or both of them. Now try to login
    with both at the same time. They both should continue with the login process as normal and not
    get the C10 error.

    © ProjectHax.com

  2. #2
    ryu1
    ryu1 is offline
    Guest
    Join Date
    2012 Aug
    Posts
    2
    Thanks Thanks Given 
    0
    Thanks Thanks Received 
    0
    Thanked in
    0 Posts
    Rep Power
    0

    nice

    Awesome tut

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •