본문 바로가기
리버스 엔지니어링/치트엔진 튜토리얼

[6편] 치트엔진 튜토리얼 - Step 6 (포인터와 오프셋)

by leedg 2022. 12. 6.
반응형

치트엔진 튜토리얼 실행

우선 치트엔진 튜토리얼을 하기 위해서, 치트엔진과 튜토리얼 프로그램을 켜보도록 하겠습니다.

치트엔진을 킨 상태에서 Help-> Cheat Engine Tutorial (64-Bit) 클릭

 


치트엔진 튜토리얼 - STEP 6

우선, 아래와 같은 사진의 프로그램이 실행되었다면 Process Attach를 해보도록 하겠습니다.

치트엔진 튜토리얼 화면

 

여러가지 프로세스 중에서 아래 사진과 같이 Tutorial-x86_64 를 Open 해 줍니다.

Process List 화면

STEP 6 설명

Step6는 아래 사진에서 볼 수 있듯이 <100>이라고 써져있는 값이 있습니다 .

이 값은 Change value 버튼을 눌렀을 때 임의의 숫자로 변경됩니다.

그리고 Change pointer버튼을 눌렀을 때 위에 출력되는 값이 가지고있는 주소가 변경이 됩니다.

하지만 포인터와 오프셋을 찾고 둘을 연동시켜주면 아무리 위에있는 주소가 변경이 되도 그 변경된 주소를 가리키기 때문에 값을 바로 찾을 수 있게 됩니다.

Step 6의 미션은 포인터를 찾고 치트엔진의 Active 기능을 활용해서 값을 5000으로 고정하는것입니다.

치트엔진 튜토리얼 Step 6의 화면


진행에 앞서 C언어에서 포인터라는 개념을 알고 치트엔진에 입문해도 치트엔진 상의 포인터에 대해서 바로 이해할 순 없습니다. 저같은 경우도 예외는 아니였고 계속 학습을 나아가다 보면 어느순간 딱 하고 이해되는 순간이 있을 겁니다!

 

먼저 오프셋과 포인터에 대해 간략한 예제와 함께 설명을 진행하도록 하겠습니다.(중요!! 별표 5개 반드시 다 읽어보세요!!)

(게임 해킹을 진행하려면 이 포인터와 오프셋의 개념은 상식중에 상식입니다!!)

 

포인터

보통 게임에서 해킹을 진행할 때, 치트엔진에서 포인터를 찾게 됩니다. 포인터를 찾는 이유는 보통 게임이나 프로그램이 껐다가 다시 키면 프로그램안에 있는 변수(ex. 체력값, 플레이어 좌표 등등)의 메모리주소는 계속 변경이 됩니다. 우리는 게임에서 핵을 만들려고 할 때, 게임이 껐다가 켜지면 다시 메모리를 찾는 수고를 덜어야 하기 때문에 포인터를 찾게 됩니다.

포인터를 찾게 되면 포인터는 프로그램안에 있는 변수의 메모리 주소를 오프셋과 함께 가리키고 있게 됩니다.

이런 로직으로 게임을 껏다 켜도 (포인터 + 오프셋)의 구조로 바뀐 메모리의 주소의 위치를 바로바로 알 수 있게됩니다.

(이 내용은 아래에서 튜토리얼 직접 해보며 다시 확실하게 이해해 봅니다!)

오프셋

struct player { // <- player 의 포인터를 찾고 ex ("ac_client.exe" + 0x123456)
	int health; // 0x0 만큼 떨어져 있음 (포인터로부터)
	int ammo; // 0x4 만큼 떨어져있음 (포인터로부터)
	float x; // 0x8 만큼 떨어져있음 (포인터로부터)
	float y; // 0xC 만큼 떨어져있음 (포인터로부터)
	float z; // 0x10 만큼 떨어져있음 (포인터로부터)
};

 

