其实这道题的原意是考察对JVM的了解
由于我们存在一个类的基地址泄露,加上Boss Know Everything的提示,最简单的思路应当是替换VIPPlayer与Boss的类,使VIPPlayer.toString()->Boss.toString(),即可获得FLAG
我们知道,Java的内存区域分为 PC寄存器(Program Counter Register)
、 Java虚拟机栈(Java Virtual Machine Stacks)
、 本地方法栈(Native Method Stack)
、 Java堆(Java Heap)
和 方法区(Method Area)
.
我们知道,在C++层面,每一个Java Class都是一个instanceOop的实例,所以我们可以看看src/share/vm/oops/instanceOop.hpp [在线版戳这里] ,得到如下代码
#ifndef SHARE_VM_OOPS_INSTANCEOOP_HPP #define SHARE_VM_OOPS_INSTANCEOOP_HPP #include "oops/oop.hpp" class instanceOopDesc : public oopDesc { public: // aligned header size. static int header_size() { return sizeof(instanceOopDesc)/HeapWordSize; } // If compressed, the offset of the fields of the instance may not be aligned. static int base_offset_in_bytes() { // offset computation code breaks if UseCompressedClassPointers // only is true return (UseCompressedOops && UseCompressedClassPointers) ? klass_gap_offset_in_bytes() : sizeof(instanceOopDesc); } static bool contains_field_offset(int offset, int nonstatic_field_size) { int base_in_bytes = base_offset_in_bytes(); return (offset >= base_in_bytes && (offset-base_in_bytes) < nonstatic_field_size * heapOopSize); } }; #endif // SHARE_VM_OOPS_INSTANCEOOP_HPP
发现它继承自oopDesc [在线版戳这里]
class oopDesc { friend class VMStructs; private: volatile markOop _mark; union _metadata { Klass* _klass; narrowKlass _compressed_klass; } _metadata; //省略一万行代码 }
所以,当我们new一个对象时,Jvm会帮我们在Java Heap中分配一块内存,结构为
+-------------------------+ | +---------------------+ | | | instanceOopDesc | | | | +-----------------+ | | | | | markOop _mark | | | | | +-----------------+ | | | | +-----------------+ | | | | | union _metadata | | | | | +-----------------+ | | | +---------------------+ | | +---------------------+ | | | instanceData | | | +---------------------+ | | +---------------------+ | | | Pedding | | | |---------------------| | +-------------------------+
题目描述所给的是Java 7 x86的环境,所以不存在指针压缩的问题,即此处 _metadata
为 Klass* _klass
,另外,Java堆区的内存默认属性为rw,所以我们只需要互换Boss与VIPPlayer的InstanceAddress+4的值就行
P.S.:为啥会有VIPPlayer呢?我就想听你们夸我帅,哪怕不是真心的
这个程序主要的东西都在
public String PraseCommand(String Command)
中,其中我们需要注意的是ShowInfo,Save,SetATK(SetHP),DebugSetDataStoreAddress
其中,Save暴露了两个类的基地址
DebugSetDataStoreAddress+ShowInfo可造成任意地址读
DebugSetDataStoreAddress+SetATK(SetHP)可造成任意地址写
所以此处就不额外放Exp了
另外,最关键的就是 VeroFessIsHandsome
(手动滑稽)
另外GetShell也是可选的高难度通关方法,但是有队师傅直接rm -rf /*了,至今不知道是谁…
前排推荐 Nu1L大佬们的高难度通关姿势