강의정리/Z0FCourse_Re

[x64] Chapter 6 - DLL/6.6 InitializePlayer.md

우와해커 2020. 1. 30. 15:32

6.6 Initialize Player
일반적으로 DLL은 범용으로 사용됩니다. 링크된 목록, 로깅 정보, 그래픽 등을 설정하는데 사용되는 DLL 일 수 있습니다.
우리가 리버싱하는 DLL은 하나 이상의 범용 작업을 수행합니다. 더 쉬운 예제를 사용하여 DLL 리버싱을 소개하고 싶기 때문입니다.
InitializePlayer, PrintPlayerStats 및 MysteryFunc 함수가 모두 서로 관련되어 있음을 알려드리고 싶습니다.
다시 말하지만, 이 DLL은 학습 도구입니다. 나중에 더 좋은 예를 들어 보겠습니다.

 

Player와 관련된 두 가지 함수가 있는 것 같습니다. Player는 아마도 구조 (구조체) 일 것입니다.
이해하기 위해서 Player 구조체를 사용하는 함수를 리버싱하고 그 함수가 구성하는 것이 무엇인지 알아야 됩니다.
InitializePlayer로 시작하겠습니다. 그것이 우리에게 가장 많은 정보를 제공할 가능성이 있기 때문입니다.
그것은 또한 Player 구조체의 최소값이 무엇인지 알려 줍니다.

 

구조체와 클래스는 동일합니다. 유일한 차이점은 하나는 비공개이고 하나는 기본적으로 공개입니다. 그 외에는 모두 똑같이 작동합니다.
C++에 대해 이야기 할 때 클래스와 구조체를 상호 교환하여 사용하는 것이 좋습니다.
C에는 클래스가 없으므로 C에 대해 이야기하는 경우 클래스 대신 구조체를 사용하십시오.

 


1.Reversing

 

x64dbg의 분석 덕분에 함수 선언을 볼 수 있습니다.

void __cdecl InitializePlayer(class Player * __ptr64)

그래서, 이 함수는 void를 반환하고 Player 클래스에 대한 포인터를 가져옵니다.
클래스/구조체의 첫 번째 요소를 참조 할 때, 클래스의 base를 사용하는 겁니다.

 

당신은 이것처럼 생각할 수 있습니다
Player = First element.
Player + F = Second element (F is the size of the first element).

 

 

어셈블리 코드를 보고 무슨 일이 일어나고 있는지 알아 봅시다.

 

MOV DWORD PTR DS:[RCX], 0x20
0x20(32)이 Player클래스의 첫 번째 요소로 이동합니다.
우리는 이제 알 수 있습니다, 플레이어의 첫 번째 요소는 정수일 가능성이 높습니다.

 

LEA RDX, QWORD PTR DS:[0x7FFF44719718]
문자열 "PLACEHOLDER"의 주소가 RDX로 로드됩니다.

 

MOV DWORD PTR DS:[RCX + 0x4], 0x42C80000
0x42C80000을 Player 객체의 두 번째 요소로 이동합니다.

그것은 이상한 값처럼 보입니다. 그 값을 float으로써 보면 100 인 것 같습니다.

100이 일반적으로 최대 health 값으로 사용된다는 점을 고려하면 흥미롭습니다.
지금은 0x42C80000이 float라고 가정 할 수 있습니다.
확실히, 우리는 이 값이 부동 소수점 레지스터(floating point register)인 XMM#로 처리되는지 확인하길 원합니다.

 

MOV R8D, 0xB
MOV RCX, 0x8
JMP dll.sub_7FFF51C233C0

이 점프는 함수의 시작으로 가고 있습니다.
또한 점프하기 전에 함수 호출에 적절한 레지스터가 설정되어있는 것 같습니다.
이것은 일종의 의사 호출일 수 있습니다.
또한 R8D와 RDX에 문자열이 삽입되었지만 사용되지 않은 것을 보십시오.
이 문자열은 11글자이며 R8D가 0xB(11)를 포함합니다. 이것이 약간 의심스럽습니다.

 

 

2.Code Jumped To

 

InitializeCalss로부터 점프되는 코드를 살펴보십시오.

점프는 분명히 call처럼 행동합니다. 호출된 코드는 함수호출이 되는 것처럼 레지스터를 사용하고 있습니다.

call 대신 jmp를 선택한 이유는 아마도 최적화 때문일 것입니다.

 

