天天看點

滴水逆向-靜态連結庫與動态連結庫

滴水逆向-靜态連結庫與動态連結庫
滴水逆向-靜态連結庫與動态連結庫
滴水逆向-靜态連結庫與動态連結庫
滴水逆向-靜态連結庫與動态連結庫

相關測試代碼

代碼複用的實作:

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内容

迷茫的人生,需要不斷努力,才能看清遠方模糊的志向!