0
点赞
收藏
分享

微信扫一扫

软件加壳之输入表转储

// 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;

 }

举报

相关推荐

0 条评论