├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── assets ├── screenshot.png └── table.png ├── notes.md └── src ├── data.rs ├── helpers.rs ├── main.rs └── render.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "cc" 5 | version = "1.0.60" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "ef611cc68ff783f18535d77ddd080185275713d852c4f5cbb6122c462a7a825c" 8 | 9 | [[package]] 10 | name = "cfg-if" 11 | version = "0.1.10" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 14 | 15 | [[package]] 16 | name = "elfedit" 17 | version = "0.2.0" 18 | dependencies = [ 19 | "pancurses", 20 | ] 21 | 22 | [[package]] 23 | name = "libc" 24 | version = "0.2.79" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" 27 | 28 | [[package]] 29 | name = "log" 30 | version = "0.4.11" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 33 | dependencies = [ 34 | "cfg-if", 35 | ] 36 | 37 | [[package]] 38 | name = "ncurses" 39 | version = "5.99.0" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "15699bee2f37e9f8828c7b35b2bc70d13846db453f2d507713b758fabe536b82" 42 | dependencies = [ 43 | "cc", 44 | "libc", 45 | "pkg-config", 46 | ] 47 | 48 | [[package]] 49 | name = "pancurses" 50 | version = "0.16.1" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "d3058bc37c433096b2ac7afef1c5cdfae49ede0a4ffec3dfc1df1df0959d0ff0" 53 | dependencies = [ 54 | "libc", 55 | "log", 56 | "ncurses", 57 | "pdcurses-sys", 58 | "winreg", 59 | ] 60 | 61 | [[package]] 62 | name = "pdcurses-sys" 63 | version = "0.7.1" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "084dd22796ff60f1225d4eb6329f33afaf4c85419d51d440ab6b8c6f4529166b" 66 | dependencies = [ 67 | "cc", 68 | "libc", 69 | ] 70 | 71 | [[package]] 72 | name = "pkg-config" 73 | version = "0.3.18" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" 76 | 77 | [[package]] 78 | name = "winapi" 79 | version = "0.3.9" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 82 | dependencies = [ 83 | "winapi-i686-pc-windows-gnu", 84 | "winapi-x86_64-pc-windows-gnu", 85 | ] 86 | 87 | [[package]] 88 | name = "winapi-i686-pc-windows-gnu" 89 | version = "0.4.0" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 92 | 93 | [[package]] 94 | name = "winapi-x86_64-pc-windows-gnu" 95 | version = "0.4.0" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 98 | 99 | [[package]] 100 | name = "winreg" 101 | version = "0.5.1" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a" 104 | dependencies = [ 105 | "winapi", 106 | ] 107 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "elfedit" 3 | version = "0.2.0" 4 | authors = ["TheThirdOne"] 5 | 6 | [dependencies] 7 | pancurses = "0.16.0" 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Benjamin Landers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Elf Edit 2 | 3 | A simple hex editing tool with highlighting and human readable form on the sidebar for ELF (Executable and linking format) files. 4 | 5 | ![Screenshot showing a tiny binary](./assets/screenshot.png) 6 | 7 | If you trust me you can get a Linux binary from the [releases](https://github.com/TheThirdOne/elf-edit/releases). 8 | 9 | ## Installation 10 | 11 | Before attempting to install this, you'll need to have cargo and rust installed, but with that done, it should be a simple install. 12 | 13 | ``` 14 | git clone https://github.com/TheThirdOne/elf-edit/ 15 | cd elf-edit 16 | cargo install 17 | cargo run fileToInspect 18 | ``` 19 | -------------------------------------------------------------------------------- /assets/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheThirdOne/elf-edit/bbb30640986b645ae5bc6386897c35265ce32819/assets/screenshot.png -------------------------------------------------------------------------------- /assets/table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheThirdOne/elf-edit/bbb30640986b645ae5bc6386897c35265ce32819/assets/table.png -------------------------------------------------------------------------------- /notes.md: -------------------------------------------------------------------------------- 1 | Resources on ELF: 2 | 3 | - [Making teensy programs](http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html) 4 | - goes down to 45 bytes 5 | - [Undestanding ELF](https://medium.com/@MrJamesFisher/understanding-the-elf-4bd60daac571) 6 | - Good high level overview / motivation explaner 7 | - [Overview of ELF and how to inspect it using tools](https://linux-audit.com/elf-binaries-on-linux-understanding-and-analysis/) 8 | - [Overview of basic format](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format) 9 | - [Spec for 64 bit](https://www.uclibc.org/docs/elf-64-gen.pdf) 10 | - has 64 bit program section format 11 | - [Full spec](http://docs.oracle.com/cd/E23824_01/pdf/819-0690.pdf) - includes a lot of extra stuff we don't need 12 | Overview of ELF: 13 | - Used for linking, dynamic libraries, executables 14 | - Format 15 | - General format is File header, ... (normally nothing here), Program Headers (not needed for dyn. lib. or object files), ... (data, code, etc), section headers (not needed for executables) 16 | - ![Format](./assets/table.png) 17 | 18 | Assembly: 19 | 20 | ```asm 21 | ; tiny.asm 22 | BITS 64 23 | GLOBAL _start 24 | SECTION .bss 25 | str:resb 1 26 | SECTION .text 27 | _start: 28 | mov [str], byte 10 29 | mov edx,1 ;message length 30 | mov ecx,str ;message to write 31 | mov ebx,1 ;file descriptor (stdout) 32 | mov eax,4 ;system call number (sys_write) 33 | int 0x80 ;call kernel 34 | mov eax, 1 35 | mov ebx, 42 36 | int 0x80 37 | ``` 38 | -------------------------------------------------------------------------------- /src/data.rs: -------------------------------------------------------------------------------- 1 | 2 | pub struct ELFinfo { 3 | pub bit_class: u8, 4 | pub endianess: u8, 5 | pub version: u8, 6 | pub abi: u8, 7 | pub file_type: u16, 8 | pub arch: u16, 9 | pub entry: u64, 10 | pub prog_head:u64, 11 | pub sect_head:u64, 12 | pub flags:u32, 13 | pub size:u16, 14 | pub prog_size:u16, 15 | pub prog_num: u16, 16 | pub sect_size:u16, 17 | pub sect_num: u16, 18 | pub shs_table_index:u16, 19 | pub progs:Vec, 20 | pub sects:Vec, 21 | pub shstr:STRTAB, 22 | pub strtabs:Vec, 23 | pub symtab: SYMTAB, 24 | pub symbols:Vec, 25 | pub reltabs: Vec, 26 | pub msg:String, 27 | pub needs_redraw: bool, 28 | } 29 | pub struct STRTAB { 30 | pub offset: u64, 31 | pub size: u64 32 | } 33 | 34 | pub struct SYMTAB { 35 | pub offset: u64, 36 | pub size: u64, 37 | pub entry_size: u64, 38 | pub link: u32, 39 | pub ei: u32 40 | } 41 | 42 | pub struct SYMBOL { 43 | pub name: u32, 44 | pub name_str:String, 45 | pub info: u8, 46 | pub other: u8, //used for visibility 47 | pub shndx: u16, 48 | pub value: u64, 49 | pub size: u64 50 | } 51 | 52 | pub struct RELTAB { 53 | pub offset: u64, 54 | pub size: u64, 55 | pub entry_size: u64, 56 | pub link: u32, 57 | pub ei: u32, 58 | pub rels: Vec 59 | } 60 | 61 | pub struct REL { 62 | pub offset: u64, 63 | pub info: u64, 64 | pub addend: i64 65 | } 66 | 67 | pub struct ProgHead { 68 | pub typ: u32, 69 | pub flags: u32, 70 | pub offset: u64, 71 | pub virt_addr: u64, 72 | pub file_size: u64, 73 | pub mem_size: u64, 74 | pub align: u64 75 | } 76 | 77 | pub struct SectHead { 78 | pub name: u32, 79 | pub name_str: String, 80 | pub typ: u32, 81 | pub flags: u64, 82 | pub virt_addr: u64, 83 | pub offset: u64, 84 | pub file_size: u64, 85 | pub sect_index: u32, 86 | pub extra_info: u32, 87 | pub align: u64, 88 | pub entry_size:u64 89 | } -------------------------------------------------------------------------------- /src/helpers.rs: -------------------------------------------------------------------------------- 1 | pub fn get_multibyte_data(data: &[u8], little_endian: bool) -> u64{ 2 | let mut sum : u64 = 0; 3 | if little_endian { 4 | for i in 1..(data.len()+1) { 5 | sum = sum*256 + (data[data.len()-i] as u64); 6 | } 7 | } else { 8 | for i in 0..data.len() { 9 | sum = sum*256 + (data[i] as u64); 10 | } 11 | } 12 | sum 13 | } 14 | 15 | pub fn get_null_string(data: &[u8],mut i: usize) -> String{ 16 | let mut tmp = "".to_owned(); 17 | while i < data.len() && data[i] != 0 && i < data.len(){ 18 | tmp.push(data[i] as char); 19 | i += 1; 20 | } 21 | tmp 22 | } 23 | 24 | pub trait SetAt { 25 | fn set_at(&mut self,u8,&mut Cursor); 26 | } 27 | impl SetAt for Vec { 28 | fn set_at(&mut self, val: u8, cursor: &mut Cursor){ 29 | if cursor.index == cursor.length-1 { 30 | self.push(val*16); // move into the upper half of the new byte 31 | cursor.length += 2; 32 | cursor.index += 1; 33 | return; 34 | } 35 | if cursor.index % 2 == 1 { 36 | self[cursor.index/2] = (self[cursor.index/2]&0xF0) + val; 37 | }else{ 38 | self[cursor.index/2] = (self[cursor.index/2]&0x0F) + val*16; 39 | } 40 | cursor.index += 1; 41 | } 42 | } 43 | 44 | pub struct Cursor { 45 | pub index: usize, 46 | pub length: usize, 47 | pub offset: usize 48 | } 49 | 50 | impl Cursor { 51 | pub fn mv(&mut self, x:i32, y:i32){ 52 | let delta = x + y*32; 53 | if delta > 0 { 54 | if self.index + delta as usize>= self.length { 55 | self.index = self.length-1 56 | } else { 57 | self.index += delta as usize; 58 | } 59 | } 60 | if self.index > (-delta) as usize { 61 | self.index -= (-delta) as usize; 62 | } else { 63 | self.index = 0; 64 | } 65 | } 66 | pub fn x(&self) -> usize { 67 | (if self.index % 32 >= 16{1}else{0} + ((self.index)%32)/2*3+self.index%2 + 10) 68 | } 69 | pub fn y(&self) -> usize { 70 | self.index/32 71 | } 72 | pub fn update_offset(&mut self, height: usize) -> bool{ 73 | if self.offset > self.y() || self.y() - self.offset > height{ 74 | let offset = if self.y() < height/2 { 75 | 0 76 | } else if self.length/32-self.y() < height/2 { 77 | if self.length/32 > height {self.length/32 - height} else {0} 78 | } else { 79 | self.y()-height/2 80 | }; 81 | if offset != self.offset { 82 | self.offset = offset; 83 | return true; 84 | } 85 | } 86 | false 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate pancurses; 2 | 3 | use std::fs::File; 4 | use std::io::Read; 5 | use pancurses::*; 6 | use std::env; 7 | use std::cmp; 8 | 9 | mod data; 10 | use data::*; 11 | mod helpers; 12 | use helpers::*; 13 | mod render; 14 | use render::*; 15 | 16 | fn main() { 17 | let window = initscr(); 18 | start_color(); 19 | use_default_colors(); 20 | 21 | noecho(); 22 | 23 | window.keypad(true); 24 | init_pair(1, COLOR_BLACK, COLOR_YELLOW); 25 | init_pair(2, COLOR_BLACK, COLOR_GREEN); 26 | init_pair(3, COLOR_BLACK, COLOR_CYAN); 27 | init_pair(4, COLOR_BLACK, COLOR_BLUE); 28 | init_pair(5, COLOR_WHITE, COLOR_RED); 29 | init_pair(6, COLOR_WHITE, COLOR_MAGENTA); 30 | 31 | 32 | let mut buffer = Vec::new(); 33 | let name = match env::args().nth(1) { 34 | Some(n)=>n, 35 | None => { 36 | endwin(); 37 | println!("Usage: elfedit exec.o"); 38 | return; 39 | } 40 | }; 41 | let _size = match File::open(name){ 42 | Ok(f)=>f, 43 | Err(e)=>{ 44 | endwin(); 45 | println!("File couldn't be opened: {}", e); 46 | return 47 | } 48 | }.read_to_end(&mut buffer); 49 | let mut info = get_elf_info(&buffer); 50 | 51 | let mut cursor = Cursor{index:0,length:buffer.len()*2+1,offset:0}; 52 | 53 | loop { 54 | if cursor.update_offset((window.get_max_y()-2) as usize) { 55 | window.clear(); // maybe try something more exact than this (using insertln and deleteln) 56 | info.needs_redraw = true; 57 | } 58 | render(&window,&buffer,&info,&cursor); 59 | 60 | window.refresh(); 61 | info.needs_redraw = false; 62 | match window.getch() { 63 | Some(Input::Character('k'))=>{cursor.mv(0,-1);}, 64 | Some(Input::KeyUp) =>{cursor.mv(0,-1);}, 65 | Some(Input::Character('j'))=>{cursor.mv(0,1);}, 66 | Some(Input::KeyDown) =>{cursor.mv(0,1);}, 67 | Some(Input::Character('h'))=>{cursor.mv(-1,0);}, 68 | Some(Input::KeyLeft) =>{cursor.mv(-1,0);}, 69 | Some(Input::Character('l'))=>{cursor.mv(1,0);}, 70 | Some(Input::KeyRight) =>{cursor.mv(1,0);}, 71 | Some(Input::Character(other)) if other >= '0' && other <= '9' => {buffer.set_at((other as u8) -b'0',&mut cursor); info = get_elf_info(&buffer);}, 72 | Some(Input::Character(other)) if other >= 'a' && other <= 'f' => {buffer.set_at((other as u8) -b'a'+10,&mut cursor); info = get_elf_info(&buffer);}, 73 | Some(Input::Character('\u{1b}')) => break, 74 | Some(other)=>{info.msg = format!("Unused keypress: {:?}",other);}, 75 | None => (), 76 | }; 77 | } 78 | endwin(); 79 | } 80 | 81 | fn render(window: &Window, buffer: &[u8], table: &ELFinfo, cursor:&Cursor){ 82 | window.mv(0,0); 83 | if table.needs_redraw { 84 | for row in cursor.offset..cmp::min(buffer.len()/16+1,cursor.offset+(window.get_max_y()-1) as usize){ 85 | window.printw(&format!("{:08X}: ",row*16)); 86 | for i in 0..16 { 87 | if row*16+i >= buffer.len() {continue;} 88 | if i == 8 {window.printw(" ");} 89 | window.attrset(ColorPair(highlight(16*row+i,&table))); 90 | window.printw(&format!("{:02X}",buffer[row*16+i])); 91 | if highlight(16*row+i,&table) != highlight(16*row+i+1,&table) || i == 15 { 92 | window.attrset(ColorPair(0)); 93 | } 94 | window.printw(" "); 95 | } 96 | window.printw("\n"); 97 | } 98 | 99 | print_elf_info(window,&table,buffer,cursor.offset as i32); 100 | } 101 | window.mv((cursor.y()-cursor.offset) as i32,cursor.x() as i32); 102 | } 103 | 104 | fn get_elf_info(buffer: &[u8]) -> ELFinfo { 105 | let mut tmp = ELFinfo{bit_class:buffer[4],endianess:buffer[5],version:buffer[6],abi:buffer[7], 106 | file_type:get_multibyte_data(&buffer[16..18],buffer[5]==1) as u16, 107 | arch:get_multibyte_data(&buffer[18..20],buffer[5]==1) as u16, 108 | entry:get_multibyte_data(&buffer[24..32],buffer[5]==1), 109 | prog_head:get_multibyte_data(&buffer[32..40],buffer[5]==1), 110 | sect_head:get_multibyte_data(&buffer[40..48],buffer[5]==1), 111 | flags:get_multibyte_data(&buffer[48..52],buffer[5]==1) as u32, 112 | size:get_multibyte_data(&buffer[52..54],buffer[5]==1) as u16, 113 | prog_size:get_multibyte_data(&buffer[54..56],buffer[5]==1) as u16, 114 | prog_num:get_multibyte_data(&buffer[56..58],buffer[5]==1) as u16, 115 | sect_size:get_multibyte_data(&buffer[58..60],buffer[5]==1) as u16, 116 | sect_num:get_multibyte_data(&buffer[60..62],buffer[5]==1) as u16, 117 | shs_table_index:get_multibyte_data(&buffer[62..64],buffer[5]==1) as u16, 118 | symtab: SYMTAB {offset:0,size:0,entry_size:0,link:0,ei:0}, 119 | progs:Vec::new(),sects:Vec::new(),shstr:STRTAB{offset:0,size:0},strtabs:Vec::new(),symbols:Vec::new(),reltabs:Vec::new(), 120 | msg:"".to_owned(),needs_redraw:true, 121 | }; 122 | if tmp.prog_head == 0 && tmp.prog_num != 0 { 123 | tmp.msg = "Program headers offset = 0, but there are program headers".to_owned(); 124 | return tmp; 125 | } 126 | if tmp.sect_head == 0 && tmp.sect_num != 0 { 127 | tmp.msg = "Section headers offset = 0, but there are program headers".to_owned(); 128 | return tmp; 129 | } 130 | if tmp.prog_head > buffer.len() as u64 || tmp.prog_head + (tmp.prog_size as u64)*(tmp.prog_num as u64) > buffer.len() as u64{ 131 | tmp.msg = "Error parsing. Program header outside file.".to_owned(); 132 | return tmp; 133 | } 134 | if tmp.sect_head > buffer.len() as u64 || tmp.sect_head + (tmp.sect_size as u64)*(tmp.sect_num as u64) > buffer.len() as u64{ 135 | tmp.msg = "Error parsing. Section header outside file.".to_owned(); 136 | return tmp; 137 | } 138 | for i in 0..tmp.prog_num { 139 | let offset = (tmp.prog_head+(tmp.prog_size as u64)*(i as u64)) as usize; 140 | tmp.progs.push(ProgHead{typ:get_multibyte_data(&buffer[offset..(offset+4)],tmp.endianess==1) as u32, 141 | flags:get_multibyte_data(&buffer[(offset+4)..(offset+8)],tmp.endianess==1) as u32, 142 | offset:get_multibyte_data(&buffer[(offset+8)..(offset+16)],tmp.endianess==1), 143 | virt_addr:get_multibyte_data(&buffer[(offset+16)..(offset+24)],tmp.endianess==1), 144 | file_size:get_multibyte_data(&buffer[(offset+32)..(offset+40)],tmp.endianess==1), 145 | mem_size:get_multibyte_data(&buffer[(offset+40)..(offset+48)],tmp.endianess==1), 146 | align:get_multibyte_data(&buffer[(offset+48)..(offset+56)],tmp.endianess==1) 147 | }); 148 | } 149 | for i in 0..tmp.sect_num { 150 | let offset = (tmp.sect_head+(tmp.sect_size as u64)*(i as u64)) as usize; 151 | tmp.sects.push(SectHead{name:get_multibyte_data(&buffer[offset..(offset+4)],tmp.endianess==1) as u32, 152 | name_str:"".to_owned(), 153 | typ:get_multibyte_data(&buffer[(offset+4)..(offset+8)],tmp.endianess==1) as u32, 154 | flags:get_multibyte_data(&buffer[(offset+8)..(offset+16)],tmp.endianess==1), 155 | virt_addr:get_multibyte_data(&buffer[(offset+16)..(offset+24)],tmp.endianess==1), 156 | offset:get_multibyte_data(&buffer[(offset+24)..(offset+32)],tmp.endianess==1), 157 | file_size:get_multibyte_data(&buffer[(offset+32)..(offset+40)],tmp.endianess==1), 158 | sect_index:get_multibyte_data(&buffer[(offset+40)..(offset+44)],tmp.endianess==1) as u32, 159 | extra_info:get_multibyte_data(&buffer[(offset+44)..(offset+48)],tmp.endianess==1) as u32, 160 | align:get_multibyte_data(&buffer[(offset+48)..(offset+56)],tmp.endianess==1), 161 | entry_size:get_multibyte_data(&buffer[(offset+56)..(offset+64)],tmp.endianess==1), 162 | }); 163 | } 164 | if (tmp.shs_table_index as usize) < tmp.sects.len(){ 165 | let i = tmp.shs_table_index as usize; 166 | if tmp.sects[i].typ == 3 { 167 | if tmp.sects[i].offset > buffer.len() as u64 { 168 | tmp.msg = "String table section not within file".to_owned(); 169 | return tmp; 170 | } else if tmp.sects[i].offset > buffer.len() as u64 { 171 | tmp.msg = "String table not entirely within file".to_owned(); 172 | return tmp; 173 | } 174 | tmp.shstr.offset = tmp.sects[i].offset; 175 | tmp.shstr.size = tmp.sects[i].file_size; 176 | } else { 177 | tmp.msg = "String table section header corrupted".to_owned(); 178 | return tmp; 179 | } 180 | } 181 | for prog in &mut tmp.progs { 182 | if prog.typ == 3 { //Interp 183 | if prog.offset <= 0 || prog.offset > buffer.len() as u64 || prog.offset + prog.file_size > buffer.len() as u64 { 184 | tmp.msg = "String for INTERP not within file".to_owned(); 185 | } else { 186 | tmp.strtabs.push(STRTAB{offset:prog.offset,size:prog.file_size}); 187 | } 188 | } 189 | } 190 | for sect in &mut tmp.sects { 191 | sect.name_str = get_null_string(&buffer[(tmp.shstr.offset as usize)..((tmp.shstr.offset+tmp.shstr.size) as usize)],sect.name as usize); 192 | if sect.typ == 3 { //String table 193 | if sect.offset <= 0 || sect.offset > buffer.len() as u64 || sect.offset + sect.file_size > buffer.len() as u64 { 194 | tmp.msg = "String table section not within file".to_owned(); 195 | } else { 196 | tmp.strtabs.push(STRTAB{offset:sect.offset,size:sect.file_size}); 197 | } 198 | }else if sect.typ == 2 { 199 | tmp.symtab.offset = sect.offset; 200 | tmp.symtab.size = sect.file_size; 201 | tmp.symtab.entry_size = sect.entry_size; 202 | tmp.symtab.link = sect.sect_index; 203 | tmp.symtab.ei = sect.extra_info; 204 | } else if sect.typ == 4 { 205 | tmp.reltabs.push(RELTAB{offset:sect.offset,size:sect.file_size,entry_size:sect.entry_size,link:sect.sect_index,ei:sect.extra_info,rels:Vec::new()}) 206 | } 207 | } 208 | 209 | for tab in &mut tmp.reltabs { 210 | if tab.offset + tab.size <= buffer.len() as u64 && tab.entry_size != 0 { 211 | for i in 0..(tab.size/tab.entry_size){ 212 | let offset = ( tab.offset+(tab.entry_size as u64)*(i as u64)) as usize; 213 | tab.rels.push(REL{offset:get_multibyte_data(&buffer[offset..(offset+8)],tmp.endianess==1) as u64, 214 | info:get_multibyte_data(&buffer[(offset+8)..(offset+16)],tmp.endianess==1) as u64, 215 | addend:get_multibyte_data(&buffer[(offset+16)..(offset+24)],tmp.endianess==1) as i64}); 216 | } 217 | } 218 | } 219 | 220 | if tmp.symtab.offset + tmp.symtab.size < buffer.len() as u64 && tmp.symtab.entry_size != 0{ 221 | let strtab = &tmp.sects[tmp.symtab.link as usize]; 222 | for i in 0..(tmp.symtab.size/tmp.symtab.entry_size){ 223 | let offset = (tmp.symtab.offset+(tmp.symtab.entry_size as u64)*(i as u64)) as usize; 224 | let mut sym = SYMBOL{name:get_multibyte_data(&buffer[offset..(offset+4)],tmp.endianess==1) as u32, 225 | name_str:"".to_owned(), 226 | info:buffer[offset+4],other:buffer[offset+5], 227 | shndx:get_multibyte_data(&buffer[(offset+6)..(offset+8)],tmp.endianess==1) as u16, 228 | value:get_multibyte_data(&buffer[(offset+8)..(offset+16)],tmp.endianess==1) as u64, 229 | size:get_multibyte_data(&buffer[(offset+16)..(offset+24)],tmp.endianess==1) as u64}; 230 | sym.name_str = get_null_string(&buffer[(strtab.offset as usize)..((strtab.offset+strtab.file_size) as usize)],sym.name as usize); 231 | tmp.symbols.push(sym); 232 | } 233 | } 234 | tmp 235 | } 236 | 237 | fn highlight(index: usize, info: &ELFinfo) -> u8{ 238 | if index <= 3 {return 1} // Magic bytes 239 | if index == 4 {return 2} // Bittedness 240 | if index == 5 {return 3} // Endianess 241 | if index == 6 {return 4} // Version 242 | if index == 7 {return 5} // OS / ABI 243 | if index < 16 {return 0} // Extra shit and reserved 244 | if index <= 17 {return 6} // Type (EXEC) 245 | if index <= 19 {return 2} // Machine type 246 | if index <= 23 {return 4} // Version 247 | if index < 32 {return 3} // Entry point 248 | if index < 40 {return 5} // Start of Program headers 249 | if index < 48 {return 6} // Start of Section headers 250 | if index <= 51 {return 1} // Flags 251 | if index <= 53 {return 2} // Size of this header 252 | if index <= 55 {return 3} // Size of Program headers 253 | if index <= 57 {return 4} // Number of Program headers 254 | if index <= 59 {return 5} // Size of Section headers 255 | if index <= 61 {return 6} // Number of Section headers 256 | if index <= 63 {return 1} // Section header string table index 257 | for i in 0..info.progs.len() { 258 | if index < (info.prog_head + (i as u64)*(info.prog_size as u64)) as usize {continue;} 259 | if index > (info.prog_head + ((i as u64)+1)*(info.prog_size as u64)) as usize{continue;} 260 | let offset = (index as u64) - info.prog_head - (i as u64)*(info.prog_size as u64); 261 | if offset <= 3 {return 2} // Type 262 | if offset <= 7 {return 3} // Flags 263 | if offset < 16 {return 4} // Offset 264 | if offset < 24 {return 5} // Virtual Address 265 | if offset < 32 {return 0} // Physical address / unused 266 | if offset < 40 {return 6} // Size in file 267 | if offset < 48 {return 1} // Size in Mem 268 | if offset < 56 {return 3} // Required alignment 269 | } 270 | for i in 0..info.sects.len() { 271 | if index < (info.sect_head + (i as u64)*(info.sect_size as u64)) as usize {continue;} 272 | if index > (info.sect_head + ((i as u64)+1)*(info.sect_size as u64)) as usize{continue;} 273 | let offset = (index as u64) - info.sect_head - (i as u64)*(info.sect_size as u64); 274 | if offset <= 3 {return 1} // Name 275 | if offset <= 7 {return 2} // Type 276 | if offset < 16 {return 3} // Flags 277 | if offset < 24 {return 4} // Virtual Address 278 | if offset < 32 {return 5} // Address in file 279 | if offset < 40 {return 6} // Size in file 280 | if offset < 44 {return 1} // Section index 281 | if offset < 48 {return 2} // Extra info 282 | if offset < 56 {return 3} // Required alignment 283 | if offset < 64 {return 4} // Entry size 284 | } 285 | for i in 0..info.symbols.len() { 286 | if index < (info.symtab.offset + (i as u64)*(info.symtab.entry_size as u64)) as usize {continue;} 287 | if index > (info.symtab.offset + (i as u64+1)*(info.symtab.entry_size as u64)) as usize{continue;} 288 | let offset = (index as u64) - info.symtab.offset - (i as u64)*(info.symtab.entry_size as u64); 289 | if offset < 4 {return 1} // Name 290 | if offset < 5 {return 2} // Info 291 | if offset < 6 {return 3} // Other / Visibility 292 | if offset < 8 {return 4} // Section index 293 | if offset < 16 {return 5} // Value 294 | if offset < 24 {return 6} // Size 295 | } 296 | for tab in &info.reltabs{ 297 | for i in 0..tab.rels.len(){ 298 | if index < (tab.offset + (i as u64)*(tab.entry_size as u64)) as usize {continue;} 299 | if index > (tab.offset + (i as u64+1)*(tab.entry_size as u64)) as usize{continue;} 300 | let offset = (index as u64) - tab.offset - (i as u64)*(tab.entry_size as u64); 301 | if offset < 8 {return 1} // Offset 302 | if info.arch == 0x3E { 303 | if offset < 12 {return 2} // Symbol index 304 | if offset < 16 {return 4} // Type 305 | } else if offset < 16 {return 2} 306 | if offset < 24 {return 3} // Addend 307 | } 308 | } 309 | 0 310 | } 311 | -------------------------------------------------------------------------------- /src/render.rs: -------------------------------------------------------------------------------- 1 | use pancurses::*; 2 | use data::*; 3 | 4 | pub fn print_elf_info(window: &Window, info: &ELFinfo, buffer: &[u8], offset: i32){ 5 | if offset == 0 { 6 | window.attrset(ColorPair(2)); 7 | window.mvaddstr(0-offset,60,if info.bit_class == 1 {"32 bit"} else {"64 bit"}); 8 | window.attrset(ColorPair(0)); 9 | window.printw(" "); 10 | window.attrset(ColorPair(3)); 11 | window.printw(if info.endianess == 1 {"Little Endian"} else {"Big Endian"}); 12 | window.attrset(ColorPair(0)); 13 | window.printw(" "); 14 | window.attrset(ColorPair(4)); 15 | window.printw(&format!("Version: {}",info.version)); 16 | window.attrset(ColorPair(0)); 17 | window.printw(" "); 18 | window.attrset(ColorPair(5)); 19 | window.printw(match info.abi { 0 => "System V", 3 => "Linux", _ => "Unknown ABI"}); 20 | window.attrset(ColorPair(6)); 21 | } 22 | if offset <= 1 { 23 | window.mvaddstr(1-offset,60,match info.file_type { 1 => "T: Relocatable", 2 => "T: Executable", 3 => "T: Shared", 24 | 4 => "T: Core", _ => "Unknown type"}); 25 | window.attrset(ColorPair(0)); 26 | window.printw(" "); 27 | window.attrset(ColorPair(2)); 28 | window.printw(match info.arch {2 => "SPARC",3=>"x86",8=>"MIPS",0x14=>"PowerPC",0x28=>"ARM",0x2A=>"SuperH",0x32=>"IA-64",0x3E=>"0x86-64",0xB7=>"AArch67", 29 | 0xF3=>"RISC-V",_=>"Unknown ISA"}); 30 | window.attrset(ColorPair(0)); 31 | window.printw(" "); 32 | window.attrset(ColorPair(3)); 33 | window.printw(&format!("Entry: 0x{:08X}",info.entry)); 34 | window.attrset(ColorPair(5)); 35 | } 36 | if offset <= 2 { 37 | window.mvaddstr(2-offset,60,&format!("&progs: 0x{:08X}",info.prog_head)); 38 | window.attrset(ColorPair(0)); 39 | window.printw(" "); 40 | window.attrset(ColorPair(6)); 41 | window.printw(&format!("§s: 0x{:08X}",info.sect_head)); 42 | window.attrset(ColorPair(1)); 43 | } 44 | if offset <= 3 { 45 | window.mvaddstr(3-offset,60,&format!("Flags: 0x{:04X}",info.flags)); 46 | window.attrset(ColorPair(0)); 47 | window.printw(" "); 48 | window.attrset(ColorPair(2)); 49 | window.printw(&format!("S: {}b",info.size)); 50 | window.attrset(ColorPair(0)); 51 | window.printw(" progs:"); 52 | window.attrset(ColorPair(4)); 53 | window.printw(&format!("{}",info.prog_num)); 54 | window.attrset(ColorPair(0)); 55 | window.printw("x"); 56 | window.attrset(ColorPair(3)); 57 | window.printw(&format!("{}b",info.prog_size)); 58 | window.attrset(ColorPair(0)); 59 | window.printw(" sects:"); 60 | window.attrset(ColorPair(6)); 61 | window.printw(&format!("{}",info.sect_num)); 62 | window.attrset(ColorPair(0)); 63 | window.printw("x"); 64 | window.attrset(ColorPair(5)); 65 | window.printw(&format!("{}b",info.sect_size)); 66 | window.attrset(ColorPair(0)); 67 | window.printw(" "); 68 | window.attrset(ColorPair(1)); 69 | window.printw(&format!("shstri: {} ",info.shs_table_index)); 70 | window.attrset(ColorPair(0)); 71 | } 72 | 73 | window.attrset(ColorPair(0)); 74 | 75 | for tab in &info.strtabs { 76 | for i in tab.offset/16..((tab.offset+tab.size)/16+1){ 77 | if i as i32 >= offset && (i as i32) - offset < window.get_max_y()-1 { 78 | window.mvaddstr(i as i32 - offset,60,"|"); 79 | for k in 0..16 { 80 | let index = (k+i*16) as usize; 81 | window.printw(&format!("{}",if index < buffer.len() && buffer[index] >= 32 && buffer[index] < 127 {buffer[index] as char} else {'.'})); 82 | } 83 | window.printw("|"); 84 | } 85 | } 86 | } 87 | 88 | for i in 0..(info.progs.len() as i32){ 89 | let head = &info.progs[i as usize]; 90 | let base = ((info.prog_head+((i as u64)*(info.prog_size as u64)))/16) as i32 - offset; 91 | 92 | if base >= 0 && base < window.get_max_y()-1{ 93 | window.attrset(ColorPair(2)); 94 | window.mvaddstr(base,60,match head.typ {0=>"Null",1=>"Load", 2=>"Dynamic",3=>"Interp",4=>"Note",5=>"SHLIB",6=>"PHDR",7=>"TLS", 95 | 0x60000000..=0x6FFFFFFF=>"OS Specific",0x70000000..=0x7FFFFFFF=>"Proccesor specific", _=>"Unknown type"}); 96 | window.attrset(ColorPair(0)); 97 | window.printw(" "); 98 | window.attrset(ColorPair(3)); 99 | window.printw(if head.flags & 4 == 0 {" "} else {"R"}); 100 | window.printw(if head.flags & 2 == 0 {" "} else {"W"}); 101 | window.printw(if head.flags & 1 == 0 {" "} else {"E"}); 102 | window.attrset(ColorPair(0)); 103 | window.printw(" "); 104 | window.attrset(ColorPair(4)); 105 | window.printw(&format!("Offset: 0x{:08X}",head.offset)); 106 | } 107 | 108 | if base+1 >= 0 && base+1 < window.get_max_y()-1{ 109 | window.attrset(ColorPair(5)); 110 | window.mvaddstr(base+1,60,&format!("Virt Addr: 0x{:08X}",head.virt_addr)); 111 | window.attrset(ColorPair(0)); 112 | window.printw(" "); 113 | window.attrset(ColorPair(6)); 114 | window.printw(&format!("Size in file: 0x{:08X}",head.file_size)); 115 | } 116 | 117 | if base+2 >= 0 && base+2 < window.get_max_y()-1{ 118 | window.attrset(ColorPair(1)); 119 | window.mvaddstr(base+2,60,&format!("Size in mem: 0x{:08X}",head.mem_size)); 120 | window.attrset(ColorPair(0)); 121 | window.printw(" "); 122 | window.attrset(ColorPair(3)); 123 | window.printw(&format!("alignment: 0x{:08X}",head.align)); 124 | } 125 | } 126 | 127 | for i in 0..(info.sects.len() as i32){ 128 | let head = &info.sects[i as usize]; 129 | let base = ((info.sect_head+((i as u64)*(info.sect_size as u64)))/16) as i32 - offset; 130 | 131 | if base >= 0 && base < window.get_max_y()-1{ 132 | window.attrset(ColorPair(1)); 133 | window.mvaddstr(base,60,&format!("Name: {} ({})", &head.name_str, head.name)); 134 | window.attrset(ColorPair(0)); 135 | window.printw(" "); 136 | window.attrset(ColorPair(2)); 137 | window.printw(match head.typ {0=>"NULL",1=>"PROGBITS", 2=>"SYMTAB",3=>"STRTAB",4=>"RELA",5=>"HASH", 138 | 6=>"DYNAMIC",7=>"NOTE",8=>"NOBITS",9=>"REL",10=>"SHLIB",11=>"DYNSYM", 139 | 0xE=>"INIT_ARRAY",0xF=>"FINI_ARRAY",0x10=>"PREINIT_ARRAY",0x11=>"GROUP",0x12=>"SYMTAB_SHNDX",0x13=>"NUM", 140 | 0x60000000..=0x6FFFFFFF=>"OS Specific",0x70000000..=0x7FFFFFFF=>"Proccesor specific", _=>"Unknown type"}); 141 | window.attrset(ColorPair(0)); 142 | window.printw(" "); 143 | window.attrset(ColorPair(3)); 144 | window.printw(if head.flags & 1 == 0 {" "} else {"R"}); 145 | window.printw(if head.flags & 2 == 0 {" "} else {"A"}); 146 | window.printw(if head.flags & 4 == 0 {" "} else {"E"}); 147 | } 148 | 149 | if base+1 >= 0 && base+1 < window.get_max_y()-1{ 150 | window.attrset(ColorPair(4)); 151 | window.mvaddstr(base+1,60,&format!("Virt Addr: 0x{:08X}",head.virt_addr)); 152 | window.attrset(ColorPair(0)); 153 | window.printw(" "); 154 | window.attrset(ColorPair(5)); 155 | window.printw(&format!("Offset: 0x{:08X}",head.offset)); 156 | } 157 | 158 | if base+2 >= 0 && base+2 < window.get_max_y()-1{ 159 | window.attrset(ColorPair(6)); 160 | window.mvaddstr(base+2,60,&format!("Size in file: 0x{:08X}",head.file_size)); 161 | window.attrset(ColorPair(0)); 162 | window.printw(" "); 163 | window.attrset(ColorPair(1)); 164 | window.printw(&format!("Section Index: {}",head.sect_index)); 165 | window.attrset(ColorPair(0)); 166 | window.printw(" "); 167 | window.attrset(ColorPair(2)); 168 | window.printw(&format!("Extra info: 0x{:04X}",head.extra_info)); 169 | } 170 | 171 | if base+3 >= 0 && base+3 < window.get_max_y()-1{ 172 | window.attrset(ColorPair(3)); 173 | window.mvaddstr(base+3,60,&format!("Alignment: 0x{:08X}",head.align)); 174 | window.attrset(ColorPair(0)); 175 | window.printw(" "); 176 | window.attrset(ColorPair(4)); 177 | window.printw(&format!("Entry size: 0x{:08X}",head.entry_size)); 178 | } 179 | } 180 | 181 | for i in 0..(info.symbols.len() as i32){ 182 | let head = &info.symbols[i as usize]; 183 | let base = ((info.symtab.offset+((i as u64)*(info.symtab.entry_size as u64)))/16) as i32 - offset; 184 | 185 | if base >= 0 && base < window.get_max_y()-1{ 186 | window.attrset(ColorPair(1)); 187 | window.mvaddstr(base,60,&format!("Name: {} ({})", &head.name_str, head.name)); 188 | window.attrset(ColorPair(0)); 189 | window.printw(" "); 190 | window.attrset(ColorPair(2)); 191 | window.printw(&format!("Bind: {}, Type: {}",match head.info >> 4 {0=>"Local",1=>"Global",2=>"Weak",10=>"LOOS",12=>"HIOS",13=>"LOPROC",15=>"HIPROC",_=>"Unknown type"}, 192 | match head.info & 0xf {0=>"No Type",1=>"Object",2=>"Func",3=>"Section",4=>"File",5=>"Common",6=>"TLS",10=>"LOOS",12=>"HIOS",13=>"LOPROC",14=>"Spark Register",15=>"HIPROC",_=>"Unknown type"})); 193 | window.attrset(ColorPair(0)); 194 | window.printw(" "); 195 | window.attrset(ColorPair(3)); 196 | window.printw(&format!("Visibility: {}",match head.other & 0x3 {0=>"Default",1=>"Internal",2=>"Hidden",3=>"Protected",4=>"Exported",5=>"Singleton",6=>"Eliminate",_=>"Unknown visbility"})); 197 | window.attrset(ColorPair(0)); 198 | window.printw(" "); 199 | window.attrset(ColorPair(4)); 200 | let str = match head.shndx {0=>"Undef",0xff00=>"LOPROC",0xff01=>"After",0xff1f=>"HIPROC",0xff20=>"LOOS",0xff3f=>"HIOS", 201 | 0xfff1=>"ABS",0xfff2=>"Common",0xffff=>"HIReserve", _=>""}; 202 | if str == ""{ 203 | window.printw(&format!("Shndx: {}",head.shndx)); 204 | }else{ 205 | window.printw(&format!("Shndx: {}",str)); 206 | } 207 | window.attrset(ColorPair(0)); 208 | window.printw(" "); 209 | window.attrset(ColorPair(5)); 210 | window.printw(&format!("Value: 0x{:04X}",head.value)); 211 | window.attrset(ColorPair(0)); 212 | window.printw(" "); 213 | window.attrset(ColorPair(6)); 214 | window.printw(&format!("Size: 0x{:04X}",head.size)); 215 | } 216 | } 217 | 218 | for tab in &info.reltabs{ 219 | for i in 0..tab.rels.len(){ 220 | let base = ((tab.offset + (i as u64)*(tab.entry_size as u64))/16) as i32 - offset; 221 | if base >= 0 && base < window.get_max_y()-1{ 222 | window.attrset(ColorPair(1)); 223 | window.mvaddstr(base,60,&format!("Offset: 0x{:04x}", tab.rels[i].offset)); 224 | window.attrset(ColorPair(0)); 225 | window.printw(" "); 226 | if info.arch == 0x3E { 227 | window.attrset(ColorPair(2)); 228 | window.printw(&format!("Symbol index: 0x{:04x}", tab.rels[i].info >> 32)); 229 | window.attrset(ColorPair(0)); 230 | window.printw(" "); 231 | window.attrset(ColorPair(4)); 232 | let str = match tab.rels[i].info & 0xffffffff { 233 | 0=>"NONE",1=>"64",2=>"PC32",3=>"GOT32",4=>"PLT32",5=>"COPY", 234 | 6=>"GLOB_DAT", 7=>"JUMP_SLOT", 8=>"RELATIVE", 9=>"GOTPCREL", _=>"", 235 | }; 236 | if str == ""{ 237 | window.printw(&format!("Type: {}",tab.rels[i].info & 0xffffffff)); 238 | }else{ 239 | window.printw(&format!("Type: R_X86_64_{}",str)); 240 | } 241 | window.attrset(ColorPair(0)); 242 | window.printw(" "); 243 | window.attrset(ColorPair(3)); 244 | window.printw(&format!("Addend: {}", tab.rels[i].addend)); 245 | } else { 246 | window.attrset(ColorPair(2)); 247 | window.printw(&format!("Info: 0x{:08x}", tab.rels[i].info)); 248 | window.attrset(ColorPair(0)); 249 | window.printw(" "); 250 | window.attrset(ColorPair(3)); 251 | window.printw(&format!("Addend: {}", tab.rels[i].addend)); 252 | } 253 | } 254 | } 255 | } 256 | 257 | window.attrset(ColorPair(0)); 258 | 259 | window.mvaddstr(window.get_max_y()-1,0,&info.msg); 260 | window.clrtoeol(); 261 | } 262 | 263 | --------------------------------------------------------------------------------