逆向初級-win32(四)


4.1.C語言中的寬字符

1、常用函數

char		wchar_ t		//多字節字符類型 寬字符類型
printf		wprintf			//打印到控制台函數
strlen		wcslen			//獲取長度
strcpy		wcscpy			//字符串復制
strcat		wcscat			//字符串拼接
strcmp		wcscmp			//字符串比較
strstr		wcsstr			//字符串查找

2、打印寬字符

#include <locale.h>
setlocale(LC_ALL,")			//使用控制台默認的編碼
wchar_ t swzStr[]= L“中國";
wprint(L"%s\n",x1);

3、字符串長度

char szStr] = "中國";
wchar_ t swzStr[] = L"中國";
strlen(szStr);		//取得多字節字符串中字符長度,不包含00
wcslen(swzStr); 	//取得多字 節字符串中字符長度,不包含00 00

4.2.Win32 API中的寬字符

1、什么是Win32 API?有哪些?在哪里?
主要是存放在C:\WINDOWS\system32下面所有的dll
幾個重要的DLL:
<1> Kernel32.dl:最核心的功能模塊,比如管理內存、進程和線程相關的函數等。
<2> User32 dIl:是Windows用 戶界面相關應用程序接口,如創建窗口和發送消息等。

<3> GDI32.dll:全稱是Graphical Device Interface(圖形設備接口),包含用於畫圖和顯示文本的函數。

2、關於Win32的數據類型:

int MessageBox(
    HWND hWnd,			// handle to owner window
    LPCTSTR IpText, 	// text in message box
    LPCTSTR lpCaption,  //message box title
    UINT uType			//message box style
);

3、常用數據類型:
匯編:

byte	BYTE	PBYTE
word	WORD	PWORD
dword	DWORD	PDWORD

C語言:

char			CHAR	PCHAR
unsigned char	UCHAR	PUCHAR
short			SHORT	PSHORT
unsigned short	USHORT	PUSHORT
int				INT		PINT
unsigned int	UINT	PUINT

C++

bool	BOOL

4、在Win32中使用字符串:

字符類型:
    CHAR szStr[]= “中國”;
    WCHAR swzStr[]= L“中國”; 
    TCHAR stzSr[] = TEXT(“中國");    //win32推薦的方式
字符串指針:
    PSTR pszStr=“中國";
    PWSTR pwszStr= L“中國";
    PTSTR ptszStr = TEXT(“中國");		//win32推薦的方式

5、第一個win32 api的使用

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

void main()
{	
	/*
	// ASSll編碼
	int MessageBox(
		HWND hWnd,			//所有者窗口的句柄
		LPCTSTR IpText,		//消息框中的文本地址
		LPCTSTR IpCaption,	//消息框標題的地址
		UINT uType			//樣式的消息框
	);
	*/
	
	/*
	//unicode編碼
	CHAR szTitle[] = "Title";
	CHAR szContent[] = "hello world";
	MessageBoxA(0,szContent,szTitle,MB_OK);
	*/
	
	/*
	WCHAR swzTitle[] = L"Title";
	WCHAR swzContent[] = L"hello world";
	MessageBoxW(0,swzContent,swzTitle,MB_OK);
	*/
    
    //推薦的方式,編譯器會自動選擇編碼方式
	TCHAR stzTitle[] = TEXT("Title");
	TCHAR stzContent[] = TEXT("hello world");
	MessageBox(0,stzContent,stzTitle,MB_OK);
    
	system("pause");
	return;
}

4.3.進程的創建過程

1、什么是進程?
進程提供程序所需的資源,如:數據、代碼等等。

2、進程內存空間的地址划分

image
3、進程的創建
<1>任何進程都是別的進程創建的: CreateProcess()
<2>進程的創建過程
1、映射EXE文件
2、創建內核對象EPROCESS
3、映射系統DLL(ntdll.dIl)
4、創建線程內核對象ETHREAD
5、系統啟動線程
映射DLL(ntll.Ldrlnitialize Thunk)
線程開始執行

4.4.創建進程

進程的創建

進程的創建:
BOOL CreateProcess(
    LPCTSTR IpApplicationName,		// name of executable module
    LPTSTR lpCommandLine,			// command line string
    LPSECURITY_ ATTRIBUTES IpProcessAttributes, 	// SD
    LPSECURITY_ ATTRIBUTES IpThreadAttributes, 	// SD
    BOOL bInheritHandles,			//I handle inheritance option
    DWORD dwCreationFlags,			// creation flags
    LPVOID IpEnvironment,			// new environment block
    LPCTSTR IpCurrentDirectory,		// current directory name
    LPSTARTUPINFO IpStartupInfo,		// startup information
    LPPROCESS_ INFORMATION IpProcessInformation 		// process information
);

創建進程例子

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

BOOL CreateChildProcess(PTCHAR szChildProcessName,PTCHAR szCommandLine)
{
	STARTUPINFO si;
	PROCESS_INFORMATION pi;

	ZeroMemory(&pi,sizeof(pi));
	ZeroMemory(&si,sizeof(si));
	si.cb = sizeof(si);
	
    //創建子進程
	if(!CreateProcess(
		szChildProcessName,		//對象名稱
		szCommandLine,			//命令行	
		NULL,					//不繼承進程句柄
		NULL,					//不繼承線程句柄
		FALSE,					//不繼承句柄
		0,						//沒有創建標志
		NULL,					//使用父進程環境差變量
		NULL,					//使用父進程目錄作為當前目錄。可以自己設置目錄
		&si,					//STARTUPINFO 結構體詳細信息
		&pi)					//PROCESS_INFOMATION結構體進程信息
		)
	{
		printf("CreateChildProcess Error:%d \n",GetLastError());
		return FALSE;
	}
	//釋放句柄
	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);
	return TRUE;
};

void main(int argc,char* argv[])
{	
	TCHAR szApplicationName[] = TEXT("C://Program Files//Tools//PE.exe");
	TCHAR szCommandLine[] = TEXT("1");
	CreateChildProcess(szApplicationName,szCommandLine);

	getchar();
	system("pause");
	return;
}

4.5.句柄表

1、什么是內核對象?
像進程、線程、文件、互斥體、事件等在內核都有一個對應的結構體,這些結構體由內核負責管理。我們管這樣的對象叫做內核對象。

2、如何管理內核對象
image
3、每個進程都有一個句柄表
image
4、多進程共享一個內核對象
image
5、句柄可以被繼承
image

4.6.進程相關API

1、ID與句柄
image
2、以掛起的形勢創建進程

<1>任何進程都是別的進程創建的: CreateProcess()
<2>進程的創建過程
1、映射EXE文件
2、創建內核對象EPROCESS
3、映射系統LLLntll.dI)
4、創建線程內核對象ETHREAD
5、如果是掛起的方式創建的
6、恢復以后再繼續執行
映射DLL(ntllI.Ldrlnitialize^ Thunk)
線程開始執行

