前言
nwjs0.54.1以及node16.4.0之前尝试 NTQQ 使用 NodeJs 运行,想起wechat-web-devtools-linux有个skyline模块;
这个模块是运行在nwjs环境的,试试看能不能换成nodejs。
文章目录[隐藏] 前言 分析 伪造QQNT.dll 总结 前言 上篇实现了Linux平台使用nodejs启动的 […]
实践
把node.dll和nw.dll都做了转发,但是启动会崩溃。
崩溃代码(from ida):
__int64 __fastcall sub_7FFC1E826EE2(__int64 *a1)
{
__int64 v2; // rdi
__int64 v3; // rax
__int64 v4; // rax
_QWORD *v5; // rcx
_QWORD *v7; // [rsp+28h] [rbp-40h] BYREF
char v8[24]; // [rsp+30h] [rbp-38h] BYREF
v2 = *(_QWORD *)(*a1 + 8);
v8::HandleScope::HandleScope(v8, v2);
v3 = *(_QWORD *)(*a1 + 40);
// Value::QuickIsNullOrUndefined 根据AI判断,是指针压缩处理
if ( (*(_DWORD *)(*a1 + 40) & 3) != 1 // if (!I::HasHeapObjectTag(obj)) return false;
|| *(_WORD *)(*(unsigned int *)(v3 - 1) + (v3 & 0xFFFFFFFF00000000ui64) + 7) != 67
|| (*(_DWORD *)(v3 + 23) & 0xFFFFFFFE) != 10 )
{
sub_7FFC1E826F8E(&v7, v2, a1);
v4 = *a1;
v5 = v7;
if ( !v7 )
v5 = (_QWORD *)(v4 + 16);
*(_QWORD *)(v4 + 24) = *v5;
}
return v8::HandleScope::~HandleScope(v8);
}
其中 (_WORD *)(*(unsigned int *)(v3 - 1) + (v3 & 0xFFFFFFFF00000000ui64) + 7) 执行时崩溃,ida自动暂停;
经检查计算得到的地址实际上指向无效内存区域。
第一部分判断:
// (*(_DWORD *)(*a1 + 40) & 3) != 1
V8_INLINE static bool HasHeapObjectTag(const internal::Address value) {
return (value & kHeapObjectTagMask/*3*/) == static_cast<Address>(kHeapObjectTag/*1*/);
}
第二部分判断:
// *(_WORD *)(*(unsigned int *)(v3 - 1) + (v3 & 0xFFFFFFFF00000000ui64) + 7) != 67 if (I::GetInstanceType(obj) != I::kOddballType/*67*/) return false; // (_WORD *)(*(unsigned int *)(v3 - 1) + (v3 & 0xFFFFFFFF00000000ui64) + 7) // *(unsigned int *)(v3 - 1) + (v3 & 0xFFFFFFFF00000000ui64) ReadTaggedPointerField(obj, kHeapObjectMapOffset/*0*/) // xxx + 7 // 开压缩就是8,不开是12 return ReadRawField<uint16_t>(map, kMapInstanceTypeOffset/*8*/); // = map + 7
第三部分判断(感觉不太对,但不重要):
// (*(_DWORD *)(v3 + 23) & 0xFFFFFFFE) != 10 I::GetOddballKind(obj) == kExternalOneByteRepresentationTag
分析得出,编译开了V8_COMPRESS_POINTERS
实际用nodejs官方版本开了压缩之后还是会崩溃,怀疑是nwjs版本混入了其它东西,暂时没有头绪。。。。。实际上操作不对没开成功。。。
写了一个hello.node,看汇编代码似乎差不多(待仔细比较),能跑,更怪了。。。
2025-10-22 复现
// hello.cc
#include <nan.h>
void Method(const v8::FunctionCallbackInfo<v8::Value>& info) {
v8::HandleScope handle_scope(info.GetIsolate());
auto data = info.NewTarget();
auto empty = data.IsEmpty();
auto d = data->IsNullOrUndefined();
printf("data empty %d nullorundefined %d\n", empty, d);
info.GetReturnValue().Set(Nan::New("world").ToLocalChecked());
}
void Init(v8::Local<v8::Object> exports) {
v8::Local<v8::Context> context =
exports->GetCreationContext().ToLocalChecked();
auto isolate = exports->GetIsolate();
exports->Set(context,
Nan::New("hello").ToLocalChecked(),
v8::FunctionTemplate::New(isolate, Method)
->GetFunction(context)
.ToLocalChecked());
}
NODE_MODULE(hello, Init)
1. 使用nw-gyp构建:nw-gyp rebuild --target=0.54.1
2. 使用vs打开项目
3. 右键项目 -> 属性 -> C/C++ -> 预处理器 -> 预处理器定义 -> 编辑 -> 移除 V8_ENABLE_CHECKS (为了IsNullOrUndefined的内部实现与上面分析一致)
4. 重新构建
使用自己编译的node调用,会崩溃:
const hello = require('D:/github/node-addon-examples/src/1-getting-started/1_hello_world/nan/build/Release/hello.node')
console.log(new hello.hello())
看到这个讨论:https://groups.google.com/g/v8-dev/c/N9BpiGxPaQs/m/Vx9VnEx-BQAJ
取消预处理器定义的继承选项,移除V8_COMPRESS_POINTERS,重新编译测试,程序正常。
V8_COMPRESS_POINTERS开启有问题?2025-10-23
使用VS检查V8_COMPRESS_POINTERS开启的问题,借助IDE高亮提示,判断Flag是否启用。
操作:
1. vcbuild.bat修改set configure_flags="--experimental-enable-pointer-compression" (在这:github issue)
2. common.gypi中'V8_COMPRESS_POINTERS'后追加'V8_ENABLE_SANDBOX', 'V8_COMPRESS_POINTERS_IN_ISOLATE_CAGE'
V8_COMPRESS_POINTERS_IN_ISOLATE_CAGE是编译报错,看源代码知道要设置的(好像是这样)
测试程序,正常。