보통 게임은 위 코드처럼 player라는 구조체가 있고 그 player는 보통 플레이어의 기본적인 정보를 변수로 선언해서 나타내는 구조를 가지고있습니다. 이러한 구조체는 player를 나타내는 포인터에서 주석처리해둔것과 같이

차례대로 0x0, 0x4, 0x8, 0xc, 0x10의 크기만큼 떨어져있다는 뜻이 됩니다.

 

한마디로 게임 내에서 player를 가르키는 포인터를 찾으면 player 구조체 안에 들어있는 변수들은 0x0부터 시작해서

일정한 크기만큼 떨어져 있다는 얘기입니다.


이제 튜토리얼을 진행해보도록 하겠습니다

먼저 튜토리얼에 표시된 숫자 100을 가리키고 있는 주소를 검색을 해보도록 하겠습니다

튜토리얼 스텝 6의 화면에 표시된 숫자
100을 검색후 New Scan 해서 값을 검색합니다 (75개의 검색된 결과가 나옴)

여기서 Change value 버튼을 클릭해서 값을 변경후 변경된값을 다시 검색해주도록 하겠습니다.

Change value버튼을 누르고 숫자가 485로 변경되었습니다.

485를 Next Scan하자 1개의 주소만이 남았습니다. 주소를 테이블로 옮겨주세요.

485를 Next Scan하자 1개의 주소만이 남았습니다!

 

테이블로 옮긴 후 이제 이 주소의 포인터를 찾아볼예정입니다.

(오늘 여기서 찾는 포인터 방법 이외에도 포인터를 찾아낼 수 있는 방법은 여러가지가 있습니다)

 

우클릭 -> Find out what accesses this address를 클릭해줍니다.

우클릭 -> Find out what accesses this address 클릭

 

이 창에서는 주소에서 어떤 것이 접근이 되는지 모든 접근에 대한 어셈블리를 띄워줍니다.

Find out what accesses this address클릭했을 때 나오는 창(화면)

그리고 우리는 이 창에서 어떤것이 접근이 되는지를 알고 싶기 때문에 Change value버튼을 눌러봅니다.

Change value 버튼 클릭!

 

그럼 아래 사진과 같이 4개의 접근을 보여줍니다

이 접근에서 어셈블리 코드를 볼 건데 먼저 대괄호 안에 있는건 저번 튜토리얼에서 배운것과 같이 주소를 나타낼 때 사용합니다. 이 4개의 접근에서 포인터의 형태는 [("모듈 이름" + 주소) ex) "ac_client" + 0x123456]로 형성이 됩니다.

mov 명령어에서 앞에있는 레지스터 뒤에있는 레지스터로 나눈다고 하면 앞에있는 레지스터는 대괄호 안에 쌓여있어야 한다는 소리가 됩니다. (포인터는 위 빨간색처럼 주소로 처리되기 때문에(대괄호안에 있는건 주소를 표시함))

따라서 아래 사진에서 2번째에 위치한 mov [rdx], eax가 우리가 찾는 어셈블리어라고 볼 수 있습니다.

[rdx]가 포인터고 eax는 change value를 눌렀을 때 넣을 랜덤한 숫자라고 볼 수 있습니다.

4개의 접근을 보여줍니다

두번째 접근에 있는 rdx 라는 레지스터 안에 포인터가 있다는것을 알았기 때문에 아래 사진과 같이 두번째 접근을 선택해주고 아래에 여러가지 정보가 뜨는 창에서 RDX의 주소를 복사해줍니다.

RDX의 값인 0x00000000000BB230 를 복사하였습니다.

rdx의 값이 0x00000000000BB230라는것을 이제 알았기 때문에 00000000000BB230를 치트엔진에서 검색해줍니다.

똑같이 Scan을 하는데 이주소는 16진수(Hex)로 표현되는 메모리 주소이기 때문에 아래 사진과 같이 왼쪽에 있는 Hex 체크박스에 체크를 하고 Value에 주소를 넣어 New Scan으로 검색해보도록 하겠습니다.

Hex체크 하고 검색한 후 초록색깔로 표시되는 주소가 검색되었습니다!

