VC编程之VC常用API+示例3
小标 2019-01-15 来源 : 阅读 1060 评论 0

摘要:本文主要向大家介绍了VC编程之VC常用API+示例3,通过具体的内容向大家展示,希望对大家学习VC编程有所帮助。

本文主要向大家介绍了VC编程之VC常用API+示例3,通过具体的内容向大家展示,希望对大家学习VC编程有所帮助。

VC编程之VC常用API+示例3

第四十一个CreateCompatibleDC创建一个兼容的内存设备上下文(DC)
简单的来说,就是复制一个模一样的DC。就把窗口看成一幅幅图画,窗口有大有小,里面的内容也不一样(颜色值),每个像素点的颜色值可能不一样,所以就用设备上下文来描述每个窗口的信息,对于DC具体是怎样描述设备上下文的,我们暂时还不需要知道,只要了解这个概念就行了。这个窗口信息,获得一个窗口设备上下文,就用GetDC函数就行了,如HDC hDC=GetDC(hWnd);而CreateCompatibleDC的作用是根据一个设备上下文,再创建一个兼容的设备上下文,如 HDC mDC=CreateCompatibleDC(hDC)。这样mDC里的信息就跟hDC里的一样,那这有什么用呢?这个将会在后面的BitBltl输出一个位图(合并两个DC)函数里会用到。
第四十二个GetObject获取一个对象信息(如位图,图标,光标)
函数定义:int GetObject(HGDIOBJ hgdiobj, int cbBuffer, LPVOID lpvObject);
第一个参数hgdiobj是对象句柄,第二个参数cbBuffer是待写入lpvObject指针指向缓存区数据大小,第三个参数lpvObject是一个指针,指向一个缓存区。
这里举一个获取位图的信息,获取位图的大小,假设E盘下有一个aa.bmp的位图文件,输出位图的宽高
#include
#include
int main()
{
 BITMAP bmInfo;//这个结构存储位图信息
 HBITMAP bmp;
 bmp=(HBITMAP)LoadImage(NULL,"e:\\aa.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
 GetObject(bmp,sizeof(BITMAP),&bmInfo);
 printf("位图宽:%d,位图高:%d\n",bmInfo.bmWidth,bmInfo.bmHeight);
 return 0;
}
第四十三个BitBlt在窗口输出一个位图
其实倒不如说这个BitBlt函数是拷贝一个设备上下文(DC),或者合并两个窗口,再延伸一下,合并两个图片?也并无不可,往大了说,窗口难道不是图片吗?用截屏软件,把窗口截成图片,这样窗口便成了图片。可能有点瞎说,大家还是按照标准来吧,反正,你只要掌握这个函数就行了,而且这个概念也不会有什么影响,那就足够了。
BitBlt的作用跟把两幅图片合在一起一样,合并两幅图片。可能两幅图片大小不一样也可以合并,但合并DC就不行了,必须两个信息一样的DC才可以合并,那要如何确保两个DC一样呢?这就要用到CreateCompatibleDC函数了。
函数定义:BOOL BitBlt(HDC hdcDest,int nXDest,int nYDest,int nWidth,int nHeight,HDC hdcSrc,int nXSrc,int nYSrc,DWORD dwRop);
第一个参数hdcDest是原DC句柄,被覆盖的DC,nXdest,nYDest,nWidth,nHeight这四个参数,指明了一个矩形,覆盖原DC哪块区域。
第六个参数hdcSrc是覆盖的DC句柄,nXSrc,nYSrc参数指明从哪里开始覆盖。(覆盖DC的左上角),第九个参数dwPop表示以何种方式覆盖。因为这里我们只要输出一个位图,所以用SRCCOPY,直接覆盖。
好了,直接举个例子,在窗口输出一副图片,假设e盘下有一个aa.bmp的位图。为了方便,我们直接在记事本窗口输出位图,先运行一个窗口名为"无标题.txt - 记事本"记事本窗口程序。
#include
#include
int main()
{
 BITMAP bmInfo;//这个结构存储位图信息
 HBITMAP bmp;
 bmp=(HBITMAP)LoadImage(NULL,"e:\\aa.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
    GetObject(bmp,sizeof(BITMAP),&bmInfo);//获取位图信息
 HWND wnd=FindWindow(NULL,"无标题.txt - 记事本");
 HDC hdc=GetDC(wnd);
    HDC memDC=::CreateCompatibleDC(hdc);//创造兼容的DC
 SelectObject(memDC,bmp);//选入位图
 while(1)
 {
 BitBlt(hdc,0,0,bmInfo.bmWidth,bmInfo.bmHeight,memDC,0,0,SRCCOPY);//输出位图
 Sleep(200);
 
 }
 return 0;
}
下面介绍一下BitBlt函数最后一个参数的常用取值及意思。
参考(百度)
BLACKNESS:表示使用与物理调色板的索引0相关的色彩来填充目标矩形区域,(对缺省的物理调色板而言,该颜色为黑色)。   
DSTINVERT:表示使目标矩形区域颜色取反。   
MERGECOPY:表示使用布尔型的AND(与)操作符将源矩形区域的颜色与特定模式组合一起。   MERGEPAINT:通过使用布尔型的OR(或)操作符将反向的源矩形区域的颜色与目标矩形区域的颜色合并。 NOTSRCCOPY:将源矩形区域颜色取反,于拷贝到目标矩形区域。   
NOTSRCERASE:使用布尔类型的OR(或)操作符组合源和目标矩形区域的颜色值,然后将合成的颜色取反。 PATCOPY:将特定的模式拷贝到目标位图上。   
PATPAINT:通过使用布尔OR(或)操作符将源矩形区域取反后的颜色值与特定模式的颜色合并。然后使用OR(或)操作符将该操作的结果与目标矩形区域内的颜色合并。   
PATINVERT:通过使用XOR(异或)操作符将源和目标矩形区域内的颜色合并。   
SRCAND:通过使用AND(与)操作符来将源和目标矩形区域内的颜色合并。   
SRCCOPY:将源矩形区域直接拷贝到目标矩形区域。   
SRCERASE:通过使用AND(与)操作符将目标矩形区域颜色取反后与源矩形区域的颜色值合并。   SRCINVERT:通过使用布尔型的XOR(异或)操作符将源和目标矩形区域的颜色合并。   
SRCPAINT:通过使用布尔型的OR(或)操作符将源和目标矩形区域的颜色合并。   
WHITENESS:使用与物理调色板中索引1有关的颜色填充目标矩形区域。(对于缺省物理调色板来说,这个颜色就是白色)
第四十四个GetWindowText根据窗口句柄获得窗口标题名
函数定义:int GetWindowText(HWND hWnd,LPTSTR lpString,int nMaxCount);
第一个参数hWnd是要获取窗口标题名的窗口句柄,第二个lpString是个字符串,窗口标题名,将会存储在这里面,第三个参数nMaxCount指明了第二个参数字符数组的大小。
下面结合GetCursorPos和WindowFromPoint举个例子,鼠标指向哪个窗口,就在界面显示那窗口的标题名
#include
#include
int main()
{
 char Text[256]={0};
 HWND wnd;
 POINT curpos;
 while(1)
 {
 GetCursorPos(&curpos);
 wnd = WindowFromPoint(curpos);
 GetWindowText(wnd,Text,256);
 printf("%s\n",Text);
 Sleep(300);
 }
return 0;
}
第四十五个SetWindowText根据窗口句柄设置窗口标题名
这个函数有两个参数,一个是窗口句柄,一个是标题名,这里就不需要解释了吧,直接看例子,设置一个窗口标题名,依旧以
"无标题.txt - 记事本"为例。
#include
#include
int main(int argc, char* argv[])
{
   HWND wnd;
   wnd=FindWindow(NULL,"无标题.txt - 记事本");//获取窗口句柄
   SetWindowText(wnd,"新的窗口标题");//设置窗口标题名
    return 0;
}
第四十六个GetCurrentProcess获得当前线程句柄
没有参数,直接调用即可,该函数返回线程句柄
第四十七个OpenProcessToken获得一个进程的访问令牌句柄
获得一个进程的访问令牌有什么用呢?主要是为了修改它的权限,前面在介绍结束一个进程的时候说过了,无法结束系统进程,是什么原因呢,原因是调用OpenProcess函数失败,无法获取系统进程句柄而引起的,那为什么会失败呢,权限不够,普通程序的进程没有SeDeDebug权限,而一个进程的权限是与访问令牌相关的,这样我们只要获取一个进程的访问令牌句柄,再以这个句柄为参数调用相应的函数提升进程的权限为SeDeDebug就可以获取系统进程句柄,进而结束它。
函数定义:BOOL OpenProcessToken(HANDLE ProcessHandle,DWORD DesiredAccess,PHANDLE TokenHandle)
第一个参数ProcessHandle待获取的进程句柄,第二个参数DesiredAccess操作类型,填TOKEN_ADJUST_PRIVILEGES就行了,
第三个TokenHandle是访问令牌句柄的指针,该参数接收句柄。
如获得本进程的访问令牌句柄:HANDLE hToken;
OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken);
第四十七个LookupPrivilegeValue函数查看对应系统权限的特权值,返回信息到一个LUID结构体里
上面讲过了,进程有权限一说,那么大家也能猜到,进程权限的信息也一定存储在一个结构体里,这个结构体描述了进程权限相关的一些信息。这个结构体在这里就不具体描述了,我们所要做的,只是把一个进程权限设置成SeDeDebug就行了,所以我们只要知道TOKEN_PRIVILEGES便是描述进程权限的结构体就可以了。而LookupPrivilegeValue函数是根据访问令牌句获取相应的权限信息吗?
不是的。TOKEN_PRIVILEGES结构里的Privileges[0].Luid跟这个函数所查询的东西相对应,也就是说,如果进程是SeDeDeBug权限,那Privileges[0].Luid的取值是怎样的呢?用LookupPrivilegeValue函数便可以获取其取值。
这个函数是这样定义的:BOOL LookupPrivilegeValue(LPCTSTR lpSystemName,LPCTSTR lpName,PLUID lpLuid);
第一个参数lpSystemName通常都填NULL,本地系统调用,第二个参数lpName填要查询的权限名,如要查询的是SeDeDebug权限则取值是SE_DEBUG_NAME,第三个参数lpLuid接收其取值。
如LUID luid;LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid);
第四十八个AdjustTokenPrivileges调整一个进程的访问令牌信息(权限)
函数定义:BOOL AdjustTokenPrivileges(HANDLE TokenHandle,BOOL DisableAllPrivileges,PTOKEN_PRIVILEGES NewState,DWORD BufferLength,PTOKEN_PRIVILEGES PreviousState,PDWORD ReturnLength)
第一个参数TokenHandle是令牌句柄,第二个是禁用所有权限标志,后面填FALSE就行了。第三个NewState是待刷进令牌句柄的PTOKEN_PRIVILEGES结构信息指针,第四个BufferLength指明TOKEN_PRIVILEGES结构大小,第五,六个参数填NULL就行了。
那么结束上面两个函数,提升一个进程权限制,让它能够结束系统进程的代码就是:
     HANDLE hToken;
     OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken );
     TOKEN_PRIVILEGES tp;
     LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid );
     tp.PrivilegeCount = 1;//tp里其它一些属性设置
     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
     AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof( TOKEN_PRIVILEGES ), NULL, NULL );
