卓越飞翔博客卓越飞翔博客

卓越飞翔 - 您值得收藏的技术分享站
技术文章30771本站已运行3725

C&C++ 关于多开的一些记录

最近无事,在网上查阅防止程序多开的方式。主要有以下几种:
     1、通过互斥体/信号量/事件等防止多开
     2、内存文件映射
     3、DLL全局共享区
     4、使用FindWindow API 函数
     5、全局Atom
      。。。。

上述只是我找到的相关资料防止多开的方法


针对微信来说它是通过互斥体来防止多开

在网上实现微信多开的方式有
      1、通过patch 互斥体名称实现多开
      2、通过.bat脚本多开

这两种方法前者实现较为麻烦,第二种通过.bat脚本进行多开方式倒是很简单,但是有个缺点就是想要启动多个微信需要对.bat文件中的内容进行修改。对于是否成功启动程序也未知,程序的相关信息获取不到,总而言之,就是不理想。


于是就去查阅Windows 启动一个exe程序的api函数。
有以下API函数
        1、HINSTANCE ShellExecute( HWND hwnd, LPCTSTR lpOperation,LPCTSTR lpFile, LPCTSTR lpParameters, LPCTSTR lpDirectory, INT nShowCmd);
           参数说明
                     hWnd:用于指定父窗口的句柄。当函数调用过程中出现错误时,它将作为Windows消息窗口的父窗口。
                     lpOperation:用于指定要进行的操作, 如: open、runas、print、edit、explore、find。当此参数为NULL时,默认执行open操作。(open:表示执行由lpFile参数指定的程序,或者打开由lpFile参数指定的文件或文件夹。explort:表示打开由lpFile参数指定的文件夹。print:表示打印由lpFile参                            数指定的文件。)
                     lpParameters:若lpFile参数是一个可执行文件,则此参数指定命令行参数,否则此参数应设为NULL。
                     lpDirectory:用于指定默认目录
                     nShowCmd:用于指定程序窗口初始显示方式
         优点
                 它的功能比前WinExec()和system()更强大,它可以执行系统的Shell命令。比如可以启动一个默认的文字处理程序来打开txt文档,再比如可以启动一个默认浏览器来打开一个网址。
         缺点
                它虽然传回一个HINSTANCE,但他并不是真正的句柄,仅能拿它来做一些错误值检查。
      2、UINT WinExec(LPCSTR lpCmdLine, UINT uCmdShow);
         参数说明
                   lpCmdLine:指向一个空结束的字符串,串中包含将要执行的应用程序的命令行(文件名加上可选参数)。
                   uCmdShow:定义Windows应用程序的窗口如何显示,并为CreateProcess函数提供STARTUPINFO参数的wShowWindow成员的值。
         优点
               启动新进程后会立即返回,因此你的程序无需等待。
              通过参数uCmdShow,可以一定程度上控制窗体的显示,比如让它后台运行而不显示出来。
         缺点
              它完全与本进程脱离,无法对做些必要的控制
              无法得知启动的程序是否退出。
              得不到启动的程序的退出码。
             不能打开txt文件或网址
     3、system
        参数说明
               就一个参数 执行dos命令
        缺点
             它不会立即返回,直到你启动的程序执行完成。如果你启动是带界面的程序,调用此函数会自动打开一个控制台,给人感觉很不友好,但如果启动的程序本身是带控制台的,而且又需要等待它的完成,那这将是比较好的选择。
             它的返回值代表是否执行成功以及程序的退出码。
             不能运行打开txt或者网站
        优点
            执行各种系统命令
     4、BOOL CreateProcess(
                           LPCTSTR lpApplicationName,
                           LPTSTR lpCommandLine,
                           LPSECURITY_ATTRIBUTES lpProcessAttributes,
                           LPSECURITY_ATTRIBUTES lpThreadAttributes,
                           BOOL bInheritHandles,
                           DWORD dwCreationFlags,
                           LPVOID lpEnvironment,
                           LPCTSTR lpCurrentDirectory,
                           LPSTARTUPINFO lpStartupInfo,
                           LPPROCESS_INFORMATIONlpProcessInformation);
     参数说明
              lpApplicationName:指向一个NULL结尾的、用来指定可执行模块的字符串。这个字符串可以是可执行模块的绝对路径,也可以是相对路径,在后一种情况下,函数使用当前驱动器和目录建立可执行模块的路径。这个参数可以被设为NULL,在这种情况下,可执行模块的名字必须处于                          lpCommandLine参数最前面并由空格符与后面的字符分开。
              lpCommandLine:指向一个以NULL结尾的字符串,该字符串指定要执行的命令行。这个参数可以为空,那么函数将使用lpApplicationName参数指定的字符串当做要运行的程序的命令行。如果lpApplicationName和lpCommandLine参数都不为空,那么lpApplicationName参数指定将要被运行的模块,lpCommandLine参数指定将被运行的模块的命令行。新运行的进程可以使用GetCommandLine函数获得整个命令行。C语言程序可以使用argc和argv参数。
              lpProcessAttributes:指向一个SECURITY_ATTRIBUTES结构体,这个结构体决定是否返回的句柄可以被子进程继承。如果lpProcessAttributes参数为空(NULL),那么句柄不能被继承。在Windows NT中:SECURITY_ATTRIBUTES结构的lpSecurityDescriptor成员指定了新进程的安全描述符,如果参数为空,新进程使用默认的安全描述符。
              lpThreadAttributes:同lpProcessAttribute,不过这个参数决定的是线程是否被继承.通常置为NULL.
              bInheritHandles:指示新进程是否从调用进程处继承了句柄。如果参数的值为真,调用进程中的每一个可继承的打开句柄都将被子进程继承。被继承的句柄与原进程拥有完全相同的值和访问权限。
              dwCreationFlags:指定附加的、用来控制优先类和进程的创建的标志。以下的创建标志可以以除下面列出的方式外的任何方式组合后指定。
              lpEnvironment:指向一个新进程的环境块。如果此参数为空,新进程使用调用进程的环境。
              lpCurrentDirectory:指向一个以NULL结尾的字符串,这个字符串用来指定子进程的工作路径。这个字符串必须是一个包含驱动器名的绝对路径。如果这个参数为空,新进程将使用与调用进程相同的驱动器和目录。这个选项是一个需要启动应用程序并指定它们的驱动器和工作目录的外壳程序的主要条件。
              lpStartupInfo:指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体。
              lpProcessInformation:指向一个用来接收新进程的识别信息的PROCESS_INFORMATION结构体。
     优点
               功能十分强大,可以指定很多参数
    缺点
            参数太多,使用起来比较麻烦

