Hi, long time no post. Took some time today to get my hands on bigger games like Silkroad, good time for it as a new patch disabled multiclients and multiclient is extremely useful in SRO.
Alright, here's the technical detail.
When you start sro_client.exe by hand it says "Run Silkroad.exe". How does sro_client.exe know who the caller is? Most probably because it gets passed some commandline args. So I wrote a little tool that I called sro_client.exe, put it into the Silkroad directory and launched Silkroad.exe to see the arguments.
They looked like
No clue what the randomly looking number is or what the rest means but when you launch sro_client.exe like this it doesn't complain. Alright.
If you try to launch a second SRO it says "Silkroad is already running!" or something like that. So let's fix it.
I launched IDA and disassembled sro_client.exe which takes quite some time. Because I knew the technique used to make a program unique are mutexes, I searched for CreateMutex references and I think there were two. So basically had to patch the code to ignore ERROR_ALREADY_EXISTS, the error CreateMutex returns if the mutex already exists. Wrote a launcher for, tried it but after some time of loading a messagebox with unreadable text appeared and SRO quitted. Crap.
If I could've read the text I could've searched for the stringref and see what caused it but thanks to the gibberish I had to go through the shitlong list of MessageBox xrefs to see what rougly looked like the one I saw and eventually found it.
Here's the code
As you see I already commented it. We see a call, a test eax,eax and a jump we WANT to take place because else the crap message is shown. We could patch another jump but I wanted to know what the problem was and had a look at the routine.
Because I knew eax was zero when the message box appeared I looked for early retn out of the routine and found it.
It checked if a call to bind() failed. Well, when can bind() fail? When the port we're trying to bind to is taken and indeed, the port SRO tries to bind to is hardcoded:
Makes sense. First SRO binds to 15779, second one tries to do that, too, bind() fails, SRO talks gibberish, quits. To solve this we have to make sure, every instance binds to another port so I wrote a little loader that does that:
- Load sro_client.exe with the commandline
- Fix two jumps so a failing CreateMutex doesn't matter
- Make the port random
Rather easy task, here's the code
And the binary is here: [Only registered and activated users can see links. Click Here To Register...]
Put it into your Silkroad directory and launch it!
Edit: Unfortunately something's still wrong. It indeed allows to start two SRO clients but you can't connect. Trying to sort it out.
Alright, here's the technical detail.
When you start sro_client.exe by hand it says "Run Silkroad.exe". How does sro_client.exe know who the caller is? Most probably because it gets passed some commandline args. So I wrote a little tool that I called sro_client.exe, put it into the Silkroad directory and launched Silkroad.exe to see the arguments.
They looked like
Code:
sro_client.exe [randomnumber] /18 0 0
If you try to launch a second SRO it says "Silkroad is already running!" or something like that. So let's fix it.
I launched IDA and disassembled sro_client.exe which takes quite some time. Because I knew the technique used to make a program unique are mutexes, I searched for CreateMutex references and I think there were two. So basically had to patch the code to ignore ERROR_ALREADY_EXISTS, the error CreateMutex returns if the mutex already exists. Wrote a launcher for, tried it but after some time of loading a messagebox with unreadable text appeared and SRO quitted. Crap.
If I could've read the text I could've searched for the stringref and see what caused it but thanks to the gibberish I had to go through the shitlong list of MessageBox xrefs to see what rougly looked like the one I saw and eventually found it.
Here's the code
Code:
.text:00722017 call DoSocketShit .text:0072201C test eax, eax ; Probably false if bind() failed? .text:0072201E jnz short loc_72208B .text:00722020 push ebx ; uType .text:00722021 push offset aSilkroad ; "Silkroad" .text:00722026 push offset aIXBR_ ; "¢Ã+®À+ÁÕ¦í +¦¦¦ ¢ÃÃÓ -¯ +Ȧ¤¦+." .text:0072202B push ebx ; hWnd .text:0072202C call ds:MessageBoxA
Because I knew eax was zero when the message box appeared I looked for early retn out of the routine and found it.
It checked if a call to bind() failed. Well, when can bind() fail? When the port we're trying to bind to is taken and indeed, the port SRO tries to bind to is hardcoded:
Code:
push 15779 ; hostshort mov [esp+1ACh+name.sa_family], AF_INET call ds:htons push 0 ; hostlong mov word ptr [esp+1ACh+name.sa_data], ax
- Load sro_client.exe with the commandline
- Fix two jumps so a failing CreateMutex doesn't matter
- Make the port random
Rather easy task, here's the code
Code:
#include <windows.h>
#include <stdio.h>
#include <time.h>
//Used to fix two jnz to jmp
void FixJump(HANDLE hProc,void *addr)
{
char Hax[] = {0xEB};
WriteProcessMemory(hProc,addr,Hax,1,NULL);
}
//Fix bind() failing
void RandomPort(HANDLE hProc)
{
srand(time(NULL));
DWORD port = rand()%2000; //purely arbitrary number
port+=15779;
WriteProcessMemory(hProc,(void*)0x9C1794,(void*)&port,4,NULL);
}
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
PROCESS_INFORMATION ProcInf;
STARTUPINFO StartUpInf;
char path[255];
GetCurrentDirectory(sizeof(path),path);
strcat(path,"\sro_client.exe 1337 /18 0 1");
memset(&StartUpInf,0,sizeof(StartUpInf));
//Create process
CreateProcess("sro_client.exe",
path,
NULL,NULL,false,
CREATE_SUSPENDED,
0,NULL,&StartUpInf,&ProcInf);
//Get access rights
DWORD dummy;
if(!VirtualProtectEx(ProcInf.hProcess,(void*)0x711280,1,PAGE_READWRITE,&dummy))
{
DWORD err = GetLastError();
char buffer[255];
sprintf(buffer,"Error: %d",err);
MessageBox(NULL,buffer,"31337",0);
return -1;
}
//Now modify the code
FixJump(ProcInf.hProcess,(void*)0x711280);
FixJump(ProcInf.hProcess,(void*)0x7112EC);
//Fix bind error
RandomPort(ProcInf.hProcess);
ResumeThread(ProcInf.hThread);
return 0;
}
Put it into your Silkroad directory and launch it!
Edit: Unfortunately something's still wrong. It indeed allows to start two SRO clients but you can't connect. Trying to sort it out.