카테고리 없음

securom 4.x 언팩 (롤러코스터 타이쿤2)

실버솔 2023. 7. 18. 11:34

 

참고:

http://blog.w4kfu.com/tag/securom

http://baboon.rce.free.fr/index.php?post/2008/03/15/11-no-cd-pour-diablo-2-lod-faites-le-vous-meme 

http://www.reversing.be/article.php?story=20061015153108847 

https://bbs.kanxue.com/thread-18600.htm

https://bbs.kanxue.com/thread-13190-1.htm

https://gbatemp.net/threads/securom-new-4-48-00-0004-reverse-engineering-technical-paper.628858/

 

securom 4.x-5.x는 굉장히 오래된 프로텍터고, 그중에서도 언팩이 굉장히 쉬운 축에 속한다. 

언팩한 후에는 별다른 복제방지 기능이 없기 떄문에, 언팩 = 노시디에 해당한다.

언팩을하기 위해서는 실제 cd(또는 데몬툴 사용)를 준비할 필요가 있다. 왜냐면 CD에서 데이터를 읽어 복호화하는 과정이 있기 때문이다.

 

다른 글들도 도움이 됐지만 실제로 도움이 된건 마지막에 있는 ArabianNights.pdf라는 가이드였기 때문에

이를 토대로 작성할것이다. 따라서 그것은 꼭 읽어보기 바란다.

가상머신을 사용한다면 스탭마다 스냅샷을 찍는것을 추천한다.

 

사실 처음에는 ollydbg로 하려고 했지만 중간에 Exception이 계속 발생되서 실행이 되지 않았다.

따라서 가이드대로 x64dbg를 사용할것이다.

 

먼저 0x000000-0xFFFFFF까지 ignored Exception에 추가한다.

 

 

그다음은 OEP를 찾아야 하는데 일반적으로 OEP를 찾는 방법은 ESP Trick이나

writeprocessmemory, VirtualAlloc, Vmprotect에 bp를 걸고 OEP를 찾는것이다.

다른 방법은 OEP 근처에 함수에 BP를 거는것인데 MSVC++ 6.0의 OEP 근처에는 Getversion이 있기 때문에 여기에 BP를 걸면 된다.

 

Ctrl+G를 누르고 GetVersion에 HW(하드웨어) BP를 건다.

 

그리고 F9를 누르다보면 바로 OEP!

CD에서 데이터를 읽어서 복호화가 이루어져서 그런지 너무 빨리 F9를 누르게 되면 오류가 나는것 같다.

아무튼 시행착오를 겪다보면 이곳에 도달하게 될것이다.

사실 OEP 바로 근처이기 때문에 여기서 덤프해도 되는데, 만일을 위해서 다시 OEP에 도달할것이다.

 

WriteProcessMemory에 HW BP

 

OEP주소로 이동하면 영 이상한 값들이 있을것이다.

이것이 우리가 봤던 모습대로 될때까지 F9를 눌러준후

 

만약 우리가 봤던 모습이다하면 여기에 HW BP걸고 F9를 걸면 OEP에 도달하게 된다.

 

 

OEP에 도달했다.

여기서 모든 ignored Exception 범위를 제거해둔다.

 

rdata에 imports가 있기때문에 메모리맵에서 rdata의 시작주소를 메모한다.

 

OEP에 OEP주소

VA에  rdata 시작주소

size에 대충 0x1000을 적고 Get Imports를 누르면

imports들 밑에 invalid한게 뜨는데

적당히 보면서 size를 줄여 나가면 된다.

clear 버튼을 누르면 imports값들이 지워진다.

 

이제 Dump를 눌러 exe저장 -> Fix Dump로 imports 수정을 할것이다.

그리고 언팩된 파일을 실행하면? 당연히 crash가 날것이다.

 

 

언팩된 파일을 olly에 올려보자 여기서 Exception이 발생한다.

call stack을 눌러보면

이곳에서 Exception이 발생한것을 알 수 있다.

콜스택은 돌아갈 주소가 있기 때문에 오류가 난곳을 보려면 위로 한칸 올리면 된다.

