dexlib2 - Инструментарий покрытия ветвей

Я пытаюсь использовать smali-код с помощью dexlib2, чтобы измерить охват ветвей. В частности, я вставляю в каждую ветвь (if и соответствующую метку) в основном две инструкции; const-string для загрузки уникальной трассировки для каждой ветки и invoke-static для вызова статического метода. Однако есть пара вопросов:

Во-первых, мне пришлось увеличить количество регистров на единицу. Это привело к перераспределению регистров некоторых инструкций, что, кажется, работает (используется отражение для увеличения номера регистра, например, изначально p0 получил v20, введя новый локальный регистр v21). Однако я заметил, что некоторые ярлыки, например. .end local v15 также потребует такого рода модификации, что кажется невозможным с dexlib2, поскольку метки не отслеживают ни такую ​​информацию, ни имя. Я также не знаю, в чем смысл/намерение этих локальных меток .end/.restart./start. Интересны ли эти метки для сборки мусора или это какая-то информация о типе для соответствующих регистров?

Во-вторых, некоторые инструкции принимают в качестве аргументов только v0-v15. Вот почему мне пришлось различать, превышает ли количество локальных регистров 16 или нет. В этом случае я в основном использую две дополнительные инструкции перемещения: (в другом случае инструментарий намного проще)

move-object/16 vNew, v0 # сохранить значение v0
(две вышеупомянутые инструкции) # использовать v0 для сохранения трассировки
move-object/16 v0, vNew # восстановить значение v0

Однако недавно я получаю следующую ошибку (и аналогичные ошибки проверки):

[0x25C] «этот» аргумент «Ссылка: java.lang.Object», а не экземпляр «Ссылка: com.android.calendar.GeneralPreferences»

Я заметил, что есть разница между использованием move и move-object, но я не знаю о конкретной разнице. Я бы предположил, что константы не являются объектами, а остальные представляют собой объекты. Если это различие необходимо, мне пришлось бы выполнять некоторый анализ последнего типа v0 в каждой ветви, что еще больше усложняет ситуацию.

В-третьих, я заметил, что метки, связанные с ветвями, в некоторых редких случаях ведут себя несколько странно. Во всем файле smali есть ветки, которые инструментируются дважды. Отладка показывает, что запрос инструкции if для ее целевых меток (другая ветвь) один раз возвращает больше меток, чем в другой раз. Вот почему теперь я использую индекс целевой метки (instruction.getTarget().getLocation().getIndex()), но все равно получаю одну ветвь, которая обрабатывается дважды.

Я прошу любой помощи по этому конкретному вопросу, а также общих советов/фактов, которые я должен учитывать. Есть ли лучший способ получить более подробную информацию об ошибках; вывод logcat не самый лучший, например. какая конкретная инструкция вызвала ошибку проверки (шестнадцатеричное значение, рассматриваемое как offest, для меня не имеет никакого смысла).

Заранее спасибо.


person auermich    schedule 15.12.2018    source источник
comment
запрос инструкции if для ее целевых меток (другая ветвь) один раз возвращает больше меток, чем в другой раз. - Можете ли вы предоставить образец кода или что-то в этом роде? Я не очень понимаю, что вы имеете в виду.   -  person JesusFreke    schedule 15.12.2018
comment
Я в основном поддерживал метки, соответствующие ветке if, в наборе набора меток и использовал только те ветки, где набор меток не включен.   -  person auermich    schedule 16.12.2018
comment
Да, мне все еще недостаточно подробностей, чтобы иметь хоть какое-то представление о последней части :)   -  person JesusFreke    schedule 16.12.2018
comment
Извините, непреднамеренный конец комментария. :(Я в основном сохранял метки, соответствующие ветке if, в наборе набора меток (Set‹Set‹Label››) и инструментировал только те ветки, где набор меток не входит в набор. Однако, по перебирая в цикле инструкции конкретного метода и запрашивая соответствующие метки для if-инструкций, я заметил, что возвращаемый набор меток отличается количеством меток (на один раз больше/меньше) для двух if-инструкций, имеющих общую целевую метку (та же ветка).   -  person auermich    schedule 16.12.2018
comment
В настоящее время класс Label не переопределяет hashCode и не обеспечивает никакой семантики равенства значений, поэтому вы, вероятно, добавляете в набор повторяющиеся метки. т. е. 2 разных экземпляра для одного и того же ярлыка/местоположения. Не стесняйтесь отправлять запрос на вытягивание, чтобы добавить семантику равенства значений в этот класс :)   -  person JesusFreke    schedule 16.12.2018
comment
Да, именно поэтому я предоставил класс-оболочку для этих меток, который в первую очередь проверяет индекс MethodLocation и дополнительно codeAddress, если индекс одинаков для двух меток, т.е. возвращает this.index == o.index || this.codeAddress == o.codeAddress; Я предполагаю, что вторая проверка вызывает проблему, поскольку индекс тот же, но с помощью инструментария codeAddress потенциально может измениться.   -  person auermich    schedule 16.12.2018