只上把上面的代码,加入结束普通进程例子的前面,那么就能结束系统进程了。
第四十九个LoadLibrary加载动态链接库,返回动态链接库模块句柄
该函数只有一个参数,那就是动态链接库的名称,如user32.dll,函数返回HMOUDLE类型的模块句柄,获得了一个动态链接库的模块句柄,就可以调用GetProcAddress函数获得模块里面的函数地址,从而调用动态链接库里的函数。
第五十个GetProcAddress根据模块句柄获取相应的函数地址
提到GetProcAddress函数,不得不讲一下怎么设计一个动态链接库,这里我们就以自己设计动态链接库作为GetProcAddress函数的例子。
动态链接库里的函数相对于头文件的函数有什么优势呢?更节省内存,相对于比较常用的函数而已。如果在一个程序里,调用一个头文件里的函数的话,那不管如何,函数的代码就会被复制一份到当前程序里,所以,当有十几个程序调用同一个函数的时候,这个函数在内存中所占用的空间,就会有十几份,分别在各自调用的进程内存空间里,而动态链接库的函数,只在内存中有一份空间(公用空间)如果哪个程序要用到这个函数的话,只要给这个函数的地址,进程就可以跑到这个空间执行函数,那么如何获取函数地址呢,用GetProcAddress函数就行了。
下面我们就自己设计一个动态链接库,点“文件->新建->工程",然后选中“Win32 Dynamic-Link Library”,再在右边给工程取一个名,点确定。接着弹出了一个对话框,询问希望创建什么类型,我们选择第二个“一个简单的DLL工程”,点完成->确定.然后单击右边的“+”号,很小的一个,接着下面会出现一个Globals的"+"号,单击该加号,然后再双击DllMain函数,进入代码编辑区,在这里编写代码,这里已经有了一些代码了,编译器自动生成的。那个DllMain函数,便是动态链接库的主函数。在程序进程加载动态链接的时候,进程会自动调用DllMain函数,也就是说会自动执行DllMain函数里的代码,也就是说,如果哪程序执行了这个语句“LoadLibrar("user32.dll")",那么执行这个语句的进程,便会自动执行user32.dll里的DllMain函数。如果是主线程加载动态库的话,那么该DllMain函数里的代码会被执行两次,分别是加载的时候执行一次,调用FreeLibrary函数释放或程序结束自动释放动态链接库的时候执行一次,至于是什么原因导致DllMain函数被调用,DllMain函数的第二个参数ul_reason_for_call说明了原因,它有四个取值,代表了四个原因。分别是:
DLL_PROCESS_ATTACH(进程加载),DLL_THREAD_ATTACH (线程加载)
DLL_THREAD_DETACH(线程释放),DLL_PROCESS_DETACH(进程释放)
因为这里我们只要设计一个动态链接函数,所以便不用管DllMain函数,DllMain函数将会在介绍CreateRemoteThread(创建一个远程线程)函数的时候讲到,所以我们只要在DllMain函数外定义一个函数就行了。
那么在动态链接库是如何定义函数呢?如果函数不需要导出的话,则跟普通函数定义没什么两样,导出是什么意思,就是可以用GetProcAddress函数获取地址的函数。那导出的函数要如何定义呢?
只要在函数前面加上extern "C" __declspec(dllexport)就行了,声明导出函数,防止函数重命名。那么接下来就举个例子。
动态链接里的代码:
#include "stdafx.h"
BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
      )
{
    return TRUE;
}
extern "C" __declspec(dllexport) int Add(int a,int b)
{
 return a+b;
}
点编译执行,然后就会弹出一个调试对话框,直接点取消,接着便生成了动态链接库DLL,然后到你的工程里把后缀名为dll的文件找到,
位置在MyProject\"你的工程名"\Debug下。接着把这个文件复制到要调用的工程下,或者直接复制C:\windows\system32目录下。
假设这个文件名为"sss.dll",那么要调用里面的Add函数便是如下代码:
 HMODULE hmod=::LoadLibrary("sss.dll");//获取sss.dll的模块,加载sss.dll动态链接库
 typedef int (*pAdd)(int a,int b);//定义一个对应的函数型,以便识别
 pAdd add=(pAdd)GetProcAddress(hmod,"Add");//获取hmod模块里的Add函数地址
 int a=add(3,5);//调用模块里的Add函数
