Custom Binary Blocked by FRP Lock + Rooting
갤럭시 벽돌 해결 순정 펌웨어 업데이트 + 안드로이드 루팅
Unity로 제작된 apk 에서 Filed Hooking
Unity를 사용하는 어플을 프리다를 사용해 후킹을 연습하기 위해 제작된 apk파일이다.
https://github.com/kylesmile1103/Learn-Frida
Apk파일을 열어보면 lib 폴더내에 여러 아키텍처에 맞는 폴더가 존재하며 다음과 같이 세개의 네이티브 라이버르리 파일이 포함되어 있다.
libunity.so 는 초기에 먼저 로드되어 il2cpp 실행에 대한 여러 초기화 등의 설정을 한 뒤 libil2cpp.so를 로드하여 작동하게 된다.
실제 기계어로 변환된 코드들은 libil2cpp.so 파일에 존재하며 해당 파일을 변조하여 앱을 수정할 수 있다.
해당 파일을 보면 기계어(어셈블리어)로 되어있기 때문에 mono와 달리 어셈블리어를 모르면 수정을 할 수가 없다. 어셈블리어를 안다면 간단한 리턴값 수정이나 nop패치등을 통해 간단하게 변조할 수 있다.
다만 mono처럼 악성광고 삽입, 특정 함수 호출코드등의 까다로운 방법은 쉽게 패치할 수 없다.
해당 il2cpp.so 로 만들어진 바이너리에 대한 필드, 메소드 등의 정보와 실제 위치를 il2cppDumper 툴을 이용하여 알아낼 수 있다.
https://github.com/Perfare/Il2CppDumper/releases
Dumper툴을 이용하여 so파일과 global-metadata.dat을 선택하면 dump.cs파일을 생성해주며 이 파일 안에는 해당 어플의 코드와 오프셋등을 표현해준다.
여기까지가 어플 후킹전의 기본 준비 과정이다.
libil2cpp.so 파일의 어드레스를 알려주는 코드이다.
il2cpp.so파일 뿐만 아닌 어떠한 모듈의 함수든 가능하다.
우선 앱에서 ShowValueField 버튼을 눌렀을때를 후킹하는 코드이다. ShowResult() 메소드의 offset주소를 가져와 코드를 작성 할 수 있다.
this를 사용해 기존 인스턴스를 불러와준 후 private int a의 주소값인 0x18을 넣어주어 a에 저장해 준후 readInt()를 사용하여 a를 불러오는 코드이다.
int값이기에 readInt()를 사용하여 불러줘야 오류가 생기지 않는다
readByteArray(number) = this function will dump address to array of byte(AOB) or hex, so we can analyeze it easier
0x24의 값은 10진수로 36의 값을 나타낸다.
writeInt()함수를 이용하여 기존값인 36의 int값을 100으로 변경후 showValueFiled 버튼을 눌르면 a가 100의 값으로 변경된 것을 확인 할 수 있다.
3.1 과 거의 동일하게 진행되는 코드이다. boolean 즉 참,거짓을 바꿔줘야되는데 참 거짓은 1과0으로도 표현이 가능하다
a와 동일하게 private bool b의 주소값 0x1C 를 가져와서 참값인 1로 바꿔주면 항상 참이되게 변경된다.
Unity app 에서는 String을 읽어들여올때 readUtf16String()을 사용한다.
근데 string값이 읽혀지지가 않았다.. 검색해보니 맞는지는 몰르겠지만 최근 str의 밸류값을 string이 아닌 pointer로 쓴다는 얘기가 나왔다.
readByteArray()를 이용하여 hex값을 dump해보니 readPointer()를 사용했을때는 스트링 값이 나왔지만 사용하지 않았을때는 나타나지 않았다.
H의 시작점은 0x14이다. readPointer()에 .add(0x14) 를 추가하여 H부터 읽어오도록 설정할 수 도 있다.
이제 정확한 시작 위치를 지정해 주었기에 readUtf16String()으로 값을 읽어들일수 있다.
wrtieUtf16String()으로 원하는 String값으로 변경을 해주었지만 기존 string의 address 값과 변경된 값의 길이가 달라 값이 깨져서 나오게된다.
부족한 값을 추가하여 더 채워주면 값이 할당되는 부분까지 인식되어 추가되고 문법이 깨지지 않게된다.
Float 실수형도 앞에 int형과 동일하게 간단하게 변경할 수 있다.
int형으로 된 배열의 값을 변경해야한다. int형이기에 readInt()를 사용하여 읽어준다.
그러나 값이 정상적으로 읽히지 않는다. 이것또한 찾아보니 배열도 pointer로 사용되는거 같다.
항상 포인터로 사용되는것인지 한번 찾아봐야겠다...
pointer를 사용했지만 제대로 읽히지 않았다. 아마 배열이기에 그런거같다..
0x18에 무언가 값이 찍혀있으니 여기부터 읽어봐야 할꺼같다.
length의 길이가 3이 찍혀 나왔다. 현재 배열의 값을보니 0,0,0 총 3개가 들어가있어서 그런거같다. int형이니 4byte씩 3번을 체크하면 값이 나올꺼같다.
배열의 값이 0,0,0 으로 되있어 찾기 힘들어 값을 변경후 검색해보니 0x20 부터 값이 들어가게 되있었다.
int형이기에 writeInt()를 사용하여 원하는 값을 넣어주니 배열의 값이 변경되는것을 확인할 수 있다.
List에 들어간 int형의 값을 바꾸는 문제이다.
ArrayList 또한 pointer로 읽어줘야한다. 우선 hex값을 살펴보면 다음과 같이 나온다
이 부분이 아직 잘 이해되진 않지만 0x10에 값이 있기에 0x10을 add시켜준다..? 정도로 이해했다
그 후에 0x20부터 값이 들어가고 int형 4byte씩 값이 변경가능하다.
리스트에 String형이 들어간 경우이다.
헥스값을 읽어들일경우 0x10에 값이 들어가있어 0x10을 추가시켜준다..? 전과 동일하게 이부분을 알아봐야한다.(아시는 분이 있으면...ㅠ)
add(0x10)을 해준후 pointer를 읽어주면 0x20부터 8byte씩 값이 들어가있는것을 확인할 수 있다.
readByteArray를 이용하여 읽어주면은 해당 값이 나오는것을 확인할 수 있다.
여기서 0x14를 해주는게 이해가 안됬는데 ByteArray를 통해 알수 있었다.
또한 처음에 hex값이 안나오고 [object ArrayBuffer] 로 값이 나와 당황했는데 확인해보니 앞에 " " 문자열을 추가해줘서 그런거같다.
ByteArray를 통해 값을 확인할수있는데 0x14 거리떨어진만큼 값이 나와있기에 이값을 추가해줘야한다.
그 후 readUtf16String() 으로 값을 읽어오면 정상적으로 Str 리스트값이 나타나게 된다.