前言
上篇实现了Linux平台使用nodejs启动的分析,这篇是Windows的,相对更麻烦了。
文章目录[隐藏] 前言 分析 总结 前言 自己有做一个机器人框架:https://github.com/pro […]
分析
一样的,在wrapper.node目录建立test.js,执行一下看看情况:
文件没找到,但是没说什么文件,抽象windows。。。。
那只能借助工具,来看程序在读取什么文件,然后失败的是什么文件。
Process Monitor设置过滤条件:进程名包含node;启动记录,然后执行一下程序。
可以看到QQNT.dll文件在当前目录没找到,然后去系统环境的目录找,一直都没找到;
继续按照这个方式,补充ffmped.dll
。
对比Linux,可以发现并没有手动补充函数的操作,这是因为两个平台的加载逻辑不一样;
Linux是类似于普通nodejs加载模块的逻辑,但是Windows不一样,下面是Windows的简图:
根据图示,Windows的node环境是在库里面的,间接调用的。
于是,可行的方法是伪造一个让程序以为是node环境的状况,让程序执行;
简单说就是伪造QQNT.dll,然后把函数调用转发给真正的node环境。
大致流程:
1. 原版QQNT.dll改名为QQNT.original.dll
2. 伪造一个QQNT.dll,直接转发调用给QQNT.original.dll
3. 修改QQNT.dll,把能转发的函数全转到node.exe
4. 尽可能脱离QQNT.original.dll(理想情况)
伪造QQNT.dll
我在尝试了aheadlib伪造后,发现这个工具不支持C++复杂符号的导出,放弃了;
转头找AI去了。。。。
导出表处理
首先,Windows加载dll的时候会严格校验导出表,如有不一样,就会报错,所以需要获取QQNT.dll的完全导出表。
使用mingw64环境的gendef获取导出定义QQNT.def;这个去网上找教程,很快。
然后,在导出表基础上做转发修改:
导出的形式: Cr_z_adler32 修改为: Cr_z_adler32=QQNT.original.dll.Cr_z_adler32 设置库的定义: set(QQNT_DEF ${PROJECT_SOURCE_DIR}/src/qqnt/qqnt.def) if(EXISTS ${QQNT_DEF}) if(MSVC) set_target_properties(QQNT PROPERTIES LINK_FLAGS "/DEF:${QQNT_DEF}") else() target_link_options(QQNT PRIVATE "-Wl,--input-def=${QQNT_DEF}") endif() else() message(FATAL_ERROR "QQNT.def not found") endif()
还需要把原导入表做链接:
execute_process(COMMAND ${CMAKE_AR} /def:QQNT.def /out:QQNT.lib /machine:x64 ${CMAKE_STATIC_LINKER_FLAGS} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/thirds/external COMMAND_ERROR_IS_FATAL ANY ) set(QQNT_LIB ${PROJECT_SOURCE_DIR}/thirds/external/QQNT.lib) if(EXISTS ${QQNT_LIB}) target_link_libraries(QQNT PRIVATE ${QQNT_LIB}) else() message(FATAL_ERROR "QQNT.lib not found") endif()
按此规则完成全部修改,编译,执行;
调整转发规则
获取wrapper.node的导入表,先把QQNT.dll中记录的函数做修改:
把QQNT.original.dll替换为node.exe,编译执行;
执行后,直接崩溃,猜测是函数交互之间底层还有交互;
于是把所有的都替换了。
根据wrapper.node的导入表检查它用到的函数哪个是node.exe没有的。
立马就找到了,IsEnvironmentStopping这个函数没有:
实现一下,然后在def替换转发目标:
// 这个函数,node没有自带 // Wrapper for mangled C++ symbol: ?IsEnvironmentStopping@node@@YA_NPEAVIsolate@v8@@@Z // Original signature: bool IsEnvironmentStopping(node::Isolate*) (approx) extern "C" __declspec(dllexport) int My_IsEnvironmentStopping_node(void* isolate) { spdlog::info("My_IsEnvironmentStopping_node called"); return 0; } // def ?IsEnvironmentStopping@node@@YA_NPEAVIsolate@v8@@@Z=My_IsEnvironmentStopping_node
总结
windows处理相比Linux复杂许多,中途报错一直没变化,快放弃了;
然后睡了一觉,想到拿一个函数少的做一下尝试(“QBar.dll”),尝试之后懂了整体流程,然后再去改的QQNT.dll这才成功的。