首页
社区
课程
招聘
[求助]关于“修复重定位表”技术的一点疑问
发表于: 2017-7-15 20:37 7574

[求助]关于“修复重定位表”技术的一点疑问

2017-7-15 20:37
7574

写程序来实现修改ImageBase后自动修复重定位表,把ImageBase改了之后,第一次是把重定位表里的每个VirtualAddress(除了最后VA和Size都为0的那个)都加上新ImageBase与旧ImageBase间的差值,行不通,改完后程序运行错误。然后第二次,不改VirtualAddress了,直接遍历把重定位表里的每个项都加上了这个差值,结果还是不行。两次试验后都先后用LordPE查看了VirtualAddress和所有项,发现都改了,而且也都加上了正确的差值,ImageBase也改的没错,但就是运行失败,不知道怎么回事,搞了整整2天了,没有一点思绪,而且也不肯定这种修复方式对不对。。。刚入门二进制安全,求各位前辈出手相助


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (17)
雪    币: 54
活跃值: (122)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
ImageBase  ->  NewImageBase    需要重定位的地址  -  OldImageBase  +  NewImageBase
2017-7-15 22:03
0
雪    币: 81
活跃值: (165)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
LYQINGYE ImageBase -> NewImageBase 需要重定位的地址 - OldImageBase + NewImageBase
不是,我这是直接对二进制程序进行修改,不是用工具,所以不能直接修改整个地址,而是只能修改表里的VA和TypeOffset,问题是我这样改却运行不起来,想问下,这样改对不对,如果对的话可能是我代码写的有问题
2017-7-16 10:28
0
雪    币: 267
活跃值: (438)
能力值: ( LV9,RANK:190 )
在线值:
发帖
回帖
粉丝
4
修改映像基地址就要对pe大手术了,而且成功率也不是有多大,除非你有源码编译的符号信息。要把pe有关所有va项全部修改
2017-7-16 17:09
0
雪    币: 81
活跃值: (165)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
linziqingl 修改映像基地址就要对pe大手术了,而且成功率也不是有多大,除非你有源码编译的符号信息。要把pe有关所有va项全部修改
前辈好,如果原来映像基地址是0x400000,把它改成0x500000后是不是只需要把重定位表里的所有VirtualAddress都加上0x100000就可以了
2017-7-16 23:20
0
雪    币: 4
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
请问楼主解决了吗?我也在尝试自己修改ImageBase,然后写程序自己修复,也是遇到了楼主一样的问题
2017-7-19 11:30
0
雪    币: 293
活跃值: (287)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7

#include <windows.h>
#include <tchar.h>
#define PAGE_SIZE 0x1000
#pragma pack(push, 1)
 
// 重定位表结构
typedef struct _OffTable{
    USHORT addr:12;
    USHORT flags:4;
}OffTable, *pOffTable;
typedef struct _RELOADTABLE{
    DWORD StartVirtualAddress;
    DWORD size;
    OffTable Table[1];
}RELOADTABLE, *pRELOADTABLE;
#pragma pack(pop)
 
// 修改重定位表 hmodule 加载的地址
// BaseAddress 将要重定位的地址,用来检查HOOK或者恢复hook时候用
BOOL RelocAddr(LPBYTE hModule, LPBYTE BaseAddress )
{
    if (hModule == NULL)
    {
        return FALSE;
    }
    PIMAGE_DOS_HEADER Header = (PIMAGE_DOS_HEADER)hModule;
    if( Header->e_magic != IMAGE_DOS_SIGNATURE )
    {
        return FALSE;
    }
    if ( Header->e_lfanew > 0x1000 || Header->e_lfanew < 0 )
    {
        return FALSE;
    }
    PIMAGE_NT_HEADERS peheader = 
        (PIMAGE_NT_HEADERS)((LPBYTE)Header + Header->e_lfanew);
    if (peheader->Signature != IMAGE_NT_SIGNATURE || 
        peheader->FileHeader.Machine != IMAGE_FILE_MACHINE_I386 &&  // x86
        peheader->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 )   // x64
    {
        return FALSE;
    }
    DWORD dwsizeOfImage = peheader->OptionalHeader.SizeOfImage;
    LPBYTE dwAddress = (LPBYTE)peheader->OptionalHeader.ImageBase;
    peheader->OptionalHeader.ImageBase = (DWORD)BaseAddress;
    if ( dwAddress == BaseAddress )
    {
        // 基地址不变 
        return TRUE;
    }
    // PE 头 offset 0x98
    DWORD pReloadOffset = peheader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
    if ( pReloadOffset  > dwsizeOfImage )
    {
        // 不落在这个文件内,重定位表错误
        return false;
    }
    PIMAGE_SECTION_HEADER SectionHeader = (PIMAGE_SECTION_HEADER)( (DWORD)peheader + 
        sizeof(peheader->FileHeader) + 
        sizeof(peheader->Signature) +
        peheader->FileHeader.SizeOfOptionalHeader ); // 节表项的开始
    WORD SectionNum = peheader->FileHeader.NumberOfSections; // 节数目
    pRELOADTABLE  reloadaddr = NULL;
    for (WORD i=0; i<SectionNum; i++) // 将节一个个复制到内存中
    {
        if (pReloadOffset>=SectionHeader[i].VirtualAddress && pReloadOffset<=SectionHeader[i].VirtualAddress+SectionHeader[i].SizeOfRawData)
        {
            reloadaddr = (pRELOADTABLE)(pReloadOffset-SectionHeader[i].VirtualAddress+SectionHeader[i].PointerToRawData+hModule);
            break;
        }
    } 
    // 遍历重定位表
    while ( reloadaddr->StartVirtualAddress != NULL && reloadaddr->size != NULL )
    {
        for (DWORD i=0; i<(reloadaddr->size-8)/2 ; i++)
        {
            __try
            {
                if ( reloadaddr->Table[i].flags == 3 )
                {
                    PDWORD* OffsetAddress;
                    DWORD dwOffset = (DWORD)(reloadaddr->Table[i].addr + reloadaddr->StartVirtualAddress);
                    for (WORD s=0; s<SectionNum; s++) 
                    {
                        if (dwOffset>=SectionHeader[s].VirtualAddress && dwOffset<=SectionHeader[s].VirtualAddress+SectionHeader[s].SizeOfRawData)
                        {
                            OffsetAddress = (PDWORD*)(dwOffset-SectionHeader[s].VirtualAddress+SectionHeader[s].PointerToRawData+hModule);
                            break;
                        }
                    } 
                    *OffsetAddress = (LPDWORD)((LPBYTE)(*OffsetAddress) - dwAddress + BaseAddress);
                }
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
                continue;
            }
        }
        reloadaddr = (pRELOADTABLE) ((DWORD)reloadaddr  + reloadaddr->size );
    }
    return TRUE; 
}

BOOL ReBuildDll(PTSTR szFileName, LPBYTE BaseAddress)
{
    HANDLE handle = CreateFile(szFileName, 
        GENERIC_READ|GENERIC_WRITE, 
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
    if (handle == INVALID_HANDLE_VALUE)
    {
        return FALSE;
    }
    DWORD dwBytes;
    DWORD dwFileSize = GetFileSize(handle, NULL);
    LPBYTE lpBuf = (LPBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwFileSize);
    ReadFile(handle, lpBuf, dwFileSize, &dwBytes, NULL);
    if( RelocAddr(lpBuf, BaseAddress) )
    {
        SetFilePointer(handle, 0, NULL, FILE_BEGIN);
        WriteFile(handle, lpBuf, dwFileSize, &dwBytes, NULL);
        CloseHandle(handle);
        return TRUE;
    }
    CloseHandle(handle);
    return FALSE;
}
 
int main(int argc, char* argv[])
{
    ReBuildDll(TEXT("CRC32.DLL"), (LPBYTE)0x08000000);
    return 0;
}

2017-7-19 12:38
1
雪    币: 4
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
瀚海云烟 #include& ...
看了瀚海云烟的代码之后发现自己一直理解错了,之前一直纠结在如何把很大的差值放到每两个字节的低12位,还是放在每一块的VirtualAddress,两个都试过了,一改DLL就不能使用了,看完他的代码之后才明白,三项加起来之后才是要修改的地址,而我之前一直想修改这三项中的VirtualAddress和低12位,瀚海云烟的是将DLL加载,我的是直接把文件中的原封不动的读到内存,然后再改ImageBase,最后保存到文件,这其中还要有一步RvaToFoa的过程,因为我不是加载,另外,看了他的代码发现我以前只是知道结构体的位字段,但是并没有使用过,今日一见,确实思路很清晰,我是靠指针之间的强转跳过8字节,用与运算得到12位,每一次都要写的很长,还容易出错,从中学习了很多
2017-7-19 15:19
0
雪    币: 267
活跃值: (438)
能力值: ( LV9,RANK:190 )
在线值:
发帖
回帖
粉丝
9
Greebees 前辈好,如果原来映像基地址是0x400000,把它改成0x500000后是不是只需要把重定位表里的所有VirtualAddress都加上0x100000就可以了
我记得以前好像在看雪上上传过一个源码是隐藏重定位信息的,对你应该有一下参考价值吧,你自己找找,我都不记得了。
2017-7-19 18:15
0
雪    币: 293
活跃值: (287)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
10

只是刚好有内存加载PE的代码,拿来用用非常方便。

2017-7-19 19:14
0
雪    币: 4
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11

//把修复重定位表这部分相关的都贴出来了,自己写的
//希望有帮助,昨天我也不清楚到底是怎么修复的

// FileBuffer
BYTE* p = NULL;

void openFile()
{
	FILE *fp = fopen("C:\\Users\\Administrator\\Desktop\\Dynamic Link Library.dll", "rb");
	fseek(fp, 0, SEEK_END);
	
	fileLen = ftell(fp);
	p = (BYTE*)malloc(fileLen);
	memset(p, 0, fileLen);
	fseek(fp, 0, SEEK_SET);
	fread(p, fileLen, 1, fp);
	fclose(fp);
	return ;
}

//在文件中还是拉伸后在内存中都可以按这个函数找
void* Switch(int index, BYTE* base)
{
	BYTE*  pBuf = base;
	//1 DOS
	IMAGE_DOS_HEADER*		pDos = (IMAGE_DOS_HEADER *)pBuf;
	//2 NT
	IMAGE_NT_HEADERS32*		pNt = (IMAGE_NT_HEADERS32 *)(pBuf + pDos->e_lfanew);
	//3 PE
	IMAGE_FILE_HEADER*		pFile = (IMAGE_FILE_HEADER *)&pNt->FileHeader;
	//4 Option
	IMAGE_OPTIONAL_HEADER32*   pOption = (IMAGE_OPTIONAL_HEADER32 *)&pNt->OptionalHeader;
	//5 DataDirectoy
	IMAGE_DATA_DIRECTORY*	pDataDir = pOption->DataDirectory;
	//6 Section table
	IMAGE_SECTION_HEADER*	pSection = (IMAGE_SECTION_HEADER *)(  (long)pNt +  (long)&(((IMAGE_NT_HEADERS32 *)0)->OptionalHeader)  + pFile->SizeOfOptionalHeader );
	switch(index)
	{
	case 1:
		return pDos;
	case 2:
		return pNt;
	case 3:
		return pFile;
	case 4:
		return pOption;
	case 5:
		return pDataDir;
	case 6:
		return pSection;
	}
	return NULL;
}

DWORD RvaToFoa(DWORD rva)
{
	int i;
	//1 DOS
	IMAGE_DOS_HEADER*		pDos = (IMAGE_DOS_HEADER *)Switch(1, p);
	//2 NT
	IMAGE_NT_HEADERS32*		pNt = (IMAGE_NT_HEADERS32 *)Switch(2, p);
	//3 PE
	IMAGE_FILE_HEADER*		pFile = (IMAGE_FILE_HEADER *)Switch(3, p);
	//4 Option
	IMAGE_OPTIONAL_HEADER32*   pOption = (IMAGE_OPTIONAL_HEADER32 *)Switch(4, p);
	//5 DataDirectoy
	IMAGE_DATA_DIRECTORY*	pDataDir = (IMAGE_DATA_DIRECTORY*)Switch(5, p);
	//6 Section table
	IMAGE_SECTION_HEADER*	pSection = (IMAGE_SECTION_HEADER *)Switch(6, p);
	
	// 如果rva在第一个节之前
	if(rva <= pSection->VirtualAddress)
	{
		return rva;
	}
	
	for(i=0 ; i < pFile->NumberOfSections ; i++)
	{
		if(
			(rva >= pSection->VirtualAddress) &&
			(rva < pSection->VirtualAddress + pSection->Misc.VirtualSize)
		  )
		{
			return rva - pSection->VirtualAddress + pSection->PointerToRawData;
		}
		pSection++;
	}
	return -1;
}

//数据目录表项
void* AddressOfTable(int index)
{
	IMAGE_DOS_HEADER*	pDos = (IMAGE_DOS_HEADER*)Switch(1, p);
	IMAGE_FILE_HEADER*	pFile = (IMAGE_FILE_HEADER*)Switch(3, p);
	IMAGE_OPTIONAL_HEADER32* pOption = (IMAGE_OPTIONAL_HEADER32*)Switch(4, p);
	IMAGE_DATA_DIRECTORY*	pDataDir = (IMAGE_DATA_DIRECTORY *)Switch(5, p);
	
	//判断表存在
	if(pDataDir[index].VirtualAddress == 0)
	{
		printf("空表\n");
		return NULL;
	}
	
	switch(index)
	{
	case 0:
		return (BYTE *)pDos + RvaToFoa(pDataDir[0].VirtualAddress);
	case 1:
		return (BYTE *)pDos + RvaToFoa(pDataDir[1].VirtualAddress);
	case 5:
		return (BYTE *)pDos + RvaToFoa(pDataDir[5].VirtualAddress);
	}
	return NULL;
}

// 修复 重定位表
void restoreRelocation()
{
	//重定位表
	IMAGE_BASE_RELOCATION	*pstart;
	WORD					*value;		//要修改的项
	
	DWORD					count;	//需要重定位的个数
	DWORD					size;
	DWORD					tmp;	//保存低12位
	DWORD					imageBase; //旧的ImageBase
	DWORD					i;
	DWORD					*offest;
	
	IMAGE_DOS_HEADER*	pDos = (IMAGE_DOS_HEADER*)Switch(1, p);
	IMAGE_FILE_HEADER*	pFile = (IMAGE_FILE_HEADER*)Switch(3, p);
	IMAGE_OPTIONAL_HEADER32* pOption = (IMAGE_OPTIONAL_HEADER32*)Switch(4, p);
	IMAGE_DATA_DIRECTORY*	pDataDir = (IMAGE_DATA_DIRECTORY *)Switch(5, p);
	IMAGE_SECTION_HEADER*	pSection = (IMAGE_SECTION_HEADER *)Switch(6, p);
	
	//重定位表
	pstart = (IMAGE_BASE_RELOCATION *)AddressOfTable(5);
	// oldImageBase
	imageBase = pOption->ImageBase;
	
	// newImageBase
	pOption->ImageBase = 0x500000;
	while(pstart->VirtualAddress != 0 && pstart->SizeOfBlock != 0)
	{
		//printf("pstart->VirtualAddress: %X\n", pstart->VirtualAddress);
		count = (pstart->SizeOfBlock - 8) / 2;
		size = pstart->SizeOfBlock;
		
		// 跳过8字节
		value = (WORD *)( (BYTE *)pstart + 8 );
	
		for(i=0 ; i < count ; i++)
		{
			// 取高4位
			tmp = *value & 0x0000F000;
			// 高四位是3
			if(tmp == 0x3000)
			{
				//输出低12位
				//printf("%X\n", (*value & 0x00000FFF) + pstart->VirtualAddress);
				
				//文件原封不动读到内存中pDos就是"MZ",每一块的VirtualAddress + 低12位的和是rva
				//我相当于在文件中找,所以要转成foa
				offest = (DWORD *)( (BYTE *)pDos + RvaToFoa( pstart->VirtualAddress + (*value & 0x00000FFF) ) );
				
			        //每一项都改
				*offest = *offest - imageBase + pOption->ImageBase;
			}	
			value++;	//2
		}
		pstart = (IMAGE_BASE_RELOCATION *)( (BYTE *)pstart + size );
	}
	return ;
}

int main()
{
	openFile();
	
	//testAddSection();
	//FileBufferToImageBuffer();
	
	//testAddCode();
	//testEnlargeSection();
	
	//testMergeSection();
	//ImageBufferToFileBuffer();
	//printDataDirectory();
	//myPlus = (lpPlus)GetFounctionAddrByIndex(p, 12);
	
	//printf("%08X\n", myPlus);
	//myPlus = (lpPlus)GetFounctionAddrByName(p, "Plus");
	
	//printf("%08X\n", myPlus);
	//printRelocation();
	//moveExportTable();
	//moveRelocation();
	
	//修复重定位表
	restoreRelocation();
	
	//printImportTable();
	
	closeFile();
	free(p);
	
	//free(p2);
	//free(p3);
	
	return 0;
}


2017-7-20 08:41
0
雪    币: 81
活跃值: (165)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
一花季落 //把修复重定位表这部分相关的都贴出来了,自己写的 //希望有帮助,昨天我也不清楚到底是怎么修复的 //&nbsp;FileBuffer BYTE*&n ...
太谢谢啦前辈!!!!原来是我之前对重定位理解错误了,看完代码真是醍醐灌顶
2017-8-2 12:27
0
雪    币: 81
活跃值: (165)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
一花季落 //把修复重定位表这部分相关的都贴出来了,自己写的 //希望有帮助,昨天我也不清楚到底是怎么修复的 //&nbsp;FileBuffer BYTE*&n ...
不过还有个疑问,在修复重定位表函数的循坏那块,既然判断高4位是否为3是这样写的:  tmp  =  *value  &amp;  0x0000F000;,那为什么输出低12位就是这样:printf(&quot;%X\n&quot;,  (*value  &amp;  0x00000FFF)  +  pstart-&gt;VirtualAddress);,而不是/printf(&quot;%X\n&quot;,  (*value  &amp;  0x0FFF0000)  +  pstart-&gt;VirtualAddress);呢
2017-8-2 12:34
0
雪    币: 10
活跃值: (71)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
装载后不用进行DLL初始化吗?
2017-8-18 18:13
0
雪    币: 4
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
Greebees 不过还有个疑问,在修复重定位表函数的循坏那块,既然判断高4位是否为3是这样写的: tmp = *value & 0x0000F000;,那为什么输出低12位就是这样:printf(" ...
        最近一直在写东西,然后又出去玩了两天,有一段时间没有上论坛。
        下面说一下原因,因为重定位表里面的每一项数据是2个字节,所以我才说高4位和低12位,16位合起来是两个字节,因为我平常在写16进制数的时候基本上都是写32位,4个字节的,所以让你误解了,这下你应该能明白。
2017-8-19 17:03
0
雪    币: 0
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
16
请问楼主解决了吗?
2017-8-21 11:56
0
雪    币: 81
活跃值: (165)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
嗷嗷嗷啊啊 请问楼主解决了吗?
明白了,谢谢前辈
2017-10-26 20:50
0
雪    币: 204
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
谁能看看我的代码啊    也是重定位的问题  哪里错了啊
2017-12-13 19:55
0
游客
登录 | 注册 方可回帖
返回
//