第五十一个SetWindowsHookEx安装一个钩子
WINDOWS是基于消息的系统,鼠标移动,单击,键盘按键,窗口关闭等都会产生相应的消息,那么钩子是什么意思呢,它可以监控一个消息,比如在一个窗口里单击了一下,首先获得这个消息的,不是应用程序,而是系统,系统获取这个消息后,就去查看这个消息是在哪个窗口产生的,找到窗口后,再把消息投递到相应程序里的消息队列里,这之间有一个传递过程,那么钩子的作用就是在消息到达应用程序之前截获它,钩子可以关联一个函数(钩子处理函数),也就是说,如果对一个进程安装了一个钩子,进程再接收到相应在消息之前,会先去执行钩子所关联的函数,
先来看一下这个函数定义:
HHOOK WINAPI SetWindowsHookEx(int idHook,HOOKPROC lpfn,HINSTANCE hmod,DWORD dwThreadId)
第一个参数idHook指明要安装的钩子类型,如WH_KEYBOARD(键盘钩子),WH_MOUSE(鼠标钩子),第二个参数是钩子处理函数的地址,该函数必须是这种固定的格式:LRESULT WINAPI HookProc(int nCode,WPARAM wParam,LPARAM lParam)
第三个参数hmod是钩子函数所在模块的句柄,第四个参数dwThreadId是线程ID,待监视消息的ID,如果为0,则为全局钩子,监视所有消息
好,接下来我们举一个例子,钩子类型为WH_KEYBOARD,全局钩子。截获键盘按键消息,并扔掉该消息,让键盘失灵。
由于是装的是全局钩子,所以钩子处理函数必须放在动态链接库里。那么我们就设计一个动态链接库吧。
现给出动态链接库的所有代码:(KeyDll.dll)
#include "stdafx.h"
#include
BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
      )
{
    return TRUE;
}
HMODULE WINAPI ModuleFromAddress(PVOID pv)//该函数根据内存地址,获得其所在的模块句柄
{
 MEMORY_BASIC_INFORMATION mbi;
 VirtualQuery(pv,&mbi,sizeof(mbi));
 return (HMODULE)mbi.AllocationBase;
}
LRESULT CALLBACK HookKey(int nCode,WPARAM wParam,LPARAM lParam)
{
 return TRUE;//返回真,扔掉该消息
}
extern "C" __declspec(dllexport) void SetHook(void)
{
 SetWindowsHookEx(WH_KEYBOARD,HookKey,ModuleFromAddress(HookKey),0);
}
生成dll文件后,把它复制到相应的目录下去。
再新建一个工程,调用用动态链接库里的函数,代码如下:
#include
int main()
{
 HMODULE hMod=LoadLibrary("KeyDll.dll");
 typedef void(*pSetHook)(void);
 pSetHook SetHook=(pSetHook)GetProcAddress(hMod,"SetHook");
 SetHook();
 while(1)
 {
  Sleep(1000);//避免程序结束,自动释放动态链接库
 }
 return 0;
}
这样当按下了一个键后,接收该按键消息的进程,会先去执行钩子处理函数,然后再处理消息,而钩子处理函数的几个参数说明了按键的详细信息,如按了哪个键,是按下(KEYDOWN)还是松开(KEYUP)。如果有兴趣的话,把上面那钩子处理函数的代码换成下面这个
LRESULT CALLBACK HookKey(int nCode,WPARAM wParam,LPARAM lParam)
{
 char sz[25];
 sprintf(sz,"%c",wParam);//这个函数头文件#include
 MessageBox(NULL,sz,sz,MB_OK);
 return FALSE;
}
每按下一个键,就会弹出一个提示框,并输出所按下的键,只对字符键有用。
第五十二个SHGetFileInfo获取一个文件的各项信息(文件关联图标,属性等)
函数定义: DWORD SHGetFileInfo(LPCSTR pszPath, DWORD dwFileAttributes, SHFILEINFOA FAR *psfi, UINT cbFileInfo, UINT uFlags);
pszPath是文件的路径,dwFileAttributes一般取0,如果想要获取文件夹信息的话,则取值为FILE_ATTRIBUTE_DIRECTORY,psfi是一个SHFILEINFO结构的指针,该结构存储文件信息,定义如下:
typedef struct _SHFILEINFOA
{
        HICON       hIcon;                      // 文件关联图标句柄
        int         iIcon;                      // 系统图标列表索引
        DWORD       dwAttributes;               // 文件的属性
        CHAR        szDisplayName[MAX_PATH];    // 文件的路径名
        CHAR        szTypeName[80];             // 文件的类型名,如是bmp文件,还是执行文件exe,或者其它
} SHFILEINFO;
第四个参数cbFileInfo指明SHFILEINFO结构的大小,填sizoef(SHFILEINFO);
最后一个参数uFlags指定获取文件的什么信息,可选取值如下:(对应着SHFILEINFO里的成员)
    SHGFI_ICON; //获得图标
  SHGFI_DISPLAYNAME; //获得显示名
  SHGFI_TYPENAME; //获得类型名
  SHGFI_USEFILEATTRIBUTES; //获得属性
  SHGFI_LARGEICON; //获得大图标
  SHGFI_SMALLICON; //获得小图标
  SHGFI_PIDL; // pszPath是一个标识符
