滴水逆向-静态链接库与动态链接库
相关测试代码
代码复用的实现: 1.静态链接库 一,创建静态链接库: (1)在VC6中创建项目:Win32 Static Library (2)在项目中创建两个文件:cntftools.h 和 cntftools.cpp; 这里创建两个文件就是上面操作完成之后,直接新建一个class即可生成这两文件; cntftools.h文件: #if !defined(AFX_TEST_H__DB32E837_3E66_4BE7_B873_C079BC621AF0__INCLUDED_) #define AFX_TEST_H__DB32E837_3E66_4BE7_B873_C079BC621AF0__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 int Plus(int x, int y); int Sub(int x, int y); int Mul(int x, int y); int Div(int x, int y); #endif cntftools.cpp文件: int Plus(int x, int y) { return x+y; } int Sub(int x, int y) { return x-y; } int Mul(int x, int y) { return x*y; } int Div(int x, int y) { return x/y; } 3.编译 --- 点击编译即可,不需要执行,调试等其他操作; 二,使用静态链接库: 方式一: (1)将上面编译完成之后,生成的cntftools.h 和 cntflibs.lib复制到要使用的项目中,这里放的位置是生成项目的文件夹,不是debug 文件夹里面; (2)在需要使用的文件中包含:#include "cntftools.h" (3)在需要使用的文件中包含:#pragma comment(lib, "cntflibs.lib") (4)下面是重新生成的一个项目文件,然后放入上面的头文件和lib文件,编译成功可正常执行,下面是对应的代码 // sjlx.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <stdlib.h> #include "cntftools.h" #pragma comment(lib,"cntflibs.lib") int main() { int x = Plus(2,3); printf("%d\r\n",x); system("pause"); return 0; } 方式二: (1)将上面编译完成之后,生成的cntftools.h 和 cntflibs.lib复制到要使用的项目中,这里放的位置是生成项目的文件夹,不是debug 文件夹里面; (2)在需要使用的文件中包含:#include "cntftools.h"; (3)在项目名称中右键-->设置-Link-->找到Object/library Module 在最后面空格一下,添加cntflibs.lib;下面是最终的结果; kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib、 advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib cntflibs.lib (4)再次编译执行,可成功运行; 三,静态链接库的缺点: (1)使用静态链接生成的可执行文件体积较大,造成浪费; (2)我们常用的printf、memcpy、strcpy等就来自这种静态库 ; (3)最重要的原因就是因为生成的函数对应代码是在变成成功之后的exe里面,所以如果要更改lib的话,很麻烦,需要; 需要再次重新编译生成exe文件;验证这一点,可以通过执行此函数的过程中查看反汇编代码;发现生成的反汇编代码; 其对应的内存地址是带入了ImageBase:0x00400000; 2.动态链接库 一,创建DLL (1)在VC6中创建项目:Win32 Dynamic-Link Library ,名称写dtljkcntf (2)再在项目中创建两个文件:mydll.h 和 mydll.cpp; 这里创建两个文件就是,直接新建一个class即可生成这两文件 1.源文件中 int __stdcall Plus(int x,int y) { return x+y; } int __stdcall Sub(int x,int y) { return x-y; } int __stdcall Mul(int x,int y) { return x*y; } int __stdcall Div(int x,int y) { return x/y; } 2.头文件中 extern "C" _declspec(dllexport) __stdcall int Plus (int x,int y); extern "C" _declspec(dllexport) __stdcall int Sub (int x,int y); extern "C" _declspec(dllexport) __stdcall int Mul (int x,int y); extern "C" _declspec(dllexport) __stdcall int Div (int x,int y); 说明: 1.extern 表示这是个全局函数,可以供各个其他的函数调用; 2."C" 按照C语言的方式进行编译、链接 __declspec(dllexport)告诉编译器此函数为导出函数; 二,使用DLL 方式一:隐式连接 步骤1:将 *.dll *.lib 放到工程目录下面 步骤2:将 #pragma comment(lib,"DLL名.lib") 添加到调用文件中 步骤3:加入函数的声明 --这里就是加在项目对应的主程序代码里面,我这里测试就在main函数入口的代码上面; extern "C" __declspec(dllimport) __stdcall int Plus (int x,int y); extern "C" __declspec(dllimport) __stdcall int Sub (int x,int y); extern "C" __declspec(dllimport) __stdcall int Mul (int x,int y); extern "C" __declspec(dllimport) __stdcall int Div (int x,int y); 说明: __declspec(dllimport)告诉编译器此函数为导入函数; 下面是成功执行的主测试代码; #include "stdafx.h" #include <stdlib.h> __declspec(dllimport) int Plus (int x,int y); __declspec(dllimport) int Sub (int x,int y); __declspec(dllimport) int Mul (int x,int y); __declspec(dllimport) int Div (int x,int y); #pragma comment(lib,"dtljkcntf.lib") int main() { int x = Plus(2,3); printf("%d\r\n",x); system("pause"); return 0; } 上面需要注意的地方: 因为我测试编译生成dll和lib文件之前写的代码是没有带入extern "C" 和__stdcall这两个关键字; 当然上面是为了测试,实际情况需要带入是最好的; 测试结果是告诉我们,如果不带入上面两个关键字,那么编译生成的函数名称编译器会给我们重新命名; 简单理解操作就会给添加一些奇怪的字符,目的就是为了防止函数重名,因为在C++里面有重载的说法; 如果函数重名会导致其他异常情况; 所以总结一下:在生成dll和lib之前我们写的什么关键字代码,那么这里就要写什么; 查看dll文件显示函数信息: 使用工具可以是微软VC6.0 ++ 自带的Dependency进行查看; 也可以用OD查看,点击按钮"E"; 使用OD查看的时候,记得要要把上面生成好的dtljkcntf.dll文件放在; 可执行程序exe的相同目录下测试查看验证; 下面是带入extern "C" 和__stdcall关键字的操作; #include "stdafx.h" #include <stdlib.h> extern "C" __declspec(dllimport) __stdcall int Plus (int x,int y); extern "C" __declspec(dllimport) __stdcall int Sub (int x,int y); extern "C" __declspec(dllimport) __stdcall int Mul (int x,int y); extern "C" __declspec(dllimport) __stdcall int Div (int x,int y); #pragma comment(lib,"dtljkcntf.lib") int main() { int x = Plus(998,663); printf("%d\r\n",x); system("pause"); return 0; } 方式二:显示链接 步骤1: //定义函数指针 typedef int (__stdcall *lpPlus)(int,int); typedef int (__stdcall *lpSub)(int,int); typedef int (__stdcall *lpMul)(int,int); typedef int (__stdcall *lpDiv)(int,int); 步骤2: //声明函数指针变量 lpPlus myPlus; lpSub mySub; lpMul myMul; lpDiv myDiv; 步骤3: // //动态加载dll到内存中 HINSTANCE hModule = LoadLibrary("DllDemo.dll"); 步骤4: //获取函数地址 myPlus = (lpPlus)GetProcAddress(hModule, "_Plus@8"); mySub = (lpSub)GetProcAddress(hModule, "_Sub@8"); myMul = (lpMul)GetProcAddress(hModule, "_Mul@8"); myDiv = (lpDiv)GetProcAddress(hModule, "_Div@8"); 步骤5: //调用函数 int a = myPlus(10,2); int b = mySub(10,2); int c = myMul(10,2); int d = myDiv(10,2); 上述操作完成之后,对应的可执行代码如下: #include "stdafx.h" #include <stdlib.h> #include <windows.h> //#include <string.h> //定义函数指针; typedef int (__stdcall *lpPlus)(int,int); typedef int (__stdcall *lpSub)(int,int); typedef int (__stdcall *lpMul)(int,int); typedef int (__stdcall *lpDiv)(int,int); //上面的*lpPlus *lpSub *lpMul *lpDiv都是指针类型; int main() { //在main函数里面声明函数指针变量; lpPlus myPlus; lpSub mySub; lpMul myMul; lpDiv myDiv; //动态加载dll到内存中; HINSTANCE hModule = LoadLibrary("dtljkcntf.dll"); //获取函数地址; myPlus = (lpPlus)GetProcAddress(hModule, "_Plus@8"); mySub = (lpSub)GetProcAddress(hModule, "_Sub@8"); myMul = (lpMul)GetProcAddress(hModule, "_Mul@8"); myDiv = (lpDiv)GetProcAddress(hModule, "_Div@8"); int x = myPlus(998,3); printf("%d\r\n",x); system("pause"); return 0; } 特别说明: Handle 是代表系统的内核对象,如文件句柄,线程句柄,进程句柄。 HMODULE 是代表应用程序载入的模块 HINSTANCE 在win32下与HMODULE是相同的东西 Win16 遗留 HWND 是窗口句柄 其实就是一个无符号整型,Windows之所以这样设计有2个目的: (1)可读性更好 (2)避免在无意中进行运算 3.使用.def导出 下面两步操作之前,先要新建一个动态链接库的文件模板,这里操作跟上面一样,为这里新建的名称是dtljkdef; 然后生成下面.h和.cpp这两个后缀的名字就是新建class操作即可; (1)dtdef.h文件 int Plus (int x,int y); int Sub (int x,int y); int Mul (int x,int y); int Div (int x,int y); (2)dtdef.cpp文件 int Plus(int x,int y) { return x+y; } int Sub(int x,int y) { return x-y; } int Mul(int x,int y) { return x*y; } int Div(int x,int y) { return x/y; } (3)cntfdefs.def文件 ---> 这一步操作就是新建一个文本文件,我这的名称是cntfdefs.def(New - Text File)写入下面文件; 需要注意:这里新建这个文本的时候,要带上后再名称def,然后要勾选上面的添加到项目的复选框,否则编译没问题使用,有问题; EXPORTS Plus @12 Sub @15 NONAME Mul @13 NONAME Div @16 上述的NONAME就是隐藏名字的意思,为这里隐藏了减法和乘法 (4)编译;编译完成之后复制dll文件到需要使用此dll文件的项目文件夹里面即可; (4)使用序号导出的好处: 名字是一段程序就精华的注释,通过名字可以直接猜测到函数的功能 通过使用序号,可以达到隐藏的目的.
动态链接库dll测试效果
将生成的dll和lib文件拷贝到新建的一个项目里面
使用显示链接的方式调用dll
dll内容
迷茫的人生,需要不断努力,才能看清远方模糊的志向!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2020-09-17 Vulnhub-靶机-SYMFONOS: 2
2020-09-17 sqlilab-Less-5-8 测试writeup