ANDROID] Frida를 이용한 apk 후킹

노션으로 옮김·2020년 4월 27일
3

skills

목록 보기
26/37
post-thumbnail

개요

FridaDBI(Dynamic Binary Instrumentation)도구이다.

DBI란 바이너리를 동적으로 실행하면서 분석하는 행위를 말한다.
Frida는 그와 관련된 유용한 기능들을 제공한다.
스크립트를 이용해 대화형도 가능하며, PINTOOL처럼 원큐 형태로 분석할 수도 있다.

PC뿐만 아니라 모바일 플랫폼도 지원하여 모바일 악성코드 분석이나 CTF 풀이에도 많이 활용된다.


설치

pip 명령어를 이용해 설치한다.

# pip install frida
# pip install frida-tools

설치가 완료되면 쉘 명령어를 실행할 수 있다.

# frida -h
Usage: frida [options] target

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -D ID, --device=ID    connect to device with the given ID
  -U, --usb             connect to USB device
  -R, --remote          connect to remote frida-server
  -H HOST, --host=HOST  connect to remote frida-server on HOST
  -f FILE, --file=FILE  spawn FILE
  -F, --attach-frontmost
                        attach to frontmost application
  -n NAME, --attach-name=NAME
                        attach to NAME

frida-server

대상 단말기에서 frida-server 파일을 실행해줘야 한다.

아래의 사이트에서 환경에 맞는 파일을 다운받는다.

https://github.com/frida/frida/releases

다운받은 파일을 압축해제하고, 다음의 명령어를 이용해 단말기에서 실행시킨다.

# adb root # might be required
# adb push frida-server /data/local/tmp/
# adb shell "chmod 755 /data/local/tmp/frida-server"
# adb shell "/data/local/tmp/frida-server &"

https://frida.re/docs/android/

확인

frida-ps를 이용하여 테스트 해본다.

# frida-ps -U
 PID  Name
----  ------------------------------------
3478  adbd
2022  android.process.acore
2181  android.process.media
1937  com.android.inputservice
2353  com.android.keychain

본론

설치한 frida를 이용해 패킷 암호화 과정에서 특정 값을 제어해보자.

apk 분석

house plant ctf의 문제파일이다.
앱을 실행하면 문제와 보기가 출력되고, 맞는 답을 선택할 경우 다음 문제로 넘어간다.

로그

새로운 문제로 넘어갈 때 로그를 확인해보면

# adb logcat
04-27 17:39:04.389  2024  2693 E WS      : IN: {"method":"question","id":"06c09777-db20-41dc-949b-f9739fd02304","questionText":"Pff91G6VGfv3scsbU8jCn8bt+TPZiiBrjKLoJyXUlkIFHJzs+byYgJRbvTBSQMWmcdQdGKEat7ihPrCY6fFtMepw0c41NEg40jc7agIc5ht49QzJMrh4H3BdMoBvsQfOgdhOVN2QDdSgGBUt4iD/yg==","options":["CwgURuYmO8M/cXsv0IVB1A==","s6OkXzIY9Sf5IVOa31lqew==","P/oEdn2xEgpWYI4ORb7r2urye5MxjsDRV2GNr4hod6c=","cf3yETb4O+NAsgWObp+i2w=="],"correctAnswer":"o1LcVqNplAatnDMh1MsKRc3f0Hwoh/hQ6jH6TaY34MI=","requestIdentifier":"d1df4a572d853f88f5f47fae3418f8b4"}

methodquestionText 등의 값이 JSON 형태로 수신되고 있음을 알 수 있다.
그리고 각각에 해당하는 value는 암호화되어 전송되고 있다.

소스

이 문제는 정답을 자동으로 찾고 제출하여, 플래그 값을 얻어야 하는 문제이다.

따라서 패킷을 암호화 복호화할 수 있어야 하고 그와 관련된 루틴을 확인해야 한다.

디컴파일된 class 파일을 확인해보자.

