吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2953|回复: 21
上一主题 下一主题
收起左侧

[系统底层] win32笔记三 创建进程

  [复制链接]
跳转到指定楼层
楼主
huchen 发表于 2024-4-16 22:40 回帖奖励
本帖最后由 huchen 于 2024-4-22 00:12 编辑

之前的索引


创建进程

在创建一个进程之前,首先介绍一个win API:CreateProcess()

给出定义

BOOL CreateProcess(
  [in, optional]    LPCTSTR lpApplicationName,                // name of executable module
  [in, out, optional]   LPTSTR lpCommandLine,                     // command line string
  [in, optional]    LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD
  [in, optional]    LPSECURITY_ATTRIBUTES lpThreadAttributes,  // SD
  [in]          BOOL bInheritHandles,                     // handle inheritance option
  [in]          DWORD dwCreationFlags,                     // creation flags
  [in, optional]    LPVOID lpEnvironment,                      // new environment block
  [in, optional]    LPCTSTR lpCurrentDirectory,                // current directory name
  [in]          LPSTARTUPINFO lpStartupInfo,               // startup information
  [out]         LPPROCESS_INFORMATION lpProcessInformation // process information
);

可以看到参数是非常多的

但是目前我们只需要知道其中的四个就可以了

lpApplicationName

应用程序路径,也就是文件的路径(我认为最好写上绝对路径),必须包含文件扩展名

lpCommandLine

命令行参数,在windows中,有两种启动.exe文件的方法,一个是双击,还有一个就是在终端输入命令,例如

#include <stdio.h>
int main(int argc,char* argv[])
{
    printf("%s - %s - %s",argv[0],argv[1],argv[3]);
    getchar();
    return 0;
}

比如上面的代码,也许见过很多次,但从没用过,这里就演示一次

编译上面的代码,并找到exe文件的路径

这里我输入了参数 AS 1   2   3   4   5

AS就算第一个参数,依次类推,参数之间以空格隔开

这就是命令行参数

lpStartupInfo

指定创建时进程的窗口工作站、桌面、标准句柄和main窗口的外观。

结构体

typedef struct _STARTUPINFOA {
  DWORD  cb;
  LPSTR  lpReserved;
  LPSTR  lpDesktop;
  LPSTR  lpTitle;
  DWORD  dwX;
  DWORD  dwY;
  DWORD  dwXSize;
  DWORD  dwYSize;
  DWORD  dwXCountChars;
  DWORD  dwYCountChars;
  DWORD  dwFillAttribute;
  DWORD  dwFlags;
  WORD   wShowWindow;
  WORD   cbReserved2;
  LPBYTE lpReserved2;
  HANDLE hStdInput;
  HANDLE hStdOutput;
  HANDLE hStdError;
} STARTUPINFOA, *LPSTARTUPINFOA;

这个不用了解的太详细,只用知道第一个cd的含义,这个就是存储的这个结构体有多大,方便后续这个结构体的更新而使大小变大,从而带来不易计算大小的麻烦,这个成员可以在很多的结构体里看到

前面还有个[in],这是个in参数,表示需要你传进去给当前的程序用,即lpStartupInfo参数给CreateProcess函数提供参数

lpProcessInformation

包含有关新创建的进程及其主线程的信息

结构体

