commit ecf7158e13fa44ef9cd4e472ebb1c5cedcf78184 Author: Sander Hautvast Date: Tue Sep 19 21:21:32 2023 +0200 reads complete class file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..40d9aca --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/.idea \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..7abed0e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "classfile_reader" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e644fdd --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "classfile_reader" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/MetaField.class b/MetaField.class new file mode 100644 index 0000000..0be7be0 Binary files /dev/null and b/MetaField.class differ diff --git a/MetaField.java b/MetaField.java new file mode 100644 index 0000000..9a05530 --- /dev/null +++ b/MetaField.java @@ -0,0 +1,20 @@ +package com.github.shautvast.reflective; + +public class MetaField { + + private final String name; + private final int modifiers; + + public MetaField(String name, int modifiers) { + this.name = name; + this.modifiers = modifiers; + } + + public String getName() { + return name; + } + + public int getModifiers() { + return modifiers; + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..e23400b --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,209 @@ +pub mod types; + +use crate::types::{CpEntry, Class, Field, Attribute, Method}; + +pub fn get_class(bytecode: Vec) -> Option { + check_magic(&bytecode); + + let constant_pool_count = get_u16(&bytecode, 8); + let mut index = 10; + let mut constant_pool: Vec = vec![]; + for _ in 0..constant_pool_count - 1 { + constant_pool.push(read_constant_pool_entry(&mut index, &bytecode)); + } + + let access_flags = get_u16(&bytecode, index); + let this_class = get_u16(&bytecode, index + 2); + let super_class = get_u16(&bytecode, index + 4); + + let interfaces_count = get_u16(&bytecode, index + 6); + index += 8; + let mut interfaces = vec![]; + for _ in 0..interfaces_count { + interfaces.push(get_u16(&bytecode, index)); + index += 2; + } + + let fields_count = get_u16(&bytecode, index); + index += 2; + let mut fields = vec![]; + for _ in 0..fields_count { + fields.push(read_field(&mut index, &bytecode)); + } + + let methods_count = get_u16(&bytecode, index); + index += 2; + let mut methods = vec![]; + for _ in 0..methods_count { + methods.push(read_method(&mut index, &bytecode)); + } + + let attributes_count = get_u16(&bytecode, index); + index += 2; + let mut attributes = vec![]; + for _ in 0..attributes_count { + attributes.push(read_attribute(&bytecode, &mut index)); + } + + Some(Class { + minor_version: get_u16(&bytecode, 4), + major_version: get_u16(&bytecode, 6), + constant_pool, + access_flags, + this_class, + super_class, + interfaces, + fields, + methods, + attributes, + }) +} + +fn check_magic(bytecode: &Vec) { + if &bytecode[0..4] != [0xCA, 0xFE, 0xBA, 0xBE] { + panic!(); //must never happen + } +} + +fn read_constant_pool_entry(index: &mut usize, bytecode: &Vec) -> CpEntry { + let tag = bytecode[*index]; + match tag { + 1 => { + let len = get_u16(bytecode, *index + 1) as usize; + let utf: Vec = Vec::from(&bytecode[*index + 3..*index + 3 + len]); + *index += len + 3; + CpEntry::Utf8(String::from_utf8(utf).unwrap()) + } + 3 => { + let value = get_i32(bytecode, *index + 1); + *index += 5; + CpEntry::Integer(value) + } + 4 => { + let value = get_f32(bytecode, *index + 1); + *index += 5; + CpEntry::Float(value) + } + 5 => { + let value = get_i64(bytecode, *index + 1); + *index += 9; + CpEntry::Long(value) + } + 6 => { + let value = get_f64(bytecode, *index + 1); + *index += 9; + CpEntry::Double(value) + } + 7 => { + let name_index = get_u16(bytecode, *index + 1); + *index += 3; + CpEntry::Class(name_index) + } + 8 => { + let string_index = get_u16(bytecode, *index + 1); + *index += 3; + CpEntry::String(string_index) + } + 9 => { + let class_index = get_u16(bytecode, *index + 1); + let name_and_type_index = get_u16(bytecode, *index + 3); + *index += 5; + CpEntry::Fieldref(class_index, name_and_type_index) + } + 10 => { + let class_index = get_u16(bytecode, *index + 1); + let name_and_type_index = get_u16(bytecode, *index + 3); + *index += 5; + CpEntry::MethodRef(class_index, name_and_type_index) + } + 11 => { + let class_index = get_u16(bytecode, *index + 1); + let name_and_type_index = get_u16(bytecode, *index + 3); + *index += 5; + CpEntry::InterfaceMethodref(class_index, name_and_type_index) + } + 12 => { + let name_index = get_u16(bytecode, *index + 1); + let descriptor_index = get_u16(bytecode, *index + 3); + *index += 5; + CpEntry::NameAndType(name_index, descriptor_index) + } + _ => panic!() + } +} + +fn read_field(index: &mut usize, bytecode: &Vec) -> Field { + let access_flags = get_u16(bytecode, *index); + let name_index = get_u16(bytecode, *index + 2); + let descriptor_index = get_u16(bytecode, *index + 4); + let attributes_count = get_u16(bytecode, *index + 6); + *index += 8; + let mut attributes = vec![]; + for _ in 0..attributes_count { + attributes.push(read_attribute(bytecode, index)); + } + Field { + access_flags, + name_index, + descriptor_index, + attributes_count, + attributes, + } +} + +fn read_method(index: &mut usize, bytecode: &Vec) -> Method { + let access_flags = get_u16(bytecode, *index); + let name_index = get_u16(bytecode, *index + 2); + let descriptor_index = get_u16(bytecode, *index + 4); + let attributes_count = get_u16(bytecode, *index + 6); + *index += 8; + let mut attributes = vec![]; + for _ in 0..attributes_count { + attributes.push(read_attribute(bytecode, index)); + } + Method { + access_flags, + name_index, + descriptor_index, + attributes_count, + attributes, + } +} + +fn read_attribute(bytecode: &Vec, index: &mut usize) -> Attribute { + let attribute_name_index = get_u16(bytecode, *index); + *index += 2; + let attribute_length = read_u32(bytecode, *index) as usize; + *index += 4; + let info: Vec = Vec::from(&bytecode[*index..*index + attribute_length]); + *index += attribute_length; + + Attribute { + attribute_name_index, + info, + } +} + +fn get_u16(data: &Vec, pos: usize) -> u16 { + u16::from_be_bytes(data[pos..pos + 2].try_into().expect("slice with incorrect length")) +} + +fn get_i32(data: &Vec, pos: usize) -> i32 { + i32::from_be_bytes(data[pos..pos + 4].try_into().expect("slice with incorrect length")) +} + +fn read_u32(data: &Vec, pos: usize) -> u32 { + u32::from_be_bytes(data[pos..pos + 4].try_into().expect("slice with incorrect length")) +} + +fn get_f32(data: &Vec, pos: usize) -> f32 { + f32::from_be_bytes(data[pos..pos + 4].try_into().expect("slice with incorrect length")) +} + +fn get_i64(data: &Vec, pos: usize) -> i64 { + i64::from_be_bytes(data[pos..pos + 8].try_into().expect("slice with incorrect length")) +} + +fn get_f64(data: &Vec, pos: usize) -> f64 { + f64::from_be_bytes(data[pos..pos + 8].try_into().expect("slice with incorrect length")) +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d25988a --- /dev/null +++ b/src/main.rs @@ -0,0 +1,17 @@ +use std::fs::{self, File}; +use std::io::Read; + +fn main() { + let bytecode = read_class_file("/Users/FJ19WK/RustroverProjects/classfile_reader/MetaField.class"); + if let Some(class) = classfile_reader::get_class(bytecode){ + println!("{:?}", class); + } +} + +fn read_class_file(name: &str) -> Vec { + let mut f = File::open(name).expect("no file found"); + let metadata = fs::metadata(name).expect("unable to read metadata"); + let mut buffer = vec![0; metadata.len() as usize]; + f.read(&mut buffer).expect("buffer overflow"); + buffer +} diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..fcfded4 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,64 @@ +#[derive(Debug)] +//TODO create factory function +pub struct Class { + pub minor_version: u16, + pub major_version: u16, + pub constant_pool: Vec, + pub access_flags: u16, + pub this_class: u16, + pub super_class: u16, + pub interfaces: Vec, + pub fields: Vec, + pub methods: Vec, + pub attributes: Vec, +} + + +#[derive(Debug)] +pub enum CpEntry { + Utf8(String), + //1 + Integer(i32), + //3 + Float(f32), + //4 + Long(i64), + //5 + Double(f64), + //6 + Class(u16), + //7 + String(u16), + //8 + Fieldref(u16, u16), + //9 + MethodRef(u16, u16), + //10 + InterfaceMethodref(u16, u16), + //11 + NameAndType(u16, u16), //12 +} + +#[derive(Debug)] +pub struct Field { + pub access_flags: u16, + pub name_index: u16, + pub descriptor_index: u16, + pub attributes_count: u16, + pub attributes: Vec, +} + +#[derive(Debug)] +pub struct Attribute { + pub attribute_name_index: u16, + pub info: Vec, +} + +#[derive(Debug)] +pub struct Method{ + pub access_flags: u16, + pub name_index: u16, + pub descriptor_index: u16, + pub attributes_count: u16, + pub attributes: Vec, +} \ No newline at end of file