call dword ptr ds:[14ed3f0]인데 이 주소의 xref를 찾아보면 굉장히 많은 곳에서 호출되는것을 알 수 있다.

여러 함수의 call이 이 함수를 거쳐 원래 함수를 호출되는 형태이다.

 

일단 오류가 난 주소로 이동해서 쭉 따라가보자. 

 

jmp eax라는 곳에서 멈추면 EAX 값에 GetVersion이라는 함수의 주소가 들어있고 이를 jmp eax를 통해 호출함을 알 수 있다.

이 정보를 이용해서 실제로 call을 원래대로 복구할것이다.

 

 

0xBA0000으로 이동하여 다음과 같이 작성한다.

;ecx에 entry point값을 넣고 증가시키면서 call dword ptr:ds[14ed3f0]가 있는지 확인
;한마디로 처음부터 ecx를 1바이트씩 증가시키면서 call dword ptr:ds[14ed3f0]이면 eax에서 call하는 함수를 확인후
;iat에서 그 주소가 있다면, 찾은 주소를 FF 15 XX XX XX XX 에 쓰겠다는 말

mov ecx,0x6F15C7		;entry point
cmp dword ptr ds:[ecx], d3f015ff //call dword ptr:ds[14ed3f0]가 있는지 확인후 있으면 ecx로 점프, 없으면 바로 inc ecx로 점프함
jne ba003A			;inc ecx로 이동
cmp word ptr ds:[ecx+4], 0x014e
jne ba003A			;inc ecx로 이동
mov dword ptr ds:[ba0090],ecx 	;store ecx
jmp ecx				;jump to ecx; ecx가 가르키는 주소로 이동해서 실행(call dword ptr:ds[14ed3f0]위치에 훅이 설치되어 있음)

mov ecx, dword ptr ds:[ba0090]  ;restore ecx  ; jmp eax에 hook 설치후 되돌아 올 위치	

;함수주소를 직접 호출하므로 iat 주소로 바꿔줌		
		
mov ebx, 0x8A4000			;iat start
cmp dword ptr ds:[ebx],eax		;ebx가 가르키는 값이랑 eax랑 비교
je ba0037				;찾으면(ebx가 가르키는 값이랑 eax랑 같은 값이면) mov dword ptr ds:[ecx+2], ebx로 이동
inc ebx					;ebx 증가
cmp ebx, 0x8A42cc			;iat end??
jne ba0028				;iat 끝이 아니면 cmp dword ptr ds:[ebx],eax로 다시 이동해서 비교
int 3					;error thunk not found ;함수 미발견 오류
mov dword ptr ds:[ecx+2], ebx		;ecx+2자리에 주소를 넣는다 FF 15 XX XX XX XX 형식임

inc ecx					;ecx 증가
cmp ecx, 6F7000			;end of section??
jne ba0005
int 3					; completed

pdf에 있는 코드를 토대로 수정했다.

주석을 달아두었으니 도움이 되었으면 좋겠다.

 

 

 

그리고 다 작성했다면 아까 확인했다 eax에 위치에 HW BP를 걸고

 

 command text에 eip=00ba001d;run를 작성한다.

다했으면 BA0000에 new Entrypoint를 설정하고 실행하면 된다.

여기서 주의사항이 있는데 원본 PDF에서는 0x401000에서 루프를 돌리지만, 이상하게 그럴경우 오류가 발생했기 때문에

먼저 OEP근처의 imports call을 복구하고 다시 0x401000부터 복구할것이다.

 

다 끝냈다면 코드 패치에 사용했던 이 메모리를 다시 00으로 비워줄것이다.

굳이 덤프파일에 이러한 코드를 남겨둘 필요가 없기 때문이다.  BA0090 근처에도 쓰레기 값이 있으니 잘 지워주자.

 

마지막으로 Scylla로 Dump하고 Fix Dump를 하면 게임 클리어!

 

사실 다 끝낸후에 알게된건데 이미 누가 노시디 패치를 만들어 둔것을 알았다... 

 

로코모션은 롤코타2랑 별로 다르진 않지만 헤맨 부분이 있었는데, 이건 나중에 다시 다루도록 하겠다.