Post

Anti-Reversing 을 통한 Androidmanifest 복원하기

Androidmanifest file format layout

Anti-Reversing 을 통한 Androidmanifest 복원하기

Introduction

최근 안드로이드 앱 분석하는 과정에서 디컴파일이 불가한 apk 가 보여 확인한 결과 디컴파일 방지 기능이 있어 이에 대해 정리하던 도중 xml format 에 대해 정리하고자 한다.

block-beta
  columns 3
  block:dex_file:3
    %% columns auto (default)
    classes.dex classes1.dex classes2.dex classes3.dex classes4.dex classes5.dex classesN.dex
  end
  block:lib:2
    columns 2
    armeabi armeabi_v7a arm64_v8a x86_64 x86 
  end  
  block:META_INF:1
    columns 1
    *.RSA *.SF *.MF 
  end
  block:etc_file:3
    %% columns auto (default)
    xml["AndroidManifest.xml"]
    assets
    res
    resource.arsc
  end
  style xml fill:#855

안드로이드 메니페스트 파일 포멧은 공식적으로 알려진 게 없다.

안드로이드 메니페스트는 안티리버싱으로 쉽게 접할 수 있는 요소중 하나이다.

오픈소스로 manifest format 을 변조하는 코드도 있으며 이전 버전에서는 여러 디컴파일 툴들이 파싱을 하지 못한 사례가 있었다.

해당 글에서는 manifest format 에서 변조될 수 있는 부분에 대하여 이를 정리해보았다.

1
2
3
4
5
6
7
8
9
10
11
00000000: 0300 0800 c408 0000 0100 1c00 f404 0000  ................
00000010: 2300 0000 0000 0000 0000 0000 a800 0000  #...............
00000020: 0000 0000 0000 0000 0e00 0000 1a00 0000  ................
00000030: 2c00 0000 4000 0000 4e00 0000 6c00 0000  ,...@...N...l...
00000040: 8600 0000 a000 0000 c400 0000 de00 0000  ................
00000050: 0401 0000 2a01 0000 6001 0000 6801 0000  ....*...`...h...
00000060: 7c01 0000 9601 0000 a601 0000 ba01 0000  |...............
00000070: cc01 0000 0402 0000 3002 0000 6802 0000  ........0...h...

                    { . . . }

layout 을 변조하는 부분을 () 로 확인해보면 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
00000000: 0300 0800(c408 0000)0100 1c00(f404 0000) ................
00000010: 2300 0000 0000 0000 0000 0000(a800 0000) #...............
00000020: 0000 0000 0000 0000 0e00 0000 1a00 0000  ................
00000030: 2c00 0000 4000 0000 4e00 0000 6c00 0000  ,...@...N...l...
00000040: 8600 0000 a000 0000 c400 0000 de00 0000  ................
00000050: 0401 0000 2a01 0000 6001 0000 6801 0000  ....*...`...h...
00000060: 7c01 0000 9601 0000 a601 0000 ba01 0000  |...............
00000070: cc01 0000 0402 0000 3002 0000 6802 0000  ........0...h...

                    { . . . }

아래에서 각 항목에 대한 설명을 아래와 같다.

File Size

1
0x04 - 0x07: File Size

파일 사이즈 같은 경우 파일의 마지막 오프셋을 확인하면 알 수 있다.

1
2
3
4
5
6
7
                    { . . . }

00000890: 1700 0000 0301 1000 1800 0000 0200 0000  ................
000008a0: ffff ffff ffff ffff 1c00 0000 0101 1000  ................
000008b0: 1800 0000 0200 0000 ffff ffff 1200 0000  ................
000008c0: 1900 0000                                ....

String Pool Chunk Size

1
0x0C - 0x0F: String Pool Chunk Size

String Pool Chunk Size 같은 경우 파일에서 사용되는 string Data(문자열 배열의 집합)가 있는데 마지막 stirng Data 가 끝나는 오프셋이다.

String Start

1
0x2C - 0x2F: String Start

String Start 같은 경우 파일에서 사용되는 string Data(문자열 배열의 집합)가 있는데 첫 string Data 가 시작하는 부분이다.

manifest format

위와 같이 manifest format 을 볼 수 있고 맨 처음에 본 format 같은 경우 아래를 통해 더 자세하게 참고할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 Magic Number(0x00080003)   4bytes 
 File Size                  4bytes 
 String Chunk 
   Chunk Type(0x001C0001)   4bytes 
   Chunk Size               4bytes 
   String Count             4bytes 
   Style Count              4bytes
   Flags                    4bytes
   String Pool Offset       4bytes
   Style Pool Offset        4bytes
   String Pool Offsets      4bytes * String Count 
   Style Offsets            4bytes * Style Count
   String Pool (String Data) * String Count
     String Length          2bytes 
     String Content         2bytes * String Length
     \0                     2bytes // \0

                        { . . . }

그 외 section 을 본다면 아래와 같이 볼 수 있다.

String Chunk 분석

