仿照windows的loader实现的pe-loader

   本人接触pe文件格式不久,参考网上的一些资料写了一个pe loader,主要是通过把需要加载的文件的所所有section加载到相应的RVA上,然后进行重定位处理、导入表和导出表处理、资源段处理。由于本loader.exe会被加载到0x0f400000处,因此可以把0x400000给被加载的文件预留了,这样可以避免重定位所带来的性能损耗。导入表的处理主要是通过调用GetProcAddress获取导入表中函数的地址。导出表一般仅在dll中使用,而此主要是用于load exe文件,所以不用做任何处理。资源段处理资料不多,我这里好像有些问题,加载console程序可以正常执行,但是当加载一部分gui程序则不能成功,而令一些gui程序则会成功。望高手能指教一二。
    下面是源代码部分:

view plaincopy to clipboardprint?
/* 
 *LocalFile是对读文件操作的一个简单封装 
 */ 
//LocalFile.h  
#pragma once  
 
class CLocalFile  
{  
public:  
    CLocalFile(const char * name);  
    ~CLocalFile(void);  
 
    int Read(size_t offset, size_t size, void *ppBuf);  
 
    size_t GetSize() const { return m_size;}  
 
private:  
    HANDLE           m_fd;  
    size_t          m_size;  
};  
 
//LocalFile.cpp  
#include "LocalFile.h"  
 
CLocalFile::CLocalFile(const char * name)  
{  
    assert(NULL != name);  
    assert(0 != name[0]);  
 
    m_fd = CreateFileA(name, GENERIC_READ,  
                        FILE_SHARE_READ, NULL,  
                        OPEN_EXISTING,  
                        FILE_ATTRIBUTE_NORMAL, NULL);  
    if (INVALID_HANDLE_VALUE == m_fd) {  
        printf("Open file %s failed, error: %d!/n",name, GetLastError());  
        return ;  
    }  
 
    m_size = GetFileSize(m_fd, NULL);  
    return;  
}  
 
CLocalFile::~CLocalFile(void)  
{  
    if (NULL != m_fd) {  
        CloseHandle(m_fd);  
    }  
}  
 
int CLocalFile::Read(size_t offset,  
                     size_t size,  
                     void * pBuf)  
{  
    DWORD ret;  
    ULONG rdsize;  
 
    ret = SetFilePointer(m_fd, offset, NULL, FILE_BEGIN);  
    if (INVALID_SET_FILE_POINTER == ret) {  
        ret = GetLastError();  
        printf("Seek file failed, error: %d!/n", ret);  
        return ret;  
    }  
 
    if (!ReadFile(m_fd, pBuf, size, &rdsize, NULL)) {  
        ret = GetLastError();  
        printf("Read file failed, error: %d/n", ret);  
        return ret;  
    }  
 
    return 0;  
}  
 
//loader.cpp  
 
#include "LocalFile.h"  
 
/*把该loader.exe的加载地址设置为0x0f400000,从而可以把0x00400000地址 
 *预留给将要被加载的程序,从而可以避免因地址重定位而带来的性能损耗。 
 */ 
#pragma comment(linker, "/BASE:0x0f400000")  
 
inline int CDECL DebugPrint(const char *fmt,...)  
{  
    int nLength = 0;  
#if defined(_DEBUG)  
    va_list ap;  
    va_start(ap, fmt);  
    nLength = vprintf(fmt, ap);  
    va_end(ap);  
#endif  
    return nLength;  
}  
 
void DumpPeInfo(PeInfo *pInfo)  
{  
    DebugPrint("------------headers info------------/n" /  
               "imageBase:        0x%x. /n" /  
               "entryPoint:       0x%x. /n" /  
               "sections:         0x%x. /n" /  
               "imageSize:        0x%x. /n" /  
               "exportRva:        0x%x. /n" /  
               "exportSize:       0x%x. /n" /  
               "importRva:        0x%x. /n" /  
               "importSize:       0x%x. /n" /  
               "resourceRva:      0x%x. /n" /  
               "resourceSize:     0x%x. /n" /  
               "relocRva:         0x%x. /n" /  
               "relocSize:        0x%x. /n" /  
               "debugRva:         0x%x. /n" /  
               "debugSize:        0x%x. /n" /  
               "offsetSections:   0x%x. /n" /  
               "fileType:         %s. /n",  
                pInfo->imageBase,  
                pInfo->entryPoint,  
                pInfo->sections,  
                pInfo->imageSize,  
                pInfo->exRva,  
                pInfo->exSize,  
                pInfo->imRva,  
                pInfo->imSize,  
                pInfo->resRva,  
                pInfo->resSize,  
                pInfo->relocRva,  
                pInfo->relocSize,  
                pInfo->dbgRva,  
                pInfo->dbgSize,  
                pInfo->offsetSection,  
                pInfo->fileType == 0 ? "exe":"dll");  
}  
 
BOOL IsPEFile(CLocalFile& lf)  
{  
    IMAGE_DOS_HEADER dh;  
    IMAGE_NT_HEADERS nh;  
 
    lf.Read(0, sizeof(IMAGE_DOS_HEADER), &dh);  
    if (IMAGE_DOS_SIGNATURE != dh.e_magic) {  
        return FALSE;  
    }  
 
    lf.Read(dh.e_lfanew, sizeof(IMAGE_NT_HEADERS), &nh);  
    if (IMAGE_NT_SIGNATURE != nh.Signature) {  
        return FALSE;  
    }  
 
    return TRUE;  
}  
 
BOOL ParseNTHeader(CLocalFile &lf, PeInfo &pe)  
{  
    IMAGE_DOS_HEADER dh;  
    IMAGE_NT_HEADERS nh;  
    PIMAGE_FILE_HEADER pfh;  
    PIMAGE_OPTIONAL_HEADER32 poh;  
 
    lf.Read(0, sizeof(IMAGE_DOS_HEADER), &dh);  
    pe.offsetSection = dh.e_lfanew + sizeof(IMAGE_NT_HEADERS);  
    lf.Read(dh.e_lfanew, sizeof(IMAGE_NT_HEADERS), &nh);  
 
    pfh = &nh.FileHeader;  
    poh = &nh.OptionalHeader;  
 
    assert(IMAGE_FILE_MACHINE_I386 == pfh->Machine);  
    assert(pfh->SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32));  
 
    pe.sections = pfh->NumberOfSections;  
    pe.imageBase = poh->ImageBase;  
    pe.entryPoint = poh->AddressOfEntryPoint;  
    pe.imageSize = poh->SizeOfImage;  
 
    pe.exRva = poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;  
    pe.exSize = poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;  
    pe.imRva = poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;  
    pe.imSize = poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;  
    pe.resRva = poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;  
    pe.resSize = poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size;  
    pe.relocRva = poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;  
    pe.relocSize = poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;  
    pe.dbgRva = poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;  
    pe.dbgSize = poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;  
 
    pe.fileType = pfh->Characteristics == IMAGE_FILE_DLL ? IMAGE_FILE_DLL : 0;  
 
    DumpPeInfo(&pe);  
 
    return TRUE;  
}  
 
BOOL LoadSections(CLocalFile &lf, PeInfo &pe, MODULEINFO &mi)  
{  
    PIMAGE_SECTION_HEADER psh, ptmp;  
    DWORD protect = 0, oldProtect = 0;  
    char name[9] = "/0";  
 
    psh = (PIMAGE_SECTION_HEADER)_alloca(pe.sections * sizeof(IMAGE_SECTION_HEADER));  
    if (NULL == psh) {  
        printf("Memory not enough!/n");  
        return FALSE;  
    }  
    lf.Read(pe.offsetSection, pe.sections * sizeof(IMAGE_SECTION_HEADER), psh);  
    ptmp = psh;  
 
    DebugPrint("/n/n------------sections info------------/n" /  
               "name/tVA/tSOD/tPTR/tPTR/tPTL/tNOR/tNOL/tCrt/n");  
 
    /* 循环从被加载的文件中读取各个section内容,并存放在该section所指定的 
     * VirtualAddress地址空间中。 
     */ 
    for (int i = 0; i < pe.sections; ++i, ptmp++) {  
        memcpy(name, ptmp->Name, 8);  
 
        if (NULL != ptmp->PointerToRawData && 0 != ptmp->SizeOfRawData) {  
            lf.Read(ptmp->PointerToRawData,  
                        ptmp->SizeOfRawData,  
                        (void *)((DWORD)mi.lpBaseOfDll + ptmp->VirtualAddress));  
        }  
 
 
        /* 此处代码主要功能是根据各个区段的Characteristics值, 
         * 来设置其所在内存的页属性,但因在RelocPeModule机制重定位时还需要对相关内存 
         * 进行写入,因此此处的代码应该在RelocPeModule函数调用之后才能执行。 
         */ 
#if 0   
        if (ptmp->Characteristics & IMAGE_SCN_MEM_READ) {  
            protect = PAGE_READONLY;  
        }  
        if (ptmp->Characteristics & IMAGE_SCN_MEM_WRITE) {  
            protect = PAGE_READWRITE;  
        }  
        if (ptmp->Characteristics & IMAGE_SCN_MEM_EXECUTE) {  
            if (protect & PAGE_READONLY) {  
                protect = PAGE_EXECUTE_READ;  
            }  
            else if (protect & PAGE_READWRITE) {  
                protect = PAGE_EXECUTE_READWRITE;  
            }  
            else {  
                protect = PAGE_EXECUTE;  
            }  
        }  
 
        if (!VirtualProtect((LPVOID)(ptmp->VirtualAddress + (DWORD)mi.lpBaseOfDll),  
                            ptmp->SizeOfRawData, protect, &oldProtect)) {  
            printf("Set memory protection failed, error: %d/n", GetLastError());  
            return FALSE;  
        }  
#endif  
        DebugPrint("%-8s" /  
                   "0x%x/t" /  
                   "0x%x/t" /  
                   "0x%x/t" /  
                   "0x%x/t" /  
                   "0x%x/t" /  
                   "0x%x/t" /  
                   "0x%x/t" /  
                   "0x%x/n",  
                   name,  
                   ptmp->VirtualAddress,  
                   ptmp->SizeOfRawData,  
                   ptmp->PointerToRawData,  
                   ptmp->PointerToRelocations,  
                   ptmp->PointerToLinenumbers,  
                   ptmp->NumberOfRelocations,  
                   ptmp->NumberOfLinenumbers,  
                   ptmp->Characteristics);  
    }  
 
    return TRUE;  
}  
 
BOOL FixupResource(PIMAGE_RESOURCE_DIRECTORY pRes, DWORD imagebase, int offsetRlc)  
{  
    PIMAGE_RESOURCE_DIRECTORY_ENTRY pEntry;  
    DWORD nEntries;  
 
    nEntries = pRes->NumberOfIdEntries + pRes->NumberOfNamedEntries;  
 
    pEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)((DWORD)pRes + sizeof(IMAGE_RESOURCE_DIRECTORY));  
 
    for (DWORD i = 0; i < nEntries; ++i, ++pEntry) {  
 
        if (IMAGE_RESOURCE_DATA_IS_DIRECTORY & pEntry->OffsetToData) {  
            PIMAGE_RESOURCE_DIRECTORY pRes2;  
            PIMAGE_RESOURCE_DIRECTORY_ENTRY pEntry2;  
            DWORD nEntries2;  
 
            pRes2 = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)pRes  
                        + (~IMAGE_RESOURCE_DATA_IS_DIRECTORY & pEntry->OffsetToData));  
            nEntries2 = pRes2->NumberOfIdEntries + pRes2->NumberOfNamedEntries;  
            pEntry2 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)((DWORD)pRes2 + sizeof(IMAGE_RESOURCE_DIRECTORY));  
              
            for (DWORD j = 0; j < nEntries2; ++j, ++pEntry2) {  
                if (IMAGE_RESOURCE_NAME_IS_STRING & pEntry2->Name) {  
                    PIMAGE_RESOURCE_DIR_STRING_U pDirStr;  
                    pDirStr = (PIMAGE_RESOURCE_DIR_STRING_U)((DWORD)pRes  
                                + (~IMAGE_RESOURCE_NAME_IS_STRING & pEntry2->Name));  
                }  
                if (IMAGE_RESOURCE_DATA_IS_DIRECTORY & pEntry2->OffsetToData) {  
                    PIMAGE_RESOURCE_DIRECTORY pRes3;  
                    PIMAGE_RESOURCE_DIRECTORY_ENTRY pEntry3;  
                    DWORD nEntries3;  
 
                    pRes3 = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)pRes  
                                + (~IMAGE_RESOURCE_DATA_IS_DIRECTORY & pEntry2->OffsetToData));  
                    nEntries3 = pRes3->NumberOfIdEntries + pRes3->NumberOfNamedEntries;  
                    pEntry3 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)((DWORD)pRes3 + sizeof(IMAGE_RESOURCE_DIRECTORY));  
 
                    for (DWORD k = 0; k < nEntries3; ++k) {  
                        PIMAGE_RESOURCE_DATA_ENTRY pData;  
 
                        assert(~IMAGE_RESOURCE_DATA_IS_DIRECTORY & pEntry3->OffsetToData);  
 
                        pData = (PIMAGE_RESOURCE_DATA_ENTRY)((DWORD)pRes + pEntry3->OffsetToData);  
                        pData->OffsetToData += (DWORD)imagebase;  
                    }  
                }  
            }  
 
        }  
    }  
 
    return TRUE;  
}  
 
BOOL RelocPeModule(PIMAGE_BASE_RELOCATION pBlc, DWORD imagebase, int offsetRlc)  
{  
    DWORD vaddr, count, offset, type;  
    WORD *items = NULL;  
 
    while (NULL != pBlc->VirtualAddress) {  
        vaddr = imagebase + pBlc->VirtualAddress;  
 
        count = (pBlc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) >> 1;  
        items = (WORD *)((char *)pBlc + sizeof(IMAGE_BASE_RELOCATION));  
 
        for (DWORD i = 0; i < count; ++i) {  
            offset = items[i] & 0x0fff;  
            type = items[i] >> 12;  
 
            if (type == 3) {  
                *(DWORD *)(vaddr + offset) += offsetRlc;  
            }  
        }  
        pBlc = (PIMAGE_BASE_RELOCATION)(items + count);  
    }  
 
    return TRUE;  
}  
 
BOOL FixupExport(PIMAGE_EXPORT_DIRECTORY pExp, DWORD imagebase)  
{  
    return TRUE;  
}  
 
BOOL FixupImport(PIMAGE_IMPORT_DESCRIPTOR pImp, DWORD imagebase)  
{  
    PIMAGE_THUNK_DATA pOrgThunk, pFirstThunk;  
    PIMAGE_IMPORT_BY_NAME pImportName;  
 
    DebugPrint("/n/n------------import table info------------/n");  
 
    while (NULL != pImp->OriginalFirstThunk) {  
        pImp->Name += imagebase;  
 
        DebugPrint("DLL: %s/n", pImp->Name);  
 
        FARPROC fpFun;  
        HINSTANCE hInstance = LoadLibraryA((LPCSTR)pImp->Name);  
        if (NULL == hInstance) {  
            printf("Load library %s failed, error: %d/n", pImp->Name, GetLastError());  
            return FALSE;  
        }  
 
        pOrgThunk = (PIMAGE_THUNK_DATA)(imagebase + pImp->OriginalFirstThunk);  
        pFirstThunk = (PIMAGE_THUNK_DATA)(imagebase + pImp->FirstThunk);  
 
        while (NULL != *(DWORD *)pOrgThunk) {  
            if (pOrgThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32) {  
                fpFun = GetProcAddress(hInstance, (LPCSTR)(pOrgThunk->u1.Ordinal & 0x0000ffff));  
 
                //DebugPrint("/t0x%x/n", pOrgThunk->u1.Ordinal);  
            }  
            else {  
                pImportName = (PIMAGE_IMPORT_BY_NAME)(imagebase + pOrgThunk->u1.AddressOfData);  
                fpFun = GetProcAddress(hInstance, (LPCSTR)pImportName->Name);  
 
                //DebugPrint("/t%s/n", pImportName->Name);  
            }  
 
            //DebugPrint("/t/t0x%x/n", fpFun);  
 
            pFirstThunk->u1.Ordinal = (LONG)fpFun;  
              
            ++pFirstThunk;  
            ++pOrgThunk;  
        }  
        FreeLibrary(hInstance);  
 
        ++pImp;  
    }  
 
    return TRUE;  
}  
 
int LoadPeModule(const char * name, int argc, _TCHAR* argv[])  
{  
    CLocalFile lf(name);  
    LPVOID addr;  
    PeInfo pe;  
    MODULEINFO mi;  
 
    /* 验证是否为合法的pe文件 */ 
    assert(IsPEFile(lf));  
 
    /* 处理nt header,获取pe文件的相关信息 */ 
    ParseNTHeader(lf, pe);  
 
    /*为image按照imageBase优先原则分配空间地址。 
     *如该空间地址已被reserved或commit,则重新分配一个可用的空间地址, 
     *但此种情况下需要做基址重定位处理。*/ 
    addr = VirtualAlloc((LPVOID)(pe.imageBase),  
                                pe.imageSize,  
                                MEM_RESERVE | MEM_COMMIT,  
                                PAGE_EXECUTE_READWRITE);  
    if (NULL == addr) {  
        printf("VirtualAlloc failed, error: %d/n", GetLastError());  
        addr = VirtualAlloc(NULL, pe.imageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);  
        if (NULL == addr) {  
            printf("VirtualAlloc failed, error: %d/n", GetLastError());  
            return -1;  
        }  
    }  
    memset((void *)addr, 0, pe.imageSize);  
 
    mi.EntryPoint = (LPVOID)pe.entryPoint;  
    mi.lpBaseOfDll = (LPVOID)addr;  
    mi.SizeOfImage = pe.imageSize;  
 
    /* 把sections加载到内存 */ 
    LoadSections(lf, pe, mi);  
 
    /* 如果实际分配的空间地址和pe文件的基址不一样,则需要做基址重定位处理 */ 
    if ((int)mi.lpBaseOfDll != pe.imageBase) {  
        if (0 == pe.relocSize) {  
            printf("Cannot reloc address!/n");  
            return -1;  
        }  
        PIMAGE_BASE_RELOCATION pBrlc = (PIMAGE_BASE_RELOCATION)((DWORD)mi.lpBaseOfDll + pe.relocRva);  
        RelocPeModule(pBrlc, (DWORD)mi.lpBaseOfDll, (DWORD)mi.lpBaseOfDll - pe.imageBase);  
    }  
 
    /* 如果该pe文件有导出表,则处理导出表区段 */ 
    if (0 != pe.exSize) {  
        PIMAGE_EXPORT_DIRECTORY pExp = (PIMAGE_EXPORT_DIRECTORY)((DWORD)mi.lpBaseOfDll + pe.exRva);  
        FixupExport(pExp, (DWORD)mi.lpBaseOfDll);  
    }  
 
    /* 如果pe文件有资源文件,则需要处理资源区段 */ 
    if (0 != pe.resSize) {  
        PIMAGE_RESOURCE_DIRECTORY pRes = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)mi.lpBaseOfDll + pe.resRva);  
        FixupResource(pRes, (DWORD)mi.lpBaseOfDll, (DWORD)mi.lpBaseOfDll - pe.imageBase);  
    }  
 
    /* 如果pe文件有导入表,则需要处理导入表区段 */ 
    if (0 != pe.imSize) {  
        PIMAGE_IMPORT_DESCRIPTOR pImp = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)mi.lpBaseOfDll + pe.imRva);  
        FixupImport(pImp, (DWORD)mi.lpBaseOfDll);  
    }  
 
    DebugPrint("/n/nentry: 0x%x/n/n", (DWORD)mi.EntryPoint + (DWORD)mi.lpBaseOfDll);  
 
    /* 进入该pe文件的entry pointer,执行该pe文件 */ 
    __asm {  
        push argc;  
        push argv;  
        mov eax, mi.EntryPoint;  
        add eax, mi.lpBaseOfDll;  
        call eax;  
    }  
}  
 
int _tmain(int argc, _TCHAR* argv[])  
{  
    //LoadPeModule("HelloWorld.exe", argc - 1, argv + 1);  
    LoadPeModule("btnlook.exe", argc - 1, argv + 1);  
 
    return 0;  

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/lf8289/archive/2010/02/09/5301269.aspx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值