우리는 이것을 리버싱하기 전에 무슨 일이 일어나고 있는지 추측하기를 원합니다.
문자열이 전달되고 있으며 문자열 길이가 전달되고 있습니다.
0xB가 사용되는 것은 우연의 일치일 수 있지만 그렇지 않은 경우 어떻게해야 합니까?
계속 진행하면서 무슨 일이 일어날지 추측하고 코드가 추측과 일치하는지 확인해 보시길 바랍니다.

 

Pushes
레지스터를 유지하기 위해 수행되는 몇 가지 푸시가 있습니다.

 

MOV R14, QWORD PTR DS:[RCX + 0x18]
Player 객체에서 0x18 오프셋 된 일부 데이터를 복사합니다.
지금 당장은 Player + 0x18이 무엇인지 모릅니다.

pretty self-explanatory한 다른 움직임이 있습니다.

 

CMP R8, R14

JA dll.7FFF51C23409

R8(0xB)과 R14(RCX + 0x18)를 비교합니다.

JA (Jump if above)는 unsigned인 경우를 제외하고 JG (큰 경우 점프)와 동일합니다.
이 점프는 RCX + 0x18에 관계없이 0xB를 테스트하고 있습니다. RCX + 0x18에 있는 것을 알아 내기 위해
이 DLL을 사용하는 프로그램을 디버깅 할 수 있지만, 지금은 모르는 채로 계속 진행하겠습니다.
이 점프를 하면 다른 함수로 넘어갑니다. 지금은 이 함수를 리버싱하는데 집중하고 싶습니다.
점프를 하지 않으면 어떻게되는지 봅시다.

 

CMP R14, 0x10

JB dll.7FFF51C233EA
R14(RCX + 0x18)와 0x10(16)을 비교합니다.
다시 말하지만, 우리는 RCX + 0x18에 무엇이 있는지 모릅니다.
RCX + 0x18이 16 미만 (이하)이면 점프합니다.

 

 

CALL dll.sub_7FFF51C2A4A0

이 호출 이전의 명령어는 흥미롭습니다. 이전의 점프가 수행되지 않았다면 RCX가 RDI로 복사됬을 겁니다.
RCX는 Player 클래스입니다. 점프가 수행된 후 RSI(0xB)는 RCX+0x10에 놓입니다.
RDI(Player인 InitializePlayer의 RCX)가 RCX로 이동되었습니다.

 

함수의 나머지 부분은 그다지 흥미롭지 않은 것 같으므로 아래 호출을 수행하고 무엇을 하는지 살펴 보겠습니다.
함수는 다음과 같습니다.

 

~~~~그림~~~~

 

MOVs
RCX(Player)가 R11로 이동되었습니다. RDX는 여전히 "PLACEHOLDER"문자열입니다.
InitializeClass 이후 RDX는 수정되지 않았습니다.

 

CMP R8, 0x10
JBE dll.7FFF51C2A500
0xB와 0x10을 비교합니다.
R8이 0x10보다 작거나 같으면 점프합니다. 우리는 R8이 0xB보다 작다는 것을 알고 있습니다. 그럼 점프를 따라갑시다.

 

MOV RAX, RCX
Player를 RAX로 옮깁니다.

 

LEA R9, QWORD PTR DS : [0x7FFF51C20000]
덤프 내부에서 0x7FFF51C20000을 따라가면 이것이 바이너리의 시작임을 알 수 있습니다.
시작부분이 "MZ"이기 때문에 구분할 수 있습니다. 또한 덤프를 더 이상 스크롤 할 수 없기 때문에 구분할 수 있습니다.

 

MOV ECX, DWORD PTR DS : [R9 + R8 * 4 + 0x45000]
경험이 좀 있다면 무슨 일이 일어 나는지 알 수 있습니다. R9는 바이너리의 base입니다.
R8은 문자열의 크기입니다. 0x45000은 오프셋의 일부이며, 아마도 일부 데이터 저장 영역에 대한 오프셋 일 수 있습니다.

 

 

3. Back To InitializeData

 

이제 우리는 InitializeData 함수의 기능을 알고 있습니다.
그것은 첫 번째 요소를 0x20 32)으로 설정합니다. 두 번째 요소는 100의 float(부동 소수점)입니다.
세 번째 요소는 "PLACEHOLDER" 문자열입니다.