Ответы (1)


Лучший способ, который я видел, чтобы справиться с проблемами, которые вы заметили при увеличении количества регистров, - это добавить пролог, который перемещает все регистры параметров после увеличения обратно в их местоположения до увеличения, а затем использовать последний регистр / регистры в качестве нового. регистры «с нуля».

например если бы параметры были равны v14-v20, и вы увеличили количество регистров на единицу, вы бы добавили код, который перемещал бы v15 обратно в v14, v16 обратно в v15 и т. д. и использовал v21 в качестве нового временного регистра.

В качестве альтернативы вы можете попробовать не выделять никаких регистров. например создайте новый метод и передайте значение из целевого метода, который вы хотите инструментировать. Вы можете использовать invoke-*/range для передачи любого отдельного регистра. Но в вашем случае это не кажется слишком выполнимым, так как вы хотите передать дополнительную строку для идентификации ветки. Теоретически вы можете создать новый метод для каждой ветки, но таким образом вы быстро столкнетесь с ограничениями методов.


.end/.restart./start local предназначены исключительно для отладочной информации. Они сообщают отладчику, какой регистр связан с какой локальной переменной в исходном коде Java. Проще всего было бы их просто раздеть. См. https://source.android.com/devices/tech/dalvik/dex-format#debug-info-item для получения дополнительной информации.


Да, move-object должен использоваться для ссылочных типов, move-wide для примитивов long/double и move для других примитивов. Также не забывайте, что широкие типы занимают 2 регистра. Поэтому, если вам может понадобиться выделить 2 дополнительных рабочих регистра, если вам нужно переместить широкое значение (длинный или двойной примитив)

dexlib2 имеет API для выполнения анализа типов в регистрах, если это необходимо. См. https://github.com/JesusFreke/smali/blob/master/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java

person JesusFreke    schedule 15.12.2018
comment
Моя идея состоит в том, чтобы получить тип v0 (тот, который мне нужен для фактического инструментария) и вставить под каждой инструкцией, модифицирующей v0, дополнительную инструкцию типа const vNew (рабочий регистр), где тип — это либо 0,1, либо 2, обозначающие не- объект, объект или широкий. Затем я просто оборачиваю свой инструментальный код оператором if, который использует перемещение в ширину, перемещение или перемещение объекта в зависимости от типа. Я предполагаю, что это позволяет избежать сложности статического определения типа v0 в каждой ветке. Можете ли вы предложить дешевый способ определения инструкций, которые изменяют v0 в данном методе? - person auermich; 17.12.2018
comment
Это не так просто, к сожалению. Рассмотрим, например, если есть 2 ветки, которые переходят в одно и то же целевое местоположение, но тип v0 в каждой ветке разный. Как только вы доберетесь до цели ветки, если типы несовместимы, то тип регистра будет неопределенным (что также означает, что вы можете безопасно удалить его содержимое). - person JesusFreke; 17.12.2018
comment
Да я вижу. @JesusFreke, предложенный вами анализатор методов выдает исключения UnresolvedClassException для стандартных классов, таких как Exception/StringBuilder, и даже для некоторых примитивных типов, таких как Boolean или Double. Вы хоть представляете, в чем причина этой проблемы? - person auermich; 18.12.2018
comment
Обычно анализатор используется с полным classpath от устройства. Но он все равно должен работать даже без этого, хотя он не сможет предоставить полную информацию о типе, но определение того, является ли регистр нормальным/широким/ссылочным, должно быть в порядке. Трассировка стека была бы полезна, чтобы попытаться выяснить, что происходит. А это звучит как отдельный вопрос :) - person JesusFreke; 19.12.2018