diff options
| author | tobil4sk <tobil4sk@outlook.com> | 2024-07-30 15:39:41 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-07-30 11:39:41 -0300 |
| commit | 99c7267241e6ed0ef62123187acf7a5539a6c2db (patch) | |
| tree | 669b5aa0b7553e36d2179d1f57e9fbe6bcb7bb61 | |
| parent | f87568a7cba69e2c19411e7a2b73016c80a14a3b (diff) | |
| download | luarocks-99c7267241e6ed0ef62123187acf7a5539a6c2db.tar.gz luarocks-99c7267241e6ed0ef62123187acf7a5539a6c2db.tar.bz2 luarocks-99c7267241e6ed0ef62123187acf7a5539a6c2db.zip | |
Update pe-parser to version v0.6 (#1699)
| -rw-r--r-- | win32/pe-parser.lua | 103 |
1 files changed, 49 insertions, 54 deletions
diff --git a/win32/pe-parser.lua b/win32/pe-parser.lua index 1aff7107..ae603a59 100644 --- a/win32/pe-parser.lua +++ b/win32/pe-parser.lua | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | -- case of 64 bit fields (bit/flag fields). Pointer arithmetic is still done numerically, so for | 5 | -- case of 64 bit fields (bit/flag fields). Pointer arithmetic is still done numerically, so for |
| 6 | -- very large files this could lead to undefined results. Use with care! | 6 | -- very large files this could lead to undefined results. Use with care! |
| 7 | -- | 7 | -- |
| 8 | -- Version 0.4, [copyright (c) 2013-2015 Thijs Schreijer](http://www.thijsschreijer.nl) | 8 | -- Version 0.5, [copyright (c) 2013-2018 Thijs Schreijer](http://www.thijsschreijer.nl) |
| 9 | -- @name pe-parser | 9 | -- @name pe-parser |
| 10 | -- @class module | 10 | -- @class module |
| 11 | 11 | ||
| @@ -16,7 +16,7 @@ local M = {} | |||
| 16 | -- For flag fields the name is extended with `_flags`. | 16 | -- For flag fields the name is extended with `_flags`. |
| 17 | -- @usage -- lookup descriptive name for the myobj.Magic value | 17 | -- @usage -- lookup descriptive name for the myobj.Magic value |
| 18 | -- local desc = pe.const.Magic(myobj.Magic) | 18 | -- local desc = pe.const.Magic(myobj.Magic) |
| 19 | -- | 19 | -- |
| 20 | -- -- get list of flag names, indexed by flag values, for the Characteristics field | 20 | -- -- get list of flag names, indexed by flag values, for the Characteristics field |
| 21 | -- local flag_list = pe.const.Characteristics_flags | 21 | -- local flag_list = pe.const.Characteristics_flags |
| 22 | M.const = { | 22 | M.const = { |
| @@ -100,7 +100,7 @@ M.const = { | |||
| 100 | ["800"] = "IMAGE_SCN_LNK_REMOVE", | 100 | ["800"] = "IMAGE_SCN_LNK_REMOVE", |
| 101 | ["1000"] = "IMAGE_SCN_LNK_COMDAT", | 101 | ["1000"] = "IMAGE_SCN_LNK_COMDAT", |
| 102 | ["8000"] = "IMAGE_SCN_GPREL", | 102 | ["8000"] = "IMAGE_SCN_GPREL", |
| 103 | ["20000"] = "IMAGE_SCN_MEM_PURGEABLE", | 103 | ["10000"] = "IMAGE_SCN_MEM_PURGEABLE", |
| 104 | ["20000"] = "IMAGE_SCN_MEM_16BIT", | 104 | ["20000"] = "IMAGE_SCN_MEM_16BIT", |
| 105 | ["40000"] = "IMAGE_SCN_MEM_LOCKED", | 105 | ["40000"] = "IMAGE_SCN_MEM_LOCKED", |
| 106 | ["80000"] = "IMAGE_SCN_MEM_PRELOAD", | 106 | ["80000"] = "IMAGE_SCN_MEM_PRELOAD", |
| @@ -128,7 +128,7 @@ M.const = { | |||
| 128 | ["80000000"] = "IMAGE_SCN_MEM_WRITE", | 128 | ["80000000"] = "IMAGE_SCN_MEM_WRITE", |
| 129 | }, | 129 | }, |
| 130 | }, | 130 | }, |
| 131 | 131 | ||
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | 134 | ||
| @@ -143,7 +143,7 @@ function M.toHex(IN, len) | |||
| 143 | IN,D=math.floor(IN/B),math.fmod(IN,B)+1 | 143 | IN,D=math.floor(IN/B),math.fmod(IN,B)+1 |
| 144 | OUT=string.sub(K,D,D)..OUT | 144 | OUT=string.sub(K,D,D)..OUT |
| 145 | end | 145 | end |
| 146 | len = len or string.len(OUT) | 146 | len = len or #OUT |
| 147 | if len<1 then len = 1 end | 147 | if len<1 then len = 1 end |
| 148 | return (string.rep("0",len) .. OUT):sub(-len,-1) | 148 | return (string.rep("0",len) .. OUT):sub(-len,-1) |
| 149 | end | 149 | end |
| @@ -193,8 +193,8 @@ local function get_list(list, f, add_to) | |||
| 193 | local r = add_to or {} | 193 | local r = add_to or {} |
| 194 | for i, t in ipairs(list) do | 194 | for i, t in ipairs(list) do |
| 195 | assert(r[t.name] == nil, "Value for '"..t.name.."' already set") | 195 | assert(r[t.name] == nil, "Value for '"..t.name.."' already set") |
| 196 | local val,err = f:read(t.size) -- read specified size in bytes | 196 | local val = f:read(t.size) -- read specified size in bytes |
| 197 | val = val or "\0" | 197 | val = val or "\0" |
| 198 | if t.is_str then -- entry is marked as a string value, read as such | 198 | if t.is_str then -- entry is marked as a string value, read as such |
| 199 | for i = 1, #val do | 199 | for i = 1, #val do |
| 200 | if val:sub(i,i) == "\0" then | 200 | if val:sub(i,i) == "\0" then |
| @@ -248,25 +248,25 @@ end | |||
| 248 | -- local obj = pe.parse("c:\lua\lua.exe") | 248 | -- local obj = pe.parse("c:\lua\lua.exe") |
| 249 | -- obj:dump() | 249 | -- obj:dump() |
| 250 | M.parse = function(target) | 250 | M.parse = function(target) |
| 251 | 251 | ||
| 252 | local list = { -- list of known architectures | 252 | -- local list = { -- list of known architectures |
| 253 | [332] = "x86", -- IMAGE_FILE_MACHINE_I386 | 253 | -- [332] = "x86", -- IMAGE_FILE_MACHINE_I386 |
| 254 | [512] = "x86_64", -- IMAGE_FILE_MACHINE_IA64 | 254 | -- [512] = "x86_64", -- IMAGE_FILE_MACHINE_IA64 |
| 255 | [34404] = "x86_64", -- IMAGE_FILE_MACHINE_AMD64 | 255 | -- [34404] = "x86_64", -- IMAGE_FILE_MACHINE_AMD64 |
| 256 | } | 256 | -- } |
| 257 | 257 | ||
| 258 | local f, err = io.open(target, "rb") | 258 | local f, err = io.open(target, "rb") |
| 259 | if not f then return nil, err end | 259 | if not f then return nil, err end |
| 260 | 260 | ||
| 261 | local MZ = f:read(2) | 261 | local MZ = f:read(2) |
| 262 | if MZ ~= "MZ" then | 262 | if MZ ~= "MZ" then |
| 263 | f:close() | 263 | f:close() |
| 264 | return nil, "Not a valid image" | 264 | return nil, "Not a valid image" |
| 265 | end | 265 | end |
| 266 | 266 | ||
| 267 | f:seek("set", 60) -- position of PE header position | 267 | f:seek("set", 60) -- position of PE header position |
| 268 | local peoffset = get_int(f:read(4)) -- read position of PE header | 268 | local peoffset = get_int(f:read(4)) -- read position of PE header |
| 269 | 269 | ||
| 270 | f:seek("set", peoffset) -- move to position of PE header | 270 | f:seek("set", peoffset) -- move to position of PE header |
| 271 | local out = get_list({ | 271 | local out = get_list({ |
| 272 | { size = 4, | 272 | { size = 4, |
| @@ -287,14 +287,14 @@ M.parse = function(target) | |||
| 287 | { size = 2, | 287 | { size = 2, |
| 288 | name = "Characteristics"}, | 288 | name = "Characteristics"}, |
| 289 | }, f) | 289 | }, f) |
| 290 | 290 | ||
| 291 | if out.PEheader ~= "PE" then | 291 | if out.PEheader ~= "PE" then |
| 292 | f:close() | 292 | f:close() |
| 293 | return nil, "Invalid PE header" | 293 | return nil, "Invalid PE header" |
| 294 | end | 294 | end |
| 295 | out.PEheader = nil -- remove it, has no value | 295 | out.PEheader = nil -- remove it, has no value |
| 296 | out.dump = M.dump -- export dump function as a method | 296 | out.dump = M.dump -- export dump function as a method |
| 297 | 297 | ||
| 298 | if M.toDec(out.SizeOfOptionalHeader) > 0 then | 298 | if M.toDec(out.SizeOfOptionalHeader) > 0 then |
| 299 | -- parse optional header; standard | 299 | -- parse optional header; standard |
| 300 | get_list({ | 300 | get_list({ |
| @@ -388,7 +388,7 @@ M.parse = function(target) | |||
| 388 | if out.DataDirectory[name] then out.DataDirectory[name].name = name end | 388 | if out.DataDirectory[name] then out.DataDirectory[name].name = name end |
| 389 | end | 389 | end |
| 390 | end | 390 | end |
| 391 | 391 | ||
| 392 | -- parse section table | 392 | -- parse section table |
| 393 | for i = 1, M.toDec(out.NumberOfSections) do | 393 | for i = 1, M.toDec(out.NumberOfSections) do |
| 394 | out.Sections = out.Sections or {} | 394 | out.Sections = out.Sections or {} |
| @@ -416,9 +416,9 @@ M.parse = function(target) | |||
| 416 | name = "Characteristics"}, | 416 | name = "Characteristics"}, |
| 417 | }, f) | 417 | }, f) |
| 418 | end | 418 | end |
| 419 | -- we now have section data, so add RVA conversion method | 419 | -- we now have section data, so add RVA convertion method |
| 420 | out.get_fileoffset = M.get_fileoffset | 420 | out.get_fileoffset = M.get_fileoffset |
| 421 | 421 | ||
| 422 | -- get the import table | 422 | -- get the import table |
| 423 | f:seek("set", out:get_fileoffset(out.DataDirectory.ImportTable.VirtualAddress)) | 423 | f:seek("set", out:get_fileoffset(out.DataDirectory.ImportTable.VirtualAddress)) |
| 424 | local done = false | 424 | local done = false |
| @@ -450,7 +450,7 @@ M.parse = function(target) | |||
| 450 | f:seek("set", out:get_fileoffset(dll.NameRVA)) | 450 | f:seek("set", out:get_fileoffset(dll.NameRVA)) |
| 451 | dll.Name = readstring(f) | 451 | dll.Name = readstring(f) |
| 452 | end | 452 | end |
| 453 | 453 | ||
| 454 | f:close() | 454 | f:close() |
| 455 | return out | 455 | return out |
| 456 | end | 456 | end |
| @@ -467,10 +467,10 @@ end | |||
| 467 | M.dump = function(obj) | 467 | M.dump = function(obj) |
| 468 | local l = 0 | 468 | local l = 0 |
| 469 | for k,v in pairs(obj) do if #k > l then l = #k end end | 469 | for k,v in pairs(obj) do if #k > l then l = #k end end |
| 470 | 470 | ||
| 471 | for k,v in pairs(obj) do | 471 | for k,v in pairs(obj) do |
| 472 | if (M.const[k] and type(v)=="string") then | 472 | if (M.const[k] and type(v)=="string") then |
| 473 | -- look up named value | 473 | -- look up named value |
| 474 | print(k..string.rep(" ", l - #k + 1)..": "..M.const[k][v]) | 474 | print(k..string.rep(" ", l - #k + 1)..": "..M.const[k][v]) |
| 475 | elseif M.const[k.."_flags"] then | 475 | elseif M.const[k.."_flags"] then |
| 476 | -- flags should be listed | 476 | -- flags should be listed |
| @@ -486,14 +486,14 @@ M.dump = function(obj) | |||
| 486 | end | 486 | end |
| 487 | end | 487 | end |
| 488 | end | 488 | end |
| 489 | 489 | ||
| 490 | if obj.DataDirectory then | 490 | if obj.DataDirectory then |
| 491 | print("DataDirectory (RVA, size):") | 491 | print("DataDirectory (RVA, size):") |
| 492 | for i, v in ipairs(obj.DataDirectory) do | 492 | for i, v in ipairs(obj.DataDirectory) do |
| 493 | print(" Entry "..M.toHex(i-1).." "..pad(v.VirtualAddress,8,"0").." "..pad(v.Size,8,"0").." "..v.name) | 493 | print(" Entry "..M.toHex(i-1).." "..pad(v.VirtualAddress,8,"0").." "..pad(v.Size,8,"0").." "..v.name) |
| 494 | end | 494 | end |
| 495 | end | 495 | end |
| 496 | 496 | ||
| 497 | if obj.Sections then | 497 | if obj.Sections then |
| 498 | print("Sections:") | 498 | print("Sections:") |
| 499 | print("idx name RVA VSize Offset RawSize") | 499 | print("idx name RVA VSize Offset RawSize") |
| @@ -501,7 +501,7 @@ M.dump = function(obj) | |||
| 501 | 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")) | 501 | 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")) |
| 502 | end | 502 | end |
| 503 | end | 503 | end |
| 504 | 504 | ||
| 505 | print("Imports:") | 505 | print("Imports:") |
| 506 | for i, dll in ipairs(obj.DataDirectory.ImportTable) do | 506 | for i, dll in ipairs(obj.DataDirectory.ImportTable) do |
| 507 | print(" "..dll.Name) | 507 | print(" "..dll.Name) |
| @@ -515,7 +515,7 @@ end | |||
| 515 | -- used (it will only look for the dlls in the same directory). | 515 | -- used (it will only look for the dlls in the same directory). |
| 516 | -- @param infile binary file to check | 516 | -- @param infile binary file to check |
| 517 | -- @return msvcrt name (uppercase, without extension) + file where the reference was found, or nil + error | 517 | -- @return msvcrt name (uppercase, without extension) + file where the reference was found, or nil + error |
| 518 | function M.msvcrt(infile) | 518 | function M.msvcrt(infile) |
| 519 | local path, file = infile:match("(.+)\\(.+)$") | 519 | local path, file = infile:match("(.+)\\(.+)$") |
| 520 | if not path then | 520 | if not path then |
| 521 | path = "" | 521 | path = "" |
| @@ -525,24 +525,34 @@ function M.msvcrt(infile) | |||
| 525 | end | 525 | end |
| 526 | local obj, err = M.parse(path..file) | 526 | local obj, err = M.parse(path..file) |
| 527 | if not obj then return obj, err end | 527 | if not obj then return obj, err end |
| 528 | 528 | ||
| 529 | for i, dll in ipairs(obj.DataDirectory.ImportTable) do | 529 | for i, dll in ipairs(obj.DataDirectory.ImportTable) do |
| 530 | dll = dll.Name:upper() | 530 | dll = dll.Name:upper() |
| 531 | local result = dll:match('(MSVCR%d*D?)%.DLL') | 531 | local result = dll:match('(MSVCR%d*D?)%.DLL') |
| 532 | if not result then | 532 | if not result then |
| 533 | result = dll:match('(MSVCRTD?)%.DLL') | 533 | result = dll:match('(MSVCRTD?)%.DLL') |
| 534 | end | 534 | end |
| 535 | if not result then | 535 | if not result then |
| 536 | result = dll:match('(VCRUNTIME%d*D?)%.DLL') | 536 | result = dll:match('(VCRUNTIME%d*D?)%.DLL') |
| 537 | end | 537 | end |
| 538 | if not result then | ||
| 539 | result = dll:match('(UCRTBASED?)%.DLL') | ||
| 540 | end | ||
| 541 | if not result then | ||
| 542 | -- api-ms-win-crt-xxx also indicate the universal runtime | ||
| 543 | result = dll:match('(API%-MS%-WIN%-CRT%-RUNTIME%-)') | ||
| 544 | if result then | ||
| 545 | result = "UCRTBASE" | ||
| 546 | end | ||
| 547 | end | ||
| 538 | -- success, found it return name + binary where it was found | 548 | -- success, found it return name + binary where it was found |
| 539 | if result then return result, infile end | 549 | if result then return result, infile end |
| 540 | end | 550 | end |
| 541 | 551 | ||
| 542 | -- not found, so traverse all imported dll's | 552 | -- not found, so traverse all imported dll's |
| 543 | for i, dll in ipairs(obj.DataDirectory.ImportTable) do | 553 | for i, dll in ipairs(obj.DataDirectory.ImportTable) do |
| 544 | local rt, ref = M.msvcrt(path..dll.Name) | 554 | local rt, ref = M.msvcrt(path..dll.Name) |
| 545 | if rt then | 555 | if rt then |
| 546 | return rt, ref -- found it | 556 | return rt, ref -- found it |
| 547 | end | 557 | end |
| 548 | end | 558 | end |
| @@ -550,19 +560,4 @@ function M.msvcrt(infile) | |||
| 550 | return nil, "No msvcrt found" | 560 | return nil, "No msvcrt found" |
| 551 | end | 561 | end |
| 552 | 562 | ||
| 553 | function M.get_architecture(program) | ||
| 554 | -- detect processor arch interpreter was compiled for | ||
| 555 | local proc = (M.parse(program) or {}).Machine | ||
| 556 | if not proc then | ||
| 557 | return nil, "Could not detect processor architecture used in "..program | ||
| 558 | end | ||
| 559 | proc = M.const.Machine[proc] -- collect name from constant value | ||
| 560 | if proc == "IMAGE_FILE_MACHINE_I386" then | ||
| 561 | proc = "x86" | ||
| 562 | else | ||
| 563 | proc = "x86_64" | ||
| 564 | end | ||
| 565 | return proc | ||
| 566 | end | ||
| 567 | |||
| 568 | return M | 563 | return M |