比如,我只要获取文件图标,那么参数填SHGFI_LARGEICON就行了。如果又想获取文件关联的图标,又想获取文件类型名,那么就是
SHGFI_LARGEICON|SHGFI_TYPENAME;
函数例子:
  SHFILEINFO   sfi;
  SHGetFileInfo("e:\\aa.bmp",0,&sfi,sizeof(sfi),
  SHGFI_ICON|SHGFI_LARGEICON|SHGFI_USEFILEATTRIBUTES|SHGFI_TYPENAME);
接着可以用DrawIcon函数画出文件关联图标:该函数定义:BOOL DrawIcon(HDC hDC,int X,int Y, HICON hlcon );
第五十三个RegCreateKeyEx在注册表里创建一个子键,或获取一个子键的句柄
在这里我们先来了解一下注册表的基本概念,打开运行对话框,输入regedit,然后回车,便打开了注册表编辑器,首先映入眼前的,便是五个根键
HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USER
HKEY_CURRENT_CONFIG
在根键下面便是主键了,如HKEY_CURRENT_CONFIG根键下有两个主键,分别是Software和System(可能会不一样),那么主键下面是什么呢,对了,就是跟 RegCreateKeyEx函数相关的子键,子键下面就是具体的键值项了,但也可以又是子键。键值有五种可选类型,分别是:字符串值(REG_SZ),二进制值(REG_BINARY),DWORD值(REG_DWORD),多字符串值(REG_MULTI_SZ)和可扩充字符值(REG_EXPAND_SZ)。键值项还有其它信息,它的名称,数据。
了解了上面这些东西,接着就来了解下RegCreateKeyEx函数的各个参数吧,先来看一下函数定义:
LONG RegCreateKeyEx (
    HKEY hKey,//根键句柄,指明要在哪个根键下创建子键,填根键名既可
    LPCSTR lpSubKey,//子键名,包含完整路径名
    DWORD Reserved,.//一般取0
    LPSTR lpClass,//一般取NULL
    DWORD dwOptions,//创建子键时的选项,可选值REG_OPTION_NON_VOLATILE,REG_OPTION_VOLATILE,这里取0既可
    REGSAM samDesired,//打开方式,填KEY_ALL_ACCESS,在任何情况都行。
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,//指定继承性,还是取0
    PHKEY phkResult,//子键对应句柄,待创建或打开的子键句柄将存储在该句柄里
    LPDWORD lpdwDisposition//打开还是创建子键,对应REG_CREATED_NEW_KEY和REG_OPENED_EXISTING_KEY
    );
