Unity App Frida Field Hookng

Unity App Frida Field Hookng

in

Unity로 제작된 apk 에서 Filed Hooking

1. 준비과정

Unity를 사용하는 어플을 프리다를 사용해 후킹을 연습하기 위해 제작된 apk파일이다.
https://github.com/kylesmile1103/Learn-Frida

learn1_1

Apk파일을 열어보면 lib 폴더내에 여러 아키텍처에 맞는 폴더가 존재하며 다음과 같이 세개의 네이티브 라이버르리 파일이 포함되어 있다.
libunity.so 는 초기에 먼저 로드되어 il2cpp 실행에 대한 여러 초기화 등의 설정을 한 뒤 libil2cpp.so를 로드하여 작동하게 된다.
실제 기계어로 변환된 코드들은 libil2cpp.so 파일에 존재하며 해당 파일을 변조하여 앱을 수정할 수 있다.

learn1_2

해당 파일을 보면 기계어(어셈블리어)로 되어있기 때문에 mono와 달리 어셈블리어를 모르면 수정을 할 수가 없다. 어셈블리어를 안다면 간단한 리턴값 수정이나 nop패치등을 통해 간단하게 변조할 수 있다.
다만 mono처럼 악성광고 삽입, 특정 함수 호출코드등의 까다로운 방법은 쉽게 패치할 수 없다.

해당 il2cpp.so 로 만들어진 바이너리에 대한 필드, 메소드 등의 정보와 실제 위치를 il2cppDumper 툴을 이용하여 알아낼 수 있다.

https://github.com/Perfare/Il2CppDumper/releases

Dumper툴을 이용하여 so파일과 global-metadata.dat을 선택하면 dump.cs파일을 생성해주며 이 파일 안에는 해당 어플의 코드와 오프셋등을 표현해준다.

learn1_3

learn1_4

여기까지가 어플 후킹전의 기본 준비 과정이다.

2.Address

libil2cpp.so 파일의 어드레스를 알려주는 코드이다.
il2cpp.so파일 뿐만 아닌 어떠한 모듈의 함수든 가능하다.

learn1_5

3.App Hooking

우선 앱에서 ShowValueField 버튼을 눌렀을때를 후킹하는 코드이다. ShowResult() 메소드의 offset주소를 가져와 코드를 작성 할 수 있다.

learn1_6

learn1_7

3.1 Change Field int a

this를 사용해 기존 인스턴스를 불러와준 후 private int a의 주소값인 0x18을 넣어주어 a에 저장해 준후 readInt()를 사용하여 a를 불러오는 코드이다.
int값이기에 readInt()를 사용하여 불러줘야 오류가 생기지 않는다

learn1_8

learn1_9

readByteArray(number) = this function will dump address to array of byte(AOB) or hex, so we can analyeze it easier
0x24의 값은 10진수로 36의 값을 나타낸다.

learn1_10

writeInt()함수를 이용하여 기존값인 36의 int값을 100으로 변경후 showValueFiled 버튼을 눌르면 a가 100의 값으로 변경된 것을 확인 할 수 있다.

learn1_11

3.2 Change Field bool b

3.1 과 거의 동일하게 진행되는 코드이다. boolean 즉 참,거짓을 바꿔줘야되는데 참 거짓은 1과0으로도 표현이 가능하다
a와 동일하게 private bool b의 주소값 0x1C 를 가져와서 참값인 1로 바꿔주면 항상 참이되게 변경된다.

learn1_12

learn1_13

3.3 Change Field str

Unity app 에서는 String을 읽어들여올때 readUtf16String()을 사용한다.

learn1_13

근데 string값이 읽혀지지가 않았다.. 검색해보니 맞는지는 몰르겠지만 최근 str의 밸류값을 string이 아닌 pointer로 쓴다는 얘기가 나왔다.

learn1_14

readByteArray()를 이용하여 hex값을 dump해보니 readPointer()를 사용했을때는 스트링 값이 나왔지만 사용하지 않았을때는 나타나지 않았다.

learn1_15

learn1_16

H의 시작점은 0x14이다. readPointer()에 .add(0x14) 를 추가하여 H부터 읽어오도록 설정할 수 도 있다.

learn1_17

이제 정확한 시작 위치를 지정해 주었기에 readUtf16String()으로 값을 읽어들일수 있다.

learn1_18

wrtieUtf16String()으로 원하는 String값으로 변경을 해주었지만 기존 string의 address 값과 변경된 값의 길이가 달라 값이 깨져서 나오게된다.

learn1_19

부족한 값을 추가하여 더 채워주면 값이 할당되는 부분까지 인식되어 추가되고 문법이 깨지지 않게된다.

learn1_20

learn1_21

3.4 Change Field float

Float 실수형도 앞에 int형과 동일하게 간단하게 변경할 수 있다.

learn1_22

3.5 Change Filed int[] Array

int형으로 된 배열의 값을 변경해야한다. int형이기에 readInt()를 사용하여 읽어준다.
그러나 값이 정상적으로 읽히지 않는다. 이것또한 찾아보니 배열도 pointer로 사용되는거 같다.
항상 포인터로 사용되는것인지 한번 찾아봐야겠다...

learn1_23

pointer를 사용했지만 제대로 읽히지 않았다. 아마 배열이기에 그런거같다..
0x18에 무언가 값이 찍혀있으니 여기부터 읽어봐야 할꺼같다.

learn1_24

learn1_25

length의 길이가 3이 찍혀 나왔다. 현재 배열의 값을보니 0,0,0 총 3개가 들어가있어서 그런거같다. int형이니 4byte씩 3번을 체크하면 값이 나올꺼같다.

learn1_26

배열의 값이 0,0,0 으로 되있어 찾기 힘들어 값을 변경후 검색해보니 0x20 부터 값이 들어가게 되있었다.

learn1_27

int형이기에 writeInt()를 사용하여 원하는 값을 넣어주니 배열의 값이 변경되는것을 확인할 수 있다.

learn1_28

3.6 Change Field List int형

List에 들어간 int형의 값을 바꾸는 문제이다.
ArrayList 또한 pointer로 읽어줘야한다. 우선 hex값을 살펴보면 다음과 같이 나온다

learn1_29

이 부분이 아직 잘 이해되진 않지만 0x10에 값이 있기에 0x10을 add시켜준다..? 정도로 이해했다
그 후에 0x20부터 값이 들어가고 int형 4byte씩 값이 변경가능하다.

learn1_30

learn1_31

3.7 Change Field List Stinrg형

리스트에 String형이 들어간 경우이다.
헥스값을 읽어들일경우 0x10에 값이 들어가있어 0x10을 추가시켜준다..? 전과 동일하게 이부분을 알아봐야한다.(아시는 분이 있으면...ㅠ)

learn1_32

add(0x10)을 해준후 pointer를 읽어주면 0x20부터 8byte씩 값이 들어가있는것을 확인할 수 있다.

learn1_33

readByteArray를 이용하여 읽어주면은 해당 값이 나오는것을 확인할 수 있다.
여기서 0x14를 해주는게 이해가 안됬는데 ByteArray를 통해 알수 있었다.
또한 처음에 hex값이 안나오고 [object ArrayBuffer] 로 값이 나와 당황했는데 확인해보니 앞에 " " 문자열을 추가해줘서 그런거같다.

learn1_38

ByteArray를 통해 값을 확인할수있는데 0x14 거리떨어진만큼 값이 나와있기에 이값을 추가해줘야한다.

learn1_37

그 후 readUtf16String() 으로 값을 읽어오면 정상적으로 Str 리스트값이 나타나게 된다.

learn1_36