Всем привет, мы снова встретились, я ваш друг. Автор оригинала: [color=blue][b]lpohvbe[/b][/color] Исходный адрес: [url]http://blog.csdn.net/lpohvbe/article/details/7981386[/url] Я давно не сталкивался с этим, если у меня есть какие-либо ошибки, пожалуйста, просветите меня. Я просто надеюсь поделиться тем, что я изучил за этот период. Для меня было бы большой честью, если бы это могло помочь нуждающимся детям. . Комментарии приветствуются при перепечатке, но, пожалуйста, укажите источник перепечатки. Спасибо! Пожалуйста, уважайте плоды труда разработчиков! Пожалуйста, не используйте его в незаконных целях!
Автор: lpohvbe | http://blog.csdn.net/lpohvbe/article/details/7981386
Эта часть включает в себя много контента. Я постараюсь начать с самого простого, но она требует от читателей наличия определенных знаний в разработке Android. Но учтите, что объяснение может быть до тошноты подробным, поэтому рассмотрите его исходя из вашего личного понимания.
APK, байт-код Dalvik и файлы smali
АПК-файл
Каждый должен знать, что файл APK на самом деле представляет собой сжатый пакет с MIME-файлом ZIP. Когда мы изменяем суффикс ZIP, мы можем увидеть внутреннюю структуру файла. Например, после изменения суффикса и открытия APK-файла Crocodile Naughty с помощью RAR. можно увидеть (загрузить полную версию из Google Play):
Where’s My Water.zip\ •asset\ <Каталог ресурсов1:assetиres都是Каталог ресурсов但有所区别,См. инструкции ниже> •lib\ <soместоположение инвентаря,Обычно составляется NDK,Обычно используется с игровыми движками или JNI. nativeВ названном проекте> •|—armeabi\ |—<soФайлы библиотеки разделены на разныеCPUАрхитектура> •|—armeabi-v7a\ •META-INF\ <Сохраните некоторые файлы свойств проекта.,НапримерManifest.MF> •res\ <Каталог ресурсов2:assetиres都是Каталог ресурсов但有所区别,См. инструкции ниже> •|—drawable\ |—<图片иверно应的xmlресурс> •|—layout\ |—<Определите макетxmlресурс> •|—… •AndroidManifest.xml <AndroidБазовый файл свойств конфигурации проекта> •classes.dex <JavaПолучено путем компиляции кодаDalvik VMФайлы, которые можно запускать напрямую,Ниже есть введение> •resources.arsc <верноres目录下的ресурс的一个索引文件,Сохранено в исходном проекте.strings.xmlДождитесь содержимого файла>
Несущественное примечание. Разница между каталогами ресурсов и каталогами ресурсов:
1. Файлы ресурсов в каталоге res автоматически генерируют индексный файл (R.java) во время компиляции и используют R.xxx.yyy для ссылки на него в коде Java, тогда как файлы ресурсов в каталоге ресурсов не нуждаются в этом; создать индекс. В коде Java для доступа необходимо использовать AssetManager;
2. Вообще говоря, помимо аудио- и видеоресурсов (которые необходимо поместить в разделы raw или assets), файлы ресурсов, используемые проектами Android, разработанными с использованием Java, будут помещены в разделы res; и т. д.) Файлы ресурсов необходимо разместить под активом.
Поскольку «Где моя вода» разработана с использованием собственного игрового движка DMO компании Disney, все файлы ресурсов, используемые в игре, хранятся в разделе «Asset», за исключением значков приложений, эти ресурсы все равно необходимо поместить в раздел «Res.
Байт-код Далвика
Dalvik — это виртуальная машина, специально разработанная Google для операционной системы Android и глубоко оптимизированная. Хотя программы на Android разрабатываются с использованием Java, Dalvik и стандартная виртуальная машина Java JVM — это все же две разные вещи. Dalvik VM основан на регистрах, тогда как JVM основан на стеке; Dalvik имеет собственный формат выполнения файлов dex (исполняемый файл dalvik), а JVM выполняет байт-код Java; Dalvik VM быстрее JVM и занимает меньше места.
Мы не можем напрямую увидеть исходный логический код через байт-код Dalvik. В этом случае нам нужно использовать такие инструменты, как Apktool или dex2jar+jd-gui, чтобы просмотреть его. Однако обратите внимание, что в конечном итоге для изменения APK нам понадобится файл .smali, а не экспортированный файл Java для перекомпиляции (а это в принципе невозможно).
smali-файл
Хорошо, теперь, когда у вас есть определенное представление о Dalvik, давайте представим ключевые моменты: smali и его синтаксис.
Проще говоря, smali — это основной код, исполняемый внутри Dalvik VM. Он имеет свой собственный набор синтаксиса, который будет представлен ниже. Если у вас есть опыт разработки JNI, вы сможете быстро его понять.
1. Типы данных smali
В smali типы данных такие же, как и в Android, но соответствующие символы меняются: •B — байт •C — символ •D — двойной •F — плавающий •Я — целое •J — длинный •S — короткий •V — пустота •Z — логическое значение •[XXX — массив •Lxxx/yyy — объект
Давайте проанализируем здесь два последних элемента. Массив представлен добавлением квадратных скобок «[» перед основным типом. Например, массив int и массив float представлены как: [I, [F] соответственно; объект начинается с L., формат LpackageName/objectName (обратите внимание, что в конце должна быть точка с запятой), например, объект String в smali: Ljava/lang/String;, где java/lang соответствует значению java.lang, и в этом пакете определена строка как объект в формате .
Может быть, кто-то спросил,Поскольку класс представлен LpackageName/objectName;,А как насчет внутренних классов в этом классе?smaliЦитируется в?Ответ:LpackageName/objectNameимя субобъекта;. То есть добавьте "" перед внутренним классом.”символ,о"
2. Определение функции
Определение функции обычно следующее:
Func-Name (Para-Type1Para-Type2Para-Type3…)Return-Type
Обратите внимание, что между параметрами нет разделителей. Это легко понять на нескольких примерах:
1. foo ()V
Да, это void foo().
2. foo (III)Z
Это логическое значение foo(int, int, int).
3. foo (Z[I[ILjava/lang/String;J)Ljava/lang/String;
Вы видите, что это String foo (boolean, int[], int[], String, long)?
3. Подробное знакомство с содержимым smali-файлов.
Давайте приступим к дальнейшему анализу конкретных примеров в smali. Давайте возьмем WMWActivity.smali в Crocodile Naughty и проанализируем его (как его получить, см. Часть 2 декомпиляции APK в следующем разделе: Знакомство с инструментом. А пока мы представим его). синтаксис smali). Его содержание выглядит примерно так:
01..class public Lcom/disney/WMW/WMWActivity;02..super Lcom/disney/common/BaseActivity;03..source “WMWActivity.java”04.05.# interfaces06..implements Lcom/burstly/lib/ui/IBurstlyAdListener;07.08.# annotations09..annotation system Ldalvik/annotation/MemberClasses;10. value = {11. Lcom/disney/WMW/WMWActivityMessageHandler;,12. Lcom/disney/WMW/WMWActivityFinishActivityArgs;13. }14..end annotation15.16.17.# static fields18..field private static final PREFS_INSTALLATION_ID:Ljava/lang/String; = “installationId”19.//…20.21.22.# instance fields23..field private _activityPackageName:Ljava/lang/String;24.//…25.26.27.# direct methods28..method static constructor <clinit>()V29. .locals 330.31. .prologue32. //…33.34. return-void35..end method36.37..method public constructor <init>()V38. .locals 339.40. .prologue41. //…42.43. return-void44..end method45.46..method static synthetic access100(Lcom/disney/WMW/WMWActivity;)V47. .locals 048. .parameter “x0”49.50. .prologue51. .line 3752. invoke-direct {p0}, Lcom/disney/WMW/WMWActivity;->initIap()V53.54. return-void55..end method56.57..method static synthetic access
Это нормально, если вы запутались. Теперь я проанализирую их один за другим. Понимание значения этих символов облегчит вам внедрение кода в дальнейшем.
1. Наследование, интерфейс и информация о пакете в smali.
Сначала посмотрите на первые несколько строк:
1] .class public Lcom/disney/WMW/WMWActivity;
2] .super Lcom/disney/common/BaseActivity;
3] .source “WMWActivity.java”
4]
5] # interfaces
6] .implements Lcom/burstly/lib/ui/IBurstlyAdListener;
7]
8] # annotations
9] .annotation system Ldalvik/annotation/MemberClasses;
10] value = {
11] Lcom/disney/WMW/WMWActivity$MessageHandler;,
12] Lcom/disney/WMW/WMWActivity$FinishActivityArgs;
13] }
14] .end annotation
Строки 1–3 определяют основную информацию: это файл smali, скомпилированный WMWActivity.java (строка 3), который является классом пакета com.disney.WMW (строка 1), унаследованным от com.disney.common.BaseActivity ( строка 2).
Строки 5–6 определяют информацию об интерфейсе: этот WMWActivity реализует интерфейс IBurstyAdListener в пакете com.burstly.lib.ui (рекламный SDK).
Строки 8–14 определяют внутренний класс: он имеет два внутренних класса-члена — MessageHandler и FinishActivityArgs. Внутренний класс будет упомянут в следующих разделах.
Проанализировав информацию в начале файла smali, мы уже можем сконструировать в своем мозгу Java-файл, который выглядит следующим образом:
[java] view plaincopy 01.class WMWActivity extends BaseActivity implements IBurstlyAdListener{ 02. //… 03. class MessageHandler { 04. //… 05. } 06. class FinishActivityArgs{ 07. //… 08. } 09.}
Да, это общая структура WMWActivity.java, переменные-члены и информация о функциях? Не волнуйтесь, я собираюсь проанализировать это ниже.
Прежде чем продолжить анализ, необходимо кое-что объяснить. Как упоминалось ранее, одно из самых больших различий между Dalvik VM и JVM заключается в том, что Dalvik VM основана на регистрах. Что значит на основе регистра? Другими словами, все операции в smali должны выполняться через регистры: локальные регистры представлены символами, начинающимися с v и заканчивающимися цифрами, например v0, v1, v2,... регистры параметров представлены символами, начинающимися с p и заканчивающимися с числами, такими как p0, p1, p2,... Важно отметить, что p0 не обязательно является функцией. Первый параметр числа. В нестатических функциях p0 относится к «этому», p1 представляет первый параметр функции, p2 представляет второй параметр функции... а в статической функции p0 соответствует параметры первого параметра (поскольку в статическом методе Java нет этого метода). Ограничений на локальные регистры нет. Теоретически их можно использовать произвольно. Ниже приведен пример.
[plain] view plaincopy 01.const/4 v0, 0x0 02.iput-boolean v0, p0, Lcom/disney/WMW/WMWActivity;->isRunning:Z
В двух приведенных выше предложениях используется локальный регистр v0, а значение 0x0 сохраняется в v0. Затем во втором предложении используется инструкция iput-boolean для сохранения значения в v0 в элементе com.disney.WMW.WMWActivity.isRunning. в переменных. Это эквивалентно: this.isRunning = false; (Как упоминалось выше, p0 представляет «это» в нестатических функциях, здесь это экземпляр com.disney.WMW.WMWActivity). Конкретные инструкции и значение этих двух предложений пока можно игнорировать. Просто сначала поймите механизм Dalvik VM. На самом деле синтаксис очень похож на язык ассемблера. Конкретные инструкции будут представлены одна за другой.
2. Переменные-члены в smali
Далее мы продолжаем знакомить вас с переменными-членами:
1 ] # static fields
2 ] .field private static final PREFS_INSTALLATION_ID:Ljava/lang/String; = “installationId”
3 ] //…
4 ]
5 ]
6 ] # instance fields
7 ] .field private _activityPackageName:Ljava/lang/String;
8 ] //…
статический, определенный выше поля и экземпляры поля — все переменные-члены, формат: .field public/private [static] [final] varName:<тип>。Однакоstatic поля и экземпляры Разница между полями все же есть, конечно разница очевидна, то есть статическая Поля статичны, а экземпляры — нет. Существуют также разные инструкции для получения этих разных переменных-членов в соответствии с этой разницей. Вообще говоря, полученные инструкции: iget, sget, iget-boolean, sget-boolean, iget-object, sget-object и т. д. Операционные инструкции: iput, sput, iput-boolean, sput-boolean, iput-object. , спут-объект и т.д. Без суффикса «-object» это означает, что объект переменной-члена операции является базовым типом данных. С «-object» это означает, что переменная-член операции является типом объекта. В частности, логический тип. управляется инструкцией с "-boolean".
(1) Команда для получения статических полей аналогична:
[plain] view plaincopy 01.sget-object v0, Lcom/disney/WMW/WMWActivity;->PREFS_INSTALLATION_ID:Ljava/lang/String;
sget-object используется для получения значения переменной и сохранения его в регистр следующего параметра,здесь,Получите переменную-член String PREFS_INSTALLATION_ID, которая отображается выше, и поместите ее в регистр v0.,Примечание. Требуется указать тип класса, к которому принадлежит переменная.,После этого вам нужно добавить двоеточие и тип переменной-члена.,Середина“->”Экспресс-принадлежность。
(2) Инструкции по получению полей экземпляра в основном такие же, как и для статических полей, за исключением того, что, поскольку это не статическая переменная, вы не можете просто указать тип класса, в котором находится переменная, но также нужен экземпляр класса, в котором находится переменная. Посмотрите на пример:
[plain] view plaincopy 01.iget-object v0, p0, Lcom/disney/WMW/WMWActivity;->_view:Lcom/disney/common/WMWView;
Вы можете видеть, что команда iget-object имеет на один параметр больше, чем sget-object, который является экземпляром класса, в котором находится переменная. Здесь это p0, что означает «this».
(3) Для получения массива существуют aget и aget-object. Инструкции аналогичны приведенным выше и не будут подробно описываться.
(4) Использование инструкции put согласуется с инструкцией get. Просто посмотрите на пример без объяснений:
[plain] view plaincopy 01.const/4 v3, 0x0 02.sput-object v3, Lcom/disney/WMW/WMWActivity;->globalIapHandler:Lcom/disney/config/GlobalPurchaseHandler;
Эквивалентно: this.globalIapHandler = null (null = 0x0);
[plain] view plaincopy 01..local v0, wait:Landroid/os/Message; 02.const/4 v1, 0x2 03.iput v1, v0, Landroid/os/Message;->what:I
Эквивалентно: wait.what = 0x2 (wait — это экземпляр сообщения)
3. Вызовы функций в smali
Функции и переменные-члены в smali также делятся на два типа, но разница между статическими и экземплярами в разных переменных-членах прямая и виртуальная. Так в чем же разница между прямым методом и виртуальным методом? Грубо говоря, прямой метод — это приватная функция, а остальные публичные и защищенные функции — виртуальные методы. Таким образом, при вызове функции существует несколько различных инструкций, таких как вызов-прямой, вызов-виртуальный, а также вызов-статический, вызов-супер и вызов-интерфейса. Конечно, на самом деле существует команда ignore-XXX/range, которая вызывается, когда имеется более 4 параметров. Это относительно редко, так что просто поймите это.
(1) Invoke-static: как следует из названия, он вызывает статическую функцию. Поскольку это статическая функция, у нее на один параметр меньше, чем у других вызовов, например:
[plain] view plaincopy 01.invoke-static {}, Lcom/disney/WMW/UnlockHelper;->unlockCrankypack()Z
Обратите внимание, что здесь имеется пара фигурных скобок «{}» после вызова-static, которая на самом деле представляет собой список экземпляров и параметров для вызова метода. Поскольку этот метод не требует параметров и является статическим, элемент {} пуст. посмотрите другой пример:
[plain] view plaincopy 01.const-string v0, “fmodex” 02.invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V<span style=”font-family: Verdana, sans-serif;”> </span>
Это метод, используемый для вызова static void System.loadLibrary(String) для загрузки библиотеки so, скомпилированной NDK. Также здесь v0 — это параметр «fmodex».
(2) Invoke-super: инструкцию, используемую для вызова метода родительского класса, можно увидеть в onCreate, onDestroy и других методах, которые опущены.
(3), вызов-прямой: вызывает частные функции, например:
[plain] view plaincopy 01.invoke-direct {p0}, Lcom/disney/WMW/WMWActivity;->getGlobalIapHandler()Lcom/disney/config/GlobalPurchaseHandler;
Здесь GlobalPurchaseHandler getGlobalIapHandler() — это частная функция, определенная в WMWActivity. Если вы неправильно используете ignore-virtual или ignore-static при изменении smali, при запуске скомпилированной программы будет возникать общая ошибка VerifyError (дополнительные сводки ошибок см. Ответ APK. Сборник «Дополнительно 3: Обзор распространенных ошибок»).
(4) Invoke-virtual: используется для вызова защищенных или общедоступных функций. Также будьте осторожны, чтобы не использовать вызывающие напрямую или статические функции при изменении smali. Пример:
[plain] view plaincopy 01.sget-object v0, Lcom/disney/WMW/WMWActivity;->shareHandler:Landroid/os/Handler; 02.invoke-virtual {v0, v3}, Landroid/os/Handler;->removeCallbacksAndMessages(Ljava/lang/Object;)V
Думаю, все это поняли. Главное понимать, что v0 — это ShareHandler:Landroid/os/Handler, а v3 — это параметр Ljava/lang/Object, передаваемый в метод removeCallbackAndMessage.
(5), ignore-xxxxxx/range: если метод имеет более 5 параметров (включая 5), приведенные выше инструкции нельзя использовать напрямую, но после них добавляется «/range», и метод использования также отличается:
[plain] view plaincopy 01.invoke-static/range {v0 .. v5}, Lcn/game189/sms/SMS;->checkFee(Ljava/lang/String;Landroid/app/Activity;Lcn/game189/sms/SMSListener;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z
Это платежный интерфейс в телекоммуникационном SDK, и ему необходимо передать 6 параметров. На данный момент параметры в фигурных скобках должны быть опущены и должны быть непрерывными (не проверено, нужно ли начинать с версии 0). ).
Кто-то, возможно, заметил, что только что рассмотренные примеры посвящены операции «вызова функции», и, похоже, здесь нет операции получения результата, возвращаемого функцией?
В коде Java вызов функции и возврат результата функции выполняются в одном операторе, но в smali их необходимо выполнять отдельно. Если после использования приведенных выше инструкций вызываемая функция возвращает непустое значение, то move-result (return). базовые типы данных) и инструкции перемещения-результата (возврата объекта):
[plain] view plaincopy 01.const/4 v2, 0x0 02.invoke-virtual {p0, v2}, Lcom/disney/WMW/WMWActivity;->getPreferences(I)Landroid/content/SharedPreferences; 03.move-result-object v1
Версия v1 сохраняет экземпляр SharedPreferences, возвращаемый вызовом метода getPreferences(int).
[plain] view plaincopy 01.invoke-virtual {v2}, Ljava/lang/String;->length()I 02.move-result v2
v2 сохраняет целочисленный тип, возвращаемый вызовом String.length().
4. Анализ функциональных объектов в smali
Начнем с введения функциональных сущностей. На самом деле, в этом нет ничего особенного. Есть только одна вещь, требующая особого внимания при внедрении кода. Вот пример:
[plain] view plaincopy 01..method protected onDestroy()V 02. .locals 0 03. 04. .prologue 05. .line 277 06. invoke-super {p0}, Lcom/disney/common/BaseActivity;->onDestroy()V 07. 08. .line 279 09. return-void 10..end method
Это функция onDestroy(), и ее роль всем известна. Сначала посмотрите первое предложение функции: .local 0. Это предложение очень важно. Оно указывает минимальное количество локальных регистров, которые вам необходимо использовать в этой функции. Здесь, поскольку необходимо вызвать процесс onDestroy() только одного родительского класса, необходимо использовать только p0, поэтому количество используемых локальных регистров равно 0. Если вы не понимаете это правило, легко забыть изменить значение .local после внедрения кода. Тогда при запуске после компиляции вы получите ошибку VerifyError, и обнаружить проблему будет чрезвычайно сложно. Эта проблема меня беспокоила много раз. Наконец, я обнаружил, что значение .local имеет такой шаблон, поэтому я проверил документацию и обнаружил, что это именно эта проблема. Например, если я добавлю в onDestroy() предложение: this.existed = true; тогда оно должно быть изменено на (обратите внимание, что значение .local изменяется на 1 — используется локальный регистр v0):
[plain] view plaincopy 01..method protected onDestroy()V 02. .locals 1 03. 04. .prologue 05. .line 277 06. const/4 v0, 0x1 07. 08. iput-boolean v0, p0, Lcom/disney/WMW/WMWActivity;->exited:Z 09. 10. invoke-super {p0}, Lcom/disney/common/BaseActivity;->onDestroy()V 11. 12. .line 279 13. return-void 14..end method
Кроме того, обратите внимание на метку .line, которая отмечает количество строк кода в исходном файле Java. Это также очень полезно, если вы используете eclipse для разработки и столкнулись с ошибкой и сбоем. catLog какой файл и какая строка вылетела? Dalvik VM сохранит это значение при запуске в .line XX. Если при выполнении этой строки возникнет ошибка, она выведет это значение в catLog, чтобы мы могли увидеть, в какой строке возникла проблема. Инструмент jd-gui также анализирует эту информацию, чтобы восстановить код smali в код Java, который мы хотим видеть. Конечно, это не обязательно, и не беда, если вы его удалите, но лучше сохранить для удобства отладки.
Заявление об авторских правах: Содержание этой статьи добровольно предоставлено пользователями Интернета, а мнения, выраженные в этой статье, представляют собой только точку зрения автора. Данный сайт лишь предоставляет услуги по хранению информации, не имеет никаких прав собственности и не несет соответствующей юридической ответственности. Если вы обнаружите на этом сайте какое-либо подозрительное нарушение авторских прав/незаконный контент, отправьте электронное письмо, чтобы сообщить. После проверки этот сайт будет немедленно удален.
Издатель: Лидер стека программистов полного стека, укажите источник для перепечатки: https://javaforall.cn/193516.html Исходная ссылка: https://javaforall.cn