掛起的方式創建進程

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

BOOL CreateChildProcess(PTCHAR szChildProcessName,PTCHAR szCommandLine)
{
	STARTUPINFO si;
	PROCESS_INFORMATION pi;

	ZeroMemory(&pi,sizeof(pi));
	ZeroMemory(&si,sizeof(si));
	si.cb = sizeof(si);
	
	if(!CreateProcess(
		szChildProcessName,
		szCommandLine,
		NULL,
		NULL,
		FALSE,
		CREATE_SUSPENDED,    //以掛起的方式創建線程
		NULL,
		NULL,
		&si,
		&pi)
		)
	{
		printf("CreateChildProcess Error:%d \n",GetLastError());
		return FALSE;
	}

	//SuspendThread(pi.hThread);   //暫停進程
	//ResumeThread(pi.hThread);	   //恢復進程
	//打印進程ID和進程句柄
	printf("PID:%X - process:%x \n",pi.dwProcessId,pi.hProcess);
	
    //在恢復進程前,需要做的操作代碼
	for(int i=0;i<5;i++)
	{
		Sleep(1000);
		printf("========\n");
	}
    //恢復進程
	ResumeThread(pi.hThread);
	//關閉進程和線程
	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);
	return TRUE;
};

void main(int argc,char* argv[])
{	
	TCHAR szApplicationName[] = TEXT("C://Program Files//Tools//PE.exe");
	TCHAR szCommandLine[] = TEXT("1");
	CreateChildProcess(szApplicationName,szCommandLine);
	
	getchar();
	system("pause");
	return;
}

3、模塊目錄和工作目錄

char strModule[256];
GetModuleFileName(NULL,strModule, 256); 	//模塊目錄
char strWork[1000];
int i=1000;
GetCurrentDirectory(1000,buf);				//工作目錄

print("模塊目錄: %s\n工作目錄: %s\n",strModule,strWork);

4、其它進程相關API

獲取進程PID
GetCurrentProcessld
獲取進程句柄
GetCurrentProcess
獲取命令行
GetCommandL ine
獲取啟動信息
GetStartupInfo
遍歷進程ID
EnumProcesses
快照
CreateToolhelp32Snapshot

4.7.創建線程

1、什么是線程?
<1>線程是附屬在進程上的執行實體,是代碼的執行流程。
<2>一個進程可以包含多個線程,但一個進程至少要包含一個線程。

2、創建線程

HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES   lpThreadAttributes,
  SIZE_T                  dwStackSize,
  LPTHREAD_START_ROUTINE  lpStartAddress,
  LPVOID 				  lpParameter,
  DWORD                   dwCreationFlags,
  LPDWORD                 lpThreadId
);

實例

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
    //強轉傳遞的參數的類型
	int* p = (int*)lpParameter;

	for(int i=0;i<*p;i++)
	{
		Sleep(500);
		printf("+++++%d \n",i);
	}
	return 0;
}
//參數
int n = 10;

void main(int argc,char* argv[])
{	
	HANDLE hThread;  
    //1.返回的是線程句柄
    //2.第三個參數是要執行的線程函數
    //3.第四個參數是要執行的線程函數的參數,類型為LPVOID
	hThread = CreateThread(NULL,0,ThreadProc,(LPVOID)&n,0,NULL);
	CloseHandle(hThread);

	for(int i=0;i<100;i++)
	{
		Sleep(500);
		printf("------%d\n",i);
	}

	system("pause");
	return;
}

4.8.線程控制

1、如何讓線程停下來

讓自己停下來:
	Sleep()
讓別人停下來:
	SuspendThread()
線程恢復:
	ResumeThread()

掛起和恢復線程

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	int* p = (int*)lpParameter;

	for(int i=0;i<*p;i++)
	{
		Sleep(500);
		printf("+++++%d \n",i);
	}
	return 0;
}

int n = 100;

void main(int argc,char* argv[])
{	
	HANDLE hThread;  
	hThread = CreateThread(NULL,0,ThreadProc,(LPVOID)&n,0,NULL);

	Sleep(5000);
	SuspendThread(hThread);    //掛起線程
	Sleep(5000);
	ResumeThread(hThread);	   //恢復線程
	
	getchar();
	CloseHandle(hThread);

	system("pause");
	return;
}

2、等待線程結束

<1> WaitForSingleObject();   

<2> WaitForMultipleObjects();

<3> GetExitCodeThread();

單個線程WaitForSingleObject()

  • 第一個參數是句柄,
  • 第二個參數是等待時間,INFINITE表示等待線程執行結束
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	int* p = (int*)lpParameter;

	for(int i=0;i<*p;i++)
	{
		Sleep(500);
		printf("+++++%d \n",i);
	}
	return 0;
}

int n = 20;

void main(int argc,char* argv[])
{	
	HANDLE hThread;  
	hThread = CreateThread(NULL,0,ThreadProc,(LPVOID)&n,0,NULL);

	WaitForSingleObject(hThread,INFINITE);  //等待線程結束,才會執行后面的代碼
	printf("線程執行完畢\n");
	
	getchar();
	CloseHandle(hThread);

	system("pause");
	return;
}

多個線程WaitForMultipleObjects()

  • 第一個參數:線程個數
  • 第二個參數:線程句柄數組
  • 第三個參數:是否等待所有線程結束
  • 第四個參數:等待時間
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	int* p = (int*)lpParameter;

	for(int i=0;i<*p;i++)
	{
		Sleep(500);
		printf("+++++%d \n",i);
	}
	return 0;
}

int n = 20;

void main(int argc,char* argv[])
{	
	HANDLE arrThread[2];  
	arrThread[0] = CreateThread(NULL,0,ThreadProc,(LPVOID)&n,0,NULL);
	arrThread[1] = CreateThread(NULL,0,ThreadProc,(LPVOID)&n,0,NULL);

	WaitForMultipleObjects(2,arrThread,TRUE,INFINITE);
	printf("線程執行完畢\n");
	
	getchar();
	CloseHandle(arrThread[0]);
    CloseHandle(arrThread[1]);

	system("pause");
	return;
}

獲取線程返回結果值GetExitCodeThread()

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
	int* p = (int*)lpParameter;

	for(int i=0;i<*p;i++)
	{
		Sleep(500);
		printf("+++++%d \n",i);
	}
	return 0;   //返回結果
}

DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
	int* p = (int*)lpParameter;

	for(int i=0;i<*p;i++)
	{
		Sleep(500);
		printf("+++++%d \n",i);
	}
	return 1;   //返回結果
}

int n = 20;

void main(int argc,char* argv[])
{	
	HANDLE arrThread[2];  
	DWORD dwResult1;
	DWORD dwResult2;
	arrThread[0] = CreateThread(NULL,0,ThreadProc1,(LPVOID)&n,0,NULL);
	arrThread[1] = CreateThread(NULL,0,ThreadProc2,(LPVOID)&n,0,NULL);

	WaitForMultipleObjects(2,arrThread,TRUE,INFINITE);
    //獲取線程返回結果值
	GetExitCodeThread(arrThread[0],&dwResult1);
	GetExitCodeThread(arrThread[1],&dwResult2);

	printf("%d %d\n",dwResult1,dwResult2);   //0  1
	printf("線程執行完畢\n");
	
	getchar();
	CloseHandle(arrThread[0]);
	CloseHandle(arrThread[1]);

	system("pause");
	return;
}

3、獲取和設置線程上下文

//獲取
BOOL GetThreadContext(
	HANDLE hThread, 		// handle to thread with context
	LPCONTEXT IpContext 	// context structure
);

//設置
BOOL SetThreadContext(
	HANDLE hThread,	     	// handle to thread
	CONST CONTEXT *lpContext    //context structure
);

獲取上下文

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
	int* p = (int*)lpParameter;

	for(int i=0;i<*p;i++)
	{
		Sleep(500);
		printf("+++++%d \n",i);
	}
	return 0;   //返回結果
}

DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
	int* p = (int*)lpParameter;

	for(int i=0;i<*p;i++)
	{
		Sleep(500);
		printf("+++++%d \n",i);
	}
	return 1;   //返回結果
}

int n = 20;

void main(int argc,char* argv[])
{	
	HANDLE arrThread[2];  
	DWORD dwResult1;
	DWORD dwResult2;

	arrThread[0] = CreateThread(NULL,0,ThreadProc1,(LPVOID)&n,0,NULL);
	arrThread[1] = CreateThread(NULL,0,ThreadProc2,(LPVOID)&n,0,NULL);

	SuspendThread(arrThread[0]);
	CONTEXT context;
	context.ContextFlags = CONTEXT_INTEGER;
	GetThreadContext(arrThread[0],&context);     //獲取上下文
	printf("%x %x\n",context.Eax,context.Ecx);
	ResumeThread(arrThread[0]);
	
	getchar();
	CloseHandle(arrThread[0]);
	CloseHandle(arrThread[1]);

	system("pause");
	return;
}

4.9.臨界區

1、線程安全問題

每個線程都有自己的棧,而局部變量是存儲在棧中的,這就意味着每個線程都有一份自己的“局部變量",如果線程僅僅使用“局部變量”那么就不存在線程安全問題。那如果多個線程共用-一個全局變量呢?

2、解決思路
image
3、臨界區實現之線程鎖

<1>創建全局變量
CRITICAL SECTION cs;
<2>初始化全局變量
InitializeCriticalSection(&cs);
<3>實現臨界區
EnterCriticalSection(&cs);
使用臨界資源
LeaveCriticalSection(&cs);

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

int g_dwTickets = 10;
CRITICAL_SECTION cs;   //創建全局變量

DWORD WINAPI MyFirstThreadProc(LPVOID lpParameter)
{
	EnterCriticalSection(&cs);     //實現臨界區
	while(g_dwTickets>0)
	{
		EnterCriticalSection(&cs);
		printf("還有:%d張票\n",g_dwTickets);
		g_dwTickets--;
		printf("賣出一張,還有:%d張票\n",g_dwTickets);
		LeaveCriticalSection(&cs);    //使用臨街資源
	}
	return 0;   
}

void main(int argc,char* argv[])
{	
	HANDLE arrThread[2];  
	DWORD dwResult1;
	DWORD dwResult2;

	InitializeCriticalSection(&cs);   //初始化全局變量

	arrThread[0] = CreateThread(NULL,0,MyFirstThreadProc,NULL,0,NULL);
	arrThread[1] = CreateThread(NULL,0,MyFirstThreadProc,NULL,0,NULL);

	WaitForMultipleObjects(2,arrThread,TRUE,INFINITE);
	GetExitCodeThread(arrThread[0],&dwResult1);
	GetExitCodeThread(arrThread[0],&dwResult2);

	getchar();
	CloseHandle(arrThread[0]);
	CloseHandle(arrThread[1]);

	system("pause");
	return;
}

4.10.互斥體

1、內核級臨街資源怎么辦
image
2、互斥體的使用
image
互斥體例子

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

void main(int argc,char* argv[])
{	
    //創建一個互斥體
	HANDLE g_hMutex = CreateMutex(NULL,FALSE,"test");  //第三個參數:隨便起一個名字
	//獲取令牌
	WaitForSingleObject(g_hMutex,INFINITE);

	for(int i=0;i<10;i++)
	{
		Sleep(1000);
		printf("%d\n",i);
	}
	//釋放令牌
	ReleaseMutex(g_hMutex);

	getchar();

	system("pause");
	return;
}

3、互斥體與線程鎖的區別
1、線程鎖只能用於單個進程間的線程控制
2、互斥體可以設定等待超時,但線程鎖不能
3、線程意外終結時,Mutex可以避免無限等待
4、Mutex效率沒有線程鎖高

4、通過互斥體防止程序多開

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

int main(int argc,char* argv[])
{	
	HANDLE hMutex = CreateMutex(NULL,FALSE,"防止多開");
    //1.第一次打開程序的時候GetLastError()返回值為:0
    //2.第二次打開程序的時候GetLastError()返回為:ERROR_ALREADY_EXISTS
    //3.其它情況則是程序出錯
	DWORD dwRet = GetLastError();
	if(hMutex)
	{
		if(ERROR_ALREADY_EXISTS == dwRet)    //如果是第二次打開程序,直接關閉進程
		{
			CloseHandle(hMutex);
			return 0;
		}
	}
	else
	{
		printf("創建失敗,程序退出\n");
		CloseHandle(hMutex);
		return 0;
	}

	while(1)
	{
		Sleep(1000);
		printf("程序執行中.....\n");
	}
		
	getchar();
	system("pause");
	return 0;
}

4.11.事件

1、通知事件

HANDLE CreateEvent(
  LPSECURITY_ATTRIBUTES lpEventAttributes, // SD
  BOOL bManualReset,                       // reset type
  BOOL bInitialState,                      // initial state
  LPCTSTR lpName                           // object name
);

