DLL延时加载技术与资源释放

0x00 前言

诸如调用非Windows的第三方库,我们或许会使用到dll文件,而这个时候原本程序运行需要相应的dll文件才能加载启动。通过DLL延时加载技术,使用延时加载的方式编译链接可执行的文件,可以先运行程序,再在依赖的DLL正式被调用时加载DLL。通常可通过资源文件写入dll,在释放完资源后,正式调用DLL时加载执行,避免丢失缺少DLL文件,从而单exe落地文件,避免麻烦。

0x01 介绍

在使用Visual Studio的场景下,如果使用DLL延时加载,只需要对项目的链接选项进行手动设置即可。

具体步骤如下:

属性—>链接器—>输入—>延迟加载的DLL—>输入:dll名称

DLL延迟加载技术的原理,其实就是在导入表中去掉xxx.dll这一项,等到DLL被正式调用的时候,才会加载DLL文件。这样,程序在正式调用DLL之前,都是可以正常执行的。

具体利用场景可以结合资源释放,在需要单exe的条件下,通过释放DLL资源,再延迟加载DLL,从而达到更方便的使用效果。

而资源释放,其实就是把一些需要额外加载的图片、文本、DLL作为资源的形式插入到主程序内,在必要的时候进行释放,使我们的主程序只有一个exe文件,降低落地计算机磁盘前期直接被发现的风险。

简单点来理解资源释放的原理,其实就是从资源中获取文件,写入到指定文件位置。

我们可以通过右键项目增加资源文件,如果文件类型不确定,可以增加自定义类型的资源文件。

0x02 编码实现

首先类似自定义的DLL,这里就写了一个小demo,起名为hello_world.dll:

Dolman.cpp

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <iostream>
#include <cstring>
using namespace std; BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
} extern "C" __declspec(dllexport) int hello()
{
int a = 666;
return a;
}

生成即自定义DLL。

再者,主程序,设置资源放入自定义dll,并设置延时加载hello_world.dll。

say_hello.cpp

// say_hello.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
// #include <iostream>
#include<windows.h>
#include "resource.h" BOOL FreeMyResource(UINT uiResouceName, char* lpszResourceType, char* lpszSaveFileName); void GetCurrentPath(char* lpszCurrentPath, DWORD dwSize); void FreeRes_ShowError(char* pszText); // CDelayLoadDll_TestDlg 消息处理程序 void FreeRes_ShowError(char* pszText)
{
char szErr[MAX_PATH] = { 0 };
::wsprintf(szErr, "%s Error[%d]\n", pszText, ::GetLastError());
::MessageBox(NULL, szErr, "ERROR", MB_OK);
} // 释放资源
BOOL FreeMyResource(UINT uiResouceName, char* lpszResourceType, char* lpszSaveFileName)
{
HRSRC hRsrc = ::FindResource(NULL, MAKEINTRESOURCE(uiResouceName), lpszResourceType);
if (NULL == hRsrc)
{
FreeRes_ShowError("FindResource");
return FALSE;
}
DWORD dwSize = ::SizeofResource(NULL, hRsrc);
if (0 >= dwSize)
{
FreeRes_ShowError("SizeofResource");
return FALSE;
}
HGLOBAL hGlobal = ::LoadResource(NULL, hRsrc);
if (NULL == hGlobal)
{
FreeRes_ShowError("LoadResource");
return FALSE;
}
LPVOID lpVoid = ::LockResource(hGlobal);
if (NULL == lpVoid)
{
FreeRes_ShowError("LockResource");
return FALSE;
} FILE* fp = NULL;
fopen_s(&fp, lpszSaveFileName, "wb+");
if (NULL == fp)
{
FreeRes_ShowError("LockResource");
return FALSE;
}
fwrite(lpVoid, sizeof(char), dwSize, fp);
fclose(fp); return TRUE;
} // 获取当前目录
void GetCurrentPath(char* lpszCurrentPath, DWORD dwSize)
{
::GetModuleFileName(NULL, lpszCurrentPath, dwSize);
char* p = ::strrchr(lpszCurrentPath, '\\');
p[0] = '\0';
} int main()
{
typedef int (*_pHello)();
//std::cout << "Hello World!\n";
// 释放DLL
// 获取当前目录
char szCurrentPath[MAX_PATH] = { 0 };
GetCurrentPath(szCurrentPath, MAX_PATH);
// 构造路径
::lstrcat(szCurrentPath, "\\hello_world.dll");
FreeMyResource(IDR_TEST1, "test", szCurrentPath);
HINSTANCE hDll = LoadLibrary("hello_world.dll");
_pHello hello = (_pHello)GetProcAddress(hDll, "hello");
int nHello = hello();
std::cout << nHello << std::endl;
}

编译即可。

0x03 实测

打包生成主程序后,拿到另一台机器运行测试。单exe运行后,释放dll资源到当前目录(可指定别的目录,如temp临时目录下),调用导出函数成功,输出666.

0x04 关于报错

Tips:

关于如果Visual Studio 2019 出现const char *“ 类型的实参与 “LPCWSTR“ 类型的形参不兼容这类错误。

解决方法:

右击项目文件 — 单击属性 — 配置属性 — 高级 — 高级属性 — 字符集

使用 Unicode 字符集”改为“使用多字节字符集

关于如果Visual Studio 2019 出现4996类错误。

解决方法:

右击项目文件 — 单击属性 — 配置属性 — c/c++ — 语言 — 符合模式

修改符合模式为否。

0x05 参考

https://www.cnblogs.com/predator-wang/p/4956615.html

