滴水逆向-重定位表

#include "stdafx.h"
#include <windows.h>
#include "stdlib.h"
#include "stdio.h"


#define FilePath_In "C:\\cntflx\\dtljkcntf.dll"
#define debug 0

//RVA格式转换FOA  --- RvaToFileOffset
DWORD RvaToFileOffset(IN LPVOID pFileBuffer,IN DWORD dwRva)
{
    PIMAGE_DOS_HEADER pDosHeader = NULL;
    PIMAGE_NT_HEADERS pNTHeader = NULL;
    PIMAGE_FILE_HEADER pPEHeader = NULL;
    PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
    PIMAGE_SECTION_HEADER pSectionHeader = NULL;
    DWORD numberOfSection = 0;
    DWORD dwFOAValue = 0;

    //判断指针是否有效
    if (!pFileBuffer)
    {
        printf("pFileBuffer 指针无效\r\n");
        return 0;
    }
    //判断是否是有效的MZ标志
    if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)
    {
        printf("pFileBuffer不是有效的MZ标志\r\n");
        return 0;
    }
    //判断是否是一个有效的PE标志
    pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
    if (*((PWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
    {
        printf("pFileBuffer不是一个有效的PE标志\r\n");
        return 0;
    }

    //printf("当前的Rva地址: %#X \r\n",dwRva);
    pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
    pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+0x04);
    pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
    pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);

    //定义个临时节表指针进行下面的计算操作
    numberOfSection = pPEHeader->NumberOfSections;
    PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;


    //判断dwRva所处的节
    if (dwRva <= pOptionHeader->SizeOfHeaders)
    {
        return (DWORD)dwRva;
    }
    //上面是判断如果rva地址所处的节在第一个节之前那么直接返回rva的地址;
    //否则下面就是开始遍历查找节;
    else
    {
        for (DWORD n = 0; n < numberOfSection; n++)
        {//下面是判断在哪个节的范围,然后根据rva所在的地址减去所在节的VirtualAddress得到的偏移值加上文件中对应节的偏移值PointerToRawData
            if ((dwRva >= pTempSectionHeader[n].VirtualAddress) && (dwRva < pTempSectionHeader[n].VirtualAddress + pTempSectionHeader[n].Misc.VirtualSize))
            {
                dwFOAValue = dwRva - pTempSectionHeader[n].VirtualAddress + pTempSectionHeader[n].PointerToRawData;
            }
        }
    }
    return dwFOAValue;
}

DWORD ReadPEFile(IN LPSTR lpszFile,OUT LPVOID* pFileBuffer )
{
	FILE	*pFile		=	NULL;
	DWORD	fileSize	=	0;			//文件大小
	LPVOID	pTempFileBuffer	=	NULL;	//缓冲区首地址


	pFile = fopen(lpszFile,"rb");	//打开文件
	if(!pFile)
	{
		printf("打开文件失败");
		return NULL;
	}

	//读取文件大小
	fseek(pFile,0,SEEK_END);		//将指针从开始的位置移动到末尾
	fileSize = ftell(pFile);		//获取数据大小

	//分配缓冲区(申请内存)
	pTempFileBuffer = malloc(fileSize);
	if(!pTempFileBuffer)
	{
		printf("分配空间失败");
		fclose(pFile);
		return NULL;
	}

	//将文件数据读取到缓冲区
	fseek(pFile,0,SEEK_SET);	//将指针指向开始
	size_t n = fread(pTempFileBuffer,fileSize,1,pFile);	//将数据读取到缓冲区中
	if(!n)
	{
		printf("读取数据失败");
		free(pTempFileBuffer);		//释放内存
		fclose(pFile);			//关闭文件
		return NULL;
	}

	//关闭文件
	*pFileBuffer = pTempFileBuffer;
	pTempFileBuffer = NULL;
	fclose(pFile);				//关闭文件
	return fileSize;
}


DWORD GetDataDirectoyOfBaseRelocation(IN PVOID pFileBuffer)
{
    // 初始化PE头部结构体
    PIMAGE_DOS_HEADER pDosHeader = NULL;
    PIMAGE_NT_HEADERS pNTHeader = NULL;
    PIMAGE_FILE_HEADER pPEHeader = NULL;
    PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
    PIMAGE_SECTION_HEADER pSectionHeader = NULL;
    PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
    PIMAGE_BASE_RELOCATION pBaseRelocation = NULL;
    DWORD RVA_BaseRelocationTable = 0;
    DWORD SizeOfBlock_BaseRelocationTable = 0;
    
    // 判断指针是否有效
    if (!pFileBuffer)
    {
        printf("pFileBuffer不是有效的指针\r\n");
        return 0;
    }
    //判断是否是有效的MZ标志
    if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)
    {
        printf("pFileBuffer不是有效的MZ文件\r\n");
        return 0;
    }
    //判断是否是一个有效的PE标志
    pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
    if (*((PWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
    {
        printf("pFileBuffer不是一个有效的PE标志\r\n");
        return 0;
    }
    
    // 强制结构体类型转换
    pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
    pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 0x04); // 这里必须强制类型转换
    pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
    pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
    //pSectionHeader = (PIMAGE_SECTION_HEADER)(pNTHeader+0x01);
    //上述表示节表指针pSectionHeader的另一种写法,就是通过NT头的这个一个整体的结构体宽度进行移动;
    //因为NT头整体结构体宽度是4+20+224=248 --> 16进制F8,通过加0x01就直接移动F8的字节,刚好落在节表位置;
    pDataDirectory = (PIMAGE_DATA_DIRECTORY)pOptionHeader->DataDirectory;

    //定位重定位表VirtualAddress,即RVA地址
    RVA_BaseRelocationTable = pDataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
    //定位重定位表SizeOfBlock, 即块的大小
    SizeOfBlock_BaseRelocationTable = pDataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;

    if (!debug)
    {
        printf("重定位表VirtualAddress地址: %#010X\r\n", RVA_BaseRelocationTable);
        printf("重定位表SizeOfBlock大小: %#010X\r\n", SizeOfBlock_BaseRelocationTable);
    }
    
    if (!RVA_BaseRelocationTable)
    {
        printf("这个程序没有导出表.\r\n");
        return 0;
    }

    //重定位表FOA地址,这里的FOA地址是为从数据目录数组[5]获取的RVA并转换为FOA,目的是为了下面
    //偏移到FileBuffer中准确的BaseRelocation重定位表起始位置
    DWORD FOA_BaseRelocationTable = RvaToFileOffset(pFileBuffer,RVA_BaseRelocationTable);

    //定位重定位表文件偏移的位置,即:FIleBuffer的文件偏移
    pBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pFileBuffer + FOA_BaseRelocationTable);

    if (!debug)
    {
        printf("重定位表FOA地址: %#010X\r\n", FOA_BaseRelocationTable);
        printf("重定位表文件偏移地址: %#010X\r\n", pBaseRelocation);
    }

    //定义节里面名称的数组变量,并置为0
    BYTE secName[9] = {0};//这里是定义9个宽度的字节数组并置为0,为后面的节操作做准备

    //
    for (DWORD i = 0;pBaseRelocation->SizeOfBlock && pBaseRelocation->VirtualAddress; i++)
    {
        //参数pVirtualOfFOA_BaseReloc的FOA地址是从重定位表处获取的VirtualAddress将其转换为FOA地址;
        DWORD pVirtualOfFOA_BaseReloc = RvaToFileOffset(pFileBuffer,pBaseRelocation->VirtualAddress);
        DWORD pSizeOfBlock_BaseReloc = (pBaseRelocation->SizeOfBlock - 8)/2;
        /*上面是根据VirtualAddress,SizeOfBlock总共占8个字节,SizeOfBlock是当前块的中大小
        进行计算,为了算出真正具体项的数量,然后判断哪些是需要修改的项;
        
        具体项宽度:2字节
        也就是这个数据,内存中的页大小是1000H 也就是说2的12次方 就可以表示,一个页内所有的偏移地址
        具体项的宽度是16字节高四位,代表类型:值为3代表的是需要修改的数据值为0代表的是,用于数据对齐的数据
        可以不用修改.也就是说 我们只关注高4位的值为3的就可以了.
        1000H --> 4096D --> 2的12次方
        12 + 4 = 16 bit
        */

        //开始通过计算确定该结构所属哪个节里面
        for (DWORD j = 0; j < pPEHeader->NumberOfSections; j++)
        {
            DWORD pLowAddressOfFoa = RvaToFileOffset(pFileBuffer,pSectionHeader[j].VirtualAddress);
            DWORD pHighAddressOfFoa = RvaToFileOffset(pFileBuffer,pSectionHeader[j].Misc.VirtualSize);

            if (pVirtualOfFOA_BaseReloc >= pLowAddressOfFoa && pVirtualOfFOA_BaseReloc <= pHighAddressOfFoa)
            {
                memcpy(secName,pSectionHeader[j].Name,8);
                break;
            }
        }
        //下面是打印本页的主要信息
        printf("节:%X -> 重定位表VA:%#010X -> 节的名称:%s -> 具体项大小:%#010X\r\n",\
            i,pBaseRelocation->VirtualAddress,secName,pSizeOfBlock_BaseReloc);
        
        //打印一个页中所有重定位信息和地址

        //下面是通过偏移8个字节指向块中第一个具体项,因为宽度是2个字节,所以使用WORD并带指针类型
        WORD* recAddr = (WORD*)((BYTE*)pBaseRelocation+0x08);
        
        for(j=0; j<pSizeOfBlock_BaseReloc; j++)//每个结构的内容进行遍历
        {
            /*下面算法解释:
            第一行代码:通过获得的第一个具体项2个字节的地址与0x0FFF进行与操作,结果就去除了高4位;
            然后就得到了准确的后面12位的偏移,刚好是咱想要的,直接按照算法来操作,使用该节所属的
            VirtualAddress进行相加偏移,当然这里的VirtualAddress是需要转换为FOA的地址之后进行计算
            此时便得到了准确需要偏移的值offset参数,通过这个参数进行偏移可得到准确的需要修改的偏移
            地址,而参数type,就是我们所说的高4位代表一个类型,我们需要使用右移的方式将其置为0x0011
            也就是3,所以代码中操作对其进行右移12位即可得到0x0011-->3;
            这里回顾下右移:因为int如果是有符号的整形数,最左端的1位是符号位,0正1负,而现在我们的最
            左端是0011,符号位是0表示正,那么右移的时候前面补0即可,所以移动12位置之后type参数的结果
            就是0x0011->3

            下面是计算其中一个节的具体数据例子:
            节:0 -> 重定位表VA:0X00001000 -> 节的名称:.text -> 具体项大小:0X00000088
            recAddr[j]:0X0000315F ---> 0x315F ---> 0011 0001 0101 1111
            offset:0X0000115F     ---> 0011 0001 0101 1111 & 0000 1111 1111 1111 --->0x015F
            0x015F + 0x1000 = 0x115F ---> offset:0X0000115F
            type:0X00000003  ---> 0011 0001 0101 1111 >> 12 ---> 0000 0000 0000 0011 --> 0x0011 --> 3
            0X0000215F,0X00000003
            */
            DWORD pRepair_RvaOffset = (recAddr[j] & 0x0FFF) + pVirtualOfFOA_BaseReloc;
            //printf("recAddr[j]:%#010X \r\n",recAddr[j]);
            //printf("offset:%#010X \r\n",offset);
            WORD type = recAddr[j] >> 12;//三位
            //printf("type:%#010X \r\n",type);
            
            if(type!=0)
            {
                printf("%#010X,%#010X\r\n",pRepair_RvaOffset,type);
                //	system("pause");
            }
        }
        memset(secName, 0, 9);
        pBaseRelocation = (PIMAGE_BASE_RELOCATION )((BYTE *)pBaseRelocation + pBaseRelocation->SizeOfBlock);
    }
    return 0;
}

void PrintBaseRelocation()
{
    LPVOID pFileBuffer = NULL;
    DWORD FileBufferSize = 0;
    DWORD BaseName_FunctionAddr = 0;
    DWORD BaseOrdinals_FUnctionAddr = 0;
    
    //File-->FileBuffer
    FileBufferSize = ReadPEFile(FilePath_In,&pFileBuffer);
    if (FileBufferSize == 0 || !pFileBuffer)
    {
        printf("文件-->缓冲区失败\r\n");
        return ;
    }
    printf("FileBufferSize: %#X \r\n",FileBufferSize);

    GetDataDirectoyOfBaseRelocation(pFileBuffer);
    
    free(pFileBuffer);
}

int main(int argc, char* argv[])
{
    PrintBaseRelocation();
    printf("Hello World That Fuck Successfully!\n");
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值