實例

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

HANDLE g_hEvent

DWORD WINAPI ThreadProc_1(LPVOID lpParameter)
{
	TCHAR szBuffer[10] = {0};
	//等待通知
	WaitForSingleObject(g_hEvent,INFINITE);

	printf("ThreadProc_1執行了\n");
	getchar();
	return 0;
}

DWORD WINAPI ThreadProc_2(LPVOID lpParameter)
{
	TCHAR szBuffer[10] = {0};
	//等待通知
	WaitForSingleObject(g_hEvent,INFINITE);

	printf("ThreadProc_2執行了\n");
	getchar();
	return 0;
}

int main(int argc,char* argv[])
{	
    //創建事件
    //參數:1.默認安全屬性;2.TRUE通知。FALSE互斥
    //參數:3.初始沒信號;4.如果想其它進程用,進起個名字
	g_hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
	HANDLE hThread[2];
    //創建兩個線程
	hThread[0] = CreateThread(NULL,0,ThreadProc_1,NULL,0,NULL);
	hThread[0] = CreateThread(NULL,0,ThreadProc_2,NULL,0,NULL);
	//設置事件為通知	
	SetEvent(g_hEvent); 
	WaitForMultipleObjects(2,hThread,TRUE,INFINITE);
	CloseHandle(hThread[0]);
	CloseHandle(hThread[1]);
	CloseHandle(g_hEvent);
		
	getchar();

	system("pause");
	return 0;
}

2、線程同步
<1>線程互斥:線程互斥是指對於共享的進程系統資源,在各單個線程訪問時的排它性。當有若干個線程都要使用某一共享資源時, 任何時刻最多只允許一個線程去使用,其它要使用該資源的線程必須等待,直到占用資源者釋放該資源。
<2>線程同步:線程同步是指線程之間所具有的一種制約關系,一個線程的執行依賴另一個線程的消息,當它沒有得到另一個線程的消息時應等待,直到消息到達時才被喚醒。

同步=互斥+有序執行

3、同步的前提是互斥

1617716232821
image
4、生產者和消費者

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

HANDLE g_hSet,g_hClear;
int g_Max = 10;
int g_Numberr = 0;

DWORD WINAPI ThreadProduct(LPVOID pM)
{
	for (int i=0;i<g_Max;i++)
	{
		WaitForSingleObject(g_hSet,INFINITE);
		g_Numberr = 1;
		DWORD id = GetCurrentThreadId();
		printf("生產者%d將數據%d防入緩沖區\n",id,g_Numberr);
		SetEvent(g_hClear);
	}
	return 0;
}

DWORD WINAPI ThreadConsumer(LPVOID pM)
{
	for (int i=0;i<g_Max;i++)
	{
		WaitForSingleObject(g_hClear,INFINITE);
		g_Numberr = 0;
		DWORD id = GetCurrentThreadId();
		printf("消費者%d將數據%d防入緩沖區\n",id,g_Numberr);
		SetEvent(g_hSet);
	}
	return 0;
}

int main(int argc,char* argv[])
{	
	HANDLE hThread[2];
	g_hSet = CreateEvent(NULL,FALSE,TRUE,NULL);      //生產者。第三個參數TRUE,默認有信號
	g_hClear = CreateEvent(NULL,FALSE,FALSE,NULL);	//消費者者。第三個參數FALSE,默認無信號
	
	hThread[0] = ::CreateThread(NULL,0,ThreadProduct,NULL,0,NULL);
	hThread[1] = ::CreateThread(NULL,0,ThreadConsumer,NULL,0,NULL);

	WaitForMultipleObjects(2,hThread,TRUE,INFINITE);
	CloseHandle(hThread[0]);
	CloseHandle(hThread[1]);
	CloseHandle(g_hSet);
	CloseHandle(g_hClear);
		
	system("pause");
	return 0;
}

4.12.窗口的本質

1、窗口的本質
image
2、GDI圖形設備接口(Graphics Device Interface)**
<1>設備對象(HWND)
<2> DC(設備上下文,Device Contexts)
<3>圖形對象
image

4.13.消息隊列

1、什么是消息?
當我們點擊鼠標的時候,或者當我們按下鍵盤的時候,操作系統都要把這些動作記錄下來,存儲到一個結構體中,這個結構體就是消息。
2、消息隊列:每個線程只有一個消息隊列
image
3、窗口與線程
image

4.14.第一個Windows程序

1、創建win32應用程序
image
image
2、WinMain函數

int WINAPI WinMain(
  HINSTANCE hInstance,      // handle to current instance
  HINSTANCE hPrevInstance,  // handle to previous instance
  LPSTR lpCmdLine,          // command line
  int nCmdShow              // show state
);

3、調試信息的輸出

char szOutBuff[0x80];
sprintf(szOutBuff,"Error: %d",GetLastError());
OutputDebugString(szOutBuff)

4、打印當前程序的入口地址

// derek.cpp : Defines the entry point for the application.
//

#include "stdafx.h"

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
	char szOutBuff[0x80];
	DWORD dwAddr = (DWORD)hInstance;
	sprintf(szOutBuff,"address: %x\n",dwAddr);
	OutputDebugString(szOutBuff);      //address:400000
    
	return 0;
}

5、創建一個window窗口程序過程
image
6、用到的API函數

typedef struct _WNDCLASS { 
    UINT       style; 
    WNDPROC    lpfnWndProc; 
    int        cbClsExtra; 
    int        cbWndExtra; 
    HINSTANCE  hInstance; 
    HICON      hIcon; 
    HCURSOR    hCursor; 
    HBRUSH     hbrBackground; 
    LPCTSTR    lpszMenuName; 
    LPCTSTR    lpszClassName; 
} WNDCLASS, *PWNDCLASS; 

LRESULT CALLBACK WindowProc(
  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
);

HWND CreateWindow(
  LPCTSTR lpClassName,  // registered class name
  LPCTSTR lpWindowName, // window name
  DWORD dwStyle,        // window style
  int x,                // horizontal position of window
  int y,                // vertical position of window
  int nWidth,           // window width
  int nHeight,          // window height
  HWND hWndParent,      // handle to parent or owner window
  HMENU hMenu,          // menu handle or child identifier
  HINSTANCE hInstance,  // handle to application instance
  LPVOID lpParam        // window-creation data
);

HWND CreateWindow(
  LPCTSTR lpClassName,  // registered class name
  LPCTSTR lpWindowName, // window name
  DWORD dwStyle,        // window style
  int x,                // horizontal position of window
  int y,                // vertical position of window
  int nWidth,           // window width
  int nHeight,          // window height
  HWND hWndParent,      // handle to parent or owner window
  HMENU hMenu,          // menu handle or child identifier
  HINSTANCE hInstance,  // handle to application instance
  LPVOID lpParam        // window-creation data
);

