웹찢남

Decoding Algorithm of AndroidManifest.XML 본문

Etc...

Decoding Algorithm of AndroidManifest.XML

harry595 2021. 2. 2. 18:32

1) 간단한 android app 구현

 

2) apkAndroidManifest.xml 추출

그 후 기존의 AndroidManifest.xml과 비교를 하며 encodingAndroidManifest.xml의 구조 분석

 

3) encodingAndroidManifest.xml 구조 분석

 

00000000 03 00 // Type (XML)

00000002 08 00 // Header Size

00000004 BC 08 00 00 // Chunk Size

------------------------------------------

00000008 01 00 // Type (String_Pool)

0000000a 1C 00 // Header Size

0000000c FC 04 00 00 // Chunk Size

------------------------------------------

00000010 21 00 00 00 // stringCount – 33

00000014 00 00 00 00 // styleCount

00000018 00 00 00 00 // flags

0000001c A0 00 00 00 // StringsStart (address 000000a8)

00000020 00 00 00 00 // StylesStart

-----------------------------------------

00000024 00 00 00 00 // String[0]                       00000028 0E 00 00 00 // String[1]

0000002c 1C 00 00 00// String[2]                        00000030 28 00 00 00 // String[3]

00000034 34 00 00 00// String[4]                        00000038 52 00 00 00// String[5]

0000003c 6C 00 00 00// String[6]                        00000040 86 00 00 00 // String[7]

------  string[8] ~ string [25]  -------

0000008c 42 03 00 00// String[26]                      00000090 9A 03 00 00// String[27]

00000094 B8 03 00 00// String[28]                      00000098 CC 03 00 00// String[29]

0000009c DE 03 00 00// String[30]                      000000a0 12 04 00 00// String[31]

000000a4 46 04 00 00// String[32]

000000a8 05 00 74 00 68 00 65 00 6D 00 65 00 00 // [0] : theme

000000b6 05 00 6C 00 61 00 62 00 65 00 6C 00 00 00 // [1] : label

000000c4 04 00 69 00 63 00 6F 00 6E 00 00 00 // [2] : icon

000000d0 04 00 6E 00 61 00 6D 00 65 00 00 00 // [3] : name

000000dc 0D 00 6D 00 69 00 6E 00 53 00 64 00 6B 00 56 00 65 00 72 00 73 00 69 00 6F 00 6E 00 00 00 // [4] : minSdkVersion

000000fa 0B 00 76 00 65 00 72 00 73 00 69 00 6F 00 6E 00 43 00 6F 00 64 00 65 00 00 00 // [5] : versionCode

00000115 0B 00 76 00 65 00 72 00 73 00 69 00 6F 00 6E 00 4E 00 61 00 6D 00 65 00 00 // [6] : versionName

0000012d 00 10 00 74 00 61 00 72 00 67 00 65 00 74 00 53 00 64 00 6B 00 56 00 65 00 72 00 73 00 69 00 6F 00 6E 00 // [7] : targetSdkVersion

00000152 0B 00 61 00 6C 00 6C 00 6F 00 77 00 42 00 61 00 63 00 6B 00 75 00 70 00 00 00 // [8]: allowBackup

0000016c 0B 00 73 00 75 00 70 00 70 00 6F 00 72 00 74 00 73 00 52 00 74 00 6C 00 00 00 // [9]: supportsRtl

00000186 09 00 72 00 6F 00 75 00 6E 00 64 00 49 00 63 00 6F 00 6E 00 00 00 // [10]: roundIcon

0000019c 11 00 63 00 6F 00 6D 00 70 00 69 00 6C 00 65 00 53 00 64 00 6B 00 56 00 65 00 72 00 73 00 69 00 6F 00 6E 00 00 00 // [11]: compilesdkversion

000001c2 19 00 63 00 6F 00 6D 00 70 00 69 00 6C 00 65 00 53 00 64 00 6B 00 56 00 65 00 72 00 73 00 69 00 6F 00 6E 00 43 00 6F 00 64 00 65 00 6E 00 61 00 6D 00 65 00 00 00 // [12]: compilesdkversioncodename

000001f8 13 00 61 00 70 00 70 00 43 00 6F 00 6D 00 70 00 6F 00 6E 00 65 00 6E 00 74 00 46 00 61 00 63 00 74 00 6F 00 72 00 79 00 00 00 // [13]: appcomponentFactory

00000222 03 00 31 00 2E 00 30 00 00 00 // [14]: 1.0

