前言 在本文将讲解高级Win32编程的内容,主要讲解静态链接库、动态链接库、以及远程线程的有关内容
win32笔记-静态链接库 现在一个软件可以包含许多的功能,就涉及到了软件的模块化开发,不可能将一个软件的所有代码全部写进一个文件,所以,win32就可以通过静态链接库提供一个模块化的开发方式。
准备 这里以VS 2019演示,新建项目,分别选择C++,Windows,库,静态库就可以了。如果在VS C++里面,就应该直接选择win32 static library创建项目。
VS 2019 c++会默认带有“framework.h”、”pch.h”、”pch.cpp”、”文件名.cpp”,这些都没什么用,全部删除。
例如,新建test.h头文件
1 2 3 4 5 6 #ifndef TEST_H #define TEST_H extern int add (int x, int y) ;#endif
新建test.cpp文件
1 2 3 4 5 6 #include "test.h" int add (int x, int y) { return x + y; }
然后在-生成-生成解决方案,就会在项目目录生成lib文件
使用 将生成的lib文件和test.h文件一起复制到项目的目录,就可以正常使用了
1 2 3 4 5 6 7 8 9 #include <windows.h> #include <stdio.h> #include "test.h" #pragma comment(lib,"StaticLib1.lib" ) int main () { printf ("%d" , add(1 , 2 )); return 0 ; }
将上面的代码用IDA反编译:
add函数:
可以看出,全局没怎么变。
静态库主要的原理就是将lib的文件复制到即将生成的exe代码中,导致可执行文件体积变大。其次包含相同的公共代码,造成浪费。
win32笔记-动态链接库 说到动态链接库,有什么好处呢?好处就是解决静态链接库的缺点。
静态链接库的缺点:
使用静态链接库生成的可执行文件体积较大
包含相同的公共代码,造成浪费
动态链接库(Dynamic Link Library,缩写为DLL),是微软公司在Windows操作系统中,实现共享函数库概念的一种方式。这些函数库的拓展名是“.dll”、“.ocx”(包括ActiveX控制的库)
准备 用VS 2019新建项目分别选着C++、windows、库、动态链接库(DLL),完成创建就行了。里面可能有一些软件创建的文件,为了简单快捷,全部删除就好,我们采用自己手动新建。
新建头文件testdll.h
1 2 3 4 5 6 #ifndef TESTDLL_H #define TESTDLL_H extern "C" _declspec(dllexport) int add (int a, int b) ;#endif
新建testdll.cpp文件
1 2 3 4 5 6 #include "testdll.h" int add (int x, int y) { return x + y; }
下面就生成解决方案。
使用DLL文件 使用动态链接库有两种方法,可以加载时使用动态链接,也可以运行时动态链接。不过两者都需要将动态链接库文件放到程序运行的目录下。所以,这里要将生成的Dll1.dll放到项目运行debug的目录下。
加载时动态链接 这种方案还需要将生成的lib文件放到项目目录下(隐式链接)
1 2 3 4 5 6 7 8 9 10 11 12 #include <windows.h> #include <stdio.h> #pragma comment(lib, "Dll1.lib" ) extern "C" _declspec(dllimport) int add (int a, int b) ;int main () { printf ("%d" , add(1 , 2 )); return 0 ; }
反编译得到:
运行时动态链接 这种方式只需要运行时,exe文件能找到dll文件就行。(显式链接)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <windows.h> #include <stdio.h> typedef int (*lpadd) (int ,int ) ;lpadd add; int main () { HINSTANCE hmodule = LoadLibrary(L"Dll1.dll" ); if ( hmodule != NULL ) { add = (lpadd)GetProcAddress(hmodule, "add" ); printf ("%d" , add(1 , 2 )); } FreeLibrary(hmodule); return 0 ; }
反编译得到:
win32笔记-远程线程 线程
线程是附属在进程上的执行实体,是代码的执行流程。
代码必须通过线程才能执行。
一个简单的线程程序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <windows.h> #include <stdio.h> #pragma comment(lib, "Dll1.lib" ) extern "C" _declspec(dllimport) int add (int a, int b) ;void myfun () { Sleep(1000 ); printf ("\n%d" , add(2 ,3 )); } DWORD WINAPI Thread1 (LPVOID LpParma) { myfun(); return 0 ; } int main () { myfun(); printf ("\n%d" , add(1 , 2 )); HANDLE hT = CreateThread(NULL , 0 , Thread1, NULL , 0 , NULL ); if (hT) { printf ("\nsuccess" ); CloseHandle(hT); } else { printf ("error" ); } getchar(); return 0 ; }
运行的结果
远程线程 一般程序都是自己创建线程,自己使用,但是微软提供了一个CreateRemoteThread()的函数,用来创建远程线程,意思就是用B程序在A程序里面创建线程。上网一个简单的程序暂时叫做A程序。
下面就写一个能操作远程线程的B程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <windows.h> #include <stdio.h> BOOL myCreateRP (DWORD dwProcessID, DWORD dwProcAddr) { HANDLE hprocess = OpenProcess( PROCESS_ALL_ACCESS, false , dwProcessID ); if (!hprocess) { printf ("hprocess error\n" ); return false ; } DWORD threadId = 0 ; HANDLE hThread = CreateRemoteThread( hprocess, NULL , 0 , (LPTHREAD_START_ROUTINE)dwProcAddr, NULL , 0 , &threadId ); if (!hThread) { printf ("hThread error\n" ); CloseHandle(hprocess); return false ; } CloseHandle(hThread); CloseHandle(hprocess); return true ; } int main () { DWORD id = 0 , addr = 0 ; printf ("please input id and addr:\n" ); scanf_s("%d %x" , &id, &addr); if (id && addr) { printf ("checking process %d and addr %x\n" , id, addr); myCreateRP(id, addr); } else { printf ("please check id and addr" ); } return 0 ; }
开始运行调试 看到这里,想必已经对上面的程序了如指掌了,那么现在进行调试和理解程序的运行。
既然是远程对A程序创建的线程进行操作,那么先将A程序运行起来
要想获得我们需要的地址,必须更改004113C0的基址,最快获得我们想要正确的地址,那就在这里打断点,等它运行起来,就能看到基址了。那也就是我们想要的地址001C13C0。
通过第一次函数地址的测试,可以发现,由于程序运行了一次,现在程序的基地址已经被修改正确,可以直接使用,即001C1F80,运行测试如下:
标有数字的第二、三个5都是通过B程序远程线程新建的。同理,还可以使用如下地址001C13C5和001C17E0,第二个地址就是myfun的函数起始地址。第一个001C13C5处的函数就是跳转到001C17E0处myfun函数。
更多可以自行探索。