BOOL GetMessage(
  LPMSG lpMsg,         // message information
  HWND hWnd,           // handle to window
  UINT wMsgFilterMin,  // first message
  UINT wMsgFilterMax   // last message
);

7、實例

// derek.cpp : Defines the entry point for the application.
//

#include "stdafx.h"

LRESULT CALLBACK WindowProc(
  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
);

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    //1.定義窗口的樣子
	char szOutBuff[0x80];
	TCHAR className[] = TEXT("My first window");
	WNDCLASS wndclass = {0};
    //wndclass:主要是下面4個參數
	wndclass.hbrBackground = (HBRUSH)COLOR_BACKGROUND;  
	wndclass.lpfnWndProc = WindowProc;
	wndclass.lpszClassName = className;
	wndclass.hInstance = hInstance;
    
	RegisterClass(&wndclass);   //注冊窗口
	
    //2.創建窗口
	HWND hwnd = CreateWindow(
		className,
		TEXT("我的第一個窗口"),
		WS_OVERLAPPEDWINDOW,
		10,
		10,
		600,
		300,
		NULL,
		NULL,
		hInstance,
		NULL	
		);
	//如果窗口沒有創建成功,打印錯誤信息
	if(hwnd == NULL)
	{
		sprintf(szOutBuff,"Error: %d\n",GetLastError());
		OutputDebugString(szOutBuff);  
		return 0;
	}
	//顯示窗口
	ShowWindow(hwnd,SW_SHOW);
	
    //3.消息處理
	MSG msg;
	BOOL bRet;
	while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
	{ 
		if (bRet == -1)
		{
			sprintf(szOutBuff,"Error: %d\n",GetLastError());
			OutputDebugString(szOutBuff);
		}
		else
		{
			//TranslateMessage(&msg);    //轉換消息
			DispatchMessage(&msg);       //分發消息
		}
    };

	return 0;
};

LRESULT CALLBACK WindowProc(
  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
)
{
	return DefWindowProc(hwnd,uMsg,wParam,lParam);    //必須提供消息的處理方式,這里按默認方式
}

4.15.消息類型

1、消息產生與處理流程
image
2、消息類型

// derek.cpp : Defines the entry point for the application.
//

#include "stdafx.h"

LRESULT CALLBACK WindowProc(
  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
);

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
	char szOutBuff[0x80];
	TCHAR className[] = TEXT("My first window");
	WNDCLASS wndclass = {0};
	wndclass.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
	wndclass.lpfnWndProc = WindowProc;
	wndclass.lpszClassName = className;
	wndclass.hInstance = hInstance;
	RegisterClass(&wndclass);
	
	HWND hwnd = CreateWindow(
		className,
		TEXT("我的第一個窗口"),
		WS_OVERLAPPEDWINDOW,
		10,
		10,
		600,
		300,
		NULL,
		NULL,
		hInstance,
		NULL	
		);
	
	if(hwnd == NULL)
	{
		sprintf(szOutBuff,"Error: %d\n",GetLastError());
		OutputDebugString(szOutBuff);  
		return 0;
	}

	ShowWindow(hwnd,SW_SHOW);

	MSG msg;
	BOOL bRet;
	while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
	{ 
		if (bRet == -1)
		{
			sprintf(szOutBuff,"Error: %d\n",GetLastError());
			OutputDebugString(szOutBuff);
		}
		else
		{
			TranslateMessage(&msg);    //把鍵盤虛擬鍵碼自動轉換成對應字符
			DispatchMessage(&msg); 
		}
    };

	return 0;
};

LRESULT CALLBACK WindowProc(
  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
)
{
	//WM_CREATE
	switch(uMsg)
	{
	case WM_DESTROY:    //窗口銷毀message
		{
			PostQuitMessage(0);   
			return 0;
		}
	case WM_CHAR:      //鍵盤按鍵消息
		{
			char szOutBuff[0x80];
			sprintf(szOutBuff,"消息:%c\n",wParam);   //獲取按了鍵盤哪個鍵
			OutputDebugString(szOutBuff);
			return 0;
		}

	}

	return DefWindowProc(hwnd,uMsg,wParam,lParam);
}

4.16.子窗口控件

<1> WINDOWS提供了幾個預定義的窗口類以方便我們的使用,我們一-般就它們叫做子窗口控件,簡稱控件。
<2>控件會自己處理消息,並在自己狀態發生改變時通知父窗口。
<3>預定義的控件有:
按鈕、復選框、編輯框、靜態字符串標簽和滾動條等

實例

// derek.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#define IDC_EDIT_1 0X100        //子窗口身份標識
#define IDC_BUTTON_1 0X101
#define IDC_BUTTON_2 0X102

HINSTANCE g_hInstance;         //定義全局的g_hInstance,子窗口可以獲取到父窗口的hInstance

LRESULT CALLBACK WindowProc(
  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
);

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
	char szOutBuff[0x80];
	g_hInstance = hInstance;   //把父窗口的hInstance賦值給全局的g_hInstance,
	TCHAR className[] = TEXT("My first window");
	WNDCLASS wndclass = {0};
	wndclass.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
	wndclass.lpfnWndProc = WindowProc;
	wndclass.lpszClassName = className;
	wndclass.hInstance = hInstance;
	RegisterClass(&wndclass);
	
	HWND hwnd = CreateWindow(
		className,
		TEXT("我的第一個窗口"),
		WS_OVERLAPPEDWINDOW,
		10,
		10,
		600,
		300,
		NULL,
		NULL,
		hInstance,
		NULL	
		);
	
	if(hwnd == NULL)
	{
		sprintf(szOutBuff,"Error: %d\n",GetLastError());
		OutputDebugString(szOutBuff);  
		return 0;
	}

	ShowWindow(hwnd,SW_SHOW);

	MSG msg;
	BOOL bRet;
	while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
	{ 
		if (bRet == -1)
		{
			sprintf(szOutBuff,"Error: %d\n",GetLastError());
			OutputDebugString(szOutBuff);
		}
		else
		{
			TranslateMessage(&msg); 
			DispatchMessage(&msg); 
		}
    };

	return 0;
};