0000022c 02 00 31 00 31 00 00 00 // [15]:11

00000235 06 00 61 00 63 00 74 00 69 00 6F 00 6E 00 00 00 // [16]: action

00000245 08 00 61 00 63 00 74 00 69 00 76 00 69 00 74 00 79 00 00 00 // [17]: activity

00000258 07 00 61 00 6E 00 64 00 72 00 6F 00 69 00 64 00 00 00 // [18]: android

0000026a 1A 00 61 00 6E 00 64 00 72 00 6F 00 69 00 64 00 2E 00 69 00 6E 00 74 00 65 00 6E 00 74 00 2E 00 61 00 63 00 74 00 69 00 6F 00 6E 00 2E 00 4D 00 41 00 49 00 4E 00 00 00 // [19]: android.intent.action.MAIN

000002a2 20 00 61 00 6E 00 64 00 72 00 6F 00 69 00 64 00 2E 00 69 00 6E 00 74 00 65 00 6E 00 74 00 2E 00 63 00 61 00 74 00 65 00 67 00 6F 00 72 00 79 00 2E 00 4C 00 41 00 55 00 4E 00 43 00 48 00 45 00 52 00 00 00 // [20]: android.intent.category.LAUNCHER

000002e6 26 00 61 00 6E 00 64 00 72 00 6F 00 69 00 64 00 78 00 2E 00 63 00 6F 00 72 00 65 00 2E 00 61 00 70 00 70 00 2E 00 43 00 6F 00 72 00 65 00 43 00 6F 00 6D 00 70 00 6F 00 6E 00 65 00 6E 00 74 00 46 00 61 00 63 00 74 00 6F 00 72 00 79 00 00 00 // [21]: androidx.core.app.CoreComponentFactory

00000336 0B 00 61 00 70 00 70 00 6C 00 69 00 63 00 61 00 74 00 69 00 6F 00 6E 00 00 00 // [22]: application

00000350 08 00 63 00 61 00 74 00 65 00 67 00 6F 00 72 00 79 00 00 00 // [23]: category

00000364 19 00 63 00 6F 00 6D 00 2E 00 65 00 78 00 61 00 6D 00 70 00 6C 00 65 00 2E 00 6D 00 79 00 61 00 70 00 70 00 6C 00 69 00 63 00 61 00 74 00 69 00 6F 00 6E 00 00 00 // [24]: com.example.myapplication

0000039a 26 00 63 00 6F 00 6D 00 2E 00 65 00 78 00 61 00 6D 00 70 00 6C 00 65 00 2E 00 6D 00 79 00 61 00 70 00 70 00 6C 00 69 00 63 00 61 00 74 00 69 00 6F 00 6E 00 2E 00 4D 00 61 00 69 00 6E 00 41 00 63 00 74 00 69 00 76 00 69 00 74 00 79 00 00 00 // [25]: com.example.myapplication.MainActivity

000003ea 2A 00 68 00 74 00 74 00 70 00 3A 00 2F 00 2F 00 73 00 63 00 68 00 65 00 6D 00 61 00 73 00 2E 00 61 00 6E 00 64 00 72 00 6F 00 69 00 64 00 2E 00 63 00 6F 00 6D 00 2F 00 61 00 70 00 6B 00 2F 00 72 00 65 00 73 00 2F 00 61 00 6E 00 64 00 72 00 6F 00 69 00 64 00 00 00 // [26]: http://schemas.android.com/apk/res/android

00000442 0D 00 69 00 6E 00 74 00 65 00 6E 00 74 00 2D 00 66 00 69 00 6C 00 74 00 65 00 72 00 00 00 // [27]: intent-filter

00000460 08 00 6D 00 61 00 6E 00 69 00 66 00 65 00 73 00 74 00 00 00 // [28]: manifest

00000474 07 00 70 00 61 00 63 00 6B 00 61 00 67 00 65 00 00 00 // [29]: package

00000486 18 00 70 00 6C 00 61 00 74 00 66 00 6F 00 72 00 6D 00 42 00 75 00 69 00 6C 00 64 00 56 00 65 00 72 00 73 00 69 00 6F 00 6E 00 43 00 6F 00 64 00 65 00 00 00 // [30]: platformBuildVersionCode

000004ba 18 00 70 00 6C 00 61 00 74 00 66 00 6F 00 72 00 6D 00 42 00 75 00 69 00 6C 00 64 00 56 00 65 00 72 00 73 00 69 00 6F 00 6E 00 4E 00 61 00 6D 00 65 00 00 00 // [31] : platformBuildVersionName

