Notice
Recent Posts
Recent Comments
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

topcue

IDA JNI 분석 본문

기타

IDA JNI 분석

topcue 2021. 6. 23. 15:26
  • 원글: 2021. 1. 14. 13:01
  • 수정: 오타 수정 및 이미지 링크 수정

IDA를 이용한 JNI(Java Native Interface) 정적 분석


JNI

JNI란 JVM이 native code(C/C++)로 만들어진 코드와 상호작용할 수 있도록 도와주는 인터페이스다.

정확히는 Android가 JAVA 또는 Kotlin으로 작성된 byte code와 native code(C/C++)의 상호작용 방법을 정의한 것이다.

개념과 예제는 아래 링크에서 확인할 수 있다.

https://developer.android.com/training/articles/perf-jni?hl=en

오라클에서 자세한 정보를 제공하기도 한다.

https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html


JNI example

JEB로 APK 파일을 분석 중이었는데, 아래와 같이 native code를 참조한다고 나와있고 함수가 어떻게 정의되어있는지 알 수 없었다.

package com.tencent.mm.jniinterface;

public class AesEcb {
	public static native byte[] aesCryptEcb(byte[] _arg0, byte[] _arg1, boolean _arg2, boolean _arg3) {
	}

	public static native void test() {
	}
}

native code를 분석하기 위해 .so 파일을 분석해야 했다.

어플리케이션이 사용하는 so 파일이 약 80개였는데 어떤 파일에 정의되어있는지 알 수 없었다.

따라서 아래와 같은 명령어로 적절히 찾아주었다.

# Windows
findstr /S "aesCryptEcb" *.so

# OS X or Linux
grep -r "aesCryptEcb" ./

libwechatcommon.so에 있다는 것을 확인했고, 다행히 클래스명으로 검색할 수 있었다.

native code에서는 함수명이 Java_[패키지]_[클래스]_[메소드]으로 정의된다.

Java_com_tencent_mm_jniinterface_AesEcb_aesCryptEcb()

그리고 인자는 두 개가 추가된다. 첫 번째 인자는 JNIEnv*, 두 번째 인자는 jobject다.

그 뒤에 실제로 전달하는 인자들이 위치한다.

이때 인자는 자바 타입에 맞게 변한다.

타입은 아래 링크에서 확인할 수 있다.

https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html

즉 다음과 같이 표현될 수 있다.

  • Java code - aesCryptEcb()
public static native byte[] aesCryptEcb(
	byte[] _arg0, byte[] _arg1, boolean _arg2, boolean _arg3
)
  • nativa code - Java_com_tencent_mm_jniinterface_AesEcb_aesCryptEcb()
void __fastcall Java_com_tencent_mm_jniinterface_AesEcb_aesCryptEcb(
	JNIEnv* env, jobject jobj,
	jbyteArray _param1, jbyteArray _param2, jboolean _param3, jboolean _param4
)

그런데 디컴파일을 하면 인자의 개수나 타입을 제대로 파싱하지 못하는 모양이다.

인자는 다음과 같았다.

(__int64 a1, __int64 a2, __int64 a3, __int64 a4, unsigned __int8 a5, char a6)

무엇보다 JNIEnv 관련 함수를 제대로 파싱하지 못한다.

아래 코드 조각에서 sub_94B14() 함수의 인자인 a1은 JNIEnv* 타입으로 env+offset형태로 함수를 호출한다. a1 + 1408LL

__int64 __fastcall sub_94B14(__int64 a1, __int64 a2, int a3)
{
  // skip..

  v5 = *(__int64 (**)(void))(*(_QWORD *)a1 + 1408LL);

  // skip..
}

실제 offet 1408의 함수는 NewByteArray()이므로 아래와 같이 디컴파일 되어야 한다.

__int64 __fastcall sub_94B14(JNIEnv *a1, __int64 a2, int a3)
{
  // skip..

  v5 = (__int64 (*)(void))(*a1)->NewByteArray;

	// skip..
}

IDA에서 JNI를 분석할 수 있는 몇가지 방법이 있어서 정리한다.


Method 1 - JNIEnv structure table

먼저 수동으로 분석하는 방법이다.

아래 사이트를 보면 env의 특정 offset에 어떤 함수가 있는지 알 수 있다.

https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html

https://gist.github.com/zhangyoufu/5814814

이런 정보를 구글시트에 정리한 자료도 있다.

https://docs.google.com/spreadsheets/d/14GgLwizg6qZzCBVxYc3HfegBGBh5Zff5XpyO3cZP64o/edit?usp=sharing

하지만 수동으로 분석하는 것은 매우 귀찮은 일이다.

다른 방법이 적용되지 않는 경우에만 사용하자.


Method 2 - Set Type JNIEnv*

단순히 타입을 JNIEnv*로 바꾸는 방법이다.

바꾸려는 타입을 우클릭한 뒤 Set lvar type(Y)를 클릭한다.

그리고 타입을 JNIEnv*로 바꿔준다.

많은 사람들이 이 방법을 사용하는 것 같은데 본인은 아래와 같은 오류가 발생한다.

IDA 버전이나 바이너리에 따라 적용이 안되는 경우도 있는 것 같다.

그래서 다른 방법을 더 찾아보았다.


Method 3 - Parsing jni.h

JNI type을 미리 정의한 헤더파일을 적용하는 방법이다.

이 방법은 IDA 버전에 상관없이 적용 가능하다.

JNIEnv를 파싱하도록 도와주는 jni_all.h 파일이 아래 github에 공개되어있다.

https://gist.github.com/Jinmo/048776db75067dcd6c57f1154e65b868

파일을 다운받고 IDA에서 적용할 수 있다.

FileLoad fileParse C header filejni_all.h 선택

이후 Method 2를 적용하면 에러 없이 잘 적용된다.


Method 4 - Add standard stuctures

JNIEnv관련 타입을 standard stuctures에 추가하는 방법이다.

이 방법은 IDA 7.0 이상에서 적용가능하다고 확인되었다. (이전 버전에서 테스트해본 사람은 못봤다.)

게다가 추가적인 파일이 필요없고, IDA 기능만으로 적용할 수 있다.

먼저 View → Open subviews → Type libraries 탭으로 이동한다.

추가를 위해 우클릭 후 Load type library 클릭

android_arm 추가

이제 standard structure에 추가해줘야 한다.

Edit → Add struct type (tool bar 이용 시 Define a new structure/union type)

Add standard structure

_JNIEnv 추가

그러면 아래처럼 standard type에 _JNIEnv 구조체를 확인할 수 있다.

이제 분석이 필요한 함수로 이동한다.

첫 번째 인자의 타입 우클릭 후 Convert to struct *

그러면 아까 추가한 _JNIEnv 타입으로 바꾸어 줄 수 있다.

그러면 IDA가 제대로 디컴파일 해준다.


참고

JNI tips: https://developer.android.com/training/articles/perf-jni?hl=en

JNI 예제: https://realapril.tistory.com/35

JNIEnv Structure Table:

https://docs.google.com/spreadsheets/d/14GgLwizg6qZzCBVxYc3HfegBGBh5Zff5XpyO3cZP64o/edit?usp=sharing

IDA decompiles so files into Jni: https://www.programmersought.com/article/19415131063/

IDA JNI Function 적용하기: https://chp747.tistory.com/330

Ida Restore Jni Function method name: https://topic.alibabacloud.com/a/ida-restore-jni-function-method-name_8_8_31390896.html


Comments