본문 바로가기

4-6. 2022-1 심화 스터디/모바일 게임 해킹

[2022.05.07] 치트 엔진(Cheat Engine)을 활용한 게임 해킹 강좌

Tutorial 6

문제: pointer의 값을 5000으로 바꾼 후 값을 얼려라.

change value 버튼: 포인터가 가리키는 값을 바꾸는 버튼. 누를 때마다 값이 바뀜.(포인터가 가리키는 값이 바뀜) change pointer 버튼: 포인터 자체를 다른걸로 바꾸는 기능. 외관상 똑같이 값이 바뀌는 것 같지만 pointer가 갖고 있는 값 자체가 바뀌는 것임.(포인터가 바뀜)

  1. change value 버튼을 클릭해서 현재 내 값이 497이기 때문에 497을 value로 갖는 address를 스캔함. 더블클릭하여 아래에 있는 주소 리스트에 추가해주었음.

  1. change pointer 버튼을 눌렀고 화면에 출력된 값이 바뀐 걸 확인할 수 있음.

  1. 그러나 1.에서 등록한 주소 리스트의 value는 변하지 않았음.

이렇게 프로그램에 포인터를 사용하면 우리가 치트엔진 같은 프로그램을 이용해서 어떠한 값을 추적하는 것이 굉장히 어려워진다는 것을 알 수 있다.

포인터를 사용하는 프로그램을 어떻게 추적할 수 있을까?

 

  1. New Scan 버튼으로 치트엔진을 리셋하고 현재 프로그램에 출력된 값이 166이기에 166을 First scan한다.
  1. 프로그램에서 Change value 버튼을 누르고 치트엔진의 address에서 바뀐 값을 체크하고 주소 리스트에 넣어준다.(나의 경우 166→422로 바뀌었음)
  1. 이제 Pointer 자체를 찾기 위해 주소리스트에 등록된 값을 우클릭 > Find out what accesses this address 을 누른다. 이 기능은 어떤 부분이 이 주소에 접근하는지, 참조하는지를 확인할 수 있다. 포인터 변수를 찾을 때 사용하는 기능이다.

  1. 프로그램에서 다시 값(value)를 변경하면 다양한 기계어가 색출된다. 클릭한 영역은 [edx]에 참조하여 eax에 들어있는 값을 [edx]에 넣어준다는 뜻이다. 우클릭 후 More info로 들어가보자.

  1. EDX 레지스터의 값이 01A508C0인 것을 알 수 있다. 어떤 포인터 변수가 이 값을 갖고 있겠구나, 어떤 포인터 변수의 값이 이 주소가 되어야지 그 포인터 변수는 여기에 접근해서 어떠한 값(eax)을 넣어줄 수 있겠구나 를 추측할 수 있다.

  1. New Scan을 누른 후 Value의 hex에 체크한 후 01A508C0를 스캔하고 나온 주소를 주소 리스트에 등록한다.

  1. Add Address Manually 버튼을 눌러 수동으로 포인터 변수를 등록할 수 있다. 우리가 등록할 값이 포인터임을 알리기 위해 Pointer에 체크를 하고 새로 뜨는 아래 화면 입력란에 포인터변수의 주소값인 006566B0을 입력한다. 그럼 → 뒤의 주소(변수)에서 그 변수가 갖고 있는 값이 540이라는 것을 확인할 수 있다.(실제로 프로그램상에서도 540이 출력되어 있음)

  1. Ok 버튼을 누르면 포인터 변수 자체가 주소 리스트에 등록된 것을 확인할 수 있다. 이 상태에서는 프로그램에서 포인터의 값을 변경하더라도 추적이 되는 것을 확인할 수 있다.

  1. 포인터 변수의 값을 5000으로 바꾼 뒤에 Active에 체크에 값을 얼려준다(고정한다).

  1. 이제 프로그램에서 Change Pointer를 눌러 포인터 변수를 바꿔준다 해도 계속해서 동일한 위치를 가리키게 되며 문제가 풀린다.

Password: 013370

Tutorial 7

 