在这里举一个例子,以便我们能更好的理解该函数。
在HKEY_CURRENT_CONFIG根键下的Software主键里创建一个名为MySelf的子键。
#include
int main()
{
  HKEY hroot;//子键句柄
 DWORD dwDisposition;//对应着最后一个参数
 RegCreateKeyEx(HKEY_CURRENT_CONFIG,"Software\\MySelf",0,NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);
  return 0;
}
第五十四个RegSetValueEx根据子键句柄在其下创建或修改一个键值
函数定义:LONG RegSetValueEx(
  HKEY hKey,           // 子键句柄
  LPCTSTR lpValueName, // 键值名称,如果提供的子键下没有该名称,则创建
  DWORD Reserved,      // 保留,填0
  DWORD dwType,        // 键值类型,
  CONST BYTE *lpData,  // 键值的数据
  DWORD cbData         // 键值的数据的大小
);
接着我们以增加开机自启动为例,来看一下函数是如何创建一个键值的,我们知道,像程序添加开机自启动一般都在
HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run下添加一个键值,键值类型为二进制(REG_SZ),而键值的数据就为要自启动程序的路径。
假设e盘下有一个AutoRun.exe的应用程序,让电脑开机时自动运行它。
#include
int main()
{
  HKEY hroot;//子键句柄
  DWORD dwDisposition;
  RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
   NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);
  RegSetValueEx(hroot,"AutoRun",0,REG_SZ,(BYTE *)"e:\\AutoRun.exe",sizeof("e:\\AutoRun.exe"));
  return 0;
}
第五十五个RegDeleteValue根据子键句柄删除其下的一个键值
这里直接举一个例子,删除RegSetValueEx函数创建的键值
#include
int main()
{
  HKEY hroot;//子键句柄
  DWORD dwDisposition;
  RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
   NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);
  RegDeleteValue(hroot,"AutoRun");//删除子键下名为AutoRun的键值
  return 0;
}
第五十六个RegQueryValueEx根据子键句柄获取一个键值数据,类型。
函数定义:LONG
RegQueryValueEx (
    HKEY hKey,//根键句柄
    LPCWSTR lpValueName,//键值名称
    LPDWORD lpReserved,//预留,填0
    LPDWORD lpType,//接收键值类型
    LPBYTE lpData,//接收键值数据
    LPDWORD lpcbData//接收数据的大小
    );