000004ee 08 00 75 00 73 00 65 00 73 00 2D 00 73 00 64 00 6B 00 00 00 // [32] : uses-sdk

 

String_Pool 정리

0

Theme

11

Activity

1

Label

12

android

2

Icon

13

android.intent.action.MAIN

3

name

14

android.intent.category.LAUNCHER

4

minSdkVersion

15

androidx.core.app.CoreComponentFactory

5

versionCode

16

application

6

versionName

17

category

7

targetSdkVersion

18

com.example.myapplication

8

allowBackup

19

com.example.myapplication.MainActivity

9

supportsRtl

1a

http://schemas.android.com/apk/res/android

A

roundIcon

1b

intent-filter

B

compilesdkversion

1c

manifest

C

compilesdkversioncodename

1d

package

D

appcomponentFactory

1e

platformBuildVersionCode

E

1,0

1f

platformBuildVersionName

F

11

20

uses-sdk

10

action

X

X

 

---------------End of String Pool------------

00000504 80 01 // type [XML_RESOURCE_MAP]

00000506 08 00 // header size

00000508 40 00 00 00 // chunk size

------------------------------------------------

0000050c 00 00 01 01 // [0]

[1]~[8]

00000540 7A 05 01 01 // [9]

------------End of XML_RESOURCE_MAP--------

00000544 00 01 // type [XML_START_NAMESPACE]

00000546 10 00 // Header Size

00000548 18 00 00 00 // Chunk Size

0000054c 02 00 00 00 // LineNumber

00000550 FF FF FF FF // Comment

00000554 12 00 00 00 // prefix

00000558 1A 00 00 00 // uri

----------End of XML_START_NAMESPACE-------

0000055c 02 01 // type [XML_START_ELEMENT]

0000055e 10 00 // header size

00000560 B0 00 00 00 // chunk size (60c까지 chunk)

-------------------------------------------------

00000564 02 00 00 00 // lineNumber

00000568 FF FF FF FF // comment

--------------------------------------------------

0000056c FF FF FF FF // ns

00000570 1C 00 00 00 // name [manifest]

00000574 14 00 // attribute Start

00000576 14 00 // attribute Size

00000578 07 00 // attribute Count

0000057a 00 00 // id Index

0000057c 00 00 // class Index

0000057e 00 00 // styleIndex

00000580 1A 00 00 00 // attribute[0] ns

00000584 05 00 00 00 // attribute[0] name [versioncode]

00000588 FF FF FF FF // attribute[0] rawValue

0000058c 08 00 // size

0000058e 00 // 0

0000058f 10 // datatype = INT

00000590 01 00 00 00 // data = 1

00000594 1A 00 00 00 // attribute[1] ns

00000598 06 00 00 00 //attribute[1] name [versionname]

0000059c 0E 00 00 00 //attribute[1] raw Value

000005a0 08 00 // size

000005a2 00 // 0

000005a3 03 // datatype =StringPool

000005a4 0E 00 00 00 // data = 1.0

000005a8 ~ 00000874 (ELEMENTS)

0000088C 03 01 // type [XML_END_ELEMENT]

0000088E 10 00 // header size

00000890 18 00 00 00 // chunk Size

00000894 0B 00 00 00 // linenumber

00000898 FF FF FF FF // comment

0000089C FF FF FF FF // ns

000008A0 1C 00 00 00 // name

--------------End of xml_end_element--------------

000008A4 01 01 // type [XML_END_NAMESPACE]

000008A6 10 00 // header size

000008A8 18 00 00 00 // chunk size

000008AC 02 00 00 00 // line number

000008B0 FF FF FF FF // comment

000008B4 12 00 00 00 // prefix

000008B8 1A 00 00 00 // uri

-------------End of XML_END_NAMESPACE--------------

-------------End of XML-----------------------------------

 

 

4) Android_manifest.xml 오픈소스 decoder 사용

기존의 Android_manifestencoding 하고 decoding할 때 값이 바뀔 것이라 생각을 하여 decoding이 완료된 모습을 보기 위해 AXMLPrinter2를 사용해봤습니다. 아래의 결과를 목표로 decoder을 개발하기로 했습니다.

 

오픈소스 AXMLPrinter2.jar을 통해 디코딩한 Android_mainfest.xml

5) Manifest_decoder 개발

