1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5//
6// Win32 Resource extractor
7//
8#include "ildasmpch.h"
9
10#ifndef FEATURE_PAL
11#include "debugmacros.h"
12#include "corpriv.h"
13#include "dasmenum.hpp"
14#include "formattype.h"
15#include "dis.h"
16#include "resource.h"
17#include "ilformatter.h"
18#include "outstring.h"
19
20#include "ceeload.h"
21#include "dynamicarray.h"
22extern IMAGE_COR20_HEADER * g_CORHeader;
23extern IMDInternalImport* g_pImport;
24extern PELoader * g_pPELoader;
25extern IMetaDataImport2* g_pPubImport;
26extern char g_szAsmCodeIndent[];
27extern unsigned g_uConsoleCP;
28
29struct ResourceHeader
30{
31 DWORD dwDataSize;
32 DWORD dwHeaderSize;
33 DWORD dwTypeID;
34 DWORD dwNameID;
35 DWORD dwDataVersion;
36 WORD wMemFlags;
37 WORD wLangID;
38 DWORD dwVersion;
39 DWORD dwCharacteristics;
40 ResourceHeader()
41 {
42 memset(this,0,sizeof(ResourceHeader));
43 dwHeaderSize = sizeof(ResourceHeader);
44 dwTypeID = dwNameID = 0xFFFF;
45 };
46};
47
48struct ResourceNode
49{
50 ResourceHeader ResHdr;
51 IMAGE_RESOURCE_DATA_ENTRY DataEntry;
52 WCHAR* wzType;
53 WCHAR* wzName;
54 ResourceNode(DWORD tid, DWORD nid, DWORD lid, DWORD dataOffset, BYTE* ptrBase)
55 {
56 if(tid & 0x80000000)
57 {
58 ResHdr.dwTypeID = 0;
59 tid &= 0x7FFFFFFF;
60 WORD L = *((WORD*)(ptrBase+tid));
61 wzType = new WCHAR[L+1];
62 memcpy(wzType,ptrBase+tid+sizeof(WORD),L*sizeof(WCHAR));
63 wzType[L]=0;
64 }
65 else
66 {
67 ResHdr.dwTypeID = (0xFFFF |((tid & 0xFFFF)<<16));
68 wzType = NULL;
69 }
70
71 if(nid & 0x80000000)
72 {
73 ResHdr.dwNameID = 0;
74 nid &= 0x7FFFFFFF;
75 WORD L = *((WORD*)(ptrBase+nid));
76 wzName = new WCHAR[L+1];
77 memcpy(wzName, ptrBase+nid+sizeof(WORD), L*sizeof(WCHAR));
78 wzName[L]=0;
79 }
80 else
81 {
82 ResHdr.dwNameID = (0xFFFF |((nid & 0xFFFF)<<16));
83 wzName = NULL;
84 }
85
86 //ResHdr.dwTypeID = (tid & 0x80000000) ? tid : (0xFFFF |((tid & 0xFFFF)<<16));
87 //ResHdr.dwNameID = (nid & 0x80000000) ? nid : (0xFFFF |((nid & 0xFFFF)<<16));
88 ResHdr.wLangID = (WORD)lid;
89 if(ptrBase) memcpy(&DataEntry,(ptrBase+dataOffset),sizeof(IMAGE_RESOURCE_DATA_ENTRY));
90 ResHdr.dwDataSize = DataEntry.Size;
91 };
92 ~ResourceNode()
93 {
94 if(wzType) VDELETE(wzType);
95 if(wzName) VDELETE(wzName);
96 };
97 void Save(FILE* pF)
98 {
99 // Dump them to pF
100 BYTE* pbData;
101 DWORD dwFiller = 0;
102 BYTE bNil[3] = {0,0,0};
103 // For each resource write header and data
104 if(g_pPELoader->getVAforRVA(VAL32(DataEntry.OffsetToData), (void **) &pbData))
105 {
106 //fwrite(&(g_prResNodePtr[i]->ResHdr),g_prResNodePtr[i]->ResHdr.dwHeaderSize,1,pF);
107 ResHdr.dwHeaderSize = sizeof(ResourceHeader);
108 if(wzType) ResHdr.dwHeaderSize += (DWORD)((wcslen(wzType) + 1)*sizeof(WCHAR) - sizeof(DWORD));
109 if(wzName) ResHdr.dwHeaderSize += (DWORD)((wcslen(wzName) + 1)*sizeof(WCHAR) - sizeof(DWORD));
110
111 //---- Constant part of the header: DWORD,DWORD
112 fwrite(&ResHdr.dwDataSize, sizeof(DWORD),1,pF);
113 fwrite(&ResHdr.dwHeaderSize, sizeof(DWORD),1,pF);
114 //--- Variable part of header: type and name
115 if(wzType)
116 {
117 fwrite(wzType,(wcslen(wzType) + 1)*sizeof(WCHAR), 1, pF);
118 dwFiller += (DWORD)wcslen(wzType) + 1;
119 }
120 else
121 fwrite(&ResHdr.dwTypeID,sizeof(DWORD),1,pF);
122 if(wzName)
123 {
124 fwrite(wzName,(wcslen(wzName) + 1)*sizeof(WCHAR), 1, pF);
125 dwFiller += (DWORD)wcslen(wzName) + 1;
126 }
127 else
128 fwrite(&ResHdr.dwNameID,sizeof(DWORD),1,pF);
129
130 // Align remaining fields on DWORD
131 if(dwFiller & 1)
132 fwrite(bNil,2,1,pF);
133
134 //---- Constant part of the header: DWORD,WORD,WORD,DWORD,DWORD
135 fwrite(&ResHdr.dwDataVersion,8*sizeof(WORD),1,pF);
136 //---- Header done, now data
137 fwrite(pbData,VAL32(DataEntry.Size),1,pF);
138 dwFiller = VAL32(DataEntry.Size) & 3;
139 if(dwFiller)
140 {
141 dwFiller = 4 - dwFiller;
142 fwrite(bNil,dwFiller,1,pF);
143 }
144 }
145 };
146};
147
148
149#define RES_FILE_DUMP_ENABLED
150
151DWORD DumpResourceToFile(__in __nullterminated WCHAR* wzFileName)
152{
153
154 BYTE* pbResBase;
155 FILE* pF = NULL;
156 DWORD ret = 0;
157 DWORD dwResDirRVA;
158 DWORD dwResDirSize;
159 unsigned ulNumResNodes=0;
160 DynamicArray<ResourceNode*> g_prResNodePtr;
161
162 if (g_pPELoader->IsPE32())
163 {
164 IMAGE_OPTIONAL_HEADER32 *pOptHeader = &(g_pPELoader->ntHeaders32()->OptionalHeader);
165
166 dwResDirRVA = VAL32(pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
167 dwResDirSize = VAL32(pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size);
168 }
169 else
170 {
171 IMAGE_OPTIONAL_HEADER64 *pOptHeader = &(g_pPELoader->ntHeaders64()->OptionalHeader);
172
173 dwResDirRVA = VAL32(pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
174 dwResDirSize = VAL32(pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size);
175 }
176
177 if(dwResDirRVA && dwResDirSize)
178 {
179 if(g_pPELoader->getVAforRVA(dwResDirRVA, (void **) &pbResBase))
180 {
181 // First, pull out all resource nodes (tree leaves), see ResourceNode struct
182 PIMAGE_RESOURCE_DIRECTORY pirdType = (PIMAGE_RESOURCE_DIRECTORY)pbResBase;
183 PIMAGE_RESOURCE_DIRECTORY_ENTRY pirdeType = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pbResBase+sizeof(IMAGE_RESOURCE_DIRECTORY));
184 DWORD dwTypeID;
185 unsigned short i = 0,N = pirdType->NumberOfNamedEntries+pirdType->NumberOfIdEntries;
186 PAL_CPP_TRY {
187 for(i=0; i < N; i++, pirdeType++)
188 {
189 dwTypeID = VAL32(IMAGE_RDE_NAME(pirdeType));
190 if(IMAGE_RDE_OFFSET_FIELD(pirdeType, DataIsDirectory))
191 {
192 BYTE* pbNameBase = pbResBase + VAL32(IMAGE_RDE_OFFSET_FIELD(pirdeType, OffsetToDirectory));
193 PIMAGE_RESOURCE_DIRECTORY pirdName = (PIMAGE_RESOURCE_DIRECTORY)pbNameBase;
194 PIMAGE_RESOURCE_DIRECTORY_ENTRY pirdeName = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pbNameBase+sizeof(IMAGE_RESOURCE_DIRECTORY));
195 DWORD dwNameID;
196 unsigned short i,N = VAL16(pirdName->NumberOfNamedEntries)+VAL16(pirdName->NumberOfIdEntries);
197
198 for(i=0; i < N; i++, pirdeName++)
199 {
200 dwNameID = VAL32(IMAGE_RDE_NAME(pirdeName));
201 if(IMAGE_RDE_OFFSET_FIELD(pirdeName, DataIsDirectory))
202 {
203 BYTE* pbLangBase = pbResBase + VAL32(IMAGE_RDE_OFFSET_FIELD(pirdeName, OffsetToDirectory));
204 PIMAGE_RESOURCE_DIRECTORY pirdLang = (PIMAGE_RESOURCE_DIRECTORY)pbLangBase;
205 PIMAGE_RESOURCE_DIRECTORY_ENTRY pirdeLang = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pbLangBase+sizeof(IMAGE_RESOURCE_DIRECTORY));
206 DWORD dwLangID;
207 unsigned short i,N = VAL16(pirdLang->NumberOfNamedEntries)+VAL16(pirdLang->NumberOfIdEntries);
208
209 for(i=0; i < N; i++, pirdeLang++)
210 {
211 dwLangID = VAL32(IMAGE_RDE_NAME(pirdeLang));
212 if(IMAGE_RDE_OFFSET_FIELD(pirdeLang, DataIsDirectory))
213 {
214 _ASSERTE(!"Resource hierarchy exceeds three levels");
215 }
216 else
217 {
218 g_prResNodePtr[ulNumResNodes++] = new ResourceNode(dwTypeID,dwNameID,dwLangID, VAL32(IMAGE_RDE_OFFSET(pirdeLang)),pbResBase);
219 }
220 }
221 }
222 else
223 {
224 g_prResNodePtr[ulNumResNodes++] = new ResourceNode(dwTypeID,dwNameID,0,VAL32(IMAGE_RDE_OFFSET(pirdeName)),pbResBase);
225 }
226 }
227 }
228 else
229 {
230 g_prResNodePtr[ulNumResNodes++] = new ResourceNode(dwTypeID,0,0,VAL32(IMAGE_RDE_OFFSET(pirdeType)),pbResBase);
231 }
232 }
233 } PAL_CPP_CATCH_ALL {
234 ret= 0xDFFFFFFF;
235 ulNumResNodes = 0;
236 }
237 PAL_CPP_ENDTRY
238 // OK, all tree leaves are in ResourceNode structs, and ulNumResNodes ptrs are in g_prResNodePtr
239 if(ulNumResNodes)
240 {
241 ret = 1;
242#ifdef RES_FILE_DUMP_ENABLED
243
244 _wfopen_s(&pF,wzFileName,L"wb");
245 if(pF)
246 {
247 // Dump them to pF
248 // Write dummy header
249 ResourceHeader *pRH = new ResourceHeader();
250 fwrite(pRH,sizeof(ResourceHeader),1,pF);
251 SDELETE(pRH);
252 // For each resource write header and data
253 PAL_CPP_TRY {
254 for(i=0; i < ulNumResNodes; i++)
255 {
256 /*
257 sprintf_s(szString,SZSTRING_SIZE,"// Res.# %d Type=0x%X Name=0x%X Lang=0x%X DataOffset=0x%X DataLength=%d",
258 i+1,
259 g_prResNodePtr[i]->ResHdr.dwTypeID,
260 g_prResNodePtr[i]->ResHdr.dwNameID,
261 g_prResNodePtr[i]->ResHdr.wLangID,
262 VAL32(g_prResNodePtr[i]->DataEntry.OffsetToData),
263 VAL32(g_prResNodePtr[i]->DataEntry.Size));
264 printLine(NULL,szString);
265 */
266 g_prResNodePtr[i]->Save(pF);
267 SDELETE(g_prResNodePtr[i]);
268 }
269 }
270 PAL_CPP_CATCH_ALL {
271 ret= 0xDFFFFFFF;
272 }
273 PAL_CPP_ENDTRY
274 fclose(pF);
275 }// end if file opened
276 else ret = 0xEFFFFFFF;
277#else
278 // Dump to text, using wzFileName as GUICookie
279 //char szString[4096];
280 void* GUICookie = (void*)wzFileName;
281 BYTE* pbData;
282 printLine(GUICookie,"");
283 sprintf_s(szString, _countof(szString), "// ========== Win32 Resource Entries (%d) ========",ulNumResNodes);
284 for(i=0; i < ulNumResNodes; i++)
285 {
286 printLine(GUICookie,"");
287 sprintf_s(szString, _countof(szString), "// Res.# %d Type=0x%X Name=0x%X Lang=0x%X DataOffset=0x%X DataLength=%d",
288 i+1,
289 g_prResNodePtr[i]->ResHdr.dwTypeID,
290 g_prResNodePtr[i]->ResHdr.dwNameID,
291 g_prResNodePtr[i]->ResHdr.wLangID,
292 VAL32(g_prResNodePtr[i]->DataEntry.OffsetToData),
293 VAL32(g_prResNodePtr[i]->DataEntry.Size));
294 printLine(GUICookie,szString);
295 if(g_pPELoader->getVAforRVA(VAL32(g_prResNodePtr[i]->DataEntry.OffsetToData), (void **) &pbData))
296 {
297 strcat(g_szAsmCodeIndent,"// ");
298 strcpy(szString,g_szAsmCodeIndent);
299 DumpByteArray(szString,pbData,VAL32(g_prResNodePtr[i]->DataEntry.Size),GUICookie);
300 printLine(GUICookie,szString);
301 g_szAsmCodeIndent[strlen(g_szAsmCodeIndent)-4] = 0;
302 }
303 SDELETE(g_prResNodePtr[i]);
304 }
305 ret = 1;
306#endif
307 } // end if there are nodes
308 }// end if got ptr to resource
309 else ret = 0xFFFFFFFF;
310 } // end if there is resource
311 else ret = 0;
312
313 return ret;
314}
315#endif // FEATURE_PAL
316
317