例子,获取RegSetValueEx函数创建的键值的类型,数据
#include
#include
int main()
{
  char Data[52];
  DWORD Size,Type;
  HKEY hroot;//子键句柄
  DWORD dwDisposition;
  RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
   NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);//获取根键句柄
  RegQueryValueEx(hroot,"AutoRun",0,&Type,(BYTE *)Data,&Size);//获取AutoRun的信息
  printf("键值名称:AutoRun ");
   switch(Type)
   {
   case REG_SZ:printf("键值类型:REG_SZ");break;
   case REG_BINARY:printf("键值类型:REG_BINARY");break;
   case REG_DWORD:printf("键值类型:REG_DWORD");break;
   case REG_MULTI_SZ:printf("键值类型:REG_MULTI_SZ");break;
   case REG_EXPAND_SZ:printf("键值类型:REG_EXPAND");break;
   }
   printf(" 键值数据:%s  %d\n",Data,Size);
  return 0;
}
第五十七个RegEnumValue根据子键句柄返回对应索引的键值信息(名称,数据,类型,子键下第一个键值索引为0,以此类推,函数成功执行返回ERROR_SUCCESS)
函数定义:LONG
RegEnumValue (
    HKEY hKey,//子键句柄
    DWORD dwIndex,//键值索引
    LPWSTR lpValueName,//接收键值名称,字符数组
    LPDWORD lpcbValueName,//指明数组大小
    LPDWORD lpReserved,//预留,0
    LPDWORD lpType,//键值类型,填NULL,不获取
    LPBYTE lpData,//键值数据,填NULL,不获取
    LPDWORD lpcbData//接收数据的大小,如果键值数据那项参数为NULL,则该项也为NULL
    );
