MENU

Windows编程快速入门

July 12, 2022 • Read: 508 • 开发日常

Windows编程快速入门

简述

Windows编程其实就是面向MSDN编程,基本上是根据官方文档以及demo或者第三方分享的demo进行编写即可。这里主要记录一些查看官方文档的小技巧以及一些小坑供学习记录用

直接调用API编程又称为SDK编程,SDK即software develop kit的缩写,它包含了Windows软件开发的文档和api函数的输入库,头文件。

编写环境

  • vs studio 2022 装有组件 使用c++桌面开发
  • 新建项目选择c++的Windows桌面向导即可,旧版本的vs就找到win32控制台项目

编写总结

基础知识

MSDN文档地址[Microsoft Docs](Microsoft Docs)

调用系统的API函数必须要有一个头文件和导入库,一般是#inclode<Windows.h>即可,导入库会默认链接,若是没有链接则需要手动添加pragma comment(lib,"xxx")

常用的数据类型

  • DWORD:32位无符号整型数据(DWORD32)
  • LONG:32位符号整型(LONG32)
  • HKEY:注册表键的句柄
  • LPSTR:字符指针,也就是字符串变量
  • LPCSTR:字符串常量
  • LPCTSTR:根据环境配置,如果定义了UNICODE宏,则是LPCWSTR类型,否则则为LPCSTR类型
  • LPCWSTR:UNICODE字符串常量
  • LPDWORD:指向DWORD类型数据的指针
  • TCHAR:如果定义了UNICODE,则为WCHAR,否则为CHAR
  • WCHAR:16位Unicode字符

Windows 数据类型名命名的规律

  • 无符号类型:一般是以“U”开头,比如“INT”对应的“UINT”。
  • 指针类型:其指向的数据类型前加“LP”或“P”,比如指向 DWORD 的指针类型为“LPDWORD”和“PDWORD”。
  • 句柄类型:以“H”开头。比如,HWND 是window(WND简写)也就是窗口的句柄,菜单(MENU)类型对应的句柄类型为 “HMENU” 等等。

熟悉了以上的数据类型之后,基本就能看懂一些api函数的参数到底要怎么填写了,填写的值是什么,

一些实例

例如,入门的小demo,也是内网中一些工具常有的,就是开启远程桌面。
这里可以通过c++操作注册表来开启
了解注册表知识可看[C++ 注册表编程 - 程序员大叔 - 博客园 (cnblogs.com)](C++ 注册表编程 - 程序员大叔 - 博客园 (cnblogs.com))

对于编程来说,基本的操作流程就是

  1. 使用 RegOpenKeyEx函数获取一个注册表访问的句柄
  2. 使用句柄,然后调用RegQueryValueEx进行值查询,调用RegSetValueEx进行项值的修改

RegOpenKeyEx官方文档)

LONG RegOpenKeyEx( 
HKEY hKey, 
LPCWSTR lpSubKey, 
DWORD ulOptions, 
REGSAM samDesired, 
PHKEY phkResult );

Pasted image 20220712160442.png
Pasted image 20220712161434.png

这里还是建议不要使用翻译,直接看原文会更好理解参数的意思
第一个参数HKEY hKey就是主键的意思,即对应注册表中的四大目录。
第二个参数LPCWSTR lpSubKey是子键的意思,就是到指定项剩余的目录,如SYSTEM\CurrentControlSet\Control\Terminal Server
第三个参数DWORD ulOptions就不用管太多,设置为0即可
第四个参数REGSAM samDesired,如果不需要权限则设为0,意思就是操作注册表的一些权限,有读,有写等。这里查询之后使用最高权限KEY_ALL_ACCESS
第五个参数PHKEY phkResult,一个指针用来接收打开的主键的,意思就是一个打开主键的句柄。

小demo

/*踩的一些坑,得使用
1. HKEY来声明接受的句柄,不能完全按照文档中的PHKEY,因为下面使用别的一些方法的时候用到这个key,而HKEY不兼容PHEY,因此这里直接都用HKEY就行了。
2. 子键类型是LPCWSTR,按照规则,是一个Unicode字符串常量,因此在引号前还需要添加一个L进行转换,或者使用TEXT("xx")转换也可以
3. 最后一个参数其实是一个指针变量,在指针变量的情况下,需要使用&,来取地址
*/
    HKEY key2;
    
    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Terminal Server", 0, KEY_ALL_ACCESS, &key2)) {
    
}

RegQueryValueEx官方文档地址)

LONG RegQueryValueEx( 
HKEY hKey, 
LPCWSTR lpValueName, 
LPDWORD lpReserved, 
LPDWORD lpType, 
LPBYTE lpData, 
LPDWORD lpcbData );

Pasted image 20220712164645.png
Pasted image 20220712164702.png
Pasted image 20220712164729.png

  • 第一个参数HKEY hKey就是打开了的句柄
  • 第二个参数LPCWSTR lpValueName是要查询的项的名称,类型是Unicode,记得加L
  • 第三个参数LPDWORD lpReserved设为NULL即可
  • 第四个参数LPDWORD lpType,指向DWORD类型数据的指针,指向关联的数据类型的指针,这里尽量跟注册表中的保持一致,否则数据可能会被强制转换。若不想考虑也可以设置为NULL。
  • 第五个参数LPBYTE lpData,指针类型的byte数据,用来接收查询的值,
  • 第六个参数,LPDWORD lpcbData,一个指向DWORD类型数据的指针,用来计算第五个参数的返回大小的。

完整的demo

//值得注意的是若是指针变量的话,就创建一个,然后使用的时候记得加上取地址符&
    HKEY key2;
    DWORD dwType = REG_DWORD;
    DWORD dwValue;
    DWORD dwSize = sizeof(REG_DWORD);

if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Terminal Server", 0, KEY_ALL_ACCESS, &key2)) {
        if (ERROR_SUCCESS==RegQueryValueEx(key2, L"fDenyTSConnections", NULL, NULL, (LPBYTE)&dwValue,&dwSize)) {
            cout <<"当前的状态" << dwValue << endl;
            if (dwValue == 1) {
                cout << "远程桌面状态为:关闭" << endl;
                cout << "是否开启远程桌面?Y/N" << endl;
                char flag = 'N';
                cin >> flag;
                if (flag == 'Y'|| flag=='y') {
                    DWORD open = 0;
                    if (ERROR_SUCCESS == RegSetValueEx(key2, L"fDenyTSConnections", 0, dwType, (LPBYTE)&open, sizeof(DWORD))){
                        cout << "成功开启" << endl;
                    }
                }
            }
            else
            {
                cout << "远程桌面状态为:开启" << endl;
            }

        }
        
        
    }
    //及时关闭句柄
    RegCloseKey(key2);

以上就是快速入门Windows编程的一些小技巧,看完这篇在实际上手编写一两个项目,就差不多可以自己动手编写一些内网的小工具了。其实内网的本质就是信息收集的过程和绕过的过程,现在有些杀毒软件的查杀其实很多都是规则类查杀,这时候去搜集一些系统的不怎么知名的调用api,说不定就能绕过杀软,如果是基于内存的检测,那就需要继续学习汇编吧,使用olldbg慢慢的调试