feat: 为反检测做准备

This commit is contained in:
手瓜一十雪
2026-02-13 14:28:25 +08:00
parent d622178b25
commit 62c9246368
8 changed files with 114 additions and 32 deletions

View File

@@ -33,6 +33,7 @@ import { NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/napcat-cor
import { proxiedListenerOf } from '@/napcat-core/helper/proxy-handler';
import { NTQQPacketApi } from './apis/packet';
import { NativePacketHandler } from './packet/handler/client';
import { Napi2NativeLoader } from './packet/handler/napi2nativeLoader';
import { container, ReceiverServiceRegistry } from './packet/handler/serviceRegister';
import { appEvent } from './packet/handler/eventList';
import { TypedEventEmitter } from './packet/handler/typeEvent';
@@ -314,6 +315,7 @@ export interface InstanceContext {
readonly basicInfoWrapper: QQBasicInfoWrapper;
readonly pathWrapper: NapCatPathWrapper;
readonly packetHandler: NativePacketHandler;
readonly napi2nativeLoader: Napi2NativeLoader;
}
export interface StableNTApiWrapper {

View File

@@ -1,11 +1,8 @@
import path, { dirname } from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
import { constants } from 'node:os';
import { LogStack } from '@/napcat-core/packet/context/clientContext';
import { NapCoreContext } from '@/napcat-core/packet/context/napCoreContext';
import { PacketLogger } from '@/napcat-core/packet/context/loggerContext';
import { OidbPacket, PacketBuf } from '@/napcat-core/packet/transformer/base';
import { Napi2NativeLoader } from '@/napcat-core/packet/handler/napi2nativeLoader';
export interface RecvPacket {
type: string, // 仅recv
data: RecvPacketData;
@@ -17,48 +14,36 @@ export interface RecvPacketData {
data: Buffer;
}
// 0 send 1 recv
export interface NativePacketExportType {
initHook?: (send: string, recv: string) => boolean;
}
export class NativePacketClient {
protected readonly napcore: NapCoreContext;
protected readonly logger: PacketLogger;
protected readonly cb = new Map<string, (json: RecvPacketData) => Promise<any> | any>(); // hash-type callback
protected readonly napi2nativeLoader: Napi2NativeLoader;
logStack: LogStack;
available: boolean = false;
private readonly supportedPlatforms = ['win32.x64', 'linux.x64', 'linux.arm64', 'darwin.x64', 'darwin.arm64'];
private readonly MoeHooExport: { exports: NativePacketExportType; } = { exports: {} };
constructor (napCore: NapCoreContext, logger: PacketLogger, logStack: LogStack) {
constructor (napCore: NapCoreContext, logger: PacketLogger, logStack: LogStack, napi2nativeLoader: Napi2NativeLoader) {
this.napcore = napCore;
this.logger = logger;
this.logStack = logStack;
this.napi2nativeLoader = napi2nativeLoader;
}
check (): boolean {
const platform = process.platform + '.' + process.arch;
if (!this.supportedPlatforms.includes(platform)) {
this.logStack.pushLogWarn(`NativePacketClient: 不支持的平台: ${platform}`);
return false;
}
const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './native/napi2native/napi2native.' + platform + '.node');
if (!fs.existsSync(moehoo_path)) {
this.logStack.pushLogWarn(`NativePacketClient: 缺失运行时文件: ${moehoo_path}`);
if (!this.napi2nativeLoader.loaded) {
this.logStack.pushLogWarn('NativePacketClient: Napi2NativeLoader 未成功加载');
return false;
}
return true;
}
async init (_pid: number, recv: string, send: string): Promise<void> {
const platform = process.platform + '.' + process.arch;
const isNewQQ = this.napcore.basicInfo.requireMinNTQQBuild('40824');
if (isNewQQ) {
const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './native/napi2native/napi2native.' + platform + '.node');
process.dlopen(this.MoeHooExport, moehoo_path, constants.dlopen.RTLD_LAZY);
this.MoeHooExport?.exports.initHook?.(send, recv);
this.available = true;
const success = this.napi2nativeLoader.initHook(send, recv);
if (success) {
this.available = true;
}
}
}

View File

@@ -2,6 +2,7 @@ import { NativePacketClient } from '@/napcat-core/packet/client/nativeClient';
import { OidbPacket } from '@/napcat-core/packet/transformer/base';
import { PacketLogger } from '@/napcat-core/packet/context/loggerContext';
import { NapCoreContext } from '@/napcat-core/packet/context/napCoreContext';
import { Napi2NativeLoader } from '@/napcat-core/packet/handler/napi2nativeLoader';
export class LogStack {
private stack: string[] = [];
@@ -43,12 +44,14 @@ export class PacketClientContext {
private readonly napCore: NapCoreContext;
private readonly logger: PacketLogger;
private readonly logStack: LogStack;
private readonly napi2nativeLoader: Napi2NativeLoader;
private readonly _client: NativePacketClient;
constructor (napCore: NapCoreContext, logger: PacketLogger) {
constructor (napCore: NapCoreContext, logger: PacketLogger, napi2nativeLoader: Napi2NativeLoader) {
this.napCore = napCore;
this.logger = logger;
this.logStack = new LogStack(logger);
this.napi2nativeLoader = napi2nativeLoader;
this._client = this.newClient();
}
@@ -71,7 +74,7 @@ export class PacketClientContext {
private newClient (): NativePacketClient {
this.logger.info('使用 NativePacketClient 作为后端');
const client = new NativePacketClient(this.napCore, this.logger, this.logStack);
const client = new NativePacketClient(this.napCore, this.logger, this.logStack, this.napi2nativeLoader);
if (!client.check()) {
throw new Error('[Core] [Packet] NativePacketClient 不可用NapCat.Packet将不会加载');
}

View File

@@ -34,5 +34,9 @@ export class NapCoreContext {
return this.core.configLoader.configData;
}
get napi2nativeLoader () {
return this.core.context.napi2nativeLoader;
}
sendSsoCmdReqByContend = (cmd: string, data: Buffer) => this.core.context.session.getMsgService().sendSsoCmdReqByContend(cmd, data);
}

View File

@@ -18,7 +18,7 @@ export class PacketContext {
this.msgConverter = new PacketMsgConverter();
this.napcore = new NapCoreContext(core);
this.logger = new PacketLogger(this.napcore);
this.client = new PacketClientContext(this.napcore, this.logger);
this.client = new PacketClientContext(this.napcore, this.logger, this.napcore.napi2nativeLoader);
this.highway = new PacketHighwayContext(this.napcore, this.logger, this.client);
this.operation = new PacketOperationContext(this);
}

View File

@@ -0,0 +1,79 @@
import path, { dirname } from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
import { constants } from 'node:os';
import { LogWrapper } from '../../helper/log';
export interface Napi2NativeExportType {
initHook?: (send: string, recv: string) => boolean;
}
export class Napi2NativeLoader {
private readonly supportedPlatforms = ['win32.x64', 'linux.x64', 'linux.arm64', 'darwin.x64', 'darwin.arm64'];
private readonly exports: { exports: Napi2NativeExportType; } = { exports: {} };
protected readonly logger: LogWrapper;
private _loaded: boolean = false;
constructor ({ logger }: { logger: LogWrapper; }) {
this.logger = logger;
this.load();
}
private load (): void {
const platform = process.platform + '.' + process.arch;
if (!this.supportedPlatforms.includes(platform)) {
this.logger.logWarn(`Napi2NativeLoader: 不支持的平台: ${platform}`);
this._loaded = false;
return;
}
const nativeModulePath = path.join(
dirname(fileURLToPath(import.meta.url)),
'./native/napi2native/napi2native.' + platform + '.node'
);
if (!fs.existsSync(nativeModulePath)) {
this.logger.logWarn(`Napi2NativeLoader: 缺失运行时文件: ${nativeModulePath}`);
this._loaded = false;
return;
}
try {
process.dlopen(this.exports, nativeModulePath, constants.dlopen.RTLD_LAZY);
this._loaded = true;
this.logger.log('[Napi2NativeLoader] 加载成功');
} catch (error) {
this.logger.logError('Napi2NativeLoader 加载出错:', error);
this._loaded = false;
}
}
get loaded (): boolean {
return this._loaded;
}
get nativeExports (): Napi2NativeExportType {
return this.exports.exports;
}
/**
* 初始化 Hook
* @param send send 偏移地址
* @param recv recv 偏移地址
* @returns 是否初始化成功
*/
initHook (send: string, recv: string): boolean {
if (!this._loaded) {
this.logger.logWarn('Napi2NativeLoader 未成功加载,无法初始化 Hook');
return false;
}
try {
return this.nativeExports.initHook?.(send, recv) ?? false;
} catch (error) {
this.logger.logError('Napi2NativeLoader initHook 出错:', error);
return false;
}
}
}

View File

@@ -2,6 +2,7 @@ import { NapCatPathWrapper } from 'napcat-common/src/path';
import { InitWebUi, WebUiConfig, webUiRuntimePort } from 'napcat-webui-backend/index';
import { NapCatAdapterManager } from 'napcat-adapter';
import { NativePacketHandler } from 'napcat-core/packet/handler/client';
import { Napi2NativeLoader } from 'napcat-core/packet/handler/napi2nativeLoader';
import { FFmpegService } from 'napcat-core/helper/ffmpeg/ffmpeg';
import { logSubscription, LogWrapper } from 'napcat-core/helper/log';
import { QQBasicInfoWrapper } from '@/napcat-core/helper/qq-basic-info';
@@ -40,6 +41,7 @@ export async function NCoreInitFramework (
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
const wrapper = loadQQWrapper(basicInfoWrapper.QQMainPath, basicInfoWrapper.getFullQQVersion());
const nativePacketHandler = new NativePacketHandler({ logger }); // 初始化 NativePacketHandler 用于后续使用
const napi2nativeLoader = new Napi2NativeLoader({ logger }); // 初始化 Napi2NativeLoader 用于后续使用
// nativePacketHandler.onAll((packet) => {
// console.log('[Packet]', packet.uin, packet.cmd, packet.hex_data);
// });
@@ -73,7 +75,7 @@ export async function NCoreInitFramework (
// 过早进入会导致addKernelMsgListener等Listener添加失败
// await sleep(2500);
// 初始化 NapCatFramework
const loaderObject = new NapCatFramework(wrapper, session, logger, selfInfo, basicInfoWrapper, pathWrapper, nativePacketHandler);
const loaderObject = new NapCatFramework(wrapper, session, logger, selfInfo, basicInfoWrapper, pathWrapper, nativePacketHandler, napi2nativeLoader);
await loaderObject.core.initCore();
// 启动WebUi
@@ -101,10 +103,12 @@ export class NapCatFramework {
selfInfo: SelfInfo,
basicInfoWrapper: QQBasicInfoWrapper,
pathWrapper: NapCatPathWrapper,
packetHandler: NativePacketHandler
packetHandler: NativePacketHandler,
napi2nativeLoader: Napi2NativeLoader
) {
this.context = {
packetHandler,
napi2nativeLoader,
workingEnv: NapCatCoreWorkingEnv.Framework,
wrapper,
session,

View File

@@ -30,6 +30,7 @@ import { NodeIO3MiscListener } from 'napcat-core/listeners/NodeIO3MiscListener';
import { sleep } from 'napcat-common/src/helper';
import { FFmpegService } from '@/napcat-core/helper/ffmpeg/ffmpeg';
import { NativePacketHandler } from 'napcat-core/packet/handler/client';
import { Napi2NativeLoader } from 'napcat-core/packet/handler/napi2nativeLoader';
import { logSubscription, LogWrapper } from '@/napcat-core/helper/log';
import { proxiedListenerOf } from '@/napcat-core/helper/proxy-handler';
import { QQBasicInfoWrapper } from '@/napcat-core/helper/qq-basic-info';
@@ -396,6 +397,7 @@ export async function NCoreInitShell () {
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
const wrapper = loadQQWrapper(basicInfoWrapper.QQMainPath, basicInfoWrapper.getFullQQVersion());
const nativePacketHandler = new NativePacketHandler({ logger }); // 初始化 NativePacketHandler 用于后续使用
const napi2nativeLoader = new Napi2NativeLoader({ logger }); // 初始化 Napi2NativeLoader 用于后续使用
// nativePacketHandler.onAll((packet) => {
// console.log('[Packet]', packet.uin, packet.cmd, packet.hex_data);
@@ -488,7 +490,8 @@ export async function NCoreInitShell () {
selfInfo,
basicInfoWrapper,
pathWrapper,
nativePacketHandler
nativePacketHandler,
napi2nativeLoader
).InitNapCat();
}
@@ -503,10 +506,12 @@ export class NapCatShell {
selfInfo: SelfInfo,
basicInfoWrapper: QQBasicInfoWrapper,
pathWrapper: NapCatPathWrapper,
packetHandler: NativePacketHandler
packetHandler: NativePacketHandler,
napi2nativeLoader: Napi2NativeLoader
) {
this.context = {
packetHandler,
napi2nativeLoader,
workingEnv: NapCatCoreWorkingEnv.Shell,
wrapper,
session,