[Linux] 分析使用nodejs启动NTQQ | 祭夜の咖啡馆
  • 欢迎光临,这个博客颜色有点多

[Linux] 分析使用nodejs启动NTQQ

猿之力 msojocs 1周前 (10-05) 26次浏览 已收录 0个评论 扫描二维码
文章目录[隐藏]

前言

自己有做一个机器人框架: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调用它;
[Linux] 分析使用nodejs启动NTQQ

代码如下:

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下不用依赖官方客户端启动了,哈哈哈。


祭夜の咖啡馆 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:[Linux] 分析使用nodejs启动NTQQ
喜欢 (0)
[1690127128@qq.com]
分享 (0)
发表我的评论
取消评论
OwO表情
贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址