├── .gitignore ├── LICENSE ├── README.md ├── elf.py └── wsym.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by http://www.gitignore.io 2 | 3 | ### Python ### 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | 45 | # Translations 46 | *.mo 47 | *.pot 48 | 49 | # Django stuff: 50 | *.log 51 | 52 | # Sphinx documentation 53 | docs/_build/ 54 | 55 | # PyBuilder 56 | target/ 57 | 58 | 59 | ### Linux ### 60 | *~ 61 | 62 | # KDE directory preferences 63 | .directory 64 | 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Wannes Rombouts 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wsym 2 | 3 | Adds symbols to an ELF file. Sort of the opposite of strip. 4 | This is in a very early stage of development and should 5 | be considered PoC/beta software. 6 | 7 | ### why 8 | 9 | I made this because I wanted gdb to know about the symbols I 10 | defined in IDA when reverse engineering. I really think there 11 | should be an easier way to do this but I couldn't find anything 12 | that gdb understands except ELF, so yeah... overkill. Please 13 | let me know if there is an easier way to tell gdb about 14 | user defined symbols. 15 | 16 | However this tool might be useful anyway since it is a more 17 | generic solution. 18 | 19 | ### how to use 20 | 21 | ``` 22 | usage: wsym.py [-h] [-v] [-f SYMBOLS] [-i SYMBOLS] [-n SYMBOLS] input output 23 | ``` 24 | 25 | There are multiple ways to provide symbols that should be added 26 | to the binary. 27 | 28 | ```-f, --flat``` 29 | > This is a very simple flat file format, one symbol per line: 30 | > 31 | > hex_addr name [hex_size] 32 | 33 | 34 | ```-i, --ida``` 35 | > Using this option you can provide a .map file generated by IDA. 36 | > 37 | > File > Produce File > Create MAP File -> segmentation & local 38 | 39 | 40 | ```-n, --nm``` 41 | > This is the output of nm, the flags outputted by nm 42 | > to describe each symbol are ignored. 43 | 44 | 45 | wsym will generate a new ELF file which can be directly run 46 | under gdb, or you can use the add-symbol-file command in 47 | order to load the symbols from the generated file while 48 | debugging the original one. 49 | 50 | **Warning:** running wsym repeatedly on a binary generated by itself 51 | will keep increasing the file size and is probably a bad idea. 52 | Always rerun on the original file. 53 | 54 | ### how this works 55 | 56 | We recreate the section header table at the end of the file. 57 | Adding original sections if there are any and re-adjusting 58 | their sh_link and sh_info if needed. 59 | 60 | We also add a GHOST section for each segment, this covers 61 | the whole segment in case no other sections are present. 62 | 63 | We create a new symtab called .wsymtab containing the provided 64 | symbols. A new strtab for this symtab is added but we also 65 | make a new shstrtab for all section names. This allows us to 66 | touch the original file as little as possible. 67 | 68 | ### future work 69 | 70 | - MORE TESTING: IIRC there is some weird stuff going on with 71 | symtabs and entries need to be in a particular order for it 72 | to work etc, etc... Maybe we should be nice and do this for 73 | the user. 74 | 75 | - Add a parser for another IDA output format in order to have sizes 76 | - Allow for symbol types (data would be nice) 77 | - Link a symbol to the smallest section containing said symbol 78 | (instead of taking the first one that matches the address) 79 | 80 | -------------------------------------------------------------------------------- /elf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import ctypes 5 | from ctypes import * 6 | from functools import wraps 7 | 8 | class PrintableStructureMixIn(object): 9 | def show(self): 10 | print(self) 11 | for field_name, field_type in self._fields_: 12 | value = getattr(self, field_name) 13 | if isinstance(value, int): 14 | value = hex(value) 15 | print("%15s: %s" % (field_name, value)) 16 | 17 | class CopyableStructureMixIn(object): 18 | def copy(self): 19 | new = type(self)() 20 | ctypes.pointer(new)[0] = self 21 | return new 22 | 23 | class BigEndianStructure(ctypes.BigEndianStructure, 24 | PrintableStructureMixIn, 25 | CopyableStructureMixIn): 26 | pass 27 | 28 | class LittleEndianStructure(ctypes.LittleEndianStructure, 29 | PrintableStructureMixIn, 30 | CopyableStructureMixIn): 31 | pass 32 | 33 | def build_structure(f): 34 | @wraps(f) 35 | def wrapper(self, *args, **kwargs): 36 | name = ''.join(w[0].upper() + w[1:] for w in f.__name__.split('_')) 37 | return type("%s%d%s" % (name, self.wordsize, self.endianess), 38 | (self.structure, ), 39 | {"_fields_": f(self, *args, **kwargs)}) 40 | return wrapper 41 | 42 | def select_class(f): 43 | @wraps(f) 44 | def wrapper(self, *args, **kwargs): 45 | return f(self, *args, **kwargs)[self.ei_class] 46 | return wrapper 47 | 48 | class ELFFactory(object): 49 | 50 | def __init__(self, ei_class, ei_data): 51 | if ei_class not in (ELFCLASS32, ELFCLASS64): 52 | raise ValueError("Unknown ei_class=%d" % ei_class) 53 | if ei_data not in (ELFDATA2LSB, ELFDATA2MSB): 54 | raise ValueError("Unknown ei_data=%d" % ei_data) 55 | 56 | self.ei_class = ei_class 57 | self.ei_data = ei_data 58 | 59 | if self.ei_class == ELFCLASS32: 60 | self.wordsize = 32 61 | elif self.ei_class == ELFCLASS64: 62 | self.wordsize = 64 63 | 64 | if self.ei_data == ELFDATA2LSB: 65 | self.structure = LittleEndianStructure 66 | self.endianess = "LSB" 67 | elif self.ei_data == ELFDATA2MSB: 68 | self.structure = BigEndianStructure 69 | self.endianess = "MSB" 70 | 71 | @build_structure 72 | @select_class 73 | def elf_ehdr(self): 74 | return { 75 | ELFCLASS32: [ 76 | ("e_ident", c_ubyte * 16), 77 | ("e_type", c_uint16), 78 | ("e_machine", c_uint16), 79 | ("e_version", c_uint32), 80 | ("e_entry", c_uint32), 81 | ("e_phoff", c_uint32), 82 | ("e_shoff", c_uint32), 83 | ("e_flags", c_uint32), 84 | ("e_ehsize", c_uint16), 85 | ("e_phentsize", c_uint16), 86 | ("e_phnum", c_uint16), 87 | ("e_shentsize", c_uint16), 88 | ("e_shnum", c_uint16), 89 | ("e_shstrndx", c_uint16), 90 | ], 91 | ELFCLASS64: [ 92 | ("e_ident", c_ubyte * 16), 93 | ("e_type", c_uint16), 94 | ("e_machine", c_uint16), 95 | ("e_version", c_uint32), 96 | ("e_entry", c_uint64), 97 | ("e_phoff", c_uint64), 98 | ("e_shoff", c_uint64), 99 | ("e_flags", c_uint32), 100 | ("e_ehsize", c_uint16), 101 | ("e_phentsize", c_uint16), 102 | ("e_phnum", c_uint16), 103 | ("e_shentsize", c_uint16), 104 | ("e_shnum", c_uint16), 105 | ("e_shstrndx", c_uint16), 106 | ] 107 | } 108 | 109 | @build_structure 110 | @select_class 111 | def elf_phdr(self): 112 | return { 113 | ELFCLASS32: [ 114 | ("p_type", c_uint32), 115 | ("p_offset", c_uint32), 116 | ("p_vaddr", c_uint32), 117 | ("p_paddr", c_uint32), 118 | ("p_filesz", c_uint32), 119 | ("p_memsz", c_uint32), 120 | ("p_flags", c_uint32), 121 | ("p_align", c_uint32), 122 | ], 123 | ELFCLASS64: [ 124 | ("p_type", c_uint32), 125 | ("p_flags", c_uint32), 126 | ("p_offset", c_uint64), 127 | ("p_vaddr", c_uint64), 128 | ("p_paddr", c_uint64), 129 | ("p_filesz", c_uint64), 130 | ("p_memsz", c_uint64), 131 | ("p_align", c_uint64), 132 | ] 133 | } 134 | 135 | @build_structure 136 | @select_class 137 | def elf_shdr(self): 138 | return { 139 | ELFCLASS32: [ 140 | ("sh_name", c_uint32), 141 | ("sh_type", c_uint32), 142 | ("sh_flags", c_uint32), 143 | ("sh_addr", c_uint32), 144 | ("sh_offset", c_uint32), 145 | ("sh_size", c_uint32), 146 | ("sh_link", c_uint32), 147 | ("sh_info", c_uint32), 148 | ("sh_addralign", c_uint32), 149 | ("sh_entsize", c_uint32), 150 | ], 151 | ELFCLASS64: [ 152 | ("sh_name", c_uint32), 153 | ("sh_type", c_uint32), 154 | ("sh_flags", c_uint64), 155 | ("sh_addr", c_uint64), 156 | ("sh_offset", c_uint64), 157 | ("sh_size", c_uint64), 158 | ("sh_link", c_uint32), 159 | ("sh_info", c_uint32), 160 | ("sh_addralign", c_uint64), 161 | ("sh_entsize", c_uint64), 162 | ] 163 | } 164 | 165 | @build_structure 166 | @select_class 167 | def elf_sym(self): 168 | return { 169 | ELFCLASS32: [ 170 | ("st_name", c_uint32), 171 | ("st_value", c_uint32), 172 | ("st_size", c_uint32), 173 | ("st_info", c_ubyte), 174 | ("st_other", c_ubyte), 175 | ("st_shndx", c_uint16), 176 | ], 177 | ELFCLASS64: [ 178 | ("st_name", c_uint32), 179 | ("st_info", c_ubyte), 180 | ("st_other", c_ubyte), 181 | ("st_shndx", c_uint16), 182 | ("st_value", c_uint64), 183 | ("st_size", c_uint64), 184 | ] 185 | } 186 | 187 | 188 | class ELFFile(ELFFactory): 189 | 190 | def __init__(self, data): 191 | 192 | if data[:4] != b"\x7fELF": 193 | raise ValueError("Data is not an elf image.") 194 | 195 | self.data = data 196 | 197 | ei_class, ei_data = data[4:6] 198 | super().__init__(ei_class, ei_data) 199 | 200 | self.ehdr = self.elf_ehdr().from_buffer(self.data) 201 | 202 | self.phdrs = (self.elf_phdr() * self.ehdr.e_phnum).from_buffer( 203 | self.data, self.ehdr.e_phoff) 204 | 205 | self.shdrs = (self.elf_shdr() * self.ehdr.e_shnum).from_buffer( 206 | self.data, self.ehdr.e_shoff) 207 | 208 | def shstr(self, shndx): 209 | 210 | strtab = self.shdrs[self.ehdr.e_shstrndx] 211 | offset = strtab.sh_offset + shndx 212 | end = self.data.find(b"\x00", offset) 213 | 214 | if end < 0: 215 | raise KeyError(shndx) 216 | 217 | return self.data[offset:end+1] 218 | 219 | EI_NIDENT = (16) 220 | EI_MAG0 = 0 221 | ELFMAG0 = 0x7f 222 | EI_MAG1 = 1 223 | ELFMAG1 = 'E' 224 | EI_MAG2 = 2 225 | ELFMAG2 = 'L' 226 | EI_MAG3 = 3 227 | ELFMAG3 = 'F' 228 | ELFMAG = "\177ELF" 229 | SELFMAG = 4 230 | EI_CLASS = 4 231 | ELFCLASSNONE = 0 232 | ELFCLASS32 = 1 233 | ELFCLASS64 = 2 234 | ELFCLASSNUM = 3 235 | EI_DATA = 5 236 | ELFDATANONE = 0 237 | ELFDATA2LSB = 1 238 | ELFDATA2MSB = 2 239 | ELFDATANUM = 3 240 | EI_VERSION = 6 241 | EI_OSABI = 7 242 | ELFOSABI_NONE = 0 243 | ELFOSABI_SYSV = 0 244 | ELFOSABI_HPUX = 1 245 | ELFOSABI_NETBSD = 2 246 | ELFOSABI_GNU = 3 247 | ELFOSABI_LINUX = ELFOSABI_GNU 248 | ELFOSABI_SOLARIS = 6 249 | ELFOSABI_AIX = 7 250 | ELFOSABI_IRIX = 8 251 | ELFOSABI_FREEBSD = 9 252 | ELFOSABI_TRU64 = 10 253 | ELFOSABI_MODESTO = 11 254 | ELFOSABI_OPENBSD = 12 255 | ELFOSABI_ARM_AEABI = 64 256 | ELFOSABI_ARM = 97 257 | ELFOSABI_STANDALONE = 255 258 | EI_ABIVERSION = 8 259 | EI_PAD = 9 260 | ET_NONE = 0 261 | ET_REL = 1 262 | ET_EXEC = 2 263 | ET_DYN = 3 264 | ET_CORE = 4 265 | ET_NUM = 5 266 | ET_LOOS = 0xfe00 267 | ET_HIOS = 0xfeff 268 | ET_LOPROC = 0xff00 269 | ET_HIPROC = 0xffff 270 | EM_NONE = 0 271 | EM_M32 = 1 272 | EM_SPARC = 2 273 | EM_386 = 3 274 | EM_68K = 4 275 | EM_88K = 5 276 | EM_860 = 7 277 | EM_MIPS = 8 278 | EM_S370 = 9 279 | EM_MIPS_RS3_LE = 10 280 | EM_PARISC = 15 281 | EM_VPP500 = 17 282 | EM_SPARC32PLUS = 18 283 | EM_960 = 19 284 | EM_PPC = 20 285 | EM_PPC64 = 21 286 | EM_S390 = 22 287 | EM_V800 = 36 288 | EM_FR20 = 37 289 | EM_RH32 = 38 290 | EM_RCE = 39 291 | EM_ARM = 40 292 | EM_FAKE_ALPHA = 41 293 | EM_SH = 42 294 | EM_SPARCV9 = 43 295 | EM_TRICORE = 44 296 | EM_ARC = 45 297 | EM_H8_300 = 46 298 | EM_H8_300H = 47 299 | EM_H8S = 48 300 | EM_H8_500 = 49 301 | EM_IA_64 = 50 302 | EM_MIPS_X = 51 303 | EM_COLDFIRE = 52 304 | EM_68HC12 = 53 305 | EM_MMA = 54 306 | EM_PCP = 55 307 | EM_NCPU = 56 308 | EM_NDR1 = 57 309 | EM_STARCORE = 58 310 | EM_ME16 = 59 311 | EM_ST100 = 60 312 | EM_TINYJ = 61 313 | EM_X86_64 = 62 314 | EM_PDSP = 63 315 | EM_FX66 = 66 316 | EM_ST9PLUS = 67 317 | EM_ST7 = 68 318 | EM_68HC16 = 69 319 | EM_68HC11 = 70 320 | EM_68HC08 = 71 321 | EM_68HC05 = 72 322 | EM_SVX = 73 323 | EM_ST19 = 74 324 | EM_VAX = 75 325 | EM_CRIS = 76 326 | EM_JAVELIN = 77 327 | EM_FIREPATH = 78 328 | EM_ZSP = 79 329 | EM_MMIX = 80 330 | EM_HUANY = 81 331 | EM_PRISM = 82 332 | EM_AVR = 83 333 | EM_FR30 = 84 334 | EM_D10V = 85 335 | EM_D30V = 86 336 | EM_V850 = 87 337 | EM_M32R = 88 338 | EM_MN10300 = 89 339 | EM_MN10200 = 90 340 | EM_PJ = 91 341 | EM_OPENRISC = 92 342 | EM_ARC_A5 = 93 343 | EM_XTENSA = 94 344 | EM_AARCH64 = 183 345 | EM_TILEPRO = 188 346 | EM_MICROBLAZE = 189 347 | EM_TILEGX = 191 348 | EM_NUM = 192 349 | EM_ALPHA = 0x9026 350 | EV_NONE = 0 351 | EV_CURRENT = 1 352 | EV_NUM = 2 353 | SHN_UNDEF = 0 354 | SHN_LORESERVE = 0xff00 355 | SHN_LOPROC = 0xff00 356 | SHN_BEFORE = 0xff00 357 | SHN_AFTER = 0xff01 358 | SHN_HIPROC = 0xff1f 359 | SHN_LOOS = 0xff20 360 | SHN_HIOS = 0xff3f 361 | SHN_ABS = 0xfff1 362 | SHN_COMMON = 0xfff2 363 | SHN_XINDEX = 0xffff 364 | SHN_HIRESERVE = 0xffff 365 | SHT_NULL = 0 366 | SHT_PROGBITS = 1 367 | SHT_SYMTAB = 2 368 | SHT_STRTAB = 3 369 | SHT_RELA = 4 370 | SHT_HASH = 5 371 | SHT_DYNAMIC = 6 372 | SHT_NOTE = 7 373 | SHT_NOBITS = 8 374 | SHT_REL = 9 375 | SHT_SHLIB = 10 376 | SHT_DYNSYM = 11 377 | SHT_INIT_ARRAY = 14 378 | SHT_FINI_ARRAY = 15 379 | SHT_PREINIT_ARRAY = 16 380 | SHT_GROUP = 17 381 | SHT_SYMTAB_SHNDX = 18 382 | SHT_NUM = 19 383 | SHT_LOOS = 0x60000000 384 | SHT_GNU_ATTRIBUTES = 0x6ffffff5 385 | SHT_GNU_HASH = 0x6ffffff6 386 | SHT_GNU_LIBLIST = 0x6ffffff7 387 | SHT_CHECKSUM = 0x6ffffff8 388 | SHT_LOSUNW = 0x6ffffffa 389 | SHT_SUNW_move = 0x6ffffffa 390 | SHT_SUNW_COMDAT = 0x6ffffffb 391 | SHT_SUNW_syminfo = 0x6ffffffc 392 | SHT_GNU_verdef = 0x6ffffffd 393 | SHT_GNU_verneed = 0x6ffffffe 394 | SHT_GNU_versym = 0x6fffffff 395 | SHT_HISUNW = 0x6fffffff 396 | SHT_HIOS = 0x6fffffff 397 | SHT_LOPROC = 0x70000000 398 | SHT_HIPROC = 0x7fffffff 399 | SHT_LOUSER = 0x80000000 400 | SHT_HIUSER = 0x8fffffff 401 | SHF_WRITE = (1 << 0) 402 | SHF_ALLOC = (1 << 1) 403 | SHF_EXECINSTR = (1 << 2) 404 | SHF_MERGE = (1 << 4) 405 | SHF_STRINGS = (1 << 5) 406 | SHF_INFO_LINK = (1 << 6) 407 | SHF_LINK_ORDER = (1 << 7) 408 | SHF_OS_NONCONFORMING = (1 << 8) 409 | SHF_GROUP = (1 << 9) 410 | SHF_TLS = (1 << 10) 411 | SHF_MASKOS = 0x0ff00000 412 | SHF_MASKPROC = 0xf0000000 413 | SHF_ORDERED = (1 << 30) 414 | SHF_EXCLUDE = (1 << 31) 415 | GRP_COMDAT = 0x1 416 | SYMINFO_BT_SELF = 0xffff 417 | SYMINFO_BT_PARENT = 0xfffe 418 | SYMINFO_BT_LOWRESERVE = 0xff00 419 | SYMINFO_FLG_DIRECT = 0x0001 420 | SYMINFO_FLG_PASSTHRU = 0x0002 421 | SYMINFO_FLG_COPY = 0x0004 422 | SYMINFO_FLG_LAZYLOAD = 0x0008 423 | SYMINFO_NONE = 0 424 | SYMINFO_CURRENT = 1 425 | SYMINFO_NUM = 2 426 | STB_LOCAL = 0 427 | STB_GLOBAL = 1 428 | STB_WEAK = 2 429 | STB_NUM = 3 430 | STB_LOOS = 10 431 | STB_GNU_UNIQUE = 10 432 | STB_HIOS = 12 433 | STB_LOPROC = 13 434 | STB_HIPROC = 15 435 | STT_NOTYPE = 0 436 | STT_OBJECT = 1 437 | STT_FUNC = 2 438 | STT_SECTION = 3 439 | STT_FILE = 4 440 | STT_COMMON = 5 441 | STT_TLS = 6 442 | STT_NUM = 7 443 | STT_LOOS = 10 444 | STT_GNU_IFUNC = 10 445 | STT_HIOS = 12 446 | STT_LOPROC = 13 447 | STT_HIPROC = 15 448 | STN_UNDEF = 0 449 | STV_DEFAULT = 0 450 | STV_INTERNAL = 1 451 | STV_HIDDEN = 2 452 | STV_PROTECTED = 3 453 | PN_XNUM = 0xffff 454 | PT_NULL = 0 455 | PT_LOAD = 1 456 | PT_DYNAMIC = 2 457 | PT_INTERP = 3 458 | PT_NOTE = 4 459 | PT_SHLIB = 5 460 | PT_PHDR = 6 461 | PT_TLS = 7 462 | PT_NUM = 8 463 | PT_LOOS = 0x60000000 464 | PT_GNU_EH_FRAME = 0x6474e550 465 | PT_GNU_STACK = 0x6474e551 466 | PT_GNU_RELRO = 0x6474e552 467 | PT_LOSUNW = 0x6ffffffa 468 | PT_SUNWBSS = 0x6ffffffa 469 | PT_SUNWSTACK = 0x6ffffffb 470 | PT_HISUNW = 0x6fffffff 471 | PT_HIOS = 0x6fffffff 472 | PT_LOPROC = 0x70000000 473 | PT_HIPROC = 0x7fffffff 474 | PF_X = (1 << 0) 475 | PF_W = (1 << 1) 476 | PF_R = (1 << 2) 477 | PF_MASKOS = 0x0ff00000 478 | PF_MASKPROC = 0xf0000000 479 | NT_PRSTATUS = 1 480 | NT_FPREGSET = 2 481 | NT_PRPSINFO = 3 482 | NT_PRXREG = 4 483 | NT_TASKSTRUCT = 4 484 | NT_PLATFORM = 5 485 | NT_AUXV = 6 486 | NT_GWINDOWS = 7 487 | NT_ASRS = 8 488 | NT_PSTATUS = 10 489 | NT_PSINFO = 13 490 | NT_PRCRED = 14 491 | NT_UTSNAME = 15 492 | NT_LWPSTATUS = 16 493 | NT_LWPSINFO = 17 494 | NT_PRFPXREG = 20 495 | NT_SIGINFO = 0x53494749 496 | NT_FILE = 0x46494c45 497 | NT_PRXFPREG = 0x46e62b7f 498 | NT_PPC_VMX = 0x100 499 | NT_PPC_SPE = 0x101 500 | NT_PPC_VSX = 0x102 501 | NT_386_TLS = 0x200 502 | NT_386_IOPERM = 0x201 503 | NT_X86_XSTATE = 0x202 504 | NT_S390_HIGH_GPRS = 0x300 505 | NT_S390_TIMER = 0x301 506 | NT_S390_TODCMP = 0x302 507 | NT_S390_TODPREG = 0x303 508 | NT_S390_CTRS = 0x304 509 | NT_S390_PREFIX = 0x305 510 | NT_S390_LAST_BREAK = 0x306 511 | NT_S390_SYSTEM_CALL = 0x307 512 | NT_S390_TDB = 0x308 513 | NT_ARM_VFP = 0x400 514 | NT_ARM_TLS = 0x401 515 | NT_ARM_HW_BREAK = 0x402 516 | NT_ARM_HW_WATCH = 0x403 517 | NT_VERSION = 1 518 | DT_NULL = 0 519 | DT_NEEDED = 1 520 | DT_PLTRELSZ = 2 521 | DT_PLTGOT = 3 522 | DT_HASH = 4 523 | DT_STRTAB = 5 524 | DT_SYMTAB = 6 525 | DT_RELA = 7 526 | DT_RELASZ = 8 527 | DT_RELAENT = 9 528 | DT_STRSZ = 10 529 | DT_SYMENT = 11 530 | DT_INIT = 12 531 | DT_FINI = 13 532 | DT_SONAME = 14 533 | DT_RPATH = 15 534 | DT_SYMBOLIC = 16 535 | DT_REL = 17 536 | DT_RELSZ = 18 537 | DT_RELENT = 19 538 | DT_PLTREL = 20 539 | DT_DEBUG = 21 540 | DT_TEXTREL = 22 541 | DT_JMPREL = 23 542 | DT_BIND_NOW = 24 543 | DT_INIT_ARRAY = 25 544 | DT_FINI_ARRAY = 26 545 | DT_INIT_ARRAYSZ = 27 546 | DT_FINI_ARRAYSZ = 28 547 | DT_RUNPATH = 29 548 | DT_FLAGS = 30 549 | DT_ENCODING = 32 550 | DT_PREINIT_ARRAY = 32 551 | DT_PREINIT_ARRAYSZ = 33 552 | DT_NUM = 34 553 | DT_LOOS = 0x6000000d 554 | DT_HIOS = 0x6ffff000 555 | DT_LOPROC = 0x70000000 556 | DT_HIPROC = 0x7fffffff 557 | DT_VALRNGLO = 0x6ffffd00 558 | DT_GNU_PRELINKED = 0x6ffffdf5 559 | DT_GNU_CONFLICTSZ = 0x6ffffdf6 560 | DT_GNU_LIBLISTSZ = 0x6ffffdf7 561 | DT_CHECKSUM = 0x6ffffdf8 562 | DT_PLTPADSZ = 0x6ffffdf9 563 | DT_MOVEENT = 0x6ffffdfa 564 | DT_MOVESZ = 0x6ffffdfb 565 | DT_FEATURE_1 = 0x6ffffdfc 566 | DT_POSFLAG_1 = 0x6ffffdfd 567 | DT_SYMINSZ = 0x6ffffdfe 568 | DT_SYMINENT = 0x6ffffdff 569 | DT_VALRNGHI = 0x6ffffdff 570 | DT_VALNUM = 12 571 | DT_ADDRRNGLO = 0x6ffffe00 572 | DT_GNU_HASH = 0x6ffffef5 573 | DT_TLSDESC_PLT = 0x6ffffef6 574 | DT_TLSDESC_GOT = 0x6ffffef7 575 | DT_GNU_CONFLICT = 0x6ffffef8 576 | DT_GNU_LIBLIST = 0x6ffffef9 577 | DT_CONFIG = 0x6ffffefa 578 | DT_DEPAUDIT = 0x6ffffefb 579 | DT_AUDIT = 0x6ffffefc 580 | DT_PLTPAD = 0x6ffffefd 581 | DT_MOVETAB = 0x6ffffefe 582 | DT_SYMINFO = 0x6ffffeff 583 | DT_ADDRRNGHI = 0x6ffffeff 584 | DT_ADDRNUM = 11 585 | DT_VERSYM = 0x6ffffff0 586 | DT_RELACOUNT = 0x6ffffff9 587 | DT_RELCOUNT = 0x6ffffffa 588 | DT_FLAGS_1 = 0x6ffffffb 589 | DT_VERDEF = 0x6ffffffc 590 | DT_VERDEFNUM = 0x6ffffffd 591 | DT_VERNEED = 0x6ffffffe 592 | DT_VERNEEDNUM = 0x6fffffff 593 | DT_VERSIONTAGNUM = 16 594 | DT_AUXILIARY = 0x7ffffffd 595 | DT_FILTER = 0x7fffffff 596 | DT_EXTRANUM = 3 597 | DF_ORIGIN = 0x00000001 598 | DF_SYMBOLIC = 0x00000002 599 | DF_TEXTREL = 0x00000004 600 | DF_BIND_NOW = 0x00000008 601 | DF_STATIC_TLS = 0x00000010 602 | DF_1_NOW = 0x00000001 603 | DF_1_GLOBAL = 0x00000002 604 | DF_1_GROUP = 0x00000004 605 | DF_1_NODELETE = 0x00000008 606 | DF_1_LOADFLTR = 0x00000010 607 | DF_1_INITFIRST = 0x00000020 608 | DF_1_NOOPEN = 0x00000040 609 | DF_1_ORIGIN = 0x00000080 610 | DF_1_DIRECT = 0x00000100 611 | DF_1_TRANS = 0x00000200 612 | DF_1_INTERPOSE = 0x00000400 613 | DF_1_NODEFLIB = 0x00000800 614 | DF_1_NODUMP = 0x00001000 615 | DF_1_CONFALT = 0x00002000 616 | DF_1_ENDFILTEE = 0x00004000 617 | DF_1_DISPRELDNE = 0x00008000 618 | DF_1_DISPRELPND = 0x00010000 619 | DF_1_NODIRECT = 0x00020000 620 | DF_1_IGNMULDEF = 0x00040000 621 | DF_1_NOKSYMS = 0x00080000 622 | DF_1_NOHDR = 0x00100000 623 | DF_1_EDITED = 0x00200000 624 | DF_1_NORELOC = 0x00400000 625 | DF_1_SYMINTPOSE = 0x00800000 626 | DF_1_GLOBAUDIT = 0x01000000 627 | DF_1_SINGLETON = 0x02000000 628 | DTF_1_PARINIT = 0x00000001 629 | DTF_1_CONFEXP = 0x00000002 630 | DF_P1_LAZYLOAD = 0x00000001 631 | DF_P1_GROUPPERM = 0x00000002 632 | VER_DEF_NONE = 0 633 | VER_DEF_CURRENT = 1 634 | VER_DEF_NUM = 2 635 | VER_FLG_BASE = 0x1 636 | VER_FLG_WEAK = 0x2 637 | VER_NDX_LOCAL = 0 638 | VER_NDX_GLOBAL = 1 639 | VER_NDX_LORESERVE = 0xff00 640 | VER_NDX_ELIMINATE = 0xff01 641 | VER_NEED_NONE = 0 642 | VER_NEED_CURRENT = 1 643 | VER_NEED_NUM = 2 644 | VER_FLG_WEAK = 0x2 645 | ELF_NOTE_SOLARIS = "SUNW Solaris" 646 | ELF_NOTE_GNU = "GNU" 647 | ELF_NOTE_PAGESIZE_HINT = 1 648 | NT_GNU_ABI_TAG = 1 649 | ELF_NOTE_ABI = NT_GNU_ABI_TAG 650 | ELF_NOTE_OS_LINUX = 0 651 | ELF_NOTE_OS_GNU = 1 652 | ELF_NOTE_OS_SOLARIS2 = 2 653 | ELF_NOTE_OS_FREEBSD = 3 654 | NT_GNU_HWCAP = 2 655 | NT_GNU_BUILD_ID = 3 656 | NT_GNU_GOLD_VERSION = 4 657 | -------------------------------------------------------------------------------- /wsym.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import ctypes 5 | import argparse 6 | 7 | from ctypes import sizeof, pointer 8 | 9 | import elf 10 | 11 | def add_symbols(elff, symbols): 12 | 13 | # 14 | # THE PLAN: 15 | # 16 | # - Keep the exiting file structure but create our own sections. 17 | # - add our data (symtab + symstrtab + shstrtab) 18 | # - Add our sections (ghosts + symtabhdr + strtabhdr + shstrtabhdr) 19 | # at the end of the file. 20 | # - Hijack e_shoff and point it to our sections. 21 | # 22 | 23 | shstrtab = b"" 24 | 25 | shdr_t = elff.elf_shdr() 26 | shdrs = [] 27 | 28 | # Add null section. 29 | nullhdr = shdr_t() 30 | shstrtab += b"\x00" 31 | shdrs.append(nullhdr) 32 | 33 | 34 | # Build a ghost section for each segment. 35 | # We need ghosts to handle binary whith no sections. 36 | nbg = 0 37 | for phdr in elff.phdrs: 38 | 39 | if phdr.p_type != elf.PT_LOAD: 40 | continue 41 | 42 | shdr = shdr_t() 43 | shdr.sh_name = len(shstrtab) 44 | shstrtab += bytes("GHOST%d_%.*x\x00" % ( 45 | nbg, elff.wordsize // 4, phdr.p_vaddr), "utf8") 46 | shdr.sh_type = elf.SHT_NOBITS 47 | shdr.sh_flags = elf.SHF_ALLOC 48 | if phdr.p_flags & elf.PF_X: 49 | shdr.sh_flags |= elf.SHF_EXECINSTR 50 | if phdr.p_flags & elf.PF_W: 51 | shdr.sh_flags |= elf.SHF_WRITE 52 | shdr.sh_addr = phdr.p_vaddr 53 | shdr.sh_offset = phdr.p_offset 54 | shdr.sh_size = phdr.p_memsz 55 | shdr.sh_link = 0 56 | shdr.sh_info = 0 57 | shdr.sh_addralign = 1 # Probably fine. 58 | shdr.sh_entsize = 0 59 | shdrs.append(shdr) 60 | nbg += 1 61 | 62 | 63 | # If there are shdr's in the original binary 64 | # we try to keep them. We do *not* need to 65 | # rewrite the original symtab. 66 | 67 | shoffset = len(shdrs) 68 | for shdr in elff.shdrs: 69 | 70 | shdr = shdr.copy() 71 | 72 | try: 73 | name = bytes(elff.shstr(shdr.sh_name)) 74 | except KeyError: 75 | name = b"corrupt\x00" 76 | 77 | shdr.sh_name = len(shstrtab) 78 | shstrtab += name 79 | 80 | shdr.sh_link += shoffset 81 | 82 | if shdr.sh_flags & elf.SHF_INFO_LINK: 83 | shdr.sh_info += shoffset 84 | 85 | shdrs.append(shdr) 86 | 87 | symstrtab = b"" 88 | 89 | 90 | # Collect symbols: 91 | 92 | sym_t = elff.elf_sym() 93 | symtab = [] 94 | 95 | nullsym = sym_t() 96 | nullsym.st_name = len(symstrtab) 97 | symstrtab += b"\x00" 98 | symtab.append(nullsym) 99 | 100 | for name, addr, size in symbols: 101 | for shndx, shdr in enumerate(shdrs): 102 | if shdr.sh_addr <= addr < shdr.sh_addr + shdr.sh_size: 103 | break 104 | else: 105 | print("ignored (bad addr): %#x %s" % (addr, name)) 106 | continue 107 | 108 | sym = sym_t() 109 | sym.st_name = len(symstrtab) 110 | symstrtab += bytes(name, "utf8") + b"\x00" 111 | sym.st_value = addr 112 | sym.st_size = size 113 | sym.st_info = (1 << 4) | 2 # GLOBAL FUNC 114 | sym.st_other = 0 115 | sym.st_shndx = shndx 116 | symtab.append(sym) 117 | 118 | 119 | # Add symtab 120 | symtabhdr = shdr_t() 121 | 122 | symtabhdr.sh_name = len(shstrtab) 123 | shstrtab += b".wsymtab\x00" 124 | symtabhdr.sh_type = elf.SHT_SYMTAB 125 | symtabhdr.sh_flags = 0 126 | symtabhdr.sh_addr = 0 127 | symtabhdr.sh_offset = len(elff.data) 128 | symtabhdr.sh_size = len(symtab) * sizeof(sym_t) 129 | symtabhdr.sh_link = len(shdrs) + 1 # list + [us, STRTAB] 130 | symtabhdr.sh_info = 0 # ? 131 | symtabhdr.sh_addralign = 1 132 | symtabhdr.sh_entsize = sizeof(sym_t) 133 | 134 | shdrs.append(symtabhdr) 135 | 136 | # Add symstrtab 137 | symstrtabhdr = shdr_t() 138 | 139 | symstrtabhdr.sh_name = len(shstrtab) 140 | shstrtab += b".strtab\x00" 141 | symstrtabhdr.sh_type = elf.SHT_STRTAB 142 | symstrtabhdr.sh_flags = 0 143 | symstrtabhdr.sh_addr = 0 144 | symstrtabhdr.sh_offset = len(elff.data) + symtabhdr.sh_size 145 | symstrtabhdr.sh_size = len(symstrtab) 146 | symstrtabhdr.sh_link = 0 147 | symstrtabhdr.sh_info = 0 148 | symstrtabhdr.sh_addralign = 1 149 | symstrtabhdr.sh_entsize = 0 150 | 151 | shdrs.append(symstrtabhdr) 152 | 153 | # Add shstrtab 154 | shstrtabhdr = shdr_t() 155 | 156 | shstrtabhdr.sh_name = len(shstrtab) 157 | shstrtab += b".shstrtab\x00" 158 | shstrtabhdr.sh_type = elf.SHT_STRTAB 159 | shstrtabhdr.sh_flags = 0 160 | shstrtabhdr.sh_addr = 0 161 | shstrtabhdr.sh_offset = len(elff.data) + symtabhdr.sh_size + symstrtabhdr.sh_size 162 | shstrtabhdr.sh_size = len(shstrtab) 163 | shstrtabhdr.sh_link = 0 164 | shstrtabhdr.sh_info = 0 165 | shstrtabhdr.sh_addralign = 1 166 | shstrtabhdr.sh_entsize = 0 167 | 168 | shdrs.append(shstrtabhdr) 169 | 170 | 171 | # We have all the elements, 172 | # build the new file. 173 | 174 | newdata = bytearray(len(elff.data) 175 | + symtabhdr.sh_size 176 | + symstrtabhdr.sh_size 177 | + shstrtabhdr.sh_size 178 | + sizeof(shdr_t) * len(shdrs)) 179 | 180 | offset = len(elff.data) 181 | newdata[0:offset] = elff.data 182 | 183 | for sym in symtab: 184 | newdata[offset:offset+sizeof(sym)] = sym 185 | offset += sizeof(sym) 186 | 187 | newdata[offset:offset+len(symstrtab)] = symstrtab 188 | offset += len(symstrtab) 189 | newdata[offset:offset+len(shstrtab)] = shstrtab 190 | offset += len(shstrtab) 191 | 192 | shoff = offset 193 | 194 | for shdr in shdrs: 195 | newdata[offset:offset+sizeof(shdr)] = shdr 196 | offset += sizeof(shdr) 197 | 198 | newelf = elf.ELFFile(newdata) 199 | 200 | # Don't forget to link everythin back to ehdr: 201 | newelf.ehdr.e_shoff = shoff 202 | newelf.ehdr.e_shentsize = ctypes.sizeof(shdr_t) 203 | newelf.ehdr.e_shnum = len(shdrs) 204 | newelf.ehdr.e_shstrndx = len(shdrs) - 1 205 | 206 | return newelf 207 | 208 | 209 | class FileParser(object): 210 | 211 | def __init__(self, path): 212 | self.file = argparse.FileType("r")(path) 213 | 214 | def log(self, msg, *args, **kwargs): 215 | print("%s: %s" % (self.__class__.__name__, msg), *args, **kwargs) 216 | 217 | 218 | class FlatParser(FileParser): 219 | 220 | def get_symbols(self, target, verbose=False): 221 | 222 | symbols = [] 223 | 224 | for line in self.file: 225 | if line.startswith("#"): 226 | continue 227 | splited = line.split() 228 | if len(splited) == 3: 229 | addr, name, size = splited 230 | elif len(splited) == 2: 231 | addr, name = splited 232 | size = "0" 233 | else: 234 | continue 235 | 236 | addr = int(addr, 16) 237 | size = int(size, 16) 238 | 239 | if verbose: 240 | self.log("%15s = %#x,\tsize=%d" % ( 241 | name, addr, size)) 242 | 243 | symbols.append((name, addr, size)) 244 | 245 | return symbols 246 | 247 | 248 | class NMParser(FileParser): 249 | 250 | def get_symbols(self, target, verbose=False): 251 | 252 | symbols = [] 253 | 254 | for line in self.file: 255 | if line.startswith("#"): 256 | continue 257 | splited = line.split() 258 | if len(splited) != 3: 259 | continue 260 | 261 | name, addr = splited[2], int(splited[0], 16) 262 | 263 | if verbose: 264 | self.log("%15s = %#x,\tsize=%d" % ( 265 | name, addr, 0)) 266 | 267 | symbols.append((name, addr, 0)) 268 | 269 | return symbols 270 | 271 | class IDAParser(FileParser): 272 | 273 | def get_symbols(self, target, verbose=False): 274 | 275 | # OK, IDA is weird, it uses section-relative addres. 276 | # UNLESS there are no sections, then it uses segments. 277 | # No way to know... Lets guess. 278 | 279 | for line in self.file: 280 | if line.split() == ["Start", "Length", "Name", "Class"]: 281 | break 282 | 283 | sections = [] 284 | for line in self.file: 285 | splited = line.split() 286 | if len(splited) != 4: 287 | break 288 | 289 | start_, _, _, name = splited 290 | start, _ = start_.split(":") 291 | name = bytes(name, "utf8") + b"\x00" 292 | sections.append([int(start, 16), name]) 293 | 294 | # Ok, this is where we guess, kinda. 295 | # Lets check if all those sections exit, 296 | # otherwise we'll consider they are segments. 297 | 298 | i = 0 299 | for shndx, shdr in enumerate(target.shdrs): 300 | if i == len(sections): 301 | break 302 | if target.shstr(shdr.sh_name) == sections[i][1]: 303 | sections[i][1] = shndx 304 | i += 1 305 | 306 | if i == len(sections): 307 | translations = {} 308 | for i, shndx in sections: 309 | translations[i] = target.shdrs[shndx].sh_addr 310 | else: 311 | self.log("Couldnt match %s as a section. Assuming segments." % (sections[i], )) 312 | translations = {} 313 | for i, _ in sections: 314 | translations[i] = target.phdrs[i+1].p_vaddr 315 | 316 | # OK, done guessing. 317 | 318 | for line in self.file: 319 | if line.split() == ["Address", "Publics", "by", "Value"]: 320 | break 321 | next(self.file) # burn empty line. 322 | 323 | symbols = [] 324 | 325 | for line in self.file: 326 | splited = line.split() 327 | if len(splited) != 2: 328 | break 329 | 330 | segment_offset, name = splited 331 | segment, offset = segment_offset.split(":") 332 | 333 | segment = int(segment, 16) 334 | offset = int(offset, 16) 335 | 336 | addr = translations[segment] + offset 337 | 338 | if verbose: 339 | self.log("%15s = %#x:%x + %#x = %#x,\tsize=%d" % ( 340 | name, segment, translations[segment], offset, addr, 0)) 341 | 342 | symbols.append((name, addr, 0)) 343 | 344 | return symbols 345 | 346 | 347 | if __name__ == '__main__': 348 | 349 | parser = argparse.ArgumentParser() 350 | parser.add_argument("input", type=argparse.FileType("rb")) 351 | parser.add_argument("output", type=argparse.FileType("wb")) 352 | 353 | parser.add_argument("-v", "--verbose", action="store_true") 354 | 355 | parser.set_defaults(symbols=[]) 356 | parser.add_argument("-f", "--flat", help="flat map format. (addr, name, [size])", 357 | type=FlatParser, dest="symbols", action="append") 358 | parser.add_argument("-i", "--ida", help="IDA .map format.", 359 | type=IDAParser, dest="symbols", action="append") 360 | parser.add_argument("-n", "--nm", help="nm format.", 361 | type=NMParser, dest="symbols", action="append") 362 | 363 | args = parser.parse_args() 364 | 365 | elff = elf.ELFFile(bytearray(args.input.read())) 366 | 367 | symbols = [] 368 | for parser in args.symbols: 369 | symbols += parser.get_symbols(elff, verbose=args.verbose) 370 | 371 | if not symbols: 372 | print("Warning: No symbols are being added. " 373 | "I'll still try though, even if its pointless.") 374 | 375 | newelf = add_symbols(elff, symbols) 376 | args.output.write(newelf.data) 377 | --------------------------------------------------------------------------------