首页 > 其他分享 >滴水逆向笔记系列-win32总结4-50.创建线程-51.线程控制_CONTEXT结构

滴水逆向笔记系列-win32总结4-50.创建线程-51.线程控制_CONTEXT结构

时间:2024-03-16 23:35:28浏览次数:21  
标签:return CONTEXT 51 50 szBuffer 线程 context DWORD

第五十课 win32 创建线程

1.进程与线程

  • 程序就是在硬盘里还没跑起来的二进制文件,进程就是已经运行中的程序,一个进程至少有一个线程,比如一个正在举行的活动需要几十个人帮忙干活,进程就是那个活动,线程就是那几十个人
  • 一个线程启动是需要占用一个cpu的
  • 一个新线程也会创建一个新堆栈
  • 进程就是一个4GB容器,线程就是EIP

2.创建线程

HANDLE CreateThread(				
  LPSECURITY_ATTRIBUTES lpThreadAttributes, // 安全属性 通常为NULL				
  SIZE_T dwStackSize,                       // 参数用于设定线程可以将多少地址空间用于它自己的堆栈				
				        					// 每个线程拥有它自己的堆栈
  LPTHREAD_START_ROUTINE lpStartAddress,    // 参数用于指明想要新线程执行的线程函数的地址				
  LPVOID lpParameter,                       // 线程函数的参数				
				        					// 在线程启动执行时将该参数传递给线程函数
				        					// 既可以是数字,也可以是指向包含其他信息的一个数据结构的指针
  DWORD dwCreationFlags,                    // 0 创建完毕立即调度  CREATE_SUSPENDED创建后挂起				
  LPDWORD lpThreadId                        // 线程ID 				
);				
				        					// 返回值:线程句柄

lpThreadId是个out类型,相当于也是一个返回值,会把线程id写进这里面

关闭句柄

下面代码并没有关闭线程,只是关闭了句柄
image.png

线程句柄与线程ID

  • 线程是由Windows内核在0环负责创建与管理的,正常我们是访问不了0环内核层的,句柄相当于一个令牌,有这个令牌就可以使用线程对象
  • 线程ID是身份证,唯一的,系统进行线程调度的时候要使用的.

线程传参问题


#include <iostream>
#include <windows.h>

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	int* p = (int*)lpParameter;
	for (int i = 0; i < 1000; i++)
	{
		Sleep(1000);
		printf("--------%d---\n", *p);
	}
	return 0;
}

void Mytest()
{
	int x = 2;
	HANDLE hThread = ::CreateThread(NULL, 0, ThreadProc, &x, 0, NULL);
	::CloseHandle(hThread);
}

int main()
{
	Mytest();
	Sleep(12000);
	return 0;
}

这里为什么运行后打印不出2,而是一个不知道什么地址?
image.png
因为这里是x变量是局部变量,而我们创建了一个新线程去循环打印x,我们新线程会一直打印,但mytest那边可能已经结束了,结束后x变量这个地址也就没了,所以会打印出奇怪的数字
所以我们可以采用全局变量去传参或者强转
image.png

3.倒计时程序(线程问题)

#include <windows.h>
#include "resource.h"
#include <stdio.h>

HWND hEDIT;
VOID Timer()
{
	//获取子窗口数值
	TCHAR szBuffer[10];
	DWORD dwTimer;
	memset(szBuffer, 0, 10);
	GetWindowText(hEDIT, szBuffer, 10);
	//转成整型
	sscanf(szBuffer, "%d", &dwTimer);
	while (dwTimer > 0)
	{
		
		Sleep(1000);
		sprintf(szBuffer, "%d", --dwTimer);

		SetWindowText(hEDIT, szBuffer);
	}
	
}
BOOL CALLBACK DialogProc(
	HWND hwndDlg,  // handle to dialog box			
	UINT uMsg,     // message			
	WPARAM wParam, // first message parameter			
	LPARAM lParam  // second message parameter			
)
{

	switch (uMsg)
	{
	case  WM_INITDIALOG:
	{

		//TCHAR szBuffer[5];

		hEDIT = GetDlgItem(hwndDlg, IDC_EDIT1);

		SetWindowText(hEDIT, TEXT("1000"));

		return TRUE;
	}
	case  WM_CLOSE:

		EndDialog(hwndDlg, 0);

		return TRUE;

	case  WM_COMMAND:

		switch (LOWORD(wParam))
		{
		case   IDC_BUTTON1:
			
			Timer();
			return TRUE;

		}
		break;
	}
	return FALSE;
}

int CALLBACK WinMain(
	_In_  HINSTANCE hInstance,
	_In_  HINSTANCE hPrevInstance,
	_In_  LPSTR lpCmdLine,
	_In_  int nCmdShow
)
{
	DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogProc);
	return 0;
}

