From 52411d0a2e53933e7a15000414ff0b1b19fcf651 Mon Sep 17 00:00:00 2001
From: Přemysl Janouch
Date: Wed, 18 Jan 2017 00:25:03 +0100
Subject: elf.lua: decode program headers
---
plugins/elf.lua | 195 +++++++++++++++++++++++++++++++++++++-------------------
1 file changed, 130 insertions(+), 65 deletions(-)
(limited to 'plugins')
diff --git a/plugins/elf.lua b/plugins/elf.lua
index 6fa2223..f891af7 100644
--- a/plugins/elf.lua
+++ b/plugins/elf.lua
@@ -20,6 +20,82 @@ local detect = function (c)
return c:read (4) == "\x7FELF"
end
+local ph_type_table = {
+ [0] = "unused segment",
+ [1] = "loadable segment",
+ [2] = "dynamic linking information",
+ [3] = "interpreter pathname",
+ [4] = "auxiliary information",
+ [5] = "reserved",
+ [6] = "the program header table itself",
+ [7] = "the thread-local storage template"
+}
+
+local xform_ph_flags = function (u32)
+ local info = {}
+ if u32 & 4 ~= 0 then table.insert (info, "read") end
+ if u32 & 2 ~= 0 then table.insert (info, "write") end
+ if u32 & 1 ~= 0 then table.insert (info, "execute") end
+
+ local junk = u32 & ~7
+ if junk ~= 0 then
+ table.insert (info, ("unknown: %#x"):format (junk))
+ end
+
+ if #info == 0 then return 0 end
+
+ local result = info[1]
+ for i = 2, #info do result = result .. ", " .. info[i] end
+ return result
+end
+
+local decode_ph = function (elf, c)
+ local ph = {}
+ ph.type = c:u32 ("type: %s", function (u32)
+ -- TODO: there are more known weird values and ranges
+ name = ph_type_table[u32]
+ if name then return name end
+ return "unknown: %#x", u32
+ end)
+ if elf.class == 2 then ph.flags = c:u32 ("flags: %s", xform_ph_flags) end
+ ph.offset = elf.uwide (c, "offset in file: %#x")
+ ph.vaddr = elf.uwide (c, "virtual address: %#x")
+ ph.paddr = elf.uwide (c, "physical address: %#x")
+ ph.filesz = elf.uwide (c, "size in file: %d")
+ ph.memsz = elf.uwide (c, "sise in memory: %d")
+ if elf.class == 1 then ph.flags = c:u32 ("flags: %s", xform_ph_flags) end
+ ph.align = elf.uwide (c, "alignment: %d")
+end
+
+local decode_sh = function (elf, c)
+ -- TODO
+end
+
+local abi_table = {
+ [0] = "UNIX System V ABI",
+ [1] = "HP-UX operating system",
+ [2] = "NetBSD",
+ [3] = "GNU/Linux",
+ [4] = "GNU/Hurd",
+ [6] = "Solaris",
+ [7] = "AIX",
+ [8] = "IRIX",
+ [9] = "FreeBSD",
+ [10] = "TRU64 UNIX",
+ [11] = "Novell Modesto",
+ [12] = "OpenBSD",
+ [13] = "OpenVMS",
+ [14] = "Hewlett-Packard Non-Stop Kernel",
+ [15] = "AROS",
+ [16] = "FenixOS",
+ [17] = "Nuxi CloudABI",
+ [64] = "Bare-metal TMS320C6000",
+ [64] = "AMD HSA runtime",
+ [65] = "Linux TMS320C6000",
+ [97] = "ARM",
+ [255] = "Standalone (embedded) application"
+}
+
local type_table = {
[0] = "no file type",
[1] = "relocatable file",
@@ -210,74 +286,20 @@ local machine_table = {
[247] = "Linux kernel bpf virtual machine"
}
-local decode32 = function (c)
- -- TODO: like the 64-bit version, maybe try to merge the code somehow
-end
-
-local decode64 = function (c)
- local type = c:u16 ("type of file: %s", function (u16)
- name = type_table[u16]
- if name then return name end
- return "unknown: %d", u16
- end)
- local machine = c:u16 ("required architecture: %s", function (u16)
- name = machine_table[u16]
- if name then return name end
- return "unknown: %d", u16
- end)
- local version = c:u32 ("version: %d")
- local entry = c:u64 ("program entry address: %#x")
- local ph_offset = c:u64 ("program header table offset: %#x")
- local sh_offset = c:u64 ("section header table offset: %#x")
- local flags = c:u32 ("processor-specific flags: %#x")
- local eh_size = c:u16 ("ELF header size: %d")
- local ph_entry_size = c:u16 ("program header size: %d")
- local ph_number = c:u16 ("program header count: %d")
- local sh_entry_size = c:u16 ("section header size: %d")
- local sh_number = c:u16 ("section header count: %d")
- local sh_string_index = c:u16 ("section header index for strings: %d")
-
- -- TODO: decode all sections as well, see man 5 elf,
- -- /usr/include/elf.h and /usr/include/llvm/Support/ELF.h
-end
-
-local abi_table = {
- [0] = "UNIX System V ABI",
- [1] = "HP-UX operating system",
- [2] = "NetBSD",
- [3] = "GNU/Linux",
- [4] = "GNU/Hurd",
- [6] = "Solaris",
- [7] = "AIX",
- [8] = "IRIX",
- [9] = "FreeBSD",
- [10] = "TRU64 UNIX",
- [11] = "Novell Modesto",
- [12] = "OpenBSD",
- [13] = "OpenVMS",
- [14] = "Hewlett-Packard Non-Stop Kernel",
- [15] = "AROS",
- [16] = "FenixOS",
- [17] = "Nuxi CloudABI",
- [64] = "Bare-metal TMS320C6000",
- [64] = "AMD HSA runtime",
- [65] = "Linux TMS320C6000",
- [97] = "ARM",
- [255] = "Standalone (embedded) application"
-}
-
local decode = function (c)
+ assert (c.position == 1)
if not detect (c ()) then error ("not an ELF file") end
local p = c.position, c:read (4)
c (p, p + 3):mark ("ELF magic")
- local class = c:u8 ("ELF class: %s", function (u8)
+ local elf = {}
+ elf.class = c:u8 ("ELF class: %s", function (u8)
if u8 == 1 then return "32-bit" end
if u8 == 2 then return "64-bit" end
return "invalid: %d", u8
end)
- local data = c:u8 ("ELF data: %s", function (u8)
+ elf.data = c:u8 ("ELF data: %s", function (u8)
if u8 == 1 then
c.endianity = "le"
return "little-endian"
@@ -288,22 +310,65 @@ local decode = function (c)
end
return "invalid: %d", u8
end)
- local version = c:u8 ("ELF version: %d")
- local abi = c:u8 ("OS ABI: %s", function (u8)
+ elf.version = c:u8 ("ELF version: %d")
+ elf.abi = c:u8 ("OS ABI: %s", function (u8)
name = abi_table[u8]
if name then return name end
return "unknown: %d", u8
end)
- local abi_version = c:u8 ("OS ABI version: %d")
+ elf.abi_version = c:u8 ("OS ABI version: %d")
-- The padding is reserved, no big sense in marking it
local padding = c:read (7)
- -- We cannot decode anything further as we don't know how
- if data ~= 1 and data ~= 2 then return end
+ -- We cannot decode anything further if we don't know endianity
+ if elf.data ~= 1 and elf.data ~= 2 then return end
+
+ -- And the same applies to the class
+ if elf.class == 1 then elf.uwide = c.u32
+ elseif elf.class == 2 then elf.uwide = c.u64
+ else return end
+
+ elf.type = c:u16 ("type of file: %s", function (u16)
+ name = type_table[u16]
+ if name then return name end
+ return "unknown: %d", u16
+ end)
+ elf.machine = c:u16 ("required architecture: %s", function (u16)
+ name = machine_table[u16]
+ if name then return name end
+ return "unknown: %d", u16
+ end)
+ elf.version = c:u32 ("version: %d")
+ elf.entry = elf.uwide (c, "program entry address: %#x")
+ elf.ph_offset = elf.uwide (c, "program header table offset: %#x")
+ elf.sh_offset = elf.uwide (c, "section header table offset: %#x")
+ elf.flags = c:u32 ("processor-specific flags: %#x")
+ elf.eh_size = c:u16 ("ELF header size: %d")
+ c (1, elf.eh_size):mark ("ELF header")
+
+ elf.ph_entry_size = c:u16 ("program header size: %d")
+ elf.ph_number = c:u16 ("program header count: %d")
+ elf.sh_entry_size = c:u16 ("section header size: %d")
+ elf.sh_number = c:u16 ("section header count: %d")
+ elf.sh_string_index = c:u16 ("section header index for strings: %d")
+
+ -- TODO: decode all headers as well, see man 5 elf,
+ -- /usr/include/elf.h and /usr/include/llvm/Support/ELF.h
+ for i = 1, elf.ph_number do
+ local start = elf.ph_offset + (i - 1) * elf.ph_entry_size
+ local ph = c (1 + start, start + elf.ph_entry_size)
+ ph:mark ("ELF program header %d", i - 1)
+ decode_ph (elf, ph)
+ end
- if class == 1 then decode32 (c) end
- if class == 2 then decode64 (c) end
+ -- TODO: we will need to decode "sh_string_index" first to get names
+ for i = 1, elf.sh_number do
+ local start = elf.sh_offset + (i - 1) * elf.sh_entry_size
+ local sh = c (1 + start, start + elf.sh_entry_size)
+ sh:mark ("ELF section header %d", i - 1)
+ decode_sh (elf, sh)
+ end
end
hex.register { type="elf", detect=detect, decode=decode }
--
cgit v1.2.3-70-g09d2