mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-03-01 08:10:25 +00:00
feat: 为反检测做准备
This commit is contained in:
@@ -33,6 +33,7 @@ import { NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/napcat-cor
|
|||||||
import { proxiedListenerOf } from '@/napcat-core/helper/proxy-handler';
|
import { proxiedListenerOf } from '@/napcat-core/helper/proxy-handler';
|
||||||
import { NTQQPacketApi } from './apis/packet';
|
import { NTQQPacketApi } from './apis/packet';
|
||||||
import { NativePacketHandler } from './packet/handler/client';
|
import { NativePacketHandler } from './packet/handler/client';
|
||||||
|
import { Napi2NativeLoader } from './packet/handler/napi2nativeLoader';
|
||||||
import { container, ReceiverServiceRegistry } from './packet/handler/serviceRegister';
|
import { container, ReceiverServiceRegistry } from './packet/handler/serviceRegister';
|
||||||
import { appEvent } from './packet/handler/eventList';
|
import { appEvent } from './packet/handler/eventList';
|
||||||
import { TypedEventEmitter } from './packet/handler/typeEvent';
|
import { TypedEventEmitter } from './packet/handler/typeEvent';
|
||||||
@@ -314,6 +315,7 @@ export interface InstanceContext {
|
|||||||
readonly basicInfoWrapper: QQBasicInfoWrapper;
|
readonly basicInfoWrapper: QQBasicInfoWrapper;
|
||||||
readonly pathWrapper: NapCatPathWrapper;
|
readonly pathWrapper: NapCatPathWrapper;
|
||||||
readonly packetHandler: NativePacketHandler;
|
readonly packetHandler: NativePacketHandler;
|
||||||
|
readonly napi2nativeLoader: Napi2NativeLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StableNTApiWrapper {
|
export interface StableNTApiWrapper {
|
||||||
|
|||||||
@@ -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 { LogStack } from '@/napcat-core/packet/context/clientContext';
|
||||||
import { NapCoreContext } from '@/napcat-core/packet/context/napCoreContext';
|
import { NapCoreContext } from '@/napcat-core/packet/context/napCoreContext';
|
||||||
import { PacketLogger } from '@/napcat-core/packet/context/loggerContext';
|
import { PacketLogger } from '@/napcat-core/packet/context/loggerContext';
|
||||||
import { OidbPacket, PacketBuf } from '@/napcat-core/packet/transformer/base';
|
import { OidbPacket, PacketBuf } from '@/napcat-core/packet/transformer/base';
|
||||||
|
import { Napi2NativeLoader } from '@/napcat-core/packet/handler/napi2nativeLoader';
|
||||||
export interface RecvPacket {
|
export interface RecvPacket {
|
||||||
type: string, // 仅recv
|
type: string, // 仅recv
|
||||||
data: RecvPacketData;
|
data: RecvPacketData;
|
||||||
@@ -17,50 +14,38 @@ export interface RecvPacketData {
|
|||||||
data: Buffer;
|
data: Buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0 send 1 recv
|
|
||||||
export interface NativePacketExportType {
|
|
||||||
initHook?: (send: string, recv: string) => boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class NativePacketClient {
|
export class NativePacketClient {
|
||||||
protected readonly napcore: NapCoreContext;
|
protected readonly napcore: NapCoreContext;
|
||||||
protected readonly logger: PacketLogger;
|
protected readonly logger: PacketLogger;
|
||||||
protected readonly cb = new Map<string, (json: RecvPacketData) => Promise<any> | any>(); // hash-type callback
|
protected readonly cb = new Map<string, (json: RecvPacketData) => Promise<any> | any>(); // hash-type callback
|
||||||
|
protected readonly napi2nativeLoader: Napi2NativeLoader;
|
||||||
logStack: LogStack;
|
logStack: LogStack;
|
||||||
available: boolean = false;
|
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.napcore = napCore;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.logStack = logStack;
|
this.logStack = logStack;
|
||||||
|
this.napi2nativeLoader = napi2nativeLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
check (): boolean {
|
check (): boolean {
|
||||||
const platform = process.platform + '.' + process.arch;
|
if (!this.napi2nativeLoader.loaded) {
|
||||||
if (!this.supportedPlatforms.includes(platform)) {
|
this.logStack.pushLogWarn('NativePacketClient: Napi2NativeLoader 未成功加载');
|
||||||
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}`);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async init (_pid: number, recv: string, send: string): Promise<void> {
|
async init (_pid: number, recv: string, send: string): Promise<void> {
|
||||||
const platform = process.platform + '.' + process.arch;
|
|
||||||
const isNewQQ = this.napcore.basicInfo.requireMinNTQQBuild('40824');
|
const isNewQQ = this.napcore.basicInfo.requireMinNTQQBuild('40824');
|
||||||
if (isNewQQ) {
|
if (isNewQQ) {
|
||||||
const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './native/napi2native/napi2native.' + platform + '.node');
|
const success = this.napi2nativeLoader.initHook(send, recv);
|
||||||
process.dlopen(this.MoeHooExport, moehoo_path, constants.dlopen.RTLD_LAZY);
|
if (success) {
|
||||||
this.MoeHooExport?.exports.initHook?.(send, recv);
|
|
||||||
this.available = true;
|
this.available = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async sendPacket (
|
async sendPacket (
|
||||||
cmd: string,
|
cmd: string,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { NativePacketClient } from '@/napcat-core/packet/client/nativeClient';
|
|||||||
import { OidbPacket } from '@/napcat-core/packet/transformer/base';
|
import { OidbPacket } from '@/napcat-core/packet/transformer/base';
|
||||||
import { PacketLogger } from '@/napcat-core/packet/context/loggerContext';
|
import { PacketLogger } from '@/napcat-core/packet/context/loggerContext';
|
||||||
import { NapCoreContext } from '@/napcat-core/packet/context/napCoreContext';
|
import { NapCoreContext } from '@/napcat-core/packet/context/napCoreContext';
|
||||||
|
import { Napi2NativeLoader } from '@/napcat-core/packet/handler/napi2nativeLoader';
|
||||||
|
|
||||||
export class LogStack {
|
export class LogStack {
|
||||||
private stack: string[] = [];
|
private stack: string[] = [];
|
||||||
@@ -43,12 +44,14 @@ export class PacketClientContext {
|
|||||||
private readonly napCore: NapCoreContext;
|
private readonly napCore: NapCoreContext;
|
||||||
private readonly logger: PacketLogger;
|
private readonly logger: PacketLogger;
|
||||||
private readonly logStack: LogStack;
|
private readonly logStack: LogStack;
|
||||||
|
private readonly napi2nativeLoader: Napi2NativeLoader;
|
||||||
private readonly _client: NativePacketClient;
|
private readonly _client: NativePacketClient;
|
||||||
|
|
||||||
constructor (napCore: NapCoreContext, logger: PacketLogger) {
|
constructor (napCore: NapCoreContext, logger: PacketLogger, napi2nativeLoader: Napi2NativeLoader) {
|
||||||
this.napCore = napCore;
|
this.napCore = napCore;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.logStack = new LogStack(logger);
|
this.logStack = new LogStack(logger);
|
||||||
|
this.napi2nativeLoader = napi2nativeLoader;
|
||||||
this._client = this.newClient();
|
this._client = this.newClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +74,7 @@ export class PacketClientContext {
|
|||||||
|
|
||||||
private newClient (): NativePacketClient {
|
private newClient (): NativePacketClient {
|
||||||
this.logger.info('使用 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()) {
|
if (!client.check()) {
|
||||||
throw new Error('[Core] [Packet] NativePacketClient 不可用,NapCat.Packet将不会加载!');
|
throw new Error('[Core] [Packet] NativePacketClient 不可用,NapCat.Packet将不会加载!');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,5 +34,9 @@ export class NapCoreContext {
|
|||||||
return this.core.configLoader.configData;
|
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);
|
sendSsoCmdReqByContend = (cmd: string, data: Buffer) => this.core.context.session.getMsgService().sendSsoCmdReqByContend(cmd, data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export class PacketContext {
|
|||||||
this.msgConverter = new PacketMsgConverter();
|
this.msgConverter = new PacketMsgConverter();
|
||||||
this.napcore = new NapCoreContext(core);
|
this.napcore = new NapCoreContext(core);
|
||||||
this.logger = new PacketLogger(this.napcore);
|
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.highway = new PacketHighwayContext(this.napcore, this.logger, this.client);
|
||||||
this.operation = new PacketOperationContext(this);
|
this.operation = new PacketOperationContext(this);
|
||||||
}
|
}
|
||||||
|
|||||||
79
packages/napcat-core/packet/handler/napi2nativeLoader.ts
Normal file
79
packages/napcat-core/packet/handler/napi2nativeLoader.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import { NapCatPathWrapper } from 'napcat-common/src/path';
|
|||||||
import { InitWebUi, WebUiConfig, webUiRuntimePort } from 'napcat-webui-backend/index';
|
import { InitWebUi, WebUiConfig, webUiRuntimePort } from 'napcat-webui-backend/index';
|
||||||
import { NapCatAdapterManager } from 'napcat-adapter';
|
import { NapCatAdapterManager } from 'napcat-adapter';
|
||||||
import { NativePacketHandler } from 'napcat-core/packet/handler/client';
|
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 { FFmpegService } from 'napcat-core/helper/ffmpeg/ffmpeg';
|
||||||
import { logSubscription, LogWrapper } from 'napcat-core/helper/log';
|
import { logSubscription, LogWrapper } from 'napcat-core/helper/log';
|
||||||
import { QQBasicInfoWrapper } from '@/napcat-core/helper/qq-basic-info';
|
import { QQBasicInfoWrapper } from '@/napcat-core/helper/qq-basic-info';
|
||||||
@@ -40,6 +41,7 @@ export async function NCoreInitFramework (
|
|||||||
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
|
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
|
||||||
const wrapper = loadQQWrapper(basicInfoWrapper.QQMainPath, basicInfoWrapper.getFullQQVersion());
|
const wrapper = loadQQWrapper(basicInfoWrapper.QQMainPath, basicInfoWrapper.getFullQQVersion());
|
||||||
const nativePacketHandler = new NativePacketHandler({ logger }); // 初始化 NativePacketHandler 用于后续使用
|
const nativePacketHandler = new NativePacketHandler({ logger }); // 初始化 NativePacketHandler 用于后续使用
|
||||||
|
const napi2nativeLoader = new Napi2NativeLoader({ logger }); // 初始化 Napi2NativeLoader 用于后续使用
|
||||||
// nativePacketHandler.onAll((packet) => {
|
// nativePacketHandler.onAll((packet) => {
|
||||||
// console.log('[Packet]', packet.uin, packet.cmd, packet.hex_data);
|
// console.log('[Packet]', packet.uin, packet.cmd, packet.hex_data);
|
||||||
// });
|
// });
|
||||||
@@ -73,7 +75,7 @@ export async function NCoreInitFramework (
|
|||||||
// 过早进入会导致addKernelMsgListener等Listener添加失败
|
// 过早进入会导致addKernelMsgListener等Listener添加失败
|
||||||
// await sleep(2500);
|
// await sleep(2500);
|
||||||
// 初始化 NapCatFramework
|
// 初始化 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();
|
await loaderObject.core.initCore();
|
||||||
|
|
||||||
// 启动WebUi
|
// 启动WebUi
|
||||||
@@ -101,10 +103,12 @@ export class NapCatFramework {
|
|||||||
selfInfo: SelfInfo,
|
selfInfo: SelfInfo,
|
||||||
basicInfoWrapper: QQBasicInfoWrapper,
|
basicInfoWrapper: QQBasicInfoWrapper,
|
||||||
pathWrapper: NapCatPathWrapper,
|
pathWrapper: NapCatPathWrapper,
|
||||||
packetHandler: NativePacketHandler
|
packetHandler: NativePacketHandler,
|
||||||
|
napi2nativeLoader: Napi2NativeLoader
|
||||||
) {
|
) {
|
||||||
this.context = {
|
this.context = {
|
||||||
packetHandler,
|
packetHandler,
|
||||||
|
napi2nativeLoader,
|
||||||
workingEnv: NapCatCoreWorkingEnv.Framework,
|
workingEnv: NapCatCoreWorkingEnv.Framework,
|
||||||
wrapper,
|
wrapper,
|
||||||
session,
|
session,
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import { NodeIO3MiscListener } from 'napcat-core/listeners/NodeIO3MiscListener';
|
|||||||
import { sleep } from 'napcat-common/src/helper';
|
import { sleep } from 'napcat-common/src/helper';
|
||||||
import { FFmpegService } from '@/napcat-core/helper/ffmpeg/ffmpeg';
|
import { FFmpegService } from '@/napcat-core/helper/ffmpeg/ffmpeg';
|
||||||
import { NativePacketHandler } from 'napcat-core/packet/handler/client';
|
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 { logSubscription, LogWrapper } from '@/napcat-core/helper/log';
|
||||||
import { proxiedListenerOf } from '@/napcat-core/helper/proxy-handler';
|
import { proxiedListenerOf } from '@/napcat-core/helper/proxy-handler';
|
||||||
import { QQBasicInfoWrapper } from '@/napcat-core/helper/qq-basic-info';
|
import { QQBasicInfoWrapper } from '@/napcat-core/helper/qq-basic-info';
|
||||||
@@ -396,6 +397,7 @@ export async function NCoreInitShell () {
|
|||||||
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
|
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
|
||||||
const wrapper = loadQQWrapper(basicInfoWrapper.QQMainPath, basicInfoWrapper.getFullQQVersion());
|
const wrapper = loadQQWrapper(basicInfoWrapper.QQMainPath, basicInfoWrapper.getFullQQVersion());
|
||||||
const nativePacketHandler = new NativePacketHandler({ logger }); // 初始化 NativePacketHandler 用于后续使用
|
const nativePacketHandler = new NativePacketHandler({ logger }); // 初始化 NativePacketHandler 用于后续使用
|
||||||
|
const napi2nativeLoader = new Napi2NativeLoader({ logger }); // 初始化 Napi2NativeLoader 用于后续使用
|
||||||
|
|
||||||
// nativePacketHandler.onAll((packet) => {
|
// nativePacketHandler.onAll((packet) => {
|
||||||
// console.log('[Packet]', packet.uin, packet.cmd, packet.hex_data);
|
// console.log('[Packet]', packet.uin, packet.cmd, packet.hex_data);
|
||||||
@@ -488,7 +490,8 @@ export async function NCoreInitShell () {
|
|||||||
selfInfo,
|
selfInfo,
|
||||||
basicInfoWrapper,
|
basicInfoWrapper,
|
||||||
pathWrapper,
|
pathWrapper,
|
||||||
nativePacketHandler
|
nativePacketHandler,
|
||||||
|
napi2nativeLoader
|
||||||
).InitNapCat();
|
).InitNapCat();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,10 +506,12 @@ export class NapCatShell {
|
|||||||
selfInfo: SelfInfo,
|
selfInfo: SelfInfo,
|
||||||
basicInfoWrapper: QQBasicInfoWrapper,
|
basicInfoWrapper: QQBasicInfoWrapper,
|
||||||
pathWrapper: NapCatPathWrapper,
|
pathWrapper: NapCatPathWrapper,
|
||||||
packetHandler: NativePacketHandler
|
packetHandler: NativePacketHandler,
|
||||||
|
napi2nativeLoader: Napi2NativeLoader
|
||||||
) {
|
) {
|
||||||
this.context = {
|
this.context = {
|
||||||
packetHandler,
|
packetHandler,
|
||||||
|
napi2nativeLoader,
|
||||||
workingEnv: NapCatCoreWorkingEnv.Shell,
|
workingEnv: NapCatCoreWorkingEnv.Shell,
|
||||||
wrapper,
|
wrapper,
|
||||||
session,
|
session,
|
||||||
|
|||||||
Reference in New Issue
Block a user