软件加壳之输入表转储

// EncrpyImport.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include<Windows.h>
#include<iostream>
#include<fstream>
#include<ImageHlp.h>
using namespace std;
#pragma comment(lib,"imagehlp.lib")
void ReaseImportDir(char*srcPath);
DWORD GetAlign(DWORD size,DWORD align)
{
    DWORD dwResult=0;
    if(size<align)
        return align;
    if(size%align)
    {
        dwResult=(size/align+1)*align;
    }
    else
    {
        dwResult=(size/align)*align;
    }
}
void StorageImportDir(char* srcPath)
{
    HANDLE hFile=CreateFileA(srcPath,GENERIC_ALL,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    if(hFile==INVALID_HANDLE_VALUE)
    {
        cout<<"打开文件失败\n";
        return ;
    }
    DWORD dwFileSize=GetFileSize(hFile,NULL);
    HANDLE hMap=CreateFileMappingA(hFile,NULL,PAGE_EXECUTE_READWRITE,0,dwFileSize+4096,NULL);
    if(INVALID_HANDLE_VALUE==hMap)
    {
        cout<<"对不起,创建文件镜像失败\n";
         return;
    }
    LPVOID lpImageBase=(LPVOID)MapViewOfFile(hMap,FILE_MAP_READ|FILE_MAP_WRITE,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)pDosHeader+pDosHeader->e_lfanew);
    if(pNtHeader->Signature!=IMAGE_NT_SIGNATURE)
    {
        cout<<"对不起,非PE文件\n";
        return;
    }
    //获得可选头部地址
    DWORD dwAddrOfOptionalHeader=(DWORD)&(pNtHeader->OptionalHeader);
    //获得区块数目
    DWORD dwSectionNum=pNtHeader->FileHeader.NumberOfSections;
    //获得原来的入口点
    DWORD dwOldOEP=pNtHeader->OptionalHeader.AddressOfEntryPoint;
    //获得文件对齐值
    DWORD dwFileAlign=pNtHeader->OptionalHeader.FileAlignment;
    //获得内存对齐值
    DWORD dwSectionAlign=pNtHeader->OptionalHeader.SectionAlignment;
    goto shellEnd;
    _asm
    {
shellCode:
        pushad
        pushfd
        push ebp;
        ;获得PEB地址
        mov eax,fs:[30h]
        ;获得LDR地址
        mov  eax,[eax+0ch];
        ;获得Flink的地址
        mov  eax,[eax+14h];
        ;保存Flink
        mov ecx,eax
        sub  eax,8
        ;入口点,第一个LDR_DATA_TABLE_ENTRY就是程序自身
        mov edx,[eax+18h]
        mov eax,[eax+1ch]

        add eax,10
        ;将起始处的dll名称字符串地址即eax压入堆栈
        push eax
        mov eax,ecx
searchDll:
        ;保存当前Flink
        mov  ecx,eax
        ;获得LDR_DATA_TABLE_ENTRY地址
        sub  eax,8;        
        ;获得模块的dllBase
        mov  eax,[eax+18h]        
        ;保存基址
        mov ebp,eax
        ;获得PE头部
        mov eax,[eax+3ch]
        ;获得导出表的偏移量
        mov eax,[ebp+eax+78h]
        ;获得导出表地址
        add eax,ebp
        ;保存导出表的地址
        mov edx,eax
        ;模块名称的偏移量
        mov ebx,[edx+0ch]
        ;恢复eax中的Flink
        mov eax,ecx
        ;获得下一个Flink
        mov eax,[eax]        
        ;获得dllname地址
        add ebx,ebp
        mov ecx,4e52454Bh  //;'NREK'
        cmp [ebx],ecx
        jnz searchDll
        mov ecx,32334C45h;'23EL'
        cmp [ebx+4],ecx;
        jnz searchDll
        mov ecx,6c6c642eh;'lld.'
        cmp [ebx+8],ecx
        jnz searchDll;
        ;到此,ebp是Kernel32的基址,edx则是导出表的地址
        ;获得导出表函数名称数组偏移量
        mov ebx,[edx+20h]
        ;获得导出表输出函数名称地址
        add ebx,ebp
        ;获得名称函数个数
        mov ecx,[edx+18h]                
searchLoadLibraryA:
        dec ecx;
        ;倒序得出输出函数的函数名
        mov esi,[ebx+ecx*4]
        add esi,ebp
        mov eax,64616f4ch;
        cmp [esi],eax
        jnz searchLoadLibraryA;
        mov eax,7262694ch
        cmp [esi+4],eax
        jnz searchLoadLibraryA
        mov eax,41797261h
        cmp [esi+8],eax
        jnz searchLoadLibraryA;    
        ;找到了函数名称,ecx即是函数名称数组中的索引,edx是导出表地址
        ;找到输出表中的函数序号数组地址偏移量
        mov ebx,[edx+24h]
        ;获得函数序号数组地址
        add ebx,ebp;
        mov cx,[ebx+ecx*2]
        ;找到输出表函数地址偏移
        mov ebx,[edx+1ch]
        ;找到输出表函数数组地址
        add ebx,ebp
        mov eax,[ebx+ecx*4]
        ;获得函数地址
        add eax,ebp
        ;找到了LoadLibraryA函数
        ;保存LoadLibraryA函数地址,之前已经压入了模块第一个dll字符串的地址
        push eax
        ;获得PE头部
        MOV eax,ebp
        mov eax,[eax+3ch]
        ;获得导出表的偏移量
        mov eax,[ebp+eax+78h]
        ;获得导出表地址
        add eax,ebp
        ;保存导出表的地址
        mov edx,eax
        //接下来寻找GetProcAddress函数地址        
        ;获得输出函数名称数组地址偏移量
        mov ebx,[edx+20h];
        ;获得输出函数名称数组地址
        add ebx,ebp
        ;获得输出函数个数
        mov ecx,[edx+18h]
searchGetProc:
        dec ecx
        mov esi,[ebx+ecx*4]
        add esi,ebp
        mov eax,50746547h
        cmp eax,[esi]
        jnz searchGetProc
        mov eax,41636f72h
        cmp eax,[esi+4]
        jnz searchGetProc
        mov eax,65726464h
        cmp eax,[esi+8]
        jnz searchGetProc;
        ;到此已经获得GetProcAddress的ecx值
        ;获得函数序号数组的偏移量
        mov ebx,[edx+24h]
        ;获得函数虚函数组地址
        add ebx,ebp
        mov cx,[ebx+ecx*2]
        ;获得输出函数地址数组偏移地址
        mov ebx,[edx+1ch]
        ;获得输出函数数组的地址
        add ebx,ebp;        
        ;获得GetProcAddress
        mov ebx,[ebx+ecx*4]
        add ebx,ebp;
        ;将GetProcAddress地址压入堆栈,之前已经压入了字符串地址,和LoadLibraryA的地址
        push ebx;
        pop  ebp;是GetProcAddress函数地址
        pop  ebx;是LoadLibraryA函数地址
        pop  edx;是第一个模块字符串地址,堆栈清空

        ;到这里开始获得输入表中的每一个函数地址
        mov esi,edx;保存模块字符串地址
        ;字符串首地址-5是FirstThunk的地址
        sub edx,5
        ;将LoadLibraryA函数地址压入堆栈
        push ebx
CallLoadLibrary:
        ;获得LoadLibraryA函数地址
        pop ebx


        ;保存FirstThunk的地址对战中只有一项
        push edx
        ;调用LoadLibraryA函数
        push esi
        call ebx;调用LoadLibraryA的地址
        ;保存当前句柄,到此堆栈中只有FirstThunk
        push eax
        ;获得该模块下的输出函数,现有两个数据了,堆栈中
CalcStrLen:
        ;使得ESI指向输出函数个数
        inc esi
        cmp byte ptr[esi],0
        jnz CalcStrLen;
        inc esi
        ;获得该模块的输出函数个数
        mov ecx,[esi]
        ;esi指向函数名称
        add esi,5


        ;弹出dll的基址到edi中
        pop edi
        ;弹出FirstThunk到edx当中
        ;到此堆栈已空
        pop edx    

        ;ebx中是LoadLibraryA函数地址,暂存,ebx作为他用
        push ebx
        ;ebx作为索引
        xor ebx,ebx
CallGetProcAddress:
        
        push edx;保存FirstThunk地址
        ;ecx保存暂作他用,执行GetProcAddress会影响ecx
        push ecx
        ;到此对战中有了三项LoadLibraryA,FirstThunk,函数个数
        
        push esi;
        push edi
        call ebp;调用GetProcAddress
        ;FirstThunk的地址
        ;重新获得函数个数
        pop  ecx
        ;获得FirstThunk地址
        pop edx


        ;拼凑FirstThunk的地址,到此堆栈中只有LoadLibraryA的地址
        push edx
        push ebx
        push esi
        push ecx

        ;获得PEB地址
        mov esi,fs:[30h]
        ;获得LDR地址
        mov  esi,[esi+0ch];
        ;获得Flink的地址
        mov  esi,[esi+14h];        
        sub  esi,8
        ;入口点,第一个LDR_DATA_TABLE_ENTRY就是程序自身
        mov esi,[esi+1ch]
        ;基址+1获得偏移量
        add esi,1
        mov ecx,[esi]
        sub esi,1
        add esi,ecx;
        add esi,5
        ;开始检测是否是0
Exam0:
        inc esi
        mov ecx,[esi]
        cmp ecx,0
        jnz Exam0;
        mov ecx,[esi+4]
        cmp ecx,0
        jnz Exam0;
        sub esi,4;
        mov ecx,[esi];获得偏移量
        ;获得偏移量的补码
        mov edx,[edx]
        not ecx
        sub esi,ecx
        
        and esi,0ffff0000h
        
        and edx,0ffffh
        add edx,esi
        mov [edx+ebx*4],eax
        
        /*
        
        
        add esi,3
        */
        pop ecx        
        pop esi
        pop ebx
        pop edx        
        ;到此堆栈中还是只有LoadLibraryA
GetNextProcName:
        ;esi跳过函数名称字符串
        inc esi
        cmp byte ptr[esi],0
        jnz GetNextProcName
        inc esi
        inc ebx
        loop CallGetProcAddress
        ;检测是否结束
        mov ecx,[esi]
        cmp ecx,0
        ;输入表便利结束
        jz  End;
        ;开始获得下一个FirstThunk,edx指向FirstThunk,esi指向dll字符串名称
        mov edx,esi
        add esi,5        
        ;esi指向dll名称
jmp        CallLoadLibrary
End:
        pop edx;平衡堆栈
        ;将LoadLibraryA弹出堆栈
        pop  ebp
        popfd
        popad
mpl:

        
    }    
shellEnd:
    char*pShellCode=NULL;
    DWORD dwShellLen=0;
    _asm
    {
        lea eax,shellCode
        lea ebx,shellEnd;
        sub ebx,eax
        mov dwShellLen,ebx
        mov pShellCode,eax

    }
    
    //获得新的输入表
    PIMAGE_IMPORT_DESCRIPTOR pImportDir=(PIMAGE_IMPORT_DESCRIPTOR)ImageRvaToVa(pNtHeader,lpImageBase,pNtHeader->OptionalHeader.DataDirectory[1].VirtualAddress,                                           NULL);
    BYTE fill=0;
    DWORD dwWriteLen=0;
    //存放变形的输入表
    char* strImportDir=new char[4096];
    char* pCurrent=strImportDir;
    memset(strImportDir,0,4096);
    while(pImportDir->Name!=NULL)
    {
        PIMAGE_THUNK_DATA pThunkData=(PIMAGE_THUNK_DATA)ImageRvaToVa(pNtHeader,lpImageBase,pImportDir->OriginalFirstThunk,NULL);
        //保存FirstThunk的RVA
        DWORD FirstThunk=pImportDir->FirstThunk;
        memcpy(strImportDir,&FirstThunk,4);        
        dwWriteLen+=sizeof(DWORD);
        strImportDir+=sizeof(DWORD);
        //写入0,标志FirstThunk结束
        memcpy(strImportDir,&fill,1);
        dwWriteLen+=1;
        strImportDir+=1;
        //获得dll名称
        char* strDllName=(char*)ImageRvaToVa(pNtHeader,lpImageBase,pImportDir->Name,NULL);
        int nDllNameLen=strlen(strDllName);
        //写入dll名称    
        memcpy(strImportDir,strDllName,nDllNameLen);
        strImportDir+=nDllNameLen;            
        dwWriteLen+=nDllNameLen;
        //写入0        
        memcpy(strImportDir,&fill,1);
        dwWriteLen+=1;
        strImportDir+=1;
        //获得输入函数个数
        int nImportFuncNum=0;
        PIMAGE_THUNK_DATA pThunkData2=pThunkData;
        while(pThunkData->u1.AddressOfData!=NULL)
        {
            PIMAGE_IMPORT_BY_NAME pImportByName=(PIMAGE_IMPORT_BY_NAME)ImageRvaToVa(pNtHeader,lpImageBase,pThunkData->u1.AddressOfData,NULL);
            pThunkData++;        
            nImportFuncNum++;
        }
        //写入输入函数个数        
        memcpy(strImportDir,&nImportFuncNum,4);
        strImportDir+=sizeof(int);
        dwWriteLen+=sizeof(int);
        //填充0
        memcpy(strImportDir,&fill,1);        
        dwWriteLen+=1;
        strImportDir+=1;
        while(pThunkData2->u1.AddressOfData!=NULL)
        {            
            PIMAGE_IMPORT_BY_NAME pImportByName=(PIMAGE_IMPORT_BY_NAME)ImageRvaToVa(pNtHeader,lpImageBase,pThunkData2->u1.AddressOfData,NULL);            
            char* strFuncName=(char*)pImportByName->Name;
            int nFuncNameLen=strlen(strFuncName);
            //写入输入函数的名称            
            memcpy(strImportDir,strFuncName,nFuncNameLen);
            strImportDir+=nFuncNameLen;
            dwWriteLen+=nFuncNameLen;
            //填充0
            memcpy(strImportDir,&fill,1);
            dwWriteLen+=1;    
            strImportDir+=1;
            pThunkData2++;    
        }
        pImportDir++;
    }
    //填充4个0
    memcpy(strImportDir,&fill,1);
    dwWriteLen+=1;    
    strImportDir+=1;
    memcpy(strImportDir,&fill,1);
    dwWriteLen+=1;    
    strImportDir+=1;
    memcpy(strImportDir,&fill,1);
    dwWriteLen+=1;    
    strImportDir+=1;
    memcpy(strImportDir,&fill,1);
    dwWriteLen+=1;    
    strImportDir+=1;
    DWORD dwTmp=0;    
    SetFilePointer(hFile,pDosHeader->e_lfanew+sizeof(IMAGE_FILE_HEADER)+4+pNtHeader->FileHeader.SizeOfOptionalHeader,0,FILE_BEGIN);
    IMAGE_SECTION_HEADER SectionTmp={0};
    //获得最后一个区块的信息
    for(int i=0;i<dwSectionNum;i++)
    {
        ReadFile(hFile,&SectionTmp,sizeof(IMAGE_SECTION_HEADER),&dwTmp,0);
    }
    IMAGE_SECTION_HEADER shellSection={0};    
    //修改区块属性
    shellSection.Characteristics=IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_WRITE;
    //填充区块的真实大小
    dwShellLen+=dwWriteLen;
    shellSection.Misc.VirtualSize=dwShellLen;
    //填充区块的PointerToRawData
    shellSection.PointerToRawData=SectionTmp.PointerToRawData+SectionTmp.SizeOfRawData;
    //填充区块的SizeOfRawData
    shellSection.SizeOfRawData=GetAlign(dwShellLen,dwFileAlign);
    //填充新区块的VirtualAddress
    shellSection.VirtualAddress=SectionTmp.VirtualAddress+GetAlign(SectionTmp.Misc.VirtualSize,dwSectionAlign);
    //区块数目加1
    pNtHeader->FileHeader.NumberOfSections++;
    //新区块的名称
    strncpy((char*)shellSection.Name,".hehe",5);
    //修改程序入口点
    pNtHeader->OptionalHeader.AddressOfEntryPoint=shellSection.VirtualAddress;
    //写入新的区块信息
    DWORD dwOptionalSize=pNtHeader->FileHeader.SizeOfOptionalHeader;
    //修改镜像大小
    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);    
    //移动文件指针    
    SetFilePointer(hFile,shellSection.PointerToRawData,0,FILE_BEGIN);    
    
    //因为该区段开始是变形的输入表,所以要跳转到相应的开始地址
    BYTE jmp=0xe9;
    WriteFile(hFile,&jmp,sizeof(BYTE),&dwTmp,0);
    WriteFile(hFile,&dwWriteLen,sizeof(DWORD),&dwTmp,0);
    WriteFile(hFile,pCurrent,dwWriteLen,&dwTmp,0);
    //移动文件指针    
    //写入shellcode
    dwShellLen-=dwWriteLen;
    WriteFile(hFile,pShellCode,dwShellLen,&dwTmp,0);
    //跳回元入口    
    WriteFile(hFile,&jmp,1,&dwTmp,0);
    //获得入口
    dwShellLen+=dwWriteLen+5;
    dwOldOEP=dwOldOEP-(shellSection.VirtualAddress+dwShellLen)-5;
    WriteFile(hFile,&dwOldOEP,4,&dwTmp,0);    
    ::UnmapViewOfFile(lpImageBase);
    CloseHandle(hMap);
    CloseHandle(hFile);
    ReaseImportDir(srcPath);
}

