[DLL 劫持] 修改 IAT ,让程序启动时加载自己的 DLL

请转到以下链接食用 🐷

原理参考:
🥙:https://www.cnblogs.com/LyShark/p/13697577.html

代码1(部分情况下有修复内存对齐的 bug):

#include <windows.h>
#include <iostream>
#include <exception>
#include <string>

using namespace std;

ULONG32 PEAlign(ULONG32 dwNumber, ULONG32 dwAlign)
{
    return(((dwNumber + dwAlign - 1) / dwAlign) * dwAlign);
}

BOOL AddNewSection(const string& strTargetFile, ULONG ulNewSectionSize)
{
    BOOL bOk = FALSE;
    HANDLE TargetFileHandle = nullptr;
    HANDLE MappingHandle = nullptr;
    PVOID FileData = nullptr;
    ULONG ulFileSize = 0;
    PIMAGE_NT_HEADERS pNtHeaders = NULL;
    PIMAGE_SECTION_HEADER pNewSectionHeader = NULL;
    PIMAGE_SECTION_HEADER pLastSectionHeader = NULL;
    DWORD FileSize = 0;
    DWORD FileOffset = 0;
    DWORD VirtualSize = 0;
    DWORD VirtualOffset = 0;
    PCHAR pNewSectionContent = NULL;
    DWORD dwWrittenLength = 0;

    // 打开文件
    TargetFileHandle = CreateFileA(strTargetFile.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (TargetFileHandle == INVALID_HANDLE_VALUE)
    {
        goto EXIT;
    }

    ulFileSize = GetFileSize(TargetFileHandle, NULL);
    if (INVALID_FILE_SIZE == ulFileSize)
    {
        goto EXIT;
    }

    // 映射文件
    MappingHandle = CreateFileMappingA(TargetFileHandle, NULL, PAGE_READWRITE, 0, ulFileSize, NULL);
    if (MappingHandle == NULL)
    {
        goto EXIT;
    }

    // 得到缓存头
    FileData = MapViewOfFile(MappingHandle, FILE_MAP_ALL_ACCESS, 0, 0, ulFileSize);
    if (FileData == NULL)
    {
        goto EXIT;
    }

    // 判断是否是PE文件
    if (((PIMAGE_DOS_HEADER)FileData)->e_magic != IMAGE_DOS_SIGNATURE)
    {
        goto EXIT;
    }

    pNtHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)FileData + ((PIMAGE_DOS_HEADER)FileData)->e_lfanew);
    if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE)
    {
        goto EXIT;
    }

    // 判断是否可以增加一个新节
    if ((pNtHeaders->FileHeader.NumberOfSections + 1) * sizeof(IMAGE_SECTION_HEADER) > pNtHeaders->OptionalHeader.SizeOfHeaders)
    {
        goto EXIT;
    }

    // 得到新节的起始地址, 最后的起始地址
    pNewSectionHeader = (PIMAGE_SECTION_HEADER)(pNtHeaders + 1) + pNtHeaders->FileHeader.NumberOfSections;
    pLastSectionHeader = pNewSectionHeader - 1;

    // 对齐RVA和偏移
    FileSize = PEAlign(ulNewSectionSize, pNtHeaders->OptionalHeader.FileAlignment);
    FileOffset = PEAlign(pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData, pNtHeaders->OptionalHeader.FileAlignment);
    VirtualSize = PEAlign(ulNewSectionSize, pNtHeaders->OptionalHeader.SectionAlignment);
    VirtualOffset = PEAlign(pLastSectionHeader->VirtualAddress + pLastSectionHeader->Misc.VirtualSize, pNtHeaders->OptionalHeader.SectionAlignment);

    // 填充新节表
    memcpy(pNewSectionHeader->Name, "Inject", strlen("Inject"));
    pNewSectionHeader->VirtualAddress = VirtualOffset;
    pNewSectionHeader->Misc.VirtualSize = VirtualSize;
    pNewSectionHeader->PointerToRawData = FileOffset;
    pNewSectionHeader->SizeOfRawData = FileSize;
    pNewSectionHeader->Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;

    // 修改IMAGE_NT_HEADERS
    pNtHeaders->FileHeader.NumberOfSections++;
    pNtHeaders->OptionalHeader.SizeOfImage += VirtualSize;
    pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;
    pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;

    // 添加新节到文件尾部
    SetFilePointer(TargetFileHandle, 0, 0, FILE_END);
    pNewSectionContent = new CHAR[FileSize];
    RtlZeroMemory(pNewSectionContent, FileSize);
    dwWrittenLength = 0;
    if (!WriteFile(TargetFileHandle, pNewSectionContent, FileSize, &dwWrittenLength, nullptr))
    {
        goto EXIT;
    }

    bOk = TRUE;
