개요
이전 "치트 엔진을 이용한 동적 메모리 값을 변조하는 방법" 포스팅에서는 "어썰트 큐브" 게임을 대상으로 동적 메모리 값을 변조하는 방법을 살펴봤다. 동적 메모리의 경우 프로그램이 실행되는 동안 데이터를 저장하고 처리하기 위해 게임 엔진에 의해 할당되는 메모리로 프로그램이 재실행 될 때 마다 주소가 변경된다. 반면, 정적 메모리는 동적 메모리와 반대로 고정적인 주소를 가지며, 이를 통해 프로그램 내부의 포인터로 직접 참조가 가능해진다. 때문에, 프로그램 재실행 여부와 상관 없이 정적 메모리 주소만 알고 있다면 사용자가 원하는 포인터로 직접 액세스가 가능해진다.
이번 포스팅에서는 치트 엔진을 통해 이러한 정적 메모리 주소를 찾는 방법과 또, 이를 변조하는 방법을 다뤄보도록 하겠다.
실습 준비
치트 엔진 다운로드
어썰트 큐브 다운로드
치트 엔진 및 어썰트 게임에 관한 자세한 사용 방법은 이전 포스팅을 참고바란다.
실습
먼저 어썰트 큐브를 실행하고 봇전으로 진입해준다.
치트 엔진을 실행하여 어썰트 큐브 프로세스를 어태치 해준다.
이번 포스팅에서도 이전 포스팅과 동일하게 캐릭터의 체력(HP)를 타겟으로 정적 메모리 값을 찾을 것이다. 먼저 동적 메모리 주소를 찾아준다. 동적 메모리 값을 찾는 방법을 모른다면 이전 포스팅을 참고하자.
찾은 동적 메모리 주소를 클릭하고 마우스 오른 쪽 버튼을 클릭한 다음 "Find out what accesses this address" 버튼을 클릭해준다. 해당 기능의 경우 어태치하고 있는 프로그램을 디버깅하여 대상의 메모리 주소에 액세스하는 명령어 또는 코드를 찾을 때 사용한다.
[그림 5]와 같이 디버깅 관련 알림 팝업이 출력되는데 "YES"를 클릭해준다.
그러면 [그림 6]과 같이 화면이 출력되며, 출력된 정보가 바로 캐릭터 체력에 관한 데이터 구조체 정보이다. 여기서 캐릭터의 체력을 소모시키거나 증감시키면 해당 주소로 액세스하는 다른 주소 정보가 기록되는데 기록이 다 되면 하단의 stop 버튼을 클릭해준다.
필자의 경우 캐릭터의 체력을 소모시켰으며, [그림 7]과 같이 sub 연산자와 관련된 메모리 주소가 액세스된 것을 볼 수 있다. 참고로 sub연산자는 두 값을 뺄셈하는 연산을 수행하는 명령어이다.
그리고 기록된 메모리 주소를 클릭하면 팝업 창 하단에서 메모리 시작 주소인 ESI 값을 확인할 수 있으며, 해당 주소가 바로 플레이어 캐릭터의 구조체를 가르키는 주소이다.
다시 메인 화면으로 돌아와서 "Memory View" 버튼을 클릭해준다.
상단의 "Tools" 탭을 클릭한 다음 "Dissect data/structures" 버튼을 클릭해준다.
"Group1" 입력 폼에 [그림 8]에서 확인한 플레이어 캐릭터 구조체 주소(ESI)를 입력해주고 상단의 "Structures" 탭을 클릭한 다음 "Define new structure" 버튼을 클릭해준다.
"Structur Name"에는 임의의 문자열을 입력하고 메모리 사이즈는 4096으로 지정 후 "OK" 버튼을 클릭해준다.
그러면 "Group 1"에 지정한 ESI 주소 즉, 플레이어 캐릭터 구조체 정보를 확인할 수 있으며, [그림 13]과 같이 캐릭터 체력을 가리키는 "0xF8" offset 값도 확인이 가능하다.
이제 체력에 해당하는 실제 데이터 offset 값을 확인했으니 치트 엔진에서 플레이어 캐릭터 구조체 주소(ESI)를 찾음 다음 해당 주소에 0xF8 만큼의 offset 값을 더해주면, 캐릭터의 체력을 가르키는 실제 포인터 값을 찾을 수 있게 된다. "New Scan" 버튼을 클릭하여 새로운 메모리를 스캔해준다.
Hex를 체크해주고 플레이어 캐릭터 주소(ESI)를 입력한 다음에 "First Scan" 버튼을 클릭해준다.
[그림 16]과 같이 플레이어 캐릭터 구조체 주소(ESI)를 가르키는 3개의 베이스 주소를 확인할 수 있으며, 이 중 하나를 더블 클릭하여 메모리 테이블에 추가해준다.
메모리 테이블에 추가된 주소를 더블 클릭하면 [그림 17]과 같이 메모리 주소 변경 관련 팝업이 출력되는데 여기서 Address를 복사해준다.
① Pointer를 체크 후 ②에 복사한 주소를 붙여넣기 하고 ③에 [그림 13]에서 확인한 offset 값을 입력한 다음 ④ Hexadecimal를 체크해제 하여 값을 10진수로 출력되게끔 해준다.
모든 과정이 끝났다면 [그림 19]와 같이 캐릭터의 체력 값을 가리키는 포인터 주소 즉, 정적 메모리 주소를 획득할 수 있게 된다. 해당 주소는 게임이 재실행 되어도 변하지 않으며, 따로 저장하고 있다가 불러오기 기능을 통해 해당 값에 바로 액세스 할 수 있게 된다. 추후 포스팅에서도 설명하겠지만 지금 이와 같은 방법은 게임 핵을 만들 때도 사용되는 방법 중 하나이다.
마무리
지금까지 어썰트 큐브 게임을 대상으로 정적 메모리 주소를 획득하는 방법을 살펴봤다. 변조를 하고자 하는 대상 주소를 가르키는 정적 메모리 주소만 획득할 수 있다면, 게임이 재시작 되어도 일일히 다시 메모리 주소를 찾을 필요가 없어 굉장히 편하다. 그리고 해당 방법은 실제 게임 핵 제작 시 사용되는 방법 중 하나이며, 추후 포스팅에서 게임 핵을 만들 때도 사용할 예정이다. 다음 포스팅에서는 치트 엔진을 이용한 코드 인젝션을 살펴보도록 하겠다.
'안드로이드 기타 > 게임 분석' 카테고리의 다른 글
치트 엔진을 이용한 코드 인젝션 (1) | 2023.08.21 |
---|---|
치트엔진을 안드로이드 디바이스에 연결시키는 방법 (1) | 2023.08.21 |
치트 엔진을 이용한 동적 메모리 값을 변조하는 방법 (0) | 2023.08.01 |
안드로이드 Assembly-CSharp.dll 덤프 모듈 frida-il2cpp-bridge (0) | 2023.07.18 |