본문 바로가기
리버싱

[PE+Reversing] 프리서버 레드문 개발

by dladbru 2019. 12. 9.

※. 프리서버 레드문은 상표권이 끝난 게임으로 더 이상의 수정이나 운영이 문제가 되지 않는다고 들었습니다. 해당 내용이 잘못됬을 경우에는 admin@hack.kr로 메일부탁드립니다.

 

 

개요.

프리서버의 특징으로 유저수가 적은 반면에 맵이 너무나 넒어서 서로 다른맵에 있는 유저들끼리의 대화가 쉽지 않다.

유저들이 외로워하는 이유로 채팅의 말머리에 "!"를 붙여서 시도할 경우에 웹으로 전송이 되게한다. (이후에 웹으로 전달된 메시지가 게임상으로 재전달시킬 생각이였다.)

 

 

 

내용.

먼저 서버파일을 분석해야한다. 레드문의 경우에는 svMapServer.exe파일이 맵마다 인자를 1, 2, 3, 4 같이 주어서 동작하게 된다. 총 60여개정도 실행되던 것으로 기억한다.

 

 

뭐 이것 저것 내용 다빼고 서술해보면,  채팅할 때 "!" 를 앞에 붙이면 소켓을 이용해 웹으로 전달될 수 있는 DLL을 코딩했다.

#include <stdio.h>
#include <winsock2.h>
DWORD SendArmyChat = 0x00463070;//->00463070
DWORD FindArmy = 0x0048849c;//->0048849c
DWORD FormatStringMapServer = 0x0048236a;//->0048236a
DWORD StringFunc = 0x00482370;//->00482370
DWORD FormatString = 0x0040836c;//mapcenter func ->0040836C
DWORD ServerSendMessage = 0x0043e0e0; //->0043e0e0
DWORD RET=0;
DWORD i=0;
DWORD j=0;
DWORD k=0;
DWORD F=0;
char buf[256]="";
char buf2[32]="";
char GAMEID[32]=")";
char ChatString[7] = "%s%c%s";
char ChatString2[3] = "%d";
char SLString[13] = "%d:%d:%d:%d.";

SOCKET      hSock;
WSADATA     wsadata;
SOCKADDR_IN servAddr;
char link[200]="GET http://redmoon.kr/WolrdChat.aspx?GameID=";
char link2[100]="&Content=";
char link3[100]=" HTTP/1.1\r\n"
"Host: redmoon.kr\r\n\r\n";
char temp[4000];
char temp[4000];

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
//	AllocConsole();
    freopen( "CON", "w", stdout ) ;
	return TRUE;
}