EXIT:
    if (TargetFileHandle != NULL)
    {
        CloseHandle(TargetFileHandle);
        TargetFileHandle = nullptr;
    }
    if (FileData != NULL)
    {
        UnmapViewOfFile(FileData);
        FileData = nullptr;
    }
    if (MappingHandle != NULL)
    {
        CloseHandle(MappingHandle);
        MappingHandle = nullptr;
    }
    return bOk;
}

PIMAGE_SECTION_HEADER GetOwnerSection(PIMAGE_NT_HEADERS pNTHeaders, DWORD dwRVA)
{
    int i;
    PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)(pNTHeaders + 1);
    for (i = 0; i < pNTHeaders->FileHeader.NumberOfSections; i++)
    {
        if ((dwRVA >= (pSectionHeader + i)->VirtualAddress) && (dwRVA <= ((pSectionHeader + i)->VirtualAddress + (pSectionHeader + i)->SizeOfRawData)))
        {
            return ((PIMAGE_SECTION_HEADER)(pSectionHeader + i));
        }
    }
    return PIMAGE_SECTION_HEADER(NULL);
}

DWORD RVAToFOA(PIMAGE_NT_HEADERS pNTHeaders, DWORD dwRVA)
{
    DWORD _offset;
    PIMAGE_SECTION_HEADER section;

    // 找到偏移所在节
    section = GetOwnerSection(pNTHeaders, dwRVA);
    if (section == NULL)
    {
        return(0);
    }

    // 修正偏移
    _offset = dwRVA + section->PointerToRawData - section->VirtualAddress;

    return(_offset);
}