typedef struct _PROCESS_INFORMATION {
  HANDLE hProcess;
  HANDLE hThread;
  DWORD  dwProcessId;
  DWORD  dwThreadId;
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;

可以看到有四个成员,分别是:进程句柄、线程句柄、进程ID、线程ID

这里先不涉及句柄是什么,后面在详细说明

这里也就应证了,创建进程的时候至少会创建一个线程

这里的[out],意思为out参数,表示当我这个程序执行完成,会通过这个指针写结果,即当程序执行成功,结果会写入到PROCESS_INFORMATION定义的指针里,相当于返回值

in参数只管往里传,out参数只管往回传


代码实验

//环境:win10 VS2022 
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <windows.h>

//启动参数的相关信息,后面的拓展会提到
void GetStartInfo()
{
    STARTUPINFO si;
    GetStartupInfo(&si);
    printf("%X %X %X %X %X %X %X %X\n", si.dwX, si.dwY, si.dwXCountChars, si.dwYCountChars, si.dwFillAttribute, si.dwXSize, si.dwYSize, si.dwFlags);
}

//创建一个子进程
BOOL CreateChildProcess(PTCHAR ChildProcessName, PTCHAR CommandLine)
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    ZeroMemory(&si, sizeof(si));        //置0
    ZeroMemory(&pi, sizeof(pi));        //置0
    si.cb = sizeof(si);                 //这个成员是要初始化的

    //创建子进程 返回是否成功
    if (!CreateProcess(
        ChildProcessName,   //对象名称的完整路径
        CommandLine,        //命令行参数
        NULL,               //不继承进程句柄
        NULL,               //不继承线程句柄
        FALSE,              //不继承句柄
        0,                  //没有创建标志
        NULL,               //使用父进程环境变量
        NULL,               //使用父进程目录作为当前目录,可以自己设置目录
        &si,                //STARTUPINFO结构体
        &pi)                //PROCESS_INFORMATION结构体
        )
    {
        printf("创建进程错误 代码:%d\n", GetLastError());
        return FALSE;
    }
    printf("进程句柄:%X\t进程ID:%X\n线程句柄:%X\t线程ID:%X\n", pi.hProcess,pi.dwProcessId, pi.hThread,pi.dwThreadId);

    //释放句柄
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    return TRUE;
}

int main(int argc,char* argv[])
{
    //C:\\Program Files\\Internet Explorer\\iexplore.exe
    //C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe
    TCHAR ApplicationName[] = TEXT("C:\\Program Files\\Internet Explorer\\iexplore.exe");
    TCHAR CmdLine[] = TEXT(" https://www.baidu.com");   //注意前面要加个空格
    CreateChildProcess(ApplicationName, CmdLine);       //调用函数
    //GetStartInfo();

    getchar();          //终端输入,类似于断点的作用
    return 0;
}

运行结果

程序也是执行成功,进程与线程的东西也是获取到了

(有个小问题,我用msedge来测试的时候不行,不给我弹百度,但是在终端命令行参数输入的时候却可以,不得已换成iexplore,还请大佬说说其中的缘由)

拓展1

在代码中,会注意到这里

//启动参数的相关信息,后面的拓展会提到
void GetStartInfo()
{
    STARTUPINFO si;
    GetStartupInfo(&si);
    printf("%X %X %X %X %X %X %X %X\n", si.dwX, si.dwY, si.dwXCountChars, si.dwYCountChars, si.dwFillAttribute, si.dwXSize, si.dwYSize, si.dwFlags);
}

这里就是lpStartupInfo参数的东西,我选择了一部分来打印

(运行的时候记得把该函数调用)

首先在VS里直接运行看效果

发现全是零

那么如果我双击运行呢

会发现不同

那在其他的调试器呢?这里不浪费篇幅,直接说结果,xdbg为全0,OD为全0

DTDebug

可以发现,不同的调试器,所给的值是不同的,那么就可以做反调试的内容了(理论上)

拓展2

在代码中有这样一段

printf("创建进程错误 代码:%d\n", GetLastError());

GetLastError()函数:检索调用线程的最后错误代码值。 最后一个错误代码按线程进行维护。 多个线程不会覆盖彼此的最后一个错误代码。

如果出现了这个问题,会在终端输出一个错误代码,例如

我把路径修改一下,然后运行

会得到一个2,那我就可以去查询是什么错误

点击菜单栏上的工具,选择错误查找,然后输入错误代码,就可以知道是什么问题了

总结


1.进程的创建通过API来实现

2.命令行参数的解释