__declspec (dllexport naked) void  NewMapServerChat()// global chat functions
{
	   __asm{
		MOV EAX,DWORD PTR [ESP+0x10]//stm
		MOVSX ECX,BYTE PTR [EAX]//st
		MOV i,ECX;
		cmp i,5
		je exit
		cmp i,6
		je exit

		MOV EAX,DWORD PTR [ESP]//stm
		mov j,EAX;
	   
		cmp i,2
		jne exit

/*ID strcpy*/
		mov EAX,DWORD PTR [ESI+0x37E0]
		mov dword ptr buf2,EAX;
		mov EAX,DWORD PTR [ESI+0x37E1]
		mov dword ptr buf2+1,EAX;
		-------------------일부 생략-------------------
		


/*Chat strcpy*/
		mov EAX,DWORD PTR [ESP+0x10]
		mov ECX,dword ptr[EAX]
		mov dword ptr buf,ECX;
		mov ECX,dword ptr[EAX+1]
		mov dword ptr buf+1,ECX;
		-------------------일부 생략-------------------

		cmp buf+1,0x21
		jne exit
   
}


	  __asm{
		pushad
	   }

	 strcpy(GAMEID,buf2);




 for(k=0;buf[k]!=0;k++){
	  if(buf[k]==0x20){
				buf[k]=0x2b;
	}
 }

	WSAStartup(MAKEWORD(2, 2), &wsadata);
        
    memset(&servAddr, 0, sizeof(servAddr));
    servAddr.sin_family = AF_INET;
    servAddr.sin_addr.s_addr = inet_addr("xxx.xxx.xxx.xxx");
    servAddr.sin_port = htons(80);

    hSock = socket(PF_INET, SOCK_STREAM, 0);
    connect(hSock,(struct sockaddr *)&servAddr,sizeof(servAddr));


	 strcpy(temp,link);
     strcat(temp,GAMEID);
     strcat(temp,link2);
	 strcat(temp,buf+2);
	 strcat(temp,link3);


     send(hSock,temp,sizeof(temp),0);
     memset(&temp,0,sizeof(temp));
     recv(hSock,temp,sizeof(temp),0);
	

	 memset(&temp,0,sizeof(temp));
	 memset(&buf,0,sizeof(buf));
	 memset(&buf2,0,sizeof(buf2));

	 closesocket(hSock);

		__asm{
		popad
		MOV EDX,DWORD PTR[ESP]
		MOV RET,EDX
		pop EDX
		add RET,0x69
		jmp RET
		} /*길드채팅 에러 수정*/


	   

exit:
	   __asm
	{
		cmp dword ptr [esp], 0x0043f49d //->0043f49d
		je func1
		cmp dword ptr [esp], 0x0043FCFB //->0043FCFB
		je func2
		cmp dword ptr [esp], 0x0046312f //->0046312f
		je func3
		cmp dword ptr [esp], 0x0043fd19 //->0043fd19
		je func4
func1:
			MOV EAX,DWORD PTR [ESP+0x10]//stm
			MOVSX ECX,BYTE PTR [EAX]//st
			cmp ecx, 8
			je gchat
		
			retn
	gchat:
			mov dword ptr [esp], 0x0043f4fb //->0043f4fb
			retn

func2:
			cmp byte ptr[ebx], 8
			je func2gchat
			MOV EAX,DWORD PTR [ESI+0x42C8]
			retn
	func2gchat:
			cmp DWORD PTR [ESI+0x37F8], 10
			jae func2next
			push eax
			push ecx
			push edx
			LEA EAX,DWORD PTR [ESP+0x30]
			push 4
			push eax
			push 23
			mov ecx, esi
			MOV DWORD PTR [ESP+0x3C],10
			CALL ServerSendMessage 
			pop edx
			pop ecx
			pop eax
			jmp func2exit1
	func2next:
			mov dword ptr [esp], 0x0043fcff//->0043fcff
			retn
	func2exit1:
			mov dword ptr [esp], 0x0043fd27//->0043fd27
			retn

func3:
			MOV EAX,DWORD PTR [ESP+0x18]//stm
			NOT ECX//st
			mov edx,dword ptr[esp+0x14]
			cmp byte ptr[edx], 8
			je func3gchat

			retn
	func3gchat:
			mov byte ptr [ebx], 8
			retn

func4: //여기까지
			cmp byte ptr [ebx], 9
			jne func4exit2
			add esp,8
			push edi
			xor edi,edi
	nextarmy:
			sub esp, 4
			lea ecx, dword ptr [esp]
			Call StringFunc
			mov eax, dword ptr [esi+0x42c8]
			push eax
			lea eax, dword ptr ChatString2
			push eax
			lea eax, dword ptr [esp+0x8]
			push eax
			call FormatStringMapServer
			mov ecx, dword ptr [esi+0x4504]
			add ecx, 0x3310
			mov eax, dword ptr [esp]
			mov eax, dword ptr [eax]
			mov dword ptr [esp],eax
			mov eax, dword ptr [FindArmy]
			mov eax, dword ptr [eax]
			call eax
			add esp, 0xC
			add eax, 0x14
			inc edi
			lea eax, dword ptr [eax+4*edi]
			cmp edi, 5
			ja func4exit1
			mov eax, dword ptr [eax]
			cmp eax, 0
			je nextarmy
			mov ecx, dword ptr [esi+0x4504]
			mov edx, dword ptr [esp+0x28]
			push edx
			push eax
			call SendArmyChat
			jmp nextarmy
func4exit1:
			pop edi
			push 0x0043fd27//->0043fd27
			retn
func4exit2:
			add esp, 4
			push eax
			call SendArmyChat
			push 0x0043fd27//->0043fd27
			retn
	}
}

   __declspec (dllexport naked) void  NewMapCenterChat()
{
	__asm
	{
		cmp dword ptr [esp], 0x00404D77//->
		je func1
		cmp dword ptr [esp], 0x00404F8E//->
		je func2
		cmp dword ptr [esp], 0x00407133//->00407133
		je func3
		cmp dword ptr [esp], 0x00405243//->
		je func4
	}
			
	__asm
	{
func1:
			cmp esi, 8
			je gchat
			cmp esi, 9
			je achat
			ADD ESI,-3//st
			CMP ESI,3//st
			retn
	gchat:
			mov dword ptr [esp], 0x00404D8D//->
			retn
	achat:
			mov dword ptr [esp], 0x00404DA7//->
			retn


func2:
			mov eax, dword ptr [esp+0x8]
			cmp eax, 8
			je func2gchat
			MOV EAX,DWORD PTR [ESI+0x21C4]//st
			retn
	func2gchat:
			mov dword ptr [esp], 0x00404FA4
			retn


func3:
			mov eax, dword ptr [esp+0x44]
			cmp eax, 8
			je func3gchat
			pop eax
			push 0x0040DA28//st ??
			push ecx//st
			push eax
			retn
	func3gchat:
			mov eax, dword ptr [esp+8]
	findnameend:
			cmp byte ptr [eax], 0x0A
			je next
			inc eax
			jmp findnameend
	next:
			mov byte ptr [eax],0
			add eax, 1
			mov dword ptr [esp+8], eax
			sub eax, 2
	findnamestart:
			cmp byte ptr [eax], 0x0
			je next2
			dec eax
			jmp findnamestart
	next2:
			add eax, 1
			add esp,8
			push 12
			push eax
			lea eax, dword ptr ChatString
			push eax
			push ecx
			call FormatString
			add esp,4
			push 0x00407138//->00407138
			retn

func4:
			cmp dword ptr [esp+8], 9
			jne func4exit2
			mov ecx, eax
			push esi
			mov esi, dword ptr [eax-8]
	func4findsymbol:
			cmp byte ptr [ecx], 0x0A
			je func4achat
			inc ecx
			dec esi
			je func4exit1
			jmp func4findsymbol
	func4achat:
			mov byte ptr [ecx], 0x0D
	func4exit1:
			pop esi
			MOV ECX,DWORD PTR [EDI+0x21C0]//st
			retn
	func4exit2:
			MOV ECX,DWORD PTR [EDI+0x21C0]//st
			retn
	}
}

 

 

