직접 주입한 frida-gum 사용하기

직접 주입한 frida-gum 사용하기

2024년 11월 12일

서론 #

출처: Frida-gum을 이용한 Android Hook - 라온

출처의 글이 4~5년 정도 되기도 했고 지금까지는 frida를 이렇게까지 쓴적이 없었지만, 이해도와 숙련도를 높이기 위해 정리해보려고 한다.


frida-gum 라이브러리 #

일반적인 프리다 사용 방식은 frida 프로젝트를 빌드하면 생성되는 frida-server 를 실행하고 cli와의 통신으로 frida-agent가 라이브러리 형태로 앱에 주입되어 내부의 frida-gum 모듈을 통해 C/C++ 함수의 후킹이 가능하게 된다.

그래서 이런 방법이 있는지도 모르고 사용하고 있었는데, frida-gum을 라이브러리 형태로 빌드하고 이걸 주입해서 후킹코드를 작성하여 클라이언트처럼 사용하는 방식이 있었다.

한번 해보자


예제 테스트 #

테스트 환경 #

  • wsl ubuntu 24.04
  • Android 13 (sdk 33) arm64
  • android-ndk-r25c (현재 지원하는 최신버전)
  • frida-gum 16.5.6

libfrida-gum.a 라이브러리 빌드 #

devkit으로 빌드하면 라이브러리와 예제 파일을 얻을 수 있다.

meson 빌드 시스템을 사용하며, configure -> releng/meson_configure.py -> meson setup 로 연결되고 기본적으로 meson.options 파일에 정의된 기본 옵션들과 인자로 전달한 추가 옵션을 파싱해서 빌드 디렉터리를 생성하게 된다.
이후 make -> Makefile -> releng/meson_make.py -> meson compile 순서로 호출되어 빌드되며 빌드디렉터리 내에 정의된 옵션과 meson.build 에 정의된 규칙에 따라 frida가 빌드되는 구조를 갖고 있다.

1git clone --recursive https://github.com/frida/frida-gum.git
2git checkout tags/16.5.6
3make
4mkdir android-arm64
5cd android-arm64
6../configure --host=android-arm64 --with-devkits=gum
7make 
8cd ./gum/devkit

73fe36a7-9d20-47d8-9c1a-9ae344b7b9eb

예제 파일 빌드 및 실행 #

예제 코드를 보면 맨위에 주석으로 어떻게 빌드하는지도 적혀있다. 하지만 나는 크로스플랫폼 타겟(안드로이드)으로 작성할 것이기 때문에 clang을 ndk에 있는걸로 써야한다.

1~/android-ndk-r25c/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android33-clang -DANDROID -ffunction-sections -fdata-sections frida-gum-example.c -o frida-gum-example -L. -lfrida-gum -llog -ldl -lm -pthread

빌드된 파일을 실행하면 이렇게 보인다. e7e2f8b9-a0b1-409d-a1b3-3ec019cdffc3


구조 분석 #

example 코드는 간단하다. 그냥 리스너 attch 후 open, close를 후킹하고 호출하고, 리스너 detach 후 호출하기만 한다.

 1int
 2main (int argc,
 3      char * argv[])
 4{
 5  // ... 인터셉터를 생성하고 리스너를 opem, close 함수에 attach 한다. 
 6  interceptor = gum_interceptor_obtain ();
 7  gum_interceptor_begin_transaction (interceptor);
 8  gum_interceptor_attach (interceptor,
 9      GSIZE_TO_POINTER (gum_module_find_export_by_name (NULL, "open")),
10      listener,
11      GSIZE_TO_POINTER (EXAMPLE_HOOK_OPEN));
12  gum_interceptor_attach (interceptor,
13      GSIZE_TO_POINTER (gum_module_find_export_by_name (NULL, "close")),
14      listener,
15      GSIZE_TO_POINTER (EXAMPLE_HOOK_CLOSE));
16  gum_interceptor_end_transaction (interceptor);
17
18  // 테스트 1. 오픈, 클로즈 테스트 
19  close (open ("/etc/hosts", O_RDONLY));
20  close (open ("/etc/fstab", O_RDONLY));
21
22  // 리스너를 detach 한다. 
23  gum_interceptor_detach (interceptor, listener);
24
25  // 잘 detach 됐는지 확인. 이때는 로그출력이 안된다. 
26  close (open ("/etc/hosts", O_RDONLY));
27  close (open ("/etc/fstab", O_RDONLY));
28
29  // detach 이후에 리스너가 호출됐는지 확인하는 코드. 
30  // 위에서 볼 수 있듯 여전히 4 call 이다.  
31  g_print ("[*] listener still has %u calls\n", EXAMPLE_LISTENER (listener)->num_calls);
32  // ... 
33}
34
35// on_enter 리스너이다. 
36static void
37example_listener_on_enter (GumInvocationListener * listener,
38                           GumInvocationContext * ic)
39{
40  ExampleListener * self = EXAMPLE_LISTENER (listener);
41  ExampleHookId hook_id = GUM_IC_GET_FUNC_DATA (ic, ExampleHookId);
42
43  switch (hook_id)
44  {
45    case EXAMPLE_HOOK_OPEN:
46      g_print ("[*] open(\"%s\")\n", (const gchar *) gum_invocation_context_get_nth_argument (ic, 0));
47      break;
48    case EXAMPLE_HOOK_CLOSE:
49      g_print ("[*] close(%d)\n", GPOINTER_TO_INT (gum_invocation_context_get_nth_argument (ic, 0)));
50      break;
51  }
52  // 호출될 때마다 호출 카운트 증가
53  self->num_calls++;
54}

직접 인젝트해서 사용 #

예제코드는 자기 자신을 후킹하는 것이기 때문에 api 테스트는 가능하지만 의미 없는 코드이다.

테스트 #

공유 라이브러리 형태로 빌드 #

main 함수를 라이브러리 로드하면서 실행할 수 있도록 .init_array 등록 코드로 변경한다.

1- int main(int argc, char * argv[])
2
3+ __attribute__((constructor))
4+ int init(void *arg)

빌드할때는 공유 라이브러리 형태로 빌드되도록 -shared 옵션을 추가하면 된다.

1~/android-ndk-r25c/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android33-clang -DANDROID -ffunction-sections -fdata-sections frida-gum-example.c -o frida-gum-example -L. -lfrida-gum -llog -ldl -lm -pthread -shared

60771c71-f492-421d-beae-ef16ebacd4ca

인젝터 빌드 #

~ 이 깃허브에서 빌드해서 사용하면 된다. ~ 방식으로 ~ 에러가 발생하기 때문에 이동시켜서 인젝트 한다.

실행 #


—– so injection 에 대해 공부 후 진행 —– #

comments powered by Disqus