3.启动信息以及进程相关信息的输出

4.错误代码的查询

免费评分

参与人数 9吾爱币 +15 热心值 +9 收起 理由
lenxueyan + 1 + 1 鼓励转贴优秀软件安全工具和文档!
tomhex + 1 + 1 谢谢@Thanks!
dafs + 1 + 1 认真学习 我很赞同!
woyucheng + 1 + 1 我很赞同!
willJ + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
唐小样儿 + 1 + 1 我很赞同!
为之奈何? + 1 + 1 我很赞同!
xjy010305 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
FchiyuT + 1 + 1 鼓励转贴优秀软件安全工具和文档!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
xialichen 发表于 2024-4-17 19:46
非常感谢楼主的分享,我在练习的时候发现一个情况
我用子线程拉起一个模拟点击的程序,能正常拉起来,但是不能正常点击,然而如果我手动传入参数却可以,您知道如何解决吗?[已经尝试管理员身份运行主程序还是不行]
[C++] 纯文本查看 复制代码
#include <iostream>
#include <Windows.h>
#include <string>

void click(HWND& hwnd, POINT& p)
{
	PostMessage(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELONG(p.x, p.y));
	Sleep(10);
	PostMessage(hwnd, WM_LBUTTONUP, 0, MAKELONG(p.x, p.y));
}

int main(int argc, char* argv[])
{
	if (argc < 5)
	{
		std::cerr << "参数缺失\n用法: " << argv[0] << " [HWND] [X] [Y] [Sleep时长]" << std::endl;
		return 1;
	}
	//解析命令参数
	HWND hwnd = reinterpret_cast<HWND>(std::stoull(argv[1], nullptr, 16));
	int x = std::stoi(argv[2]);
	int y = std::stoi(argv[3]);
	POINT p = { x, y };
	std::cout << "HWND: " << hwnd << std::endl;
	std::cout << "Point: (" << p.x << ", " << p.y << ")" << std::endl;
	std::cout << "SleepTime: " << std::stoi(argv[4]) << std::endl;
	Sleep(std::stoi(argv[4]));
	click(hwnd, p);
	return 0;
}
沙发
magiclyan 发表于 2024-4-16 23:21
lz请注意某些论坛对于技术性文章享有著作权禁止以任何方式转载
3#
MJ_B 发表于 2024-4-17 00:36
edge打开baidu
CreateProcess("msedge.exe", "1 https://www.baidu.com",xxxxxx)
或者
CreateProcess(NULL, "msedge.exe https://www.baidu.com",xxxxxx)
4#
xiaoxiao0000xia 发表于 2024-4-17 01:21
学习学习
5#
xiaohong 发表于 2024-4-17 06:35
666666666666666666
6#
 楼主| huchen 发表于 2024-4-17 10:49 |楼主
MJ_B 发表于 2024-4-17 00:36
edge打开baidu
CreateProcess("msedge.exe", "1 https://www.baidu.com",xxxxxx)
或者

不传百度那个参数进去也能正常运行,传了百度参数进去,结果还是跟没传一样
7#
 楼主| huchen 发表于 2024-4-17 10:50 |楼主
magiclyan 发表于 2024-4-16 23:21
lz请注意某些论坛对于技术性文章享有著作权禁止以任何方式转载

好的,我以后会注意的,感谢
8#
MJ_B 发表于 2024-4-17 11:21
huchen 发表于 2024-4-17 10:49
不传百度那个参数进去也能正常运行,传了百度参数进去,结果还是跟没传一样

试了这两种写法吗?这两种写法都可以
CreateProcess("msedge.exe", "1 https://www.baidu.com",xxxxxx)
CreateProcess(NULL, "msedge.exe https://www.baidu.com",xxxxxx)
9#
PJ997272250 发表于 2024-4-17 12:51

学习学习
10#
jing11070413 发表于 2024-4-17 16:14
学到了,感谢大佬的分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-21 16:02

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表