例子:输出Run下的所有键值名
#include
#include
int main()
{
  char Name[52];
  int Index=0;
  DWORD dwSize=52;
  DWORD Size,Type;
  HKEY hroot;//子键句柄
  DWORD dwDisposition;
  RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
   NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);//获取根键句柄
  while(RegEnumValue(hroot,Index,Name,&dwSize,NULL,NULL,NULL,NULL)==ERROR_SUCCESS)
  {
   printf("%s\n",Name);
   Index++;//索引从0开始每次自增一,函数如果执行失败,则索引已到头
  }
  return 0;
}
其实也还可以扩充一下,可以像msconfig程序那样列出当前计算机的所有开机自启动程序,当然,注册表也不只就前面的那一个子键下可以添加自启动程序,在HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run下也可以添加,所以这些子键都需要去查看,更多添加自启动程序的子键可以到百度里去搜一下,大家如果掌握前面那几个注册表操作函数,可以结合起来试着做一个可以添加,查看,删除开机自启动程序的小程序。
第五十八个ExitWindowsEx关机,重启,注销计算机函数
这个函数只有两个参数,后一个参数为系统预留,填0就可以了,而第一个参数则,指明关机,还是重启,或注销,可选值如下:
EWX_LOGOFF//注销    EWX_REBOOT//重启 NT系统中需SE_SHUTDOWN_NAME 特权 EWX_SHUTDOWN//关机,需权限。
例子:关闭计算机,由于需要SE_SHUTDOWN_NAME权限,所以我们得先提升权限,代码如下:
#include
int main()
{
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken);
LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME,&tkp.Privileges[0].Luid);
tkp.PrivilegeCount=1;
tkp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken,FALSE,&tkp,0,(PTOKEN_PRIVILEGES)NULL,0);
::ExitWindowsEx(EWX_SHUTDOWN,0);
  return 0;
}
第五十九个VirtualAllocEx在其它的进程中分配内存空间
函数定义:LPVOID
VirtualAllocEx(
    HANDLE hProcess,//进程句柄,将会在该进程句柄相关的进程分配空间
    LPVOID lpAddress,//默认为系统指定,填NUL
    DWORD dwSize,//分配多大的内存
    DWORD flAllocationType,//填MEM_COMMIT
    DWORD flProtect//指定分配的内存属性,为PAGE_READWRITE,内存可读写
    );
函数返回分配的内存首地址,
第六十个CreateRemoteThread创建一个远程线程(在其它进程中创建线程)
函数定义:HANDLE
WINAPI
CreateRemoteThread(HANDLE hProcess,//进程句柄,函数将在这个进程句柄关联的进程创建线程
LPSECURITY_ATTRIBUTES lpThreadAttributes,
 DWORD dwStackSize,
    LPTHREAD_START_ROUTINE lpStartAddress,
    LPVOID lpParameter,
    DWORD dwCreationFlags,
    LPDWORD lpThreadId
    );
