![article thumbnail image](https://blog.kakaocdn.net/dn/brbCD7/btrAPVPvLeV/lHCnbXfae5g2WFkJwF1OEk/img.png)
Smali 파일 변조를 하려면 결국 코드가 어떻게 굴러가는건지 아는게 중요하다
그래야 원리 파악이 가능하다.
기초편 글에서 보았던 코드들 중 일부를 심화분석해보자
루팅 탐지하는 자바코드
먼저 불린(boolean) 메소드 a 를 살펴보면 test-keys를 검사하는 로직인데 이 test-keys가 뭐냐면
안드로이드 단말기 내에 Build.prop이란 설정파일을 살펴보는 것이다.
기존값은 realese-keys 로 되어 있지만 루팅을 하게되면 test-keys로 변경되는 경우가 있다.
private final boolean a() {
boolean o;
String str = Build.TAGS;
f.d(str, "TAGS");
o = p.o(str, "test-keys", false, 2, null);
return o;
}
두번째 b 메소드를 살펴보면 가장 일반적으로 많이 사용되는 루팅탐지 코드로써 루팅 시
생성되는 파일 및 경로 xbin, sbin, Superuser.apk , su 등을 검사하는 로직이다.
파일을 배열로 만들어서 검사 후 있다면 true를 없다면 false를 반환하여 검증한다.
private final boolean b() {
String[] strArr = {"/system/app/Superuser.apk", "/sbin/su", "/system/bin/su",
"/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su",
"/system/sd/xbin/su", "/system/bin/failsafe/su", "/data/local/su",
"/SYSTEM/APP/SUPERUSER.APK", "/SBIN/SU", "/SYSTEM/BIN/SU", "/SYSTEM/XBIN/SU",
"/DATA/LOCAL/XBIN/SU", "/DATA/LOCAL/BIN/SU", "/SYSTEM/SD/XBIN/SU", "/SYSTEM/BIN/FAILSAFE/SU", "/DATA/LOCAL/SU"};
int i2 = 0;
while (i2 < 18) {
String str = strArr[i2];
i2++;
if (new File(str).exists()) {
return true;
}
}
return false;
}
자바코드는 어찌저찌 코드를 해독할 수 있지만 스말리(Smali)코드는 일종의 어셈블리어라서 공부하지 않으면
바로 해석하기가 힘들다.
test-keys를 확인하는 a메소드의 스말리 코드는 아래 그림과 같다.
1. a method를 선언한다
2. a 메소드의 레지스터는 6개로 지정
4. v0 주소에 android/os/Build;->TAGS:Ljava/lang/String; Build.TAGS 호출
6. 스트링 주소 v1 에 "TAGS" 저장
8. v0(object)값과 v1(string=TAGS) 값을 인자로 받아 h.s.c.f -> d(Ljava/lang/Object;Ljava/lang/String;)V 호출
10. 스트링 주소 v1에 "test-keys" 저장
12~16. v2부터 v3 v4 까지 각 주소에 4바이트로 0x0(boolean) , 0x2(int), 0x0(null) 값 저장
18. v0에서 v4까지 인자로 받아 Lh/y/f;->o(Ljava/lang/CharSequence;Ljava/lang/CharSequence;ZILjava/lang/Object;)Z
호출
20. 18번 줄의 결과를 v0 에 저장
22. v0 반환
.method private final a()Z
.registers 6
sget-object v0, Landroid/os/Build;->TAGS:Ljava/lang/String;
const-string v1, "TAGS"
invoke-static {v0, v1}, Lh/s/c/f;->d(Ljava/lang/Object;Ljava/lang/String;)V
const-string v1, "test-keys"
const/4 v2, 0x0
const/4 v3, 0x2
const/4 v4, 0x0
invoke-static {v0, v1, v2, v3, v4}, Lh/y/f;->o(Ljava/lang/CharSequence;Ljava/lang/CharSequence;ZILjava/lang/Object;)Z
move-result v0
return v0
.end method
코드 요약
Build.TAGS를 호출해서 가져온 문자열과 v1주소값에 고정으로 지정된 "test-keys" 문자열을 비교해서 그 결과값을 반환
이어서 불린(boolean) b 메소드의 스말리코드를 살펴보면 아래 그림과 같다.
25. 메소드 b 선언
26. 레지스터 19개 지정
28. 스트링 주소 v0에 "/system/app/Superuser.apk" 저장
마찬가지로 쭉 62번 줄까지 스트링 주소 v17까지 문자열을 저장한다.
64. v0~v17 까지 18개 인덱스의 배열 생성
66. 결과를 v0 에 저장
68. v1 주소에 4바이트 0x0 값 저장
70. v2 주소에 v1 값 저장
72. 분기명 cond_2a 지정
73. v3 주소에 16바이트 0x12값 저장
75. vs >= v3 일 때, cond_3f로 분기
77. 배열 v0에서 v2의 요소를 가져와서 v3 주소에 저장
79. v2+0x1 값을 v2에 저장
81. v4 인스턴스 생성
83. v4, v3 인자값을 받아 Ljava/io/File;-><init>(Ljava/lang/String;)V 호출
85. v4 인자값을 받아 Ljava/io/File;->exists()Z 호출
87. 결과값 v3 주소에 저장
89. v3 주소의 값이 0(Zero) [eqz는 equal Zero의 약자] 일 때 cond_2a 로 분기
91. v0에 4바이트로 0x1 저장
93. v0 값 반환
95. cond_3f 분기명 지정
96. v1 값 반환
97. 메소드 끝
.method private final b()Z
.registers 19
const-string v0, "/system/app/Superuser.apk"
const-string v1, "/sbin/su"
const-string v2, "/system/bin/su"
const-string v3, "/system/xbin/su"
const-string v4, "/data/local/xbin/su"
const-string v5, "/data/local/bin/su"
const-string v6, "/system/sd/xbin/su"
const-string v7, "/system/bin/failsafe/su"
const-string v8, "/data/local/su"
const-string v9, "/SYSTEM/APP/SUPERUSER.APK"
const-string v10, "/SBIN/SU"
const-string v11, "/SYSTEM/BIN/SU"
const-string v12, "/SYSTEM/XBIN/SU"
const-string v13, "/DATA/LOCAL/XBIN/SU"
const-string v14, "/DATA/LOCAL/BIN/SU"
const-string v15, "/SYSTEM/SD/XBIN/SU"
const-string v16, "/SYSTEM/BIN/FAILSAFE/SU"
const-string v17, "/DATA/LOCAL/SU"
filled-new-array/range {v0 .. v17}, [Ljava/lang/String;
move-result-object v0
const/4 v1, 0x0
move v2, v1
:cond_2a
const/16 v3, 0x12
if-ge v2, v3, :cond_3f
aget-object v3, v0, v2
add-int/lit8 v2, v2, 0x1
new-instance v4, Ljava/io/File;
invoke-direct {v4, v3}, Ljava/io/File;-><init>(Ljava/lang/String;)V
invoke-virtual {v4}, Ljava/io/File;->exists()Z
move-result v3
if-eqz v3, :cond_2a
const/4 v0, 0x1
return v0
:cond_3f
return v1
.end method
위 코드를 전체적으로 보면 파일 및 경로 리스트들을 열거하고 그 리스트들을 배열로 생성한다
스말리 코드에서
const/4 v1, 0x0
move v2, v1
에 해당하는 부분은 아래 사진부분 0 값을 주고
if-ge v2, v3, :cond_3f 코드는 while 문을 뜻한다.
v3은 const/16 v3, 0x12 형태로 선언되었는데 0x12는 16진수로 10진수 정수로 표현하면 18이다.
배열의 개수는 총 18개 즉 파일의 리스트들을 확인하는 코드인데,
add-int/lit8 v2, v2, 0x1 와 같은 코드는 이렇게 1씩 증가하는 코드이다.
const/4 v1, 0x0
move v2, v1
:cond_2a
const/16 v3, 0x12
if-ge v2, v3, :cond_3f
aget-object v3, v0, v2
add-int/lit8 v2, v2, 0x1
new-instance v4, Ljava/io/File;
invoke-direct {v4, v3}, Ljava/io/File;-><init>(Ljava/lang/String;)V
invoke-virtual {v4}, Ljava/io/File;->exists()Z
move-result v3
if-eqz v3, :cond_2a
const/4 v0, 0x1
return v0
:cond_3f
return v1
.end method
while문이 끝났을 때 파일이 존재하지 않는다면,
if-eqz v3, :cond_2a 코드에 의해 v3의 주소값이 0이니까 cond_2a로 다시 올라가서 cond_3f로 분기되어
False값이 반환되고
파일이 존재한다면 그대로
const/4 v0, 0x1
return v0
v0에 0x1이 저장되어 1 (True)값이 반환되는 코드
Smali 코드를 분석하는 법을 배워두면 조금 더 논리적으로 분석하여 다양한 우회 시도를 해볼 수 있고
원리를 이해할 수 있다.
'모바일 > Android' 카테고리의 다른 글
[Android] 안드로이드 Frida Hooking을 통한 루팅탐지우회 2(분석글) (6) | 2022.05.11 |
---|---|
[Android] 안드로이드 Frida Hooking을 통한 루팅탐지우회 (0) | 2022.05.10 |
[Android] Smali 코드 분석 및 변조를 통한 루팅 탐지 우회 (기초) (1) | 2022.04.29 |
[Android] Magisk 다운그레이드 및 헬조선 Magisk 설치 (5) | 2022.04.20 |