2011年10月25日
エンコードされた AndroidManifest.xml を読む
アプリケーションマニフェストである AndroidManifest.xml をはじめ、Android アプリを構成する各種 XML ファイルは apk へのパッケージングの段階でパースされ独自のバイナリ形式にエンコードされます。
このファイルを扱う処理をコンパクトに実装したいと思ったのですが、現時点では形式に関する公式の資料が存在しないことがわかり aapt を参考に手元でフォーマットの分析を行いました。パーサ試作例とともにその内容を公開します。
1. データ例
(A) テストアプリ「MyApp」用に記述した生の AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="jp.klab.sample.myapp" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="4" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".MyApp" android:label="@string/app_name" android:launchMode="singleTask" android:excludeFromRecents="false" android:configChanges="orientation|keyboardHidden"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> </manifest>
(B) パッケージ後の MyApp.apk に含まれる エンコードずみ AndroidManifest.xml のダンプ
(C) 試作コードにより上記 (B) に含まれる情報を XML ツリー形式に再構成したもの
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="jp.klab.sample.myapp"> <uses-sdk android:minSdkVersion="4" /> <application android:label="@0x7F050001" android:icon="@0x7F020000"> <activity android:label="@0x7F050001" android:name=".MyApp" android:excludeFromRecents="false" android:launchMode="2" android:configChanges="0x000000A0"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> </manifest>
- エンコードされたデータには元の XML 記述中の名前空間情報・要素名・属性名・属性値・タグの階層情報等が含まれており、属性値として指定されたリソース ID や定数に対するシンボル名は値に解決された状態で保持される
- エンコードの目的はサイズ圧縮ではなく端末においてより簡単にデータをパース可能とすることにある ("This is designed to express everything in an XML document, in a form that is much easier to parse on the device.")
2. エンコードずみ XML ファイルのデータレイアウト
00h ┌───────────┐ │ファイルヘッダ:8 byte│ ├───────────┴───────────────┐ │文字列プールヘッダ:28 byte │ ├───────────────────────────┴──┐ │文字列プール上の各エントリの開始位置情報:可変長 │ ├──────────────────────────────┴┐ │文字列プール:可変長 │ │ │ │ 元の XML 記述中のタグ要素名・属性名・文字列定数の属性値等の │ │ 文字列が重複なく格納されている │ │ │ ├───────────┬───────────────────┘ │XML 情報ヘッダ:8 byte│ ├───────────┴─────────────┐ │XML 使用属性 ID テーブル:可変長 │ ├───────────────────────┬─┘ │XML ツリー開始情報ノード:16 byte │ ├───────────┬───────────┘ │名前空間情報:8 byte │ └─┬─────────┴─────────────┐ ─┐ │タグ 1 開始情報ノード:16 byte │ │ ├───────────────────────┴─┐│ │タグ 1 開始情報拡張ノード:20 byte ││元の XML 記述における └─┬─────────────────────┬─┘│階層イメージをそのまま │属性 1 情報ノード:12 byte │ │表現した形で記述される ├───────────┬─────────┘ │ │属性 1 値情報:8 byte │ │属性情報の存在しないタグや └───────────┘ │タグのネストも表現される : │ ┌───────────────────────┐ │ │タグ 1 終了情報ノード:16 byte │ │ ├───────────┬───────────┘ │ │〃 拡張ノード:8 byte │ │ ┌─┴───────────┴─────────┐ ──┘ │XML ツリー終了情報ノード:16 byte │ ├───────────┬───────────┘ │名前空間情報:8 byte │ └───────────┘
3. エンコードずみ XML ファイルのフォーマット
※バイトオーダーはリトルエンディアン [1.ファイルヘッダ:8バイト固定長 →構造体"ResChunk_header"] 00h-01h│uint16 識別子 = 0x0003 (= RES_XML_TYPE) 02h-03h│uint16 本ヘッダのバイト長 = 0x0008 = 8バイト 固定 04h-07h│uint32 本ファイル全体のバイト長 [2.文字列プールヘッダ:28バイト固定長 →構造体"ResStringPool_header"] 00h-01h│uint16 識別子 = 0x0001 (= RES_STRING_POOL_TYPE) 02h-03h│uint16 本ヘッダのバイト長 = 0x001C = 28バイト 固定 04h-07h│uint32 本ヘッダ+(C)+(D)のバイト長 08h-0Bh│uint32 文字列プールの保持する文字列データ数 (A) 0Ch-0Fh│uint32 文字列プールの保持する style span arrays の数 注:本記事の時点では値が 0 以外の実例が見当たらないためこの部分は未検証 10h-13h│uint32 フラグ 0でなければ以下の値をとる SORTED_FLAG = 1<<0:プールのインデックスは文字列の辞書順である UTF8_FLAG = 1<<8 :プール上の文字列のエンコーディングは UTF-8 (B) 14h-17h│uint32 文字列プールの開始オフセット(本ブロック先頭アドレスが基点) 18h-1Bh│uint32 style 文字列プールの開始オフセット(〃) [2-1.文字列プール上の各エントリの開始位置情報:可変長] (C) 文字列プール上の全文字列エントリの開始オフセットが、0件めの文字列の先頭アドレスを 基点にuint32 幅で並ぶ。 [2-2.文字列プール:可変長] (D) 文字列プールの本体は以下の体裁 ・上記 (B) の UTF-8 フラグが立っていない場合は UTF-16 で文字列を保持 ┌──┬──┬──┬──┬──┬──┬──┬ ┬──┬──┬──┬──┬──┬──┬ │文字列0長 │ 文字列0 (UTF-16 形式) ・・・・│ \0 │ \0 │文字列1長 │ 文字列1・・ └─(uint16)┴──┴──┴──┴──┴──┴ ┴──┴──┴──┴──┴──┴──┴ ・上記 (B) の UTF-8 フラグが立っている場合は UTF-8 で文字列を保持 ┌──┬──┬──┬──┬──┬──┬──┬ ┬──┬──┬──┬──┬──┬─ │文字列0長 │ 文字列0 (UTF-8 形式) ・・・・ │ \0 │文字列1長 │ 文字列1・・ └──┴──┴──┴──┴──┴──┴──┴ ┴──┴──┴──┴──┴──┴─ ・文字列プールには元の XML 記述に出現する名前空間名・要素名・属性名・属性値等の 文字列が重複なく格納される ・文字列プールへの格納順は次の通り (2011.10.28 追記) 1) android 名前空間上の属性名文字列群 2) その他の文字列群 ・文字列プールの終端は偶数長の 0x00 でパディングされるケースがある (2011.10.29 追記) [3.XML 情報ヘッダ:8バイト固定長 →構造体"ResXMLTree_header"] 00h-01h│uint16 識別子 = 0x0180 (= RES_XML_RESOURCE_MAP_TYPE) 02h-03h│uint16 本ヘッダのバイト長 = 0x0008 = 8バイト 固定 04h-07h│uint32 本ヘッダ+(E) のバイト長 [3-1.XML 使用属性 ID テーブル:可変長] (E) (2011.10.28 修正)「この XML 内に出現するすべての属性」について、各 Attribute に割り振られた 32ビットの ID が初出順に重複なく並ぶ。文字列プールの前方に配置された「android 名前空間上の属性名文字列群」に対応する 32ビットの 属性リソース ID 群が順番に並ぶ。 ※属性リソース ID の一例 [frameworks/base/core/res/res/values/public.xml] 0x01010000 android:theme 0x01010001 android:label 0x01010002 android:icon 0x01010003 android:name 0x01010010 android:exported 0x0101000B android:sharedUserId 0x0101000F android:debuggable 0x01010017 android:excludeFromRecents 0x0101001D android:launchMode 0x0101001F android:configChanges 0x0101020C android:minSdkVersion 0x01010270 android:targetSdkVersion 0x0101021B android:versionCode 0x0101021C android:versionName [4.XML ツリー開始情報ノード:16バイト固定長 →構造体"ResXMLTree_node"] 00h-01h│uint16 識別子 = 0x0100 (= RES_XML_START_NAMESPACE_TYPE) 02h-03h│uint16 本ノードのバイト長 = 0x0010 = 16バイト 固定 04h-07h│uint32 本ノード+(F) のバイト長 08h-0Bh│uint32 元の XML ファイル上の XML 記述開始位置の行番号 0Ch-0Fh│uint32 本ノードへのコメント文字列の 文字列プール上の要素番号 (0xFFFFFFFF ならコメントなし) [4-1.XML 名前空間情報ノード:8バイト固定長 →構造体"ResXMLTree_namespaceExt"] (F) 00h-03h│uint32 この XML の名前空間接頭辞(通常は"android")の 文字列プール上の要素番号 04h-07h│uint32 この XML の名前空間 URI の 文字列プール上の要素番号 (通常は "http://schemas.android.com/apk/res/android") [5.XML タグ開始情報ノード:16バイト固定長 →構造体"ResXMLTree_node"] 00h-01h│uint16 識別子 = 0x0102 (= RES_XML_START_ELEMENT_TYPE) 02h-03h│uint16 本ノードのバイト長 = 0x0010 = 16バイト 固定 04h-07h│uint32 本ノード+(G)+(I+K)*(H) のバイト長 08h-0Bh│uint32 元の XML ファイル上の本タグ記述開始位置の行番号 0Ch-0Fh│uint32 本ノードへのコメント文字列の 文字列プール上の要素番号 (0xFFFFFFFF ならコメントなし) [5-1.XML タグ開始情報拡張ノード:20バイト固定長 →構造体"ResXMLTree_attrExt"] (G) 00h-03h│uint32 この要素のフル名前空間名の 文字列プール上の要素番号 (0xFFFFFFFF なら名前空間なし) 04h-07h│uint32 このタグの要素名の 文字列プール上の要素番号 08h-09h│uint16 属性情報開始位置 通常は 0x0014 = 20 ※(G)の先頭が基点 0Ah-0Bh│uint16 属性情報一件あたりのバイト数 通常は 0x0014 = 20 0Ch-0Dh│uint16 属性情報の数 (0なら属性なし) (H) 0Eh-0Fh│uint16 「id」属性の要素番号。1オリジン:同属性が存在しなければ 0 10h-11h│uint16 「class」属性の要素番号。1オリジン:同属性が存在しなければ 0 12h-13h│uint16 「style」属性の要素番号。1オリジン:同属性が存在しなければ 0 [5-2.属性情報ノード:12バイト固定長 →構造体"ResXMLTree_attribute"] (I) 00h-03h│uint32 この属性の名前空間 URI の文字列プール上の要素番号 (通常は "http://schemas.android.com/apk/res/android") 04h-07h│uint32 この属性の名前の 文字列プール上の要素番号 08h-0Bh│uint32 この属性の値の 文字列プール上の要素番号 (J) (0xFFFFFFFF = 値は文字列プール上の文字列値でない) [5-3.属性値情報ノード:8バイト固定長 構造体"Res_value"] (K) 00h-01h│uint16 本ノードのバイト長 = 0x0008 = 8バイト 固定 02h│uint8 常に 0x00 03h│uint8 属性値のタイプ (L) 以下、[frameworks/base/include/utils/ResourceTypes.h 上の定義]より引用 // Contains no data. TYPE_NULL = 0x00, // The 'data' holds a ResTable_ref, a reference to another resource // table entry. TYPE_REFERENCE = 0x01, // The 'data' holds an attribute resource identifier. TYPE_ATTRIBUTE = 0x02, // The 'data' holds an index into the containing resource table's // global value string pool. TYPE_STRING = 0x03, // The 'data' holds a single-precision floating point number. TYPE_FLOAT = 0x04, // The 'data' holds a complex number encoding a dimension value, // such as "100in". TYPE_DIMENSION = 0x05, // The 'data' holds a complex number encoding a fraction of a // container. TYPE_FRACTION = 0x06, // Beginning of integer flavors... TYPE_FIRST_INT = 0x10, // The 'data' is a raw integer value of the form n..n. TYPE_INT_DEC = 0x10, // The 'data' is a raw integer value of the form 0xn..n. TYPE_INT_HEX = 0x11, // The 'data' is either 0 or 1, for input "false" or "true" respectively. TYPE_INT_BOOLEAN = 0x12, // Beginning of color integer flavors... TYPE_FIRST_COLOR_INT = 0x1c, // The 'data' is a raw integer value of the form #aarrggbb. TYPE_INT_COLOR_ARGB8 = 0x1c, // The 'data' is a raw integer value of the form #rrggbb. TYPE_INT_COLOR_RGB8 = 0x1d, // The 'data' is a raw integer value of the form #argb. TYPE_INT_COLOR_ARGB4 = 0x1e, // The 'data' is a raw integer value of the form #rgb. TYPE_INT_COLOR_RGB4 = 0x1f, // ...end of integer flavors. TYPE_LAST_COLOR_INT = 0x1f, // ...end of integer flavors. TYPE_LAST_INT = 0x1f 04h-07h│uint32 この属性の値 (M) (J) != 0xFFFFFFFF、つまり (L) == 0x03 TYPE_STRING の場合は (J) と同じ要素番号が格納される。それ以外の場合はこの値を タイプに準じて解釈する。 たとえば (L) == 0x12 TYPE_INT_BOOLEAN で (M) == 0x00000000 なら値は "false"、(M) == 0xFFFFFFFF なら "true" である。 なお、属性の中には元の XML 記述において値に Android の定数を 指定するものがあるが、定数のシンボル名は値に解決された状態で 保持される。 (L) == 0x10 TYPE_INT_DEC の例 ・元の XML 記述 :android:launchMode="singleTask" ・保持される情報:android:launchMode="2" (L) == 0x11 TYPE_INT_HEX の例 ・元の XML 記述 :android:configChanges="orientation|keyboardHidden" ・保持される情報:android:configChanges="160" 各定数は以下に定義されている。 ・frameworks/base/native/include/android/configuration.h ・frameworks/base/core/java/android/content/pm/ActivityInfo.java 属性値に指定されたリソース ID も同様に値のみが保持される。 (L) == 0x01 TYPE_REFERENCE の例 ・元の XML 記述 :android:label="@string/app_name" ・保持される情報:android:label="0x7f090000" [6.XML タグ終了情報ノード:16バイト固定長 →構造体"ResXMLTree_node"] 00h-01h│uint16 識別子 = 0x0103 (= RES_XML_END_ELEMENT_TYPE) 02h-03h│uint16 本ノードのバイト長 = 0x0010 = 16バイト 固定 04h-07h│uint32 本ノード+(N) のバイト長 08h-0Bh│uint32 元の XML ファイル上の本タグ記述終了位置の行番号 0Ch-0Fh│uint32 本ノードへのコメント文字列の 文字列プール上の要素番号 (0xFFFFFFFF ならコメントなし) [6-1.XML タグ終了情報拡張ノード:8バイト固定長 構造体"ResXMLTree_endElementExt"](N) 00h-03h│uint32 本タグ要素のフル名前空間名の 文字列プール上の要素番号 (0xFFFFFFFF なら名前空間なし) 04h-07h│uint32 このタグの要素名の 文字列プール上の要素番号 ───────────────────────────────────────── 注:タグは XML 記述上ネストが可能であるため [タグ1 開始−タグ1 終了] ばかりでなく [タグ1 開始−タグ2 開始−タグ2 終了−タグ1 終了] という配置が普通に起こりうる。 エンコードずみ XML では元のタグ記述の階層表現がそのまま保持されるため、後続の 情報が何であるかは後続ノードの識別子を参照して判断する必要がある。 ───────────────────────────────────────── [7.XML ツリー終了情報ノード:16バイト固定長 →構造体"ResXMLTree_node"] 00h-01h│uint16 識別子 = 0x0101 (= RES_XML_END_NAMESPACE_TYPE) 02h-03h│uint16 本ノードのバイト長 = 0x0010 = 16バイト 固定 04h-07h│uint32 本ノード+(O) のバイト長 08h-0Bh│uint32 元の XML ファイル上の XML 記述終了位置の行番号 0Ch-0Fh│uint32 本ノードへのコメント文字列の 文字列プール上の要素番号 (0xFFFFFFFF ならコメントなし) [7-1.XML 名前空間情報ノード:8バイト固定長 →構造体"ResXMLTree_namespaceExt"] (O) 00h-03h│uint32 この XML の名前空間接頭辞(通常は"android")の 文字列プール上の要素番号 04h-07h│uint32 この XML の名前空間 URI の 文字列プール上の要素番号 (通常は "http://schemas.android.com/apk/res/android")
4. C 言語による試作パーサ
- Android 系ライブラリに依存しないコンパクトなコマンドラインツール
- 前掲の フォーマット説明 との照合性を重視し愚直さを優先した内容
- おそらく Microsoft VC++, gcc の広いバージョンでビルド可能
/* * parseEncodedXml.c * * apk へのパッケージングの際にエンコードされた AndroidManifest.xml を読む * * 使い方: parseEncodedXml [エンコードずみ XML ファイル名] * * Copyright(c) 2011 KLab Inc. * */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <memory.h> #include <sys/stat.h> //#include <wchar.h> #ifdef _MSC_VER typedef signed __int8 int8_t; typedef signed __int16 int16_t; typedef signed __int32 int32_t; typedef signed __int64 int64_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; typedef unsigned __int64 uint64_t; #else #include <stdint.h> #endif typedef unsigned char byte; // 以下の定数と構造体の定義は // frameworks/base/include/utils/ResourceTypes.h からの抜粋 enum { RES_NULL_TYPE = 0x0000, RES_STRING_POOL_TYPE = 0x0001, RES_TABLE_TYPE = 0x0002, RES_XML_TYPE = 0x0003, RES_XML_FIRST_CHUNK_TYPE = 0x0100, RES_XML_START_NAMESPACE_TYPE = 0x0100, RES_XML_END_NAMESPACE_TYPE = 0x0101, RES_XML_START_ELEMENT_TYPE = 0x0102, RES_XML_END_ELEMENT_TYPE = 0x0103, RES_XML_CDATA_TYPE = 0x0104, RES_XML_LAST_CHUNK_TYPE = 0x017f, RES_XML_RESOURCE_MAP_TYPE = 0x0180, RES_TABLE_PACKAGE_TYPE = 0x0200, RES_TABLE_TYPE_TYPE = 0x0201, RES_TABLE_TYPE_SPEC_TYPE = 0x0202 }; struct ResChunk_header { uint16_t type; uint16_t headerSize; uint32_t size; }; enum { SORTED_FLAG = 1<<0, UTF8_FLAG = 1<<8 }; struct ResStringPool_header { struct ResChunk_header header; uint32_t stringCount; uint32_t styleCount; uint32_t flags; uint32_t stringsStart; uint32_t stylesStart; }; struct ResXMLTree_header { struct ResChunk_header header; }; struct ResStringPool_ref { uint32_t index; }; struct ResXMLTree_node { struct ResChunk_header header; uint32_t lineNumber; struct ResStringPool_ref comment; }; struct ResXMLTree_namespaceExt { struct ResStringPool_ref prefix; struct ResStringPool_ref uri; }; struct ResXMLTree_attrExt { struct ResStringPool_ref ns; struct ResStringPool_ref name; uint16_t attributeStart; uint16_t attributeSize; uint16_t attributeCount; uint16_t idIndex; uint16_t classIndex; uint16_t styleIndex; }; enum { TYPE_NULL = 0x00, TYPE_REFERENCE = 0x01, TYPE_ATTRIBUTE = 0x02, TYPE_STRING = 0x03, TYPE_FLOAT = 0x04, TYPE_DIMENSION = 0x05, TYPE_FRACTION = 0x06, TYPE_FIRST_INT = 0x10, TYPE_INT_DEC = 0x10, TYPE_INT_HEX = 0x11, TYPE_INT_BOOLEAN = 0x12, TYPE_FIRST_COLOR_INT = 0x1c, TYPE_INT_COLOR_ARGB8 = 0x1c, TYPE_INT_COLOR_RGB8 = 0x1d, TYPE_INT_COLOR_ARGB4 = 0x1e, TYPE_INT_COLOR_RGB4 = 0x1f, TYPE_LAST_COLOR_INT = 0x1f, TYPE_LAST_INT = 0x1f }; struct Res_value { uint16_t size; uint8_t res0; uint8_t dataType; uint32_t data; }; struct ResXMLTree_attribute { struct ResStringPool_ref ns; struct ResStringPool_ref name; struct ResStringPool_ref rawValue; struct Res_value typedValue; }; struct ResXMLTree_endElementExt { struct ResStringPool_ref ns; struct ResStringPool_ref name; }; char *dump(void *p, int len) { byte *buffer = (byte*)p; static char buf[80]; int i, cnt = 0; for (i = 0; i < len; i++) { sprintf(&buf[cnt], "%02X ", buffer[i]); cnt += 3; if ((i+1) % 8 == 0) { strcat(buf, " "); cnt++; } if ((i+1) % 16 == 0) { printf("%s\n", buf); cnt = 0; buf[0] = '\0'; } } return buf; } char *makeAttrValue(uint8_t dataType, uint32_t data) { static char fmt[32]; switch (dataType) { case TYPE_NULL: sprintf(fmt, ""); break; case TYPE_REFERENCE: sprintf(fmt, "@0x%08X", data); break; case TYPE_INT_DEC: sprintf(fmt, "%d", data); break; case TYPE_INT_HEX: sprintf(fmt, "0x%08X", data); break; case TYPE_INT_BOOLEAN: sprintf(fmt, "%s", (data == 0) ? "false" : "true"); break; default: // 他はとりあえず適当 sprintf(fmt, "0x%08X", data); break; } return fmt; } char *myDup(byte *data, int len) { int i; char *p = (char *)malloc(len+1); for (i = 0; i < len; i++) { p[i] = data[i*2]; } p[len] = '\0'; return p; } int doParse(int ac, char *av[]) { FILE *fp = NULL; int i, ret, num, depth, inTag; int stringCount = 0; uint32_t val32; uint16_t val16; size_t size; byte *bp, *p = NULL; char **ppStrArray = NULL; struct stat st; struct ResChunk_header h, *rchp; struct ResStringPool_header *rsph; struct ResXMLTree_header *rxth; struct ResXMLTree_node *rxtn; struct ResXMLTree_namespaceExt *rxnse; struct ResXMLTree_attrExt *rxtae; struct ResXMLTree_attribute *rxta; struct Res_value *rv; struct ResXMLTree_endElementExt *rxteee; if (ac < 2) { printf("usage: %s [Encoded XML-file]\n", av[0]); return 0; } if (stat(av[1], &st) < 0 || (fp = fopen(av[1], "rb")) == NULL) { printf("can't open %s\n", av[1]); return -1; } if (fread(&h, sizeof(h), 1, fp) < 1) { printf("can't read %s\n", av[1]); ret = -2; goto DONE; } // ヘッダの正当性をチェック if (h.type != RES_XML_TYPE || h.headerSize != sizeof(h) || h.size != (uint32_t)st.st_size) { printf("invalid data\n"); ret = -3; goto DONE; } // 丸ごとメモリへ読み込む rewind(fp); if ((p = (byte*)malloc(st.st_size)) == NULL || fread(p, st.st_size, 1, fp) < 1) { printf("buffering error\n"); ret = -4; goto DONE; } fclose(fp); fp = NULL; printf("[1.ファイルヘッダ]\n"); rchp = (struct ResChunk_header*)p; printf("%s: 識別子 = 0x%04X\n", dump(&rchp->type, 2), rchp->type); printf("%s: 本ヘッダサイズ = %d byte\n", dump(&rchp->headerSize, 2), rchp->headerSize); printf("%s: ファイルサイズ = %d byte\n", dump(&rchp->size, 4), rchp->size); p += sizeof(struct ResChunk_header); putchar('\n'); printf("[2.文字列プールヘッダ]\n"); rsph = (struct ResStringPool_header*)p; rchp = &rsph->header; printf("%s: 識別子 = 0x%04X\n", dump(&rchp->type, 2), rchp->type); printf("%s: 本ヘッダサイズ = %d byte\n", dump(&rchp->headerSize, 2), rchp->headerSize); printf("%s: 本ヘッダ+(C)+(D)のサイズ = %d byte\n", dump(&rchp->size, 4), rchp->size); printf("%s: 文字列プールの保持する文字列データ数 = %d\n", dump(&rsph->stringCount, 4), rsph->stringCount); stringCount = rsph->stringCount; printf("%s: 文字列プールの保持する style span arrays の数 = %d\n", dump(&rsph->styleCount, 4), rsph->styleCount); if (rsph->styleCount != 0) { printf("style データの処理には対応していません\n"); ret = -5; goto DONE; } printf("%s: フラグ = 0x%08X\n", dump(&rsph->flags, 4), rsph->flags); if (rsph->flags & UTF8_FLAG) { printf("UTF-8 の文字列プールには対応していません\n"); ret = -5; goto DONE; } printf("%s: 文字列プールの開始オフセット = 0x%08X (%d)\n", dump(&rsph->stringsStart, 4), rsph->stringsStart, rsph->stringsStart); printf("%s: style 文字列プールの開始オフセット = 0x%08X (%d)\n", dump(&rsph->stylesStart, 4), rsph->stylesStart, rsph->stylesStart); p += sizeof(struct ResStringPool_header); putchar('\n'); printf("[2-1.文字列プール上の各エントリの開始位置情報:可変長] (C)\n"); for (i = 0; i < (int)rsph->stringCount; i++) { val32 = *((uint32_t*)(p+(i*sizeof(uint32_t)))); printf("%s: [%02d] = 0x%08X (%d)\n", dump(&val32, 4), i, val32, val32); } putchar('\n'); printf("[2-2.文字列プール:可変長] (D)\n"); // 後続処理での参照のために文字列用の配列を作成 size = rsph->stringCount * sizeof(char*); ppStrArray = (char**)malloc(size); memset(ppStrArray, 0, size); for (i = 0; i < (int)rsph->stringCount; i++) { val32 = *((uint32_t*)(p+(i*sizeof(uint32_t)))); bp = (byte*)rsph + rsph->stringsStart + val32; val16 = *((uint16_t*)bp); printf("%s: len=%.2d; [%02d]", dump(&val16, 2), val16, i); ppStrArray[i] = myDup(bp + sizeof(val16), val16); printf(" = [%s]\n", ppStrArray[i]); } putchar('\n'); p = (byte*)rsph + rsph->header.size; printf("[3.XML 情報ヘッダ]\n"); rxth = (struct ResXMLTree_header*)p; printf("%s: 識別子 = 0x%04X\n", dump(&rxth->header.type, 2), rxth->header.type); printf("%s: 本ヘッダサイズ = %d byte\n", dump(&rxth->header.headerSize, 2), rxth->header.headerSize); printf("%s: 本ヘッダ+(E)のサイズ = %d byte\n", dump(&rxth->header.size, 4), rxth->header.size); putchar('\n'); p += rxth->header.headerSize; printf("[3-1.XML 使用属性 ID テーブル] (E)\n"); // テーブル上の ID 数を計算 num = (rxth->header.size - rxth->header.headerSize) / sizeof(uint32_t); for (i = 0; i < num; i++) { val32 = *((uint32_t*)(p+(i*sizeof(uint32_t)))); printf("%s: [%02d] = 0x%08X\n", dump(&val32, 4), i, val32); } putchar('\n'); p = (byte*)rxth + rxth->header.size; printf("[4.XML ツリー開始情報ノード]\n"); rxtn = (struct ResXMLTree_node*)p; printf("%s: 識別子 = 0x%04X\n", dump(&rxtn->header.type, 2), rxtn->header.type); printf("%s: 本ノードのサイズ = %d byte\n", dump(&rxtn->header.headerSize, 2), rxtn->header.headerSize); printf("%s: 本ノード+(F)のサイズ = %d byte\n", dump(&rxtn->header.size, 4), rxtn->header.size); printf("%s: 元の XML ファイル上の XML 記述開始位置の行番号 = %d\n", dump(&rxtn->lineNumber, 4), rxtn->lineNumber); printf("%s: 本ノードへのコメントの 文字列プール上の要素番号 = %d %s\n", dump(&rxtn->comment.index, 4), rxtn->comment.index, (rxtn->comment.index == 0xFFFFFFFF) ? "(none)" : ""); putchar('\n'); p += rxtn->header.headerSize; printf("[4-1.XML 名前空間情報ノード] (F)\n"); rxnse = (struct ResXMLTree_namespaceExt*)p; printf("%s: XML 名前空間接頭辞の 文字列プール上の要素番号 = %d\n", dump(&rxnse->prefix.index, 4), rxnse->prefix.index); if (rxnse->prefix.index != 0xFFFFFFFF) { printf("%*s-> pool[%02d] = [%s]\n", 14, "", rxnse->prefix.index, ppStrArray[rxnse->prefix.index]); } printf("%s: XML 名前空間 URI の 文字列プール上の要素番号 = %d\n", dump(&rxnse->uri.index, 4), rxnse->uri.index); if (rxnse->uri.index != 0xFFFFFFFF) { printf("%*s-> pool[%02d] = [%s]\n", 14, "", rxnse->uri.index, ppStrArray[rxnse->uri.index]); } putchar('\n'); p = (byte*)rxtn + rxtn->header.size; bp = p; for (;;) { val16 = *((uint16_t*)bp); if (val16 == RES_XML_START_ELEMENT_TYPE) { printf("[5.XML タグ開始情報ノード]\n"); rxtn = (struct ResXMLTree_node*)bp; printf("%s: 識別子 = 0x%04X\n", dump(&rxtn->header.type, 2), rxtn->header.type); printf("%s: 本ノードのサイズ = %d byte\n", dump(&rxtn->header.headerSize, 2), rxtn->header.headerSize); printf("%s: 本ノード+(G)+(I+K)*(H)のサイズ = %d byte\n", dump(&rxtn->header.size, 4), rxtn->header.size); printf("%s: 元の XML ファイル上の 本タグ記述開始位置の行番号 = %d\n", dump(&rxtn->lineNumber, 4), rxtn->lineNumber); printf("%s: 本ノードへのコメントの 文字列プール上の要素番号 = %d %s\n", dump(&rxtn->comment.index, 4), rxtn->comment.index, (rxtn->comment.index == 0xFFFFFFFF) ? "(none)" : ""); putchar('\n'); bp += rxtn->header.headerSize; printf("[5-1.XML タグ開始情報拡張ノード] (G)\n"); rxtae = (struct ResXMLTree_attrExt*)bp; printf("%s: この要素のフル名前空間名の 文字列プール上の要素番号 = %d %s\n", dump(&rxtae->ns.index, 4), rxtae->ns.index, (rxtae->ns.index == 0xFFFFFFFF) ? "(none)" : ""); printf("%s: このタグの要素名の 文字列プール上の要素番号 = %d\n", dump(&rxtae->name.index, 4), rxtae->name.index); if (rxtae->name.index != 0xFFFFFFFF) { printf("%*s-> pool[%02d] = [%s]\n", 14, "", rxtae->name.index, ppStrArray[rxtae->name.index]); } printf("%s: 属性情報開始位置 = 0x%04X (%d)\n", dump(&rxtae->attributeStart, 2), rxtae->attributeStart, rxtae->attributeStart); printf("%s: 属性情報一件あたりのサイズ = %d\n", dump(&rxtae->attributeSize, 2), rxtae->attributeSize); printf("%s: 属性情報の数 = %d (H)\n", dump(&rxtae->attributeCount, 2), rxtae->attributeCount); printf("%s: 「id」属性の要素番号 = %d (1オリジン; 存在しなければ 0)\n", dump(&rxtae->idIndex, 2), rxtae->idIndex); printf("%s: 「class」属性の要素番号 = %d (1オリジン; 存在しなければ 0)\n", dump(&rxtae->classIndex, 2), rxtae->classIndex); printf("%s: 「style」属性の要素番号 = %d (1オリジン; 存在しなければ 0)\n", dump(&rxtae->styleIndex, 2), rxtae->styleIndex); putchar('\n'); bp += sizeof(struct ResXMLTree_attrExt); if (rxtae->attributeCount == 0) { // 属性情報無し continue; } for (i = 0; i < rxtae->attributeCount; i++) { printf("[5-2.属性情報ノード] (I)\n"); rxta = (struct ResXMLTree_attribute*)bp; printf("%s: この要素のフル名前空間名の 文字列プール上の要素番号 = %d\n", dump(&rxta->ns.index, 4), rxta->ns.index); if (rxta->ns.index != 0xFFFFFFFF) { printf("%*s-> pool[%02d] = [%s]\n", 14, "", rxta->ns.index, ppStrArray[rxta->ns.index]); } printf("%s: この属性の名前の 文字列プール上の要素番号 = %d\n", dump(&rxta->name.index, 4), rxta->name.index); if (rxta->name.index != 0xFFFFFFFF) { printf("%*s-> pool[%02d] = [%s]\n", 14, "", rxta->name.index, ppStrArray[rxta->name.index]); } printf("%s: この属性の値の 文字列プール上の要素番号 = %d\n", dump(&rxta->rawValue.index, 4), rxta->rawValue.index); if (rxta->rawValue.index != 0xFFFFFFFF) { printf("%*s-> pool[%02d] = [%s]\n", 14, "", rxta->rawValue.index, ppStrArray[rxta->rawValue.index]); } putchar('\n'); printf("[5-3.属性値情報ノード] (K)\n"); rv = &rxta->typedValue; printf("%s: 本ノードのバイト長 = 0x%04X (%d)\n", dump(&rv->size, 2), rv->size, rv->size); printf("%s: (pad) = 0x%02X\n", dump(&rv->res0, 1), rv->res0); printf("%s: 属性値のタイプ = 0x%02X\n", dump(&rv->dataType, 1), rv->dataType); printf("%s: この属性の値 = 0x%08X (%d)\n", dump(&rv->data, 4), rv->data, rv->data); putchar('\n'); bp += sizeof(struct ResXMLTree_attribute); } } else if (val16 == RES_XML_END_ELEMENT_TYPE) { printf("[6.XML タグ終了情報ノード]\n"); rxtn = (struct ResXMLTree_node*)bp; printf("%s: 識別子 = 0x%04X\n", dump(&rxtn->header.type, 2), rxtn->header.type); printf("%s: 本ノードのサイズ = %d byte\n", dump(&rxtn->header.headerSize, 2), rxtn->header.headerSize); printf("%s: 本ノード+(N)のサイズ = %d byte\n", dump(&rxtn->header.size, 4), rxtn->header.size); printf("%s: 元の XML ファイル上の 本タグ記述開始位置の行番号 = %d\n", dump(&rxtn->lineNumber, 4), rxtn->lineNumber); printf("%s: 本ノードへのコメントの 文字列プール上の要素番号 = %d %s\n", dump(&rxtn->comment.index, 4), rxtn->comment.index, (rxtn->comment.index == 0xFFFFFFFF) ? "(none)" : ""); putchar('\n'); bp += rxtn->header.headerSize; printf("[6-1.XML タグ終了情報拡張ノード] (N)\n"); rxteee = (struct ResXMLTree_endElementExt*)bp; printf("%s: 本タグ要素のフル名前空間名の 文字列プール上の要素番号 = %d\n", dump(&rxteee->ns.index, 4), rxteee->ns.index); if (rxteee->ns.index != 0xFFFFFFFF) { printf("%*s-> pool[%02d] = [%s]\n", 14, "", rxteee->ns.index, ppStrArray[rxteee->ns.index]); } printf("%s: このタグの要素名の 文字列プール上の要素番号 = %d\n", dump(&rxteee->name.index, 4), rxteee->name.index); if (rxteee->name.index != 0xFFFFFFFF) { printf("%*s-> pool[%02d] = [%s]\n", 14, "", rxteee->name.index, ppStrArray[rxteee->name.index]); } putchar('\n'); bp += sizeof(struct ResXMLTree_endElementExt); } else if (val16 == RES_XML_END_NAMESPACE_TYPE) { printf("[7.XML ツリー終了情報ノード]\n"); rxtn = (struct ResXMLTree_node*)bp; printf("%s: 識別子 = 0x%04X\n", dump(&rxtn->header.type, 2), rxtn->header.type); printf("%s: 本ノードのサイズ = %d byte\n", dump(&rxtn->header.headerSize, 2), rxtn->header.headerSize); printf("%s: 本ノード+(O)のサイズ = %d byte\n", dump(&rxtn->header.size, 4), rxtn->header.size); printf("%s: 元の XML ファイル上の XML 記述終了位置の行番号 = %d\n", dump(&rxtn->lineNumber, 4), rxtn->lineNumber); printf("%s: 本ノードへのコメントの 文字列プール上の要素番号 = %d %s\n", dump(&rxtn->comment.index, 4), rxtn->comment.index, (rxtn->comment.index == 0xFFFFFFFF) ? "(none)" : ""); putchar('\n'); bp += rxtn->header.headerSize; printf("[7-1.XML 名前空間情報ノード] (O)\n"); rxnse = (struct ResXMLTree_namespaceExt*)bp; printf("%s: XML 名前空間接頭辞の 文字列プール上の要素番号 = %d\n", dump(&rxnse->prefix.index, 4), rxnse->prefix.index); if (rxnse->prefix.index != 0xFFFFFFFF) { printf("%*s-> pool[%02d] = [%s]\n", 14, "", rxnse->prefix.index, ppStrArray[rxnse->prefix.index]); } printf("%s: XML 名前空間 URI の 文字列プール上の要素番号 = %d\n", dump(&rxnse->uri.index, 4), rxnse->uri.index); if (rxnse->uri.index != 0xFFFFFFFF) { printf("%*s-> pool[%02d] = [%s]\n", 14, "", rxnse->uri.index, ppStrArray[rxnse->uri.index]); } putchar('\n'); break; } } // XML ツリーを再現してみる bp = p; depth = 0; inTag = 0; for (;;) { val16 = *((uint16_t*)bp); if (val16 == RES_XML_START_ELEMENT_TYPE) { // 親要素の開始タグを閉じる if (inTag == 1) { printf(">\n"); } inTag = 1; rxtn = (struct ResXMLTree_node*)bp; bp += rxtn->header.headerSize; rxtae = (struct ResXMLTree_attrExt*)bp; printf("%*s<%s", depth*4, "", ppStrArray[rxtae->name.index]); if (depth == 0) { printf(" xmlns:%s=\"%s\"", ppStrArray[rxnse->prefix.index], ppStrArray[rxnse->uri.index]); } bp += sizeof(struct ResXMLTree_attrExt); for (i = 0; i < rxtae->attributeCount; i++) { rxta = (struct ResXMLTree_attribute*)bp; if (rxtae->attributeCount > 2) { printf("\n%*s", (depth+1)*4, ""); } else { printf(" "); } if (rxta->ns.index == rxnse->uri.index) { printf("%s:", ppStrArray[rxnse->prefix.index]); } printf("%s", ppStrArray[rxta->name.index]); if (rxta->rawValue.index != 0xFFFFFFFF) { printf("=\"%s\"", ppStrArray[rxta->rawValue.index]); } else { rv = &rxta->typedValue; printf("=\"%s\"", makeAttrValue(rv->dataType, rv->data)); } bp += sizeof(struct ResXMLTree_attribute); } depth++; } else if (val16 == RES_XML_END_ELEMENT_TYPE) { depth--; rxtn = (struct ResXMLTree_node*)bp; bp += rxtn->header.headerSize; rxteee = (struct ResXMLTree_endElementExt*)bp; // タグを閉じる if (inTag == 1) { printf(" />\n"); inTag = 0; } else { printf("%*s</%s>\n", depth*4, "", ppStrArray[rxteee->name.index]); } bp += sizeof(struct ResXMLTree_endElementExt); } else if (val16 == RES_XML_END_NAMESPACE_TYPE) { break; } } ret = 0; DONE: if (ppStrArray) { for (i = 0; i < stringCount; i++) { if (ppStrArray[i]) { free(ppStrArray[i]); } } free(ppStrArray); } if (p) { free(p); } if (fp) { fclose(fp); } return ret; } int main(int ac, char *av[]) { return doParse(ac, av); }
5. 試作パーサの実行例
以下は冒頭に記載したサンプルアプリ「MyApp」の apk に含まれる AndroidManifest.xml を上のツールで処理した結果の出力です。
[1.ファイルヘッダ] 03 00 : 識別子 = 0x0003 08 00 : 本ヘッダサイズ = 8 byte 0C 07 00 00 : ファイルサイズ = 1804 byte [2.文字列プールヘッダ] 01 00 : 識別子 = 0x0001 1C 00 : 本ヘッダサイズ = 28 byte B0 03 00 00 : 本ヘッダ+(C)+(D)のサイズ = 944 byte 1B 00 00 00 : 文字列プールの保持する文字列データ数 = 27 00 00 00 00 : 文字列プールの保持する style span arrays の数 = 0 00 00 00 00 : フラグ = 0x00000000 88 00 00 00 : 文字列プールの開始オフセット = 0x00000088 (136) 00 00 00 00 : style 文字列プールの開始オフセット = 0x00000000 (0) [2-1.文字列プール上の各エントリの開始位置情報:可変長] (C) 00 00 00 00 : [00] = 0x00000000 (0) 1A 00 00 00 : [01] = 0x0000001A (26) 34 00 00 00 : [02] = 0x00000034 (52) 52 00 00 00 : [03] = 0x00000052 (82) 5E 00 00 00 : [04] = 0x0000005E (94) 6C 00 00 00 : [05] = 0x0000006C (108) 78 00 00 00 : [06] = 0x00000078 (120) 90 00 00 00 : [07] = 0x00000090 (144) B8 00 00 00 : [08] = 0x000000B8 (184) D6 00 00 00 : [09] = 0x000000D6 (214) E8 00 00 00 : [10] = 0x000000E8 (232) 40 01 00 00 : [11] = 0x00000140 (320) 44 01 00 00 : [12] = 0x00000144 (324) 56 01 00 00 : [13] = 0x00000156 (342) 6A 01 00 00 : [14] = 0x0000016A (362) 96 01 00 00 : [15] = 0x00000196 (406) A0 01 00 00 : [16] = 0x000001A0 (416) B4 01 00 00 : [17] = 0x000001B4 (436) CE 01 00 00 : [18] = 0x000001CE (462) E2 01 00 00 : [19] = 0x000001E2 (482) F2 01 00 00 : [20] = 0x000001F2 (498) 10 02 00 00 : [21] = 0x00000210 (528) 20 02 00 00 : [22] = 0x00000220 (544) 58 02 00 00 : [23] = 0x00000258 (600) 6C 02 00 00 : [24] = 0x0000026C (620) B0 02 00 00 : [25] = 0x000002B0 (688) D2 02 00 00 : [26] = 0x000002D2 (722) [2-2.文字列プール:可変長] (D) 0B 00 : len=11; [00] = [versionCode] 0B 00 : len=11; [01] = [versionName] 0D 00 : len=13; [02] = [minSdkVersion] 04 00 : len=04; [03] = [icon] 05 00 : len=05; [04] = [label] 04 00 : len=04; [05] = [name] 0A 00 : len=10; [06] = [launchMode] 12 00 : len=18; [07] = [excludeFromRecents] 0D 00 : len=13; [08] = [configChanges] 07 00 : len=07; [09] = [android] 2A 00 : len=42; [10] = [http://schemas.android.com/apk/res/android] 00 00 : len=00; [11] = [] 07 00 : len=07; [12] = [package] 08 00 : len=08; [13] = [manifest] 14 00 : len=20; [14] = [jp.klab.sample.myapp] 03 00 : len=03; [15] = [1.0] 08 00 : len=08; [16] = [uses-sdk] 0B 00 : len=11; [17] = [application] 08 00 : len=08; [18] = [activity] 06 00 : len=06; [19] = [.MyApp] 0D 00 : len=13; [20] = [intent-filter] 06 00 : len=06; [21] = [action] 1A 00 : len=26; [22] = [android.intent.action.MAIN] 08 00 : len=08; [23] = [category] 20 00 : len=32; [24] = [android.intent.category.LAUNCHER] 0F 00 : len=15; [25] = [uses-permission] 29 00 : len=41; [26] = [android.permission.WRITE_EXTERNAL_STORAGE] [3.XML 情報ヘッダ] 80 01 : 識別子 = 0x0180 08 00 : 本ヘッダサイズ = 8 byte 2C 00 00 00 : 本ヘッダ+(E)のサイズ = 44 byte [3-1.XML 使用属性 ID テーブル] (E) 1B 02 01 01 : [00] = 0x0101021B 1C 02 01 01 : [01] = 0x0101021C 0C 02 01 01 : [02] = 0x0101020C 02 00 01 01 : [03] = 0x01010002 01 00 01 01 : [04] = 0x01010001 03 00 01 01 : [05] = 0x01010003 1D 00 01 01 : [06] = 0x0101001D 17 00 01 01 : [07] = 0x01010017 1F 00 01 01 : [08] = 0x0101001F [4.XML ツリー開始情報ノード] 00 01 : 識別子 = 0x0100 10 00 : 本ノードのサイズ = 16 byte 18 00 00 00 : 本ノード+(F)のサイズ = 24 byte 02 00 00 00 : 元の XML ファイル上の XML 記述開始位置の行番号 = 2 FF FF FF FF : 本ノードへのコメントの 文字列プール上の要素番号 = -1 (none) [4-1.XML 名前空間情報ノード] (F) 09 00 00 00 : XML 名前空間接頭辞の 文字列プール上の要素番号 = 9 -> pool[09] = [android] 0A 00 00 00 : XML 名前空間 URI の 文字列プール上の要素番号 = 10 -> pool[10] = [http://schemas.android.com/apk/res/android] [5.XML タグ開始情報ノード] 02 01 : 識別子 = 0x0102 10 00 : 本ノードのサイズ = 16 byte 60 00 00 00 : 本ノード+(G)+(I+K)*(H)のサイズ = 96 byte 02 00 00 00 : 元の XML ファイル上の 本タグ記述開始位置の行番号 = 2 FF FF FF FF : 本ノードへのコメントの 文字列プール上の要素番号 = -1 (none) [5-1.XML タグ開始情報拡張ノード] (G) FF FF FF FF : この要素のフル名前空間名の 文字列プール上の要素番号 = -1 (none) 0D 00 00 00 : このタグの要素名の 文字列プール上の要素番号 = 13 -> pool[13] = [manifest] 14 00 : 属性情報開始位置 = 0x0014 (20) 14 00 : 属性情報一件あたりのサイズ = 20 03 00 : 属性情報の数 = 3 (H) 00 00 : 「id」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) 00 00 : 「class」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) 00 00 : 「style」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) [5-2.属性情報ノード] (I) 0A 00 00 00 : この要素のフル名前空間名の 文字列プール上の要素番号 = 10 -> pool[10] = [http://schemas.android.com/apk/res/android] 00 00 00 00 : この属性の名前の 文字列プール上の要素番号 = 0 -> pool[00] = [versionCode] FF FF FF FF : この属性の値の 文字列プール上の要素番号 = -1 [5-3.属性値情報ノード] (K) 08 00 : 本ノードのバイト長 = 0x0008 (8) 00 : (pad) = 0x00 10 : 属性値のタイプ = 0x10 01 00 00 00 : この属性の値 = 0x00000001 (1) [5-2.属性情報ノード] (I) 0A 00 00 00 : この要素のフル名前空間名の 文字列プール上の要素番号 = 10 -> pool[10] = [http://schemas.android.com/apk/res/android] 01 00 00 00 : この属性の名前の 文字列プール上の要素番号 = 1 -> pool[01] = [versionName] 0F 00 00 00 : この属性の値の 文字列プール上の要素番号 = 15 -> pool[15] = [1.0] [5-3.属性値情報ノード] (K) 08 00 : 本ノードのバイト長 = 0x0008 (8) 00 : (pad) = 0x00 03 : 属性値のタイプ = 0x03 0F 00 00 00 : この属性の値 = 0x0000000F (15) [5-2.属性情報ノード] (I) FF FF FF FF : この要素のフル名前空間名の 文字列プール上の要素番号 = -1 0C 00 00 00 : この属性の名前の 文字列プール上の要素番号 = 12 -> pool[12] = [package] 0E 00 00 00 : この属性の値の 文字列プール上の要素番号 = 14 -> pool[14] = [jp.klab.sample.myapp] [5-3.属性値情報ノード] (K) 08 00 : 本ノードのバイト長 = 0x0008 (8) 00 : (pad) = 0x00 03 : 属性値のタイプ = 0x03 0E 00 00 00 : この属性の値 = 0x0000000E (14) [5.XML タグ開始情報ノード] 02 01 : 識別子 = 0x0102 10 00 : 本ノードのサイズ = 16 byte 38 00 00 00 : 本ノード+(G)+(I+K)*(H)のサイズ = 56 byte 06 00 00 00 : 元の XML ファイル上の 本タグ記述開始位置の行番号 = 6 FF FF FF FF : 本ノードへのコメントの 文字列プール上の要素番号 = -1 (none) [5-1.XML タグ開始情報拡張ノード] (G) FF FF FF FF : この要素のフル名前空間名の 文字列プール上の要素番号 = -1 (none) 10 00 00 00 : このタグの要素名の 文字列プール上の要素番号 = 16 -> pool[16] = [uses-sdk] 14 00 : 属性情報開始位置 = 0x0014 (20) 14 00 : 属性情報一件あたりのサイズ = 20 01 00 : 属性情報の数 = 1 (H) 00 00 : 「id」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) 00 00 : 「class」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) 00 00 : 「style」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) [5-2.属性情報ノード] (I) 0A 00 00 00 : この要素のフル名前空間名の 文字列プール上の要素番号 = 10 -> pool[10] = [http://schemas.android.com/apk/res/android] 02 00 00 00 : この属性の名前の 文字列プール上の要素番号 = 2 -> pool[02] = [minSdkVersion] FF FF FF FF : この属性の値の 文字列プール上の要素番号 = -1 [5-3.属性値情報ノード] (K) 08 00 : 本ノードのバイト長 = 0x0008 (8) 00 : (pad) = 0x00 10 : 属性値のタイプ = 0x10 04 00 00 00 : この属性の値 = 0x00000004 (4) [6.XML タグ終了情報ノード] 03 01 : 識別子 = 0x0103 10 00 : 本ノードのサイズ = 16 byte 18 00 00 00 : 本ノード+(N)のサイズ = 24 byte 06 00 00 00 : 元の XML ファイル上の 本タグ記述開始位置の行番号 = 6 FF FF FF FF : 本ノードへのコメントの 文字列プール上の要素番号 = -1 (none) [6-1.XML タグ終了情報拡張ノード] (N) FF FF FF FF : 本タグ要素のフル名前空間名の 文字列プール上の要素番号 = -1 10 00 00 00 : このタグの要素名の 文字列プール上の要素番号 = 16 -> pool[16] = [uses-sdk] [5.XML タグ開始情報ノード] 02 01 : 識別子 = 0x0102 10 00 : 本ノードのサイズ = 16 byte 4C 00 00 00 : 本ノード+(G)+(I+K)*(H)のサイズ = 76 byte 07 00 00 00 : 元の XML ファイル上の 本タグ記述開始位置の行番号 = 7 FF FF FF FF : 本ノードへのコメントの 文字列プール上の要素番号 = -1 (none) [5-1.XML タグ開始情報拡張ノード] (G) FF FF FF FF : この要素のフル名前空間名の 文字列プール上の要素番号 = -1 (none) 11 00 00 00 : このタグの要素名の 文字列プール上の要素番号 = 17 -> pool[17] = [application] 14 00 : 属性情報開始位置 = 0x0014 (20) 14 00 : 属性情報一件あたりのサイズ = 20 02 00 : 属性情報の数 = 2 (H) 00 00 : 「id」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) 00 00 : 「class」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) 00 00 : 「style」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) [5-2.属性情報ノード] (I) 0A 00 00 00 : この要素のフル名前空間名の 文字列プール上の要素番号 = 10 -> pool[10] = [http://schemas.android.com/apk/res/android] 04 00 00 00 : この属性の名前の 文字列プール上の要素番号 = 4 -> pool[04] = [label] FF FF FF FF : この属性の値の 文字列プール上の要素番号 = -1 [5-3.属性値情報ノード] (K) 08 00 : 本ノードのバイト長 = 0x0008 (8) 00 : (pad) = 0x00 01 : 属性値のタイプ = 0x01 01 00 05 7F : この属性の値 = 0x7F050001 (2131034113) [5-2.属性情報ノード] (I) 0A 00 00 00 : この要素のフル名前空間名の 文字列プール上の要素番号 = 10 -> pool[10] = [http://schemas.android.com/apk/res/android] 03 00 00 00 : この属性の名前の 文字列プール上の要素番号 = 3 -> pool[03] = [icon] FF FF FF FF : この属性の値の 文字列プール上の要素番号 = -1 [5-3.属性値情報ノード] (K) 08 00 : 本ノードのバイト長 = 0x0008 (8) 00 : (pad) = 0x00 01 : 属性値のタイプ = 0x01 00 00 02 7F : この属性の値 = 0x7F020000 (2130837504) [5.XML タグ開始情報ノード] 02 01 : 識別子 = 0x0102 10 00 : 本ノードのサイズ = 16 byte 88 00 00 00 : 本ノード+(G)+(I+K)*(H)のサイズ = 136 byte 08 00 00 00 : 元の XML ファイル上の 本タグ記述開始位置の行番号 = 8 FF FF FF FF : 本ノードへのコメントの 文字列プール上の要素番号 = -1 (none) [5-1.XML タグ開始情報拡張ノード] (G) FF FF FF FF : この要素のフル名前空間名の 文字列プール上の要素番号 = -1 (none) 12 00 00 00 : このタグの要素名の 文字列プール上の要素番号 = 18 -> pool[18] = [activity] 14 00 : 属性情報開始位置 = 0x0014 (20) 14 00 : 属性情報一件あたりのサイズ = 20 05 00 : 属性情報の数 = 5 (H) 00 00 : 「id」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) 00 00 : 「class」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) 00 00 : 「style」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) [5-2.属性情報ノード] (I) 0A 00 00 00 : この要素のフル名前空間名の 文字列プール上の要素番号 = 10 -> pool[10] = [http://schemas.android.com/apk/res/android] 04 00 00 00 : この属性の名前の 文字列プール上の要素番号 = 4 -> pool[04] = [label] FF FF FF FF : この属性の値の 文字列プール上の要素番号 = -1 [5-3.属性値情報ノード] (K) 08 00 : 本ノードのバイト長 = 0x0008 (8) 00 : (pad) = 0x00 01 : 属性値のタイプ = 0x01 01 00 05 7F : この属性の値 = 0x7F050001 (2131034113) [5-2.属性情報ノード] (I) 0A 00 00 00 : この要素のフル名前空間名の 文字列プール上の要素番号 = 10 -> pool[10] = [http://schemas.android.com/apk/res/android] 05 00 00 00 : この属性の名前の 文字列プール上の要素番号 = 5 -> pool[05] = [name] 13 00 00 00 : この属性の値の 文字列プール上の要素番号 = 19 -> pool[19] = [.MyApp] [5-3.属性値情報ノード] (K) 08 00 : 本ノードのバイト長 = 0x0008 (8) 00 : (pad) = 0x00 03 : 属性値のタイプ = 0x03 13 00 00 00 : この属性の値 = 0x00000013 (19) [5-2.属性情報ノード] (I) 0A 00 00 00 : この要素のフル名前空間名の 文字列プール上の要素番号 = 10 -> pool[10] = [http://schemas.android.com/apk/res/android] 07 00 00 00 : この属性の名前の 文字列プール上の要素番号 = 7 -> pool[07] = [excludeFromRecents] FF FF FF FF : この属性の値の 文字列プール上の要素番号 = -1 [5-3.属性値情報ノード] (K) 08 00 : 本ノードのバイト長 = 0x0008 (8) 00 : (pad) = 0x00 12 : 属性値のタイプ = 0x12 00 00 00 00 : この属性の値 = 0x00000000 (0) [5-2.属性情報ノード] (I) 0A 00 00 00 : この要素のフル名前空間名の 文字列プール上の要素番号 = 10 -> pool[10] = [http://schemas.android.com/apk/res/android] 06 00 00 00 : この属性の名前の 文字列プール上の要素番号 = 6 -> pool[06] = [launchMode] FF FF FF FF : この属性の値の 文字列プール上の要素番号 = -1 [5-3.属性値情報ノード] (K) 08 00 : 本ノードのバイト長 = 0x0008 (8) 00 : (pad) = 0x00 10 : 属性値のタイプ = 0x10 02 00 00 00 : この属性の値 = 0x00000002 (2) [5-2.属性情報ノード] (I) 0A 00 00 00 : この要素のフル名前空間名の 文字列プール上の要素番号 = 10 -> pool[10] = [http://schemas.android.com/apk/res/android] 08 00 00 00 : この属性の名前の 文字列プール上の要素番号 = 8 -> pool[08] = [configChanges] FF FF FF FF : この属性の値の 文字列プール上の要素番号 = -1 [5-3.属性値情報ノード] (K) 08 00 : 本ノードのバイト長 = 0x0008 (8) 00 : (pad) = 0x00 11 : 属性値のタイプ = 0x11 A0 00 00 00 : この属性の値 = 0x000000A0 (160) [5.XML タグ開始情報ノード] 02 01 : 識別子 = 0x0102 10 00 : 本ノードのサイズ = 16 byte 24 00 00 00 : 本ノード+(G)+(I+K)*(H)のサイズ = 36 byte 0D 00 00 00 : 元の XML ファイル上の 本タグ記述開始位置の行番号 = 13 FF FF FF FF : 本ノードへのコメントの 文字列プール上の要素番号 = -1 (none) [5-1.XML タグ開始情報拡張ノード] (G) FF FF FF FF : この要素のフル名前空間名の 文字列プール上の要素番号 = -1 (none) 14 00 00 00 : このタグの要素名の 文字列プール上の要素番号 = 20 -> pool[20] = [intent-filter] 14 00 : 属性情報開始位置 = 0x0014 (20) 14 00 : 属性情報一件あたりのサイズ = 20 00 00 : 属性情報の数 = 0 (H) 00 00 : 「id」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) 00 00 : 「class」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) 00 00 : 「style」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) [5.XML タグ開始情報ノード] 02 01 : 識別子 = 0x0102 10 00 : 本ノードのサイズ = 16 byte 38 00 00 00 : 本ノード+(G)+(I+K)*(H)のサイズ = 56 byte 0E 00 00 00 : 元の XML ファイル上の 本タグ記述開始位置の行番号 = 14 FF FF FF FF : 本ノードへのコメントの 文字列プール上の要素番号 = -1 (none) [5-1.XML タグ開始情報拡張ノード] (G) FF FF FF FF : この要素のフル名前空間名の 文字列プール上の要素番号 = -1 (none) 15 00 00 00 : このタグの要素名の 文字列プール上の要素番号 = 21 -> pool[21] = [action] 14 00 : 属性情報開始位置 = 0x0014 (20) 14 00 : 属性情報一件あたりのサイズ = 20 01 00 : 属性情報の数 = 1 (H) 00 00 : 「id」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) 00 00 : 「class」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) 00 00 : 「style」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) [5-2.属性情報ノード] (I) 0A 00 00 00 : この要素のフル名前空間名の 文字列プール上の要素番号 = 10 -> pool[10] = [http://schemas.android.com/apk/res/android] 05 00 00 00 : この属性の名前の 文字列プール上の要素番号 = 5 -> pool[05] = [name] 16 00 00 00 : この属性の値の 文字列プール上の要素番号 = 22 -> pool[22] = [android.intent.action.MAIN] [5-3.属性値情報ノード] (K) 08 00 : 本ノードのバイト長 = 0x0008 (8) 00 : (pad) = 0x00 03 : 属性値のタイプ = 0x03 16 00 00 00 : この属性の値 = 0x00000016 (22) [6.XML タグ終了情報ノード] 03 01 : 識別子 = 0x0103 10 00 : 本ノードのサイズ = 16 byte 18 00 00 00 : 本ノード+(N)のサイズ = 24 byte 0E 00 00 00 : 元の XML ファイル上の 本タグ記述開始位置の行番号 = 14 FF FF FF FF : 本ノードへのコメントの 文字列プール上の要素番号 = -1 (none) [6-1.XML タグ終了情報拡張ノード] (N) FF FF FF FF : 本タグ要素のフル名前空間名の 文字列プール上の要素番号 = -1 15 00 00 00 : このタグの要素名の 文字列プール上の要素番号 = 21 -> pool[21] = [action] [5.XML タグ開始情報ノード] 02 01 : 識別子 = 0x0102 10 00 : 本ノードのサイズ = 16 byte 38 00 00 00 : 本ノード+(G)+(I+K)*(H)のサイズ = 56 byte 0F 00 00 00 : 元の XML ファイル上の 本タグ記述開始位置の行番号 = 15 FF FF FF FF : 本ノードへのコメントの 文字列プール上の要素番号 = -1 (none) [5-1.XML タグ開始情報拡張ノード] (G) FF FF FF FF : この要素のフル名前空間名の 文字列プール上の要素番号 = -1 (none) 17 00 00 00 : このタグの要素名の 文字列プール上の要素番号 = 23 -> pool[23] = [category] 14 00 : 属性情報開始位置 = 0x0014 (20) 14 00 : 属性情報一件あたりのサイズ = 20 01 00 : 属性情報の数 = 1 (H) 00 00 : 「id」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) 00 00 : 「class」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) 00 00 : 「style」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) [5-2.属性情報ノード] (I) 0A 00 00 00 : この要素のフル名前空間名の 文字列プール上の要素番号 = 10 -> pool[10] = [http://schemas.android.com/apk/res/android] 05 00 00 00 : この属性の名前の 文字列プール上の要素番号 = 5 -> pool[05] = [name] 18 00 00 00 : この属性の値の 文字列プール上の要素番号 = 24 -> pool[24] = [android.intent.category.LAUNCHER] [5-3.属性値情報ノード] (K) 08 00 : 本ノードのバイト長 = 0x0008 (8) 00 : (pad) = 0x00 03 : 属性値のタイプ = 0x03 18 00 00 00 : この属性の値 = 0x00000018 (24) [6.XML タグ終了情報ノード] 03 01 : 識別子 = 0x0103 10 00 : 本ノードのサイズ = 16 byte 18 00 00 00 : 本ノード+(N)のサイズ = 24 byte 0F 00 00 00 : 元の XML ファイル上の 本タグ記述開始位置の行番号 = 15 FF FF FF FF : 本ノードへのコメントの 文字列プール上の要素番号 = -1 (none) [6-1.XML タグ終了情報拡張ノード] (N) FF FF FF FF : 本タグ要素のフル名前空間名の 文字列プール上の要素番号 = -1 17 00 00 00 : このタグの要素名の 文字列プール上の要素番号 = 23 -> pool[23] = [category] [6.XML タグ終了情報ノード] 03 01 : 識別子 = 0x0103 10 00 : 本ノードのサイズ = 16 byte 18 00 00 00 : 本ノード+(N)のサイズ = 24 byte 10 00 00 00 : 元の XML ファイル上の 本タグ記述開始位置の行番号 = 16 FF FF FF FF : 本ノードへのコメントの 文字列プール上の要素番号 = -1 (none) [6-1.XML タグ終了情報拡張ノード] (N) FF FF FF FF : 本タグ要素のフル名前空間名の 文字列プール上の要素番号 = -1 14 00 00 00 : このタグの要素名の 文字列プール上の要素番号 = 20 -> pool[20] = [intent-filter] [6.XML タグ終了情報ノード] 03 01 : 識別子 = 0x0103 10 00 : 本ノードのサイズ = 16 byte 18 00 00 00 : 本ノード+(N)のサイズ = 24 byte 11 00 00 00 : 元の XML ファイル上の 本タグ記述開始位置の行番号 = 17 FF FF FF FF : 本ノードへのコメントの 文字列プール上の要素番号 = -1 (none) [6-1.XML タグ終了情報拡張ノード] (N) FF FF FF FF : 本タグ要素のフル名前空間名の 文字列プール上の要素番号 = -1 12 00 00 00 : このタグの要素名の 文字列プール上の要素番号 = 18 -> pool[18] = [activity] [6.XML タグ終了情報ノード] 03 01 : 識別子 = 0x0103 10 00 : 本ノードのサイズ = 16 byte 18 00 00 00 : 本ノード+(N)のサイズ = 24 byte 13 00 00 00 : 元の XML ファイル上の 本タグ記述開始位置の行番号 = 19 FF FF FF FF : 本ノードへのコメントの 文字列プール上の要素番号 = -1 (none) [6-1.XML タグ終了情報拡張ノード] (N) FF FF FF FF : 本タグ要素のフル名前空間名の 文字列プール上の要素番号 = -1 11 00 00 00 : このタグの要素名の 文字列プール上の要素番号 = 17 -> pool[17] = [application] [5.XML タグ開始情報ノード] 02 01 : 識別子 = 0x0102 10 00 : 本ノードのサイズ = 16 byte 38 00 00 00 : 本ノード+(G)+(I+K)*(H)のサイズ = 56 byte 14 00 00 00 : 元の XML ファイル上の 本タグ記述開始位置の行番号 = 20 FF FF FF FF : 本ノードへのコメントの 文字列プール上の要素番号 = -1 (none) [5-1.XML タグ開始情報拡張ノード] (G) FF FF FF FF : この要素のフル名前空間名の 文字列プール上の要素番号 = -1 (none) 19 00 00 00 : このタグの要素名の 文字列プール上の要素番号 = 25 -> pool[25] = [uses-permission] 14 00 : 属性情報開始位置 = 0x0014 (20) 14 00 : 属性情報一件あたりのサイズ = 20 01 00 : 属性情報の数 = 1 (H) 00 00 : 「id」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) 00 00 : 「class」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) 00 00 : 「style」属性の要素番号 = 0 (1オリジン; 存在しなければ 0) [5-2.属性情報ノード] (I) 0A 00 00 00 : この要素のフル名前空間名の 文字列プール上の要素番号 = 10 -> pool[10] = [http://schemas.android.com/apk/res/android] 05 00 00 00 : この属性の名前の 文字列プール上の要素番号 = 5 -> pool[05] = [name] 1A 00 00 00 : この属性の値の 文字列プール上の要素番号 = 26 -> pool[26] = [android.permission.WRITE_EXTERNAL_STORAGE] [5-3.属性値情報ノード] (K) 08 00 : 本ノードのバイト長 = 0x0008 (8) 00 : (pad) = 0x00 03 : 属性値のタイプ = 0x03 1A 00 00 00 : この属性の値 = 0x0000001A (26) [6.XML タグ終了情報ノード] 03 01 : 識別子 = 0x0103 10 00 : 本ノードのサイズ = 16 byte 18 00 00 00 : 本ノード+(N)のサイズ = 24 byte 14 00 00 00 : 元の XML ファイル上の 本タグ記述開始位置の行番号 = 20 FF FF FF FF : 本ノードへのコメントの 文字列プール上の要素番号 = -1 (none) [6-1.XML タグ終了情報拡張ノード] (N) FF FF FF FF : 本タグ要素のフル名前空間名の 文字列プール上の要素番号 = -1 19 00 00 00 : このタグの要素名の 文字列プール上の要素番号 = 25 -> pool[25] = [uses-permission] [6.XML タグ終了情報ノード] 03 01 : 識別子 = 0x0103 10 00 : 本ノードのサイズ = 16 byte 18 00 00 00 : 本ノード+(N)のサイズ = 24 byte 15 00 00 00 : 元の XML ファイル上の 本タグ記述開始位置の行番号 = 21 FF FF FF FF : 本ノードへのコメントの 文字列プール上の要素番号 = -1 (none) [6-1.XML タグ終了情報拡張ノード] (N) FF FF FF FF : 本タグ要素のフル名前空間名の 文字列プール上の要素番号 = -1 0D 00 00 00 : このタグの要素名の 文字列プール上の要素番号 = 13 -> pool[13] = [manifest] [7.XML ツリー終了情報ノード] 01 01 : 識別子 = 0x0101 10 00 : 本ノードのサイズ = 16 byte 18 00 00 00 : 本ノード+(O)のサイズ = 24 byte 15 00 00 00 : 元の XML ファイル上の XML 記述終了位置の行番号 = 21 FF FF FF FF : 本ノードへのコメントの 文字列プール上の要素番号 = -1 (none) [7-1.XML 名前空間情報ノード] (O) 09 00 00 00 : XML 名前空間接頭辞の 文字列プール上の要素番号 = 9 -> pool[09] = [android] 0A 00 00 00 : XML 名前空間 URI の 文字列プール上の要素番号 = 10 -> pool[10] = [http://schemas.android.com/apk/res/android] <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="jp.klab.sample.myapp"> <uses-sdk android:minSdkVersion="4" /> <application android:label="@0x7F050001" android:icon="@0x7F020000"> <activity android:label="@0x7F050001" android:name=".MyApp" android:excludeFromRecents="false" android:launchMode="2" android:configChanges="0x000000A0"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> </manifest>
(tanabe)