最近在写PE加载代码,写到LoadStringA时一直找不到字中的ID,网上搜索的资源只分析三层目录,之后的找不相关资料,只好自己分析。
资料显示pe资源表 分三层目录,之后是节点,再指向数据,
第二层的iD = 字串ID/16+1 ,
字串ID=(第二层的iD-1)*16+i,I由不同的分组得来,一组0到15
字串数据是按IMAGE_RESOURCE_DIR_STRING_U 顺序排列,空串占2字节(长度0),非空串长度=字符数+长度(2字节)+结束(00 00 占2字节),
检索字串 满足 序号0~15,所有字串结构长度相加不大于 节点中的Size;
宽字符还是其他格式应该与第三层的ID有关。
另外注意要将 IMAGE_RESOURCE_DIR_STRING_U 结构中的WCHAR NameString[] 方括号空出来,否则sizeof计算会出错。
void * get_resource(HINSTANCE hInstance, UINT uID, LPSTR lpBuffer, int mode) {DWORD pFileBuffer = hInstance;PIMAGE_DOS_HEADER pDosHeader = NULL;PIMAGE_NT_HEADERS pNtHeaders = NULL;PIMAGE_RESOURCE_DIRECTORY_ENTRY pResDirEntry1 = NULL;PIMAGE_RESOURCE_DIRECTORY_ENTRY pResDirEntry2 = NULL;PIMAGE_RESOURCE_DIRECTORY_ENTRY pResDirEntry3 = NULL;PIMAGE_RESOURCE_DIRECTORY pResDir1 = NULL;PIMAGE_RESOURCE_DIRECTORY pResDir2 = NULL;PIMAGE_RESOURCE_DIRECTORY pResDir3 = NULL;PIMAGE_RESOURCE_DIR_STRING_U pUStr = NULL;PIMAGE_RESOURCE_DATA_ENTRY pResDataEntry = NULL;DWORD dwResourceAddr = 0;DWORD dwSize, dwSize2, dwSize3, dwResDataSize;pDosHeader = (PIMAGE_DOS_HEADER) hInstance;if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {KPrintf("not a mz header!\n");return NULL;}pNtHeaders =(PIMAGE_NT_HEADERS) ((DWORD) pDosHeader + pDosHeader->e_lfanew);if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE) {KPrintf("not a PE header!\n");return NULL;}//资源表在文件中的偏移dwResourceAddr = (DWORD) pFileBuffer+ pNtHeaders->OptionalHeader.DataDirectory[2].VirtualAddress;pResDir1 = (PIMAGE_RESOURCE_DIRECTORY) dwResourceAddr;pResDirEntry1 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY) ((DWORD) pResDir1+ sizeof(IMAGE_RESOURCE_DIRECTORY));dwSize = pResDir1->NumberOfIdEntries + pResDir1->NumberOfNamedEntries;//KPrintf("一级,%d,%x!\n", dwSize, pResDirEntry1);for (int i = 0; i < dwSize; i++) {//第一层if (pResDirEntry1->DUMMYUNIONNAME.Id == mode) {pResDir2 =(PIMAGE_RESOURCE_DIRECTORY) (dwResourceAddr+ pResDirEntry1->DUMMYUNIONNAME2.DUMMYSTRUCTNAME2.OffsetToDirectory);pResDirEntry2 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY) ((DWORD) pResDir2+ sizeof(IMAGE_RESOURCE_DIRECTORY));dwSize2 = pResDir2->NumberOfIdEntries+ pResDir2->NumberOfNamedEntries;for (int j = 0; j < dwSize2; j++) {if (pResDirEntry2->DUMMYUNIONNAME2.DUMMYSTRUCTNAME2.DataIsDirectory) {pResDir3 =(PIMAGE_RESOURCE_DIRECTORY) (dwResourceAddr+ pResDirEntry2->DUMMYUNIONNAME2.DUMMYSTRUCTNAME2.OffsetToDirectory);pResDirEntry3 =(PIMAGE_RESOURCE_DIRECTORY_ENTRY) ((DWORD) pResDir3+ sizeof(IMAGE_RESOURCE_DIRECTORY));dwSize3 = pResDir3->NumberOfIdEntries+ pResDir3->NumberOfNamedEntries;for (int k = 0; k < dwSize3; k++) {//name指向的是代码页的编号,一般都为2056简体中文。这里省略pResDataEntry =(PIMAGE_RESOURCE_DIRECTORY) (dwResourceAddr+ pResDirEntry3->DUMMYUNIONNAME2.OffsetToData);dwResDataSize = pResDataEntry->Size;//KPrintf("name:%d ID:%x ,size %d ,%x\n",// pResDirEntry3->DUMMYUNIONNAME.Id,// pResDataEntry->OffsetToData,dwResDataSize,pResDataEntry);// 每16个字符串为一个串组,字符串的排序以字符串的资源ID号为依据,// 0 - 15为第一串组,16 - 31位第二串组,32 - 47位第三串组,...。// 串组ID号从1开始,字符串ID号从0开始。由此可以得到如下公式:// 串组ID = (取整)(字符串ID / 16) + 1// 串组ID = pParam->dwResID//if (pResDirEntry3->DUMMYUNIONNAME.Id == 2052)//(L"LANGUAGE LANG_CHINESE,\r\n");//else if (pResDirEntry3->DUMMYUNIONNAME.Id == 1033)//(L"LANGUAGE LANG_ENGLISH,\r\n");DWORD stAddr = pResDataEntry->OffsetToData+ pFileBuffer;int offset = 0;for (int i = 1; i <= 16; i++) {pUStr = stAddr + offset;offset += (DWORD)(pUStr->Length)*2+ sizeof(IMAGE_RESOURCE_DIR_STRING_U);if(pUStr->Length>0){offset+=2;}// 计算字符串长度// 判断字符串长度if(offset>dwResDataSize) return 0;// 字符编码是UNICODEKPrintf("字串id :%d ,地址:%x,长度:%d \n",((pResDirEntry2->DUMMYUNIONNAME.Id - 1) * 16+ i), pUStr,pUStr->Length);}pResDirEntry3++;} //3级}//if (pResDirEntry2->DUMMYUNIONNAME2.DUMMYSTRUCTNAME2.DataIsDirectory)pResDirEntry2++;} //二级} //if (pResDirEntry1->DUMMYUNIONNAME.Id == mode)pResDirEntry1++;} //一级
}