효투의 세상 로딩중...
효투의 세상 로딩중...
반응형

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 코드를 분석하는 법을 배워두면 조금 더 논리적으로 분석하여 다양한 우회 시도를 해볼 수 있고

원리를 이해할 수 있다.

 

 

반응형
  • hyotwo7658@gmail.com

복사 완료 👍