为什么按照上面的写法程序会直接卡死?
因为我们把倒计时功能写在了主线程,主线程被用去跑倒计时了,程序也就卡死了,所以这时候我们应该另外创建一个线程去跑
image.png

#include <windows.h>
#include "resource.h"
#include <stdio.h>

HWND hEDIT;
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	//获取子窗口数值
	TCHAR szBuffer[10];
	DWORD dwTimer;
	memset(szBuffer, 0, 10);
	GetWindowText(hEDIT, szBuffer, 10);
	//转成整型
	sscanf(szBuffer, "%d", &dwTimer);
	while (dwTimer > 0)
	{
		
		Sleep(1000);
		sprintf(szBuffer, "%d", --dwTimer);

		SetWindowText(hEDIT, szBuffer);
	}
	return 0;
}


BOOL CALLBACK DialogProc(
	HWND hwndDlg,  // handle to dialog box			
	UINT uMsg,     // message			
	WPARAM wParam, // first message parameter			
	LPARAM lParam  // second message parameter			
)
{

	switch (uMsg)
	{
	case  WM_INITDIALOG:
	{

		//TCHAR szBuffer[5];

		hEDIT = GetDlgItem(hwndDlg, IDC_EDIT1);

		SetWindowText(hEDIT, TEXT("1000"));

		return TRUE;
	}

	case  WM_CLOSE:

		EndDialog(hwndDlg, 0);
		return TRUE;

	case  WM_COMMAND:

		switch (LOWORD(wParam))
		{
		case   IDC_BUTTON1:
			
			HANDLE hThread = ::CreateThread(NULL, 0, ThreadProc, 0, 0, NULL);
			return TRUE;

		}
		break;
	}

	return FALSE;
}

int CALLBACK WinMain(
	_In_  HINSTANCE hInstance,
	_In_  HINSTANCE hPrevInstance,
	_In_  LPSTR lpCmdLine,
	_In_  int nCmdShow
)
{
	DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogProc);
	return 0;
}

Dialog问题

1、提示函数使用不安全是可以在属性设置禁用安全检查
2、字符串类型不匹配的报错可以尝试设置符合模式为否或者修改字符集

需要用到的API函数:

:::info
文本框赋值:
SetWindowText(句柄,数据缓存区);
文本框取值:
GetWindowText(句柄,数据缓冲区,长度);
数字转字符:
sprintf(数据缓冲区,"%d",数字);
字符转数字:
sscanf( szBuffer, "%d", &dwTimer );
获取子窗口:
GetDlgItem(hDlg,IDC_EDIT_TIMER);
:::

第五十一课 win32 线程控制_CONTEXT结构

进程就是4GB,线程就是EIP

1.线程控制函数

挂起线程:

::SuspendThread(hThread);

恢复线程:

::ResumeThread(hThread);

终止线程:

方式一:
::ExitThread(DWORD dwExitCode);
可以看到并不是用句柄控制线程,所以一般用在线程函数内,执行会返回一个值,会直接清除堆栈,没办法去释放资源
image.png
方式二:
线程函数返回(自然死亡)自然退出循环后执行后面的代码(一般是释放资源等操作),然后线程自然结束
image.png
方式三:
::TerminateThread(hThread,2);
::WaitForSingleObject(hThread,INFINITE);
TerminateThread函数属于异步调用,他会另起一个线程去执行结束这个线程,所以代码还是在继续往下走的,所以如果后面有函数需要结束线程后才能运行的话,就有可能出现线程后面结束就执行函数的问题,所以我们需要使用WaitForSingleObject函数在后面堵塞住确定线程被关闭后才继续运行,而且不会清理堆栈
image.png

判断线程是否结束

BOOL GetExitCodeThread(				
    HANDLE hThread,				
    LPDWORD lpExitCode				
);
STILL_ACTIVE 正在运行								
			
参数:				
hThread: 要结束的线程句柄								
dwExitCode: 指定线程的退出代码。可以通过GetExitCodeThread来查看一个线程的退出代码								

2.CONTEXT结构体(线程切换)

每个线程在执行的时候,都会独自占用一个CPU,当系统中的线程数量 > CPU的数量时,就会存在多个线程共用一个CPU的情况。但CPU每次只能运行一个线程,Windows每隔20毫秒会进行线程的切换,那比如线程A执行到地址:0x2345678 eax:1 ecx:2 edx:3 ebx:4...还有eflag标志寄存器中的值等等。。。
此时,线程执行时间到了,被切换到了线程B。。。。当线程B的时间片也到了,再切换会线程A时,系统是如何知道该从哪个地址开始执行呢?被切换前用到的各种寄存器的值该如何恢复呢?

typedef struct _CONTEXT {							
							
    //							
    // The flags values within this flag control the contents of							
    // a CONTEXT record.							
    //							
    // If the context record is used as an input parameter, then							
    // for each portion of the context record controlled by a flag							
    // whose value is set, it is assumed that that portion of the							
    // context record contains valid context. If the context record							
    // is being used to modify a threads context, then only that							
    // portion of the threads context will be modified.							
    //							
    // If the context record is used as an IN OUT parameter to capture							
    // the context of a thread, then only those portions of the thread's							
    // context corresponding to set flags will be returned.							
    //							
    // The context record is never used as an OUT only parameter.							
    //							
							
    DWORD ContextFlags;							
							
    //							
    // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is							
    // set in ContextFlags.  Note that CONTEXT_DEBUG_REGISTERS is NOT							
    // included in CONTEXT_FULL.							
    //							
							
    DWORD   Dr0;							
    DWORD   Dr1;							
    DWORD   Dr2;							
    DWORD   Dr3;							
    DWORD   Dr6;							
    DWORD   Dr7;							
							
    //							
    // This section is specified/returned if the							
    // ContextFlags word contians the flag CONTEXT_FLOATING_POINT.							
    //							
							
    FLOATING_SAVE_AREA FloatSave;							
							
    //							
    // This section is specified/returned if the							
    // ContextFlags word contians the flag CONTEXT_SEGMENTS.							
    //							
							
    DWORD   SegGs;							
    DWORD   SegFs;							
    DWORD   SegEs;							
    DWORD   SegDs;							
							
    //							
    // This section is specified/returned if the							
    // ContextFlags word contians the flag CONTEXT_INTEGER.							
    //							
							
    DWORD   Edi;							
    DWORD   Esi;							
    DWORD   Ebx;							
    DWORD   Edx;							
    DWORD   Ecx;							
    DWORD   Eax;							
							
    //							
    // This section is specified/returned if the							
    // ContextFlags word contians the flag CONTEXT_CONTROL.							
    //							
							
    DWORD   Ebp;							
    DWORD   Eip;							
    DWORD   SegCs;              // MUST BE SANITIZED							
    DWORD   EFlags;             // MUST BE SANITIZED							
    DWORD   Esp;							
    DWORD   SegSs;							
							
    //							
    // This section is specified/returned if the ContextFlags word							
    // contains the flag CONTEXT_EXTENDED_REGISTERS.							
    // The format and contexts are processor specific							
    //							
							
    BYTE    ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];							
							
} CONTEXT;							

获取线程CONTEXT结构:

//挂起线程
SuspendThread(线程句柄);
CONTEXT context
//设置要获取的类型
context.ContextFlags = CONTEXT_CONTROL;
//获取
BOOL ok = ::GetThreadContext(hThread,&context);
//设置
context.Eip = 0x401000;
SetThreadContext(hThread,&context);

作业

问题:为什么下面代码会出现问题
两个线程各自加到100,结果是200,正常
两个线程各自加到1000,结果是1340,不是2000,有问题
image.png

HWND hEdit ;					
DWORD WINAPI ThreadProc1(LPVOID lpParameter)					
{					
	TCHAR szBuffer[10];				
	DWORD dwIndex = 0;				
	DWORD dwCount;				
					
	while(dwIndex<10)				
	{				
		GetWindowText(hEdit,szBuffer,10);			
		sscanf( szBuffer, "%d", &dwCount );			
		dwCount++;			
		memset(szBuffer,0,10);			
		sprintf(szBuffer,"%d",dwCount);			
		SetWindowText(hEdit,szBuffer);			
		dwIndex++;			
	}				
					
	return 0;				
}					
DWORD WINAPI ThreadProc2(LPVOID lpParameter)					
{					
	TCHAR szBuffer[10];				
	DWORD dwIndex = 0;				
	DWORD dwCount;				
					
					
	while(dwIndex<10)				
	{				
		GetWindowText(hEdit,szBuffer,10);			
		sscanf( szBuffer, "%d", &dwCount );			
		dwCount++;			
		memset(szBuffer,0,10);			
		sprintf(szBuffer,"%d",dwCount);			
		SetWindowText(hEdit,szBuffer);			
		dwIndex++;			
	}				
					
	return 0;				
}					
					
BOOL CALLBACK MainDlgProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)					
{					
	BOOL bRet = FALSE;				
					
	switch(uMsg)				
	{				
	case WM_CLOSE:				
		{			
			EndDialog(hDlg,0);		
			break;		
		}			
	case WM_INITDIALOG:				
		{			
			hEdit = GetDlgItem(hDlg,IDC_EDIT1);		
			SetWindowText(hEdit,"0");		
					
			break;		
		}			
	case WM_COMMAND:				
					
		switch (LOWORD (wParam))			
		{			
		case IDC_BUTTON_T1:			
			{		
				HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1, 	
					NULL, 0, NULL);
					
				::CloseHandle(hThread1);	
				return TRUE;	
			}		
		case IDC_BUTTON_T2:			
			{		
				HANDLE hThread2 = ::CreateThread(NULL, 0, ThreadProc2, 	
					NULL, 0, NULL);
					
				::CloseHandle(hThread2);	
				return TRUE;	
			}		
		}			
		break ;			
	}				
					
	return bRet;				
}					
					