JSONObject jSONObject = new JSONObject(this.d);
byte[] a2 = nx.a(new nx(Game.this.getIntent().getStringExtra("id"), Game.this.getResources()).a() + ":" + jSONObject.getString("id"));
byte[] b2 = nx.b(jSONObject.getString("requestIdentifier"));
SecretKeySpec secretKeySpec = new SecretKeySpec(a2, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(b2);
Cipher instance = Cipher.getInstance("AES/CBC/PKCS7Padding");
instance.init(2, secretKeySpec, ivParameterSpec);
byte[] doFinal = instance.doFinal(Base64.decode(jSONObject.getString("questionText"), 0));

자바로 AES 암호화할 때의 코드형태란 것을 알 수 있다.
다만, 사용되는 초기 벡터값 값을 확인할 수는 없다.

값을 생성하는데 사용하는 nx 클래스의 하위 메소드들을 보면 복호화하기 힘든 형태로 난독화하고 있기 때문이다.

따라서 앱을 후킹하여 실제 전송되는 값을 캡쳐해야 한다.

implementation

후킹 함수를 지정할 수 있다.
후킹된 함수가 호출되면 인자로 전달되는 값과 출력값 등을 확인할 수 있다.

Java.perform(function () {
    var nx = Java.use('nx');
    nx.b.implementation = function(x) {
      console.log(x);
      var tmp = this.b.call(this, x);
      console.log(JSON.stringify(tmp));
      return tmp;
    }
});

출력되는 로그를 보면

$ frida -U -f wtf.riceteacatpanda.quiz -l test.js
...
...
...
Spawned `wtf.riceteacatpanda.quiz`. Resuming main thread!
[Redmi 4X::wtf.riceteacatpanda.quiz]-> 435db0475dae503ff9f8f2e829bc1eff
[67,93,-80,71,93,-82,80,63,-7,-8,-14,-24,41,-68,30,-1]
[Redmi 4X::wtf.riceteacatpanda.quiz]->

nx.b에 전달된 인자 x와 그 인자로 nx.b를 호출했을 때의 결과값을 확인할 수 있다.

overload

overload는 후킹함수가 오버로드된 함수, 즉 동일한 이름을 가진 함수가 여러개일 때 대상을 지정하기 위해 사용한다.

키 값을 생성하는 함수는 라이브러리이므로 중복되는 코드를 가진다.
따라서 overload를 이용해 후킹함수를 지정해줘야 한다.

Java.perform(function () {
    var SecretKeySpec = Java.use('javax.crypto.spec.SecretKeySpec');
    SecretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function(p0, p1) {
        console.log('SecretKeySpec.$init("' + bytes2hex(p0) + '", "' + p1 + '")');
        return this.$init(p0, p1);
    };
});
function bytes2hex(array) {
    var result = '';
    console.log('len = ' + array.length);
    for(var i = 0; i < array.length; ++i)
        result += ('0' + (array[i] & 0xFF).toString(16)).slice(-2);
    return result;
}
# frida -l test.js -U  wtf.riceteacatpanda.quiz --no-pause
     ____
    / _  |   Frida 12.8.20 - A world-class dynamic instrumentation toolkit
   | (_| |
    > _  |   Commands:
   /_/ |_|       help      -> Displays the help system
   . . . .       object?   -> Display information about 'object'
   . . . .       exit/quit -> Exit
   . . . .
   . . . .   More info at https://www.frida.re/docs/home/
                                                                                
[Houseplant::wtf.riceteacatpanda.quiz]-> len = 32
SecretKeySpec.$init("14001d5c791ab9cb6bb714d71324544f6a2acdea8c80f4417f376c6b7bc4902e", "AES")

부록

Failed to spawn: unable to inject library into process without libc

frida 실행 중 위와 같은 오류가 발생했을 때, 버전의 문제일 확률이 크다.

https://github.com/frida/frida/issues/936

frida-server의 release 페이지에서 단말기에 맞는 버전을 다운받아야 한다.


참조

https://hyunmini.tistory.com/94
: dbi 개념

https://tech.sangron.com/archives/300
: java 암호화 순서도

https://m.blog.naver.com/ssod015/220271930218
: 암호화 용어

https://awakened1712.github.io/hacking/hacking-frida/
: frida cheat

0개의 댓글