├── .gitignore
├── LICENSE
├── README.md
├── ZwoELF
├── Compatibility.py
├── Elf.py
├── ElfParserLib.py
└── __init__.py
└── examples
├── IDAPython
└── addDynamicSymbols_ida.py
├── addRandomSections.py
├── createFalseDynstrSection.py
├── indirectJmpWithMemoryData.py
├── modifySymbolValue.py
├── newEntryPoint.py
├── obfuscatePltSection.py
└── readElf.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ZwoELF
2 | ============
3 |
4 | An ELF parsing and manipulation library for Python
5 |
6 | Why is it named "ZwoELF" (German for "twelve")? Because ELF is German for "eleven" and I like to go one step further than others ;-)
7 |
8 | This library works only with x86 and x86_64 ELF binaries. It was written as a parser library to understand the ELF format and gained some manipulation functions after a while. In contrast to most ELF analysis tools (for example "readelf"), I tried to use the information that the ELF loader uses to load the binary. As a result, the library ignores the "sections" of the binary in order to work even if the "sections" held wrong information.
9 |
10 | It is not finished yet (due to time problems) nor the manipulation in any state of reliable use.
11 |
12 |
13 | How to use it
14 | ============
15 |
16 | I added some examples in the directory "examples" that will show how the library can be used.
17 |
--------------------------------------------------------------------------------
/ZwoELF/Compatibility.py:
--------------------------------------------------------------------------------
1 | import struct
2 |
3 |
4 | """
5 | Python 2.7.3 issue:
6 | struct.unpack(fmt, buffer) does not work if buffer is a bytearray.
7 | (Works with Python 2.7.8.)
8 |
9 | This compatibility workaround was written when python 2.7.3 was current in
10 | Debian stable.
11 | """
12 | try:
13 | struct.unpack('>8)
655 | #define ELF32_R_TYPE(i) ((unsigned char)(i))
656 | #define ELF32_R_INFO(s,t) (((s)<<8)+(unsigned char)(t))
657 |
658 | Macros for 64 bit systems
659 | #define ELF64_R_SYM(i) ((i)>>32)
660 | #define ELF64_R_TYPE(i) ((i)&0xffffffff)
661 | #define ELF64_R_INFO(s,t) (((s)<<32)+(t))
662 | '''
663 | def __init__(self):
664 | # in executable and share object files => r_offset holds a virtual address
665 | self.r_offset = None
666 |
667 | # for 32 bit systems:
668 | # r_info = (r_sym << 8) + (r_type & 0xFF)
669 | self.r_info = None
670 |
671 | # for 32 bit systems calculated: "(unsigned char)(r_info)" or just "r_info & 0xFF"
672 | self.r_type = None
673 |
674 | # for 32 bit systems calculated: "r_info >> 8"
675 | self.r_sym = None
676 |
677 | # for 32 bit systems
678 | self.symbol = DynamicSymbol()
679 |
680 |
681 | class ElfN_Rela(object):
682 | '''
683 | typedef struct
684 | {
685 | Elf32_Addr r_offset; /* Address */
686 | Elf32_Word r_info; /* Relocation type and symbol index */
687 | Elf32_Sword r_addend; /* Addend */
688 | } Elf32_Rela;
689 |
690 | typedef struct
691 | {
692 | Elf64_Addr r_offset; /* Address */
693 | Elf64_Xword r_info; /* Relocation type and symbol index */
694 | Elf64_Sxword r_addend; /* Addend */
695 | } Elf64_Rela;
696 |
697 | Macros for 32/64 bit systems: see description for ElfN_Rel
698 | '''
699 | def __init__(self):
700 | # in executable and share object files => r_offset holds a virtual address
701 | self.r_offset = None
702 |
703 | self.r_info = None
704 | self.r_type = None
705 | self.r_sym = None
706 |
707 | self.r_addend = None
708 |
709 | self.symbol = DynamicSymbol()
710 |
711 |
712 | class ElfN_Sym(object):
713 | '''
714 | typedef struct elf32_sym {
715 | Elf32_Word st_name;
716 | Elf32_Addr st_value;
717 | Elf32_Word st_size;
718 | unsigned char st_info;
719 | unsigned char st_other;
720 | Elf32_Half st_shndx;
721 | } Elf32_Sym;
722 |
723 | typedef struct elf64_sym {
724 | Elf64_Word st_name; /* Symbol name, index in string tbl */
725 | unsigned char st_info; /* Type and binding attributes */
726 | unsigned char st_other; /* No defined meaning, 0 */
727 | Elf64_Half st_shndx; /* Associated section index */
728 | Elf64_Addr st_value; /* Value of the symbol */
729 | Elf64_Xword st_size; /* Associated symbol size */
730 | } Elf64_Sym;
731 | '''
732 | def __init__(self):
733 | st_name = None
734 | st_value = None
735 | st_size = None
736 | st_info = None
737 | st_other = None
738 | st_shndx = None
739 |
740 |
741 | class R_type(object):
742 | '''
743 | R_386_GOT32 This relocation type computes the distance from the
744 | base of the global offset
745 | table to the symbol's global offset table entry.
746 | It additionally instructs the link
747 | editor to build a global offset table.
748 |
749 | R_386_PLT32 This relocation type computes the address of the
750 | symbol's procedure linkage
751 | table entry and additionally instructs the link editor to
752 | build a procedure linkage table.
753 |
754 | R_386_COPY The link editor creates this relocation type for
755 | dynamic linking. Its offset
756 | member refers to a location in a writable segment.
757 | The symbol table index specifies a symbol that should exist
758 | both in the current object file and in a shared
759 | object. During execution, the dynamic linker copies data
760 | associated with the shared object's symbol to the location
761 | specified by the offset.
762 |
763 | R_386_GLOB_DAT This relocation type is used to set a global offset
764 | table entry to the address of the specified symbol. The
765 | special relocation type allows one to determine the
766 | correspondence between symbols and global offset table entries.
767 |
768 | R_3862_JMP_SLOT The link editor creates this relocation type for
769 | dynamic linking. Its offset member gives the location of a
770 | procedure linkage table entry. The dynamic linker modifies
771 | the procedure linkage table entry to transfer control to the
772 | designated symbol's address.
773 |
774 | R_386_RELATIVE The link editor creates this relocation type for
775 | dynamic linking. Its offset member gives a location within a
776 | shared object that contains a value representing a relative address.
777 | The dynamic linker computes the corresponding virtual
778 | address by adding the virtual address at which the shared object
779 | was loaded to the relative address. Relocation entries for this
780 | type must specify 0 for the symbol table index.
781 |
782 | R_386_GOTOFF This relocation type computes the difference between a
783 | symbol's value and the address of the global offset table. It
784 | additionally instructs the link editor to build the global
785 | offset table.
786 |
787 | R_386_GOTPC This relocation type resembles R_386_PC32, except it uses
788 | the address of the global offset table in its calculation.
789 | The symbol referenced in this relocation
790 | normally is _GLOBAL_OFFSET_TABLE_, which additionally instructs
791 | the link editor to build the global offset table.
792 | '''
793 | reverse_lookup = {0: "R_386_NONE", 1: "R_386_32", 2: "R_386_PC32",
794 | 3: "R_386_GOT32", 4: "R_386_PLT32", 5: "R_386_COPY",
795 | 6: "R_386_GLOB_DAT", 7: "R_386_JMP_SLOT", 8: "R_386_RELATIVE",
796 | 9: "R_386_GOTOFF", 10: "R_386_GOTPC"}
797 | R_386_NONE = 0
798 | R_386_32 = 1
799 | R_386_PC32 = 2
800 | R_386_GOT32 = 3
801 | R_386_PLT32 = 4
802 | R_386_COPY = 5
803 | R_386_GLOB_DAT = 6
804 | R_386_JMP_SLOT = 7
805 | R_386_RELATIVE = 8
806 | R_386_GOTOFF = 9
807 | R_386_GOTPC = 10
808 |
--------------------------------------------------------------------------------
/ZwoELF/ElfParserLib.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | # written by sqall
4 | # twitter: https://twitter.com/sqall01
5 | # blog: http://blog.h4des.org
6 | # github: https://github.com/sqall01
7 | #
8 | # Licensed under the GNU Public License, version 2.
9 |
10 | import binascii
11 | import struct
12 | import sys
13 | import hashlib
14 | from Elf import ElfN_Ehdr, Shstrndx, ElfN_Shdr, SH_flags, SH_type, \
15 | Elf32_Phdr, P_type, P_flags, D_tag, ElfN_Dyn, \
16 | ElfN_Rel, ElfN_Rela, ElfN_Sym, R_type, \
17 | Section, Segment, DynamicSymbol
18 |
19 |
20 | class ElfParser(object):
21 |
22 | def __init__(self, filename, force=False, startOffset=0,
23 | forceDynSymParsing=0, onlyParseHeader=False):
24 | self.forceDynSymParsing = forceDynSymParsing
25 | self.header = None
26 | self.segments = list()
27 | self.sections = list()
28 | self.fileParsed = False
29 | self.dynamicSymbolEntries = list()
30 | self.dynamicSegmentEntries = list()
31 | self.jumpRelocationEntries = list()
32 | self.relocationEntries = list()
33 | self.startOffset = startOffset
34 | self.data = bytearray()
35 | self.bits = 0
36 |
37 | # read file and convert data to list
38 | f = open(filename, "rb")
39 | f.seek(self.startOffset, 0)
40 | self.data = bytearray(f.read())
41 | f.close()
42 |
43 | # parse ELF file
44 | self.parseElf(self.data, onlyParseHeader=onlyParseHeader)
45 |
46 | # check if parsed ELF file and new generated one are the same
47 | if self.fileParsed is True and force is False:
48 | # generate md5 hash of file that was parsed
49 | tempHash = hashlib.md5()
50 | tempHash.update(self.data)
51 | oldFileHash = tempHash.digest()
52 |
53 | # generate md5 hash of file that was newly generated
54 | tempHash = hashlib.md5()
55 | tempHash.update(self.generateElf())
56 | newFileHash = tempHash.digest()
57 |
58 | if oldFileHash != newFileHash:
59 | raise NotImplementedError('Not able to parse and ' \
60 | + 're-generate ELF file correctly. This can happen '\
61 | + 'when the ELF file is parsed out of an other file '\
62 | + 'like a core dump. Use "force=True" to ignore this '\
63 | + 'check.')
64 |
65 | # this function interprets the r_info field from ElfN_Rel(a) structs
66 | # depending on self.bits
67 | def relocationSymIdxAndTypeFromInfo(self, rInfo):
68 | if self.bits == 32:
69 | rSym = (rInfo >> 8)
70 | rType = (rInfo & 0xff)
71 | elif self.bits == 64:
72 | rSym = (rInfo >> 32)
73 | rType = (rInfo & 0xffffffff)
74 | return (rSym, rType)
75 |
76 |
77 | # this function converts a section header entry to a list of data
78 | # return values: (bytearray) converted section header entry
79 | def sectionHeaderEntryToBytearray(self, sectionHeaderEntryToWrite):
80 | if self.bits == 32:
81 | structFormat = '< 2I 4I 2I 2I'
82 | elif self.bits == 64:
83 | structFormat = '< 2I 4Q 2I 2Q'
84 |
85 | sectionHeaderEntryRaw = bytearray(struct.pack(structFormat,
86 | # uint32_t sh_name;
87 | sectionHeaderEntryToWrite.sh_name,
88 | # uint32_t sh_type;
89 | sectionHeaderEntryToWrite.sh_type,
90 | # uintN_t sh_flags; (N = 32/64)
91 | sectionHeaderEntryToWrite.sh_flags,
92 | # ElfN_Addr sh_addr; (N = 32/64)
93 | sectionHeaderEntryToWrite.sh_addr,
94 | # ElfN_Off sh_offset; (N = 32/64)
95 | sectionHeaderEntryToWrite.sh_offset,
96 | # uintN_t sh_size; (N = 32/64)
97 | sectionHeaderEntryToWrite.sh_size,
98 | # uint32_t sh_link;
99 | sectionHeaderEntryToWrite.sh_link,
100 | # uint32_t sh_info;
101 | sectionHeaderEntryToWrite.sh_info,
102 | # uintN_t sh_addralign; (N = 32/64)
103 | sectionHeaderEntryToWrite.sh_addralign,
104 | # uintN_t sh_entsize; (N = 32/64)
105 | sectionHeaderEntryToWrite.sh_entsize,
106 | ))
107 |
108 | return sectionHeaderEntryRaw
109 |
110 |
111 | # this function generates a new section
112 | # return values: (Section) new generated section
113 | def generateNewSection(self, sectionName, sh_name, sh_type, sh_flags,
114 | sh_addr, sh_offset, sh_size, sh_link, sh_info, sh_addralign,
115 | sh_entsize):
116 | newsection = Section()
117 |
118 | newsection.sectionName = sectionName
119 |
120 | '''
121 | uint32_t sh_name;
122 | '''
123 | newsection.elfN_shdr.sh_name = sh_name
124 |
125 | '''
126 | uint32_t sh_type;
127 | '''
128 | newsection.elfN_shdr.sh_type = sh_type
129 |
130 | '''
131 | uintN_t sh_flags; (N = 32/64)
132 | '''
133 | newsection.elfN_shdr.sh_flags = sh_flags
134 |
135 | '''
136 | ElfN_Addr sh_addr; (N = 32/64)
137 | '''
138 | newsection.elfN_shdr.sh_addr = sh_addr
139 |
140 | '''
141 | ElfN_Off sh_offset; (N = 32/64)
142 | '''
143 | newsection.elfN_shdr.sh_offset = sh_offset
144 |
145 | '''
146 | uintN_t sh_size; (N = 32/64)
147 | '''
148 | newsection.elfN_shdr.sh_size = sh_size
149 |
150 | '''
151 | uint32_t sh_link;
152 | '''
153 | newsection.elfN_shdr.sh_link = sh_link
154 |
155 | '''
156 | uint32_t sh_info;
157 | '''
158 | newsection.elfN_shdr.sh_info = sh_info
159 |
160 | '''
161 | uintN_t sh_addralign; (N = 32/64)
162 | '''
163 | newsection.elfN_shdr.sh_addralign = sh_addralign
164 |
165 | '''
166 | uintN_t sh_entsize; (N = 32/64)
167 | '''
168 | newsection.elfN_shdr.sh_entsize = sh_entsize
169 |
170 | return newsection
171 |
172 |
173 | # this function parses a dynamic symbol at the given offset
174 | # return values: (DynamicSymbol) the parsed dynamic symbol
175 | def _parseDynamicSymbol(self, offset, stringTableOffset, stringTableSize):
176 |
177 | # check if the file was completely parsed before
178 | if self.fileParsed is False:
179 | raise ValueError("Operation not possible. " \
180 | + "File was not completely parsed before.")
181 |
182 | tempSymbol = DynamicSymbol()
183 |
184 | # get values from the symbol table
185 | """
186 | typedef struct {
187 | uint32_t st_name;
188 | Elf32_Addr st_value; // *
189 | uint32_t st_size; // *
190 | unsigned char st_info;
191 | unsigned char st_other;
192 | uint16_t st_shndx;
193 | } Elf32_Sym;
194 |
195 | typedef struct {
196 | uint32_t st_name;
197 | unsigned char st_info;
198 | unsigned char st_other;
199 | uint16_t st_shndx;
200 | Elf64_Addr st_value; // *
201 | uint64_t st_size; // *
202 | } Elf64_Sym;
203 |
204 | Difference: order (*)
205 | """
206 | if self.bits == 32:
207 | fmt = ' sh_size of string table section = 0
696 | # => Non-zero indexes to string table are invalid
697 |
698 | # list of sections not empty => read whole string table
699 | if self.sections:
700 | nStart = self.sections[self.header.e_shstrndx].elfN_shdr.sh_offset
701 | nEnd = nStart + self.sections[self.header.e_shstrndx].elfN_shdr.sh_size
702 | stringtable_str = buffer_list[nStart:nEnd]
703 |
704 | # get name from string table for each section
705 | for i in range(len(self.sections)):
706 |
707 | # check if string table exists => abort reading
708 | if len(stringtable_str) == 0:
709 | break
710 |
711 | nStart = self.sections[i].elfN_shdr.sh_name
712 | nEnd = stringtable_str.find('\x00', nStart)
713 | # use empty string if string is not terminated (nEnd == -1)
714 | nEnd = max(nStart, nEnd)
715 | self.sections[i].sectionName = bytes(stringtable_str[nStart:nEnd])
716 |
717 |
718 | ###############################################
719 | # parse program header table
720 |
721 | '''
722 | typedef struct {
723 | uint32_t p_type;
724 | Elf32_Off p_offset;
725 | Elf32_Addr p_vaddr;
726 | Elf32_Addr p_paddr;
727 | uint32_t p_filesz;
728 | uint32_t p_memsz;
729 | uint32_t p_flags;
730 | uint32_t p_align;
731 | } Elf32_Phdr;
732 |
733 | typedef struct {
734 | uint32_t p_type;
735 | uint32_t p_flags;
736 | Elf64_Off p_offset;
737 | Elf64_Addr p_vaddr;
738 | Elf64_Addr p_paddr;
739 | uint64_t p_filesz;
740 | uint64_t p_memsz;
741 | uint64_t p_align;
742 | } Elf64_Phdr;
743 |
744 | The main difference lies in the location of p_flags within the struct.
745 | '''
746 |
747 | # create a list of the program_header_table
748 | self.segments = list()
749 |
750 | for i in range(self.header.e_phnum):
751 | '''
752 | uint32_t p_type;
753 |
754 | This member of the Phdr struct tells what kind of segment
755 | this array element describes or how to interpret the array
756 | element's information.
757 |
758 |
759 | (uint32_t p_flags; (Elf64_Phdr only, see below))
760 |
761 |
762 | ElfN_Off p_offset; (N = 32/64)
763 |
764 | This member holds the offset from the beginning of the
765 | file at which the first byte of the segment resides.
766 |
767 |
768 | ElfN_Addr p_vaddr; (N = 32/64)
769 |
770 | This member holds the virtual address at which the first
771 | byte of the segment resides in memory.
772 |
773 |
774 | ElfN_Addr p_paddr; (N = 32/64)
775 |
776 | On systems for which physical addressing is relevant, this
777 | member is reserved for the segment's physical address.
778 | Under BSD this member is not used and must be zero.
779 |
780 |
781 | uintN_t p_filesz; (N = 32/64)
782 |
783 | This member holds the number of bytes in the file image of
784 | the segment. It may be zero.
785 |
786 |
787 | uintN_t p_memsz; (N = 32/64)
788 |
789 | This member holds the number of bytes in the memory image
790 | of the segment. It may be zero.
791 |
792 |
793 | uint32_t p_flags; (Elf32_Phdr only, for 64 see above)
794 |
795 | This member holds a bitmask of flags relevant to the segment:
796 |
797 | PF_X An executable segment.
798 | PF_W A writable segment.
799 | PF_R A readable segment.
800 |
801 | A text segment commonly has the flags PF_X and PF_R.
802 | A data segment commonly has PF_X, PF_W and PF_R.
803 |
804 | uintN_t p_align; (N = 32/64)
805 |
806 | This member holds the value to which the segments are aligned
807 | in memory and in the file. Loadable process segments
808 | must have congruent values for p_vaddr and p_offset, modulo
809 | the page size. Values of zero and one mean no alignment is
810 | required. Otherwise, p_align should be a positive, integral
811 | power of two, and p_vaddr should equal p_offset, modulo
812 | p_align.
813 | '''
814 |
815 | tempSegment = Segment()
816 | tempOffset = self.header.e_phoff + i*self.header.e_phentsize
817 |
818 | if self.bits == 32:
819 | unpackedSegment = struct.unpack('< I 5I I I', \
820 | buffer_list[tempOffset:tempOffset+32])
821 | elif self.bits == 64:
822 | unpackedSegment = struct.unpack('< I I 5Q Q', \
823 | buffer_list[tempOffset:tempOffset+56])
824 | # order elements as in Elf32_Phdr
825 | unpackedSegment = unpackedSegment[0:1] + unpackedSegment[2:7] \
826 | + unpackedSegment[1:2] + unpackedSegment[7:8]
827 |
828 | del tempOffset
829 |
830 | (
831 | tempSegment.elfN_Phdr.p_type,
832 | tempSegment.elfN_Phdr.p_offset, # 32/64 bit!
833 | tempSegment.elfN_Phdr.p_vaddr, # 32/64 bit!
834 | tempSegment.elfN_Phdr.p_paddr, # 32/64 bit!
835 | tempSegment.elfN_Phdr.p_filesz, # 32/64 bit!
836 | tempSegment.elfN_Phdr.p_memsz, # 32/64 bit!
837 | tempSegment.elfN_Phdr.p_flags, # position as in Elf32_Phdr
838 | tempSegment.elfN_Phdr.p_align, # 32/64 bit!
839 | ) = unpackedSegment
840 |
841 | # check which sections are in the current segment
842 | # (in memory) and add them
843 | for section in self.sections:
844 | segStart = tempSegment.elfN_Phdr.p_vaddr
845 | segEnd = segStart + tempSegment.elfN_Phdr.p_memsz
846 | sectionStart = section.elfN_shdr.sh_addr
847 | sectionEnd = sectionStart + section.elfN_shdr.sh_size
848 |
849 | if segStart <= sectionStart and sectionEnd <= segEnd:
850 | tempSegment.sectionsWithin.append(section)
851 |
852 | self.segments.append(tempSegment)
853 |
854 |
855 | # get all segments within a segment
856 | for outerSegment in self.segments:
857 | # PT_GNU_STACK only holds access rights
858 | if outerSegment.elfN_Phdr.p_type == P_type.PT_GNU_STACK:
859 | continue
860 |
861 | for segmentWithin in self.segments:
862 | # PT_GNU_STACK only holds access rights
863 | if segmentWithin.elfN_Phdr.p_type == P_type.PT_GNU_STACK:
864 | continue
865 |
866 | # skip if segments are the same
867 | if segmentWithin == outerSegment:
868 | continue
869 |
870 | # check if segmentWithin lies within the outerSegment
871 | innerStart = segmentWithin.elfN_Phdr.p_offset
872 | innerEnd = innerStart + segmentWithin.elfN_Phdr.p_filesz
873 | outerStart = outerSegment.elfN_Phdr.p_offset
874 | outerEnd = outerStart + outerSegment.elfN_Phdr.p_filesz
875 |
876 | if outerStart <= innerStart and innerEnd <= outerEnd:
877 | outerSegment.segmentsWithin.append(segmentWithin)
878 |
879 |
880 | ###############################################
881 | # parse dynamic segment entries
882 |
883 | '''
884 | typedef struct {
885 | Elf32_Sword d_tag;
886 | union {
887 | Elf32_Word d_val;
888 | Elf32_Addr d_ptr;
889 | } d_un;
890 | } Elf32_Dyn;
891 |
892 | typedef struct {
893 | Elf64_Sxword d_tag;
894 | union {
895 | Elf64_Xword d_val;
896 | Elf64_Addr d_ptr;
897 | } d_un;
898 | } Elf64_Dyn;
899 | '''
900 |
901 | # find dynamic segment
902 | dynamicSegment = None
903 | for segment in self.segments:
904 | if segment.elfN_Phdr.p_type == P_type.PT_DYNAMIC:
905 | dynamicSegment = segment
906 | break
907 | if dynamicSegment is None:
908 | raise ValueError("Segment of type PT_DYNAMIC was not found.")
909 |
910 | # create a list for all dynamic segment entries
911 | self.dynamicSegmentEntries = list()
912 |
913 | if self.bits == 32:
914 | structFmt = ' size of symbol table is difference between string and symbol table
1036 | estimatedSymbolTableSize = stringTableOffset - symbolTableOffset
1037 |
1038 | # find .dynsym section in sections
1039 | # and only use if it exists once
1040 | dynSymSection = None
1041 | dynSymSectionDuplicated = False
1042 | dynSymSectionIgnore = False
1043 | dynSymEstimationIgnore = False
1044 | for section in self.sections:
1045 | if section.sectionName == ".dynsym":
1046 | # check if .dynsym section only exists once
1047 | # (because section entries are optional and can
1048 | # be easily manipulated)
1049 | if dynSymSection is None:
1050 | dynSymSection = section
1051 |
1052 | # when .dynsym section exists multiple times
1053 | # do not use it
1054 | else:
1055 | dynSymSectionDuplicated = True
1056 | break
1057 |
1058 | # check if .dynsym section exists
1059 | if dynSymSection is None:
1060 | print 'NOTE: ".dynsym" section was not found. Trying to use ' \
1061 | + 'estimation to parse all symbols from the symbol table'
1062 | dynSymSectionIgnore = True
1063 |
1064 | # check if .dynsym section was found multiple times
1065 | elif dynSymSectionDuplicated is True:
1066 | print 'NOTE: ".dynsym" section was found multiple times. ' \
1067 | + 'Trying to use estimation to parse all symbols from' \
1068 | + 'the symbol table'
1069 | dynSymSectionIgnore = True
1070 |
1071 | # check if symbol table offset matches the offset of the
1072 | # ".dynsym" section
1073 | elif dynSymSection.elfN_shdr.sh_offset != symbolTableOffset:
1074 | print 'NOTE: ".dynsym" section offset does not match ' \
1075 | + 'offset of symbol table. Ignoring the section ' \
1076 | + 'and using the estimation.'
1077 | dynSymSectionIgnore = True
1078 |
1079 | # check if the size of the ".dynsym" section matches the
1080 | # estimated size
1081 | elif dynSymSection.elfN_shdr.sh_size != estimatedSymbolTableSize:
1082 |
1083 | # check if forceDynSymParsing was not set (default value is 0)
1084 | if self.forceDynSymParsing == 0:
1085 | print 'WARNING: ".dynsym" size does not match the estimated ' \
1086 | + 'size. One (or both) are wrong. Ignoring the dynamic ' \
1087 | + ' symbols. You can force the using of the ".dynsym" ' \
1088 | + 'section by setting "forceDynSymParsing=1" or force ' \
1089 | + 'the using of the estimated size by setting ' \
1090 | + '"forceDynSymParsing=2".'
1091 |
1092 | # ignore dynamic symbols
1093 | dynSymSectionIgnore = True
1094 | dynSymEstimationIgnore = True
1095 |
1096 | # forcing the use of the ".dynsym" section
1097 | elif self.forceDynSymParsing == 1:
1098 |
1099 | dynSymSectionIgnore = False
1100 | dynSymEstimationIgnore = True
1101 |
1102 | # forcing the use of the estimation
1103 | elif self.forceDynSymParsing == 2:
1104 |
1105 | dynSymSectionIgnore = True
1106 | dynSymEstimationIgnore = False
1107 |
1108 | # value does not exists
1109 | else:
1110 | raise TypeError('"forceDynSymParsing" uses an invalid value.')
1111 |
1112 | # use ".dynsym" section information (when considered correct)
1113 | if dynSymSectionIgnore is False:
1114 |
1115 | # parse the complete symbol table based on the
1116 | # ".dynsym" section
1117 | for i in range(dynSymSection.elfN_shdr.sh_size \
1118 | / symbolEntrySize):
1119 |
1120 | tempOffset = symbolTableOffset + (i*symbolEntrySize)
1121 | tempSymbol = self._parseDynamicSymbol(tempOffset,
1122 | stringTableOffset, stringTableSize)
1123 |
1124 | # add entry to dynamic symbol entries list
1125 | self.dynamicSymbolEntries.append(tempSymbol)
1126 |
1127 | # use estimation to parse dynamic symbols
1128 | elif (dynSymSectionIgnore is True
1129 | and dynSymEstimationIgnore is False):
1130 |
1131 | # parse the complete symbol table based on the
1132 | # estimation
1133 | for i in range(estimatedSymbolTableSize \
1134 | / symbolEntrySize):
1135 |
1136 | tempOffset = symbolTableOffset + (i*symbolEntrySize)
1137 | tempSymbol = self._parseDynamicSymbol(tempOffset,
1138 | stringTableOffset, stringTableSize)
1139 |
1140 | # add entry to dynamic symbol entries list
1141 | self.dynamicSymbolEntries.append(tempSymbol)
1142 |
1143 | # holds tuples: (type, offset, size, targetlist)
1144 | relocTODO = []
1145 |
1146 | # DT_JMPREL
1147 | if jmpRelOffset is not None:
1148 | if pltRelType is None:
1149 | raise ValueError('DT_JMPREL present but DT_PLTREL not.')
1150 | if pltRelSize is None:
1151 | raise ValueError('DT_JMPREL present but DT_PLTRELSZ not.')
1152 |
1153 | if pltRelType == D_tag.DT_REL:
1154 | if relEntrySize is None:
1155 | raise ValueError('DT_JMPREL present with ' \
1156 | + ' DT_PLTREL == DT_REL, but DT_RELSZ not present')
1157 | elif pltRelType == D_tag.DT_RELA:
1158 | if relaEntrySize is None:
1159 | raise ValueError('DT_JMPREL present with ' \
1160 | + ' DT_PLTREL == DT_RELA, but DT_RELASZ not present')
1161 | else:
1162 | raise ValueError('Invalid/unexpected DT_PLTREL (pltRelType).')
1163 |
1164 | self.jumpRelocationEntries = list()
1165 | relocTODO.append((pltRelType, jmpRelOffset, pltRelSize,
1166 | self.jumpRelocationEntries))
1167 |
1168 | # DT_REL (only mandatory hwn DT_RELA is not present)
1169 | if relOffset is not None:
1170 | if relSize is None:
1171 | raise ValueError('DT_REL present but DT_RELSZ not.')
1172 |
1173 | if relEntrySize is None:
1174 | raise ValueError('DT_REL present but DT_RELENT not.')
1175 |
1176 | self.relocationEntries = list()
1177 | relocTODO.append((D_tag.DT_REL, relOffset, relSize,
1178 | self.relocationEntries))
1179 |
1180 | # DT_RELA
1181 | if relaOffset is not None:
1182 | if relaSize is None:
1183 | raise ValueError('DT_RELA present but DT_RELASZ not.')
1184 |
1185 | if relaEntrySize is None:
1186 | raise ValueError('DT_RELA present but DT_RELAENT not.')
1187 |
1188 | self.relocationEntries = list()
1189 | relocTODO.append((D_tag.DT_RELA, relaOffset, relaSize,
1190 | self.relocationEntries))
1191 |
1192 | if relOffset is not None and relaOffset is not None:
1193 | raise RuntimeError('INTERNAL ERROR: TODO REL READ 1')
1194 |
1195 | if len(relocTODO) < 1:
1196 | raise RuntimeError('INTERNAL ERROR: TODO REL READ 2')
1197 |
1198 |
1199 | for relocType, relocOffset, relocSize, relocList in relocTODO:
1200 | if relocType == D_tag.DT_REL:
1201 | relocEntrySize = relEntrySize
1202 | elif relocType == D_tag.DT_RELA:
1203 | relocEntrySize = relaEntrySize
1204 |
1205 | if relocType == D_tag.DT_REL:
1206 | if self.bits == 32:
1207 | structFmt = ' r_offset holds a virtual address
1227 | relocEntry.r_offset,
1228 |
1229 | # ElfN_Word r_info; (N = 32/64)
1230 | relocEntry.r_info,
1231 | ) = struct.unpack(structFmt, self.data[tempOffset:tempOffset+relocEntrySize])
1232 | elif relocType == D_tag.DT_RELA:
1233 | relocEntry = ElfN_Rela()
1234 | (
1235 | # ElfN_Addr r_offset; (N = 32/64)
1236 | # in executable and share object files
1237 | # => r_offset holds a virtual address
1238 | relocEntry.r_offset,
1239 |
1240 | # ElfN_Word r_info; (N = 32/64)
1241 | relocEntry.r_info,
1242 |
1243 | relocEntry.r_addend,
1244 | ) = struct.unpack(structFmt, self.data[tempOffset:tempOffset+relocEntrySize])
1245 |
1246 | del tempOffset
1247 |
1248 | (relocEntry.r_sym, relocEntry.r_type) = \
1249 | self.relocationSymIdxAndTypeFromInfo(relocEntry.r_info)
1250 |
1251 | # get values from the symbol table
1252 | tempOffset = symbolTableOffset \
1253 | + (relocEntry.r_sym*symbolEntrySize)
1254 | tempSymbol = self._parseDynamicSymbol(tempOffset,
1255 | stringTableOffset, stringTableSize)
1256 |
1257 | # check if parsed dynamic symbol already exists
1258 | # if it does => use already existed dynamic symbol
1259 | # else => use newly parsed dynamic symbol
1260 | dynamicSymbolFound = False
1261 | for dynamicSymbol in self.dynamicSymbolEntries:
1262 | if (tempSymbol.ElfN_Sym.st_name
1263 | == dynamicSymbol.ElfN_Sym.st_name
1264 | and tempSymbol.ElfN_Sym.st_value
1265 | == dynamicSymbol.ElfN_Sym.st_value
1266 | and tempSymbol.ElfN_Sym.st_size
1267 | == dynamicSymbol.ElfN_Sym.st_size
1268 | and tempSymbol.ElfN_Sym.st_info
1269 | == dynamicSymbol.ElfN_Sym.st_info
1270 | and tempSymbol.ElfN_Sym.st_other
1271 | == dynamicSymbol.ElfN_Sym.st_other
1272 | and tempSymbol.ElfN_Sym.st_shndx
1273 | == dynamicSymbol.ElfN_Sym.st_shndx):
1274 | relocEntry.symbol = dynamicSymbol
1275 | dynamicSymbolFound = True
1276 | break
1277 | if dynamicSymbolFound is False:
1278 | relocEntry.symbol = tempSymbol
1279 |
1280 | relocList.append(relocEntry)
1281 |
1282 |
1283 | # this function dumps a list of relocations (used in printElf())
1284 | # return values: None
1285 | def printRelocations(self, relocationList, title):
1286 | printAddend = len(relocationList) \
1287 | and type(relocationList[0]) == ElfN_Rela
1288 |
1289 | # output all jump relocation entries
1290 | print("%s (%d entries)" % (title, len(relocationList)))
1291 | print("No."),
1292 | print("\t"),
1293 | print("MemAddr"),
1294 | print("\t"),
1295 | print("File offset"),
1296 | print("\t"),
1297 | print("Info"),
1298 | print("\t\t"),
1299 | print("Type"),
1300 | print("\t\t"),
1301 | if printAddend:
1302 | print("Addend"),
1303 | print("\t\t"),
1304 | print("Sym. value"),
1305 | print("\t"),
1306 | print("Sym. name"),
1307 | print
1308 | print("\t"),
1309 | print("(r_offset)"),
1310 | print("\t"),
1311 | print("\t"),
1312 | print("\t"),
1313 | print("(r_info)"),
1314 | print("\t"),
1315 | print("(r_type)"),
1316 | if printAddend:
1317 | print("\t"),
1318 | print("(r_addend)"),
1319 | print
1320 |
1321 | counter = 0
1322 | for entry in relocationList:
1323 | symbol = entry.symbol.ElfN_Sym
1324 | print("%d" % counter),
1325 | print("\t"),
1326 | print("0x" + ("%x" % entry.r_offset).zfill(8)),
1327 | print("\t"),
1328 |
1329 | # try to convert the virtual memory address to a file offset
1330 | # in executable and share object files
1331 | # => r_offset holds a virtual address
1332 | try:
1333 | print("0x" + ("%x" \
1334 | % self.virtualMemoryAddrToFileOffset(
1335 | entry.r_offset)).zfill(8)),
1336 | except:
1337 | print("None\t"),
1338 |
1339 | print("\t"),
1340 | print("0x" + ("%x" % entry.r_info).zfill(8)),
1341 | print("\t"),
1342 |
1343 | # translate type
1344 | if entry.r_type in R_type.reverse_lookup.keys():
1345 | print("%s" % R_type.reverse_lookup[entry.r_type]),
1346 | else:
1347 | print("0x%x" % entry.r_type),
1348 |
1349 | if printAddend:
1350 | if type(entry) == ElfN_Rela:
1351 | print("\t"),
1352 | print("0x" + ("%x" % entry.r_addend).zfill(8)),
1353 | else:
1354 | print("\t\t"),
1355 |
1356 | print("\t"),
1357 | print("0x" + ("%x" % symbol.st_value).zfill(8)),
1358 |
1359 | print("\t"),
1360 | print(entry.symbol.symbolName),
1361 |
1362 | print
1363 |
1364 | counter += 1
1365 |
1366 | print
1367 |
1368 |
1369 | # this function outputs the parsed ELF file (like readelf)
1370 | # return values: None
1371 | def printElf(self):
1372 |
1373 | # check if the file was completely parsed before
1374 | if self.fileParsed is False:
1375 | raise ValueError("Operation not possible. " \
1376 | + "File was not completely parsed before.")
1377 |
1378 | # output header
1379 | print "ELF header:"
1380 | print "Type: %s" % ElfN_Ehdr.E_type.reverse_lookup[self.header.e_type]
1381 | print "Version: %s" \
1382 | % ElfN_Ehdr.EI_VERSION.reverse_lookup[self.header.e_ident[6]]
1383 | print "Machine: %s" \
1384 | % ElfN_Ehdr.E_machine.reverse_lookup[self.header.e_machine]
1385 | print "Entry point address: 0x%x" % self.header.e_entry
1386 | print "Program header table offset in bytes: 0x%x (%d)" \
1387 | % (self.header.e_phoff, self.header.e_phoff)
1388 | print "Section header table offset in bytes: 0x%x (%d)" \
1389 | % (self.header.e_shoff, self.header.e_shoff)
1390 | print "Flags: 0x%x (%d)" % (self.header.e_flags, self.header.e_flags)
1391 | print "Size of ELF header in bytes: 0x%x (%d)" \
1392 | % (self.header.e_ehsize, self.header.e_ehsize)
1393 | print "Size of each program header entry in bytes: 0x%x (%d)" \
1394 | % (self.header.e_phentsize, self.header.e_phentsize)
1395 | print "Number of program header entries: %d" % self.header.e_phnum
1396 | print "Size of each sections header entry in bytes: 0x%x (%d)" \
1397 | % (self.header.e_shentsize, self.header.e_shentsize)
1398 | print "Number of section header entries: %d" % self.header.e_shnum
1399 | print "Section header string table index: %d" % self.header.e_shstrndx
1400 | print
1401 |
1402 |
1403 | # output of all sections
1404 | counter = 0
1405 | for section in self.sections:
1406 | print "Section No. %d" % counter
1407 | print "Name: %s" % section.sectionName
1408 |
1409 | # translate type
1410 | if section.elfN_shdr.sh_type in SH_type.reverse_lookup.keys():
1411 | print "Type: %s" \
1412 | % SH_type.reverse_lookup[section.elfN_shdr.sh_type]
1413 | else:
1414 | print "Unknown Type: 0x%x (%d)" \
1415 | % (section.elfN_shdr.sh_type, section.elfN_shdr.sh_type)
1416 |
1417 | print "Addr: 0x%x" % section.elfN_shdr.sh_addr
1418 | print "Off: 0x%x" % section.elfN_shdr.sh_offset
1419 | print "Size: 0x%x (%d)" \
1420 | % (section.elfN_shdr.sh_size, section.elfN_shdr.sh_size)
1421 | print "ES: %d" % section.elfN_shdr.sh_entsize
1422 |
1423 | # translate flags
1424 | temp = ""
1425 | if (section.elfN_shdr.sh_flags & SH_flags.SHF_WRITE) != 0:
1426 | temp += "W"
1427 | if (section.elfN_shdr.sh_flags & SH_flags.SHF_ALLOC) != 0:
1428 | temp += "A"
1429 | if (section.elfN_shdr.sh_flags & SH_flags.SHF_EXECINSTR) != 0:
1430 | temp += "X"
1431 |
1432 | print "FLG: %s" % temp
1433 | print "Lk: %d" % section.elfN_shdr.sh_link
1434 | print "Inf: %d" % section.elfN_shdr.sh_info
1435 | print "Al: %d" % section.elfN_shdr.sh_addralign
1436 | print
1437 | counter += 1
1438 |
1439 |
1440 | # output of all segments
1441 | counter = 0
1442 | for segment in self.segments:
1443 | print "Segment No. %d" % counter
1444 |
1445 | # translate type
1446 | if segment.elfN_Phdr.p_type in P_type.reverse_lookup.keys():
1447 | print "Type: %s" \
1448 | % P_type.reverse_lookup[segment.elfN_Phdr.p_type]
1449 | else:
1450 | print "Unknown Type: 0x%x (%d)" \
1451 | % (segment.elfN_Phdr.p_type, segment.elfN_Phdr.p_type)
1452 |
1453 | print "Offset: 0x%x" % segment.elfN_Phdr.p_offset
1454 | print "Virtual Addr: 0x%x" % segment.elfN_Phdr.p_vaddr
1455 | print "Physical Addr: 0x%x" % segment.elfN_Phdr.p_paddr
1456 | print "File Size: 0x%x (%d)" \
1457 | % (segment.elfN_Phdr.p_filesz, segment.elfN_Phdr.p_filesz)
1458 | print "Mem Size: 0x%x (%d)" \
1459 | % (segment.elfN_Phdr.p_memsz, segment.elfN_Phdr.p_memsz)
1460 |
1461 | # translate flags
1462 | temp = ""
1463 | if (segment.elfN_Phdr.p_flags & P_flags.PF_R) != 0:
1464 | temp += "R"
1465 | if (segment.elfN_Phdr.p_flags & P_flags.PF_W) != 0:
1466 | temp += "W"
1467 | if (segment.elfN_Phdr.p_flags & P_flags.PF_X) != 0:
1468 | temp += "X"
1469 | print "Flags: %s" % temp
1470 |
1471 | print "Align: 0x%x" % segment.elfN_Phdr.p_align
1472 |
1473 | # print which sections are in the current segment (in memory)
1474 | temp = ""
1475 | for section in segment.sectionsWithin:
1476 | temp += section.sectionName + " "
1477 | if temp != "":
1478 | print "Sections in segment: " + temp
1479 |
1480 | # print which segments are within current segment (in file)
1481 | temp = ""
1482 | for segmentWithin in segment.segmentsWithin:
1483 | for i in range(len(self.segments)):
1484 | if segmentWithin == self.segments[i]:
1485 | temp += "%d, " % i
1486 | break
1487 | if temp != "":
1488 | print "Segments within segment: " + temp
1489 |
1490 | # get interpreter if segment is for interpreter
1491 | # null-terminated string
1492 | if segment.elfN_Phdr.p_type == P_type.PT_INTERP:
1493 | nStart = segment.elfN_Phdr.p_offset
1494 | nEnd = nStart + segment.elfN_Phdr.p_filesz
1495 | print "Interpreter: %s" % self.data[nStart:nEnd]
1496 |
1497 | print
1498 | counter += 1
1499 |
1500 |
1501 | # search string table entry, string table size,
1502 | # symbol table entry and symbol table entry size
1503 | stringTableOffset = None
1504 | stringTableSize = None
1505 | symbolTableOffset = None
1506 | symbolEntrySize = None
1507 | for searchEntry in self.dynamicSegmentEntries:
1508 | if searchEntry.d_tag == D_tag.DT_STRTAB:
1509 | # data contains virtual memory address
1510 | # => calculate offset in file
1511 | stringTableOffset = \
1512 | self.virtualMemoryAddrToFileOffset(searchEntry.d_un)
1513 | if searchEntry.d_tag == D_tag.DT_STRSZ:
1514 | stringTableSize = searchEntry.d_un
1515 | if searchEntry.d_tag == D_tag.DT_SYMTAB:
1516 | # data contains virtual memory address
1517 | # => calculate offset in file
1518 | symbolTableOffset = \
1519 | self.virtualMemoryAddrToFileOffset(searchEntry.d_un)
1520 | if searchEntry.d_tag == D_tag.DT_SYMENT:
1521 | symbolEntrySize = searchEntry.d_un
1522 |
1523 | if (stringTableOffset is None
1524 | or stringTableSize is None
1525 | or symbolTableOffset is None
1526 | or symbolEntrySize is None):
1527 | raise ValueError("No dynamic section entry of type DT_STRTAB," \
1528 | + " DT_STRSZ, DT_SYMTAB and/or DT_SYMENT found (malformed"\
1529 | + " ELF executable/shared object).")
1530 |
1531 |
1532 | # output all dynamic segment entries
1533 | counter = 0
1534 | for entry in self.dynamicSegmentEntries:
1535 | print "Dynamic segment entry No. %d" % counter
1536 | if entry.d_tag in D_tag.reverse_lookup.keys():
1537 | print "Type: %s" % D_tag.reverse_lookup[entry.d_tag]
1538 | else:
1539 | print "Unknwon Type: 0x%x (%d)" % (entry.d_tag, entry.d_tag)
1540 |
1541 | # check if entry tag equals DT_NEEDED => get library name
1542 | if entry.d_tag == D_tag.DT_NEEDED:
1543 | nStart = stringTableOffset + entry.d_un
1544 | nMaxEnd = stringTableOffset + stringTableSize
1545 | nEnd = self.data.find('\x00', nStart, nMaxEnd)
1546 | nEnd = max(nStart, nEnd)
1547 | temp = bytes(self.data[nStart:nEnd])
1548 | print "Name/Value: 0x%x (%d) (%s)" \
1549 | % (entry.d_un, entry.d_un, temp)
1550 | else:
1551 | print "Name/Value: 0x%x (%d)" % (entry.d_un, entry.d_un)
1552 |
1553 | print
1554 | counter += 1
1555 |
1556 | self.printRelocations(self.jumpRelocationEntries,
1557 | "Jump relocation entries")
1558 |
1559 | self.printRelocations(self.relocationEntries,
1560 | "Relocation entries")
1561 |
1562 | # output all dynamic symbol entries
1563 | print("Dynamic symbols (%d entries)" % len(self.dynamicSymbolEntries))
1564 | print("No."),
1565 | print("\t"),
1566 | print("Value"),
1567 | print("\t\t"),
1568 | print("Size"),
1569 | print("\t"),
1570 | print("Name"),
1571 | print
1572 |
1573 | counter = 0
1574 | for entry in self.dynamicSymbolEntries:
1575 | symbol = entry.ElfN_Sym
1576 | print("%d" % counter),
1577 | print("\t"),
1578 | print("0x" + ("%x" % symbol.st_value).zfill(8)),
1579 | print("\t"),
1580 | print("0x" + ("%x" % symbol.st_size).zfill(3)),
1581 | print("\t"),
1582 | print("%s" % entry.symbolName),
1583 |
1584 | print
1585 | counter += 1
1586 |
1587 |
1588 | # this function generates a new ELF file from the attributes of the object
1589 | # return values: (list) generated ELF file data
1590 | def generateElf(self):
1591 |
1592 | # check if the file was completely parsed before
1593 | if self.fileParsed is False:
1594 | raise ValueError("Operation not possible. " \
1595 | + "File was not completely parsed before.")
1596 |
1597 | # copy binary data to new list
1598 | newfile = self.data[:]
1599 |
1600 | # ------
1601 |
1602 | # get position of section header table
1603 | writePosition = self.header.e_shoff
1604 |
1605 | # fill list with null until writePosition is reached
1606 | if len(newfile) < writePosition:
1607 | newfile.extend(bytearray(writePosition - len(newfile)))
1608 |
1609 | # write section header table back
1610 | for section in self.sections:
1611 | temp = self.sectionHeaderEntryToBytearray(section.elfN_shdr)
1612 | newfile[writePosition:writePosition+len(temp)] = temp
1613 | writePosition += len(temp)
1614 |
1615 | # ------
1616 |
1617 | # when defined => write string table back
1618 | if self.header.e_shstrndx != Shstrndx.SHN_UNDEF:
1619 | for section in self.sections:
1620 | # calculate the position on which the name should be written
1621 | writePosition = \
1622 | self.sections[self.header.e_shstrndx].elfN_shdr.sh_offset \
1623 | + section.elfN_shdr.sh_name
1624 |
1625 | # fill list with null until writePosition is reached
1626 | if len(newfile) < writePosition:
1627 | newfile.extend(bytearray(writePosition - len(newfile)))
1628 |
1629 | # write name of all sections into string table
1630 | data = bytearray(section.sectionName) + b'\x00'
1631 | newfile[writePosition:writePosition+len(data)] = data
1632 | writePosition += len(data)
1633 |
1634 | # ------
1635 |
1636 | # write ELF header back
1637 | newfile[0:len(self.header.e_ident)] = self.header.e_ident
1638 |
1639 | headerFields = (
1640 | # uint16_t e_type;
1641 | self.header.e_type,
1642 | # uint16_t e_machine;
1643 | self.header.e_machine,
1644 | # uint32_t e_version;
1645 | self.header.e_version,
1646 | # ElfN_Addr e_entry; (32/64 bit)
1647 | self.header.e_entry,
1648 | # ElfN_Off e_phoff; (32/64 bit)
1649 | self.header.e_phoff,
1650 | # ElfN_Off e_shoff; (32/64 bit)
1651 | self.header.e_shoff,
1652 | # uint32_t e_flags;
1653 | self.header.e_flags,
1654 | # uint16_t e_ehsize;
1655 | self.header.e_ehsize,
1656 | # uint16_t e_phentsize;
1657 | self.header.e_phentsize,
1658 | # uint16_t e_phnum;
1659 | self.header.e_phnum,
1660 | # uint16_t e_shentsize;
1661 | self.header.e_shentsize,
1662 | # uint16_t e_shnum;
1663 | self.header.e_shnum,
1664 | # uint16_t e_shstrndx;
1665 | self.header.e_shstrndx
1666 | )
1667 |
1668 | if self.bits == 32:
1669 | newfile[16:52] = struct.pack('< 2H I 3I I 6H', *headerFields)
1670 | elif self.bits == 64:
1671 | newfile[16:64] = struct.pack('< 2H I 3Q I 6H', *headerFields)
1672 |
1673 | # ------
1674 |
1675 | # write programm header table back
1676 | for i in range(len(self.segments)):
1677 |
1678 | # add placeholder bytes to new file when the bytes do not already
1679 | # exist in the new file until size of header entry fits
1680 | requiredSize = self.header.e_phoff + ((i+1) * self.header.e_phentsize)
1681 | if len(newfile) < requiredSize:
1682 | newfile.extend(bytearray(requiredSize - len(newfile)))
1683 |
1684 | tempOffset = self.header.e_phoff + i*self.header.e_phentsize
1685 | '''
1686 | typedef struct {
1687 | uint32_t p_type;
1688 | Elf32_Off p_offset;
1689 | Elf32_Addr p_vaddr;
1690 | Elf32_Addr p_paddr;
1691 | uint32_t p_filesz;
1692 | uint32_t p_memsz;
1693 | uint32_t p_flags; // *
1694 | uint32_t p_align;
1695 | } Elf32_Phdr;
1696 |
1697 | typedef struct {
1698 | uint32_t p_type;
1699 | uint32_t p_flags; // *
1700 | Elf64_Off p_offset;
1701 | Elf64_Addr p_vaddr;
1702 | Elf64_Addr p_paddr;
1703 | uint64_t p_filesz;
1704 | uint64_t p_memsz;
1705 | uint64_t p_align;
1706 | } Elf64_Phdr;
1707 |
1708 | The main difference lies in the location of p_flags within the struct.
1709 | '''
1710 | if self.bits == 32:
1711 | fmt = '< I 5I I I'
1712 | fmtSize = struct.calcsize(fmt)
1713 | assert self.header.e_phentsize == fmtSize
1714 | newfile[tempOffset:tempOffset+fmtSize] = struct.pack(fmt,
1715 | self.segments[i].elfN_Phdr.p_type,
1716 | self.segments[i].elfN_Phdr.p_offset,
1717 | self.segments[i].elfN_Phdr.p_vaddr,
1718 | self.segments[i].elfN_Phdr.p_paddr,
1719 | self.segments[i].elfN_Phdr.p_filesz,
1720 | self.segments[i].elfN_Phdr.p_memsz,
1721 | self.segments[i].elfN_Phdr.p_flags, # <- p_flags
1722 | self.segments[i].elfN_Phdr.p_align,
1723 | )
1724 | elif self.bits == 64:
1725 | fmt = '< I I 5Q Q'
1726 | fmtSize = struct.calcsize(fmt)
1727 | assert self.header.e_phentsize == fmtSize
1728 | newfile[tempOffset:tempOffset+fmtSize] = struct.pack(fmt,
1729 | self.segments[i].elfN_Phdr.p_type,
1730 | self.segments[i].elfN_Phdr.p_flags, # <- p_flags
1731 | self.segments[i].elfN_Phdr.p_offset,
1732 | self.segments[i].elfN_Phdr.p_vaddr,
1733 | self.segments[i].elfN_Phdr.p_paddr,
1734 | self.segments[i].elfN_Phdr.p_filesz,
1735 | self.segments[i].elfN_Phdr.p_memsz,
1736 | self.segments[i].elfN_Phdr.p_align,
1737 | )
1738 | del tempOffset
1739 |
1740 |
1741 | # ------
1742 |
1743 | # find dynamic segment
1744 | dynamicSegment = None
1745 | for segment in self.segments:
1746 | if segment.elfN_Phdr.p_type == P_type.PT_DYNAMIC:
1747 | dynamicSegment = segment
1748 | break
1749 | if dynamicSegment is None:
1750 | raise ValueError("Segment of type PT_DYNAMIC was not found.")
1751 |
1752 | if self.bits == 32:
1753 | structFmt = ' write (jump) relocation entries back
1859 |
1860 | # holds tuples: (type, offset, size, sourcelist)
1861 | relocTODO = []
1862 |
1863 | # DT_JMPREL
1864 | if jmpRelOffset is not None:
1865 | relocTODO.append((pltRelType, jmpRelOffset, pltRelSize,
1866 | self.jumpRelocationEntries))
1867 |
1868 | # DT_REL
1869 | if relOffset is not None:
1870 | relocTODO.append((D_tag.DT_REL, relOffset, relSize,
1871 | self.relocationEntries))
1872 |
1873 | # DT_RELA
1874 | if relaOffset is not None:
1875 | relocTODO.append((D_tag.DT_RELA, relaOffset, relaSize,
1876 | self.relocationEntries))
1877 |
1878 | if relOffset is not None and relaOffset is not None:
1879 | raise RuntimeError('INTERNAL ERROR: TODO REL WRITE 1')
1880 |
1881 |
1882 | for relocType, relocOffset, relocSize, relocList in relocTODO:
1883 | if relocType == D_tag.DT_REL:
1884 | relocEntrySize = relEntrySize
1885 | elif relocType == D_tag.DT_RELA:
1886 | relocEntrySize = relaEntrySize
1887 |
1888 | if relocType == D_tag.DT_REL:
1889 | if self.bits == 32:
1890 | structFmt = ' write dynamic symbol back
1923 | dynSym = relocEntry.symbol
1924 | if (dynSym not in dynSymSet and symbolTableOffset is not None):
1925 | self._writeDynamicSymbol(newfile, symbolTableOffset \
1926 | + relocEntry.r_sym * symbolEntrySize,
1927 | dynSym.ElfN_Sym)
1928 |
1929 | # ------
1930 |
1931 | return newfile
1932 |
1933 |
1934 | # this function writes the generated ELF file back
1935 | # return values: None
1936 | def writeElf(self, filename):
1937 |
1938 | # check if the file was completely parsed before
1939 | if self.fileParsed is False:
1940 | raise ValueError("Operation not possible. " \
1941 | + "File was not completely parsed before.")
1942 |
1943 | f = open(filename, "w")
1944 | f.write(self.generateElf())
1945 | f.close()
1946 |
1947 |
1948 | # this function appends data to a selected segment number (if it fits)
1949 | # return values: (int) offset in file of appended data,
1950 | # (int) address in memory of appended data
1951 | def appendDataToSegment(self, data, segmentNumber, addNewSection=False,
1952 | newSectionName=None, extendExistingSection=False):
1953 |
1954 | # check if the file was completely parsed before
1955 | if self.fileParsed is False:
1956 | raise ValueError("Operation not possible. " \
1957 | + "File was not completely parsed before.")
1958 |
1959 | segmentToExtend = self.segments[segmentNumber]
1960 |
1961 | # find segment that comes directly after the segment
1962 | # to manipulate in the virtual memory
1963 | nextSegment, diff_p_vaddr \
1964 | = self.getNextSegmentAndFreeSpace(segmentToExtend)
1965 |
1966 | # check if a segment exists directly after the segment
1967 | # to manipulate in the virtual memory
1968 | if nextSegment is None:
1969 | # segment directly after segment to
1970 | # manipulate does not exist in virtual memory
1971 |
1972 | # get memory address and offset in file of appended data
1973 | newDataMemoryAddr = segmentToExtend.elfN_Phdr.p_vaddr \
1974 | + segmentToExtend.elfN_Phdr.p_memsz
1975 | newDataOffset = segmentToExtend.elfN_Phdr.p_offset \
1976 | + segmentToExtend.elfN_Phdr.p_filesz
1977 |
1978 | # insert data
1979 | for i in range(len(data)):
1980 | self.data.insert((newDataOffset + i), data[i])
1981 |
1982 | # adjust offsets of all following section
1983 | # (for example symbol sections are often behind all segments)
1984 | for section in self.sections:
1985 | if (section.elfN_shdr.sh_offset >=
1986 | (segmentToExtend.elfN_Phdr.p_offset
1987 | + segmentToExtend.elfN_Phdr.p_filesz)):
1988 | section.elfN_shdr.sh_offset += len(data)
1989 |
1990 | # extend size of data in file of the modifed segment
1991 | segmentToExtend.elfN_Phdr.p_filesz += len(data)
1992 |
1993 | # extend size of data in memory of the modifed segment
1994 | segmentToExtend.elfN_Phdr.p_memsz += len(data)
1995 |
1996 |
1997 | else:
1998 | # segment directly after segment to
1999 | # manipulate exists in virtual memory
2000 |
2001 | # check if data to append fits
2002 | if len(data) >= diff_p_vaddr:
2003 | raise ValueError("Size of data to append: %d " \
2004 | + "Size of memory space: %d" % (len(data), diff_p_vaddr))
2005 |
2006 | # p_offset and p_vaddr are congruend modulo alignment
2007 | # for example:
2008 | # p_align: 0x1000 (default for LOAD segment)
2009 | # p_offset: 0x016f88
2010 | # p_vaddr: 0x0805ff88
2011 | # => 0x016f88 % 0x1000 = 0xf88
2012 | # both must have 0xf88 at the end of the address
2013 |
2014 | # get how often the appended data fits in the
2015 | # alignment of the segment
2016 | alignmentMultiplier = int(len(data) \
2017 | / segmentToExtend.elfN_Phdr.p_align) + 1
2018 |
2019 | # calculate the size to add to the offsets
2020 | offsetAddition = alignmentMultiplier \
2021 | * segmentToExtend.elfN_Phdr.p_align
2022 |
2023 | # adjust offsets of all following section
2024 | for section in self.sections:
2025 | if (section.elfN_shdr.sh_offset
2026 | >= nextSegment.elfN_Phdr.p_offset):
2027 | section.elfN_shdr.sh_offset += offsetAddition
2028 |
2029 | # adjust offsets of following segments
2030 | # (ignore the directly followed segment)
2031 | for segment in self.segments:
2032 | if segment != segmentToExtend and segment != nextSegment:
2033 | # use offset of the directly followed segment in order to
2034 | # ignore segments that lies within the
2035 | # segment to manipulate
2036 | if (segment.elfN_Phdr.p_offset
2037 | > nextSegment.elfN_Phdr.p_offset):
2038 | segment.elfN_Phdr.p_offset += offsetAddition
2039 |
2040 | # adjust offset of the directly following segment of the
2041 | # segment to manipulate
2042 | nextSegment.elfN_Phdr.p_offset += offsetAddition
2043 |
2044 | # if program header table lies behind the segment to manipulate
2045 | # => move it
2046 | if (self.header.e_phoff > (segmentToExtend.elfN_Phdr.p_offset
2047 | + segmentToExtend.elfN_Phdr.p_filesz)):
2048 | self.header.e_phoff += offsetAddition
2049 |
2050 | # if section header table lies behind the segment to manipulate
2051 | # => move it
2052 | if (self.header.e_shoff > (segmentToExtend.elfN_Phdr.p_offset
2053 | + segmentToExtend.elfN_Phdr.p_filesz)):
2054 | self.header.e_shoff += offsetAddition
2055 |
2056 | # get memory address and offset in file of appended data
2057 | newDataMemoryAddr = segmentToExtend.elfN_Phdr.p_vaddr \
2058 | + segmentToExtend.elfN_Phdr.p_memsz
2059 | newDataOffset = segmentToExtend.elfN_Phdr.p_offset \
2060 | + segmentToExtend.elfN_Phdr.p_filesz
2061 |
2062 | # insert data
2063 | for i in range(len(data)):
2064 | self.data.insert((newDataOffset + i), data[i])
2065 |
2066 | # fill the rest with 0x00 until the offset addition in the
2067 | # file is reached
2068 | for i in range((offsetAddition - len(data))):
2069 | self.data.insert((newDataOffset + len(data) + i), "\x00")
2070 |
2071 | # extend size of data in file of the modifed segment
2072 | segmentToExtend.elfN_Phdr.p_filesz += len(data)
2073 |
2074 | # extend size of data in memory of the modifed segment
2075 | segmentToExtend.elfN_Phdr.p_memsz += len(data)
2076 |
2077 |
2078 | # if added data should have an own section => add new section
2079 | if addNewSection and not extendExistingSection:
2080 |
2081 | # calculate alignment of new section
2082 | # start with 16 as alignment (is used by .text section)
2083 | newSectionAddrAlign = 16
2084 | while newSectionAddrAlign != 1:
2085 | if (len(data) % newSectionAddrAlign) == 0:
2086 | break
2087 | else:
2088 | newSectionAddrAlign = newSectionAddrAlign / 2
2089 |
2090 | # add section
2091 | # addNewSection(newSectionName, newSectionType, newSectionFlag,
2092 | # newSectionAddr, newSectionOffset, newSectionSize,
2093 | # newSectionLink, newSectionInfo, newSectionAddrAlign,
2094 | # newSectionEntsize)
2095 | self.addNewSection(newSectionName, SH_type.SHT_PROGBITS,
2096 | (SH_flags.SHF_EXECINSTR | SH_flags.SHF_ALLOC),
2097 | newDataMemoryAddr, newDataOffset, len(data), 0, 0,
2098 | newSectionAddrAlign, 0)
2099 |
2100 | # if added data should extend an existing section
2101 | # => search this section and extend it
2102 | if extendExistingSection and not addNewSection:
2103 | for section in self.sections:
2104 | # the end of an existing section in the virtual
2105 | # memory is generally equal
2106 | # to the virtual memory address of the added data
2107 | if ((section.elfN_shdr.sh_addr + section.elfN_shdr.sh_size)
2108 | == newDataMemoryAddr):
2109 | # check if data is not appended to last section
2110 | # => use free space between segments for section
2111 | if diff_p_vaddr is not None:
2112 | # extend the existing section
2113 | self.extendSection(section, diff_p_vaddr)
2114 | else:
2115 | # extend the existing section
2116 | self.extendSection(section, len(data))
2117 |
2118 | break
2119 |
2120 | if not extendExistingSection and not addNewSection:
2121 | print "NOTE: if appended data do not belong to a section they " \
2122 | + "will not be seen by tools that interpret sections " \
2123 | + "(like 'IDA 6.1.x' without the correct settings or " \
2124 | + "'strings' in the default configuration)."
2125 |
2126 | # return offset of appended data in file and address in memory
2127 | return newDataOffset, newDataMemoryAddr
2128 |
2129 |
2130 | # this function generates and adds a new section to the ELF file
2131 | # return values: None
2132 | def addNewSection(self, newSectionName, newSectionType, newSectionFlag,
2133 | newSectionAddr, newSectionOffset, newSectionSize, newSectionLink,
2134 | newSectionInfo, newSectionAddrAlign, newSectionEntsize):
2135 |
2136 | # check if the file was completely parsed before
2137 | if self.fileParsed is False:
2138 | raise ValueError("Operation not possible. " \
2139 | + "File was not completely parsed before.")
2140 |
2141 | # check if sections do not exist
2142 | # => create new section header table
2143 | if len(self.sections) == 0:
2144 |
2145 | # restore section header entry size
2146 | if self.bits == 32:
2147 | self.header.e_shentsize = struct.calcsize('< 2I 4I 2I 2I')
2148 | elif self.bits == 64:
2149 | self.header.e_shentsize = struct.calcsize('< 2I 4Q 2I 2Q')
2150 |
2151 | # when using gcc, first section is NULL section
2152 | # => create one and add it
2153 | # generateNewSection(sectionName, sh_name, sh_type,
2154 | # sh_flags, sh_addr, sh_offset, sh_size, sh_link,
2155 | # sh_info, sh_addralign, sh_entsize)
2156 | newNullSection = self.generateNewSection("", 0, SH_type.SHT_NULL, 0,
2157 | 0, 0, 0, 0, 0, 0, 0)
2158 | self.sections.append(newNullSection)
2159 |
2160 | # increase count of sections
2161 | self.header.e_shnum += 1
2162 |
2163 | # create new ".shstrtab" section (section header string table)
2164 | # and add it to the end of the file
2165 | offsetNewShstrtab = len(self.data)
2166 | nameNewShstrtab = ".shstrtab"
2167 |
2168 | # use third entry in new section header string table
2169 | # as index for the new created section (name for ".shstrtab" is
2170 | # second, name for NULL section first)
2171 | newSectionStringTableIndex = len(nameNewShstrtab) + 1 + 1
2172 |
2173 | # generate new section object and add it
2174 | # generateNewSection(sectionName, sh_name, sh_type,
2175 | # sh_flags, sh_addr, sh_offset, sh_size, sh_link,
2176 | # sh_info, sh_addralign, sh_entsize)
2177 | newSection = self.generateNewSection(newSectionName,
2178 | newSectionStringTableIndex, newSectionType, newSectionFlag,
2179 | newSectionAddr, newSectionOffset, newSectionSize,
2180 | newSectionLink, newSectionInfo, newSectionAddrAlign,
2181 | newSectionEntsize)
2182 | self.sections.append(newSection)
2183 |
2184 | # increase count of sections
2185 | self.header.e_shnum += 1
2186 |
2187 | # calculate length of ".shstrtab" section
2188 | lengthNewShstrtab = len(nameNewShstrtab) + 1 \
2189 | + len(newSectionName) + 1 + 1
2190 |
2191 | # generate ".shstrtab" section object and add it
2192 | # generateNewSection(sectionName, sh_name, sh_type,
2193 | # sh_flags, sh_addr, sh_offset, sh_size, sh_link,
2194 | # sh_info, sh_addralign, sh_entsize)
2195 | newShstrtabsection = self.generateNewSection(nameNewShstrtab,
2196 | 1, SH_type.SHT_STRTAB, 0,
2197 | 0, offsetNewShstrtab, lengthNewShstrtab, 0, 0, 1, 0)
2198 | self.sections.append(newShstrtabsection)
2199 |
2200 | # increase count of sections
2201 | self.header.e_shnum += 1
2202 |
2203 | # add section header table to the end of the file new file
2204 | self.header.e_shoff = offsetNewShstrtab + lengthNewShstrtab
2205 |
2206 | # new section string table index is the third section
2207 | self.header.e_shstrndx = 2
2208 |
2209 |
2210 | # sections exist
2211 | # => just add section
2212 | else:
2213 | # get index in the string table of the name of the new section
2214 | # (use size of string table to just append new name to string
2215 | # table)
2216 | newSectionStringTableIndex \
2217 | = self.sections[self.header.e_shstrndx].elfN_shdr.sh_size
2218 |
2219 | # generate new section object
2220 | # generateNewSection(sectionName, sh_name, sh_type,
2221 | # sh_flags, sh_addr, sh_offset, sh_size, sh_link,
2222 | # sh_info, sh_addralign, sh_entsize)
2223 | newsection = self.generateNewSection(newSectionName,
2224 | newSectionStringTableIndex, newSectionType, newSectionFlag,
2225 | newSectionAddr, newSectionOffset, newSectionSize,
2226 | newSectionLink, newSectionInfo, newSectionAddrAlign,
2227 | newSectionEntsize)
2228 |
2229 | # get position of new section
2230 | positionNewSection = None
2231 | for i in range(self.header.e_shnum):
2232 | if (i+1) < self.header.e_shnum:
2233 | if (self.sections[i].elfN_shdr.sh_offset < newSectionOffset
2234 | and self.sections[i+1].elfN_shdr.sh_offset
2235 | >= newSectionOffset):
2236 | positionNewSection = i+1
2237 |
2238 | # if new section comes before string table section
2239 | # => adjust string table section index
2240 | if positionNewSection <= self.header.e_shstrndx:
2241 | self.header.e_shstrndx += 1
2242 | break
2243 | # insert new section at calculated position
2244 | if positionNewSection is None:
2245 | self.sections.append(newsection)
2246 | else:
2247 | self.sections.insert(positionNewSection, newsection)
2248 |
2249 | # section header table lies oft directly behind the string table
2250 | # check if new section name would overwrite data of
2251 | # section header table
2252 | # => move section header table
2253 | if (self.header.e_shoff
2254 | >= (self.sections[self.header.e_shstrndx].elfN_shdr.sh_offset
2255 | + self.sections[self.header.e_shstrndx].elfN_shdr.sh_size)
2256 | and self.header.e_shoff
2257 | <= (self.sections[self.header.e_shstrndx].elfN_shdr.sh_offset
2258 | + self.sections[self.header.e_shstrndx].elfN_shdr.sh_size
2259 | + len(newSectionName) + 1)):
2260 | self.header.e_shoff += len(newSectionName) + 1
2261 |
2262 | # add size of new name to string table + 1 for
2263 | # null-terminated C string
2264 | self.sections[self.header.e_shstrndx].elfN_shdr.sh_size \
2265 | += len(newSectionName) + 1
2266 |
2267 | # increase count of sections
2268 | self.header.e_shnum += 1
2269 |
2270 |
2271 | # this function extends the section size by the given size
2272 | # return values: None
2273 | def extendSection(self, sectionToExtend, size):
2274 |
2275 | # check if the file was completely parsed before
2276 | if self.fileParsed is False:
2277 | raise ValueError("Operation not possible. " \
2278 | + "File was not completely parsed before.")
2279 |
2280 | sectionToExtend.elfN_shdr.sh_size += size
2281 |
2282 |
2283 | # this function searches for a executable segment from type
2284 | # PT_LOAD in which the data fits
2285 | # return values: (class Segment) manipulated segment,
2286 | # (int) offset in file of appended data,
2287 | # (int) address in memory of appended data
2288 | def appendDataToExecutableSegment(self, data, addNewSection=False,
2289 | newSectionName=None, extendExistingSection=False):
2290 |
2291 | # check if the file was completely parsed before
2292 | if self.fileParsed is False:
2293 | raise ValueError("Operation not possible. " \
2294 | + "File was not completely parsed before.")
2295 |
2296 | # get all executable segments from type PT_LOAD
2297 | possibleSegments = list()
2298 | for segment in self.segments:
2299 | if ((segment.elfN_Phdr.p_flags & P_flags.PF_X) == 1
2300 | and segment.elfN_Phdr.p_type == P_type.PT_LOAD):
2301 | possibleSegments.append(segment)
2302 |
2303 | # find space for data in all possible executable segments
2304 | found = False
2305 | for possibleSegment in possibleSegments:
2306 | diff_p_vaddr = None
2307 | # find segment that comes directly after the segment to
2308 | # manipulate in the virtual memory
2309 | # and get the free memory space in between
2310 | for i in range(len(self.segments)):
2311 | if self.segments[i] != possibleSegment:
2312 | if ((self.segments[i].elfN_Phdr.p_vaddr
2313 | - (possibleSegment.elfN_Phdr.p_vaddr
2314 | + possibleSegment.elfN_Phdr.p_memsz)) > 0):
2315 | if (diff_p_vaddr is None
2316 | or (self.segments[i].elfN_Phdr.p_vaddr
2317 | - (possibleSegment.elfN_Phdr.p_vaddr
2318 | + possibleSegment.elfN_Phdr.p_memsz))
2319 | < diff_p_vaddr):
2320 | diff_p_vaddr = self.segments[i].elfN_Phdr.p_vaddr \
2321 | - (possibleSegment.elfN_Phdr.p_vaddr \
2322 | + possibleSegment.elfN_Phdr.p_memsz)
2323 | else: # get position in list of possible segment
2324 | segmentNumber = i
2325 | # check if data to append fits in space
2326 | if diff_p_vaddr > len(data):
2327 | found = True
2328 | break
2329 | if not found:
2330 | raise ValueError(("Size of data to append: %d. Not enough space" \
2331 | + " after existing executable segment found.") % len(data))
2332 |
2333 | # append data to segment
2334 | newDataOffset, newDataMemoryAddr = self.appendDataToSegment(data,
2335 | segmentNumber, addNewSection=addNewSection,
2336 | newSectionName=newSectionName,
2337 | extendExistingSection=extendExistingSection)
2338 |
2339 | # return manipulated segment, offset of appended data in file and
2340 | # memory address of appended data
2341 | return self.segments[segmentNumber], newDataOffset, newDataMemoryAddr
2342 |
2343 |
2344 | # this function gets the next segment of the given one and the
2345 | # free space in memory in between
2346 | # return values: (class Segment) next segment, (int) free space;
2347 | # both None if no following segment was found
2348 | def getNextSegmentAndFreeSpace(self, segmentToSearch):
2349 |
2350 | # check if the file was completely parsed before
2351 | if self.fileParsed is False:
2352 | raise ValueError("Operation not possible. " \
2353 | + "File was not completely parsed before.")
2354 |
2355 | # find segment that comes directly after the segment to
2356 | # manipulate in the virtual memory
2357 | diff_p_vaddr = None
2358 | nextSegment = None
2359 | for segment in self.segments:
2360 | if segment != segmentToSearch:
2361 | if ((segment.elfN_Phdr.p_vaddr
2362 | - (segmentToSearch.elfN_Phdr.p_vaddr
2363 | + segmentToSearch.elfN_Phdr.p_memsz)) > 0):
2364 | if (diff_p_vaddr is None
2365 | or (segment.elfN_Phdr.p_vaddr
2366 | - (segmentToSearch.elfN_Phdr.p_vaddr
2367 | + segmentToSearch.elfN_Phdr.p_memsz))
2368 | < diff_p_vaddr):
2369 | diff_p_vaddr = segment.elfN_Phdr.p_vaddr \
2370 | - (segmentToSearch.elfN_Phdr.p_vaddr \
2371 | + segmentToSearch.elfN_Phdr.p_memsz)
2372 | nextSegment = segment
2373 |
2374 | # return nextSegment and free space
2375 | return nextSegment, diff_p_vaddr
2376 |
2377 |
2378 | # this function is a wrapper function for
2379 | # getNextSegmentAndFreeSpace(segmentToSearch)
2380 | # which returns only the free space in memory after the segment
2381 | # return values: (int) free space; None if no following segment was found
2382 | def getFreeSpaceAfterSegment(self, segmentToSearch):
2383 |
2384 | # check if the file was completely parsed before
2385 | if self.fileParsed is False:
2386 | raise ValueError("Operation not possible. " \
2387 | + "File was not completely parsed before.")
2388 |
2389 | nextSegment, diff_p_vaddr \
2390 | = self.getNextSegmentAndFreeSpace(segmentToSearch)
2391 | return diff_p_vaddr
2392 |
2393 |
2394 | # this function removes all section header entries
2395 | # return values: None
2396 | def removeSectionHeaderTable(self):
2397 |
2398 | # check if the file was completely parsed before
2399 | if self.fileParsed is False:
2400 | raise ValueError("Operation not possible. " \
2401 | + "File was not completely parsed before.")
2402 |
2403 | self.header.e_shoff = 0
2404 | self.header.e_shnum = 0
2405 | self.header.e_shentsize = 0
2406 | self.header.e_shstrndx = Shstrndx.SHN_UNDEF
2407 | self.sections = list()
2408 |
2409 |
2410 | # this function overwrites data on the given offset
2411 | # return values: None
2412 | def writeDataToFileOffset(self, offset, data, force=False):
2413 |
2414 | # check if the file was completely parsed before
2415 | if self.fileParsed is False:
2416 | raise ValueError("Operation not possible. " \
2417 | + "File was not completely parsed before.")
2418 |
2419 | # get the segment to which the changed data belongs to
2420 | segmentToManipulate = None
2421 | for segment in self.segments:
2422 | segStart = segment.elfN_Phdr.p_offset
2423 | segEnd = segStart + segment.elfN_Phdr.p_filesz
2424 | if segStart <= offset and offset < segEnd:
2425 | segmentToManipulate = segment
2426 | break
2427 |
2428 | # check if segment was found
2429 | if force is False and segmentToManipulate is None:
2430 | raise ValueError(('Segment with offset 0x%x not found ' \
2431 | + '(use "force=True" to ignore this check).') % offset)
2432 |
2433 | # (previous check ensures that now either force is True or segEnd has been set)
2434 | # check if data to manipulate fits in segment
2435 | if force is False and offset + len(data) >= segEnd:
2436 | raise ValueError(('Size of data to manipulate: %d. Not enough ' \
2437 | + 'space in segment (Available: %d; use "force=True" to ' \
2438 | + 'ignore this check).') \
2439 | % (len(data), (segEnd - offset)))
2440 |
2441 | # change data
2442 | self.data[offset:offset+len(data)] = data
2443 |
2444 |
2445 | # this function converts the virtual memory address to the file offset
2446 | # return value: (int) offset in file (or None if not found)
2447 | def virtualMemoryAddrToFileOffset(self, memoryAddr):
2448 |
2449 | # check if the file was completely parsed before
2450 | if self.fileParsed is False:
2451 | raise ValueError("Operation not possible. " \
2452 | + "File was not completely parsed before.")
2453 |
2454 | # get the segment to which the virtual memory address belongs to
2455 | foundSegment = None
2456 | for segment in self.segments:
2457 | segStart = segment.elfN_Phdr.p_vaddr
2458 | segEnd = segStart + segment.elfN_Phdr.p_memsz
2459 | if segStart <= memoryAddr and memoryAddr < segEnd:
2460 | foundSegment = segment
2461 | break
2462 |
2463 | # check if segment was found
2464 | if foundSegment is None:
2465 | return None
2466 |
2467 | relOffset = memoryAddr - foundSegment.elfN_Phdr.p_vaddr
2468 | # relOffset >= 0 due to condition in segment search loop
2469 |
2470 | # check if file is mapped 1:1 to memory
2471 | if foundSegment.elfN_Phdr.p_filesz != foundSegment.elfN_Phdr.p_memsz:
2472 | # check if the memory address relative to the virtual memory
2473 | # address of the segment lies within the file size of the segment
2474 | if relOffset >= foundSegment.elfN_Phdr.p_filesz:
2475 | raise ValueError("Can not convert virtual memory address " \
2476 | + "to file offset.")
2477 |
2478 | return foundSegment.elfN_Phdr.p_offset + relOffset
2479 |
2480 |
2481 | # this function converts the file offset to the virtual memory address
2482 | # return value: (int) virtual memory address (or None if not found)
2483 | def fileOffsetToVirtualMemoryAddr(self, offset):
2484 |
2485 | # check if the file was completely parsed before
2486 | if self.fileParsed is False:
2487 | raise ValueError("Operation not possible. " \
2488 | + "File was not completely parsed before.")
2489 |
2490 | # get the segment to which the file offset belongs to
2491 | foundSegment = None
2492 | for segment in self.segments:
2493 | segStart = segment.elfN_Phdr.p_offset
2494 | segEnd = segStart + segment.elfN_Phdr.p_filesz
2495 | if segStart <= offset and offset < segEnd:
2496 | foundSegment = segment
2497 | break
2498 |
2499 | # check if segment was found
2500 | if foundSegment is None:
2501 | return None
2502 |
2503 | relOffset = offset - foundSegment.elfN_Phdr.p_offset
2504 | # relOffset >= 0 due to condition in segment search loop
2505 |
2506 | # check if file is mapped 1:1 to memory
2507 | if foundSegment.elfN_Phdr.p_filesz != foundSegment.elfN_Phdr.p_memsz:
2508 | if relOffset >= foundSegment.elfN_Phdr.p_memsz:
2509 | raise ValueError("Data not mapped 1:1 from file to memory." \
2510 | + " Can not convert virtual memory address to file offset.")
2511 |
2512 | return foundSegment.elfN_Phdr.p_vaddr + relOffset
2513 |
2514 |
2515 | # this function overwrites an entry in the got
2516 | # (global offset table) in the file
2517 | # return values: None
2518 | def modifyGotEntryAddr(self, name, memoryAddr):
2519 |
2520 | # check if the file was completely parsed before
2521 | if self.fileParsed is False:
2522 | raise ValueError("Operation not possible. " \
2523 | + "File was not completely parsed before.")
2524 |
2525 | # search for name in jump relocation entries
2526 | entryToModify = None
2527 | for jmpEntry in self.jumpRelocationEntries:
2528 | if jmpEntry.name == name:
2529 | entryToModify = jmpEntry
2530 | break
2531 | if entryToModify is None:
2532 | raise ValueError('Jump relocation entry with the name' \
2533 | + ' "%s" was not found.' % name)
2534 |
2535 | # calculate file offset of got
2536 | entryOffset = self.virtualMemoryAddrToFileOffset(
2537 | entryToModify.r_offset)
2538 |
2539 | # generate list with new memory address for got
2540 | if self.bits == 32:
2541 | fmt = ' change section string table index and number of sections
2630 | if sectionNo < self.header.e_shstrndx:
2631 | self.header.e_shstrndx = self.header.e_shstrndx - 1
2632 | elif sectionNo == self.header.e_shstrndx:
2633 | self.header.e_shstrndx = 0
2634 | self.header.e_shnum = self.header.e_shnum - 1
2635 |
2636 |
2637 | # this function searches for the first jump relocation entry given by name
2638 | # return values: (ElfN_Rel) jump relocation entry
2639 | def getJmpRelEntryByName(self, name):
2640 |
2641 | # check if the file was completely parsed before
2642 | if self.fileParsed is False:
2643 | raise ValueError("Operation not possible. " \
2644 | + "File was not completely parsed before.")
2645 |
2646 | # search for the first jump relocation entry with the given name
2647 | foundEntry = None
2648 | for jmpRelEntry in self.jumpRelocationEntries:
2649 | if jmpRelEntry.symbol.symbolName == name:
2650 | foundEntry = jmpRelEntry
2651 | break
2652 |
2653 | # check if jump relocation entry was found
2654 | if foundEntry is None:
2655 | raise ValueError('Jump relocation entry with the name' \
2656 | + ' "%s" was not found.' % name)
2657 |
2658 | return foundEntry
2659 |
--------------------------------------------------------------------------------
/ZwoELF/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | # written by sqall
4 | # twitter: https://twitter.com/sqall01
5 | # blog: http://blog.h4des.org
6 | # github: https://github.com/sqall01
7 | #
8 | # Licensed under the GNU Public License, version 2.
9 |
10 | import Compatibility
11 | from ElfParserLib import ElfParser, Section, Segment
12 | from Elf import ElfN_Ehdr, Shstrndx, ElfN_Shdr, SH_flags, SH_type, \
13 | Elf32_Phdr, P_type, P_flags, D_tag, ElfN_Dyn, \
14 | ElfN_Rel, ElfN_Rela, ElfN_Sym, R_type, \
15 | Section, Segment, DynamicSymbol
16 |
--------------------------------------------------------------------------------
/examples/IDAPython/addDynamicSymbols_ida.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | # written by sqall
4 | # twitter: https://twitter.com/sqall01
5 | # blog: http://blog.h4des.org
6 | # github: https://github.com/sqall01
7 | #
8 | # Licensed under the GNU Public License, version 2.
9 |
10 | from ZwoELF import ElfParser
11 | from idautils import *
12 |
13 | currentFile = GetInputFilePath()
14 | elfFile = ElfParser(currentFile)
15 |
16 | # rename all symbols from the jump entries in ida
17 | for jmpRelEntry in elfFile.jumpRelocationEntries:
18 | name = jmpRelEntry.symbol.symbolName
19 |
20 | print "Add references for symbol: %s (0x%x)" % (name, jmpRelEntry.r_offset)
21 | MakeRptCmt(jmpRelEntry.r_offset, "%s (restored by script)" % name)
22 | dataRefs = DataRefsTo(jmpRelEntry.r_offset)
23 |
24 | # get address of the data reference (usually there is only one reference)
25 | address = list(dataRefs)[0]
26 |
27 | # rename address
28 | MakeName(address, name + "__restored")
--------------------------------------------------------------------------------
/examples/addRandomSections.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | # written by sqall
4 | # twitter: https://twitter.com/sqall01
5 | # blog: http://blog.h4des.org
6 | #
7 | # Licensed under the GNU Public License, version 2.
8 |
9 | import sys
10 | import os
11 | from ctypes import c_uint
12 | from ZwoELF import ElfParser, SH_type, SH_flags
13 | import random
14 |
15 | random.seed()
16 |
17 | try:
18 | if sys.argv[1] == '--seed':
19 | random.seed(int(sys.argv[2]))
20 | sys.argv[1:3] = []
21 |
22 | inputFile = sys.argv[1]
23 | outputFile = sys.argv[2]
24 | except:
25 | print('usage: {} [--seed ]