- обзор
- Что такое файл Dex
- Как создается файл Dex
- Из .java в .class
- От .class до .dex
- Конкретный формат файла Dex
- Заголовок файла
- область идентификатора
- Область идентификатора строки
- Введите идентификатор области
- Область идентификатора прототипа метода
- Область идентификатора участника
- Область идентификатора метода
- Область определения класса
- Инструменты для разбора файлов dex
- Применение файла Dex в Android Tinker для горячего ремонта
- Генерация патчей
- Синтез патчей
- Напиши в конце
- Справочные ресурсы
обзор
После понимания файла Dex я смог глубже понять некоторые проблемы, возникающие в повседневной разработке. Такие как: APK для похудения, горячий ремонт, плагин, усиление приложений, Android-реверс-инжиниринг, ограничение метода 64 К.
Что такое файл Dex
Прежде чем понять, что такое файл Dex, вы должны сначала понять JVM, Dalvik и ART. JVM — это виртуальная машина JAVA, используемая для запуска программ байт-кода JAVA. Dalvik — это среда выполнения, разработанная Google для платформы Android, которая подходит для систем с ограниченной памятью и скоростью процессора в мобильной среде. ART, или Android Runtime, — это новая среда выполнения Android, разработанная Google для замены Dalvik, и она была запущена в Android 4.4. ART обладает лучшими характеристиками, чем Dalvik. Программы для Android обычно разрабатываются с использованием языка Java, но виртуальная машина Dalvik не поддерживает непосредственное выполнение байт-кода JAVA, поэтому скомпилированные файлы .class будут переведены, восстановлены, интерпретированы, сжаты и т. Д. Этот процесс выполняется dx После обработки продукт, сгенерированный после обработки, оканчивается на .dex, который называется Dex file. Формат файла Dex — это формат сжатия, разработанный специально для Dalvik. Таким образом, его можно просто понять как: Dex-файл является продуктом многих обработанных файлов .class и, наконец, может быть выполнен в среде выполнения Android.
Как создается файл Dex
Процесс преобразования Java-кода в dex-файл показан на рисунке. Конечно, этот процесс не так прост. Вот только визуальное отображение:
Примечание: картина приходит из интернета
Теперь давайте воспользуемся простым примером для реализации преобразования кода Java в файл dex.
Из .java в .class
Давайте сначала создадим файл Hello.java. Чтобы упростить анализ, приведем простой код. код шоу, как показано ниже:
public class Hello < private String helloString = "hello! youzan"; public static void main(String[] args) < Hello hello = new Hello(); hello.fun(hello.helloString); >public void fun(String a) < System.out.println(a); >>
Используйте javac JDK для компиляции java-файла в том же каталоге уровня файла.
После выполнения команды javac файл Hello.class будет сгенерирован в текущем каталоге, и файл Hello.class уже может быть непосредственно выполнен на виртуальной машине JVM. Используйте команду, чтобы выполнить файл здесь.
После выполнения он должен распечатать «привет! Youzan» на консоли
Здесь вы также можете выполнить команду javap для дизассемблирования файла Hello.class.
Результаты выполнения следующие:
public class Hello < public Hello(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: aload_0 5: ldc #2 // String hello! youzan 7: putfield #3 // Field helloString:Ljava/lang/String; 10: return public static void main(java.lang.String[]); Code: 0: new #4 // class Hello 3: dup 4: invokespecial #5 // Method "":()V 7: astore_1 8: aload_1 9: aload_1 10: getfield #3 // Field helloString:Ljava/lang/String; 13: invokevirtual #6 // Method fun:(Ljava/lang/String;)V 16: return public void fun(java.lang.String); Code: 0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream; 3: aload_1 4: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 7: return >
После кода существуют конкретные инструкции для виртуальной машины JVM для выполнения. Для конкретного значения инструкции, пожалуйста, обратитесь к официальному документу JAVA.
От .class до .dex
Хотя созданный выше файл .class можно запускать в среде JVM, если он должен выполняться в среде выполнения Android, требуется специальная обработка, то есть обработка dx, которая преобразует, реконструирует, интерпретирует и интерпретирует файл .class. Операции, такие как сжатие.
Для обработки dx будет использоваться инструмент dx.jar, этот файл находится в SDK, конкретный каталог примерноВаш корневой каталог SDK / build-tools / любая версия внутри. Используйте инструмент dx для обработки файла Hello.class, сгенерированного выше, и используйте следующую команду в каталоге Hello.class:
dx --dex --output=Hello.dex Hello.class
После завершения выполнения файл Hello.dex будет создан в текущем каталоге. Этот файл .dex может быть выполнен непосредственно в среде выполнения Android, а файл dex обычно может быть загружен через PathClassLoader. Теперь выполнить в текущем каталогеdexdump Имя для декомпиляции:
Результаты выполнения следующие (значение некоторых областей описано ниже):
Processing 'Hello.dex'. Opened 'Hello.dex', DEX version '035' ------ Вот информация о классе Hello.java написано ------ Class #0 - Class descriptor : 'LHello;' Access flags : 0x0001 (PUBLIC) Superclass : 'Ljava/lang/Object;' Interfaces - Static fields - Instance fields - #0 : (in LHello;) name : 'helloString' type : 'Ljava/lang/String;' access : 0x0002 (PRIVATE) ------ Следующая область описывает информацию о методе строительства. Цифры, такие как 7010 0400 0100 1a00 0b00 - это инструкции, переведенные в код метода. Dalvik использует 16-битную кодовую единицу, поэтому здесь есть группа из 4 чисел, каждое число в шестнадцатеричном формате. invoke-direct Это мнемоника, соответствующая предыдущим инструкциям, а также представляющая фактическую работу этих инструкций. Если вы заинтересованы в этих преобразованиях инструкций, вы можете перейти по адресу https://source.android.com/devices/tech/dalvik/instruction-formats для просмотра ------ Direct methods - #0 : (in LHello;) name: '' --- Имя метода: Это, очевидно, метод построения --- type: '() V' --- прототип метода, () означает входной параметр, () означает возвращаемое значение, V означает void --- access: 0x10001 (PUBLIC CONSTRUCTOR) --- Тип доступа к методу --- code - registers: 2 --- количество регистров, используемых методом --- ins: 1 --- Метод вводит параметры. В дополнение к определенным нами параметрам система также по умолчанию принимает специальный параметр --- outs : 1 размер insns: 8 16-битных кодовых единиц --- размер инструкции --- 000148: |[000148] Hello.:()V 000158: 7010 0400 0100 |0000: invoke-direct , Ljava/lang/Object;.:()V // [email protected] 00015e: 1a00 0b00 |0003: const-string v0, "hello! youzan" // [email protected] 000162: 5b10 0000 |0005: iput-object v0, v1, LHello;.helloString:Ljava/lang/String; // [email protected] 000166: 0e00 |0007: return-void catches : (none) positions : 0x0000 line=1 0x0003 line=2 locals : 0x0000 - 0x0008 reg=1 this LHello; #1 : (in LHello;) name : 'main' type : '([Ljava/lang/String;)V' access : 0x0009 (PUBLIC STATIC) code - registers : 3 ins : 1 outs : 2 insns size : 11 16-bit code units 000168: |[000168] Hello.main:([Ljava/lang/String;)V 000178: 2200 0000 |0000: new-instance v0, LHello; // [email protected] 00017c: 7010 0000 0000 |0002: invoke-direct , LHello;.:()V // [email protected] 000182: 5401 0000 |0005: iget-object v1, v0, LHello;.helloString:Ljava/lang/String; // [email protected] 000186: 6e20 0100 1000 |0007: invoke-virtual , LHello;.fun:(Ljava/lang/String;)V // [email protected] 00018c: 0e00 |000a: return-void catches : (none) positions : 0x0000 line=5 0x0005 line=6 0x000a line=7 locals : Virtual methods - #0 : (in LHello;) name : 'fun' type : '(Ljava/lang/String;)V' access : 0x0001 (PUBLIC) code - registers : 3 ins : 2 outs : 2 insns size : 6 16-bit code units 000190: |[000190] Hello.fun:(Ljava/lang/String;)V 0001a0: 6200 0100 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // [email protected] 0001a4: 6e20 0300 2000 |0002: invoke-virtual , Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // [email protected] 0001aa: 0e00 |0005: return-void catches : (none) positions : 0x0000 line=10 0x0005 line=11 locals : 0x0000 - 0x0006 reg=1 this LHello; source_file_idx : 1 (Hello.java)
До сих пор код Java был преобразован в исполняемый файл Dalvik, а именно dex.
Конкретный формат файла Dex
Теперь давайте проанализируем конкретный формат файлов Dex. Как и файлы MP3, MP4, JPG и PNG, файлы Dex также имеют свои собственные форматы. Только при соблюдении этих форматов они могут быть правильно распознаны средой выполнения Android.
Общая схема файла Dex показана ниже:
Данные в этих областях взаимосвязаны и относятся друг к другу. Из-за нехватки места здесь показаны только ассоциации некоторых областей, перейдите на официальный веб-сайт, чтобы самостоятельно проверить соответствующие данные. Поля на рисунке ниже подробно описаны в подробном описании каждой области.
Заголовок файла, область индекса и область определения класса будут кратко представлены ниже. Вы можете перейти на официальный сайт Android, чтобы узнать о других областях.
Заголовок файла
Область заголовка файла определяет, как читать этот файл. Конкретный формат выглядит следующим образом (порядок в файле — это порядок в таблице ниже):
область идентификатора
Область идентификатора хранит смещение реальных данных строки, типа, прототипа, поля и ресурсов метода в файле. Мы можем найти фактические данные, соответствующие идентификатору, в соответствии со смещением области идентификатора.
Область идентификатора строки
Этот блок представляет собой список смещений, каждое смещение соответствует реальному строковому ресурсу, и каждое смещение занимает 32 бита. Мы можем найти соответствующие фактические строковые данные по смещению. Конкретный формат выглядит следующим образом:
Конечная позиция этого смещения должна находиться в области данных. После нахождения позиции этого смещения конкретные данные этого строкового ресурса могут быть считаны в следующем формате:
Введите идентификатор области
Этот блок является списком индексов, и значение индекса соответствует элементу в списке смещений области идентификатора строки. Формат данных следующий:
Если мы хотим найти значение определенного типа, нам нужно найти соответствующий элемент в списке идентификаторов строк в соответствии со значением индекса в списке идентификаторов типов.Строк строки, соответствующий смещению, хранимому в этом элементе, является строкой этого типа. описание.
Область идентификатора прототипа метода
Этот блок представляет собой список идентификатора прототипа метода, формат данных:
Область идентификатора участника
В этом блоке хранится список идентификаторов прототипов, формат данных:
Область идентификатора метода
Этот блок хранит список идентификаторов методов, формат данных: Этот блок хранит список идентификаторов прототипов, формат данных:
Область определения класса
В этой области хранится список определений классов. Конкретная структура данных выглядит следующим образом:
Инструменты для разбора файлов dex
Вот инструмент 010 Editor, который может анализировать файлы dex. Это позволяет нам лучше понять формат файла dex с помощью предустановленных шаблонов.
Применение файла Dex в Android Tinker для горячего ремонта
В текущем распространенном решении горячего ремонта Android Tinker обладает преимуществами бесплатного, открытого исходного кода и большого числа пользователей, поэтому Youzan также создает сервис горячего ремонта Android на основе Tinker. Основным принципом оперативного исправления Tinker является создание пакета исправлений путем сравнения файла dex старого APK с файлом dex нового APK, а затем синтезирование нового файла dex в приложении через пакет исправлений и файл dex старого APK. Процесс показан на рисунке ниже:
Примечание: изображение с официального сайта Тинкера
Генерация патчей
Тинкер официально использует собственную схему синтеза DexDiff. Он основан на характеристиках формата файла Dex и имеет преимущества небольших пакетов исправлений и низкого потребления памяти. В алгоритме DexDiff файл Dex делится на блоки разных типов в соответствии с форматом файла Dex, как показано на следующем рисунке:
Эти блоки имеют унифицированную структуру данных, и основные данные включают в себя фактический тип данных, соответствующий блоку и смещению в файле. Как показано ниже:
При фактическом типе данных и смещении в данных блока фактические данные, содержащиеся в этом блоке, могут быть считаны из файла в соответствии со структурой данных, соответствующей фактическому типу данных. Принимая область заголовка в качестве примера, код чтения выглядит следующим образом (некоторые нерелевантные коды были удалены, и код может ссылаться на описание заголовка файла в формате файла Dex выше):
private void readHeader(Dex.Section headerIn) throws UnsupportedEncodingException
После прочтения конкретных данных каждого блока старых и новых файлов Dex из файла, вы можете сравнить и сгенерировать пакет исправлений. Поскольку структура данных каждого блока является непоследовательной, каждый блок имеет соответствующий алгоритм сравнения для обработки генерации патчей и синтеза каждого блока. Список алгоритмов показан на рисунке:
Эти алгоритмы сравнивают разницу между новым и старым файлами Dex после их преобразования в структуры данных, а затем генерируют соответствующие инструкции по эксплуатации, сохраняют их в файле исправлений и отправляют их клиенту.
Синтез патчей
После того, как клиент получит файл исправления, он будет использовать тот же метод чтения, чтобы преобразовать старый файл Dex в соответствующую структуру данных, а затем использовать инструкции по эксплуатации в пакете исправлений, чтобы изменить старые данные Dex, чтобы сгенерировать новые данные Dex и, наконец, данные Напишите файл для генерации нового файла Dex, завершив тем самым синтез патча.
Напиши в конце
Эта статья не содержит ничего особенно подробного и не описывает полностью формат файла dex. Главным образом, чтобы поделиться с вами общей структурой файла dex, а также некоторыми практическими приложениями. Когда в будущем вы столкнетесь с подобными проблемами, у вас могут быть некоторые указания, чтобы понять файл dex и решить проблему. Наконец, если у вас есть какие-либо предложения или комментарии, обратная связь приветствуется.