LRESULT CALLBACK WindowProc(
  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
)
{
	char szOutBuff[0x80];
	//sprintf(szOutBuff,"消息: %x\n",uMsg);   //查看消息類型
	//OutputDebugString(szOutBuff); 
	//WM_CREATE
	switch(uMsg)
	{
	case WM_DESTROY:
		{
			PostQuitMessage(0);
			break;
		}

	case WM_COMMAND:
		{
			switch(LOWORD(wParam))          //低16位
			{
			case IDC_BUTTON_1:
				{
					SetDlgItemText(hwnd,IDC_EDIT_1,TEXT("測試"));
					break;
				}

			case IDC_BUTTON_2:
				{
					GetDlgItemText(hwnd,IDC_EDIT_1,szOutBuff,100);
					MessageBox(hwnd,szOutBuff,szOutBuff,MB_OK);
					break;
				}
			}

			break;
		}

	case WM_CREATE:
		{
			CreateWindow(
				"EDIT",
				"",
				WS_CHILD|WS_VISIBLE|WS_VSCROLL|ES_MULTILINE,    //子窗口通用style和特殊style
				0,
				0,
				500,
				300,
				hwnd,                    //父窗口
				(HMENU)IDC_EDIT_1,       //子窗口身份標識
				g_hInstance,
				NULL	
				);
			CreateWindow(
				"BUTTON",
				"設置",
				WS_CHILD|WS_VISIBLE,
				520,
				180,
				60,
				30,
				hwnd,
				(HMENU)IDC_BUTTON_1,
				g_hInstance,
				NULL	
				);
			CreateWindow(
				"BUTTON",
				"獲取",
				WS_CHILD|WS_VISIBLE,
				520,
				220,
				60,
				30,
				hwnd,
				(HMENU)IDC_BUTTON_2,
				g_hInstance,
				NULL	
				);
			break;
		}
	}
	return DefWindowProc(hwnd,uMsg,wParam,lParam);
};

4.17.虛擬內存與物理內存

1、虛擬內存與物理內存
image
2、虛擬內存地址划分
image
3、物理內存

可供使用的物理內存:
MmNumberOfPhysicalPages* 4=物理內存
虛擬內存(硬盤)
能夠識別的物理內存:
32位系統最多可以識別物理內存為64G,但由於操作系統的限制
比如XP,只能識別4G(Windows 2003服務器版本可以識別4G以上)

4.18.私有內存的申請釋放

1、申請內存的兩種方式

<1>通過VirtualAlloc/NirtualAllocEx申請的: Private Memory
<2>通過CreateFileMapping映射的: Mapped Memory

2.申請內存 VirtualAlloc

LPVOID VirtualAlloc(
  LPVOID lpAddress,        // region to reserve or commit
  SIZE_T dwSize,           // size of region
  DWORD flAllocationType,  // type of allocation
  DWORD flProtect          // type of access protection
);
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

int main(int argc,char* argv[])
{	
	LPVOID p = VirtualAlloc(
		NULL,       		//要分配的內存區域的地址:通常都為NULL      
		0x1000*2,   		//分配的大小:兩頁        	
		MEM_COMMIT, 		//分級的類型:MEM_COMMIT或者MEM_RESERVE
		PAGE_READWRITE      //該內存的初保護屬性:PAGE_EXECUTE_READWRITE, PAGE_READWRITE等
	);

	system("pause");
	return 0;
}

3、釋放內存

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

int main(int argc,char* argv[])
{	
	LPVOID p = VirtualAlloc(
		NULL,        
		0x1000*2,           
		MEM_COMMIT, 
		PAGE_READWRITE         
	);

	VirtualFree(p,0,MEM_RELEASE);    //釋放內存

	system("pause");
	return 0;
}

4.19.共享內存的申請釋放

共享內存

HANDLE CreateFileMapping(
  HANDLE hFile,                       // handle to file
  LPSECURITY_ATTRIBUTES lpAttributes, // security
  DWORD flProtect,                    // protection
  DWORD dwMaximumSizeHigh,            // high-order DWORD of size
  DWORD dwMaximumSizeLow,             // low-order DWORD of size
  LPCTSTR lpName                      // object name
);

LPVOID MapViewOfFile(
  HANDLE hFileMappingObject,   // handle to file-mapping object
  DWORD dwDesiredAccess,       // access mode
  DWORD dwFileOffsetHigh,      // high-order DWORD of offset
  DWORD dwFileOffsetLow,       // low-order DWORD of offset
  SIZE_T dwNumberOfBytesToMap  // number of bytes to map
);
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

#define MapFileName "共享內存"
#define BUF_SIZE 0X1000

HANDLE g_hMapFile;
LPTSTR g_lpBuff;

int main(int argc,char* argv[])
{	
    //內核對象,物理頁
	g_hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,BUF_SIZE,MapFileName);
	//將物理頁與線性地址進行映射
	g_lpBuff = (LPTSTR)MapViewOfFile(g_hMapFile,FILE_MAP_WRITE,0,0,BUF_SIZE);
	//關閉映射
	UnmapViewOfFile(g_lpBuff);
    //關閉句柄
	CloseHandle(g_hMapFile);

	getchar();
	system("pause");
	return 0;
}

4.20.文件系統

1、文件系統

文件系統是操作系統用於管理磁盤上文件的方法和數據結構;簡單點說就是
在磁盤上如何組織文件的方法。
image
2、卷相關API

<1>獲取卷
GetLogicalDrives()
<2>獲取一個所卷的盤符的字符串
GetLogicalDriveStrings()
<3>獲取卷的類型
GetLogicalDriveType()
<4>獲取卷信息
GetVolumeInformation()

3、目錄相關API

<1>創建目錄
CreateDirectory();
<2>刪除目錄
RemoveDirectory();
<3>修改目錄名稱
MoveFile();
<4>獲取程序當前目錄
GetCurrentDirectory( );

<5>設置程序當前目錄

SetCurrentDirectory();

4、文件相關API

<1>創建文件
CreateFile();
<2>關閉文件的
CloseHandle();
<3>獲取文件長度
GetFileSize();
<4>獲取文件的屬性和信息
GetFileAttributes()/GetFileAttributesEx();
<5>讀/寫/拷 貝/刪除文件
ReadFile()witeFile()/CopyFile()/DeleteFile();
<6>查找文件
FindFirstFile()/FindNextFile();

4.21.內存映射文件

1、內存映射文件

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

#define MAPPINGNAME "共享內存"

