📄 unupxshell.cpp
字号:
// UpxDecode.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <windows.h>
#include <winnt.h>
#define SIZE_OF_NT_SIGNATURE sizeof( DWORD ) // PE标志大小
#define NTSIGNATURE(a) (( LPVOID )(( BYTE* )a + \
(( PIMAGE_DOS_HEADER )a )->e_lfanew )) //定位到PE文件头部
#define PEFHDROFFSET(a) (( LPVOID )(( BYTE* )a + \
(( PIMAGE_DOS_HEADER )a )->e_lfanew + SIZE_OF_NT_SIGNATURE )) //定位到IMAGE_FILE_HEADER结构处
#define OPTHDROFFSET(a) (( LPVOID )(( BYTE* )a + \
(( PIMAGE_DOS_HEADER )a )->e_lfanew + SIZE_OF_NT_SIGNATURE + sizeof( IMAGE_FILE_HEADER ))) //定位到IMAGE_OPTIONAL_HEADER32结构处
#define SECHDROFFSET(a) (( LPVOID )(( BYTE* )a + \
(( PIMAGE_DOS_HEADER )a )->e_lfanew + SIZE_OF_NT_SIGNATURE + sizeof( IMAGE_FILE_HEADER ) + sizeof ( IMAGE_OPTIONAL_HEADER ))) //定位到节表
//判断可执行文件是否是PE文件
DWORD WINAPI ImageFileType( LPVOID lpFile )
{
//首先出现的是DOS文件标志
if( *( USHORT* )lpFile == IMAGE_DOS_SIGNATURE )
{
//由DOS头部决定PE文件头部的位置
if( LOWORD( *( DWORD* )NTSIGNATURE( lpFile ) ) == \
IMAGE_OS2_SIGNATURE ||
LOWORD( *( DWORD* )NTSIGNATURE( lpFile ) ) == \
IMAGE_OS2_SIGNATURE_LE )
return ( DWORD )LOWORD( *( DWORD * )NTSIGNATURE( lpFile ) );
else if ( *( DWORD * )NTSIGNATURE( lpFile ) ==
IMAGE_NT_SIGNATURE )
return IMAGE_NT_SIGNATURE;
else
return IMAGE_DOS_SIGNATURE;
}
else
//不明文件种类
return 0;
}
//得到含有节的数目
int WINAPI NumberOfSections( LPVOID lpFile )
{
return ( int )(( PIMAGE_FILE_HEADER )PEFHDROFFSET( lpFile ))->NumberOfSections;
}
//获得可执行映像的入口点
LPVOID WINAPI GetModuleEntryPoint( LPVOID lpFile )
{
PIMAGE_OPTIONAL_HEADER pEntry;
pEntry = ( PIMAGE_OPTIONAL_HEADER )OPTHDROFFSET( lpFile );
if( pEntry != NULL )
return ( LPVOID )pEntry->AddressOfEntryPoint;
else
return NULL;
}
//通过段名称来获得段头部信息
BOOL WINAPI GetSectionHdrByName( LPVOID lpFile, IMAGE_SECTION_HEADER *pSec, char *szSecName )
{
PIMAGE_SECTION_HEADER pShr;
int nSections = NumberOfSections( lpFile );
int i;
if( ( pShr = ( PIMAGE_SECTION_HEADER )SECHDROFFSET( lpFile )) != NULL )
{
//由名称查找
for( i = 0; i < nSections; i ++ )
{
if( !strcmp( ( char* )pShr->Name, szSecName ))
{
CopyMemory( ( LPVOID )pSec, ( LPVOID )pShr, sizeof( IMAGE_SECTION_HEADER ));
return TRUE;
}
else
pShr++;
}
}
return FALSE;
}
//定位数据目录
LPVOID WINAPI ImageDirectoryOffset( LPVOID lpFile, DWORD dwImage_Directory )
{
PIMAGE_OPTIONAL_HEADER poh;
PIMAGE_SECTION_HEADER psh;
int nSections = NumberOfSections( lpFile );
int i = 0;
LPVOID VAImageDir;
//获得可选头部和段头部的偏移量
poh = ( PIMAGE_OPTIONAL_HEADER )OPTHDROFFSET( lpFile );
psh = ( PIMAGE_SECTION_HEADER )SECHDROFFSET( lpFile );
//必须在0和NumberOfRawAndSizes-1之间
if( dwImage_Directory >= poh->NumberOfRvaAndSizes )
return NULL;
//定位映像目录的相对虚拟地址
VAImageDir = ( LPVOID )poh->DataDirectory[dwImage_Directory].VirtualAddress;
//定位包含映像目录的段
while( i++ < nSections )
{
if( psh->VirtualAddress <= ( DWORD )VAImageDir && \
psh->VirtualAddress + psh->SizeOfRawData > ( DWORD )VAImageDir )
break;
psh++;
}
if( i > nSections )
return NULL;
//返回映像导入目录的文件偏移量
return ( LPVOID )( ( int )lpFile + ( int )VAImageDir - ( int )psh->VirtualAddress + ( int )psh->PointerToRawData );
}
//
//将虚拟地址转换成文件地址
LPVOID WINAPI RVAToOffset( LPVOID lpFile, DWORD dwRVA )
{
PIMAGE_OPTIONAL_HEADER poh;
PIMAGE_SECTION_HEADER psh;
int nSections = NumberOfSections( lpFile );
int i = 0;
poh = ( PIMAGE_OPTIONAL_HEADER )OPTHDROFFSET( lpFile );
psh = ( PIMAGE_SECTION_HEADER )SECHDROFFSET( lpFile );
while( i++ < nSections )
{
if( psh->VirtualAddress <= dwRVA && \
psh->VirtualAddress + psh->SizeOfRawData > dwRVA )
break;
psh++;
}
if( i > nSections )
return NULL;
//返回文件偏移量
return ( LPVOID )(( int )lpFile + ( int )dwRVA - ( int )psh->VirtualAddress + ( int )psh->PointerToRawData );
}
//得到虚拟地址所在节的名称
PCHAR WINAPI GetRVASectionName( LPVOID lpFile, DWORD dwRVA )
{
PIMAGE_SECTION_HEADER psh;
int nSections = NumberOfSections( lpFile );
int i = 0;
psh = ( PIMAGE_SECTION_HEADER )SECHDROFFSET( lpFile );
while( i++ < nSections )
{
if( psh->VirtualAddress <= dwRVA && \
psh->VirtualAddress + psh->SizeOfRawData > dwRVA )
break;
psh++;
}
if( i > nSections )
return NULL;
return ( PCHAR )(( int )lpFile + ( int )psh->PointerToRawData );
}
//解密函数
void UnUpxShell( LPVOID lpFile ,const char *lpFileName )
{
PIMAGE_SECTION_HEADER psh;
BYTE *pEntry;
DWORD dwFileSize, dwDLL, dwEDI, dwEntry, dwPackEntry, dwImageBase, dwESI, dwCount = 0;
DWORD dwVoffset_UPX0, dwOffset, dwSize, dwEAX, dwEncryptData;
FILE *fp;
BYTE *lpSection, *lpOldSection, *lpImageOfUpx1, *lpAsciiName;
DWORD *lpIID;
DWORD dwEBX;
long dwVOffset;
dwImageBase = (( PIMAGE_OPTIONAL_HEADER )OPTHDROFFSET( lpFile ))->ImageBase;
dwFileSize = 0;
psh = ( PIMAGE_SECTION_HEADER )SECHDROFFSET( lpFile );
for( int i = 0; i < NumberOfSections( lpFile ); i++ )
dwFileSize += ( psh + i )->Misc.VirtualSize; //获得所有节的内存映像大小
dwVOffset = dwFileSize - ( psh + NumberOfSections( lpFile ) - 1 )->Misc.VirtualSize;
lpOldSection = ( BYTE* )calloc( dwFileSize + 4096 * 2, 1 );
dwVOffset = ( DWORD )lpOldSection + dwVOffset;//最后一个节的在内存中的位置
dwVOffset = ( psh + NumberOfSections( lpFile ) - 1 )->VirtualAddress - dwVOffset;
lpSection = lpOldSection;
for( int i = 1; i< NumberOfSections( lpFile ); i++ )//模拟PEloader装载各个节
{
lpSection += ( psh + i - 1 )->Misc.VirtualSize;
dwOffset = ( psh + i )->PointerToRawData;
dwSize = ( psh + i )->SizeOfRawData;
memcpy( ( PVOID )lpSection, ( PVOID )(( int )lpFile + ( int )dwOffset ), dwSize );
}
dwVoffset_UPX0 = psh->VirtualAddress; //UPX0在内存中的偏移
lpImageOfUpx1 = ( BYTE* )(( int )lpOldSection + ( int )psh->Misc.VirtualSize );//指向upx1的内存映象
dwPackEntry = ( DWORD )GetModuleEntryPoint( lpFile );
pEntry = ( BYTE* )RVAToOffset( lpFile, dwPackEntry );
dwVoffset_UPX0 += dwImageBase;
dwEncryptData = *(( DWORD* )( pEntry + 2 ));//加密数据的内存地址
dwEncryptData -= dwImageBase;
dwEncryptData -= ( psh + 1 )->VirtualAddress;
dwEncryptData += ( DWORD )lpImageOfUpx1;
for( int i = 0; i < 400; i++ )
{
if( *( DWORD* )( pEntry + i ) == 0xbe8dd9e2 )
break;
}
if( i >= 400 )
{
printf( "Unpack failed: LOOP/LEA not found\n" );
goto end;
}
dwEDI = *( DWORD* )( pEntry + i + 4 ); //dwEDI + esi
*( pEntry + i + 2 ) = 0xc3;//ret
for( ; i < 400; i++ )
{
if( *( DWORD* )( pEntry + i ) == 0x30848d04 )
break;
}
if( i >= 400 )
{
printf( "Unpack failed: MOV/LEA not found\n" );
goto end;
}
dwDLL = *( DWORD* )( pEntry + i + 4 );
for( ; i < 400; i++ )
{
if( *( WORD* )( pEntry + i ) == 0xe961 )
break;
}
if( i > 400 )
{
printf( " Unpack failed : POPAD/JMP not found\n" );
goto end;
}
dwEntry = *( DWORD* )( pEntry + i + 2 ); //跳转的位移量
dwPackEntry = dwPackEntry + i + 1;
dwEntry = dwEntry + dwPackEntry + 5;
//开始解码
pEntry += 12;
_asm
{
pushad
push ebp
mov esi,dwEncryptData
mov edi,lpOldSection
call pEntry
pop ebp
mov edi,dwEDI
add edi,esi
mov dwEDI,edi
mov dwESI,esi
popad
}
lpAsciiName = lpOldSection + dwFileSize + 1024;
lpIID = ( PDWORD )( lpOldSection + dwFileSize );
for( ; ( dwEAX = *(( PDWORD )dwEDI )) != 0; )
{
dwCount++;
dwEBX = *(( PDWORD )dwEDI + 1 );
dwEAX = dwESI + dwEAX + dwDLL;//指向dll名字字符串
( ( int )lpAsciiName % 2 ) != 0 ? lpAsciiName++ : lpAsciiName;//函数名或dll名开始地址按偶数对齐
memcpy( lpAsciiName, ( PVOID )dwEAX, strlen( ( PCHAR )dwEAX ));
*( lpIID + 3 ) = ( DWORD )lpAsciiName + dwVOffset;
lpAsciiName += strlen( ( PCHAR )lpAsciiName );
lpAsciiName++;
dwEBX += dwESI;
*( lpIID + 4 ) = dwEBX + dwVOffset;//IAT
*( lpIID ) = dwEBX + dwVOffset;//目前先和FirstThunk指向同一数组
lpIID += 5;
dwEDI += 8;
while( *(( PBYTE )dwEDI++ ) != 0 )
{
//char c1 = *(( PBYTE )dwEDI );
char c2 = *(( PBYTE )dwEDI + 1 );
if( !isprint( c2 ) )//可能会出现问题
{
*(( PDWORD )dwEBX ) = IMAGE_ORDINAL_FLAG32 | *(( PWORD )dwEDI );
dwEBX += 4;
dwEDI += 2;
continue;
}
( ( int )lpAsciiName % 2 ) != 0 ? lpAsciiName++ : lpAsciiName;//函数名或dll地址按偶数对齐
*(( PDWORD )dwEBX ) = ( DWORD )lpAsciiName + dwVOffset;
dwEBX += 4;
lpAsciiName += 2;//留两个字节放函数序号
memcpy( lpAsciiName, ( LPVOID )dwEDI,strlen( ( PCHAR )dwEDI ));
lpAsciiName += strlen( ( PCHAR )dwEDI );
lpAsciiName++;
dwEDI += strlen( ( PCHAR )dwEDI );
dwEDI++;
}
}
//PE头中需要修改的字段
(( PIMAGE_OPTIONAL_HEADER )OPTHDROFFSET( lpFile ))->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = \
( DWORD )( lpOldSection + dwFileSize + dwVOffset );
(( PIMAGE_OPTIONAL_HEADER )OPTHDROFFSET( lpFile ))->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = \
dwCount * 20;
(( PIMAGE_OPTIONAL_HEADER )OPTHDROFFSET( lpFile ))->AddressOfEntryPoint = dwEntry;
(( PIMAGE_OPTIONAL_HEADER )OPTHDROFFSET( lpFile ))->SizeOfImage += ( 4096 * 2 );
//节表中需要修改的字段
for( int i = 0; i < NumberOfSections( lpFile ) - 1; i++ )
{
( psh + i )->SizeOfRawData = ( psh + i )->Misc.VirtualSize;
( psh + i )->PointerToRawData = ( psh + i )->VirtualAddress;
}
//最后一个节的节头需要特殊处理
( psh + i )->Misc.VirtualSize += ( 4096 *2 );
( psh + i )->SizeOfRawData = ( psh + i )->Misc.VirtualSize;
( psh + i )->PointerToRawData = ( psh + i )->VirtualAddress;
strcpy( ( char* )( psh + i )->Name," KV " );
fp = fopen( lpFileName, "wb" );
fwrite( lpFile, 1, (( PIMAGE_OPTIONAL_HEADER )OPTHDROFFSET( lpFile ))->SizeOfHeaders, fp );
fwrite( lpOldSection, 1, dwFileSize + 4096 * 2, fp );
fclose( fp );
end:
free( lpFile );
free( lpOldSection );
}
int _tmain(int argc, _TCHAR* argv[])
{
FILE *fp;
DWORD dwFileType;
LONG dwPos;
LPVOID pHead;
BYTE *pCheckUpx;
if( argc != 3 ){
printf( "Usage: UpxDecode FileUpx Unpack\n" );
exit( EXIT_FAILURE );
}
fp = fopen( argv[1], "rb" );
if( fp == NULL )
{
perror( argv[1] );
exit( EXIT_FAILURE );
}
fseek( fp, 0L, SEEK_END );
dwPos = ftell( fp );
fseek( fp, 0L, SEEK_SET );
pHead = malloc( dwPos );
fread( pHead, 1, dwPos , fp );
fclose( fp );
//判断是否是PE文件
dwFileType = ImageFileType( pHead );
if( dwFileType != IMAGE_NT_SIGNATURE )
{
printf( "Not PE file!\n" );
exit( EXIT_FAILURE );
}
//根据字节流判断是否被upx压缩过
pCheckUpx = ( BYTE* )GetModuleEntryPoint( pHead );
pCheckUpx =( BYTE* ) RVAToOffset( pHead, ( DWORD )pCheckUpx );
pCheckUpx++; //跳过pushad
if( *pCheckUpx != 0xbe || *( pCheckUpx + 5 ) != 0x8d || \
*( pCheckUpx + 11 ) != 0x57 || *( PDWORD )( pCheckUpx + 12 ) != 0xebffcd83 )
{
printf( "This file not packed by UPX!\n" );
exit( EXIT_FAILURE );
}
UnUpxShell( pHead, argv[2] );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -