// 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;
}
标签:eax,mov,转储,加壳,地址,软件,ebx,esi,ecx
From: https://blog.51cto.com/u_15995156/6166928