문제: 프로그램 실행 중간에 껴서 우리가 원하는 코드를 삽입하라. (Hit me 버튼을 누를 때 마다 값이 1씩 감소하는 프로그램이 있는데 버튼을 누를 때마다 값이 2씩 증가하도록 Code Injection 수행하기.)

  1. 맨 처음 디폴트 값이 100이니까 100으로 First Scan을 수행하고 Hit me 버튼을 눌러 값을 계속 감소시키면서 따라 감소하는 address를 찾아 주소 리스트에 등록한다.
  2.  

  1. 주소리스트에 들어있는 값을 우클릭 > Find out what writes to this address를 클릭한다. 이 기능은 이 주소에 접근하는 기계어를 볼 수 있다. 프로그램에서 Hit me 버튼을 눌러 다시 값을 감소시키고 나온 Instruction을 더블클릭해 상세정보를 확인한다. sub 명령을 이용해서 [ebx+ ...]의 주소에 담긴 값을 1만큼 감소시킨다고 해석할 수 있다.
  2.  

  1. OK 버튼을 누르고 빠져나와 Show disasseambler 버튼을 누르면 더 상세하게 어셈블리코드를 볼 수 있다. Tools > Auto assemble을 눌러 자동으로 코드 인젝션을 위한 준비를 한다. Template > Code Injection > OK 를 누르면 코드 인젝션을 위한 기본 템플릿이 사진과 같이 마련된다.
add dword ptr [ebx+000004A4], 02
  1. line 10을 건드려 2씩 값이 증가하게 코드를 조작하고 execute 버튼을 누른다.
  1. 프로그램으로 돌아와 Hit me 버튼을 누르면 값이 2씩 증가하는 것을 볼 수 있다.

Password: 525927

Tutorial 8