여기서 초록색으로 검색된 결과 한개가 나왔는데, 치트엔진에서 초록색으로 검색되는 값은 모두 포인터를 나타냅니다.

한마디로 우리는 아까 Value를 나타냈던 포인터를 찾은셈인거죠!

 

이 포인터를 테이블로 가져와주세요

포인터를 테이블로 가져왔습니다.

아까 제가 포인터의 구조가 어떤식으로 생겼는지 예시를 들었는데 여기서는 어떻게 생겼는지 직접 확인을 해보겠습니다.

포인터의 Address에 해당하는 부분을 더블클릭 해주세요!

포인터의 구조 화면

그럼 위 사진에 보이는것과 같이 제가 아까 말했던 구조처럼 Address 가 형성되어있는것을 알 수 있습니다.

여기서 오프셋의 개념이 사용되는데, 공식을 설명드리겠습니다.

 

오프셋 = (찾을 메모리 주소) - (포인터의 값(16진수 형태))

 

윈도우가 제공하는 계산기에서는 프로그래머용 계산기가 있어서 쉽게 계산할 수 있습니다.

 

먼저 위 공식에서 찾을 메모리의 주소는  위 사진에있는 Address부분이 되겠습니다.

그리고 두번째로 찾을 포인터의 값(16진수 형태) 는 아래 사진에서 Value의 부분이 되겠습니다.

 

가끔 이 포인터의 값이 16진수인지 10진수인지 모를 수도 있는데 이를 확인하는 방법은 테이블에 있는 주소를 우클릭

하면 아래 사진과 같이 Show as decimal 이나 Show as hexadecimal가 나오게 됩니다

 Show as decimal이 뜬다면 값이 16진수로 뜨고 있는거고 Show as hexadecimal로 뜬다면 값이 10진수로 뜨고있는겁니다.

 

이제 직접 이 공식을 적용시켜보면 0x000BB230 - 0x000BB230 = 0x0이 됩니다.

 

그러므로 오프셋의 값은 0x0인것을 알 수 있습니다.

 

 

 

오프셋을 알았으니 포인터에 적용시켜보도록 하겠습니다.

우선, 찾은 포인터의 Address 부분을 더블클릭 해줍니다.

찾은 포인터의 Address부분을 더블클릭 했습니다.

이 화면에서 Pointer의 체크박스에 체크를 해줍니다.

그럼 아래 화면과 같이 체크박스 아래 칸이 한개 생기게 되는데

그 칸에 우리가 찾은 오프셋의 값인 0을 입력해주도록 하겠습니다.

 

여기까지 따라 오셨다면 아마 값이 아래 사진처럼 16진수로 표현되는것을 알 수 있습니다.

10진수로 변경해줍니다. 우클릭 -> show as decimal

테이블 화면 (16진수로 표현되고 있음)

그럼 아래 사진처럼 원래 주소와 오프셋을 적용한 포인터의 Value값이 같다는것을 알 수 있습니다.

테이블 화면 (10진수로 표현되고 있음)
튜토리얼 화면

이후 Step 6의 튜토리얼에 다시 들어가서 Change pointer 버튼을 눌러주도록 하겠습니다.

그럼 위 사진과 같이 값과 함께 포인터가 변경이 되었는데 테이블을 보시면

원래 찾았던 메모리의 주소는 이상한 값을 가지고 있고 포인터는 계속 튜토리얼에 표시된 값을 가지고 있습니다.

 

 

이제 포인터의 Value 값을 5000으로 맞춰주고 왼쪽 끝에있는 Active(고정)체크박스에 체크를 해주면 됩니다.

여기까지 하셨으면 다시 Change pointer 버튼을 눌러봅시다!

 

그럼 아래사진과 같이 Next버튼이 활성화되게 됩니다!!!!!!!!!!!!!!!!!!

Next 버튼이 활성화 되었습니다!!

 

 

해킹 강의 : https://studycodes.tistory.com/16

댓글