서버파일의 PE헤더

하지만 원하는 기능이 개발된 DLL을 서버파일에서 사용하지 않는다면 의미가 없다. 서버파일을 실행시마다 매번 DLL인젝션으로 넣어줄 수도 없지 않은가?

PE헤더에 내가만든 "rmKMS.dll의 NewMapServerChat, NewMapCenterChat 함수를 참조시킨다면 자연스레 실행시마다 rmKMS.dll 파일을 사용 할터였다.

 

 

드래그한 공간 뒤에 빈공간이 없어..

 

위 사진의 지정한 "00"*16 공간은 건드릴 수 없다.  뒤에 여유공간이 없는 터이다..  "00"르로 차있는 부분이 한 블럭만치만 더있었다면..

 

코드 케이빙..이라고 하던가 이걸? 기억이 안나네

 

그러나 파일의 어딘가에 빈 메모리공간은 충분히 존재한다. 나는 리소스 영역의 뒷편으로 "IMPORT Directory Table" 섹션을 빈 노란 공간으로 복사시켰다. 그리고 보라색 영역에 우리가 추가할 함수에 대해 추가해주었으며, 복사한 영역을 참조해 실행하도록 수정했다.

 

대부분 필요한 정보는 또 여기다놨지..

함수의 이름에서 2바이트 공간을 비워놨던 이유는 .. 아래 사진을 참고하자. 그렇게 필수적이진 않다고 한다 ㅇㅇ;

 

https://yokang90.tistory.com/26 에서 퍼옴

 

 

소스코드보고 이해하자. 나도 기억이 잘안난다.

이제 서버에서 채팅을 받았을 때 함수가 호출되게 하면 모든 것이 완벽해졌다.

 

어셈블리어로 스택을 직접 참조해서 동작하기 때문에 Hexray로는 이해되지 않는다. 하지만 사용자가 별개로 만든 DLL을 참조하고 더 이상의 개발이 불가능한 게임에 새로운 기능이 도입되었다는데 의의가 있다.

 

동작하는 부분에 대해서는 아래를 확인하면 될 것 같다.

 

 

[동작 영상]

반응형

댓글