From ecf7158e13fa44ef9cd4e472ebb1c5cedcf78184 Mon Sep 17 00:00:00 2001 From: Sander Hautvast Date: Tue, 19 Sep 2023 21:21:32 +0200 Subject: [PATCH] reads complete class file --- .gitignore | 2 + Cargo.lock | 7 ++ Cargo.toml | 8 ++ MetaField.class | Bin 0 -> 639 bytes MetaField.java | 20 +++++ src/lib.rs | 209 ++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 17 ++++ src/types.rs | 64 +++++++++++++++ 8 files changed, 327 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 MetaField.class create mode 100644 MetaField.java create mode 100644 src/lib.rs create mode 100644 src/main.rs create mode 100644 src/types.rs 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 0000000000000000000000000000000000000000..0be7be0df56687ca2d76457d1a100e495ac19498 GIT binary patch literal 639 zcma)3OG^S#6#nk`7`5@yWcD(soukk!TNrHuK~P&5Ld&aO^~N{@>v4bV-u55FKrtk*q%YMq zL#*QT(nuj~B4c3(7K1YgrtVm($-M8*CVZ|Ie5Tw`j652Z3*okf;teUhAw%5fQ^8=j zHWXbIN`G9}pg9Y96d2OeU?@jYgfj-#WGGd(`E27^Tn?TubK5Gl@dgLya*4XMTCz7KGr z*Ty-mYS85k*!qGL*?MpCF)(_9U9vW1_xf4L=c}(^)!GtfwY)_3nMwxs$l8dLO_5F0 zmqCu;ic~pUjbR@p$_OB$VNs-s3?ewd;hM%Jum2^gFEF1dSb-;_VlD!-Rt;-Fh9ewP UMQi*yoPGQ?Z2viY5_Ofo0O>h)Q2+n{ literal 0 HcmV?d00001 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