https://www.write-bug.com/article/1601.html

DLL延时加载技术与资源释放的更多相关文章

  1. [整理]DLL延时加载 && 设置进程私有环境变量

    DLL延时加载鉴于静态和动态加载之间,即无需在代码中显示加载但它内队依然是动态加载的方式只是系统帮处理了.这样做好处是: 1. 可以加快启动时间(因为它是动态加载在需要的时间加载), 2. 减小编写L ...

  2. JAVA的单例模式与延时加载

    延迟加载(lazy load)是(也称为懒加载),也叫延迟实例化,延迟初始化等,主要表达的思想就是:把对象的创建延迟到使用的时候创建,而不是对象实例化的时候创建.延迟加载机制是为了避免一些无谓的性能开 ...

  3. Image Lazy Load:那些延时加载图片的开源插件(jQuery)

    图片延时加载技术对大流量的网站来说是十分实用的.目前图片在网站中大量使用,如果不加处理的话会对服务器和带宽造成级大压力,通过只渲染当前用户可见区域的图片,可以极大地减少网站的请求数,降低网络带宽资源. ...

  4. 服务器文档下载zip格式 SQL Server SQL分页查询 C#过滤html标签 EF 延时加载与死锁 在JS方法中返回多个值的三种方法(转载) IEnumerable,ICollection,IList接口问题 不吹不擂,你想要的Python面试都在这里了【315+道题】 基于mvc三层架构和ajax技术实现最简单的文件上传 事件管理

    服务器文档下载zip格式   刚好这次项目中遇到了这个东西,就来弄一下,挺简单的,但是前台调用的时候弄错了,浪费了大半天的时间,本人也是菜鸟一枚.开始吧.(MVC的) @using Rattan.Co ...

  5. 011.Delphi插件之QPlugins,延时加载服务

    这个DEMO是是把DLL插件的相关信息做成了一个配置文件,主程序加载这个配置文件,从而起到延时加载的作用 主程序代码如下 unit Frm_Main; interface uses Winapi.Wi ...

  6. MyBatis入门(五)---延时加载、缓存

    一.创建数据库 1.1.建立数据库 /* SQLyog Enterprise v12.09 (64 bit) MySQL - 5.7.9-log : Database - mybatis ****** ...

  7. Android 高仿腾讯旗下app的 皮肤加载技术

    http://www.cnblogs.com/punkisnotdead/p/4968851.html 以前写的这篇文章 可以高仿出 知乎 新浪微博等 绝大多数app的换肤技术,但是遗漏了腾讯的效果, ...

  8. 图片延时加载jquery.inview.js用法详解

    我们在网站上总能见到这样的效果,若是有图片,图片都是先用loading加载一小段时间,然后紧接着出来要显示的图片,即效果如下: v2_loading.gif,几秒钟时间过渡到v2_pic_01_s.j ...

  9. FaceBook页面加载技术

    1. 技术背景 FaceBook页面加载技术 试想这样一个场景,一个经常访问的网站,每次打开它的页面都要要花费6 秒:同时另外一个网站提供了相似的服务,但响应时间只需3 秒,那么你会如何选择呢?数据表 ...

随机推荐

  1. 关于document.write()方法重绘页面问题

    学习的时候,document.write()被告知是用来将内容写进页面里面,同时也被告知document.write()方法会重绘页面,但是关于什么时候会重绘,什么时候不会重绘页面没有太多解释. 首先 ...

  2. DataGrid列显示隐藏配置

    1.列右键事件 private void data1_MouseRightButtonDown(object sender, MouseButtonEventArgs e) { ContextMenu ...

  3. HTML5(十二)——一文读懂 WebSocket 原理

    一.WebSocket 由来 WebSocket 是一个持久化的协议,通过第一次 HTTP Request 建立连接之后,再把通信协议升级成 websocket,保持连接状态,后续的数据交换不需要再重 ...

  4. protocol buffer的高效编码方式

    目录 简介 定义一个简单的message Base 128 Varints 消息体的结构 符号整数 字符串 嵌套的消息 总结 简介 protocol buffer这种优秀的编码方式,究竟底层是怎么工作 ...

  5. SpringBoot快速入门(一)

    本文内容 SpringBoot概述 SpringBoot快速入门 SpringBoot起步依赖原理分析 SpringBoot配置 SpringBoot整合其他框架 1.SpringBoot概述 1.1 ...

  6. Zabbix邮箱告警

    一.安装邮箱 yum install mailx 二.配置邮箱 vim /etc/mail.rc set from=875667601@qq.com set smtp=smtp.qq.com set ...

  7. Java-线程池专题 (美团)

    实现多线程的三种方式,继承Thread,实现Runnable 和 实现 Executor接口 ,具体参考:Java 多线程 三种实现方式 去美团,问到了什么是线程池,如何使用,为什么要用,以下做个总结 ...

  8. uwp 中的appservice

    在上篇里,我使用的是寄宿在WPF上的WCF进行两个程序间的通信,在解决问题的同时,我的同事也在思考能否使用UWP来做这件事.于是,我们发现了App Service,两个UWP应用沟通的桥梁. App ...

  9. NameNode&Secondary NameNode 工作机制

    NameNode&Secondary NameNode 工作机制 NameNode: 1.启动时,加载编辑日志和镜像文件到内存 2.当客户端对元数据进行增删改,请求NameNode 3.Nam ...

  10. gdb调试用命令与一般调试方法

    示例代码 1 #include <iostream> 2 using namespace std; 3 4 void Print() 5 { 6 cout<<"hel ...