DWORD MappingFile(LPSTR lpcFile)
{
	HANDLE hFile;
	HANDLE hMapFile;
	DWORD dwFileMapSize;
	LPVOID lpAddr;
	
    //1.得到文件句柄
	hFile = CreateFile(
		lpcFile,
		GENERIC_READ|GENERIC_WRITE,
		0,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL
		);
	if(hFile == INVALID_HANDLE_VALUE)
	{
		printf("CreateFIle 失敗:%d \n",GetLastError());
		return 0;
	}
	
    //2.創建FileMapping對象
	hMapFile = CreateFileMapping(
		hFile,
		NULL,
		PAGE_READWRITE,
		0,
		0,
		MAPPINGNAME
		);

	if(hMapFile == NULL)
	{
		printf("CreateFileMapping 失敗:%d \n",GetLastError());
		CloseHandle(hFile);
		return 0;
	}
	
    //3.映射到虛擬內存
	lpAddr = MapViewOfFile(
		hMapFile,
		FILE_MAP_COPY,
		0,
		0,
		0
		);

	if(lpAddr == NULL)
	{
		printf("MapViewOfFile 失敗:%d \n",GetLastError());
		CloseHandle(hMapFile);
		CloseHandle(hFile);
		return 0;	
	}
	
    //4.讀取文件
	DWORD dwTest1 = *(PDWORD)lpAddr;
	printf("%x %x \n",dwTest1);
	
    //5.寫入文件
	//*(PDWORD)lpAddr = 0x42424242;
    //強制更新緩存
	//FlushViewOfFile((PDWORD)lpAddr,4)      
	
    //6.關閉資源
	UnmapViewOfFile(lpAddr);
	CloseHandle(hMapFile);
	CloseHandle(hFile);

}

int main(int argc,char* argv[])
{	
	
	MappingFile("C:\\Program Files\\Tools\\PE.exe");


	system("pause");
	return 0;
}

2、內存映射文件之共享
image

4.22.靜態鏈接庫

1、靜態鏈接庫的使用

<1>將生成的.h與.lib文件復制到項目根目錄,然后在代碼中引用:

include "xxxx.h"

pragma comment(lib, "XxXx.lib")

<2>將xxxx.H與xxxx.LIB文件復制到VC6安裝目錄,與庫文件放在-一起
C:\Program FilesMicrosoft Visual Studio\VC98\Include(xxxx.h)
C:\Program FilesMicrosoft Visual StudiolVC98\ixbxxx.ib)
然后在工程>設置->連接->對象/庫模塊中添加xxx.lib

2、靜態鏈接庫的缺點

<1>使用靜態鏈接生成的可執行文件體積較大
<2>包含相同的公共代碼,造成浪費

4.23.動態鏈接庫

1、什么是動態鏈接庫?
動態鏈接庫(Dynamic Link Library,縮寫為DLL),是微軟公司在微軟Windows操作系統中,實現共享函數庫概念的一種方式。
這些庫函數的擴展名是”.dll"、".ocx" (包含ActiveX控制的庫)。

2、創建動態鏈接庫
<1> extern "C" _declspec(dllexport) 調用約定 返回類型 函數名(參數列表);
<2>使用.def文件:
EXPORTS
函數名 @編號
函數名 @編號 NONAME

使用序號導出的好處:

名字是一段程序就精華的注釋,通過名字可以直接猜測到函數的功能通過使用序號,可以達到隱藏的目的

3、dll的創建和使用

步驟1: :
//定義函數指針如: 
typedef int (_stdcall *IpPlus)(int,int);
typedef int(_stdall *lpSub)(int,int);
步驟2:
//聲明函數指針變量如:
IpPlus myPlus;
lpSub mySub.
步驟3:
//動態加載dll到內存中如:
HINSTANCE hModule = LoadLibrary("DIIDemo.dl);
步驟4: 
//獲取函數地址如:
myPlus = (lpPlus)GetProcAddress(hModule, "Plus");
mySub= (IpSub)GetProcAddress(hModue. (char*)0x10),
步驟5:
//調用函數如:
int a = myPlus(10,2);
int b = mySub(10,2);
步驟6:
//釋放動態鏈接庫如:
FreeLibrary(hModule);

image
image
創建mydll文件

mydll.h

// mydll.h: interface for the mydll class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_MYDLL_H__2BDF500F_EC63_4907_A7B0_A7BA5F2EACC0__INCLUDED_)
#define AFX_MYDLL_H__2BDF500F_EC63_4907_A7B0_A7BA5F2EACC0__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

//extern "C" _declspec(dllexport) _stdcall int Plus(int x,int y);

//extern "C" _declspec(dllexport) _stdcall int Sub(int x,int y);

_stdcall int Plus(int x,int y);

_stdcall int Sub(int x,int y);

#endif // !defined(AFX_MYDLL_H__2BDF500F_EC63_4907_A7B0_A7BA5F2EACC0__INCLUDED_)

mydll.cpp

// mydll.h: interface for the mydll class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_MYDLL_H__2BDF500F_EC63_4907_A7B0_A7BA5F2EACC0__INCLUDED_)
#define AFX_MYDLL_H__2BDF500F_EC63_4907_A7B0_A7BA5F2EACC0__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

//extern "C" _declspec(dllexport) _stdcall int Plus(int x,int y);

//extern "C" _declspec(dllexport) _stdcall int Sub(int x,int y);

_stdcall int Plus(int x,int y);

_stdcall int Sub(int x,int y);

#endif // !defined(AFX_MYDLL_H__2BDF500F_EC63_4907_A7B0_A7BA5F2EACC0__INCLUDED_)

創建first_dll.def文件

EXPORT

Plus @12
Sub @13 NONAME

image
然后編譯生產first_dll.dll.

其它程序使用dll,把first_dll.dll復制到程序目錄下面

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

typedef int (_stdcall *lpPlus)(int,int);
typedef int (_stdcall *lpSub)(int,int);

lpPlus myPlus;
lpSub mySub;

int main(int argc,char* argv[])
{	
	HINSTANCE hModule = LoadLibrary("first_dll.dll");

	myPlus = (lpPlus)GetProcAddress(hModule,"Plus");
	mySub = (lpSub)GetProcAddress(hModule,(char*)0xD);
	
	int a = myPlus(1,2);
	int b = mySub(5,3);

	FreeLibrary(hModule);

	system("pause");
	return 0;
}

4.24.隱式鏈接

1、隱式鏈接
步驟1:將*.dll *.lib 放到工程目錄下面
步驟2:將#pragma comment(lib,"DLL名.ib)添加到調用文件中
步驟3:加入函數的聲明

__declspec(dllimport) __stdcall int Plus (int x,int y);
__declspec(dllimport) __stdcall int Sub (int x,int y);
注意:
extern "C" _declspec(dllexport)調用約定 返回類型 函數名(參數列表);
extern "C" _declspec(llimport) 調用約定 返回類型 函數名(參數列表);
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

#pragma comment(lib,"first_dll.lib")

__declspec(dllimport) __stdcall int Plus(int x,int y);
__declspec(dllimport) __stdcall int Sub(int x,int y);


int main(int argc,char* argv[])
{	
	int a = Plus(1,2);
	int b = Sub(4,1);
	
	system("pause");
	return 0;
}

2、dll的優點
image

4.25.遠程線程

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>


void fun()
{
	for(int i=1;i<10;i++)
	{
		Sleep(1000);
		printf("%d\n",i);
	}
}

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	fun();
	return 0;
}

