刘收获

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

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");

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// 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\r\n", 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\r\n");
        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\r\n");
        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();
}

  

posted on   沉疴  阅读(375)  评论(0编辑  收藏  举报

编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗
点击右上角即可分享
微信分享提示