BOOL AddNewImportDescriptor(const string& strTargetFile, const string& strInjectDllName, const string& strFunctionName)
{
    BOOL bOk = FALSE;
    ULONG ulFileSize = 0;
    HANDLE TargetFileHandle = nullptr;
    HANDLE MappingHandle = nullptr;
    PVOID FileData = nullptr;
    PIMAGE_IMPORT_DESCRIPTOR pImportTable = nullptr;
    PIMAGE_NT_HEADERS pNtHeaders = NULL;
    BOOL  bBoundImport = FALSE;
    PIMAGE_SECTION_HEADER pNewSectionHeader = NULL;
    PBYTE pNewSectionData = NULL;
    PBYTE pNewImportDescriptor = NULL;
    INT i = 0;
    DWORD dwDelt = 0;
    PIMAGE_THUNK_DATA pNewThunkData = NULL;
    PBYTE pszDllName = NULL;
    PIMAGE_IMPORT_BY_NAME pImportByName = NULL;

    // 打开文件
    TargetFileHandle = CreateFileA(strTargetFile.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (TargetFileHandle == INVALID_HANDLE_VALUE)
    {
        goto EXIT;
    }

    ulFileSize = GetFileSize(TargetFileHandle, NULL);
    if (INVALID_FILE_SIZE == ulFileSize)
    {
        goto EXIT;
    }

    // 映射文件
    MappingHandle = CreateFileMappingA(TargetFileHandle, NULL, PAGE_READWRITE, 0, ulFileSize, NULL);
    if (MappingHandle == NULL)
    {
        goto EXIT;
    }

    // 得到缓存头
    FileData = MapViewOfFile(MappingHandle, FILE_MAP_ALL_ACCESS, 0, 0, ulFileSize);
    if (FileData == NULL)
    {
        goto EXIT;
    }

    // 判断是否是PE文件
    if (((PIMAGE_DOS_HEADER)FileData)->e_magic != IMAGE_DOS_SIGNATURE)
    {
        goto EXIT;
    }

    pNtHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)FileData + ((PIMAGE_DOS_HEADER)FileData)->e_lfanew);
    if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE)
    {
        goto EXIT;
    }

    // 得到原导入表
    pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((ULONG_PTR)FileData + RVAToFOA(pNtHeaders, pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress));

    // 判断是否使用了绑定导入表
    if (pImportTable->Characteristics == 0 && pImportTable->FirstThunk != 0)
    {
        bBoundImport = TRUE;
        pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;
        pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;
    }

    // 找到自己添加的新节
    pNewSectionHeader = (PIMAGE_SECTION_HEADER)(pNtHeaders + 1) + pNtHeaders->FileHeader.NumberOfSections - 1;
    pNewSectionData = pNewSectionHeader->PointerToRawData + (PBYTE)FileData;
    pNewImportDescriptor = pNewSectionData;

    // 往新节中拷贝原导入表内容
    i = 0;
    while (pImportTable->FirstThunk != 0 || pImportTable->Characteristics != 0)
    {
        memcpy(pNewSectionData + i * sizeof(IMAGE_IMPORT_DESCRIPTOR), pImportTable, sizeof(IMAGE_IMPORT_DESCRIPTOR));
        pImportTable++;
        pNewImportDescriptor += sizeof(IMAGE_IMPORT_DESCRIPTOR);
        i++;
    }

    // 复制最后一个描述符
    memcpy(pNewImportDescriptor, pNewImportDescriptor - sizeof(IMAGE_IMPORT_DESCRIPTOR), sizeof(IMAGE_IMPORT_DESCRIPTOR));

    // 计算修正值
    dwDelt = pNewSectionHeader->VirtualAddress - pNewSectionHeader->PointerToRawData;

    // pNewImportDescriptor 当前指向要构造的新描述符 再空出一个空描述符作为导入表的结束符 所以是 2 *
    pNewThunkData = PIMAGE_THUNK_DATA(pNewImportDescriptor + 2 * sizeof(IMAGE_IMPORT_DESCRIPTOR));
    pszDllName = (PBYTE)(pNewThunkData + 2);
    memcpy(pszDllName, strInjectDllName.c_str(), strInjectDllName.length());

    // 确定 DllName 的位置
    pszDllName[strInjectDllName.length() + 1] = 0;

    // 确定 IMAGE_IMPORT_BY_NAM 的位置
    pImportByName = (PIMAGE_IMPORT_BY_NAME)(pszDllName + strInjectDllName.length() + 1);

    // 初始化 IMAGE_THUNK_DATA
    pNewThunkData->u1.Ordinal = (DWORD_PTR)pImportByName - (DWORD_PTR)FileData + /*加上修正值 - 这里应该填充在内存中的地址*/dwDelt;

    // 初始化 IMAGE_IMPORT_BY_NAME
    pImportByName->Hint = 1;
    memcpy(pImportByName->Name, strFunctionName.c_str(), strFunctionName.length());
    pImportByName->Name[strFunctionName.length() + 1] = 0;

    // 初始化 PIMAGE_IMPORT_DESCRIPTOR
    if (bBoundImport)
    {
        ((PIMAGE_IMPORT_DESCRIPTOR)pNewImportDescriptor)->OriginalFirstThunk = 0;
    }
    else
    {
        ((PIMAGE_IMPORT_DESCRIPTOR)pNewImportDescriptor)->OriginalFirstThunk = dwDelt + (DWORD_PTR)pNewThunkData - (DWORD_PTR)FileData;
    }
    ((PIMAGE_IMPORT_DESCRIPTOR)pNewImportDescriptor)->FirstThunk = dwDelt + (DWORD_PTR)pNewThunkData - (DWORD_PTR)FileData;
    ((PIMAGE_IMPORT_DESCRIPTOR)pNewImportDescriptor)->Name = dwDelt + (DWORD_PTR)pszDllName - (DWORD_PTR)FileData;

    // 修改导入表入口
    pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = pNewSectionHeader->VirtualAddress;
    pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = (i + 1) * sizeof(IMAGE_IMPORT_DESCRIPTOR);

    bOk = TRUE;
EXIT:
    if (TargetFileHandle != NULL)
    {
        CloseHandle(TargetFileHandle);
        TargetFileHandle = nullptr;
    }

    if (FileData != NULL)
    {
        UnmapViewOfFile(FileData);
        FileData = nullptr;
    }

    if (MappingHandle != NULL)
    {
        CloseHandle(MappingHandle);
        MappingHandle = nullptr;
    }
    return bOk;
}

BOOL AddImportTable(const string& strTargetFile, const string& strInjectDllName, const string& strFunctionName)
{
    BOOL bOk = FALSE;

    if (!AddNewSection(strTargetFile, 256))
    {
        goto end;
    }

    if (!AddNewImportDescriptor(strTargetFile, strInjectDllName, strFunctionName))
    {
        goto end;
    }

    bOk = TRUE;
end:
    return bOk;
}