int main(int argc,char* argv[])
{
	HANDLE hThrad = CreateThread(NULL,0,ThreadProc,NULL,0,NULL);
	CloseHandle(hThrad);

	getchar();

	return 0;

}

遠程線程

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

BOOL MyCreateRemoteThread(DWORD dwProcessID,DWORD dwProcAddr)
{
	HANDLE hProcess;
	HANDLE hThread;
	DWORD dwThreadID;
	hProcess = 0;
	dwThreadID = 0;
	//獲取進程句柄	
	hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessID);
	if(hProcess == NULL)
	{
		OutputDebugString("OpenProcess Error \n");
		return FALSE;
	}
	//創建遠程線程
	hThread = CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)dwProcAddr,NULL,0,&dwThreadID);
	if(hThread == NULL)
	{
		OutputDebugString("CreateRemoteThread Error \n");
		CloseHandle(hProcess);
		return FALSE;
	}
	
	CloseHandle(hThread);
	CloseHandle(hProcess);

	return true;
}


int main(int argc,char* argv[])
{
	MyCreateRemoteThread(940,0x4010C0);  //參數1:遠程進程ID;參數2:遠程線程函數地址

	return 0;

}

4.26.遠程線程注入

1、什么是注入?
所謂注入就是在第三方進程不知道或者不允許的情況下將模塊或者代碼寫入對方進程空間,並設法執行的技術。

在安全領域,“注入”是非常重要的一一種技術手段,注入與反注入也一直處於不斷變化的,而且也愈來愈激烈的對抗當中。

己知的注入方式:
遠程線程注入、APC注入、消息鈎子注入、注冊表注入、導入表注入、輸入法注入等等。

2、遠程注入的流程
image
3.創建dll
image
MyDll.h

// MyDll.h: interface for the MyDll class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_MYDLL_H__91269728_7DE5_44BC_8EC0_9844FD01E838__INCLUDED_)
#define AFX_MYDLL_H__91269728_7DE5_44BC_8EC0_9844FD01E838__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

int Plus(int x,int y);

int Sub(int x,int y);

#endif // !defined(AFX_MYDLL_H__91269728_7DE5_44BC_8EC0_9844FD01E838__INCLUDED_)

MyDll.cpp

// MyDll.cpp: implementation of the MyDll class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "MyDll.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

int Plus(int x,int y)
{
	return x+y;
}

int Sub(int x,int y)
{
	return x-y;
}

TestDll.def

EXPORTS

Plus	@1
Sub		@2

testdll.cpp

// testdll.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	for(;;)
	{
		Sleep(1000);
		printf("TestDll++++++++\n");
	}
	return 0;
}

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
	switch(ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		{
			CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadProc,NULL,0,NULL);
			break;
		}
	case DLL_THREAD_ATTACH:
		break;
	case DLL_THREAD_DETACH:
		break;
	case DLL_PROCESS_DETACH:
		break;
	}
	
    return TRUE;
}

生產dll,把testdll.dll放到桌面test文件夾下面

4、遠程注入dll

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

BOOL LoadDll(DWORD dwProcessID,char *szDllPathName)
{
	BOOL bRet;
	HANDLE hProcess;
	HANDLE hThread;
	DWORD dwLength;
	DWORD dwLoadAddr;
	LPVOID lpAllocAddr;
	DWORD dwThreadID;
	HMODULE hModule;

	bRet = 0;
	hProcess = 0;
	dwLoadAddr = 0;
	dwThreadID = 0;
	
    //1.獲取進程句柄
	hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessID);
	if(hProcess == NULL)
	{
		OutputDebugString("OpenProcess Error \n");
		return FALSE;
	}
	//2.計算DLL路徑名字長度,並且要加上0結尾的長度
	dwLength = strlen(szDllPathName)+1;
	//3.在目標進程分配內存
	lpAllocAddr = VirtualAllocEx(hProcess,NULL,dwLength,MEM_COMMIT,PAGE_READWRITE);

	if(lpAllocAddr == NULL)
	{
		OutputDebugString("VirtualAllocEX Error \n");
		CloseHandle(hProcess);
		return FALSE;
	}
	//4.拷貝DLL路徑名字到目標進程的內存
	bRet = WriteProcessMemory(hProcess,lpAllocAddr,szDllPathName,dwLength,NULL);
	if(!bRet)
	{
		OutputDebugString("WriteProcessMemory Error \n");
		CloseHandle(hProcess);
		return FALSE;
	}
	//5.獲取模塊地址
	hModule = GetModuleHandle("Kernel32.dll");
	if(!hModule)
	{
		OutputDebugString("GetModuleHandle Error \n");
		CloseHandle(hProcess);
		return FALSE;
	}
	//6.獲取LoadLibraryA函數地址
	dwLoadAddr = (DWORD)GetProcAddress(hModule,"LoadLibraryA");
	if(!dwLoadAddr)
	{
		OutputDebugString("GetProcAddress Error \n");
		CloseHandle(hProcess);
		CloseHandle(hModule);
		return FALSE;
	}
	//7.創建遠程線程,加載DLL
	hThread = CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)dwLoadAddr,lpAllocAddr,0,NULL);
	if(!hThread)
	{
		OutputDebugString("CreateRemoteThread Error \n");
		CloseHandle(hProcess);
		return FALSE;
	}
	//8.關閉進程句柄
	CloseHandle(hProcess);

	return true;
}

int main(int argc,char* argv[])
{
    //參數:進程ID和注入的dll路徑
	LoadDll(208,"C:\\Documents and Settings\\Administrator\\桌面\\test\\testdll.dll");
	
	return 0;

}

image

4.27.模塊隱藏

1、模塊隱藏之斷鏈
<1> TEB(Thread Environment Block ),它記錄的相關線程的信息,每一個線程
都有自己的TEB,FS:[0]即是 當前線程的TEB。
mov eax,fs:[0]
<2> PEB(Process Environment Block,進程環境塊)存放進程信息,每個進程都;
有自己的PEB信息,TEB偏移0x30即當前進程的PEB。

mov eax,fs:[0x30]
mov PEB, eax

<3>TEB與PEB都在用戶空間

2、最好的隱藏

無模塊注入,也就是代碼注入


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM