aboutsummaryrefslogtreecommitdiff
path: root/win32
diff options
context:
space:
mode:
authorThijs Schreijer <thijs@thijsschreijer.nl>2013-10-11 23:54:51 +0200
committerThijs Schreijer <thijs@thijsschreijer.nl>2013-10-11 23:54:51 +0200
commit23214ad83c36ca2d6f74e1532bcd7f1acd87db82 (patch)
tree9e0c40d1f9b5776de5f053e5f22b988f86606c0e /win32
parent68878f0fdde4749be50eeb141631eafa5b7aa8e3 (diff)
downloadluarocks-23214ad83c36ca2d6f74e1532bcd7f1acd87db82.tar.gz
luarocks-23214ad83c36ca2d6f74e1532bcd7f1acd87db82.tar.bz2
luarocks-23214ad83c36ca2d6f74e1532bcd7f1acd87db82.zip
included pe-parser module (no longer objdump.exe, which is removed), updated installer to check architecture (32/64) interpreter was compiled for (instead of system architecture). Runtime detection should now also properly work on 64bit binaries.
Diffstat (limited to 'win32')
-rw-r--r--win32/bin/bin/objdump.exebin2247694 -> 0 bytes
-rw-r--r--win32/bin/pe-parser.lua546
2 files changed, 546 insertions, 0 deletions
diff --git a/win32/bin/bin/objdump.exe b/win32/bin/bin/objdump.exe
deleted file mode 100644
index 4429d103..00000000
--- a/win32/bin/bin/objdump.exe
+++ /dev/null
Binary files differ
diff --git a/win32/bin/pe-parser.lua b/win32/bin/pe-parser.lua
new file mode 100644
index 00000000..30bb8390
--- /dev/null
+++ b/win32/bin/pe-parser.lua
@@ -0,0 +1,546 @@
1---------------------------------------------------------------------------------------
2-- Lua module to parse a Portable Executable (.exe , .dll, etc.) file and extract metadata.
3--
4-- Version 0.1, [copyright (c) 2013 - Thijs Schreijer](http://www.thijsschreijer.nl)
5-- @name pe-parser
6-- @class module
7
8local M = {}
9
10--- Table with named constants/flag-constants.
11-- Named elements can be looked up by their name in the `const` table. The sub tables are index by value.
12-- For flag fields the name is extended with `_flags`.
13-- @usage -- lookup descriptive name for the myobj.Magic value
14-- local desc = pe.const.Magic(myobj.Magic)
15--
16-- -- get list of flag names, indexed by flag values, for the Characteristics field
17-- local flag_list = pe.const.Characteristics_flags
18M.const = {
19 Magic = {
20 ["10b"] = "PE32",
21 ["20b"] = "PE32+",
22 },
23 Machine = {
24 ["0"] = "IMAGE_FILE_MACHINE_UNKNOWN",
25 ["1d3"] = "IMAGE_FILE_MACHINE_AM33",
26 ["8664"] = "IMAGE_FILE_MACHINE_AMD64",
27 ["1c0"] = "IMAGE_FILE_MACHINE_ARM",
28 ["1c4"] = "IMAGE_FILE_MACHINE_ARMNT",
29 ["aa64"] = "IMAGE_FILE_MACHINE_ARM64",
30 ["ebc"] = "IMAGE_FILE_MACHINE_EBC",
31 ["14c"] = "IMAGE_FILE_MACHINE_I386",
32 ["200"] = "IMAGE_FILE_MACHINE_IA64",
33 ["9041"] = "IMAGE_FILE_MACHINE_M32R",
34 ["266"] = "IMAGE_FILE_MACHINE_MIPS16",
35 ["366"] = "IMAGE_FILE_MACHINE_MIPSFPU",
36 ["466"] = "IMAGE_FILE_MACHINE_MIPSFPU16",
37 ["1f0"] = "IMAGE_FILE_MACHINE_POWERPC",
38 ["1f1"] = "IMAGE_FILE_MACHINE_POWERPCFP",
39 ["166"] = "IMAGE_FILE_MACHINE_R4000",
40 ["1a2"] = "IMAGE_FILE_MACHINE_SH3",
41 ["1a3"] = "IMAGE_FILE_MACHINE_SH3DSP",
42 ["1a6"] = "IMAGE_FILE_MACHINE_SH4",
43 ["1a8"] = "IMAGE_FILE_MACHINE_SH5",
44 ["1c2"] = "IMAGE_FILE_MACHINE_THUMB",
45 ["169"] = "IMAGE_FILE_MACHINE_WCEMIPSV2",
46 },
47 Characteristics_flags = {
48 ["1"] = "IMAGE_FILE_RELOCS_STRIPPED",
49 ["2"] = "IMAGE_FILE_EXECUTABLE_IMAGE",
50 ["4"] = "IMAGE_FILE_LINE_NUMS_STRIPPED",
51 ["8"] = "IMAGE_FILE_LOCAL_SYMS_STRIPPED",
52 ["10"] = "IMAGE_FILE_AGGRESSIVE_WS_TRIM",
53 ["20"] = "IMAGE_FILE_LARGE_ADDRESS_AWARE",
54 ["40"] = "Reserved for future use",
55 ["80"] = "IMAGE_FILE_BYTES_REVERSED_LO",
56 ["100"] = "IMAGE_FILE_32BIT_MACHINE",
57 ["200"] = "IMAGE_FILE_DEBUG_STRIPPED",
58 ["400"] = "IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP",
59 ["800"] = "IMAGE_FILE_NET_RUN_FROM_SWAP",
60 ["1000"] = "IMAGE_FILE_SYSTEM",
61 ["2000"] = "IMAGE_FILE_DLL",
62 ["4000"] = "IMAGE_FILE_UP_SYSTEM_ONLY",
63 ["8000"] = "IMAGE_FILE_BYTES_REVERSED_HI",
64 },
65 Subsystem = {
66 ["0"] = "IMAGE_SUBSYSTEM_UNKNOWN",
67 ["1"] = "IMAGE_SUBSYSTEM_NATIVE",
68 ["2"] = "IMAGE_SUBSYSTEM_WINDOWS_GUI",
69 ["3"] = "IMAGE_SUBSYSTEM_WINDOWS_CUI",
70 ["7"] = "IMAGE_SUBSYSTEM_POSIX_CUI",
71 ["9"] = "IMAGE_SUBSYSTEM_WINDOWS_CE_GUI",
72 ["a"] = "IMAGE_SUBSYSTEM_EFI_APPLICATION",
73 ["b"] = "IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER",
74 ["c"] = "IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER",
75 ["d"] = "IMAGE_SUBSYSTEM_EFI_ROM",
76 ["e"] = "IMAGE_SUBSYSTEM_XBOX",
77 },
78 DllCharacteristics_flags = {
79 ["40"] = "IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE",
80 ["80"] = "IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY",
81 ["100"] = "IMAGE_DLL_CHARACTERISTICS_NX_COMPAT",
82 ["200"] = "IMAGE_DLLCHARACTERISTICS_NO_ISOLATION",
83 ["400"] = "IMAGE_DLLCHARACTERISTICS_NO_SEH",
84 ["800"] = "IMAGE_DLLCHARACTERISTICS_NO_BIND",
85 ["2000"] = "IMAGE_DLLCHARACTERISTICS_WDM_DRIVER",
86 ["8000"] = "IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE",
87 },
88 Sections = {
89 Characteristics_flags = {
90 ["8"] = "IMAGE_SCN_TYPE_NO_PAD",
91 ["20"] = "IMAGE_SCN_CNT_CODE",
92 ["40"] = "IMAGE_SCN_CNT_INITIALIZED_DATA",
93 ["80"] = "IMAGE_SCN_CNT_UNINITIALIZED_ DATA",
94 ["100"] = "IMAGE_SCN_LNK_OTHER",
95 ["200"] = "IMAGE_SCN_LNK_INFO",
96 ["800"] = "IMAGE_SCN_LNK_REMOVE",
97 ["1000"] = "IMAGE_SCN_LNK_COMDAT",
98 ["8000"] = "IMAGE_SCN_GPREL",
99 ["20000"] = "IMAGE_SCN_MEM_PURGEABLE",
100 ["20000"] = "IMAGE_SCN_MEM_16BIT",
101 ["40000"] = "IMAGE_SCN_MEM_LOCKED",
102 ["80000"] = "IMAGE_SCN_MEM_PRELOAD",
103 ["100000"] = "IMAGE_SCN_ALIGN_1BYTES",
104 ["200000"] = "IMAGE_SCN_ALIGN_2BYTES",
105 ["300000"] = "IMAGE_SCN_ALIGN_4BYTES",
106 ["400000"] = "IMAGE_SCN_ALIGN_8BYTES",
107 ["500000"] = "IMAGE_SCN_ALIGN_16BYTES",
108 ["600000"] = "IMAGE_SCN_ALIGN_32BYTES",
109 ["700000"] = "IMAGE_SCN_ALIGN_64BYTES",
110 ["800000"] = "IMAGE_SCN_ALIGN_128BYTES",
111 ["900000"] = "IMAGE_SCN_ALIGN_256BYTES",
112 ["a00000"] = "IMAGE_SCN_ALIGN_512BYTES",
113 ["b00000"] = "IMAGE_SCN_ALIGN_1024BYTES",
114 ["c00000"] = "IMAGE_SCN_ALIGN_2048BYTES",
115 ["d00000"] = "IMAGE_SCN_ALIGN_4096BYTES",
116 ["e00000"] = "IMAGE_SCN_ALIGN_8192BYTES",
117 ["1000000"] = "IMAGE_SCN_LNK_NRELOC_OVFL",
118 ["2000000"] = "IMAGE_SCN_MEM_DISCARDABLE",
119 ["4000000"] = "IMAGE_SCN_MEM_NOT_CACHED",
120 ["8000000"] = "IMAGE_SCN_MEM_NOT_PAGED",
121 ["10000000"] = "IMAGE_SCN_MEM_SHARED",
122 ["20000000"] = "IMAGE_SCN_MEM_EXECUTE",
123 ["40000000"] = "IMAGE_SCN_MEM_READ",
124 ["80000000"] = "IMAGE_SCN_MEM_WRITE",
125 },
126 },
127
128}
129
130
131--- convert integer to HEX representation
132-- @param IN the number to convert to hex
133-- @param len the size to return, any result smaller will be prefixed by "0"s
134-- @return string containing hex representation
135function M.toHex(IN, len)
136 local B,K,OUT,I,D=16,"0123456789abcdef","",0
137 while IN>0 do
138 I=I+1
139 IN,D=math.floor(IN/B),math.fmod(IN,B)+1
140 OUT=string.sub(K,D,D)..OUT
141 end
142 len = len or string.len(OUT)
143 if len<1 then len = 1 end
144 return (string.rep("0",len) .. OUT):sub(-len,-1)
145end
146
147--- convert HEX to integer
148-- @param IN the string to convert to dec
149-- @return number in dec format
150function M.toDec(IN)
151 assert(type(IN)=="string")
152 local OUT = 0
153 IN = IN:lower()
154 while #IN > 0 do
155 local b = string.find("0123456789abcdef",IN:sub(1,1))
156 OUT = OUT * 16 + (b-1)
157 IN = IN:sub(2,-1)
158 end
159 return OUT
160end
161
162local function get_int(str)
163 -- convert a byte-sequence to an integer
164 assert(str)
165 local r = 0
166 for i = #str, 1, -1 do
167 r = r*256 + string.byte(str,i,i)
168 end
169 return r
170end
171
172local function get_hex(str)
173 -- convert a byte-sequence to a hex string
174 assert(str)
175 local r = ""
176 for i = #str, 1, -1 do
177 r = r .. M.toHex(string.byte(str,i,i),2)
178 end
179 while (#r > 1) and (r:sub(1,1) == "0") do
180 r = r:sub(2, -1)
181 end
182 return r
183end
184
185local function get_list(list, f, add_to)
186 -- list: list of tables with 'size' and 'name' and is_str
187 -- f: file to read from
188 -- add_to: table to add results to (optional)
189 local r = add_to or {}
190 for i, t in ipairs(list) do
191 assert(r[t.name] == nil, "Value for '"..t.name.."' already set")
192 local val,err = f:read(t.size) -- read specified size in bytes
193 val = val or "\0"
194 if t.is_str then -- entry is marked as a string value, read as such
195 for i = 1, #val do
196 if val:sub(i,i) == "\0" then
197 r[t.name] = val:sub(1,i-1)
198 break
199 end
200 end
201 r[t.name] = r[t.name] or val
202 else -- entry not marked, so always read as hex value
203 r[t.name] = get_hex(val)
204 end
205 end
206 return r
207end
208
209--- Calculates the fileoffset of a given RVA.
210-- This function is also available as a method on the parsed output table
211-- @param obj a parsed object (return value from `parse`)
212-- @param RVA an RVA value to convert to a fileoffset (either number or hex-string)
213-- @return fileoffset of the given RVA (number)
214M.get_fileoffset = function(obj, RVA)
215 -- given an object with a section table, and an RVA, it returns
216 -- the fileoffset for the data
217 if type(RVA)=="string" then RVA = M.toDec(RVA) end
218 local section
219 for i, s in ipairs(obj.Sections) do
220 if M.toDec(s.VirtualAddress) <= RVA and M.toDec(s.VirtualAddress) + M.toDec(s.VirtualSize) >= RVA then
221 section = s
222 break
223 end
224 end
225 if not section then return nil, "No match RVA with Section list, RVA out of bounds" end
226 return RVA - M.toDec(section.VirtualAddress) + M.toDec(section.PointerToRawData)
227end
228
229local function readstring(f)
230 -- reads a null-terminated string from the current file posistion
231 local name = ""
232 while true do
233 local c = f:read(1)
234 if c == "\0" then break end
235 name = name .. c
236 end
237 return name
238end
239
240--- Parses a file and extracts the information.
241-- All numbers are delivered as "string" types containing hex values, see `toHex` and `toDec` conversion functions.
242-- @return table with data, or nil + error
243-- @usage local pe = require("pe-parser")
244-- local obj = pe.parse("c:\lua\lua.exe")
245-- obj:dump()
246M.parse = function(target)
247
248 local list = { -- list of known architectures
249 [332] = "x86", -- IMAGE_FILE_MACHINE_I386
250 [512] = "x86_64", -- IMAGE_FILE_MACHINE_IA64
251 [34404] = "x86_64", -- IMAGE_FILE_MACHINE_AMD64
252 }
253
254 local f, err = io.open(target, "rb")
255 if not f then return nil, err end
256
257 local MZ = f:read(2)
258 if MZ ~= "MZ" then
259 f:close()
260 return nil, "Not a valid image"
261 end
262
263 f:seek("set", 60) -- position of PE header position
264 local peoffset = get_int(f:read(4)) -- read position of PE header
265
266 f:seek("set", peoffset) -- move to position of PE header
267 local out = get_list({
268 { size = 4,
269 name = "PEheader",
270 is_str = true },
271 { size = 2,
272 name = "Machine" },
273 { size = 2,
274 name = "NumberOfSections"},
275 { size = 4,
276 name = "TimeDateStamp" },
277 { size = 4,
278 name = "PointerToSymbolTable"},
279 { size = 4,
280 name = "NumberOfSymbols"},
281 { size = 2,
282 name = "SizeOfOptionalHeader"},
283 { size = 2,
284 name = "Characteristics"},
285 }, f)
286
287 if out.PEheader ~= "PE" then
288 f:close()
289 return nil, "Invalid PE header"
290 end
291 out.PEheader = nil -- remove it, has no value
292 out.dump = M.dump -- export dump function as a method
293
294 if M.toDec(out.SizeOfOptionalHeader) > 0 then
295 -- parse optional header; standard
296 get_list({
297 { size = 2,
298 name = "Magic" },
299 { size = 1,
300 name = "MajorLinkerVersion"},
301 { size = 1,
302 name = "MinorLinkerVersion"},
303 { size = 4,
304 name = "SizeOfCode"},
305 { size = 4,
306 name = "SizeOfInitializedData"},
307 { size = 4,
308 name = "SizeOfUninitializedData"},
309 { size = 4,
310 name = "AddressOfEntryPoint"},
311 { size = 4,
312 name = "BaseOfCode"},
313 }, f, out)
314 local plus = (out.Magic == "20b")
315 if not plus then -- plain PE32, not PE32+
316 get_list({
317 { size = 4,
318 name = "BaseOfData" },
319 }, f, out)
320 end
321 -- parse optional header; windows-fields
322 local plussize = 4
323 if plus then plussize = 8 end
324 get_list({
325 { size = plussize,
326 name = "ImageBase"},
327 { size = 4,
328 name = "SectionAlignment"},
329 { size = 4,
330 name = "FileAlignment"},
331 { size = 2,
332 name = "MajorOperatingSystemVersion"},
333 { size = 2,
334 name = "MinorOperatingSystemVersion"},
335 { size = 2,
336 name = "MajorImageVersion"},
337 { size = 2,
338 name = "MinorImageVersion"},
339 { size = 2,
340 name = "MajorSubsystemVersion"},
341 { size = 2,
342 name = "MinorSubsystemVersion"},
343 { size = 4,
344 name = "Win32VersionValue"},
345 { size = 4,
346 name = "SizeOfImage"},
347 { size = 4,
348 name = "SizeOfHeaders"},
349 { size = 4,
350 name = "CheckSum"},
351 { size = 2,
352 name = "Subsystem"},
353 { size = 2,
354 name = "DllCharacteristics"},
355 { size = plussize,
356 name = "SizeOfStackReserve"},
357 { size = plussize,
358 name = "SizeOfStackCommit"},
359 { size = plussize,
360 name = "SizeOfHeapReserve"},
361 { size = plussize,
362 name = "SizeOfHeapCommit"},
363 { size = 4,
364 name = "LoaderFlags"},
365 { size = 4,
366 name = "NumberOfRvaAndSizes"},
367 }, f, out)
368 -- Read data directory entries
369 for i = 1, M.toDec(out.NumberOfRvaAndSizes) do
370 out.DataDirectory = out.DataDirectory or {}
371 out.DataDirectory[i] = get_list({
372 { size = 4,
373 name = "VirtualAddress"},
374 { size = 4,
375 name = "Size"},
376 }, f)
377 end
378 for i, name in ipairs{"ExportTable", "ImportTable", "ResourceTable",
379 "ExceptionTable", "CertificateTable", "BaseRelocationTable",
380 "Debug", "Architecture", "GlobalPtr", "TLSTable",
381 "LoadConfigTable", "BoundImport", "IAT",
382 "DelayImportDescriptor", "CLRRuntimeHeader", "Reserved"} do
383 out.DataDirectory[name] = out.DataDirectory[i]
384 if out.DataDirectory[name] then out.DataDirectory[name].name = name end
385 end
386 end
387
388 -- parse section table
389 for i = 1, M.toDec(out.NumberOfSections) do
390 out.Sections = out.Sections or {}
391 out.Sections[i] = get_list({
392 { size = 8,
393 name = "Name",
394 is_str = true},
395 { size = 4,
396 name = "VirtualSize"},
397 { size = 4,
398 name = "VirtualAddress"},
399 { size = 4,
400 name = "SizeOfRawData"},
401 { size = 4,
402 name = "PointerToRawData"},
403 { size = 4,
404 name = "PointerToRelocations"},
405 { size = 4,
406 name = "PointerToLinenumbers"},
407 { size = 2,
408 name = "NumberOfRelocations"},
409 { size = 2,
410 name = "NumberOfLinenumbers"},
411 { size = 4,
412 name = "Characteristics"},
413 }, f)
414 end
415 -- we now have section data, so add RVA convertion method
416 out.get_fileoffset = M.get_fileoffset
417
418 -- get the import table
419 f:seek("set", out:get_fileoffset(out.DataDirectory.ImportTable.VirtualAddress))
420 local done = false
421 local cnt = 1
422 while not done do
423 local dll = get_list({
424 { size = 4,
425 name = "ImportLookupTableRVA"},
426 { size = 4,
427 name = "TimeDateStamp"},
428 { size = 4,
429 name = "ForwarderChain"},
430 { size = 4,
431 name = "NameRVA"},
432 { size = 4,
433 name = "ImportAddressTableRVA"},
434 }, f)
435 if M.toDec(dll.NameRVA) == 0 then
436 -- this is the final NULL entry, so we're done
437 done = true
438 else
439 -- store the import entry
440 out.DataDirectory.ImportTable[cnt] = dll
441 cnt = cnt + 1
442 end
443 end
444 -- resolve imported DLL names
445 for i, dll in ipairs(out.DataDirectory.ImportTable) do
446 f:seek("set", out:get_fileoffset(dll.NameRVA))
447 dll.Name = readstring(f)
448 end
449
450 f:close()
451 return out
452end
453
454-- pad a string (prefix) to a specific length
455local function pad(str, l, chr)
456 chr = chr or " "
457 l = l or 0
458 return string.rep(chr,l-#str)..str
459end
460
461--- Dumps the output parsed.
462-- This function is also available as a method on the parsed output table
463M.dump = function(obj)
464 local l = 0
465 for k,v in pairs(obj) do if #k > l then l = #k end end
466
467 for k,v in pairs(obj) do
468 if (M.const[k] and type(v)=="string") then
469 -- look up named value
470 print(k..string.rep(" ", l - #k + 1)..": "..M.const[k][v])
471 elseif M.const[k.."_flags"] then
472 -- flags should be listed
473 print(k..string.rep(" ", l - #k + 1)..": "..v.." (flag field)")
474 else
475 -- regular values
476 if type(v) == "number" then
477 print(k..string.rep(" ", l - #k + 1)..": "..v.." (dec)")
478 else
479 if (type(v)=="string") and (k ~= "DataDirectory") and (k ~= "Sections") then
480 print(k..string.rep(" ", l - #k + 1)..": "..v)
481 end
482 end
483 end
484 end
485
486 if obj.DataDirectory then
487 print("DataDirectory (RVA, size):")
488 for i, v in ipairs(obj.DataDirectory) do
489 print(" Entry "..M.toHex(i-1).." "..pad(v.VirtualAddress,8,"0").." "..pad(v.Size,8,"0").." "..v.name)
490 end
491 end
492
493 if obj.Sections then
494 print("Sections:")
495 print("idx name RVA VSize Offset RawSize")
496 for i, v in ipairs(obj.Sections) do
497 print(" "..i.." "..v.Name.. string.rep(" ",9-#v.Name)..pad(v.VirtualAddress,8,"0").." "..pad(v.VirtualSize,8,"0").." "..pad(v.PointerToRawData,8,"0").." "..pad(v.SizeOfRawData,8,"0"))
498 end
499 end
500
501 print("Imports:")
502 for i, dll in ipairs(obj.DataDirectory.ImportTable) do
503 print(" "..dll.Name)
504 end
505end
506
507--- Checks the msvcrt dll the binary was linked against.
508-- Mixing and matching dlls only works when they all are using the same runtime, if
509-- not unexpected errors will probably occur.
510-- Checks the binary provided and then traverses all imported dlls to find the msvcrt
511-- used (it will only look for the dlls in the same directory).
512-- @param infile binary file to check
513-- @return msvcrt name (uppercase, without extension) + file where the reference was found, or nil + error
514function M.msvcrt(infile)
515 local path, file = infile:match("(.+)\\(.+)$")
516 if not path then
517 path = ""
518 file = infile
519 else
520 path=path .. "\\"
521 end
522 local obj, err = M.parse(path..file)
523 if not obj then return obj, err end
524
525 for i, dll in ipairs(obj.DataDirectory.ImportTable) do
526 dll = dll.Name:upper()
527 local result = dll:match('(MSVCR%d*)%.DLL')
528 if not result then
529 result = dll:match('(MSVCRT)%.DLL')
530 end
531 -- success, found it return name + binary where it was found
532 if result then return result, infile end
533 end
534
535 -- not found, so traverse all imported dll's
536 for i, dll in ipairs(obj.DataDirectory.ImportTable) do
537 local rt, ref = M.msvcrt(path..dll.Name)
538 if rt then
539 return rt, ref -- found it
540 end
541 end
542
543 return nil, "No msvcrt found"
544end
545
546return M