| /* | |
| genidl - Generate interface defintion language listing from a | |
| Portable Executable. | |
| Copyright (C) 2009-2016 mingw-w64 project | |
| This program is free software: you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation, either version 3 of the License, or | |
| (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | |
| #define _CRT_SECURE_NO_WARNINGS | |
| #include "genidl_cfg.h" | |
| #include "genidl_readpe.h" | |
| static int32_t readResourceDirectory (FILE *fp, unsigned char **dta, size_t *length, size_t *resRVA); | |
| static void walk_res_dir (sImgResourceDirectory *resDir, unsigned char *base,uint32_t level, uint32_t resourceType, int32_t beTypelib, int32_t *noRes,uint32_t *zOff, uint32_t *zSize); | |
| int32_t | |
| genidl_pe_typelib_resource_count (FILE *fp) | |
| { | |
| int32_t cnt = 0x2000; | |
| uint32_t zOff = 0,zSize = 0; | |
| size_t resRVA = 0; | |
| unsigned char *dta = NULL; | |
| size_t length = 0; | |
| if (!readResourceDirectory (fp, &dta, &length,&resRVA)) | |
| return 0; | |
| if (!length || !dta) | |
| return 0; | |
| zOff = zSize = 0; | |
| walk_res_dir ((sImgResourceDirectory *) dta, dta,0, 0, 0, &cnt,&zOff,&zSize); | |
| free (dta); | |
| return 0x2000 - cnt; | |
| } | |
| int32_t | |
| genidl_pe_typelib_resource_read (FILE *fp, int32_t noRes, unsigned char **pDta, size_t *szDta) | |
| { | |
| uint32_t zOff,zSize; | |
| unsigned char *dta = NULL; | |
| size_t length = 0; | |
| size_t resRVA = 0; | |
| if (!readResourceDirectory (fp, &dta, &length, &resRVA)) | |
| return 0; | |
| if (!length || !dta) | |
| return 0; | |
| zOff = zSize = 0; | |
| walk_res_dir ((sImgResourceDirectory *) dta, dta,0, 0, 0, &noRes,&zOff,&zSize); | |
| if (zOff != 0) | |
| { | |
| pDta[0] = (unsigned char *) malloc (zSize + 1); | |
| szDta[0] = zSize; | |
| memcpy (pDta[0], dta + zOff - resRVA , zSize); | |
| free (dta); | |
| return 1; | |
| } | |
| free (dta); | |
| return 0; | |
| } | |
| static void get_res_name_by_id (uint32_t id, unsigned char *resourceBase, char *buffer, uint32_t cBytes); | |
| static void walk_res_entry (sImgResourceDirectoryEntry *resDirEntry, unsigned char *resourceBase, uint32_t level, int32_t beTypelib, int32_t *noRes,uint32_t *zOff,uint32_t *zSize); | |
| static void | |
| walk_res_dir (sImgResourceDirectory *resDir, unsigned char *base,uint32_t level, uint32_t resourceType, int32_t beTypelib, int32_t *noRes,uint32_t *zOff,uint32_t *zSize) | |
| { | |
| sImgResourceDirectoryEntry *resDirEntry; | |
| char szType[64]; | |
| uint32_t i; | |
| uint32_t mloop = 0; | |
| if (resourceType & GENIDL_IMG_RESNAME_IS_STR) | |
| { | |
| get_res_name_by_id (resourceType, base,szType, sizeof(szType)); | |
| } | |
| else | |
| sprintf(szType, "%X", resourceType); | |
| if (!strcmp (szType,"TYPELIB")) | |
| beTypelib = 1; | |
| resDirEntry = (sImgResourceDirectoryEntry *) (resDir + 1); | |
| mloop = resDir->NumberOfNamedEntries + resDir->NumberOfIdEntries; | |
| for (i=0; i < mloop; i++, resDirEntry++) | |
| { | |
| walk_res_entry (resDirEntry, base, level + 1, beTypelib, noRes,zOff,zSize); | |
| if (zOff[0] != 0) | |
| return; | |
| } | |
| if (!strcmp (szType,"TYPELIB")) | |
| beTypelib = 0; | |
| } | |
| static void | |
| walk_res_entry (sImgResourceDirectoryEntry *resDirEntry, unsigned char *resourceBase, uint32_t level, int32_t beTypelib,int32_t *noRes,uint32_t *zOff,uint32_t *zSize) | |
| { | |
| sImgResourceDataEntry *pResDataEntry; | |
| if (resDirEntry->OffsetToData & GENIDL_IMG_RESDATA_IS_DIR) { | |
| walk_res_dir ((sImgResourceDirectory *) ((resDirEntry->OffsetToData & 0x7FFFFFFF) + resourceBase), | |
| resourceBase, level, resDirEntry->Name, beTypelib, noRes,zOff,zSize); | |
| return; | |
| } | |
| if (!beTypelib) | |
| return; | |
| if (noRes[0] != 0) | |
| { | |
| noRes[0] -= 1; | |
| return; | |
| } | |
| pResDataEntry = (sImgResourceDataEntry *) | |
| (resourceBase + resDirEntry->OffsetToData); | |
| zOff[0] = pResDataEntry->OffsetToData; /* RVA!!! */ | |
| zSize[0] = pResDataEntry->Size; | |
| } | |
| static void | |
| get_res_name_by_id (uint32_t id, unsigned char *resourceBase, char *buffer, uint32_t cBytes) | |
| { | |
| sImgResourceDirStringWC *prdsu; | |
| if ( !(id & GENIDL_IMG_RESNAME_IS_STR) ) | |
| { | |
| sprintf (buffer, "%X", id); | |
| return; | |
| } | |
| id &= 0x7FFFFFFF; | |
| prdsu = (sImgResourceDirStringWC *) (resourceBase + id); | |
| #ifdef _WIN32 | |
| WideCharToMultiByte(CP_ACP, 0, prdsu->NameString, prdsu->Length, buffer, cBytes, 0, 0); | |
| buffer[min(cBytes - 1, prdsu->Length)] = 0; /* Null terminate it!!! */ | |
| #else | |
| { | |
| uint32_t mi = cBytes - 1; | |
| uint32_t i; | |
| if (mi > prdsu->Length) | |
| mi = prdsu->Length; | |
| for (i = 0; i < mi; i++) | |
| { | |
| uint16_t ch = prdsu->NameString[i]; | |
| if (!ch) | |
| break; | |
| if (ch >= 128) | |
| ch = '?'; | |
| buffer[i] = (char) (ch & 0xff); | |
| } | |
| buffer[i] = 0; | |
| } | |
| #endif | |
| } | |
| static int32_t | |
| readResourceDirectory (FILE *fp, unsigned char **dta, size_t *length, size_t *resRVA) | |
| { | |
| long pe_header; | |
| int32_t be64; | |
| uImgHeader hdr; | |
| uint32_t res_size = 0; | |
| uint32_t sect_count; | |
| sImgSectionHdr sec; | |
| pe_header = genidl_ispe (fp, &be64); | |
| *dta = NULL; length[0]=0; | |
| if (!pe_header) | |
| return 0; | |
| fseek (fp, pe_header, SEEK_SET); | |
| if (be64) | |
| { | |
| fread (&hdr.hdr64, 1, sizeof (sImgNtHeaders64), fp); | |
| if (hdr.hdr64.OptionalHeader.NumberOfRvaAndSizes > 2) | |
| res_size = hdr.hdr64.OptionalHeader.DataDirectory[2].size; | |
| sect_count = hdr.hdr64.FileHeader.NumberOfSections; | |
| } | |
| else | |
| { | |
| fread (&hdr.hdr32, 1, sizeof (sImgNtHeaders32), fp); | |
| if (hdr.hdr32.OptionalHeader.NumberOfRvaAndSizes > 2) | |
| res_size = hdr.hdr32.OptionalHeader.DataDirectory[2].size; | |
| sect_count = hdr.hdr32.FileHeader.NumberOfSections; | |
| } | |
| if (!res_size) | |
| return 1; | |
| while (sect_count > 0) | |
| { | |
| fread (&sec, 1, sizeof (sImgSectionHdr),fp); | |
| if (!strcmp ((char *) sec.Name, ".rsrc")) | |
| { | |
| fseek (fp, (int32_t) sec.PointerToRawData, SEEK_SET); | |
| resRVA[0] = sec.VirtualAddress; | |
| dta[0] = (unsigned char *) malloc (res_size); | |
| fread (dta[0], 1, res_size, fp); | |
| length[0] = res_size; | |
| return 1; | |
| } | |
| sect_count--; | |
| } | |
| return 1; | |
| } | |
| int32_t | |
| genidl_ispe (FILE *fp, int32_t *be64) | |
| { | |
| sDosHeader hdr; | |
| uImgHeader img; | |
| *be64 = 0; | |
| fseek(fp, 0, SEEK_SET); | |
| if (fread (&hdr, 1, sizeof (hdr), fp) != sizeof (hdr)) | |
| return 0; | |
| if (hdr.magic != 0x5a4d) | |
| return 0; | |
| if (hdr.lfanew == 0) | |
| return 0; | |
| fseek (fp, (int32_t) hdr.lfanew, SEEK_SET); | |
| if (fread (&img, 1, sizeof (img), fp) != sizeof (img)) | |
| return 0; | |
| if (img.hdr32.Signature != 0x4550) | |
| return 0; | |
| if (img.hdr32.OptionalHeader.Magic == 0x10b) | |
| *be64 = 0; | |
| else if (img.hdr64.OptionalHeader.Magic == 0x20b) | |
| *be64 = 1; | |
| else | |
| return 0; | |
| return (int32_t) hdr.lfanew; | |
| } |