APC(Asynchronous Procedure Call,异步过程调用)是在一个特定线程环境下被异步执行的函数,分为用户模式APC和内核模式APC。每个线程都有一个APC队列。在用户模式下,当线程调用SleepEx、WaitForSingleObjectEx等进入"Alterable WaitStatus"状态(可警告的等待状态)的时候,系统会遍历该进程的APC队列,然后按照先进先出的顺序来执行这些APC。
在用户模式下,微软提供了QueueUerAPC这个API来向一个线程插入APC。
声明如下:
WINBASEAPI
DWORD
WINAPI
QueueUserAPC(
_In_ PAPCFUNC pfnAPC,
_In_ HANDLE hThread,
_In_ ULONG_PTR dwData
);
pfnAPC:指向一个APC函数
hThread:将要插入APC的线程句柄
dwData:APC函数的参数
先右键项目,点击属性->链接器->系统->子系统 选择窗口(/SUBSYSTEM:Windows)
然后将_tmain()函数改为WinMain()函数
// LoadExe.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <Windows.h>
#include <iostream>
using namespace std;
typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef struct _INJECT_STRUCT {
UINT_PTR LdrLoadDllAddress; //4
UNICODE_STRING DllFullPath; //4,4
HANDLE OutHandle;
} INJECT_STRUCT, *PINJECT_STRUCT;
int GrantDebugPrivileges();
BOOL InjectByAPC(int ProcessID,int ThreadID,const char *szDllFullPath);
UINT32 MakeShellCode(UINT8* ShellCodeData, PVOID Address);
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
int ProcessID = 0;
int ThreadID = 0;
if (__argc < 2)
{
return -1;
}
if (!strcmp(__argv[1], "Inject"))
{
GrantDebugPrivileges();
ProcessID = atoi(__argv[2]);
ThreadID = atoi(__argv[3]);
return InjectByAPC(ProcessID, ThreadID, __argv[4]);
}
return 0;
}
int GrantDebugPrivileges()
{
HANDLE TokenHandle = NULL;
TOKEN_PRIVILEGES PrivilegesToken;
LUID v1;
int iRet;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &TokenHandle))
{
return 0;
}
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &v1))
{
CloseHandle(TokenHandle);
return 0;
}
PrivilegesToken.PrivilegeCount = 1;
PrivilegesToken.Privileges[0].Luid = v1;
PrivilegesToken.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
iRet = AdjustTokenPrivileges(TokenHandle, FALSE, &PrivilegesToken, sizeof(PrivilegesToken), NULL, NULL);
CloseHandle(TokenHandle);
return iRet;
}
BOOL InjectByAPC(int ProcessID,int ThreadID,const char *szDllFullPath)
{
HANDLE ProcessHandle = NULL;
HANDLE ThreadHandle = NULL;
if (ProcessID <= 0 || ThreadID < 0)
{
return FALSE;
}
printf("Success\r\n");
ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessID); //打开系统所有进程
if (ProcessHandle == NULL)
{
return FALSE;
}
if (ThreadID > 0)
{
ThreadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadID);//打开所有线程
if (ThreadHandle == NULL)
{
CloseHandle(ProcessHandle);
return FALSE;
}
}
//注入都要在目标进程空间中申请内存 写入Dll的绝对路径
UINT32 DllPathLength = 0;
DllPathLength = (UINT32)strlen(szDllFullPath);
WCHAR* wzDllFullPath = (WCHAR*)calloc(1, (DllPathLength + 1) * sizeof(WCHAR)); //在LoadEx进程空间中
if (wzDllFullPath == NULL)
{
return FALSE;
}
for (int i=0;i<DllPathLength;i++)
{
wzDllFullPath[i] = (UINT16)szDllFullPath[i];
}
//单字转换双字
WCHAR* wzDllFullPathData = (WCHAR*)VirtualAllocEx(ProcessHandle, NULL, (wcslen(wzDllFullPath) + 1) * sizeof(WCHAR), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (wzDllFullPathData == NULL)
{
free(wzDllFullPath);
wzDllFullPath = NULL;
return FALSE;
}
SIZE_T ReturnSize = 0;
if (!WriteProcessMemory(ProcessHandle, wzDllFullPathData, wzDllFullPath, (wcslen(wzDllFullPath) + 1) * sizeof(WCHAR), &ReturnSize))
{
free(wzDllFullPath);
wzDllFullPath = NULL;
return FALSE;
}
LPVOID LdrLoadDll;
LdrLoadDll = GetProcAddress(GetModuleHandleA("ntdll.dll"), "LdrLoadDll"); //LadrloadDll导出地址
INJECT_STRUCT InjectStruct = {0};
InjectStruct.LdrLoadDllAddress = (UINT_PTR)LdrLoadDll;
InjectStruct.DllFullPath.Buffer = wzDllFullPathData;
InjectStruct.DllFullPath.Length = InjectStruct.DllFullPath.MaximumLength = (USHORT)(wcslen(wzDllFullPath) * sizeof(WCHAR));
PINJECT_STRUCT InjectStructData = NULL;
//注:VirtualAllocEx()函数是在别人的内存空间中申请内存
InjectStructData = (PINJECT_STRUCT)VirtualAllocEx(ProcessHandle, NULL, sizeof(INJECT_STRUCT), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (InjectStructData == NULL)
{
free(wzDllFullPath);
wzDllFullPath = NULL;
return FALSE;
}
if (!WriteProcessMemory(ProcessHandle, InjectStructData, &InjectStruct, sizeof(INJECT_STRUCT), &ReturnSize))
{
free(wzDllFullPath);
wzDllFullPath = NULL;
return FALSE;
}
char szShellCode[64] = {0};
UINT32 ShellCodeSize = MakeShellCode((UINT8*)szShellCode, InjectStructData);
CHAR* szShellCodeData = NULL;
szShellCodeData =(CHAR*)VirtualAllocEx(ProcessHandle, NULL, ShellCodeSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (szShellCodeData == NULL)
{
free(wzDllFullPath);
wzDllFullPath = NULL;
return FALSE;
}
if (!WriteProcessMemory(ProcessHandle, szShellCodeData, szShellCode, ShellCodeSize, &ReturnSize))
{
free(wzDllFullPath);
wzDllFullPath = NULL;
return FALSE;
}
if (ThreadHandle)
{
if(!QueueUserAPC((PAPCFUNC)szShellCodeData,ThreadHandle, (ULONG_PTR)InjectStructData))
{
free(wzDllFullPath);
wzDllFullPath = NULL;
return FALSE;
}
}
free(wzDllFullPath);
wzDllFullPath = NULL;
return TRUE;
}
UINT32 MakeShellCode(UINT8* ShellCodeData, PVOID Address)
{
#ifndef _WIN64
ShellCodeData[0] = 0xb8; // mov eax, InjectStructData
memcpy(&ShellCodeData[1], &Address, sizeof(Address));
ShellCodeData[5] = 0x53; // push ebx //保存ebx
ShellCodeData[6] = 0x8d; // lea ebx, InjectStructData+offsetof(INJECT_STRUCT.OutHandle)
ShellCodeData[7] = 0x58;
ShellCodeData[8] = (UINT8)offsetof(INJECT_STRUCT, OutHandle);
ShellCodeData[9] = 0x53; // push ebx ; ModuleHandle arg
ShellCodeData[10] = 0x8d; // lea ebx, InjectStructData+offsetof(INJECT_STRUCT.DllFullPath)
ShellCodeData[11] = 0x58;
ShellCodeData[12] = (UINT8)offsetof(INJECT_STRUCT, DllFullPath);
ShellCodeData[13] = 0x53; // push ebx ; ModuleFileName arg
ShellCodeData[14] = 0x6a; // push 0 (flags arg)
ShellCodeData[15] = 0x00;
ShellCodeData[16] = 0x6a; // push 0 (PathToFile arg)
ShellCodeData[17] = 0x00;
ShellCodeData[18] = 0x8b; // mov ebx, InjectStructData+offsetof(INJECT_STRUCT.LdrLoadDllAddress)
ShellCodeData[19] = 0x58;
ShellCodeData[20] = (UCHAR)offsetof(INJECT_STRUCT, LdrLoadDllAddress);
ShellCodeData[21] = 0xff; // call ebx
ShellCodeData[22] = 0xd3;
ShellCodeData[23] = 0x5b; // pop ebx
ShellCodeData[24] = 0xc2; // retn 0x4
ShellCodeData[25] = 0x04;
ShellCodeData[26] = 0x00;
return 27;
#else
ShellCodeData[0] = 0x53; // push rbx
ShellCodeData[1] = 0x48; // sub rsp, 0x20
ShellCodeData[2] = 0x83;
ShellCodeData[3] = 0xec;
ShellCodeData[4] = 0x20;
ShellCodeData[5] = 0x48; // mov rax, rcx (InjectStructData)
ShellCodeData[6] = 0x8b;
ShellCodeData[7] = 0xc1;
ShellCodeData[8] = 0x48; // lea rbx, Address+offsetof(INJECT_STRUCT.OutHandle)
ShellCodeData[9] = 0x8d;
ShellCodeData[10] = 0x58;
ShellCodeData[11] = (UINT8)offsetof(INJECT_STRUCT, OutHandle);
ShellCodeData[12] = 0x49; // mov r9, rbx ; ModuleHandle arg
ShellCodeData[13] = 0x89;
ShellCodeData[14] = 0xd9;
ShellCodeData[15] = 0x48; // lea rbx, injstructaddr+offsetof(INJECT_STRUCT.DllFullPath)
ShellCodeData[16] = 0x8d;
ShellCodeData[17] = 0x58;
ShellCodeData[18] = (UINT8)offsetof(INJECT_STRUCT, DllFullPath);
ShellCodeData[19] = 0x49; // mov r8, rbx ; ModuleFileName arg
ShellCodeData[20] = 0x89;
ShellCodeData[21] = 0xd8;
ShellCodeData[22] = 0x48; // xor rdx, rdx ; Flags arg
ShellCodeData[23] = 0x31;
ShellCodeData[24] = 0xd2;
ShellCodeData[25] = 0x48; // xor rcx, rcx ; PathToFile arg
ShellCodeData[26] = 0x31;
ShellCodeData[27] = 0xd1;
ShellCodeData[28] = 0x48; // mov rbx, Address+offsetof(INJECT_STRUCT.LdrLoadDllAddress)
ShellCodeData[29] = 0x8b;
ShellCodeData[30] = 0x58;
ShellCodeData[31] = (UINT8)offsetof(INJECT_STRUCT, LdrLoadDllAddress);
ShellCodeData[32] = 0xff; // call rbx
ShellCodeData[33] = 0xd3;
ShellCodeData[34] = 0x48; // add rsp, 0x20
ShellCodeData[35] = 0x83;
ShellCodeData[36] = 0xc4;
ShellCodeData[37] = 0x20;
ShellCodeData[38] = 0x5b; // pop rbx
ShellCodeData[39] = 0xc3; // ret
return 40;
#endif
}