前言
对win32程序的图标资源解析并导出,具体操作如下.
效果展示
icons.exe target.exe
思维导图
代码实现思路
PE文件资源段的定位思路
资源段内容解析思路
ico文件格式拆解思路
https://www.moon-soft.com/program/FORMAT/windows/icons.htm
ps:ico文件格式可以拆成头部和身体两部分去理解,身体可以在资源段中获取,头部则可以由身体反推.
代码展示(缩进排版可见附件main.cpp源码)
#include <stdio.h>
#include <tchar.h>
#include<windows.h>
#include<malloc.h>
typedef struct{
BYTE bwidth; // width, in pixels, of the image
BYTE bheight; // height, in pixels, of the image
BYTE bcolorcount; // number of colors in image (0 if >=8bpp)
BYTE breserved; // reserved ( must be 0)
WORD wplanes; // color planes
WORD wbitcount; // bits per pixel
DWORD dwBYTEsinres; // how many BYTEs in this resource?
DWORD dwimageoffset; // where in the file is this image?
} icondirentry, *lpicondirentry;
typedef struct{
WORD idreserved; // reserved (must be 0)
WORD idtype; // resource type (1 for icons)
WORD idcount; // how many images?
icondirentry identries[1]; // an entry for each image (idcount of 'em)
} icondir, *lpicondir;
typedef struct{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
}iconbitmapinfoheader, *lpiconbitmapinfoheader;
typedef struct{
DWORD IconFOA;
DWORD IcoSize;
}mIcon,*lpmIcon;
//char* FileName:文件名
//ReadPEToMemory:读取文件,申请内存,拷贝内容
//LPBYTE p:返回指向了填充了文件数据的内存地址
LPBYTE ReadPEToMemory(char* FileName){
HANDLE hFile = CreateFileA(FileName,
GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwSize = GetFileSize(hFile, NULL);
LPBYTE p = new BYTE[dwSize]{};
ReadFile(hFile, p, dwSize, &dwSize, NULL);
return p;
}
//LPBYTE fp:指向指向了填充了文件数据的内存地址
//IsPE32:获取PE的NT头部,获取FileHeader.Machine|e_magic|Signature的内容,判断是否为PE32文件
//BOOL ret:TRUE则是PE32文件
BOOL IsPE32(LPBYTE fp){
BOOL ret = FALSE;
IMAGE_DOS_HEADER *pDos = (PIMAGE_DOS_HEADER)fp;
IMAGE_NT_HEADERS *pNt = (PIMAGE_NT_HEADERS)(fp+pDos->e_lfanew);
if (pNt->FileHeader.Machine==0x8664)
printf("不支持x64程序的解析!!\n");
if (pDos->e_magic == IMAGE_DOS_SIGNATURE && pNt->Signature == IMAGE_NT_SIGNATURE&&pNt->FileHeader.Machine == 0x14c)
ret = TRUE;
return ret;
}
DWORD RVAToFOA(PIMAGE_NT_HEADERS pNt, DWORD rva)
{
PIMAGE_SECTION_HEADER pSec = IMAGE_FIRST_SECTION(pNt);
for (size_t i = 0; i < pNt->FileHeader.NumberOfSections; i++){
// 落在哪个区段内 大于虚拟地址 小于 虚拟地址+区段在文件中的大小
if (rva >= pSec[i].VirtualAddress && rva <= pSec[i].VirtualAddress + pSec[i].SizeOfRawData){
if (pSec[i].SizeOfRawData == 0)
return 0;
// rva在内存中的区段偏移 + 区段在文件中的基址
return rva - pSec[i].VirtualAddress + pSec[i].PointerToRawData;
}
}
return 0;
}
//mIcon* ImageDataArry,DWORD nCount,LPBYTE fp:图标数据数组指针,图标数量,指向指向了填充了文件数据的内存地址
//WriteIcon:构造ico头部数据,拼接ico数据,二进制数据写入文件保存,循环操作
//BOOL ret:返回TRUE写入ico文件成功
BOOL WriteIcon(mIcon* ImageDataArry, DWORD nCount, LPBYTE fp){
BOOL ret = TRUE;
WORD idReserved = 0;
WORD idType = 1;
WORD idCount = 1;
printf("共计%d张icon\n\n",nCount);
int N = sizeof(icondir);
icondir idtmp = {};
for (DWORD i = 0; i < nCount; i++){
iconbitmapinfoheader* tmp = (lpiconbitmapinfoheader)(ImageDataArry->IconFOA + (DWORD)fp);
idtmp.idreserved = 0;
idtmp.idtype = 1;
idtmp.idcount = 1;
idtmp.identries->dwBYTEsinres = ImageDataArry->IcoSize;
idtmp.identries->dwimageoffset = 22;
errno_t err;
char fileName[1024] = "";
char* ori = "icon";
sprintf_s(fileName,"%s%d.ico",ori,i+1);
FILE* p;
err = fopen_s(&p,fileName,"wb+");
if (err)
ret = FALSE;
fwrite(&idtmp,6,1,p);
fwrite(&idtmp.identries, 16, 1, p);
fwrite((void*)(ImageDataArry->IconFOA + (DWORD)fp), ImageDataArry->IcoSize, 1, p);
fclose(p);
printf("dump 第%d张Icon\n",i+1);
ImageDataArry++;
}
return ret;
}
//LPBYTE fp,mIcon* pArry:指向了填充了文件数据的内存地址,用于存放图表数据的数组指针(函数结束时返回)
//ResourceInfo:解析PE头部(DOS头部NT头部),定位资源段位置(PIMAGE_DATA_DIRECTORY),解析资源段的三层目录,定位图标的数据内容的偏移并记录,循环操作
//DWORD IconCount:返回图标的数量
DWORD ResourceInfo(LPBYTE fp,mIcon* pArry){
DWORD TypeResCount = 0;
DWORD ThisResCount = 0;
DWORD IconCount = 0;
IMAGE_DOS_HEADER* pDos = (PIMAGE_DOS_HEADER)fp;
IMAGE_NT_HEADERS* pNt = (PIMAGE_NT_HEADERS)(fp + pDos->e_lfanew);
PIMAGE_DATA_DIRECTORY pDirData = &(pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE]);
//First Dir
PIMAGE_RESOURCE_DIRECTORY pFirstDirRes = (PIMAGE_RESOURCE_DIRECTORY)(RVAToFOA(pNt, pDirData->VirtualAddress) + (DWORD)fp);
TypeResCount = pFirstDirRes->NumberOfIdEntries + pFirstDirRes->NumberOfNamedEntries;
PIMAGE_RESOURCE_DIRECTORY_ENTRY pFirstEntryRes = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pFirstDirRes + 1);
printf("pDos:0x%08x\npNt:0x%08x\npResData:0x%08x\n\n",
pDos, pNt, pFirstDirRes);
for (DWORD i = 0; i < TypeResCount; i++){
if (pFirstEntryRes[i].Id != (WORD)RT_ICON)
continue;
//Second Dir
IMAGE_RESOURCE_DIRECTORY_ENTRY tmp = pFirstEntryRes[i];
PIMAGE_RESOURCE_DIRECTORY pSecondDirRes = (PIMAGE_RESOURCE_DIRECTORY)(pFirstEntryRes[i].OffsetToDirectory + (DWORD)pFirstDirRes);
ThisResCount = pSecondDirRes->NumberOfIdEntries + pSecondDirRes->NumberOfNamedEntries;
PIMAGE_RESOURCE_DIRECTORY_ENTRY pSecondEntryRes = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pSecondDirRes + 1);
IconCount = ThisResCount;
printf("Resource:%d种资源\nRT_ICON:%d张图标\n\n",
TypeResCount, IconCount);
for (DWORD j = 0; j < ThisResCount; j++){
if (pSecondEntryRes->NameIsString){
PIMAGE_RESOURCE_DIR_STRING_U pNameThisRes = (PIMAGE_RESOURCE_DIR_STRING_U)(pSecondEntryRes[j].NameOffset + pSecondDirRes);
WCHAR *pName = new WCHAR[pNameThisRes->Length + 1]{};
memcpy_s(pName, sizeof(WCHAR)*pNameThisRes->Length, pNameThisRes->NameString, sizeof(WCHAR)*pNameThisRes->Length);
printf("Name:%S\n", pName);
delete[] pName;
}
else
printf("id:%d\n", pSecondEntryRes[j].Id);
//Third Dir
PIMAGE_RESOURCE_DIRECTORY pThirdDirRes = (PIMAGE_RESOURCE_DIRECTORY)(pSecondEntryRes[j].OffsetToDirectory + (DWORD)pFirstDirRes);
PIMAGE_RESOURCE_DIRECTORY_ENTRY pThirdEntryRes = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pThirdDirRes + 1);
//IconData:RVA&Size
PIMAGE_RESOURCE_DATA_ENTRY pDataInfo = (PIMAGE_RESOURCE_DATA_ENTRY)(pThirdEntryRes->OffsetToData + (DWORD)pFirstDirRes);
pArry->IconFOA = RVAToFOA(pNt, pDataInfo->OffsetToData);
pArry->IcoSize = pDataInfo->Size;
pArry++;
printf("->第一层:%08x\n-->第二层:%08x\n--->第三层:%08x\n=>资源位置数据:%08x\n\n",
pFirstDirRes, pSecondDirRes, pThirdDirRes,pDataInfo);
printf("OffsetToData:%08x\nSize:%08x\nFOA:%08x\nIconData:%08x\n\n",
pDataInfo->OffsetToData, pDataInfo->Size, RVAToFOA(pNt, pDataInfo->OffsetToData), RVAToFOA(pNt, pDataInfo->OffsetToData) + fp);
}
}
return IconCount;
}
BOOL Analysis(LPBYTE fp){
mIcon* ImageData = new mIcon{};
if (!IsPE32(fp))
return FALSE;
DWORD nCount = ResourceInfo(fp,ImageData);
if (!WriteIcon(ImageData, nCount, fp))
printf("在该目录下没有读写文件的权限,请换至其他位置如d盘下的目录\n");
return TRUE;
}
int main(int argc,char* args[]){
if (argc == 2){
LPBYTE fp = ReadPEToMemory(args[1]);
printf("FileName:%s\nAddress:0x%08x\n\n", args[1], fp);
if (Analysis(fp))
printf("解析完毕!!\n");
else
printf("解析失败!!\n");
}
else
printf("input FileName\n");
return 0;
}
后记
早上
老大突然来了一句:
"你要不搞下安卓吧,我们人不是很够."
我说:
"哦,好啊."(内心还是有点小慌)
然后
我默默的关掉burpsuite,打开了androidkiller.
看着看着,突然有感而发:
“哎呦,卧槽,这图标有点帅啊.”
然后就没有然后了……
ps:当时resourcehacker不知道啥时候被我删了,然后就百度谷歌后明确了思路写了这段代码.
main.zip (3.0 KB)