zoukankan      html  css  js  c++  java
  • APC注入(Ring3)

      首先简单介绍一下APC队列和Alertable.

      看看MSDN上的一段介绍(https://msdn.microsoft.com/en-us/library/ms810047.aspx):

      The system delivers most user-mode APCs when a thread unwinds from kernel mode back to user mode after an alertable wait. User-mode APCs do not interrupt user-mode code. After an application queues a user-mode APC to a thread, the application can cause the system to deliver the APCs by calling a wait function with the Alertable parameter set to TRUE. (The user-mode terminate APC is an exception. This APC is queued to terminate a thread, and the system delivers it whenever the thread unwinds back to user mode from kernel mode, not only after an alertable wait.)

      上述写道,正常情况下,用户模式的APC是不会打断用户态程序的执行流的。除非,线程是Alertable——可唤醒的

      

       所以存在的一个麻烦就是,我想要注入的对方进程,哪个线程是Alertable的状态,所以采取了一种暴力的方式——向目标进程的所有线程的UserMode Apc队列(线程有两个Apc队列:Kernel和User)上插入Apc对象。

      代码流程:

      

      1.提权(EnableSeDebugPrivilege) 
      (1)获得令牌token,OpenThreadToken(),OpenProcessToken ()
      WINADVAPI
      BOOL
      WINAPI
      OpenThreadToken(
        _In_ HANDLE ThreadHandle,
        _In_ DWORD DesiredAccess,
        _In_ BOOL OpenAsSelf,
        _Outptr_ PHANDLE TokenHandle
        );

      OpenAsSelf参数
      [in]  true 指定应使用调用线程的进程安全上下文执行访问检查;
        false 指定应使用调用线程本身的安全上下文执行访问检查。 
      如果线程正在模拟客户端,则此安全上下文可以是一个客户端进程的安全上下文。
     

      WINADVAPI
      BOOL
      WINAPI
      OpenProcessToken(
        _In_ HANDLE ProcessHandle,
        _In_ DWORD DesiredAccess,
        _Outptr_ PHANDLE TokenHandle
      );

      (2)将提升的权限放到TokenPrivileges结构中 

      WINADVAPI
      BOOL
      WINAPI
      AdjustTokenPrivileges(
        _In_ HANDLE TokenHandle,
        _In_ BOOL DisableAllPrivileges, 标志这个函数是否禁用该令牌的所有特权.如果为TRUE,这个函数禁用所有特权,NewState参数无效.
        如果为假,以NewState参数指针的信息为基础来修改特权.
        _In_opt_ PTOKEN_PRIVILEGES NewState,
        _In_ DWORD BufferLength,
        _Out_writes_bytes_to_opt_(BufferLength, *ReturnLength) PTOKEN_PRIVILEGES PreviousState,
        _Out_opt_ PDWORD ReturnLength
        );

      (3)将获取的令牌TokenHandle与TokenPrivileges结构关联

    2.通过进程ImageName获取进程ID(GetProcessIDByProcessImageName) 
      (1)给系统的所有进程快照ProcessSnapshotHandle
      函数原型:
      HANDLE WINAPI CreateToolhelp32Snapshot(
        DWORD dwFlags, //用来指定“快照”中需要返回的对象,可以是TH32CS_SNAPPROCESS等
        DWORD th32ProcessID //一个进程ID号,用来指定要获取哪一个进程的快照,当获取系统进程列表或获取 当前进程快照时可以设为0
        );


    指定快照中包含的系统内容,dwFlags这个参数能够使用下列数值(常量)中的一个或多个。
    TH32CS_INHERIT - 声明快照句柄是可继承的。
    TH32CS_SNAPALL - 在快照中包含系统中所有的进程和线程。
    TH32CS_SNAPHEAPLIST - 在快照中包含在th32ProcessID中指定的进程的所有的堆。
    TH32CS_SNAPMODULE - 在快照中包含在th32ProcessID中指定的进程的所有的模块。
    TH32CS_SNAPPROCESS - 在快照中包含系统中所有的进程。
    TH32CS_SNAPTHREAD - 在快照中包含系统中所有的线程。
    #define TH32CS_SNAPHEAPLIST 0x00000001
    #define TH32CS_SNAPPROCESS 0x00000002
    #define TH32CS_SNAPTHREAD 0x00000004
    #define TH32CS_SNAPMODULE 0x00000008
    #define TH32CS_SNAPMODULE32 0x00000010
    #define TH32CS_SNAPALL (TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE)
    #define TH32CS_INHERIT 0x80000000
    (2)通过PROCESSENTRY32结构得到ProcessID
    PROCESSENTRY32,用来存放快照进程信息的一个结构体。(存放进程信息和调用成员输出进程信息)
    用来 Process32First指向第一个进程信息,并将进程信息抽取到PROCESSENTRY32中。
    用Process32Next指向下一条进程信息。
    typedef struct tagPROCESSENTRY32
    {
      DWORD dwSize;
      DWORD cntUsage;
      DWORD th32ProcessID; // this process
      ULONG_PTR th32DefaultHeapID;
      DWORD th32ModuleID; // associated exe
      DWORD cntThreads;
      DWORD th32ParentProcessID; // this process's parent process
      LONG pcPriClassBase; // Base priority of process's threads
      DWORD dwFlags;
      CHAR szExeFile[MAX_PATH]; // Path
    } PROCESSENTRY32;

    process32First,Process32Next是两个个进程获取函数,当我们利用函数CreateToolhelp32Snapshot()获得当前运行进程的快照后,
    我们可以利用process32First函数来获得第一个进程的句柄,利用Process32Next函数来获得下一个进程的句柄。
     
    3.通过进程ID获取线程ID

    4.注入
    (1)在对方进程空间申请内存

    LPVOID
    WINAPI
    VirtualAllocEx(
      _In_ HANDLE hProcess,
      _In_opt_ LPVOID lpAddress,
      _In_ SIZE_T dwSize,
      _In_ DWORD flAllocationType,
      _In_ DWORD flProtect
    );
    flAllocationType参数设置:MEM_COMMIT | MEM_RESERVE
    (如果物理内存申请失败,将保留进程的虚拟地址空间,到真正执行时,会触发异常,进行物理内存分配)
    MEM_COMMIT:为特定的页面区域分配内存中或磁盘的页面文件中的物理存储
    MEM_RESERVE:保留进程的虚拟地址空间,而不分配任何物理存储。保留页面可通过继续调用VirtualAlloc()而被占用

    (2)将DllFullPath写入内存当中
    WriteProcessMemory()函数能写入某一进程的内存区域。入口区必须可以访问,否则操作将失败。
    BOOL
    WINAPI
    WriteProcessMemory(
      _In_ HANDLE hProcess,
      _In_ LPVOID lpBaseAddress,
      _In_reads_bytes_(nSize) LPCVOID lpBuffer,
      _In_ SIZE_T nSize,
      _Out_opt_ SIZE_T * lpNumberOfBytesWritten
    );
    (3)加载dll文件
    LoadLibraryWAddress = (UINT_PTR)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");

    // QueryUserAPC.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "QueryUserAPC.h"
    
    int main()
    {
    	if (EnableSeDebugPrivilege(L"SeDebugPrivilege", TRUE) == FALSE)
    	{
    		return 0;
    	}
    	SeQueueUserAPC(L"explorer.exe", L"Dll.dll");
        return 0;
    }
    BOOL EnableSeDebugPrivilege(IN const WCHAR*  PriviledgeName, BOOL IsEnable)
    {
    	HANDLE  ProcessHandle = GetCurrentProcess();
    	HANDLE  TokenHandle = NULL;
    	TOKEN_PRIVILEGES TokenPrivileges = { 0 };
    	if (!OpenProcessToken(ProcessHandle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle))
    	{
    		return FALSE;
    	}
    	LUID			 v1;
    	if (!LookupPrivilegeValue(NULL, PriviledgeName, &v1))		
    	{
    		CloseHandle(TokenHandle);
    		TokenHandle = NULL;
    		return FALSE;
    	}
    
    	TokenPrivileges.PrivilegeCount = 1;	
    	TokenPrivileges.Privileges[0].Attributes = IsEnable == TRUE ? SE_PRIVILEGE_ENABLED : 0;  
    	TokenPrivileges.Privileges[0].Luid = v1;
    	if (!AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivileges,
    		sizeof(TOKEN_PRIVILEGES), NULL, NULL))
    	{
    		printf("%d
    ", GetLastError());
    		CloseHandle(TokenHandle);
    		TokenHandle = NULL;
    		return FALSE;
    	}
    	CloseHandle(TokenHandle);
    	TokenHandle = NULL;
    	return TRUE;
    }
    BOOL SeQueueUserAPC(WCHAR* ProcessImageName, WCHAR* DllNameData)
    {
    	TCHAR DllFullPathData[MAX_PATH];
    	GetFullPathName(DllNameData, MAX_PATH, DllFullPathData, NULL);
    
    	DWORD ProcessID{};
    	vector<DWORD> ThreadID{};
    
    	if (!SeEnumProcessThreadIDByToolhelp32(ProcessImageName, ProcessID, ThreadID))
    	{
    		return FALSE;
    	}
    
    	auto ProcessHandle = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, ProcessID);
    	if (!ProcessHandle)
    	{
    		printf("OpenProcess() Error
    ");
    		return FALSE;
    	}
    
    	auto RemoteBufferLength = 0x2000;
    	auto RemoteBufferData = VirtualAllocEx(ProcessHandle,
    		NULL,
    		RemoteBufferLength,
    		MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    	if (!WriteProcessMemory(ProcessHandle, RemoteBufferData, DllFullPathData, sizeof(DllFullPathData), NULL))
    	{
    
    		printf("WriteProcessMemory() Error
    ");
    		VirtualFreeEx(ProcessHandle, RemoteBufferData, RemoteBufferLength, MEM_RELEASE);
    		CloseHandle(ProcessHandle);
    		ProcessHandle = NULL;
    		return FALSE;
    	}
    	
    /*	for (const auto &v1 : ThreadID)    //Win7 崩溃
    	{
    		auto ThreadHandle = OpenThread(THREAD_SET_CONTEXT, FALSE, v1);
    		if (ThreadHandle)
    		{
    
    			//向目标进程中的各个线程的APC队列插入执行体
    			QueueUserAPC(
    				(PAPCFUNC)GetProcAddress(
    					GetModuleHandle(L"kernel32"),
    					"LoadLibraryW"),
    				ThreadHandle,
    				(ULONG_PTR)RemoteBufferData);
    
    			CloseHandle(ThreadHandle);
    		}
    	}*/
    
    	int i = 0;
    	for (int i=ThreadID.size()-1;i>=0;i--)   //Win10 Win7 通用
    	{
    		auto ThreadHandle = OpenThread(THREAD_SET_CONTEXT, FALSE, ThreadID[i]);
    		if (ThreadHandle)
    		{
    
    			//向目标进程中的各个线程的APC队列插入执行体
    			QueueUserAPC(
    				(PAPCFUNC)GetProcAddress(
    					GetModuleHandle(L"kernel32"),
    					"LoadLibraryW"),
    				ThreadHandle,
    				(ULONG_PTR)RemoteBufferData);
    
    			CloseHandle(ThreadHandle);
    		}
    	}
    	CloseHandle(ProcessHandle);
    	return TRUE;
    }
    BOOL SeEnumProcessThreadIDByToolhelp32(PCWSTR ProcessImageName, DWORD& ProcessID, vector<DWORD>& ThreadID)
    {
    	auto SnapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0);
    	if (SnapshotHandle == INVALID_HANDLE_VALUE)
    	{
    		return FALSE;
    	}
    	ProcessID = 0;
    
    	PROCESSENTRY32 ProcessEntry32 = { sizeof(ProcessEntry32) };
    	if (::Process32First(SnapshotHandle, &ProcessEntry32))
    	{
    		do {
    			if (_wcsicmp(ProcessEntry32.szExeFile, ProcessImageName) == 0) 
    			{
    				ProcessID = ProcessEntry32.th32ProcessID;
    				THREADENTRY32 ThreadEntry32 = { sizeof(ThreadEntry32) };
    				if (Thread32First(SnapshotHandle, &ThreadEntry32)) {
    					do {
    						if (ThreadEntry32.th32OwnerProcessID == ProcessID) {
    							ThreadID.push_back(ThreadEntry32.th32ThreadID);
    						}
    					} while (Thread32Next(SnapshotHandle, &ThreadEntry32));
    				}
    				break;
    			}
    		} while (Process32Next(SnapshotHandle, &ProcessEntry32));
    	}
    
    	CloseHandle(SnapshotHandle);
    	return ProcessID > 0 && !ThreadID.empty();
    }
    

      

  • 相关阅读:
    从零入门 Serverless | Serverless Kubernetes 应用部署及扩缩容
    从单体迈向 Serverless 的避坑指南
    从零入门 Serverless | 教你使用 IDE/Maven 快速部署 Serverless 应用
    开发函数计算的正确姿势——OCR 服务
    从零入门 Serverless | 函数计算的开发与配置
    全部满分!阿里云函数计算通过可信云21项测试
    登录接口+三方登录 微博
    注册接口文档
    异步发送短信验证与 注册接口完善
    图片验证码接口
  • 原文地址:https://www.cnblogs.com/lsh123/p/7399813.html
Copyright © 2011-2022 走看看