int main()
{
    getchar();
    AddImportTable("被注入进程.exe", "要注入的模块.dll", "模块导出函数名");

    system("pause");
    return true;
}

代码2(测试正常,推荐):

// 这段代码要放在文件头部才能生效 (。・∀・)ノ
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif

BOOL iatInsertDll(CONST TCHAR dllName[MAX_PATH],CONST TCHAR funcName[MAX_PATH],CONST TCHAR sourceFile[MAX_PATH],CONST TCHAR targetFile[MAX_PATH])
{
	BOOL ret = FALSE;
	PTCHAR pExe = nullptr;									// 原文件二进制
	UINT fileSize = 0;												// 文件大小
	FILE* pfin = nullptr;											// 输入文件
	FILE* pfout = nullptr;										// 输出文件
	TCHAR injectStr[64] = { 0 };								// 导入函数字符信息
	UINT injectStrLen = 0;										// 字符信息长度
	UINT injectStrThunk = 0;									// 以 '\0' 结尾的 dll 名字符串
	IMAGE_DOS_HEADER dos_header;											// dos 头
	IMAGE_NT_HEADERS* nt_headers_ptr;									// nt 头
	IMAGE_SECTION_HEADER* section_headers;						// 可选头
	IMAGE_IMPORT_DESCRIPTOR* import_descriptor;				// 导入表
	IMAGE_IMPORT_DESCRIPTOR newImportStruct;					// 新导入项(非导入表)
	LONG IatOffset = 0;											// 导入表所在区段 rof 、rva 差值
	LONG finalSectionOffset = 0;							// 最后一个区段 rof 、 rva 的差值
	LONG finalSectionEnd = 0;								// 最后一个区段末尾rof
	UINT finalSectionIndex = 0;							// 最后一个区段的下标
	INT importCount = -1;										// 导入函数个数
	ULONG injectAddr = 0;									// 要插入的导入函数地址
	PTCHAR szEndPad = { 0 };																			// 末尾填充字符串
	UINT padLen = 0;																			// 填充长度

	// 只读方式打开二进制文件
	pfin = fopen(sourceFile, "rb");
	if (!pfin)
	{
		printf("fopen failed. \n");
		goto end;
	}

	// 将文件二进制读入内存
	fseek(pfin, 0, SEEK_END);
	fileSize = ftell(pfin);
	fseek(pfin, 0, SEEK_SET);
	pExe = (char*)malloc(fileSize);
	fread(pExe, fileSize, 1, pfin);
	fclose(pfin);

	// 拼接插入dll 和导入函数的字符信息
	sprintf(injectStr, "%s%c%c%c%s%c%c%c%c%c", dllName, 0, 0, 0, funcName, 0, 0, 0, 0, 0);
	injectStrLen = strlen(dllName) + 3 + strlen(funcName) + 5;
	injectStrThunk = strlen(dllName) + 1;

	// 判断是否为有效的 pe 文件
	dos_header = *(IMAGE_DOS_HEADER*)(pExe);
	nt_headers_ptr = (IMAGE_NT_HEADERS*)(pExe + dos_header.e_lfanew);
	if (dos_header.e_magic != 0x5A4D || (*nt_headers_ptr).Signature != 0x4550)
	{
		printf("Invalid PE file. \n");
		goto end;
	}

	// 计算可选头
	section_headers =
		(IMAGE_SECTION_HEADER*)
		(pExe
			+ dos_header.e_lfanew
			+ sizeof((*nt_headers_ptr).Signature)
			+ sizeof((*nt_headers_ptr).FileHeader)
			+ (*nt_headers_ptr).FileHeader.SizeOfOptionalHeader);

	// 计算最后一个区段的下标
	finalSectionIndex = (*nt_headers_ptr).FileHeader.NumberOfSections - 1;

	// 计算最后一个 区段的 rof 和 rva 的差值
	finalSectionOffset = section_headers[finalSectionIndex].PointerToRawData - section_headers[finalSectionIndex].VirtualAddress;

	// 计算最后一个区段的末尾 rof
	finalSectionEnd = section_headers[finalSectionIndex].PointerToRawData + section_headers[finalSectionIndex].SizeOfRawData;

	// 找到导入表所在区段 rof、rva 的差值
	for (int i = finalSectionIndex; i > -1; i--)
		// 因为是 i-- ,所以区段的 VirtualAddress 是越来越小的,当 VirtualAddress 首次小于导入表的 VirtualAddress 时,说明导入表在这个区段中
		if (section_headers[i].VirtualAddress <= (*nt_headers_ptr).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)
		{
			IatOffset = section_headers[i].PointerToRawData - section_headers[i].VirtualAddress;
			break;
		}

	// 计算导入表 rof(=区段rof+导入表所在区段的段内偏移)
	import_descriptor =
		(IMAGE_IMPORT_DESCRIPTOR*)
		(pExe
			+ (*nt_headers_ptr).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
			+ IatOffset);

	// 计算导入函数个数
	while (import_descriptor[++importCount].OriginalFirstThunk);

	// 重定向导入表到最后一个区段末尾
	(*nt_headers_ptr).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = finalSectionEnd - finalSectionOffset;

	// 调整 pe 以适应新的导入表(扩充大小、内存对齐、可写)
	(*nt_headers_ptr).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size += sizeof(IMAGE_IMPORT_DESCRIPTOR);
	(*nt_headers_ptr).OptionalHeader.SizeOfImage += (*nt_headers_ptr).OptionalHeader.SectionAlignment;
	section_headers[finalSectionIndex].SizeOfRawData += (*nt_headers_ptr).OptionalHeader.SectionAlignment;
	section_headers[finalSectionIndex].Misc.VirtualSize += (*nt_headers_ptr).OptionalHeader.SectionAlignment;
	section_headers[finalSectionIndex].Characteristics |= IMAGE_SCN_MEM_WRITE;

// 计算扩充后的导入函数地址
	injectAddr = (importCount + 2) * sizeof(IMAGE_IMPORT_DESCRIPTOR) + finalSectionEnd - finalSectionOffset;

	// 在导入字符信息后追加导入函数地址信息(让新副本找到导入函数)
	*(DWORD*)(injectStr + injectStrLen - 4) = injectAddr + injectStrThunk;

	// 初始化导入项信息
	newImportStruct.OriginalFirstThunk = injectAddr + injectStrLen - 4;			// 给 Name 留(inject_strlen - 4)长度的空位
	newImportStruct.TimeDateStamp = 0;
	newImportStruct.ForwarderChain = 0;
	newImportStruct.Name = injectAddr;																	// Name 是扩充后的导入函数头部地址
	newImportStruct.FirstThunk = injectAddr + injectStrLen - 4;						// 新导入函数 rva (紧随头部地址之后) 

	// 写方式打开二进制文件,拷贝一份副本
	pfout = fopen(targetFile, "wb");
	if (!pfout)
	{
		printf("fopen failed. \n");
		goto end;
	}
	fwrite(pExe, finalSectionEnd, 1, pfout);

	// 修改新副本导入表地址
	fwrite(import_descriptor, importCount * sizeof(IMAGE_IMPORT_DESCRIPTOR), 1, pfout);

	// 追加新导入项
	fwrite(&newImportStruct, sizeof(IMAGE_IMPORT_DESCRIPTOR), 1, pfout);

	// 填充 sizeof(IMAGE_IMPORT_DESCRIPTOR) 个 '\0' ,表示导入表结构到此结束
	fwrite("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", sizeof(IMAGE_IMPORT_DESCRIPTOR), 1, pfout);

	// 设置导入函数字符串信息
	fwrite(injectStr, injectStrLen, 1, pfout);

	// 将文件填充到正确的对齐位置
	padLen = (*nt_headers_ptr).OptionalHeader.SectionAlignment - (importCount + 2) * sizeof(IMAGE_IMPORT_DESCRIPTOR) - injectStrLen;
	szEndPad = (PTCHAR)calloc(1, padLen);
	fwrite(szEndPad, padLen, 1, pfout);
	

	ret = TRUE;
end:
	if (pfin)
	{
		fclose(pfin);
	}
	if (pfout)
	{
		fclose(pfout);
	}
	if (pExe)
	{
		free(pExe);
	}
	if (szEndPad)
	{
		free(szEndPad);
	}
	return ret;
}

int main()
{
	iatInsertDll("Insert.dll", "justExport", "before.exe", "after.exe");

	return 0;
}

在这里插入图片描述

注意,如果要注入 x64 进程,记得改下 PE 的结构体类型(64). 🙃

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值