前言
自己有做一个机器人框架:https://github.com/project-yui
一直以来都是使用官方客户端启动框架,但这也存在问题:官方客户端包含electron的一些组件,无用库比较多;
昨天,突发奇想,想着尝试一下使用nodejs启动。
分析
根据之前开发框架经验,入口是wrapper.node,直接编一个test.js看什么反应:
// test.js
const wrapper = require('./wrapper.node')
console.info('wrapper:', wrapper)
以下是输出,看起来再尝试加载,但是有东西没找到,那就补一下看看。
$ node test.js
node:internal/modules/cjs/loader:1865
return process.dlopen(module, path.toNamespacedPath(filename));
^
Error: /xxxxxxxxx/program/libbugly.so: undefined symbol: gnutls_free
at Object..node (node:internal/modules/cjs/loader:1865:18)
at Module.load (node:internal/modules/cjs/loader:1441:32)
at Function._load (node:internal/modules/cjs/loader:1263:12)
at TracingChannel.traceSync (node:diagnostics_channel:322:14)
at wrapModuleLoad (node:internal/modules/cjs/loader:237:24)
at Module.require (node:internal/modules/cjs/loader:1463:12)
at require (node:internal/modules/helpers:147:16)
at Object.<anonymous> (/xxxxxxxxx/Yui/program/test.js:1:17)
at Module._compile (node:internal/modules/cjs/loader:1706:14)
at Object..js (node:internal/modules/cjs/loader:1839:10) {
code: 'ERR_DLOPEN_FAILED'
}
Node.js v22.20.0
编一个preload.so文件,加载guntls;
extern "C" {
#if defined(__linux__) || defined(__APPLE__)
// Ensure gnutls_xxx is linked in
void * _force_gnutls_global_init __attribute__((used)) = (void *)gnutls_global_init;
#else
#endif
}
使用LD_PRELOAD=preload.so启动node,可以看到报错发生了变化:
$ LD_PRELOAD=/xxxxxx/preload.so node test.js node: symbol lookup error: /xxxxxxx/program/wrapper.node: undefined symbol: qq_magic_napi_register
实现一个空函数看看:
// Keep the existing registration helper
void qq_magic_napi_register() {
spdlog::info("call qq_magic_napi_register");
}
编译执行,报错变了,提示没用自己注册,看来此函数实在注册模块,使用ida看看怎么注册的。
$ LD_PRELOAD=/xxxxxx/preload.so node test.js
[2025-10-05 16:14:56.976] [info] call qq_magic_napi_register
node:internal/modules/cjs/loader:1865
return process.dlopen(module, path.toNamespacedPath(filename));
^
Error: Module did not self-register: '/xxxxxxx/program/wrapper.node'.
at Object..node (node:internal/modules/cjs/loader:1865:18)
at Module.load (node:internal/modules/cjs/loader:1441:32)
at Function._load (node:internal/modules/cjs/loader:1263:12)
at TracingChannel.traceSync (node:diagnostics_channel:322:14)
at wrapModuleLoad (node:internal/modules/cjs/loader:237:24)
at Module.require (node:internal/modules/cjs/loader:1463:12)
at require (node:internal/modules/helpers:147:16)
at Object.<anonymous> (/xxxxxxxx/program/test.js:1:17)
at Module._compile (node:internal/modules/cjs/loader:1706:14)
at Object..js (node:internal/modules/cjs/loader:1839:10) {
code: 'ERR_DLOPEN_FAILED'
}
Node.js v22.20.0
看起来是napi_module_register换了个名字,那就引入napi调用它;
代码如下:
extern "C" {
// Keep the existing registration helper
void qq_magic_napi_register(napi_module *m) {
spdlog::info("call qq_magic_napi_register");
napi_module_register(m);
}
#if defined(__linux__) || defined(__APPLE__)
// Ensure gnutls_xxx is linked in
void * _force_gnutls_global_init __attribute__((used)) = (void *)gnutls_global_init;
#else
#endif
}
编译执行,看到执行成功没有报错:
$ LD_PRELOAD=/xxxxxx/preload.so node test.js
[2025-10-05 16:22:27.940] [info] call qq_magic_napi_register
wrapper: {
NodeIQQNTWrapperEngine: [Function: NodeIQQNTWrapperEngine],
NodeIKernelThirdPartySigService: [Function: NodeIKernelThirdPartySigService],
NodeIKernelECDHService: [Function: NodeIKernelECDHService],
NodeIKernelLoginService: [Function: NodeIKernelLoginService],
NodeIOPSafePwdEdit: [Function: NodeIOPSafePwdEdit],
NodeIQQNTWrapperSession: [Function: NodeIQQNTWrapperSession],
NodeIBatchUploadManager: [Function: NodeIBatchUploadManager],
NodeIKernelBatchUploadService: [Function: NodeIKernelBatchUploadService],
.......
最后使用更优雅的方式处理,在框架里面加代码:
if (process.platform === 'linux') {
log.info('preload.node')
const m = { exports: {} };
const preload = process.env['YUI_PRELOAD'] || resolve(__dirname, './preload.node')
process.dlopen(
m,
preload,
constants.dlopen.RTLD_NOW | constants.dlopen.RTLD_GLOBAL
)
}
后面使用框架测试,能正常登录账号,机器人工作正常。
总结
有点难度,需要知道LD_PRELOAD的使用,ida分析,以及快速识别napi函数。
现在,Linux下不用依赖官方客户端启动了,哈哈哈。
![[Linux] 分析使用nodejs启动NTQQ [Linux] 分析使用nodejs启动NTQQ](https://cdn.jysafe.cn/wp-content/uploads/2025/10/2025100508203273.png!water.jpg)

