JVM学习(1)--修改classfile有关源码

任务目标

增加一种class文件格式变换方法,用工具对class文件进行变换,class parser提示并逆变换加载
修改一个class文件,将magic改掉,class parser能提示并正常加载

目标

test.java

public class test
{
        public static void main(String[] args)
        {
                System.out.println("Test");
        }
}

在vim中用%!xxd 打开16进制文件
00000000: cafe babe 0000 0035 001d 0a00 0600 0f09 .......5........
可以发现目前的magic值是0XCAFEBABE
我们编写的magic_change工具源码

#include<iostream>
#include<fstream>
#include<string.h>
using namespace std;

bool name_is_class(char* name)
{
	int i;
	int len = strlen(name);
	for (i = 0; i < len; i++)
	{
		if (name[i] == '.')
			break;
	}
	if (i == len)
		return false;
	string str_name = name;
	string suffixname = str_name.substr(i + 1);
	if (suffixname == "class")
		return true;
	return false;

}

int main(int argc, char** argv)
{
	if (argv[1] == NULL)
	{
		cout << "give a proper class";
		return 0;
	}
	char* classname = argv[1];
	string filename;
	if (!name_is_class(classname))
	{
		cout << classname << " is not a class file ";
		return 0;
	}
	char* buffer = (char*)malloc(sizeof(char)*4);
	fstream classfile(classname, ios::in | ios::out | ios::binary);
	if (!classfile)
	{
		cout << filename << " can't open";
		return 0;
	}
	char c;

	classfile.seekg(3,ios::beg);
	c = 0xBA;
	classfile.write((char*)&c, sizeof(char));
	classfile.close();
	return 0;
}

Tips:vim中全部删除:按esc后,dG

经过magic_change后,

00000000: cafe baba 0000 0035 001d 0a00 0600 0f09 .......5........
MAGIC值被改变了。

那么要做到class分析的时候读取再将MAGIC值改回,就要对源码进行读写修改了。

修改源码

首先找到hotspot目录下的classfile文件夹,
理解classflie中的数据流处理方式;
那么很自然的grep MAGIC定位到classFileParser.cpp文件

Tips:
grep -rn "string_for_search"可以用来找在当前目录下的包含目标字符串的值
find . -name "name_for_search"找寻包含目标名的文件

发现在classFileParser.cpp文件中

#define JAVA_CLASSFILE_MAGIC              0xCAFEBABE

在该文件中搜索,发现有

 stream->guarantee_more(8, CHECK);  // magic, major, minor
  // Magic value
  const u4 magic = stream->get_u4_fast();
  
  guarantee_property(magic == JAVA_CLASSFILE_MAGIC,
                     "Incompatible magic value %u in class file %s",
                     magic, CHECK);

这个函数整体的前一部分是

void ClassFileParser::parse_stream(const ClassFileStream* const stream,
                                   TRAPS) {

  assert(stream != NULL, "invariant");
  assert(_class_name != NULL, "invariant");

  // BEGIN STREAM PARSING
  stream->guarantee_more(8, CHECK);  // magic, major, minor
  // Magic value
  const u4 magic = stream->get_u4_fast();
  
  guarantee_property(magic == JAVA_CLASSFILE_MAGIC,
                     "Incompatible magic value %u in class file %s",
                     magic, CHECK);

  // Version numbers
  _minor_version = stream->get_u2_fast();
  _major_version = stream->get_u2_fast();

分析:

// BEGIN STREAM PARSING
  stream->guarantee_more(8, CHECK);  // magic, major, minor

这个是确保有8个字节以上的内容,
函数原型是

 void guarantee_more(int size, TRAPS) const {
    size_t remaining = (size_t)(_buffer_end - _current);
    unsigned int usize = (unsigned int)size;
    check_truncated_file(usize > remaining, CHECK);
  }

Tips:在Visual Studio中看源码的时候可利用ctrl+shift+8返回在Ubuntu中主要利用了grep -rn "需要查找的信息名"

然后就是读取magic值

  const u4 magic = stream->get_u4_fast();

函数原型:

  u4 get_u4_fast() const {
    u4 res = Bytes::get_Java_u4((address)_current);
    _current += 4;
    return res;
  }

读到这里分析一下应该是从目前的指针处往后读一定的字节,然后指针后移

那么可能这个put_Java_u4函数就是我可以用的,想找定义,
在这里插入图片描述
这里看文件夹名都是在cpu文件下,是不同类型对应的bytes处理方式,大概都大同小异,打开bytes_x86.hpp

  // Efficient reading and writing of unaligned unsigned data in platform-specific byte ordering
  // (no special code is needed since x86 CPUs can access unaligned data)
  static inline u2   get_native_u2(address p)         { return *(u2*)p; }
  static inline u4   get_native_u4(address p)         { return *(u4*)p; }
  static inline u8   get_native_u8(address p)         { return *(u8*)p; }

  static inline void put_native_u2(address p, u2 x)   { *(u2*)p = x; }
  static inline void put_native_u4(address p, u4 x)   { *(u4*)p = x; }
  static inline void put_native_u8(address p, u8 x)   { *(u8*)p = x; }


  // Efficient reading and writing of unaligned unsigned data in Java
  // byte ordering (i.e. big-endian ordering). Byte-order reversal is
  // needed since x86 CPUs use little-endian format.
  static inline u2   get_Java_u2(address p)           { return swap_u2(get_native_u2(p)); }
  static inline u4   get_Java_u4(address p)           { return swap_u4(get_native_u4(p)); }
  static inline u8   get_Java_u8(address p)           { return swap_u8(get_native_u8(p)); }

  static inline void put_Java_u2(address p, u2 x)     { put_native_u2(p, swap_u2(x)); }
  static inline void put_Java_u4(address p, u4 x)     { put_native_u4(p, swap_u4(x)); }
  static inline void put_Java_u8(address p, u8 x)     { put_native_u8(p, swap_u8(x)); }


  // Efficient swapping of byte ordering
  static inline u2   swap_u2(u2 x);                   // compiler-dependent implementation
  static inline u4   swap_u4(u4 x);                   // compiler-dependent implementation
  static inline u8   swap_u8(u8 x);
};

例如

 static inline void put_Java_u4(address p, u4 x)     { put_native_u4(p, swap_u4(x)); }
 static inline void put_native_u4(address p, u4 x)   { *(u4*)p = x; }

将地址p的值写入u4类型的x;
在这里比较疑惑的是swap函数的作用,根据注释以及java的特性理解了一下,应该是大小端的区别,进行适当的调整写入。
那么我们回到classFileSteam.hpp文件,仿照get函数写一个change函数

  void change_u4_fast() const {
     _current -= 4;
      Bytes::put_Java_u4((address)_current, (u4)0xCAFEBABE);
      _current += 4;
  }

再在classFileParser中修改为

// Magic value
  u4 magic_check = stream->get_u4_fast();
  if (magic_check == 0xCAFEBABA)
  {
      stream->change_u4_fast();
      magic_check = 0xCAFEBABE;
  }
  const u4 magic =(const u4) magic_check;
  guarantee_property(magic == JAVA_CLASSFILE_MAGIC,
                     "Incompatible magic value %u in class file %s",
                     magic, CHECK);

重新编译后,通过了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值