这个函数比CreateThread函数多了一个参数,就是这个函数的第一个hProcess(函数在该进程里创建线程),后面的六个参数跟第三十九个函数CreateThread的六个参数一样,这里就不再解释了。
例子:远程线程注入
创建一个远程线程,就必须得有一个线程函数供线程执行,而线程函数又不能在其它程序里。那要怎么办呢?大家看一下线程函数的定义,和LoadLibrary函数的定义,它们的定义相似,都是只有一个参数,而且每个程序都能调用LoadLibrary函数,这样我们便能把LoadLibrary函数作为线程函数。这样创建的线程就会去执行LoadLibrary函数。因而我们就有了一次让其它程序调用LoadLibrar函数的机会,并还可以指定LoadLibrary函数的参数(通过创建远程线程函数传递)。前面在动态链接库提到,一个程序如果调用LoadLibrary函数,它都会自动去执行相应动态链接库里的DllMain函数,所以我们自己可以编写一个动态链接库,在DllMain函数里写入想要其它程序执行的代码。再通过CreateRemoteThread函数在其它程序创建一个线程去执行LoadLibary加载我们已经编写好的动态链接库,这样就可以让其它程序执行我们的代码了。这里还有一个问题,CreateRemoteThread函数传递过去的参数,因为要供注入的那个程序访问,所以参数数据所存储的空间不能在调用CreateRemoteThread函数的程序里。必须调用VirtualAllocEx函数,在注入程序里分配一个空间,把数据(动态链接库的名称)存在里面,而新分配空间的首地址则作为CreateRemoteThread函数的参数传过去。这样注入程序访问的是自己的地址空间。
远程线程注入:
假设动态链接库为“ReCode.dll”它的代码如下:
#include
BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
      )//DllMain函数,只要加载这个动态链接库的程序,都会跑来执行这个函数
{//在这里填让其它程序执行的代码
 while(1)
 {
 MessageBox(NULL,"aaaa","aaaa",MB_OK);//简单的让其它程序每隔3秒弹出一个提示框
 Sleep(3000);
 }
    return TRUE;
}
编译运行,然后把生成的“ReCode.dll”文件复制到c:\\windows\\system23下去。
注入线程的代码:
//选择ctfmon.exe(输入法管理)作为我们要注入进线程的程序
#include
#include
#include
int main()

  char DllName[25]="ReCode.dll";
 HANDLE hProcess;//用于存储ctfmon.exe的进程句柄
 //先提升进程权限,使其能获取任何进程句柄,并对其进行操作
  HANDLE hToken;
     OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken );
     TOKEN_PRIVILEGES tp;
     LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid );
     tp.PrivilegeCount = 1;
     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
     AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof( TOKEN_PRIVILEGES ), NULL, NULL );
  ////////////////////////////////////////////////////////////////////////////
     //Process32First和Process32Next函数结合(寻找)获取ctfmon.exe进程ID号
  //再调用OpenProcess函数根据进程ID获得进程句柄
     PROCESSENTRY32 pe32;//进程相关信息存储这个结构里
     pe32.dwSize=sizeof(pe32);
      //给系统内的所有进程拍一个快照
     HANDLE hProcessSnap=::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
     BOOL bMore=::Process32First(hProcessSnap,&pe32);
      while(bMore)
   {
      if(strcmp("ctfmon.exe",pe32.szExeFile)==0)//如果找到进程名为ctfmon.exe
      hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pe32.th32ProcessID);//获取句柄
       bMore=::Process32Next(hProcessSnap,&pe32);//寻找下一个
   }
 
   //在ctfmon进程中分配空间
  LPVOID lpBuf=VirtualAllocEx(hProcess,NULL,strlen(DllName),MEM_COMMIT, PAGE_READWRITE );
  DWORD WrSize;
  //把DllName里的数据写入到分配的空间里
   WriteProcessMemory(hProcess, lpBuf, (LPVOID)DllName, strlen(DllName), &WrSize);
   //创建远程线程
   CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)LoadLibraryA,lpBuf,0,NULL);
   return 0;//程序使命完成,结束
}
当然,给一个程序安装钩子,也可以让指定的应用程序加载特定的动态链接库,但要了解,加载动态链接库的是是应用程序的主程序,你总不能让应用程序不干它自己的事,而来一直执行DllMain函数里的代码吧!而且即使这样,当安装钩子的程序退出或卸载钩子的时候,那么被系统强迫加载动态链接库的程序,也会自动释放动态链库,退出DllMain函数。如此,那就没有办法了吗?,办法肯定是有的,用CreateThread函数。当其它程序主线程执行DllMain函数的时候,使其调用CreateThread再创建一个线程,就行了    

以上就介绍了VC/MFC的学习,希望对VC/MFC有兴趣的朋友有所帮助。了解更多内容,请关注职坐标编程语言VC/MFC频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程