然后我就一一去测试上述函数,然后发现一个令人惊喜的结果,通过CreateProcess()函数能成功的绕过互斥体多开检测并运行。


微信在注册表中有写程序的安装目录,可以通过注册表查找到微信的安装位置然后,再去调用该函数去启动微信。

下面是示例代码
#include <fstream>
#include <cstdlib>
#include <Windows.h>
#include <TlHelp32.h>
#include <tchar.h>
void startWeChatByReg() {
    std::string str;
    std::wstring path;
    HKEY hKey;
    LONG result = RegOpenKeyEx(HKEY_CURRENT_USER, L"SOFTWARE\\Tencent\\WeChat", 0, KEY_ALL_ACCESS, &hKey);
    if (result == ERROR_SUCCESS) {
        std::cout << "成功打开注册表键" << std::endl;
 
        // 在这里可以执行其他与注册表相关的操作,例如读取或写入值
        wchar_t buffer[256] = { 0 };
        DWORD bufferSize = sizeof(buffer);
        result = RegQueryValueEx(hKey, L"InstallPath", nullptr, nullptr, reinterpret_cast<LPBYTE>(buffer), &bufferSize);
        std::wstring reg;
        path += buffer;
        if (result == ERROR_SUCCESS) {
 
            std::wcout << "读取的值为: " << buffer << std::endl;
        }
        else {
            std::cerr << "无法读取值,错误代码为 :" << result << std::endl;
        }
        // 关闭注册表键
        RegCloseKey(hKey);
    }
    else {
        std::cerr << "无法打开注册表键,错误代码:" << result << std::endl;
    }
 
 
 
    std::wcout << "path value is : " << path << std::endl;
    path += L"\\WeChat.exe";
    std::wcout << "path value is : " << path << std::endl;
 
    STARTUPINFO StartInfo;
    PROCESS_INFORMATION pInfo;
    memset(&StartInfo, 0, sizeof(STARTUPINFO));
    StartInfo.cb = sizeof(STARTUPINFO);
    // StartInfo.dwFlags = STARTF_USEPOSITION | STARTF_USESIZE;
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    for (int i = 0; i < 4; i++) {
        CreateProcess(path.c_str(), NULL, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &StartInfo, &pInfo);
        SetConsoleTextAttribute(hConsole, FOREGROUND_RED);
        printf("StartInfo.lpTitle :%ls\n", StartInfo.lpTitle);
        printf("StartInfo.lpDesktop : %ls\n", StartInfo.lpDesktop);
        printf("StartInfo.dwX : %d\n", StartInfo.dwX);
        printf("StartInfo.dwY : %d\n", StartInfo.dwY);
        printf("StartInfo.dwXSize : %d\n", StartInfo.dwXSize);
        printf("StartInfo.dwYSize : %d\n", StartInfo.dwYSize);
        printf("pInfo.dwProcessId : %d\n", pInfo.dwProcessId);
        printf("pInfo.dwThreadId : %d\n", pInfo.dwThreadId);
        SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
        printf("================================================\n");
 
    }
 
    // 恢复默认文本颜色 FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
    SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
 
}
 
卓越飞翔博客
上一篇: C&C++ ModBus数据CRC计算小工具
下一篇: 返回列表
留言与评论(共有 0 条评论)
   
验证码:
隐藏边栏