int APIENTRY WinMain(HINSTANCE hInstance,					
                     HINSTANCE hPrevInstance,					
                     LPSTR     lpCmdLine,					
                     int       nCmdShow)					
{					
 	// TODO: Place code here.				
					
	DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG_MAIN),NULL,MainDlgProc);				
					
	return 0;				
}					

标签:return,CONTEXT,51,50,szBuffer,线程,context,DWORD
From: https://www.cnblogs.com/xiaoxin07/p/18077877

相关文章

  • 51单片机学习
    3.上面说了点亮一个Led灯,还有控制Led灯的闪烁,下面可以用一个比较笨的方法来做一个流水灯,思路就是先点亮第一个灯,然后点亮第二个,剩下的也是依次点亮,在每一个亮灭中间进行停顿,这样就能够形成所谓的流水灯效果了。代码如下#include<REGX52.H>#include<INTRINS.H>voidDelay500ms......
  • 第五十七天| 647. 回文子串、5.最长回文子串、516.最长回文子序列
    Leetcode 647.回文子串题目链接:647回文子串题干:给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。回文字符串 是正着读和倒过来读一样的字符串。子字符串 是字符串中的由连续字符组成的一个序列。具有不同开始位置或结束位置的子串,即使是由相同的......
  • 【转载】Redis -- IO多路复用及redis6的多线程
    都知道redis是通过单线程+io多路复用来避免并发问题的,然后在redis6的时候redis引入了多线程,这里就来详细说说IO多路复用模型以及redis的多线程。Redis的I/O多路复用模型有效的解决单线程的服务端,使用不阻塞方式处理多个client端请求问题。在看I/O多路复用知识之前,我们先来......
  • Living-Dream 系列笔记 第50期
    T1构思分讨。很自然地,我们令\(dp_{i,j}\)表示\([i,j]\)的初始字母方案数。但是这个状态信息过少,不足以解决此问题。于是我们增加状态维度,令\(dp_{i,j,0/1/2/3}\)表示\([i,j]\)是否能由W/I/N/D演变而来。答案即为\(dp_{1,n,0}\mid\middp_{1,n,1}\mid\middp_{......
  • 多线程
    1. 程序、进程与线程程序(program):        为完成特定任务,用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。进程(process):        程序的一次执行过程,或是正在内存中运行的应用程序。如:运行中的QQ,运行中的网易音乐播放器。每个进程都有......
  • rt-thread 线程管理
        一个实时操作系统,最重要的就是线程管理,rt-thread是一个硬实时系统,支持抢占式和时间片轮转的方式进行线程调度。    接下来简单描述下线程管理,并重点描述如何实现线程调度。    首先,一个程序是为了实现某种具体功能,而这个功能可能是由诸多小功能......
  • lc2250 统计包含每个点的矩形数目
    有n个矩形,第i个矩形左下角在(0,0)处,右上角在(l[i],h[i])。另给出m个点(x[i],y[i]),问有多少个矩形覆盖了这个点,点在边上也算是覆盖。1<=n,m<=5e4;1<=l[i],h[i]<=1e9;1<=h[i],y[i]<=100;所有矩形互不相同,所有查询点互不相同。二维偏序统计问题,可以离线处理,先对其中一维排序,将......
  • 51单片机学习
    单片机介绍是一种集成电路芯片,是采用超大规模集成电路技术把具有数据处理能力的中央处理器CPU、随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计数器等功能(可能还包括显示驱动电路、脉宽调制电路、模拟多路转换器、A/D转换器等电路)集成到一块硅片上构成的一个小而......
  • RX 6750 GRE 10GB和rtx 4080对比 评测
    RX6750GRE10GB基于RDNA2架构Navi22核心,晶体管数为172亿,拥有36组计算单元共计2304个流处理器,36个光追单元;加速频率2450MHz,TGP功耗158W,TBP功耗为170W;无限缓存80MB,采用GDDR6显存,显存位宽160-bit;主要用于1080p及1440p级别游戏市场。选RX6750GRE10GB还是rtx4080这些点很......
  • Ultra 9 185H和i7 12650h选哪个 酷睿Ultra9185H和i712650h对比
    i712650H采用Intel7工艺6大核4小核设计,拥有8核心12线程,三级缓存高达24MB选Ultra9185H还是i712650h这些点很重要看过你就懂了http://www.adiannao.cn/dyUltra9185H处理器是16核心,22线程,Ultra9185H处理器具有6个性能核心,8个效能核心,2个低功耗核心,超大24MB的三级......