本帖最后由 糖心疼 于 2023-11-23 18:14 编辑
编译和独立编译所依赖的核心库文件krnln.fnr 实在是大,很多时候开发用不到这么多命令和组件。 支持库本质也是动态链接库DLL,就想着看能否用C++重构并升级,精简掉不必要的,只保留必须的依赖。减小空程序的体积。
首先用DLL函数查看器分析krnln.fnr,得到若干DLL导出的函数。和支持库所必须的函数GetNewInf。 经过反汇编调试跟踪,发现在编译后的exe中并未调用 GetNewInf函数。反而是使用的 GetNewSock函数。 IDA调试跟踪后,代码运行流程如下: LoadLibraryA 加载krnln.fnr,加载失败:读注册表Software\\FlySky\\E\\Install 尝试从易语言 安装目录里读取krnln.fne。加载成功:获取GetNewSock函数地址。 进行函数强制转换 设置返回值int 调用方式__cdecl 传递参数1000。v5 = (void (__cdecl *)(int))((int (__stdcall *)(int))GetNewSock)(1000); 得到返回值后,执行v5(4206592); 所有关键的代码都在 GetNewSock里面。其他的应该都是易语言编译出来的软件的格式自带的代码。无需干预。
[C++] 纯文本查看 复制代码
int sub_40108E()
{
const CHAR *v0; // eax
HMODULE LibraryA; // eax
char *v2; // ebx
const CHAR *v3; // eax
FARPROC GetNewSock; // eax
void (__cdecl *v5)(int); // eax
LSTATUS v7; // [esp+4h] [ebp-114h]
DWORD cbData; // [esp+8h] [ebp-110h] BYREF
HKEY phkResult; // [esp+Ch] [ebp-10Ch] BYREF
HMODULE hLibModule; // [esp+10h] [ebp-108h]
char String1[260]; // [esp+14h] [ebp-104h] BYREF
sub_4011E4(String1);
v0 = lstrcatA(String1, "krnln.fnr");
LibraryA = LoadLibraryA(v0);
if ( LibraryA )
goto LABEL_7;
if ( !RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\FlySky\\E\\Install", 0, 0x20019u, &phkResult) )
{
cbData = 259;
v7 = RegQueryValueExA(phkResult, "Path", 0, 0, (LPBYTE)String1, &cbData);
RegCloseKey(phkResult);
if ( !v7 )
{
v2 = &String1[lstrlenA(String1) - 1];
if ( *v2 != 92 )
*(_WORD *)v2 = 92;
v3 = lstrcatA(String1, "krnln.fne");
LibraryA = LoadLibraryA(v3);
if ( LibraryA )
{
LABEL_7:
hLibModule = LibraryA;
GetNewSock = GetProcAddress(LibraryA, "GetNewSock");
if ( GetNewSock )
{
v5 = (void (__cdecl *)(int))((int (__stdcall *)(int))GetNewSock)(1000);
if ( v5 )
{
v5(4206592);
ExitProcess(0);
}
}
FreeLibrary(hLibModule);
}
}
}
MessageBoxA(0, "Not found the kernel library or the kernel library is invalid!", "Error", 0x10u);
return -1;
}
继续跟踪GetNewSock [C++] 纯文本查看 复制代码
int (__stdcall *__userpurge GetNewSock@<eax>(int a1@<ebx>, int a2))(int)
{
int (__stdcall *result)(int); // eax
switch ( a2 )
{
case 1000:
goto LABEL_4;
case 1001:
result = sub_1002E172;
break;
case 1002:
result = sub_1005D6F0;
break;
case 1003:
sub_1002FBF0(dword_1011A834 + 1);
sub_1002FBF0(a1);
LABEL_4:
result = sub_1002E172;
break;
default:
result = 0;
break;
}
return result;
}
switch中的1000直接返回了result = sub_1002E172,进入sub_1002E172
[C++] 纯文本查看 复制代码
int __stdcall sub_1002E172(int a1)
{
return sub_1002D62F(a1, 0, 0);
}
既然返回了这个函数。根据上面的代码。得处这个int a1其实是4206592
[C++] 纯文本查看 复制代码
int __thiscall sub_1002D62F(_DWORD *this, int a2, SIZE_T dwSize, LPCSTR lpString)
{
int (*v4)(void); // eax
int result; // eax
this[298] = GetProcessHeap();
++this[49];
v4 = (int (*)(void))sub_1005F880(a2, dwSize, lpString);
result = v4();
--this[49];
return result;
}
来到这一步发现他获取了默认堆栈句柄。a2就是之前的a1也就是4206592、SIZE_T dwSize =0、 LPCSTR lpString=0、this[49]进行了偏移,v4 = (int (*)(void))sub_1005F880(a2, dwSize, lpString); 又是一个里面的代码返回的函数指针。
[C++] 纯文本查看 复制代码
const CHAR *__thiscall sub_1005F880(int this, const CHAR *a2, SIZE_T dwSize, LPCSTR lpString){
}
最后这个代码就不上了。没啥意义了。761行。代码量很大。里面也有调用其他函数。大概操作就是初始化库的一些操作。不是说重构不了。只能说工作量和工期都很大,不值得投入了。
易语言这个核心库,和平时其他的支持库还是不一样的。他文档中说的是GetNewInf就够了。但实际上核心库完全就没用这个函数。反而是自己造了个函数。还搞了自己的格式。完全和易语言支持库的标准不同。
最后因代码量的问题,放弃了重构核心库的想法。但也并非没有实质性的收获,目前是可以把易语言编译的程序精简化。相当于套壳,原始文件当系统文件写入电脑。
具体的操作如下:
1、将 krnln.fnr拷贝一份,或者放在服务器上,让他可以通过http请求进行下载,或者其他的方式提供下载。
2、编写DLL,声明API GetNewSock_ 库文件名为 krnln.fnr 库中对应名称为 GetNewSock 参数为整数型 参数名随便写。推荐用C++写。C++写的不依赖 krnln.fnr 或者静态编译写。
3、自己设定一个路径,一般为电脑的临时目录,dll加载的时候或者启动子程序的时候,检测临时目录是否存在 krnln.fnr,如果存在就不管他。如果不存在就http请求下载并写到临时目录。
4、dll中写一个函数,名称为 GetNewSock 记得公开。并且注意检查声明的API 和 远程下载保存的 krnln.fnr的路径问题。可以用 置DLL装载目录切换到临时目录
都做完以后,编译DLL。将后缀改为fnr,放到易语言的lib目录中,记得备份krnln.fnr。
至此。易语言再编译出来的依赖就会是你自己编译的krnln.fnr,而不再是原本那个大体积的了。
以后这台电脑只要运行过一次软件。其他软件再运行再也不会下载。体积依然保持的是精简后的体积。‘
也是不得以的方法了。如果以后谁有功夫去重构GetNewSock,记得艾特一下。 感谢汇编提供帮助的 NULL!