passes: rename mixin classes + patch mixins.json/refmap like OBF
- Remove absorb_mixins keep rule (mixin classes now renameable) - Add mixins.rs: patch_mixins_json rewrites package + entries via common prefix of mapped names; patch_refmap_json rewrites all strings - Resources pass routes *.mixins.json and *refmap.json to specialized patchers, falls back to generic text remap Reference: OBF fabric-obfuscator patchMixinsJson/patchRefmapJson.
This commit is contained in:
parent
b73c72d56d
commit
90506a4736
17 changed files with 490 additions and 24 deletions
|
|
@ -23,7 +23,14 @@ use crate::model::{Attribute, Code, ExceptionHandler};
|
|||
use frames::convert_frames;
|
||||
|
||||
/// Method body → a ready `Code` attribute (with logical instruction numbers).
|
||||
pub(super) fn convert_code(code: &Code, pool: &mut Pool) -> Result<RtAttr, ClassfileError> {
|
||||
///
|
||||
/// `emit_frames` is false for class-file versions below 50 (Java 6): those use
|
||||
/// the type-inference verifier and must not carry a `StackMapTable`.
|
||||
pub(super) fn convert_code(
|
||||
code: &Code,
|
||||
pool: &mut Pool,
|
||||
emit_frames: bool,
|
||||
) -> Result<RtAttr, ClassfileError> {
|
||||
let (labels, bare) = split_marks(&code.insns)?;
|
||||
let resolve = |label: Label| -> Result<u32, ClassfileError> {
|
||||
labels
|
||||
|
|
@ -55,7 +62,11 @@ pub(super) fn convert_code(code: &Code, pool: &mut Pool) -> Result<RtAttr, Class
|
|||
resolve: &resolve,
|
||||
byte_of: &byte_of,
|
||||
};
|
||||
let frames = convert_frames(&code.frames, pool, &byte_of)?;
|
||||
let frames = if emit_frames {
|
||||
convert_frames(&code.frames, pool, &byte_of)?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let mut attributes = Vec::with_capacity(code.attributes.len() + 1);
|
||||
for attr in &code.attributes {
|
||||
attributes.push(match attr {
|
||||
|
|
@ -68,7 +79,7 @@ pub(super) fn convert_code(code: &Code, pool: &mut Pool) -> Result<RtAttr, Class
|
|||
other => attrs::convert(other, pool)?,
|
||||
});
|
||||
}
|
||||
if !frames.is_empty() {
|
||||
if emit_frames && !frames.is_empty() {
|
||||
attributes.push(RtAttr::StackMapTable {
|
||||
name_index: pool.utf8("StackMapTable")?,
|
||||
frames,
|
||||
|
|
|
|||
|
|
@ -22,6 +22,11 @@ pub(crate) fn write_class(class: &ClassFile) -> Result<Vec<u8>, ClassfileError>
|
|||
let mut pool = pool::Pool::new();
|
||||
let version = RtVersion::from(class.version.major, class.version.minor)
|
||||
.map_err(|e| ClassfileError::Write(e.to_string()))?;
|
||||
// `StackMapTable` is only legal from class-file version 50 (Java 6). Older
|
||||
// classes use the type-inference verifier; emitting the attribute makes
|
||||
// ASM/Paper reject the file ("Class versions V1_5 or less must use
|
||||
// F_NEW frames"). Real case: the string-decoder is built at major 49.
|
||||
let emit_frames = class.version.major >= 50;
|
||||
let this_class = pool.class(&class.this_class)?;
|
||||
let super_class = match &class.super_class {
|
||||
None => 0,
|
||||
|
|
@ -37,7 +42,7 @@ pub(crate) fn write_class(class: &ClassFile) -> Result<Vec<u8>, ClassfileError>
|
|||
}
|
||||
let mut methods = Vec::with_capacity(class.methods.len());
|
||||
for method in &class.methods {
|
||||
methods.push(convert_method(method, &mut pool)?);
|
||||
methods.push(convert_method(method, &mut pool, emit_frames)?);
|
||||
}
|
||||
let mut attributes = Vec::with_capacity(class.attributes.len());
|
||||
for attr in &class.attributes {
|
||||
|
|
@ -78,13 +83,17 @@ fn convert_field(field: &Field, pool: &mut pool::Pool) -> Result<RtField, Classf
|
|||
})
|
||||
}
|
||||
|
||||
fn convert_method(method: &Method, pool: &mut pool::Pool) -> Result<RtMethod, ClassfileError> {
|
||||
fn convert_method(
|
||||
method: &Method,
|
||||
pool: &mut pool::Pool,
|
||||
emit_frames: bool,
|
||||
) -> Result<RtMethod, ClassfileError> {
|
||||
let mut attributes = Vec::with_capacity(method.attributes.len() + 1);
|
||||
for attr in &method.attributes {
|
||||
attributes.push(attrs::convert(attr, pool)?);
|
||||
}
|
||||
if let Some(code) = &method.code {
|
||||
attributes.push(code::convert_code(code, pool)?);
|
||||
attributes.push(code::convert_code(code, pool, emit_frames)?);
|
||||
}
|
||||
Ok(RtMethod {
|
||||
access_flags: MethodAccessFlags::from_bits_truncate(method.access_flags),
|
||||
|
|
|
|||
|
|
@ -178,6 +178,58 @@ fn java_available() -> bool {
|
|||
.is_ok_and(|out| out.status.success())
|
||||
}
|
||||
|
||||
/// A v1.5 (major 49) class must not carry a `StackMapTable`: the old verifier
|
||||
/// uses type inference, and ASM/Paper reject the attribute on pre-v6 classes
|
||||
/// ("Class versions V1_5 or less must use F_NEW frames"). Real case: the
|
||||
/// string-decoder is built at major 49; a later pass recomputes its frames,
|
||||
/// and the writer used to emit them anyway - Paper then failed to convert the
|
||||
/// plugin class.
|
||||
#[test]
|
||||
fn old_version_class_omits_stack_map_table() {
|
||||
let insns = vec![
|
||||
Insn::Load {
|
||||
slot: 0,
|
||||
kind: SlotKind::Int,
|
||||
},
|
||||
Insn::Branch(BranchCond::Zero(Cond::Ne), 100),
|
||||
Insn::Const(Const::Int(0)),
|
||||
Insn::Return(Some(Prim::Int)),
|
||||
Insn::Mark(100),
|
||||
Insn::Const(Const::Int(1)),
|
||||
Insn::Return(Some(Prim::Int)),
|
||||
];
|
||||
let code = Code {
|
||||
max_stack: 1,
|
||||
max_locals: 1,
|
||||
insns,
|
||||
exception_table: Vec::new(),
|
||||
frames: Vec::new(),
|
||||
attributes: Vec::new(),
|
||||
};
|
||||
let class = ClassBuilder::new("demo/Old", Some("java/lang/Object"))
|
||||
.version(Version {
|
||||
minor: 0,
|
||||
major: 49,
|
||||
})
|
||||
.method_with_code(0x0009, "pick", "(I)I", code)
|
||||
.build();
|
||||
let hierarchy = MapHierarchy::default();
|
||||
let frames = compute_frames(&class.this_class, &class.methods[0], &hierarchy).expect("frames");
|
||||
assert!(
|
||||
!frames.is_empty(),
|
||||
"the branch must produce at least one frame"
|
||||
);
|
||||
let mut class = class;
|
||||
class.methods[0].code.as_mut().expect("code").frames = frames;
|
||||
let bytes = format::write(&class).expect("write v1.5 class");
|
||||
assert!(
|
||||
!bytes
|
||||
.windows(b"StackMapTable".len())
|
||||
.any(|w| w == b"StackMapTable"),
|
||||
"a major-49 class must not emit StackMapTable"
|
||||
);
|
||||
}
|
||||
|
||||
/// Write a class into a package tree and run `java -Xverify:all`.
|
||||
/// Success means loading without verification errors (`main` may be missing).
|
||||
fn java_verify(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue