aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--install.bat106
-rw-r--r--win32/bin/bin/objdump.exebin2247694 -> 0 bytes
-rw-r--r--win32/bin/pe-parser.lua546
3 files changed, 576 insertions, 76 deletions
diff --git a/install.bat b/install.bat
index 0f34276d..f6601aa8 100644
--- a/install.bat
+++ b/install.bat
@@ -31,6 +31,9 @@ local REGISTRY = false
31--- 31---
32-- Some helpers 32-- Some helpers
33-- 33--
34
35local pe = assert(loadfile(".\\bin\\pe-parser.lua"))()
36
34local function die(message) 37local function die(message)
35 if message then print(message) end 38 if message then print(message) end
36 print() 39 print()
@@ -275,71 +278,35 @@ local function look_for_headers (directory)
275 return false 278 return false
276end 279end
277 280
278-- Checks a binary file for the runtime dll used by it. If nu runtime is found, it returns an
279-- array of dll's is depends upon.
280-- result: string = runtime used, table = list of dll's depended upon, nil = nothing found.
281local function get_file_runtime(p,f) -- path, filename
282 local infile = p.."\\"..f
283 local outfile = "output.txt"
284 local content
285 -- analyze binary
286 if exec([[.\bin\bin\objdump -x "]]..infile..[[" > ]]..outfile..[[ 2<&1]]) then
287 -- read temp file
288 local fh = io.open(outfile)
289 content = fh:read("*a")
290 fh:close()
291 end
292 -- delete temp file
293 os.remove(outfile)
294 if not content then
295 print(" Failed to analyze "..infile.." for the runtime used")
296 return nil
297 end
298
299 -- lookup
300 content = content:upper()
301 local result = content:match('DLL NAME%: (MSVCR%d*)%.DLL')
302 if not result then
303 result = content:match('DLL NAME%: (MSVCRT)%.DLL')
304 end
305
306 if result then
307 print(" "..f.." uses "..tostring(result)..".DLL as runtime")
308 else
309 print(" No runtime found for "..f)
310 -- so; create a list of dll's this file is depending upon, next level of the tree
311 result = {}
312 for name in content:gmatch("DLL NAME%: (.-%.DLL)") do
313 --print("found dll:", name)
314 table.insert(result, name)
315 end
316 end
317 return result
318end
319 281
320local function get_runtime() 282local function get_runtime()
321 -- first check interpreter 283 local f
322 vars.LUA_RUNTIME = get_file_runtime(vars.LUA_BINDIR, vars.LUA_INTERPRETER) 284 vars.LUA_RUNTIME, f = pe.msvcrt(vars.LUA_BINDIR.."\\"..vars.LUA_INTERPRETER)
323 if type(vars.LUA_RUNTIME) == "table" then
324 -- a table with dll's depended upon was returned, check this list
325 -- note: we only check 1 level deep
326 for _,dll in ipairs(vars.LUA_RUNTIME) do
327 local t = get_file_runtime(vars.LUA_BINDIR, dll)
328 if type(t) == "string" then
329 -- found it
330 vars.LUA_RUNTIME = t
331 break
332 end
333 end
334 end
335 if type(vars.LUA_RUNTIME) ~= "string" then 285 if type(vars.LUA_RUNTIME) ~= "string" then
336 -- analysis failed, issue a warning 286 -- analysis failed, issue a warning
337 vars.LUA_RUNTIME = "MSVCR80" 287 vars.LUA_RUNTIME = "MSVCR80"
338 print("*** WARNING ***: could not analyse the runtime used, defaulting to "..vars.LUA_RUNTIME) 288 print("*** WARNING ***: could not analyse the runtime used, defaulting to "..vars.LUA_RUNTIME)
289 else
290 print(" "..f.." uses "..vars.LUA_RUNTIME..".DLL as runtime")
339 end 291 end
340 return true 292 return true
341end 293end
342 294
295local function get_architecture()
296 -- detect processor arch interpreter was compiled for
297 local proc = (pe.parse(vars.LUA_BINDIR.."\\"..vars.LUA_INTERPRETER) or {}).Machine
298 if not proc then
299 die("Could not detect processor architecture used in "..vars.LUA_INTERPRETER)
300 end
301 proc = pe.const.Machine[proc] -- collect name from constant value
302 if proc == "IMAGE_FILE_MACHINE_I386" then
303 proc = "x86"
304 else
305 proc = "x86_64"
306 end
307 return proc
308end
309
343local function look_for_lua_install () 310local function look_for_lua_install ()
344 print("Looking for Lua interpreter") 311 print("Looking for Lua interpreter")
345 local directories = { [[c:\lua5.1.2]], [[c:\lua]], [[c:\kepler\1.1]] } 312 local directories = { [[c:\lua5.1.2]], [[c:\lua]], [[c:\kepler\1.1]] }
@@ -388,26 +355,6 @@ local function look_for_lua_install ()
388 return false 355 return false
389end 356end
390 357
391local function get_architecture()
392 -- detect processor arch
393 local tmpname = [[.\_architect_temp.txt]]
394 local cmd = [[REG.exe Query HKLM\Hardware\Description\System\CentralProcessor\0 >"]]..tmpname.. [["]]
395 if not exec(cmd) then
396 die("Could not detect processor architecture")
397 end
398 local f = io.open(tmpname, "r")
399 local proc = f:read('*a')
400 f:close()
401 os.remove(tmpname)
402
403 if proc:match("x86") then
404 proc = "x86"
405 else
406 proc = "x86_64"
407 end
408 return proc
409end
410
411--- 358---
412-- Poor man's command-line parsing 359-- Poor man's command-line parsing
413local config = {} 360local config = {}
@@ -449,7 +396,6 @@ vars.LIBDIR = vars.FULL_PREFIX
449vars.LUADIR = S"$FULL_PREFIX\\lua" 396vars.LUADIR = S"$FULL_PREFIX\\lua"
450vars.INCDIR = S"$FULL_PREFIX\\include" 397vars.INCDIR = S"$FULL_PREFIX\\include"
451vars.LUA_SHORTV = vars.LUA_VERSION:gsub("%.", "") 398vars.LUA_SHORTV = vars.LUA_VERSION:gsub("%.", "")
452vars.UNAME_M = get_architecture()
453 399
454if not look_for_lua_install() then 400if not look_for_lua_install() then
455 print("Could not find Lua. Will install its own copy.") 401 print("Could not find Lua. Will install its own copy.")
@@ -464,7 +410,9 @@ if not look_for_lua_install() then
464 vars.LUA_INCDIR = vars.INCDIR 410 vars.LUA_INCDIR = vars.INCDIR
465 vars.LUA_LIBNAME = "lua5.1.lib" 411 vars.LUA_LIBNAME = "lua5.1.lib"
466 vars.LUA_RUNTIME = "MSVCR80" 412 vars.LUA_RUNTIME = "MSVCR80"
413 vars.UNAME_M = "x86"
467else 414else
415 vars.UNAME_M = get_architecture()
468 print(S[[ 416 print(S[[
469 417
470Will configure LuaRocks with the following paths: 418Will configure LuaRocks with the following paths:
@@ -668,8 +616,14 @@ if REGISTRY then
668 exec( S[[lua5.1\bin\lua5.1.exe "$FULL_PREFIX\LuaRocks.reg.lua" "$FULL_PREFIX\LuaRocks.reg.template"]] ) 616 exec( S[[lua5.1\bin\lua5.1.exe "$FULL_PREFIX\LuaRocks.reg.lua" "$FULL_PREFIX\LuaRocks.reg.template"]] )
669 exec( S"$FULL_PREFIX\\LuaRocks.reg" ) 617 exec( S"$FULL_PREFIX\\LuaRocks.reg" )
670end 618end
619
620-- ***********************************************************
621-- Cleanup
622-- ***********************************************************
671-- remove regsitry related files, no longer needed 623-- remove regsitry related files, no longer needed
672exec( S[[del "$FULL_PREFIX\LuaRocks.reg.*" > nul]] ) 624exec( S[[del "$FULL_PREFIX\LuaRocks.reg.*" > nul]] )
625-- remove pe-parser module
626exec( S[[del "$FULL_PREFIX\pe-parser.lua" > nul]] )
673 627
674-- *********************************************************** 628-- ***********************************************************
675-- Exit handlers 629-- Exit handlers
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