개발 환경은 python 3.7.9를 사용하였으며 3번의 제가 만든 encodingxml파일의 분석을 기준으로, 4번의 decoding 결과를 목표로 개발을 진행하였습니다.

개발 routineXML_header -> stringPool -> resource_map -> RECURSIVE(Start_namespace, start element , End_namespace, End element)로 계획을 세웠습니다.

 

코드를 3분할하여 코드를 설명하겠습니다.

lbbyteint로 바꾸는 함수로 data를 처리할 때 값을 비교하거나 사용하기 위해 사용합니다.

check_routin”은 위에서 설명한 RECURSIVE( Start_namespace, start element , End_namespace, End element) 부분인데. Xml 파일을 분석해본 결과 resource_map 다음에 이 4개의 함수가 반복되어 한 함수에서 Chunk가 끝나면 check_routine으로 돌아와 앞 두 바이트를 통해 type을 확인하고 다른 함수로 넘어갑니다.

XML_END_ELEMENTSTART_ELEMENT와 대비되며 열린 태그를 닫습니다.

XML_ENDNAMESPACEend_prefix,uri를 반환하고 해당 함수의 data를 처리합니다.

 

XML_START_ELEMENT는 출력을 담당하는 함수입니다. 우선 ELEMENTHeader 정보를 인자에 넘겨준 후 태그를 열고 값을 출력합니다. 보통의 manifest.xml에서는 다음과 같이 uriprefix를 설명하는 xmlns:android=http://schemas.android.com/apk/res/android line이 한 줄 나와 변수를 통해 한번만 나오도록 설정하였습니다. URIPREFIXnamespace에서 인자로 넘기도록 하였습니다. 그 후 header에 있던 element_count만큼의 반복문을 돌립니다. 그 후 태그 내의 값들을 attribute_name=attribute_data 방식으로 출력합니다. 이때 element_type을 통해 type을 설정하는데 0x03일 경우 string_pool 속의 indexdata에 주어지고 0x01resource값으로@+hex값으로 표현합니다. 0x12 Boolean, 0x11hex0x10intint else처리를 했습니다. 그 후 for문이 끝난 후 다시 check_routine으로 돌아갑니다.

 

XML_START_NAMESPACE는 저장한 값인 prefixuriStart_ELEMENT 전달하는 역할을 합니다.

Resource_map의 역할에 대해 자료를 찾지 못해 해당 Chunk size만 처리하도록 하였는데 아마 start element속의 datatype 0x01resourcetype내의 데이터를 처리하지 않나 생각합니다.

String_Pool함수는 Make_String_Listcountdata를 넘겨줍니다. MakeStringList에서는 StringPool함수에서 받은 String_Count만큼의 for문을 돌리는데 이때 따로 전역 List를 만들어 해당 Liststring들을 append하여 추후에 사용할 수 있도록 저장합니다.

Main 함수에서는 HeaderSize가 끝난 후의 데이터를 String_Pool로 넘겨주는 역할을 합니다. 또한 맨 첫 라인이 sys.setrecursionlimit(10000) 인데, 카카오톡의 android_manifest.xml을 돌려본 결과 recursive로 코드를 짜 놓은 탓에 recursive funcdepth3000이 넘어가 오류가 발생하여 3000이 넘어도 돌아갈 수 있도록 10000으로 limit을 설정해 놨습니다.

 

6) 결과

아래와 같이 manifest.xml 오픈소스 decoder과 똑같은 결과를 이끌어 냈습니다. Indentation 또한 tab 인자를 따로 만들어 처리를 하는 등 보기에 불편함이 없도록 개발을 하였습니다. 오류의 해결에 관한 부분도 실제 상용 앱의 manifest.xml파일을 집어넣어 처리하며 여러 처리과정을 거쳤습니다.

위의 캡처는 추가로 카카오톡의 apk androidmanifest.xmldecoding한 것인데 문제없이 디코딩에 성공하였습니다.

 

 

7) pypi 배포

pypi.org/project/PyAmdecoder/

 

PyAmdecoder

AndroidManifest.xml PyDecoder

pypi.org

배포도 해봤습니다. 솔직히 pip 배포 경험을 위해 한거긴한데 급박하게 개발을 진행한 모듈이고 저의 apk에 맞춰 개발을 진행하여 다른 몇몇 apk에는 디코딩이 잘 이루어지지 않은 것 같긴한데, pypi 링크에 github 주소도 있으니 손봐서 사용하셔도 괜찮습니다!

Comments