문제: (4 개의 포인터가 중첩되어 있다.) 특정한 변수를 가리키는 포인터의 포인터의 포인터의 포인터의 포인터를 발견해서 그 값을 5000으로 얼려라.

  1. 현재 프로그램의 value인 1774를 치트엔진에 검색해 주소리스트에 등록시키고 우클릭 > Find out what accesses this address 로 들어가 다시 프로그램에서 change value를 누르고 치트엔진을 확인한다. [esi+18]에 eax값을 넣는다는 것을 볼 수 있다. 따라서 어떠한 포인터는 esi값을 갖고 있다는 것을 알 수 있다. esi의 값을 복사해준 이후에 나와준다.
  2.  

  1. esi값 01AC8D78을 치트엔진에서 hex를 누르고 First Scan 해주고 나온 address를 주소 리스트에 넣어준다.(여러개 나오면 묶어서 한꺼번에 넣어준다.) 1.의 방식과 마찬가지로 이 주소를 참조하는 다른 포인터를 확인해준다. 실질적으로 mov가 어떠한 값을 넣어주는 명령이므로 이 라인을 보면 된다. esi에 [esi] 주소에 들어있는 값을 넣어주라는 의미이다. 현재 우리 포인터 변수의 주소값(을 어떤 address가 갖고 있는지 다시 New Scan 해서 알아보자.
  2.  

  1. 똑같이 앞에서 했던 작업들을 수행하고 mov 명령 라인을 보자. [esi+14]에 담긴 값을 esi로 넣으라는 명령이다. 참고로 [esi+14]는 특정한 주소에 14h를 더해서 참조하겠다는 의미이다. 다시 말해, 현재 주소값에서 14h를 뺀 주소를 어떠한 포인터 변수가 갖고 있다는 의미이다. 즉 현재 우리 address 주소가 01A5A35C인데 여기에 14h를 뺀 값(01A5A348)을 어떠한 포인터 변수가 참조하고 있다는 얘기이다.

  1. New Scan 하고 01A5A348를 검색해 또 같은 작업들을 수행해보자. 3.에서와 동일한 구조로 나왔다. 즉, 현재 주소에서 0C를 빼준 주소를 또 어떤 포인터 변수가 참조하고 있다는 소리이다. 현재 주소는 01A29214이니 0C를 빼면 01A29208이다. 다시 New Scan 해보자.

일반적으로 address에 초록색 표시가 된 주소가 다중 포인터의 시작 주소일 가능성이 크다.

  1. 그럼 이제 최종적으로 이 포인터의 출발점인 포인터 변수가 뜬다. 이를 주소리스트에 등록시킨다.

  1. 지금까지 모은 다중 포인터들을 처리하기 위해 Add Address Manually 버튼을 눌러 수동으로 등록해준다. Pointer에 check를 해주고 출발주소인 006566E0을 하단 메시지박스에 입력하고 바로 위에 오프셋이라고 할 수 있는 0C를 더해준다. 그리고 add Offset을 눌러 다중 포인터를 만들 수 있게 해주고 차차 더해준다. 결과적으로 현재 기록되어 있는 1174를 찾을 수 있게 된다. Description에는 등록할 다중 포인터의 이름을 지정해준다.

  1. 만들어준 다중 포인터의 value를 5000으로 바꾼 후 Active 체크박스를 눌러 얼리고 프로그램에서 Change pointer를 눌러 바꾸더라도 실제적으로 시작 포인터가 가리키고 있는 값은 5000으로 고정되어 있는 것을 확인해볼 수 있다.

Password: 31337157

Tutorial 9

문제: Player1,2는 우리팀, 3,4는 상대팀이다. 우리팀은 체력 100, 상대팀은 500으로 시작하는 상황에서 게임을 실행했을 때 우리팀이 이기도록 하라. (우리팀이 공격을 받아도 체력이 안 깎이게 만들기)

Player 1과 그의 Health, Attack은 하나의 구조체로 묶여 있을 것이다. 다른 플레이어들에게도 마찬가지.

  1. Dave의 체력값(float형)의 위치를 확인하고 주소 리스트에 등록한다. Attack 버튼을 눌렀을 때 공격이 수행되기 때문에 그 기능을 막기 위해서 우클릭 > Find out what writes to this address에 들어가 프로그램을 한 번 더 실행시키고 출력되는 기계어를 확인한다. eax에 담긴 값을 [ebx+04]의 주소에 담겠다는 뜻이다. ebx에는 061F3210값이 담겨 있다.

  1. Dave의 구조체 형태의 변수를 확인하기 위해 주소리스트의 값을 우클릭 > Browse this memory region을 눌러 실질적으로 메모리 영역상에서 061F3210가 어디에 있는지 확인한다. 마지막이 10으로 끝나는 위치(파란블록)를 찾아 왼쪽 아래를 확인하면 잘 찾았다는 것을 의미한다. 파란 블록부터 구조체가 시작하는 부분이라고 추측할 수 있다. 또 아래에는 Player의 이름 정보가 있는데 여기에서(061F3225) 시작주소를 빼면 15h가 나오고 15h가 오프셋이라는 것을 알 수 있다.

  1. 다시 1.의 화면으로 돌아와 Show disassembler > Tools > Auto assemble > Template > Code injection을 눌러 코드 템플릿을 생성하고 Dave의 체력값이 깎이지 않게 코드를 수정한다. 원본 코드에서는 체력을 깎는 명령을 수행한 후 exit하는데 우리는 Player의 이름이 Dave일 경우 체력을 감소시키지 않겠다는 명령을 작성할 것이다.

newmem:부분에서 Player의 아이디값을 검증하는 코드를 작성한다.

  1. line 8: 구조체의 시작주소에서 15h를 더한 값이 ‘Dave’의 시작주소였으니까 ‘D’와 비교하는 코드를 작성했다. line 9: 앞 라인이 참이면 exit으로 점프한다. line 15: 원본 코드에서 mov 명령(체력을 깎는 명령)을 수행하고 알수없는 명령을 실행했는데 이 명령은 우리가 조작하려던 코드가 아니므로 exit: 에 넣어준다.

  1. execute를 하고 프로그램에서 Restart game and autoplay를 눌러주면 Dave를 제외한 플레이어가 체력이 깎여 죽어있다는 것을 볼 수 있다.