void ReaseImportDir(char*srcPath)
{
    HANDLE hFile=CreateFileA(srcPath,GENERIC_ALL,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    if(INVALID_HANDLE_VALUE==hFile)
    {
        cout<<"文件打开失败\n";
        return ;
    }
    DWORD dwFileSize=GetFileSize(hFile,NULL);
    HANDLE hMap=CreateFileMappingA(hFile,NULL,PAGE_EXECUTE_READWRITE,0,dwFileSize,NULL);
    if(INVALID_HANDLE_VALUE==hMap)
    {
        cout<<"创建文件映像失败\n";
        return ;
    }
    LPVOID lpImageBase=(LPVOID)MapViewOfFile(hMap,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
    //获得DOS文件头部
    PIMAGE_DOS_HEADER pDosHeader=(PIMAGE_DOS_HEADER)lpImageBase;
    if(pDosHeader->e_magic!=IMAGE_DOS_SIGNATURE)
    {
        cout<<"非PE文件\n";
        return;
    }
    //获得PE文件头部
    PIMAGE_NT_HEADERS pNtHeader=(PIMAGE_NT_HEADERS)((DWORD)pDosHeader+pDosHeader->e_lfanew);
    if(pNtHeader->Signature!=IMAGE_NT_SIGNATURE)
    {
        cout<<"非PE文件\n";
        return;
    }
    //获得可选头部地址
    DWORD dwAddrOfOptionalHeader=(DWORD)&(pNtHeader->OptionalHeader);
    //读出区块地址
    PIMAGE_SECTION_HEADER pSectionHeader=(PIMAGE_SECTION_HEADER)(dwAddrOfOptionalHeader+pNtHeader->FileHeader.SizeOfOptionalHeader);
    //读出区块数目
    DWORD dwSectionNum=pNtHeader->FileHeader.NumberOfSections;
    for(int i=0;i<dwSectionNum;i++)
    {
        cout<<pSectionHeader->Name<<endl;
        pSectionHeader++;
    }
    //读取输入表结构
    PIMAGE_IMPORT_DESCRIPTOR pImportDir=(PIMAGE_IMPORT_DESCRIPTOR)ImageRvaToVa(pNtHeader,lpImageBase,pNtHeader->OptionalHeader.DataDirectory[1].VirtualAddress,NULL);
    while(pImportDir->Name!=NULL)
    {
        char* strDllName=(char*)pImportDir->Name;
        PIMAGE_THUNK_DATA pThunkData=(PIMAGE_THUNK_DATA)ImageRvaToVa(pNtHeader,lpImageBase,pImportDir->OriginalFirstThunk,NULL);
        int nDllNameLen=strlen(strDllName);
        //dll文件名清除
        memset(strDllName,0,nDllNameLen);
        cout<<"当前模块是:"<<strDllName<<endl;
        while(pThunkData->u1.AddressOfData!=NULL)
        {
            PIMAGE_IMPORT_BY_NAME pImportByName=(PIMAGE_IMPORT_BY_NAME)ImageRvaToVa(pNtHeader,lpImageBase,pThunkData->u1.AddressOfData,NULL);
            char* strFuncName=(char*)pImportByName->Name;
            cout<<strFuncName<<endl;
            int nFuncNameLen=strlen(strFuncName);
            //输入函数名清除
            memset(strFuncName,0,nFuncNameLen);
            //IMAGE_THUNK_DATA清除
            memset(pThunkData,0,sizeof(DWORD));
            pThunkData++;
        }
        //IMAGE_IMPORT_DESCRIPTOR清除
        memset(pImportDir,0,sizeof(IMAGE_IMPORT_DESCRIPTOR));
        pImportDir++;
    }
    ::UnmapViewOfFile(lpImageBase);
    CloseHandle(hMap);
    CloseHandle(hFile);    
}
int _tmain(int argc, _TCHAR* argv[])
{
    StorageImportDir("D:\\project\\凯撒加密算法\\Debug\\凯撒加密算法.exe");
    ReaseImportDir("D:\\project\\凯撒加密算法\\Debug\\凯撒加密算法.exe");
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

世纪殇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值