A. String Chunk 헤더 구조

  1. Chunk Type (0x001c0001): StringChunk 유형을 나타내며, 고정된 4바이트 크기.
  2. Chunk Size: String Chunk의 전체 크기, 고정된 4바이트.
  3. String Count: String Chunk에 포함된 문자열 개수, 고정된 4바이트. 문자열을 해석할 때 사용됨.
  4. Style Count: String Chunk에 포함된 스타일 개수, 고정된 4바이트. 일반적으로 0x00000000으로 설정됨.
  5. Unknown: 알려지지 않은 영역으로, 고정된 4바이트이며 해석 과정에서 무시됨.
  6. String Pool Offset: 문자열 풀의 오프셋 값, 고정된 4바이트. 이 오프셋 값은 StringChunk 헤더(8바이트 위치) 기준 (8 + 0x000000A0) 에 상대적임.
  7. Style Pool Offset: 스타일 풀의 오프셋 값, 고정된 4바이트.
  8. String Pool: 각 문자열의 오프셋 목록이며, 문자열 개수 × 4바이트 크기를 가짐.
  9. Style Pool: 각 스타일의 오프셋 목록이며, 스타일 개수 × 4바이트 크기를 가짐.

B. String Chunk 내용 파싱

  1. StringChunk 헤더 시작 위치는 0x08.
  2. 문자열 풀 오프셋 값은 0x000000A0.
    • 이 값은 StringChunk 헤더 (0x08) 기준 상대적이므로 실제 위치는 0x000000A8.
  3. 0x000000A8 위치에 있는 0x0005 값은 문자열 길이를 나타냄.
    • 문자열의 길이는 0x0005 * 2 바이트 (각 문자가 2바이트).
  4. 그 뒤의 10바이트는 문자열 데이터를 저장함.
  5. 문자열은 00 00으로 종료됨.
    • UTF-8 문자열은 00으로, UTF-16 문자열은 00 00으로 종료됨.

ResourceId Chunk 분석

  1. ChunkType: ResourceId Chunk 유형, 고정된 4바이트 (0x00080180).
  2. ChunkSize: ResourceId Chunk의 크기, 고정된 4바이트.
  3. ResourceIds: 리소스 ID가 차지하는 전체 바이트 크기.
    • 계산식: (ChunkSize / 4 - 2) * 4 bytes.
    • 상세 분해:
1
2
3
4
a) ChunkSize: ResourceId Chunk의 크기 (0x00000040 → 10진수로 변환하면 64).
b) ChunkSize / 4: 리소스 블록의 개수 (4는 4바이트 단위).
c) -2: ChunkType과 ChunkSize의 헤더 크기 제거 (8 / 4 = 2).
d) * 4: ResourceIds가 차지하는 총 바이트 크기.
  • 예제 계산: (60 / 4 - 2) * 4 = 52 (0x0034).

Start Namespace Chunk 분석

  1. ChunkType: Start Namespace Chunk 유형, 고정된 4바이트 (0x00100100).
  2. ChunkSize: Start Namespace Chunk 크기, 고정된 4바이트.
  3. LineNumber: AndroidManifest.xml에서 해당 네임스페이스가 위치한 행 번호, 고정된 4바이트.
  4. 알 수 없는 데이터: 고정된 4바이트.
  5. Prefix: 네임스페이스 접두어 (예: android:xxx).
  6. Uri: 네임스페이스의 URI (예: http://schemas.android.com/tools).

Start Tag Thunk 분석

  1. ChunkType: Start Tag(Ele) Thunk 유형, 고정된 4바이트 (0x00100102).
  2. ChunkSize: Start Tag Thunk 크기, 고정된 4바이트.
  3. LineNumber: AndroidManifest.xml에서 해당 태그가 위치한 행 번호, 고정된 4바이트.
  4. 알 수 없는 데이터: 고정된 4바이트.
  5. Namespace Uri: 네임스페이스의 URI (예: android=”http://schemas.android.com/apk/res/android”), 고정된 4바이트.
  6. Name: 태그 이름 (StringChunkContent 내의 인덱스 값).
  7. Flags (0x00140014): 태그 유형, 고정된 4바이트.
  8. Attribute Count: 태그의 속성 개수, 고정된 4바이트.
  9. Class Attribute: 태그의 클래스 속성, 고정된 4바이트.
  10. Attributes: 속성 정보.
    • 각 속성은 5 * 4 bytes (총 20바이트)로 구성됨.
    • 실제로는 크기가 5인 1차원 배열이며, 각 값의 의미: [Namespace Uri, Name, Value String, Type, Data]
    • Type 값은 24비트 우측 이동하여 가져와야 함.

부정확한 부분이 많아 좀 더 다음에 자세히 다뤄보겠다.

Conclusion

위와 같은 구조를 가질 수 있는 structure 를 찾을 수 있었으나 더 확실하게 확인하기 위해서는 하나씩 파싱해보는 것이 가장 좋을 것으로 보인다.

This post is licensed under CC BY 4.0 by the author.
If you find any errors, please let me know by comment or email. Thank you.

© Ruffalo. Some rights reserved.

I'm

Using the Chirpy theme for Jekyll.