代码改变世界

[Windows编程]释放DLL资源

2021-09-22 14:17  rnss  阅读(658)  评论(0编辑  收藏  举报

作用:将dll作为资源添加到程序中,程序运行时将dll释放到指定位置。

整体流程为:添加资源 -> 资源释放 -> 写入文件

添加资源

下面以Visual Studio 2015举例

 

 

 

 

选择要导入的dll文件

 

 

资源类型填写"DLL"

 

 

添加完成后会生成resource.h头文件和Project4.rc资源文件

 

 

打开resource.h头文件,可以看到定义了IDR_DLL1,这个常量后面会用到

 

 

资源释放

资源释放要用到4个Windows API

// 确定模块中指定类型和名称的资源所在位置。
HRSRC FindResourceW(
  HMODULE hModule,  // 处理包含资源的可执行文件模块,NULL表示系统从当前进程的模块中装载资源。
  LPCWSTR lpName,  // 指定资源名称,MAKEINTRESOURCE类型,这里是MAKEINTRESOURCE(IDR_DLL1)或MAKEINTRESOURCE(101),IDR_DLL1是resource.h中定义的常量。
  LPCWSTR lpType  // 指定资源类型,这里是"DLL"。
);
// 获取指定资源的字节数。
DWORD SizeofResource(
  HMODULE hModule,  // 包含资源的可执行文件模块的句柄。NULL表示系统从当前进程的模块中装载资源。
  HRSRC   hResInfo  // 资源句柄。此句柄必须由函数FindResource或FindResourceEx来创建。
);
// 装载指定资源到全局存储器。
HGLOBAL LoadResource(
  HMODULE hModule,  // 处理资源可执行文件的模块句柄。NULL表示系统从当前进程的模块中装载资源。
  HRSRC   hResInfo  // 资源句柄。此句柄必须由函数FindResource或FindResourceEx来创建。
);
// 锁定资源并得到资源在内存中第一个字节的指针。
LPVOID LockResource(
  HGLOBAL hResData  // 装载资源的句柄。函数LoadResource可以返回这个句柄。
);

 

写入文件

写入文件也要用到4个Windows API:

// 从堆中分配指定数量的字节。
DECLSPEC_ALLOCATOR HGLOBAL GlobalAlloc(
  UINT   uFlags,  // 内存分配属性。GPTR表示分配固定内存。返回值是一个指针,并将内存内容初始化为零。
  SIZE_T dwBytes  // 要分配的字节数。
);
// 将一块内存从一个位置复制到另一个位置。
void CopyMemory(
  _In_       PVOID  Destination,  // 指向复制块目标起始地址的指针。
  _In_ const VOID   *Source,  // 指向要复制的内存块起始地址的指针。
  _In_       SIZE_T Length  // 要复制的内存块的大小,以字节为单位。
);
// 此函数创建、打开或截断文件、COM 端口、设备、服务或控制台。它返回一个句柄来访问对象。
HANDLE CreateFile(
  LPCTSTR lpFileName,  // 要创建文件的路径。
  DWORD dwDesiredAccess,  // 访问对象的类型。GENERIC_WRITE表示对对象的写访问。数据可以写入文件,文件指针可以移动。
  DWORD dwShareMode,  // 对象的共享模式。如果此参数设置为零,则无法共享对象。对对象的后续打开操作将失败,直到句柄关闭。
  LPSECURITY_ATTRIBUTES lpSecurityAttributes,  // 未使用。
  DWORD dwCreationDisposition,  // 对存在的文件采取的行动,以及在文件不存在时采取的行动。CREATE_ALWAYS表示创建一个新文件。如果文件存在,该函数将覆盖该文件并清除现有属性。
  DWORD dwFlagsAndAttributes,  // 文件的文件属性和标志。
  HANDLE hTemplateFile  // 忽略
);
// 将数据写入指定的文件或输入/输出 (I/O) 设备。
BOOL WriteFile(
  HANDLE       hFile,  //文件的句柄,该文件必须已创建且有写权限。
  LPCVOID      lpBuffer,  // 指向包含要写入文件或设备的数据的缓冲区的指针。该缓冲区必须在写操作期间保持有效。在写操作完成之前,调用者不得使用此缓冲区。
  DWORD        nNumberOfBytesToWrite,  // 要写入文件或设备的字节数。
  LPDWORD      lpNumberOfBytesWritten,  // 一个指向接收使用同步hFile参数时写入的字节数的变量的指针。
  LPOVERLAPPED lpOverlapped  // 这里是NULL。
);

 

编程实现

可与远线程注入配合,实现只执行一个exe,不需要外部dll的dll注入。

#include <stdio.h>
#include <Windows.h>
#include "resource4.h"void ShowError(char *pszText)
{
    char szErr[MAX_PATH] = { 0 };
    ::wsprintf(szErr, "%s Error[%d]\n", pszText, ::GetLastError());
    ::MessageBox(NULL, szErr, "ERROR", MB_OK);
}
​
bool CreateMyFile(char* strFilePath, LPBYTE lpBuffer, DWORD dwSize)
{
    DWORD dwWritten;
​
    HANDLE hFile = CreateFile(strFilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
    if (hFile != NULL)
    {
        WriteFile(hFile, (LPCVOID)lpBuffer, dwSize, &dwWritten, NULL);
    }
    else
    {
        return false;
    }
    CloseHandle(hFile);
    return true;
}
​
bool CreateEXE(char* strFilePath, int nResourceID, char* strResourceName)
{
    HRSRC hResInfo;
    HGLOBAL hResData;
    DWORD dwSize;
    LPBYTE p;
    // 查找所需的资源
    hResInfo = FindResource(NULL, MAKEINTRESOURCE(nResourceID), strResourceName);
    if (hResInfo == NULL)
    {
        MessageBox(NULL, "查找资源失败!", "错误", MB_OK | MB_ICONINFORMATION);
        return false;
    }
    // 获得资源尺寸
    dwSize = SizeofResource(NULL, hResInfo);
    // 装载资源
    hResData = LoadResource(NULL, hResInfo);
    if (hResData == NULL)
    {
        MessageBox(NULL, "装载资源失败!", "错误", MB_OK | MB_ICONINFORMATION);
        return false;
    }
    // 为数据分配空间
    p = (LPBYTE)GlobalAlloc(GPTR, dwSize);
    if (p == NULL)
    {
        MessageBox(NULL, "分配内存失败!", "错误", MB_OK | MB_ICONINFORMATION);
        return false;
    }
    // 复制资源数据
    CopyMemory((LPVOID)p, (LPCVOID)LockResource(hResData), dwSize);
​
    bool bRet = CreateMyFile(strFilePath, p, dwSize);
    if (!bRet)
    {
        GlobalFree((HGLOBAL)p);
        return false;
    }
​
    GlobalFree((HGLOBAL)p);
​
    return true;
}
​
BOOL CreateRemoteThreadInjectDll(DWORD dwProcessId, char *pszDllFileName)
{
    HANDLE hProcess = NULL;
    SIZE_T dwSize = 0;
    LPVOID pDllAddr = NULL;
    FARPROC pFuncProcAddr = NULL;
​
    // 打开注入进程,获取进程句柄
    hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
    if (NULL == hProcess)
    {
        ShowError("OpenProcess");
        return FALSE;
    }
    // 在注入进程中申请内存
    dwSize = 1 + ::lstrlen(pszDllFileName);
    pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
    if (NULL == pDllAddr)
    {
        ShowError("VirtualAllocEx");
        return FALSE;
    }
    // 向申请的内存中写入数据
    if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL))
    {
        ShowError("WriteProcessMemory");
        return FALSE;
    }
    // 获取LoadLibraryA函数地址
    pFuncProcAddr = ::GetProcAddress(::GetModuleHandle("kernel32.dll"), "LoadLibraryA");
    if (NULL == pFuncProcAddr)
    {
        ShowError("GetProcAddress_LoadLibraryA");
        return FALSE;
    }
    // 使用 CreateRemoteThread 创建远线程, 实现 DLL 注入
    HANDLE hRemoteThread = ::CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, NULL);
    if (NULL == hRemoteThread)
    {
        ShowError("CreateRemoteThread");
return FALSE;
    }
// 关闭句柄
    ::CloseHandle(hProcess);
​
return TRUE;
}
​
int main()
{
CreateEXE("C:\\Windows\\System32\\0409\\a.dll", IDR_DLL1, "DLL");
​
// 远线程注入 DLL
BOOL bRet = CreateRemoteThreadInjectDll(9120, "C:\\Windows\\System32\\0409\\a.dll");
​
if (FALSE == bRet)
    {
printf("Inject Dll Error.\n");
    }
printf("Inject Dll OK.\n");
system("pause");
return 0;
}

 参考:《Windows黑客编程技术详解》

https://blog.csdn.net/sunjikui1255326447/article/details/90520970