frames: JDK hierarchy fallback, array-operand rewrite, aaload fixes; LoVisual 131 to 0 FAIL
This commit is contained in:
parent
1c417f596c
commit
63630dd223
14 changed files with 562 additions and 116 deletions
|
|
@ -9,7 +9,7 @@ mod forms;
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use noxis_analysis::ClassHierarchy;
|
||||
use noxis_analysis::{ClassHierarchy, JdkHierarchy};
|
||||
use noxis_classfile::{Const, Insn, MAX_METHOD_INSNS, Program, compute_frames};
|
||||
use noxis_core::{Context, CoreError, Pass, PassId, PassReport};
|
||||
|
||||
|
|
@ -100,8 +100,9 @@ impl Pass for ConstantsPass {
|
|||
&mut predicates,
|
||||
)?;
|
||||
let hierarchy = ClassHierarchy::build(program);
|
||||
let frames = JdkHierarchy::wrap(&hierarchy);
|
||||
for (ci, mi) in &mutated {
|
||||
refresh_frames(program, *ci, *mi, &hierarchy)?;
|
||||
refresh_frames(program, *ci, *mi, &frames)?;
|
||||
}
|
||||
Ok(report(replaced, predicates, skipped))
|
||||
}
|
||||
|
|
@ -187,7 +188,7 @@ fn refresh_frames(
|
|||
program: &mut Program,
|
||||
ci: usize,
|
||||
mi: usize,
|
||||
hierarchy: &ClassHierarchy,
|
||||
hierarchy: &JdkHierarchy<'_>,
|
||||
) -> Result<(), CoreError> {
|
||||
let (name, snapshot) = {
|
||||
let class = program
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ mod rewire;
|
|||
mod tests;
|
||||
|
||||
use dispatch::{lookup_dispatch, opaque_guard, table_dispatch};
|
||||
use noxis_analysis::ClassHierarchy;
|
||||
use noxis_analysis::{ClassHierarchy, JdkHierarchy};
|
||||
use noxis_classfile::{Code, Insn, Label, Program, compute_frames};
|
||||
use noxis_core::{Context, CoreError, Pass, PassId, PassReport, Rng};
|
||||
use rewire::{dead_catch, invert_branches, split_blocks};
|
||||
|
|
@ -106,8 +106,9 @@ impl Pass for FlowPass {
|
|||
}
|
||||
}
|
||||
let hierarchy = ClassHierarchy::build(program);
|
||||
let frames = JdkHierarchy::wrap(&hierarchy);
|
||||
for (ci, mi) in &mutated {
|
||||
refresh_frames(program, *ci, *mi, &hierarchy)?;
|
||||
refresh_frames(program, *ci, *mi, &frames)?;
|
||||
}
|
||||
Ok(report(&per_scheme, mutated.len(), skipped))
|
||||
}
|
||||
|
|
@ -225,7 +226,7 @@ fn refresh_frames(
|
|||
program: &mut Program,
|
||||
ci: usize,
|
||||
mi: usize,
|
||||
hierarchy: &ClassHierarchy,
|
||||
hierarchy: &JdkHierarchy<'_>,
|
||||
) -> Result<(), CoreError> {
|
||||
let (name, snapshot) = {
|
||||
let class = program
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ mod tests;
|
|||
use crate::namegen::NameGenerator;
|
||||
use crate::rename::assign::parent_package;
|
||||
use body::{entry_body, helper_body};
|
||||
use noxis_analysis::ClassHierarchy;
|
||||
use noxis_analysis::{ClassHierarchy, JdkHierarchy};
|
||||
use noxis_classfile::{ClassBuilder, Field, Program, Version, compute_frames};
|
||||
use noxis_core::{Context, CoreError, Pass, PassId, PassReport};
|
||||
use weave::weave_first_reachable;
|
||||
|
|
@ -132,8 +132,9 @@ impl Pass for NoisePass {
|
|||
|
||||
// Frames for every generated branching method.
|
||||
let hierarchy = ClassHierarchy::build(program);
|
||||
let frames = JdkHierarchy::wrap(&hierarchy);
|
||||
for (ci, mi) in &fresh_methods {
|
||||
refresh_frames(program, *ci, *mi, &hierarchy)?;
|
||||
refresh_frames(program, *ci, *mi, &frames)?;
|
||||
}
|
||||
|
||||
let mut woven = 0usize;
|
||||
|
|
@ -177,7 +178,7 @@ fn refresh_frames(
|
|||
program: &mut Program,
|
||||
ci: usize,
|
||||
mi: usize,
|
||||
hierarchy: &ClassHierarchy,
|
||||
hierarchy: &JdkHierarchy<'_>,
|
||||
) -> Result<(), CoreError> {
|
||||
let (name, snapshot) = {
|
||||
let class = program
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! Weaving junk classes into the reachability graph of live methods.
|
||||
|
||||
use noxis_analysis::ClassHierarchy;
|
||||
use noxis_analysis::{ClassHierarchy, JdkHierarchy};
|
||||
use noxis_classfile::{Insn, InvokeKind, Program, compute_frames};
|
||||
use noxis_core::{CoreError, PassId};
|
||||
|
||||
|
|
@ -68,7 +68,8 @@ pub(super) fn weave_first_reachable(
|
|||
.cloned()
|
||||
.ok_or_else(missing)?;
|
||||
let hierarchy = ClassHierarchy::build(program);
|
||||
let frames = compute_frames(&class_name, &snapshot, &hierarchy)
|
||||
let frames_hierarchy = JdkHierarchy::wrap(&hierarchy);
|
||||
let frames = compute_frames(&class_name, &snapshot, &frames_hierarchy)
|
||||
.map_err(|e| CoreError::pass(PassId::Noise, e.to_string()))?;
|
||||
program
|
||||
.classes
|
||||
|
|
|
|||
|
|
@ -69,14 +69,25 @@ pub(super) fn rewrite_code(
|
|||
}
|
||||
*descriptor = rewrite_descriptor(descriptor, &maps.classes);
|
||||
}
|
||||
Insn::New(name)
|
||||
| Insn::CheckCast(name)
|
||||
| Insn::InstanceOf(name)
|
||||
| Insn::ANewArray(name) => {
|
||||
Insn::New(name) => {
|
||||
if let Some(mapped) = maps.classes.get(name.as_str()) {
|
||||
*name = mapped.clone();
|
||||
}
|
||||
}
|
||||
// Type operands may be array descriptors (`[Lpkg/Cls;`, `[B`):
|
||||
// the element class inside follows the mapping, while a plain
|
||||
// name takes the exact entry. A stale descriptor is worse than
|
||||
// useless - later frame merges treat it as a ghost supertype.
|
||||
Insn::CheckCast(name) | Insn::InstanceOf(name) | Insn::ANewArray(name) => {
|
||||
if name.starts_with('[') {
|
||||
*name = rewrite_descriptor(name, &maps.classes);
|
||||
} else if let Some(mapped) = maps.classes.get(name.as_str()) {
|
||||
*name = mapped.clone();
|
||||
}
|
||||
}
|
||||
Insn::MultiANewArray { descriptor, .. } => {
|
||||
*descriptor = rewrite_descriptor(descriptor, &maps.classes);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -191,3 +191,77 @@ fn subclass_gets_alias_for_inherited_member() {
|
|||
// Чужого члена не выдумываем.
|
||||
assert_eq!(maps.method("p/Sub", "other", "()V"), None);
|
||||
}
|
||||
|
||||
/// Array type operands (`checkcast`, `instanceof`, `anewarray`,
|
||||
/// `multianewarray`) carry descriptors, not plain names: the element class
|
||||
/// inside must follow the mapping, or the reference silently goes stale
|
||||
/// (and the next frame computation merges against a ghost type).
|
||||
#[test]
|
||||
fn array_type_operands_follow_their_element() {
|
||||
let mut program = sample();
|
||||
let main = program
|
||||
.classes
|
||||
.iter_mut()
|
||||
.find(|c| c.this_class == "app/Main")
|
||||
.expect("main");
|
||||
let probe = main
|
||||
.methods
|
||||
.iter_mut()
|
||||
.find(|m| m.name == "main")
|
||||
.expect("main method");
|
||||
let code = probe.code.as_mut().expect("code");
|
||||
code.insns.extend([
|
||||
Insn::CheckCast("[Lapp/Helper;".to_string()),
|
||||
Insn::Pop,
|
||||
Insn::InstanceOf("[Lapp/Helper;".to_string()),
|
||||
Insn::Pop,
|
||||
Insn::ANewArray("app/Helper".to_string()),
|
||||
Insn::Pop,
|
||||
Insn::MultiANewArray {
|
||||
descriptor: "[[Lapp/Helper;".to_string(),
|
||||
dims: 2,
|
||||
},
|
||||
Insn::Pop,
|
||||
]);
|
||||
let ctx = run_rename(&mut program);
|
||||
let helper = ctx.mapping.class("app/Helper").expect("renamed");
|
||||
let main = program.class("app/Main").expect("kept");
|
||||
let method = main
|
||||
.methods
|
||||
.iter()
|
||||
.find(|m| m.name == "main")
|
||||
.expect("main");
|
||||
let insns = &method.code.as_ref().expect("code").insns;
|
||||
let cast = insns
|
||||
.iter()
|
||||
.find_map(|i| match i {
|
||||
Insn::CheckCast(name) => Some(name),
|
||||
_ => None,
|
||||
})
|
||||
.expect("checkcast");
|
||||
assert_eq!(cast, &format!("[L{helper};"));
|
||||
let inst = insns
|
||||
.iter()
|
||||
.find_map(|i| match i {
|
||||
Insn::InstanceOf(name) => Some(name),
|
||||
_ => None,
|
||||
})
|
||||
.expect("instanceof");
|
||||
assert_eq!(inst, &format!("[L{helper};"));
|
||||
let anew = insns
|
||||
.iter()
|
||||
.find_map(|i| match i {
|
||||
Insn::ANewArray(name) => Some(name),
|
||||
_ => None,
|
||||
})
|
||||
.expect("anewarray");
|
||||
assert_eq!(anew, helper);
|
||||
let multi = insns
|
||||
.iter()
|
||||
.find_map(|i| match i {
|
||||
Insn::MultiANewArray { descriptor, .. } => Some(descriptor),
|
||||
_ => None,
|
||||
})
|
||||
.expect("multianewarray");
|
||||
assert_eq!(multi, &format!("[[L{helper};"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ mod tests;
|
|||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use noxis_analysis::ClassHierarchy;
|
||||
use noxis_analysis::{ClassHierarchy, JdkHierarchy};
|
||||
use noxis_classfile::{Const, Insn, MAX_METHOD_INSNS, Program, compute_frames};
|
||||
use noxis_core::{Context, CoreError, NameStyle, Pass, PassId, PassReport, derive_seed};
|
||||
use noxis_runtime::{Scheme, decoder_class, decoder_owner, encrypt};
|
||||
|
|
@ -128,6 +128,7 @@ impl Pass for StringsPass {
|
|||
ctx.note(format!("string decoder: {owner}"));
|
||||
// Phase 4: frames of the touched methods.
|
||||
let hierarchy = ClassHierarchy::build(program);
|
||||
let frames_hierarchy = JdkHierarchy::wrap(&hierarchy);
|
||||
for (ci, mi) in &mutated {
|
||||
let (name, snapshot) = {
|
||||
let class = program
|
||||
|
|
@ -143,7 +144,7 @@ impl Pass for StringsPass {
|
|||
.ok_or_else(|| pass_error("method vanished"))?,
|
||||
)
|
||||
};
|
||||
let frames = compute_frames(&name, &snapshot, &hierarchy)
|
||||
let frames = compute_frames(&name, &snapshot, &frames_hierarchy)
|
||||
.map_err(|e| pass_error(e.to_string()))?;
|
||||
program
|
||||
.classes
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue