PE文件代码段的加密与解密

来源:互联网 发布:c语言输出图案 编辑:程序博客网 时间:2024/05/16 07:19
// 区块合并.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include<Windows.h>
#include<ImageHlp.h>
#include<iostream>
using namespace std;
#pragma comment(lib,"imagehlp.lib")
char* strSrcExePath="D:\\project\\tmp\\Debug\\tmp.exe";

////////////////////////////////////////////////////////////////////
//函数名称:GetAlign
//入口参数:size,align
//出口参数:DWORD
//函数描述:获得对齐后的大小
////////////////////////////////////////////////////////////////////
DWORD GetAlign(DWORD size,DWORD align)
{
    DWORD dwResult;
    if(size<align)
    {
        dwResult=align;
    }
    if(size%align)
    {
        dwResult=(size/align+1)*align;
    }
    else
    {
        dwResult=(size/align)*align;
    }
    return dwResult;
}
////////////////////////////////////////////////////////////////////
//函数名称:EncryptText
//入口参数:char*,可执行程序的路径
//出口参数:void
//函数描述:将代码段进行与操作
////////////////////////////////////////////////////////////////////
void EncryptText(char*strExePath)
{
    HANDLE hFile=CreateFileA(strExePath,GENERIC_ALL,FILE_SHARE_READ,NULL,OPEN_EXISTING,NULL,NULL);
    if(INVALID_HANDLE_VALUE==hFile)
    {
        cout<<"文件打开失败\n";
        return;
    }
    DWORD dwFileSize=GetFileSize(hFile,0);
    HANDLE hMap=CreateFileMappingA(hFile,NULL,PAGE_EXECUTE_READWRITE,0,dwFileSize+4096,NULL);
    if(hMap==INVALID_HANDLE_VALUE)
    {
        cout<<"创建文件映像失败\n";
        return;
    }
    PVOID lpImageBase=MapViewOfFile(hMap,FILE_MAP_READ|FILE_MAP_WRITE|FILE_MAP_EXECUTE,0,0,0);
    //获得DOS头部
    PIMAGE_DOS_HEADER pDosHeader=(PIMAGE_DOS_HEADER)lpImageBase;
    if(pDosHeader->e_magic!=IMAGE_DOS_SIGNATURE)
    {
        cout<<"对不起,并非PE文件\n";
        return ;
    }
    //获得NT头部
    PIMAGE_NT_HEADERS pNtHeader=(PIMAGE_NT_HEADERS)((DWORD)lpImageBase+pDosHeader->e_lfanew);
    if(pNtHeader->Signature!=IMAGE_NT_SIGNATURE)
    {
        cout<<"NT对不起,非PE文件\n";
        return;
    }
    //获得可选头部地址
    DWORD dwAddrOfOptionalHeader=(DWORD)&pNtHeader->OptionalHeader;
    //获得区块信息
    PIMAGE_SECTION_HEADER pSectionHeader=(PIMAGE_SECTION_HEADER)(dwAddrOfOptionalHeader+pNtHeader->FileHeader.SizeOfOptionalHeader);
    DWORD dwSectionNum=pNtHeader->FileHeader.NumberOfSections;
    DWORD dwTmp=0;    
    for(DWORD i=0;i<dwSectionNum;i++)
    {
        //使得区段可写
            DWORD dwSectionSize=pSectionHeader->Misc.VirtualSize;
            DWORD dwCharacter=pSectionHeader->Characteristics;
            dwCharacter=dwCharacter|0x80000000;
            pSectionHeader->Characteristics=dwCharacter;
        char* strHeaderName=(char*)pSectionHeader->Name;        
        if((strcmp(strHeaderName,".text")==0))
        {            

            //加密操作,与0异或,解密操作也是与0异或
            SetFilePointer(hFile,pSectionHeader->PointerToRawData,0,0);
            BYTE tmp=0;
                    
            for(int j=0;j<dwSectionSize;j++)
            {
                ReadFile(hFile,&tmp,1,&dwTmp,NULL);
                tmp=tmp^0xff;
                SetFilePointer(hFile,-1,NULL,FILE_CURRENT);
                WriteFile(hFile,&tmp,1,&dwTmp,NULL);
            }
        }
        pSectionHeader++;
    }
    goto shellEnd;
    _asm
    {
ShellCode://在此进行区块的解密,首先找到本区块的Flink,然后PE头部,进而是区块信息,然后是text的地址
        pushad;
        pushfd;
        push ebp
        ;获得PEB
        mov eax,fs:[30h]
        ;获得LDR
        mov eax,[eax+0ch]
        ;获得Flink
        mov eax,[eax+14h]
        ;获得LDR_DATA_TABLE_
        sub eax,8
        ;获得本模块的基址
        mov eax,[eax+18h]
        ;保存本模块的基址
        mov ebp,eax
        ;获得本模块的PE头部
        mov eax,[eax+3ch]
        ;获得文件头部的地址
        add eax,4
        ;获得可选头部大小地址
        add eax,10h
        add eax,ebp
        ;ebx中即是可选头部大小
        xor ebx,ebx
        mov bx,[eax]
        ;得到可选头部地址
        add eax,4
        ;可选头部地址加上可选头部大小即是区块信息
        add eax,ebx
        ;方便循环的使用
        sub eax,40
searchSection:
        add eax,40
        mov ebx,7865742eh
        cmp [eax],ebx;'xet.'
        jnz searchSection;
        mov ebx,74h
        cmp [eax+4],ebx
        jnz searchSection;
        ;到此已经找到相应的区块,查找区块的RVA
        add eax,0ch
        ;获得区块对齐后的大小,SizeOfRawData
        mov ebx,[eax+4h]    
        mov eax,[eax]
        ;找到区块的地址
        add eax,ebp;
        mov ecx,ebx;
UndoEncrypt:
        ;解密操作
        mov bl,byte ptr[eax]
        xor bl,0ffh
        mov byte ptr[eax],bl
        inc eax
        loop UnDoEncrypt        

        ;区块解密完成之后还要进行重定位操作
        ;获得PEB
        mov eax,fs:[30h]
        ;获得LDR
        mov eax,[eax+0ch]
        ;获得Flink
        mov eax,[eax+14h]
        ;获得LDR_DATA_TABLE_
        sub eax,8
        ;获得本模块的基址
        mov eax,[eax+18h]
        ;获得差值
        mov esi,eax
       ;成功可是why???
        add esi,10000h;
        ;将差值压入堆栈
        push ebx
        ;保存本模块的基址
        mov ebp,eax
        ;获得本模块的PE头部
        mov eax,[eax+3ch]
        ;获得重定位表偏移
        mov eax,[eax+ebp+0a0h]
        ;获得重定位表的地址
        add eax,ebp;    
ModifyReloc:
        
        ;进行修改,首先获得VirtualAddress
        mov ebx,[eax];
        ;获得重定位的数量
        mov ecx,[eax+4];
        sub ecx,8
        shr ecx,1
        ;到此已经得到了需要重定位数组数量
        add eax,8;现在eax指向数组
ActualModify:
        xor edx,edx
        mov dx,[eax];
        ;取后12位
        and dx,0fffh;
        ;VirtuallAddress+后12为获得完整的RVA
        add edx,ebx;//edx指向了RVA
        ;指向需要修改数据的地址
        add edx,ebp;
        ;将ECX进行暂存
        push ecx
        ;取得数据进行修改
        mov ecx,[edx]
        ;比较看看是否是应用于对齐的
        cmp ecx,0        
        jz  jiance    
        //ecx取得后面4位
        and ecx,0ffffh;
        ;esi获得高四位
        and esi,0ffff0000h
        add ecx,esi                
        mov [edx],ecx
        
jiance:
        ;ecx弹出堆栈
        pop ecx
        ;获得下一个需要重定位的数据地址
        add eax,2
        loop ActualModify;
        mov edx,[eax]
        cmp edx,0
        jnz ModifyReloc


        




        
        pop ebp
        popfd;
        popad;
    }
shellEnd:
    char* strShellCode=NULL;
    DWORD dwShellSize=0;
    _asm
    {
        lea eax,ShellCode;
        lea ebx,shellEnd;
        sub ebx,eax
        mov dwShellSize,ebx;
        mov strShellCode,eax;
    }
    //将文件指针移动到区块信息开始的地方
    SetFilePointer(hFile,pDosHeader->e_lfanew+4+sizeof(IMAGE_FILE_HEADER)+pNtHeader->FileHeader.SizeOfOptionalHeader,0,FILE_BEGIN);
    IMAGE_SECTION_HEADER LastSection={0};
    for(int i=0;i<pNtHeader->FileHeader.NumberOfSections;i++)
    {
        ReadFile(hFile,&LastSection,sizeof(IMAGE_SECTION_HEADER),&dwTmp,0);
        char* strSectionName=(char*)LastSection.Name;
        
        cout<<strSectionName<<endl;
    }
    IMAGE_SECTION_HEADER ShellSection={0};
    //记录下原来的入口点
    DWORD dwOldOEP=pNtHeader->OptionalHeader.AddressOfEntryPoint;
    //获得文件对齐值,和内存对齐值
    DWORD dwFileAlign=pNtHeader->OptionalHeader.FileAlignment;
    DWORD dwSectionAlign=pNtHeader->OptionalHeader.SectionAlignment;
    //设置区块名称
    strcpy((char*)ShellSection.Name,".try");
    //设置区块属性
    ShellSection.Characteristics=IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE|IMAGE_SCN_MEM_EXECUTE;
    //设置区块真实大小
    ShellSection.Misc.VirtualSize=dwShellSize;
    //设置区块的PointerToRawData
    ShellSection.PointerToRawData=LastSection.PointerToRawData+LastSection.SizeOfRawData;
    //设置区块的sizeOfRawData
    ShellSection.SizeOfRawData=GetAlign(dwShellSize,dwFileAlign);
    //设置区块的VirtualAddress
    ShellSection.VirtualAddress=LastSection.VirtualAddress+GetAlign(LastSection.Misc.VirtualSize,dwSectionAlign);
    //文件头部区块数目加1
    pNtHeader->FileHeader.NumberOfSections++;
    //可选头部入口点改变
    pNtHeader->OptionalHeader.AddressOfEntryPoint=ShellSection.VirtualAddress;
    //文件镜像和代码大小改变
    pNtHeader->OptionalHeader.SizeOfImage+=GetAlign(ShellSection.Misc.VirtualSize,dwSectionAlign);
    pNtHeader->OptionalHeader.SizeOfCode+=GetAlign(ShellSection.Misc.VirtualSize,dwSectionAlign);
    //写入新区块信息
    WriteFile(hFile,&ShellSection,sizeof(IMAGE_SECTION_HEADER),&dwTmp,0);
    //写入shellcode
    SetFilePointer(hFile,ShellSection.PointerToRawData,0,FILE_BEGIN);
    WriteFile(hFile,strShellCode,dwShellSize,&dwTmp,0);
    //写入JMP指令
    BYTE jmp=0xe9;
    WriteFile(hFile,&jmp,1,&dwTmp,0);
    dwOldOEP=dwOldOEP-(ShellSection.VirtualAddress+dwShellSize)-5;
    WriteFile(hFile,&dwOldOEP,4,&dwTmp,0);
    ::UnmapViewOfFile(lpImageBase);
    CloseHandle(hMap);
    CloseHandle(hFile);        
}
int _tmain(int argc, _TCHAR* argv[])
{
    EncryptText(strSrcExePath);
    return 1;
}

PS:

现在简述下自己程序的流程:

1.首先检测是否是PE文件,不是则退出

2.检测区段信息,如果是text,则进行与1异或的加密操作

3.增加新的区块,并根据上一区块的信息填写本区段的相关内容

4.加密完成(注意的是因为要进行解密操作,所以各区段的属性应该是可写的,要记得这点)

现在看下解密过程

1.在shellcode中根据区段名称获得区段的地址,于是在text中进行与1的异或操作进行解密,同时修改了源exe文件

2.要注意重定位的操作,现在的问题是,为什么在取得进程基地址的高四位和要进行转换的数据指针的后四位,便完成了重定位,而且是正确的

现在的问题是:

如何在内存中展开而不影响磁盘中的文件?????

原创粉丝点击