fix switch and array alignement
This commit is contained in:
parent
cd6c638080
commit
a0ecb1a18d
5 changed files with 165 additions and 51 deletions
6
TODO.md
6
TODO.md
|
|
@ -1,8 +1,4 @@
|
|||
- sanity checks
|
||||
- tests
|
||||
- https://source.android.com/docs/core/runtime/dex-format#system-annotation
|
||||
-
|
||||
- Caused by: java.lang.VerifyError: Verifier rejected class androidx.appcompat.app.AppCompatViewInflater: android.view.View androidx.appcompat.app.AppCompatViewInflater.createView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet, boolean, boolean, boolean, boolean): [0xFFFFFFFF] unaligned switch table: at 32, switch offset 319 (declaration of 'androidx.appcompat.app.AppCompatViewInflater' appears in /data/app/~~P3In-lzdBi-g5oUouTDKsA==/com.example.testapplication-rXxXwHN5Yo2wxHAcnkq7iw==/base.apk
|
||||
|
||||
|
||||
|
||||
- goto size computation
|
||||
|
|
|
|||
|
|
@ -721,22 +721,6 @@ impl Apk {
|
|||
let code = if code_off == 0 {
|
||||
None
|
||||
} else {
|
||||
if descriptor
|
||||
== IdMethod::from_smali(
|
||||
"Landroidx/appcompat/app/AppCompatViewInflater;\
|
||||
->createView(Landroid/view/View;Ljava/lang/String;\
|
||||
Landroid/content/Context;Landroid/util/AttributeSet;\
|
||||
ZZZZ)Landroid/view/View;",
|
||||
)
|
||||
.unwrap()
|
||||
{
|
||||
let code_item = dex.get_struct_at_offset::<CodeItem>(code_off)?;
|
||||
let mut off = 0;
|
||||
for ins in &code_item.insns {
|
||||
println!("INS 0x{off:x}: {ins:x?}");
|
||||
off += ins.size();
|
||||
}
|
||||
}
|
||||
Some(Self::get_code_from_off(code_off, dex).with_context(|| {
|
||||
format!("Failed to parse code of method {}", descriptor.__str__())
|
||||
})?)
|
||||
|
|
|
|||
|
|
@ -466,6 +466,9 @@ impl DexWriter {
|
|||
let mut payloads = vec![];
|
||||
let mut goto_idx = 0;
|
||||
let mut payload_addr = addr;
|
||||
if payload_addr % 2 != 0 {
|
||||
payload_addr += 1; // For switch and array table alignment
|
||||
}
|
||||
addr = 0;
|
||||
for ins in &code.insns {
|
||||
match ins {
|
||||
|
|
@ -544,6 +547,14 @@ impl DexWriter {
|
|||
elt_width: ins.elt_width,
|
||||
data: ins.data.clone(),
|
||||
};
|
||||
if payload_addr % 2 != 0 {
|
||||
// https://cs.android.com/android/platform/superproject/main/+/main:art/runtime/verifier/method_verifier.cc;drc=e8c3e7be783937a340cd4f3280b69962d6f1ea0c;l=1347
|
||||
// The ART check if the array data table is 4 bytes aligned (= 2 ins alligned)
|
||||
// TODO: check how it is donne in android and other dex generation code.
|
||||
let nop = Instruction::Nop(Nop).get_raw_ins()?;
|
||||
payload_addr += nop.size() / 2;
|
||||
payloads.push(nop);
|
||||
}
|
||||
let data_offset = payload_addr as i32 - addr as i32;
|
||||
payload_addr += payload.size() / 2;
|
||||
payloads.push(payload);
|
||||
|
|
@ -578,7 +589,7 @@ impl DexWriter {
|
|||
key_targets.sort_by_key(|(key, _)| *key);
|
||||
let payload = if ins.is_packed() {
|
||||
let (first_key, _) = *key_targets.first().ok_or(anyhow!(
|
||||
"Found empty swithc in code of {}",
|
||||
"Found empty swith in code of {}",
|
||||
method_id.__repr__()
|
||||
))?;
|
||||
let targets: Vec<_> =
|
||||
|
|
@ -587,6 +598,14 @@ impl DexWriter {
|
|||
} else {
|
||||
InsFormat::FormatSparseSwitchPayload { key_targets }
|
||||
};
|
||||
if payload_addr % 2 != 0 {
|
||||
// https://cs.android.com/android/platform/superproject/main/+/main:art/runtime/verifier/method_verifier.cc;drc=e8c3e7be783937a340cd4f3280b69962d6f1ea0c;l=1464
|
||||
// The ART check if the switch table is 4 bytes aligned (= 2 ins alligned)
|
||||
// TODO: check how it is donne in android and other dex generation code.
|
||||
let nop = Instruction::Nop(Nop).get_raw_ins()?;
|
||||
payload_addr += nop.size() / 2;
|
||||
payloads.push(nop);
|
||||
}
|
||||
let data_offset = payload_addr as i32 - addr as i32;
|
||||
payload_addr += payload.size() / 2;
|
||||
payloads.push(payload);
|
||||
|
|
@ -1302,6 +1321,12 @@ impl DexWriter {
|
|||
}
|
||||
}
|
||||
}
|
||||
if addr % 2 != 0 {
|
||||
// make sure the payload section is 4 bytes aligned
|
||||
let nop = Instruction::Nop(Nop).get_raw_ins()?;
|
||||
//addr += nop.size() / 2;
|
||||
insns.push(nop);
|
||||
}
|
||||
insns.extend(payloads);
|
||||
|
||||
for try_ in &mut tries {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use super::*;
|
||||
use androscalpel_serializer::Instruction as InsFormat;
|
||||
use androscalpel_serializer::*;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
|
|
@ -31,6 +32,65 @@ fn get_hello_world_recompilled() -> &'static [u8] {
|
|||
HELLO_WORLD_RECOMP.get_or_init(|| get_hello_world_apk().gen_raw_dex().unwrap().pop().unwrap())
|
||||
}
|
||||
|
||||
fn get_class_dex<'a, 'b>(name: &'a str, dex: &'b DexFileReader) -> Option<&'b ClassDefItem> {
|
||||
let name = name.into();
|
||||
for class in dex.get_class_defs() {
|
||||
let class_name = dex
|
||||
.get_string(
|
||||
dex.get_type_id(class.class_idx as usize)
|
||||
.unwrap()
|
||||
.descriptor_idx,
|
||||
)
|
||||
.unwrap();
|
||||
if class_name == name {
|
||||
return Some(class);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn get_method_code_dex(name: &str, dex: &DexFileReader) -> Option<CodeItem> {
|
||||
let method = IdMethod::from_smali(name).unwrap();
|
||||
let class_name: String = (&method.class_.0).into();
|
||||
let class = get_class_dex(&class_name, dex);
|
||||
let class = if let Some(class) = class {
|
||||
class
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
if class.class_data_off == 0 {
|
||||
return None;
|
||||
}
|
||||
let data = dex
|
||||
.get_struct_at_offset::<ClassDataItem>(class.class_data_off)
|
||||
.unwrap();
|
||||
let mut idx = 0;
|
||||
for meth in data.direct_methods {
|
||||
idx += meth.method_idx_diff.0;
|
||||
let meth_id = Apk::get_id_method_from_idx(idx as usize, dex).unwrap();
|
||||
println!("{}", meth_id.__str__());
|
||||
if meth_id == method && meth.code_off.0 != 0 {
|
||||
return Some(
|
||||
dex.get_struct_at_offset::<CodeItem>(meth.code_off.0)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
let mut idx = 0;
|
||||
for meth in data.virtual_methods {
|
||||
idx += meth.method_idx_diff.0;
|
||||
let meth_id = Apk::get_id_method_from_idx(idx as usize, dex).unwrap();
|
||||
println!("{}", meth_id.__str__());
|
||||
if meth_id == method && meth.code_off.0 != 0 {
|
||||
return Some(
|
||||
dex.get_struct_at_offset::<CodeItem>(meth.code_off.0)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generated_data_size() {
|
||||
let dex_bin = get_hello_world_recompilled();
|
||||
|
|
@ -91,6 +151,54 @@ fn test_string_order_in_dex() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_switch_table_alignement() {
|
||||
let new_dex = DexFileReader::new(get_hello_world_recompilled()).unwrap();
|
||||
let code = get_method_code_dex(
|
||||
"Landroidx/appcompat/app/AppCompatViewInflater;\
|
||||
->createView(\
|
||||
Landroid/view/View;Ljava/lang/String;\
|
||||
Landroid/content/Context;Landroid/util/AttributeSet;\
|
||||
ZZZZ\
|
||||
)Landroid/view/View;",
|
||||
&new_dex,
|
||||
)
|
||||
.unwrap();
|
||||
let mut addr = 0;
|
||||
for ins in code.insns {
|
||||
match ins {
|
||||
InsFormat::FormatPackedSwitchPayload { .. } => {
|
||||
assert_eq!(addr % 4, 0, "Switch table not aligned")
|
||||
}
|
||||
InsFormat::FormatSparseSwitchPayload { .. } => {
|
||||
assert_eq!(addr % 4, 0, "Switch table not aligned")
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
addr += ins.size();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_array_table_alignement() {
|
||||
let new_dex = DexFileReader::new(get_hello_world_recompilled()).unwrap();
|
||||
let code = get_method_code_dex(
|
||||
"Landroidx/constraintlayout/core/widgets/ConstraintWidget;-><init>()V",
|
||||
&new_dex,
|
||||
)
|
||||
.unwrap();
|
||||
let mut addr = 0;
|
||||
for ins in code.insns {
|
||||
match ins {
|
||||
InsFormat::FormatFillArrayDataPayload { .. } => {
|
||||
assert_eq!(addr % 4, 0, "Array Data table not aligned")
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
addr += ins.size();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_type_list() {
|
||||
let types = IdType::get_list_from_str("IILjavalangObject;LjavalangObject;Z").unwrap();
|
||||
|
|
@ -147,6 +255,7 @@ fn test_parse_method_smali() {
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_field_smali() {
|
||||
let proto = IdField::from_smali(
|
||||
|
|
@ -174,7 +283,7 @@ fn test_sort_fields() {
|
|||
"Lcom/google/android/material/search/SearchBarAnimationHelper$$ExternalSyntheticLambda4;\
|
||||
->f$0:Landroid/animation/Animator;",
|
||||
)
|
||||
.unwrap();list1
|
||||
.unwrap();
|
||||
let mut list1 = vec![f2.clone(), f1.clone()];
|
||||
let mut list2 = vec![f1.clone(), f2.clone()];
|
||||
list1.sort();
|
||||
|
|
|
|||
46
test.py
46
test.py
|
|
@ -59,24 +59,24 @@ print(f"[+] New code of {method_id} ")
|
|||
for i in code.insns:
|
||||
print(f" {i}")
|
||||
|
||||
# Strip class for debugging
|
||||
classes = list(
|
||||
filter(
|
||||
lambda x: x
|
||||
not in [
|
||||
# # Strip class for debugging
|
||||
# classes = list(
|
||||
# filter(
|
||||
# lambda x: x
|
||||
# not in [
|
||||
# IdType("Lcom/example/testapplication/ui/home/HomeViewModel;"),
|
||||
# IdType("Landroidx/navigation/NavDeepLink$Builder;"),
|
||||
# IdType("Landroidx/constraintlayout/core/widgets/ConstraintWidget$1;"),
|
||||
# IdType("Landroidx/appcompat/app/ActionBar;"),
|
||||
# IdType("Landroidx/constraintlayout/core/state/WidgetFrame;"),
|
||||
IdType("Landroidx/appcompat/app/AppCompatViewInflater;"),
|
||||
],
|
||||
apk.classes.keys(),
|
||||
)
|
||||
)
|
||||
for cls in classes:
|
||||
apk.remove_class(cls)
|
||||
|
||||
# IdType("Landroidx/appcompat/app/AppCompatViewInflater;"),
|
||||
# ],
|
||||
# apk.classes.keys(),
|
||||
# )
|
||||
# )
|
||||
# for cls in classes:
|
||||
# apk.remove_class(cls)
|
||||
#
|
||||
print("[+] Recompile")
|
||||
|
||||
dex_raw = apk.gen_raw_dex()
|
||||
|
|
@ -86,16 +86,16 @@ for dex in dex_raw:
|
|||
new_apk.add_dex_file(dex)
|
||||
|
||||
|
||||
# print("[+] Repackage")
|
||||
#
|
||||
# utils.replace_dex(
|
||||
# APK_NAME,
|
||||
# APK_NAME.parent / (APK_NAME.name.removesuffix(".apk") + "-instrumented.apk"),
|
||||
# dex_raw,
|
||||
# Path().parent / "my-release-key.jks",
|
||||
# zipalign=Path.home() / "Android" / "Sdk" / "build-tools" / "34.0.0" / "zipalign",
|
||||
# apksigner=Path.home() / "Android" / "Sdk" / "build-tools" / "34.0.0" / "apksigner",
|
||||
# )
|
||||
print("[+] Repackage")
|
||||
|
||||
utils.replace_dex(
|
||||
APK_NAME,
|
||||
APK_NAME.parent / (APK_NAME.name.removesuffix(".apk") + "-instrumented.apk"),
|
||||
dex_raw,
|
||||
Path().parent / "my-release-key.jks",
|
||||
zipalign=Path.home() / "Android" / "Sdk" / "build-tools" / "34.0.0" / "zipalign",
|
||||
apksigner=Path.home() / "Android" / "Sdk" / "build-tools" / "34.0.0" / "apksigner",
|
||||
)
|
||||
|
||||
last_id = None
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue