├── LICENSE.txt ├── README.md ├── experiments ├── MPK_rop_results.txt ├── search_hexstring.sh └── sizediff.sh ├── presentation └── xom-switch-bhasia-2018-v1.3.pdf └── src ├── .gitignore ├── analysis ├── README.md ├── check_cpuinfo.sh ├── patch-binary.sh ├── patch-libc.sh ├── patch-loader.sh ├── scripts │ ├── adjust_phnum.sh │ ├── analyze_callsites.sh │ ├── analyze_mmap_callsites.sh │ ├── analyze_pltcall.sh │ ├── analyze_syscall.sh │ ├── filling_gap.sh │ ├── patch_call_of_injectedbin.sh │ ├── patch_calls2injectedcode_in_original_code.sh │ ├── patch_calls2origcode_in_injected_code.sh │ ├── patch_calls_of_origbin.sh │ ├── transform_symbol.sh │ └── unused │ │ ├── analyze_mmap_syscall.sh │ │ └── patch_backward_jump.sh └── tests │ ├── binfiles │ ├── files │ └── h.lua │ └── test.sh ├── patch ├── common │ ├── func.s │ └── jump2original_gen.sh ├── tutorial │ ├── Makefile │ ├── README.md │ ├── ld.script │ ├── main.c │ └── trampoline.s └── xomenable │ ├── Makefile │ ├── README.md │ ├── enable_xom.c │ ├── ld.script │ ├── main.c │ ├── trampoline.s │ ├── utils.c │ └── utils.h └── rewriter ├── .gitignore ├── README.md ├── __init__.py ├── base_instrumentor.py ├── elf_basic.py ├── elf_helper.py ├── examples ├── inject_instrumentation.py ├── inject_only_code_segment.py └── xom_enable.py ├── hex_match.py ├── instrumentor.py └── section_basic.py /LICENSE.txt: -------------------------------------------------------------------------------- 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 | 294 | Copyright (C) 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 | , 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. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DISCONTINUATION OF PROJECT. 2 | 3 | This project will no longer be maintained by Intel. 4 | 5 | Intel has ceased development and contributions including, but not limited to, maintenance, bug fixes, new releases, or updates, to this project. 6 | 7 | Intel no longer accepts patches to this project. 8 | 9 | If you have an ongoing need to use this project, are interested in independently developing it, or would like to maintain patches for the open source software community, please create your own fork of this project. 10 | # XOM-Switch 11 | **(eXecutable-Only Memory Switch)** 12 | 13 | xom-switch is the eXecutable-Only-Memory (XOM) enabling tool for x86 Linux system. It aims to mitigate code disclosure guided ROP attacks. This is is the 1st tool using Intel's [Memory Protection Keys (MPK)](https://lwn.net/Articles/643797/) feature for XOM enabling. xom-switch protects all code modules in the runtime including executable and dependent libraries without requiring source code or heavyweight binary translation/rewriting. xom-switch uses non-intrusive way to intercept program runtime by instrumenting program loader (ld.so). 14 | 15 | **BlackHat Asia 2018 [Description](https://www.blackhat.com/asia-18/briefings.html#xom-switch-hiding-your-code-from-advanced-code-reuse-attacks-in-one-shot) and [Presentation](https://www.blackhat.com/docs/asia-18/asia-18-Zhang-Liu-Xom-switch--v1.3.pdf)** 16 | 17 | **xom-switch could run in [Amazon AWS C5 Instance](https://aws.amazon.com/ec2/instance-types/c5/). Try it out!** 18 | 19 | **Fedora 28 will support GOT protection in lazy binding using memory protection keys. [Link1](https://www.phoronix.com/scan.php?page=news_item&px=Glibc-Memory-Protection-Keys), [Link2](https://fedoraproject.org/wiki/Changes/HardeningFlags28).** 20 | 21 | ## Background 22 | 23 | ### Why eXecutable-Only Memory 24 | - Protect randomized code. 25 | - Defend against [JIT-ROP Attack](https://cs.unc.edu/~fabian/papers/oakland2013.pdf). 26 | - Defend against [Hacking Blind Attack](http://www.scs.stanford.edu/~sorbo/brop/bittau-brop.pdf). 27 | 28 | ### Hardware Support 29 | - [LWN: Memory Protection Keys](https://lwn.net/Articles/643797/) 30 | - [Intel's Memory Protection Keys Specification](https://software.intel.com/sites/default/files/managed/7c/f1/253668-sdm-vol-3a.pdf) 31 | - [ARM's eXecutable-Only Memory](http://infocenter.arm.com/help/topic/com.arm.doc.dui0471j/chr1368698326509.html) 32 | 33 | ### Software Enabling 34 | - [XOM Enabling on Intel: BlackHat Asia 2018 Presentation](presentation/xom-switch-bhasia-2018-v1.3.pdf) 35 | - [XOM Enabling on ARM: NORAX](https://www.longlu.org/downloads/NORAX.pdf) 36 | 37 | 38 | ## Getting Started 39 | 40 | ### Platform Prerequsites 41 | To run xom-switch properly, you need to have hardware and OS support first: 42 | - Intel CPU with protection keys feature on, e.g, [INTEL® XEON® SCALABLE PROCESSORS](https://www.intel.com/content/www/us/en/products/processors/xeon/scalable.html) **AND** 43 | - Linux kernel 4.9 or later. 44 | **OR** 45 | - Use VM in AWS, choose Amazon Linux 2 LTS Candidate AMI 2017.12.0 (HVM) and then **C5 Instance**. 46 | 47 | ### Software Prerequsites 48 | xom-switch requires two tools: 49 | - common tools: bc, binutils, gcc, python 2.7 50 | - radare2 (v2.3.0): a static binary analyzer, which could be found in [here](https://github.com/radare/radare2.git) 51 | 52 | ### Components 53 | xom-switch consists of three modules: 54 | - [binary rewriter](src/rewriter/README.md): a static binary rewriter for x86 ELF binaries. 55 | - patch: C code pieces (see [tutorial](src/patch/tutorial/README.md) to write your own instrumentation) that will be patched into program loader. 56 | - analysis: analyzer/instrumentor of the program loader using radare2. 57 | 58 | ### Patching 59 | - install python 2.7 and radare2 60 | - patch your loader: `src/analysis/patch-loader.sh /lib64/ld-linux-x86-64.so.2 /your/new/ld.so` 61 | - copy your loader to system dir: ```sudo mv /your/new/ld.so /lib64/ld-xom.so``` 62 | - patch your libc.so (optional): ```src/analysis/patch-libc.sh /lib/x86_64-linux-gnu/libc.so.6 /your/new/libc.so``` 63 | 64 | Note: patching your libc allows you to apply XOM to their child processes spawned through execve(2). 65 | 66 | ### Running 67 | - apply XOM to your program: `/lib64/ld-xom.so /path/to/your/program` 68 | - apply XOM to your program and its children: `LD_PRELOAD=/your/new/libc.so /lib64/ld-xom.so /path/to/your/program` 69 | 70 | ## License 71 | 72 | This code is published under GPLv2 version. 73 | 74 | 75 | ## Project Status 76 | 77 | This code is for demo purpose only and the status of code is **beta**. 78 | 79 | ## Know Limitation 80 | xom-switch has known limitation in the following cases: 81 | - When binaries has data embedded in the middle of code, xom-switch may crash. To avoid that xom-switch has a white list embedded in code. see 82 | - Since code modules (exe and libs) are not compiled with XOM support, there would be at least two code pages (the 1st and last code page) for each module where code and data co-exist. xom-switch avoids the issue by marking them as readable and executable. In the future, we will solve that using static analysis. 83 | ## Task List 84 | 85 | - [x] Support CentOS 7.2. 86 | - [x] Support CentOS 7.4. 87 | - [x] Support Ubuntu 16.04. 88 | - [x] Support Ubuntu 17.04. 89 | - [x] Support simple instrumentation like function interception. 90 | - [x] Support Amazon Linux 2 LTS Candidate AMI 2017.12.0 (HVM) C5 VM. 91 | - [ ] Adding page fault handling to let go legitimate data read. 92 | 93 | 94 | -------------------------------------------------------------------------------- /experiments/MPK_rop_results.txt: -------------------------------------------------------------------------------- 1 | 2 | @ Tested binaries: 3 | @ binutils 2.26, 2.27, 2.28, 2.29 4 | @ coreutils 8.24, 8.25, 8.26, 8.27, 8.28 5 | @ glibc 2.22, 2.23, 2.24, 2.25, 2.26 6 | @ SPEC2006 7 | 8 | @ Result format 9 | @ filename offset: Opcode 10 | @ section 11 | @ 10 bytes following the Opcode 12 | 13 | 14 | -------------- Opcode ------------- 15 | /opt/glibc/2.26/lib/libc-2.26.so 0x00000000002c14a0: 0f01ee; 16 | .debug_info 17 | ['0xe', '0x0', '0x0', '0x4', '0xb2', '0x4f', '0x0', '0x0', '0x11', '0x17'] 18 | 19 | -------------- Opcode ------------- 20 | /opt/glibc/2.26/lib/libc-2.26.so 0x00000000002e6d30: 0f01ee; 21 | .debug_info 22 | ['0x0', '0x0', '0x0', '0xf9', '0x19', '0xb', '0x0', '0x38', '0xcd', '0xcf'] 23 | 24 | -------------- Opcode ------------- 25 | /opt/glibc/2.26/lib/libc-2.26.so 0x00000000002f3b2d: 0f01ee; 26 | .debug_info 27 | ['0xe', '0x0', '0x0', '0x4', '0xb2', '0x4f', '0x0', '0x0', '0x11', '0x17'] 28 | 29 | -------------- Opcode ------------- 30 | /opt/glibc/2.26/lib/libc-2.26.so 0x00000000002f4e68: 0f01ee; 31 | .debug_info 32 | ['0xe', '0x0', '0x0', '0x4', '0xb2', '0x4f', '0x0', '0x0', '0x11', '0x17'] 33 | 34 | -------------- Opcode ------------- 35 | /opt/glibc/2.26/lib/libc-2.26.so 0x000000000031cbb7: 0f01ee; 36 | .debug_info 37 | ['0xe', '0x0', '0x0', '0x4', '0xb2', '0x4f', '0x0', '0x0', '0x11', '0x17'] 38 | 39 | -------------- Opcode ------------- 40 | /opt/glibc/2.26/lib/libc-2.26.so 0x000000000031dffd: 0f01ee; 41 | .debug_info 42 | ['0xe', '0x0', '0x0', '0x4', '0xb2', '0x4f', '0x0', '0x0', '0x11', '0x17'] 43 | 44 | -------------- Opcode ------------- 45 | /opt/glibc/2.26/lib/libc-2.26.so 0x00000000003440f3: 0f01ee; 46 | .debug_info 47 | ['0xe', '0x0', '0x0', '0x4', '0xb2', '0x4f', '0x0', '0x0', '0x11', '0x17'] 48 | 49 | -------------- Opcode ------------- 50 | /opt/glibc/2.26/lib/libc-2.26.so 0x0000000000347aae: 0f01ee; 51 | .debug_info 52 | ['0xe', '0x0', '0x0', '0x4', '0xb2', '0x4f', '0x0', '0x0', '0x11', '0x17'] 53 | 54 | -------------- Opcode ------------- 55 | /opt/glibc/2.26/lib/libc-2.26.so 0x0000000000423bc2: 0f01ee; 56 | .debug_info 57 | ['0xe', '0x0', '0x0', '0x4', '0xb2', '0x4f', '0x0', '0x0', '0x11', '0x17'] 58 | 59 | -------------- Opcode ------------- 60 | /opt/glibc/2.26/lib/libc-2.26.so 0x00000000005831b0: 0f01ee; 61 | .debug_info 62 | ['0xe', '0x0', '0x0', '0x10', '0xb2', '0x4f', '0x0', '0x0', '0x11', '0x17'] 63 | 64 | -------------- Opcode ------------- 65 | /opt/glibc/2.26/lib/libc-2.26.so 0x0000000000585737: 0f01ee; 66 | .debug_info 67 | ['0xe', '0x0', '0x0', '0x10', '0xb2', '0x4f', '0x0', '0x0', '0x11', '0x17'] 68 | 69 | -------------- Opcode ------------- 70 | /opt/glibc/2.26/lib/libc-2.26.so 0x000000000059e058: 0f01ee; 71 | .debug_info 72 | ['0xe', '0x0', '0x0', '0x7', '0xb2', '0x4f', '0x0', '0x0', '0x11', '0x17'] 73 | 74 | -------------- Opcode ------------- 75 | /opt/glibc/2.26/lib/libc-2.26.so 0x00000000005ad57b: 0f01ee; 76 | .debug_info 77 | ['0xe', '0x0', '0x0', '0x4', '0xb2', '0x4f', '0x0', '0x0', '0x11', '0x17'] 78 | 79 | -------------- Opcode ------------- 80 | /opt/glibc/2.25/lib/libm-2.25.so 0x0000000000179803: 0f011f; 81 | .debug_info 82 | ['0x14', '0x1c', '0x0', '0x0', '0x14', '0x1c', '0x0', '0x0', '0x4', '0xef'] 83 | 84 | -------------- Opcode ------------- 85 | /opt/glibc/2.25/lib/libm-2.25.a 0x00000000003d7079: 0f011f; 86 | [ERROR] Failed to resolve section tables for /opt/glibc/2.25/lib/libm-2.25.a 87 | ['0x0', '0x0', '0x0', '0x0', '0x0', '0x0', '0x0', '0x0', '0x4', '0xef'] 88 | 89 | -------------- Opcode ------------- 90 | /opt/glibc/2.24/lib/libm-2.24.so 0x0000000000141eed: 0f011f; 91 | .debug_info 92 | ['0x91', '0x3a', '0x0', '0x0', '0x91', '0x3a', '0x0', '0x0', '0x4', '0xef'] 93 | 94 | -------------- Opcode ------------- 95 | /opt/glibc/2.24/lib/libm.a 0x000000000024a6c5: 0f011f; 96 | [ERROR] Failed to resolve section tables for /opt/glibc/2.24/lib/libm.a 97 | ['0x0', '0x0', '0x0', '0x0', '0x0', '0x0', '0x0', '0x0', '0x4', '0xef'] 98 | 99 | -------------- Opcode ------------- 100 | /opt/glibc/2.23/lib/libm-2.23.so 0x0000000000141b31: 0f011f; 101 | .debug_info 102 | ['0xae', '0x39', '0x0', '0x0', '0xae', '0x39', '0x0', '0x0', '0x4', '0xef'] 103 | 104 | -------------- Opcode ------------- 105 | /opt/glibc/2.23/lib/libm.a 0x0000000000249521: 0f011f; 106 | [ERROR] Failed to resolve section tables for /opt/glibc/2.23/lib/libm.a 107 | ['0x0', '0x0', '0x0', '0x0', '0x0', '0x0', '0x0', '0x0', '0x4', '0xef'] 108 | 109 | -------------- Opcode ------------- 110 | /opt/glibc/2.22/bin/localedef 0x000000000007e1f2: 0f01ee; 111 | .debug_info 112 | ['0x30', '0x0', '0x0', '0x2d', '0x57', '0x23', '0x0', '0x0', '0x8d', '0x54'] 113 | 114 | -------------------------------------------------------------------------------- /experiments/search_hexstring.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | tmp=$(mktemp) 3 | xxd -g 1 -p $1 |tr '\n' ' '|sed 's/ //g' |grep -o --byte-offset "0f01ef"| sed 's/:/ /g' >$tmp 4 | while read offset str; 5 | do 6 | a=$((offset%2)) 7 | if [ $a -eq 0 ]; then 8 | realoff=$((offset/2)) 9 | realoff=$(echo "obase=16;ibase=10;$realoff"|bc) 10 | echo "offset: 0x$realoff"; 11 | fi 12 | done <$tmp 13 | rm $tmp 14 | -------------------------------------------------------------------------------- /experiments/sizediff.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | src=$(readlink -f $1) 3 | dst=$(readlink -f $2) 4 | srcsize=$(stat --printf="%s" $src) 5 | dstsize=$(stat --printf="%s" $dst) 6 | echo $(((dstsize-srcsize)*100/srcsize)) 7 | -------------------------------------------------------------------------------- /presentation/xom-switch-bhasia-2018-v1.3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xom-switch/22cf7a855a180b450be506b8e1745b1ba770dd93/presentation/xom-switch-bhasia-2018-v1.3.pdf -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | log_* 3 | ld.so 4 | libkkk.so.6 5 | libc.so.6 6 | log 7 | -------------------------------------------------------------------------------- /src/analysis/README.md: -------------------------------------------------------------------------------- 1 | ## Build 2 | ``` 3 | ./patch-loader.sh [your new loader path] 4 | 5 | ./patch-libc.sh [new libc path] 6 | ``` 7 | 8 | ## Usage 9 | ``` 10 | ./your_loader.so 11 | ``` 12 | OR 13 | ``` 14 | LD_PRELOAD=/path/to/new_libc.so.6 ./your_loader.so 15 | ``` 16 | 17 | ## Instructions 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/analysis/check_cpuinfo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pku=$(cat /proc/cpuinfo|grep -o pku) 3 | ospke=$(cat /proc/cpuinfo|grep -o ospke) 4 | 5 | if [ "$pku" == "" ] || [ "$ospke" == "" ]; then 6 | echo "Memory Protection Key is not yet supported in your kernel. Please"\ 7 | "make sure your CPU has MPK support and then upgrade your kernel." 8 | exit 1 9 | fi 10 | echo "Great! You are ready to use memory protection keys!" 11 | exit 0 12 | -------------------------------------------------------------------------------- /src/analysis/patch-binary.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | progdir=$(readlink -f $(dirname $0)) 4 | rewriterpath=$progdir/../rewriter/examples 5 | scriptdir=$progdir/scripts 6 | 7 | if [ $# -lt 1 ]; then 8 | echo "" 9 | echo "[USAGE] [final binary]" 10 | echo "" 11 | exit 1 12 | fi 13 | exe=$1 14 | if [ ! -e $exe ]; then 15 | echo "[Error] binary $exe does not exists, please specify a valid binary." 16 | exit 1 17 | fi 18 | iself=$(file $(readlink -f $exe)|grep -o "ELF") 19 | if [ "$iself" == "" ]; then 20 | echo "[Error] file $exe is not an ELF executable." 21 | exit 1 22 | fi 23 | elf2inject=$2 24 | if [ ! -e $elf2inject ]; then 25 | echo "[Error] binary $elf2inject does not exists, please specify a valid one." 26 | exit 1 27 | fi 28 | targetexe=$3 29 | if [ "$targetexe" == "" ]; then 30 | targetexe=$(mktemp) 31 | echo "final binary name: $targetexe" 32 | fi 33 | r2path=$(command -v r2) 34 | if [ "$r2path" == "" ]; then 35 | echo "[Error] Please ensure that Radare2 is properly installed." 36 | exit 1 37 | fi 38 | 39 | $rewriterpath/inject_instrumentation.py -i $elf2inject -f $exe -o $targetexe 40 | $scriptdir/patch_calls2origcode_in_injected_code.sh $targetexe $elf2inject $exe 41 | $scriptdir/patch_calls2injectedcode_in_original_code.sh $targetexe $elf2inject $exe 42 | 43 | echo "injected executable has been saved as $targetexe" 44 | -------------------------------------------------------------------------------- /src/analysis/patch-libc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | progdir=$(readlink -f $(dirname $0)) 4 | rewriterpath=$progdir/../rewriter/examples 5 | scriptdir=$progdir/scripts 6 | 7 | if [ $# -lt 1 ]; then 8 | echo "" 9 | echo "[USAGE] [final binary]" 10 | echo "" 11 | exit 1 12 | fi 13 | exe=$1 14 | if [ ! -e $exe ]; then 15 | echo "[Error] binary $exe does not exists, please specify a valid ld.so." 16 | exit 1 17 | fi 18 | iself=$(file $(readlink -f $exe)|grep -o "ELF") 19 | if [ "$iself" == "" ]; then 20 | echo "[Error] file $exe is not an ELF executable." 21 | exit 1 22 | fi 23 | targetexe=$2 24 | if [ "$targetexe" == "" ]; then 25 | targetexe=$(mktemp) 26 | echo "final binary name: $targetexe" 27 | fi 28 | r2path=$(command -v r2) 29 | if [ "$r2path" == "" ]; then 30 | echo "[Error] Please ensure that Radare2 is properly installed." 31 | exit 1 32 | fi 33 | # Compile to-be-injected binary. 34 | echo "[Generating] xomenable code..." 35 | cd $progdir 36 | make -C ../patch/xomenable/ clean 37 | make -C ../patch/xomenable/ 38 | if [ $? -ne 0 ]; then 39 | echo "[Error] compiling xomenable code, please check your gcc setup." 40 | cd $OLDPWD 41 | exit 1 42 | fi 43 | cd $OLDPWD 44 | elf2inject=$progdir/../patch/xomenable/xomenable 45 | 46 | addrfile=$(mktemp) 47 | newexe=$(mktemp) 48 | 49 | $rewriterpath/inject_instrumentation.py -i $elf2inject -f $exe -o $targetexe 50 | 51 | execveaddr=$(r2 $exe -qc "/s~execve:0[:1]") 52 | if [ "$execveaddr" == "" ]; then 53 | echo "[Error] failed to find execve address, abort" 54 | exit 1 55 | fi 56 | echo $execveaddr > $addrfile 57 | 58 | $scriptdir/patch_calls_of_origbin.sh $addrfile _wrapper_syscall_execve \ 59 | $targetexe $elf2inject; 60 | $scriptdir/patch_call_of_injectedbin.sh post_syscall_execve syscall:execve \ 61 | $targetexe $elf2inject $exe 13; 62 | rm $newexe 63 | rm $addrfile 64 | 65 | echo "injected executable has been saved as $targetexe" 66 | -------------------------------------------------------------------------------- /src/analysis/patch-loader.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | progdir=$(readlink -f $(dirname $0)) 4 | rewriterpath=$progdir/../rewriter/examples 5 | scriptdir=$progdir/scripts 6 | 7 | if [ $# -lt 1 ]; then 8 | echo "" 9 | echo "[USAGE] [final binary]" 10 | echo "" 11 | exit 1 12 | fi 13 | exe=$1 14 | if [ ! -e $exe ]; then 15 | echo "[Error] binary $exe does not exists, please specify a valid ld.so." 16 | exit 1 17 | fi 18 | iself=$(file $(readlink -f $exe)|grep -o "ELF") 19 | if [ "$iself" == "" ]; then 20 | echo "[Error] file $exe is not an ELF executable." 21 | exit 1 22 | fi 23 | targetexe=$2 24 | if [ "$targetexe" == "" ]; then 25 | targetexe=$(mktemp) 26 | echo "final binary name: $targetexe" 27 | fi 28 | r2path=$(command -v r2) 29 | if [ "$r2path" == "" ]; then 30 | echo "[Error] Please ensure that Radare2 is properly installed." 31 | exit 1 32 | fi 33 | # Compile to-be-injected binary. 34 | echo "[Generating] xomenable code..." 35 | cd $progdir 36 | make -C ../patch/xomenable/ clean 37 | make -C ../patch/xomenable/ 38 | if [ $? -ne 0 ]; then 39 | echo "[Error] compiling xomenable code, please check your gcc setup." 40 | cd $OLDPWD 41 | exit 1 42 | fi 43 | cd $OLDPWD 44 | elf2inject=$progdir/../patch/xomenable/xomenable 45 | 46 | addrfile=$(mktemp) 47 | newexe=$(mktemp) 48 | 49 | $rewriterpath/xom_enable.py -f $exe -o $newexe 50 | 51 | echo "We assume XOM has been added to the ELF binary, so first we remove it" 52 | # 53 | # Removing XOM is done by removing the XOM related segments temporarily. This 54 | # is because these segments are not compatible with radare2, i.e., analysis 55 | # results would not show up. We achieve this by shrinking the phdr but not 56 | # reall removing those segments so that we could later add them back by 57 | # increase the phdr entry number. 58 | # 59 | $scriptdir/adjust_phnum.sh decrease $newexe 60 | 61 | $rewriterpath/inject_instrumentation.py -i $elf2inject -f $newexe -o $targetexe 62 | 63 | $scriptdir/patch_call_of_injectedbin.sh ldso_mmap syscall:mmap $targetexe \ 64 | $elf2inject $exe; 65 | $scriptdir/patch_call_of_injectedbin.sh ldso_mprotect syscall:mprotect \ 66 | $targetexe $elf2inject $exe; 67 | $scriptdir/patch_call_of_injectedbin.sh _dl_debug_vdprintf syscall:writev \ 68 | $targetexe $elf2inject $exe; 69 | 70 | $scriptdir/analyze_mmap_callsites.sh mmap $newexe > $addrfile 71 | 72 | $scriptdir/patch_calls_of_origbin.sh $addrfile _wrapper_mmap $targetexe \ 73 | $elf2inject; 74 | 75 | echo "We add XOM related segments back to the ELF binary" 76 | $scriptdir/adjust_phnum.sh increase $targetexe 77 | 78 | $scriptdir/filling_gap.sh $targetexe 79 | rm $newexe 80 | rm $addrfile 81 | 82 | echo "injected executable has been saved as $targetexe" 83 | -------------------------------------------------------------------------------- /src/analysis/scripts/adjust_phnum.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $# -ne 2 ]; then 3 | echo "[USAGE] " 4 | exit 1 5 | fi 6 | 7 | option=$1 8 | exe=$2 9 | if [ $option != "increase" ] && [ "$option" != "decrease" ]; then 10 | echo "invalid option" 11 | exit 1 12 | fi 13 | phnum=$(r2 -nn -qc 'pf.elf_header.phnum~[3]' $exe) 14 | offset=$(r2 -nn -qc 'pf.elf_header.phnum~[2]' $exe) 15 | echo $phnum 16 | echo $offset 17 | if [ $option == "increase" ]; then 18 | phnum=$(echo $(($phnum + 4))) 19 | echo $phnum 20 | else 21 | phnum=$(echo $(($phnum - 4))) 22 | echo $phnum 23 | fi 24 | phnum=$(printf "%02x" $phnum) 25 | echo $phnum 26 | r2 -nn -w -qc "wx $phnum @ $offset" $exe 27 | -------------------------------------------------------------------------------- /src/analysis/scripts/analyze_callsites.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $# -ne 2 ]; then 3 | echo "[USAGE]
" 4 | exit 0 5 | fi 6 | address=$1 7 | exe=$2 8 | progdir=$(readlink -f $(dirname $0)) 9 | r2 -qc "aaa; /r $address" $exe 2>/dev/null |sed 's/^\[.*\]\s*//g'|awk '{print $2 "\t" $3}' 10 | -------------------------------------------------------------------------------- /src/analysis/scripts/analyze_mmap_callsites.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $# -ne 2 ]; then 3 | echo "[USAGE] " 4 | exit 0 5 | fi 6 | syscall=$1 7 | exe=$2 8 | progdir=$(readlink -f $(dirname $0)) 9 | func=$($progdir/analyze_syscall.sh $syscall $exe) 10 | r2 -qc "aaa; /r $func" $exe 2>/dev/null |awk '{print $2}' 11 | -------------------------------------------------------------------------------- /src/analysis/scripts/analyze_pltcall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $# -ne 2 ]; then 3 | echo "[USAGE] " 4 | exit 0 5 | fi 6 | pltcall=$1 7 | exe=$2 8 | a=$(r2 -qc "aa; afl~sym.imp.$pltcall[0]" $exe) 9 | if [ "$a" == "" ]; then 10 | #echo "cannot find address of pltcall $pltcall, abort" 11 | exit 1 12 | fi 13 | echo $a 14 | -------------------------------------------------------------------------------- /src/analysis/scripts/analyze_syscall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $# -ne 2 ]; then 3 | echo "[USAGE] " 4 | exit 0 5 | fi 6 | syscall=$1 7 | exe=$2 8 | a=$(r2 -qc "/s~$syscall[:0]" $exe | awk '{print $1}') 9 | if [ "$a" == "" ]; then 10 | #echo "cannot find address of syscall $syscall, abort" 11 | exit 1 12 | fi 13 | #echo $a 14 | func=$(r2 -A -qc "s $a; sf.; s" $exe) 15 | echo $func 16 | -------------------------------------------------------------------------------- /src/analysis/scripts/filling_gap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $# != 1 ]; then 3 | exit 1 4 | fi 5 | exe=$1 6 | if [ ! -e $exe ]; then 7 | exit 1 8 | fi 9 | offsetbss=$(readelf -W -S $exe|sed 's/^ *\[//g'|grep " \.bss "|awk '{print $5}') 10 | offsetnextsec=$(readelf -W -S $exe|sed 's/^ *\[//g'|grep -A 1 " \.bss "|tail -1|awk '{print $5}') 11 | if [ "$offsetbss" == "" ] || [ "$offsetnextsec" == "" ]; then 12 | exit 1 13 | fi 14 | offsetbss="0x$offsetbss" 15 | offsetnextsec="0x$offsetnextsec" 16 | echo "offsetbss: $offsetbss" 17 | echo "offsetnextsec: $offsetnextsec" 18 | size=$(echo $(($offsetnextsec-$offsetbss))) 19 | dd if=/dev/zero of=$exe bs=1 count=$(echo $(($offsetnextsec - $offsetbss))) \ 20 | seek=$(echo $(($offsetbss))) conv=notrunc 21 | echo "done filling the gap" 22 | -------------------------------------------------------------------------------- /src/analysis/scripts/patch_call_of_injectedbin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $# -lt 5 ]; then 3 | echo "" 4 | echo " [USAGE] "\ 5 | " "\ 6 | "[offset to symbol]" 7 | echo "" 8 | echo " [NOTE]: this program patches symbols in injected binary to target"\ 9 | "its corresponding syscall wrapper in ld.so." 10 | echo " e.g.: the wrapper of mmap is mmap; the wrapper of writev in ld.so"\ 11 | "is actually _dl_debug_vdprintf (simple version of printf)." 12 | echo "" 13 | exit 1 14 | fi 15 | callname=$1 16 | syscall=$2 17 | exe=$3 18 | injectexe=$4 19 | origexe=$5 20 | offset2symbol=$6 21 | if [ "$offset2symbol" == "" ]; then 22 | offsetsymbol=0 23 | fi 24 | progdir=$(readlink -f $(dirname $0)) 25 | ftype=$(echo $syscall|awk -F":" '{print $1}') 26 | fname=$(echo $syscall|sed "s/^${ftype}//g; s/^://g") 27 | if [ "$fname" == "" ]; then 28 | echo " [ERROR] invalid function name." 29 | exit 1 30 | fi 31 | syscalladdr=0 32 | if [ "$ftype" == "syscall" ]; then 33 | syscalladdr=$($progdir/analyze_syscall.sh $fname $origexe) 34 | syscalladdr=$((syscalladdr+offsetsymbol)) 35 | elif [ "$ftype" == "pltcall" ]; then 36 | syscalladdr=$($progdir/analyze_pltcall.sh $fname $origexe) 37 | syscalladdr=$((syscalladdr+offsetsymbol)) 38 | elif [ "$ftype" == "dynsym" ]; then 39 | echo " [ERROR] function type not supported yet." 40 | exit 1 41 | elif [ "$ftype" == "symbol" ]; then 42 | echo " [ERROR] function type not supported yet." 43 | exit 1 44 | elif [ "$ftype" == "addr" ]; then 45 | echo " [ERROR] function type not supported yet." 46 | exit 1 47 | else 48 | echo " [ERROR] invalid function type" 49 | exit 1 50 | fi 51 | if [ "$syscalladdr" == "" ] || [ "$syscalladdr" == "0" ]; then 52 | echo " [ERROR] cannot find function $syscall" 53 | exit 1 54 | fi 55 | echo "$fname" 56 | echo "$syscall wrapper function address: $syscalladdr" 57 | textoffset=$(r2 -qc "iS~.text[1]" $injectexe) 58 | echo "textoffset: $textoffset" 59 | textoffset4BC=$(printf "%X" $textoffset) 60 | #we preserve the offset within page 61 | offsetinpage=$(echo "obase=10; ibase=16; $textoffset4BC % 1000" | bc) 62 | echo "offset in page: $offsetinpage" 63 | offsetinpageHEX=$(printf "0x%x" $offsetinpage) 64 | echo "offset in page (hex): $offsetinpageHEX" 65 | echo "callname: $callname" 66 | topatchoffset=$(r2 -qc "is~$callname$" $injectexe|head -1|awk '{print $2}') 67 | echo "topatchoffset: $topatchoffset" 68 | echo "textoffset: $textoffset" 69 | echo "callname: $callname" 70 | offset=$((topatchoffset - textoffset)) 71 | base=$(r2 -qc "iS~.instrumented_code[3]" $exe) 72 | echo "base: $base" 73 | 74 | patchaddr=$((base + offset + offsetinpage)) 75 | echo "address to patch: $patchaddr" 76 | r2 -Aw -qc "s $patchaddr; wa jmp $syscalladdr; save; quit" $exe 77 | 78 | -------------------------------------------------------------------------------- /src/analysis/scripts/patch_calls2injectedcode_in_original_code.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $# -ne 3 ]; then 3 | echo "" 4 | echo " [USAGE] "\ 5 | "" 6 | echo "" 7 | echo " [NOTE]: this program patches calls in binary to patch to target"\ 8 | "its hooking function wrapper in injected code section." 9 | echo "" 10 | exit 1 11 | fi 12 | exe=$1 13 | injectexe=$2 14 | origexe=$3 15 | progdir=$(readlink -f $(dirname $0)) 16 | readelf -W -s $injectexe |grep FUNC |grep " intercept_"|awk '{print $8}' |\ 17 | while read symbol; do 18 | addrfile=$(mktemp) 19 | directcallfile=$(mktemp) 20 | echo "[Processing] function: $symbol" 21 | addr=$($progdir/transform_symbol.sh $symbol $origexe) 22 | ret=$? 23 | if [ "$addr" == "" ] || [ $ret -ne 0 ]; then 24 | echo " [ERROR] cannot find address of $symbol, proceed" 25 | continue 26 | fi 27 | echo "addr: $addr" 28 | echo "exe: $origexe" 29 | $progdir/analyze_callsites.sh $addr $origexe > $addrfile 30 | echo "save address list in $addrfile" 31 | cat $addrfile|grep "\[call\]" |awk '{print $1}' > $directcallfile 32 | $progdir/patch_calls_of_origbin.sh $directcallfile $symbol \ 33 | $exe $injectexe; 34 | done 35 | -------------------------------------------------------------------------------- /src/analysis/scripts/patch_calls2origcode_in_injected_code.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $# -ne 3 ]; then 3 | echo "" 4 | echo " [USAGE] "\ 5 | "" 6 | echo "" 7 | echo " [NOTE]: this program patches symbols in injected binary to target"\ 8 | "its corresponding function wrapper in binary to patch." 9 | echo "" 10 | exit 1 11 | fi 12 | exe=$1 13 | injectexe=$2 14 | origexe=$3 15 | progdir=$(readlink -f $(dirname $0)) 16 | readelf -W -s $injectexe |grep FUNC |awk '{print $8}' | grep "^original_"|\ 17 | while read callname; do 18 | cname=$(echo $callname|sed 's/^original_//g') 19 | ftype=$(echo $cname|awk -F"_" '{print $1}') 20 | fname=$(echo $cname|sed "s/^${ftype}//g; s/^_//g") 21 | if [ "$fname" == "" ]; then 22 | echo " [ERROR] invalid function name." 23 | exit 1 24 | fi 25 | if [ "$ftype" == "syscall" ]; then 26 | fname="syscall:$fname" 27 | elif [ "$ftype" == "pltcall" ]; then 28 | fname="pltcall:$fname" 29 | elif [ "$ftype" == "dynsym" ]; then 30 | fname="dynsym:$fname" 31 | elif [ "$ftype" == "symbol" ]; then 32 | fname="symbol:$fname" 33 | elif [ "$ftype" == "addr" ]; then 34 | fname="addr:$fname" 35 | else 36 | echo " [ERROR] invalid function type" 37 | exit 1 38 | fi 39 | echo "[Processing] $callname" 40 | $progdir/patch_call_of_injectedbin.sh $callname $fname $exe $injectexe \ 41 | $origexe 42 | done 43 | -------------------------------------------------------------------------------- /src/analysis/scripts/patch_calls_of_origbin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $# -ne 4 ]; then 3 | echo "" 4 | echo " [NOTE]: This program is to change callsites specified in of to targeting the corresponding"\ 6 | "symbol address in " 7 | echo "" 8 | echo " [NOTE]: This program should be invoked after binary injection." 9 | 10 | echo "" 11 | echo " [USAGE] "\ 12 | " " 13 | echo "" 14 | exit 1 15 | fi 16 | addrfile=$1 17 | symbol=$2 18 | exe=$3 19 | injectexe=$4 20 | #target address was the entry point of original binary 21 | echo "patching addresses stored in file: $addrfile" 22 | 23 | textoffset=$(r2 -qc "iS~.text[1]" $injectexe) 24 | echo $textoffset 25 | textoffset4BC=$(printf "%X" $textoffset) 26 | #we preserve the offset within page 27 | offsetinpage=$(echo "obase=10; ibase=16; $textoffset4BC % 1000" | bc) 28 | echo "offset in page: $offsetinpage" 29 | offsetinpageHEX=$(printf "0x%x" $offsetinpage) 30 | 31 | entryoffset=$(r2 -qc "is~ $symbol[1]" $injectexe) 32 | echo "entryoffset: $entryoffset" 33 | offset=$((entryoffset-textoffset)) 34 | echo $offset 35 | base=$(r2 -qc "iS~.instrumented_code[3]" $exe) 36 | if [ "$base" == "" ]; then 37 | echo " [ERROR] invalid base address, please check if the binary is"\ 38 | "instrumented or not." 39 | exit 1 40 | fi 41 | target=$((base + offset + offsetinpage)) 42 | echo "target value is: $target" 43 | 44 | while read addr; do 45 | echo "patching address: $addr with \"call $target\"" 46 | r2 -w -qc "s $addr; wa call $target; save; quit" $exe 47 | done <$addrfile 48 | -------------------------------------------------------------------------------- /src/analysis/scripts/transform_symbol.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ***************************************************************************** 3 | # 4 | # THis program transform a symbol in injected binary to the location 5 | # in original binary. 6 | # 7 | # For instance, symbol, "original_pltcall_printf", will need to be transformed 8 | # into one address in original binary that is the location of the pltcall stub 9 | # of printf. 10 | # 11 | # ***************************************************************************** 12 | # 13 | # Note: symbol name should **always** be like the following format: 14 | # 15 | # ##hook-type##_##calltype##_##original-function-name-or-address## 16 | # 17 | # This program will ignore the hook-type, but take a look of all the remaining 18 | # fields. For instance, 19 | # 20 | # calltype could be: 21 | # - syscall : system call wrapper function entry address. 22 | # - pltcall : plt call stub code entry address. 23 | # - dynsym : exported function entry address. 24 | # - symbol : function entry address specified in static symbol table. 25 | # - addr : address (in hex without '0x') of a function. 26 | # 27 | # calltype and original-function-name-or-address together help identify the 28 | # location of original function to hook. 29 | # 30 | # ***************************************************************************** 31 | 32 | symbol=$1 33 | exe=$2 34 | progdir=$(readlink -f $(dirname $0)) 35 | cname=$(echo $symbol|sed 's/^[^_]\+_//g') 36 | ftype=$(echo $cname|awk -F"_" '{print $1}') 37 | fname=$(echo $cname|sed "s/^${ftype}//g; s/^_//g") 38 | if [ "$fname" == "" ]; then 39 | echo " [ERROR] invalid function name." 40 | exit 1 41 | fi 42 | origfuncaddr=0 43 | if [ "$ftype" == "syscall" ]; then 44 | origfuncaddr=$($progdir/analyze_syscall.sh $fname $exe) 45 | elif [ "$ftype" == "pltcall" ]; then 46 | origfuncaddr=$($progdir/analyze_pltcall.sh $fname $exe) 47 | elif [ "$ftype" == "dynsym" ]; then 48 | echo " [ERROR] function type not supported yet." 49 | exit 1 50 | elif [ "$ftype" == "symbol" ]; then 51 | echo " [ERROR] function type not supported yet." 52 | exit 1 53 | elif [ "$ftype" == "addr" ]; then 54 | echo " [ERROR] function type not supported yet." 55 | exit 1 56 | else 57 | echo " [ERROR] invalid function type" 58 | exit 1 59 | fi 60 | echo $origfuncaddr 61 | -------------------------------------------------------------------------------- /src/analysis/scripts/unused/analyze_mmap_syscall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $# -ne 1 ]; then 3 | echo "[USAGE] " 4 | exit 0 5 | fi 6 | exe=$1 7 | a=$(r2 -A -qc '/s | grep mmap |sed -n "1p"' $exe | awk '{print $1}') 8 | if [ "$a" == "" ]; then 9 | echo "cannot find mmap syscall address, abort" 10 | exit 1 11 | fi 12 | #echo $a 13 | func=$(r2 -qc "aaa; s $a; sf.; s" $exe) 14 | echo $func 15 | -------------------------------------------------------------------------------- /src/analysis/scripts/unused/patch_backward_jump.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $# -ne 2 ]; then 3 | echo "[USAGE] " 4 | exit 1 5 | fi 6 | exe=$1 7 | injectexe=$2 8 | progdir=$(readlink -f $(dirname $0)) 9 | mmap=$($progdir/analyze_mmap_syscall.sh $1) 10 | echo "mmap function address: $mmap" 11 | textoffset=$(r2 -A -qc "iS~.text[1]" $injectexe) 12 | echo $textoffset 13 | topatchoffset=$(r2 -A -qc "is~ldso_mmap[1]" $injectexe) 14 | echo $topatchoffset 15 | offset=$((topatchoffset - textoffset)) 16 | base=$(r2 -A -qc "iS~.instrumented_code[3]" $exe) 17 | echo $base 18 | 19 | patchaddr=$((base + offset)) 20 | echo "address to patch: $patchaddr" 21 | r2 -Aw -qc "s $patchaddr; wa jmp $mmap; save; quit" $exe 22 | 23 | -------------------------------------------------------------------------------- /src/analysis/tests/binfiles: -------------------------------------------------------------------------------- 1 | 0 /bin/ls 2 | 0 /bin/sleep 1 3 | 0 /usr/bin/apt-get -v 4 | 0 /usr/bin/nmap -v 5 | 0 /usr/bin/luatex --luaonly files/h.lua 6 | -------------------------------------------------------------------------------- /src/analysis/tests/files/h.lua: -------------------------------------------------------------------------------- 1 | print("Hello World") 2 | -------------------------------------------------------------------------------- /src/analysis/tests/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | binary=$1 3 | progdir=$(readlink -f $(dirname $0)) 4 | binfile="$progdir/binfiles" 5 | echo "[Rewriting] loader code" 6 | $progdir/../patch-loader.sh /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 \ 7 | $progdir/../../patch/xomenable/xomenable $progdir/ld.so \ 8 | >$progdir/.rewritelog 2>&1 9 | if [ $? -ne 0 ]; then 10 | echo "[Error] generating ld.so" 11 | exit 1 12 | fi 13 | if [ "$binary" != "" ]; then 14 | $progdir/ld.so $binary 15 | exit 0 16 | fi 17 | echo "[Testing] binaries: " 18 | while read bin; 19 | do 20 | retval=$(echo $bin | awk '{print $1}') 21 | biname=$(echo $bin | awk '{print $2}') 22 | cmdline=$(echo $bin | awk '{$1=""}1') 23 | timeout 60 $progdir/ld.so $cmdline >$progdir/.runlog 2>&1 24 | if [ "$?" == "$retal" ]; then 25 | echo "[Error] testing $biname" 26 | exit 1 27 | else 28 | echo " [Succeed] testing $biname" 29 | fi 30 | done < $binfile 31 | -------------------------------------------------------------------------------- /src/patch/common/func.s: -------------------------------------------------------------------------------- 1 | .text 2 | .globl FUNCNAME 3 | .type FUNCNAME, @function 4 | FUNCNAME: 5 | .byte 0xe9,0x00, 0x00, 0x00, 0x00 6 | FUNCNAME_ret: 7 | ret 8 | .size FUNCNAME, .-FUNCNAME 9 | 10 | -------------------------------------------------------------------------------- /src/patch/common/jump2original_gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | bindir=$1 3 | funcname=$1 4 | progdir=$(readlink -f $(dirname $0)) 5 | cat $bindir/trampoline.s > $bindir/trampoline_final.s 6 | cat $bindir/*.c |grep "original_.*(" |\ 7 | sed 's/^.*original_\(.*\)*\s*(.*$/\1/g'|sort|uniq|\ 8 | while read f; do 9 | echo "func: $f" 10 | cat $progdir/func.s |sed "s/FUNCNAME/original_$f/g" \ 11 | >> $bindir/trampoline_final.s 12 | done 13 | -------------------------------------------------------------------------------- /src/patch/tutorial/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | ../common/jump2original_gen.sh . 3 | gcc -nostdlib -fno-exceptions -fno-stack-protector -fPIC -c main.c 4 | gcc -Wl,-T,ld.script -nostdlib -o tutorial main.o trampoline_final.s 5 | clean: 6 | rm -f trampoline_final.s tutorial *.o 7 | -------------------------------------------------------------------------------- /src/patch/tutorial/README.md: -------------------------------------------------------------------------------- 1 | ## Instrumentation Tutorial 2 | 3 | This tutorial tells you how to write your instrumentation code in C. We demonstrate the instrumentation steps as follows: 4 | 5 | #### Write your instrumentation 6 | The following is a short sample: 7 | ```C 8 | #include 9 | #include 10 | #include 11 | 12 | /* intercepting calls to fwrite_unlocked */ 13 | int intercept_pltcall_fwrite_unlocked(const void *ptr, size_t size, size_t n, 14 | FILE *stream) 15 | { 16 | original_pltcall___printf_chk(1, "\n**hacker** fwrite is hooked\n"); 17 | return original_pltcall_fwrite_unlocked(ptr, size, n, stream); 18 | } 19 | ``` 20 | You may put your code logic in your instrumentation function `intercept_pltcall_fwrite_unlocked`. Please note the function naming format. `intercept` means a method of interception. Basically `intercept` works by modifying all call sites to target your interception function where you decide when to call the original function. `pltcall` indicates that the function to be intercepted is a PLT code stub calling the actual library function. `fwrite_unlocked` means the function to intercept. 21 | 22 | #### Define usage of original functions 23 | ```C 24 | /* This function represents an auto-generated stub code that contains a jump to 25 | * the original function 'printf'. */ 26 | extern int original_pltcall___printf_chk(int flag, const char *fmt, ...); 27 | extern int original_pltcall_fwrite_unlocked(const void *ptr, size_t size, 28 | size_t n, FILE *stream); 29 | ``` 30 | The functions starting with `original_` represent code stubs that target original functions in your target binaries. For instance, `original_pltcall_fwrite_unlocked` is the stub code that jump to the location of original function. Note that if your target binary does not contain such a function, it will be an *empty* stub call that does nothing but returns. Code stubs are auto-generated so users do not have to care about where they are. 31 | 32 | #### Complete instrumentation code 33 | ```C 34 | #include 35 | #include 36 | #include 37 | /* This function represents an auto-generated stub code that contains a jump to 38 | * the original function 'printf'. */ 39 | extern int original_pltcall___printf_chk(int flag, const char *fmt, ...); 40 | extern int original_pltcall_fwrite_unlocked(const void *ptr, size_t size, 41 | size_t n, FILE *stream); 42 | 43 | /* intercepting calls to fwrite_unlocked */ 44 | int intercept_pltcall_fwrite_unlocked(const void *ptr, size_t size, size_t n, 45 | FILE *stream) 46 | { 47 | original_pltcall___printf_chk(1, "\n**hacker** fwrite is hooked\n"); 48 | return original_pltcall_fwrite_unlocked(ptr, size, n, stream); 49 | } 50 | ``` 51 | #### Compile your code 52 | Go to src/patch/tutorial and run `make` and you will see a binary `tutorial` in path `src/patch/tutorial`. 53 | 54 | #### Instrument your binary 55 | Run the following command to patch `tutorial` into `/bin/ls`: 56 | ```Bash 57 | src/analysis/patch-binary.sh /bin/ls src/patch/tutorial/tutorial ./my_ls 58 | ``` 59 | #### Run and see result 60 | You may run `my_ls` and see the result as follows: 61 | ``` 62 | **hacker** fwrite is hooked 63 | check_cpuinfo.sh 64 | **hacker** fwrite is hooked 65 | libc-xom.so.6 66 | **hacker** fwrite is hooked 67 | patch-binary.sh 68 | **hacker** fwrite is hooked 69 | patch-loader.sh 70 | **hacker** fwrite is hooked 71 | scripts 72 | 73 | **hacker** fwrite is hooked 74 | ld-xom.so 75 | **hacker** fwrite is hooked 76 | newls 77 | **hacker** fwrite is hooked 78 | patch-libc.sh 79 | **hacker** fwrite is hooked 80 | README.md 81 | **hacker** fwrite is hooked 82 | tests 83 | ``` 84 | As you can see that every time your `newls` prints something to standard output, it will print an extra line that you inserted! 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/patch/tutorial/ld.script: -------------------------------------------------------------------------------- 1 | /* Script for -z combreloc: combine and sort reloc sections */ 2 | /* Copyright (C) 2014-2017 Free Software Foundation, Inc. 3 | Copying and distribution of this script, with or without modification, 4 | are permitted in any medium without royalty provided the copyright 5 | notice and this notice are preserved. */ 6 | OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", 7 | "elf64-x86-64") 8 | OUTPUT_ARCH(i386:x86-64) 9 | ENTRY(_start) 10 | SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib"); 11 | SECTIONS 12 | { 13 | /* Read-only sections, merged into text segment: */ 14 | PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS; 15 | .interp : { *(.interp) } 16 | .note.gnu.build-id : { *(.note.gnu.build-id) } 17 | .hash : { *(.hash) } 18 | .gnu.hash : { *(.gnu.hash) } 19 | .dynsym : { *(.dynsym) } 20 | .dynstr : { *(.dynstr) } 21 | .gnu.version : { *(.gnu.version) } 22 | .gnu.version_d : { *(.gnu.version_d) } 23 | .gnu.version_r : { *(.gnu.version_r) } 24 | .rela.dyn : 25 | { 26 | *(.rela.init) 27 | *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) 28 | *(.rela.fini) 29 | *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) 30 | *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) 31 | *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) 32 | *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) 33 | *(.rela.ctors) 34 | *(.rela.dtors) 35 | *(.rela.got) 36 | *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) 37 | *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*) 38 | *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*) 39 | *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*) 40 | *(.rela.ifunc) 41 | } 42 | .rela.plt : 43 | { 44 | *(.rela.plt) 45 | PROVIDE_HIDDEN (__rela_iplt_start = .); 46 | *(.rela.iplt) 47 | PROVIDE_HIDDEN (__rela_iplt_end = .); 48 | } 49 | .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } 50 | .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } 51 | .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table 52 | .gcc_except_table.*) } 53 | .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } 54 | /* These sections are generated by the Sun/Oracle C++ compiler. */ 55 | .exception_ranges : ONLY_IF_RO { *(.exception_ranges 56 | .exception_ranges*) } 57 | .init : 58 | { 59 | KEEP (*(SORT_NONE(.init))) 60 | } 61 | .plt : { *(.plt) *(.iplt) } 62 | .plt.got : { *(.plt.got) } 63 | .plt.bnd : { *(.plt.bnd) } 64 | .fini : 65 | { 66 | KEEP (*(SORT_NONE(.fini))) 67 | } 68 | .text : 69 | { 70 | *(.text.unlikely .text.*_unlikely .text.unlikely.*) 71 | *(.text.exit .text.exit.*) 72 | *(.text.startup .text.startup.*) 73 | *(.text.hot .text.hot.*) 74 | *(.text .stub .text.* .gnu.linkonce.t.*) 75 | /* .gnu.warning sections are handled specially by elf32.em. */ 76 | *(.gnu.warning) 77 | } 78 | PROVIDE (__etext = .); 79 | PROVIDE (_etext = .); 80 | PROVIDE (etext = .); 81 | . = ALIGN(4K); 82 | .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } 83 | .rodata1 : { *(.rodata1) } 84 | /* Adjust the address for the data segment. We want to adjust up to 85 | the same address within the page on the next page up. */ 86 | . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); 87 | .data : 88 | { 89 | *(.data .data.* .gnu.linkonce.d.*) 90 | SORT(CONSTRUCTORS) 91 | } 92 | .data1 : { *(.data1) } 93 | 94 | /* Exception handling */ 95 | .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } 96 | .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } 97 | .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } 98 | .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } 99 | /* Thread Local Storage sections */ 100 | .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } 101 | .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } 102 | .preinit_array : 103 | { 104 | PROVIDE_HIDDEN (__preinit_array_start = .); 105 | KEEP (*(.preinit_array)) 106 | PROVIDE_HIDDEN (__preinit_array_end = .); 107 | } 108 | .init_array : 109 | { 110 | PROVIDE_HIDDEN (__init_array_start = .); 111 | KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) 112 | KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) 113 | PROVIDE_HIDDEN (__init_array_end = .); 114 | } 115 | .fini_array : 116 | { 117 | PROVIDE_HIDDEN (__fini_array_start = .); 118 | KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) 119 | KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) 120 | PROVIDE_HIDDEN (__fini_array_end = .); 121 | } 122 | .ctors : 123 | { 124 | /* gcc uses crtbegin.o to find the start of 125 | the constructors, so we make sure it is 126 | first. Because this is a wildcard, it 127 | doesn't matter if the user does not 128 | actually link against crtbegin.o; the 129 | linker won't look for a file to match a 130 | wildcard. The wildcard also means that it 131 | doesn't matter which directory crtbegin.o 132 | is in. */ 133 | KEEP (*crtbegin.o(.ctors)) 134 | KEEP (*crtbegin?.o(.ctors)) 135 | /* We don't want to include the .ctor section from 136 | the crtend.o file until after the sorted ctors. 137 | The .ctor section from the crtend file contains the 138 | end of ctors marker and it must be last */ 139 | KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) 140 | KEEP (*(SORT(.ctors.*))) 141 | KEEP (*(.ctors)) 142 | } 143 | .dtors : 144 | { 145 | KEEP (*crtbegin.o(.dtors)) 146 | KEEP (*crtbegin?.o(.dtors)) 147 | KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) 148 | KEEP (*(SORT(.dtors.*))) 149 | KEEP (*(.dtors)) 150 | } 151 | .jcr : { KEEP (*(.jcr)) } 152 | .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } 153 | .dynamic : { *(.dynamic) } 154 | .got : { *(.got) *(.igot) } 155 | . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .); 156 | .got.plt : { *(.got.plt) *(.igot.plt) } 157 | _edata = .; PROVIDE (edata = .); 158 | . = .; 159 | __bss_start = .; 160 | .bss : 161 | { 162 | *(.dynbss) 163 | *(.bss .bss.* .gnu.linkonce.b.*) 164 | *(COMMON) 165 | /* Align here to ensure that the .bss section occupies space up to 166 | _end. Align after .bss to ensure correct alignment even if the 167 | .bss section disappears because there are no input sections. 168 | FIXME: Why do we need it? When there is no .bss section, we don't 169 | pad the .data section. */ 170 | . = ALIGN(. != 0 ? 64 / 8 : 1); 171 | } 172 | .lbss : 173 | { 174 | *(.dynlbss) 175 | *(.lbss .lbss.* .gnu.linkonce.lb.*) 176 | *(LARGE_COMMON) 177 | } 178 | . = ALIGN(64 / 8); 179 | . = SEGMENT_START("ldata-segment", .); 180 | .lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : 181 | { 182 | *(.lrodata .lrodata.* .gnu.linkonce.lr.*) 183 | } 184 | .ldata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : 185 | { 186 | *(.ldata .ldata.* .gnu.linkonce.l.*) 187 | . = ALIGN(. != 0 ? 64 / 8 : 1); 188 | } 189 | . = ALIGN(64 / 8); 190 | _end = .; PROVIDE (end = .); 191 | . = DATA_SEGMENT_END (.); 192 | /* Stabs debugging sections. */ 193 | .stab 0 : { *(.stab) } 194 | .stabstr 0 : { *(.stabstr) } 195 | .stab.excl 0 : { *(.stab.excl) } 196 | .stab.exclstr 0 : { *(.stab.exclstr) } 197 | .stab.index 0 : { *(.stab.index) } 198 | .stab.indexstr 0 : { *(.stab.indexstr) } 199 | .comment 0 : { *(.comment) } 200 | /* DWARF debug sections. 201 | Symbols in the DWARF debugging sections are relative to the beginning 202 | of the section so we begin them at 0. */ 203 | /* DWARF 1 */ 204 | .debug 0 : { *(.debug) } 205 | .line 0 : { *(.line) } 206 | /* GNU DWARF 1 extensions */ 207 | .debug_srcinfo 0 : { *(.debug_srcinfo) } 208 | .debug_sfnames 0 : { *(.debug_sfnames) } 209 | /* DWARF 1.1 and DWARF 2 */ 210 | .debug_aranges 0 : { *(.debug_aranges) } 211 | .debug_pubnames 0 : { *(.debug_pubnames) } 212 | /* DWARF 2 */ 213 | .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } 214 | .debug_abbrev 0 : { *(.debug_abbrev) } 215 | .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end ) } 216 | .debug_frame 0 : { *(.debug_frame) } 217 | .debug_str 0 : { *(.debug_str) } 218 | .debug_loc 0 : { *(.debug_loc) } 219 | .debug_macinfo 0 : { *(.debug_macinfo) } 220 | /* SGI/MIPS DWARF 2 extensions */ 221 | .debug_weaknames 0 : { *(.debug_weaknames) } 222 | .debug_funcnames 0 : { *(.debug_funcnames) } 223 | .debug_typenames 0 : { *(.debug_typenames) } 224 | .debug_varnames 0 : { *(.debug_varnames) } 225 | /* DWARF 3 */ 226 | .debug_pubtypes 0 : { *(.debug_pubtypes) } 227 | .debug_ranges 0 : { *(.debug_ranges) } 228 | /* DWARF Extension. */ 229 | .debug_macro 0 : { *(.debug_macro) } 230 | .debug_addr 0 : { *(.debug_addr) } 231 | .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } 232 | /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } 233 | } 234 | -------------------------------------------------------------------------------- /src/patch/tutorial/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | /* This function represents an auto-generated stub code that contains a jump to 5 | * the original function 'printf'. */ 6 | extern int original_pltcall___printf_chk(int flag, const char *fmt, ...); 7 | extern int original_pltcall_fwrite_unlocked(const void *ptr, size_t size, 8 | size_t n, FILE *stream); 9 | 10 | /* Interception function name should **always** be like the following format: 11 | * 12 | * ##hook-type##_##calltype##_##original-function-name-or-address## 13 | * 14 | * hook-type right now supports only "intercept". "Intercept" means that your 15 | * instrumentation function will be like this in runtime: 16 | * 17 | * (orig code) ... call intercept_funcA -> 18 | * (your code) ... call original_funcA -> 19 | * (orig code) ... ret 20 | * 21 | * Note: only "direct call sites" are patched to your interception function. 22 | * "indirect call sites" are currently not hooked. 23 | * 24 | * calltype could be: 25 | * - syscall : system call wrapper function entry address. 26 | * - pltcall : plt call stub code entry address. 27 | * - dynsym : exported function entry address. 28 | * - symbol : function entry address specified in static symbol table. 29 | * - addr : address (in hex without '0x') of a function. 30 | * 31 | * Note: calltype and original-function-name-or-address together help identify 32 | * the location of original function to hook. 33 | */ 34 | 35 | /* intercepting calls to fwrite_unlocked */ 36 | int intercept_pltcall_fwrite_unlocked(const void *ptr, size_t size, size_t n, 37 | FILE *stream) 38 | { 39 | original_pltcall___printf_chk(1, "\n**hacker** fwrite is hooked\n"); 40 | return original_pltcall_fwrite_unlocked(ptr, size, n, stream); 41 | } 42 | -------------------------------------------------------------------------------- /src/patch/tutorial/trampoline.s: -------------------------------------------------------------------------------- 1 | .text 2 | .globl _start 3 | .type _start, @function 4 | _start: 5 | hlt 6 | .size _start, .-_start 7 | -------------------------------------------------------------------------------- /src/patch/xomenable/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc -Wall -nostdlib -fno-exceptions -fno-stack-protector -fPIC -c main.c 3 | gcc -Wall -nostdlib -fno-exceptions -fno-stack-protector -fPIC -c enable_xom.c 4 | gcc -Wall -nostdlib -fno-exceptions -fno-stack-protector -fPIC -c utils.c 5 | gcc -Wall -Wl,-T,ld.script -nostdlib -o xomenable main.o enable_xom.o trampoline.s utils.o 6 | clean: 7 | rm -f xomenable *.o 8 | -------------------------------------------------------------------------------- /src/patch/xomenable/README.md: -------------------------------------------------------------------------------- 1 | Compile this code and you got the executable that will be injected into ld.so! 2 | 3 | Note: this executable cannot be directly executed, so don't ./program :-) 4 | -------------------------------------------------------------------------------- /src/patch/xomenable/enable_xom.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "utils.h" 9 | extern void *ldso_mmap(void *addr, size_t len, int prot, int flags, 10 | int filedes, off_t off); 11 | extern void *ldso_mprotect(void *addr, size_t len, int prot); 12 | extern void simple_printf(char *fmt, ...); 13 | extern int __syscall(int number, ...); 14 | 15 | char *get_elf_soname(void *base, Elf64_Ehdr *elfhdr) 16 | { 17 | int idx = 0; 18 | int phdrent = elfhdr->e_phnum; 19 | int soname_offset = -1; 20 | int dynstr_offset = -1; 21 | Elf64_Dyn *dynamic = NULL; 22 | Elf64_Phdr *phdr = base + elfhdr->e_phoff; 23 | /* Get Dynamic Segment Offset. */ 24 | for (idx = 0; idx < phdrent; idx++) { 25 | if (phdr->p_type == PT_DYNAMIC) { 26 | dynamic = (Elf64_Dyn *)(base + phdr->p_offset); 27 | break; 28 | } 29 | phdr++; 30 | } 31 | if (dynamic == NULL) { 32 | //simple_printf("failed to find dynamic segment.\n"); 33 | return NULL; 34 | } 35 | /* Traverse Dynamic Segment. */ 36 | while (dynamic->d_tag != DT_NULL) { 37 | if (dynamic->d_tag == DT_SONAME) 38 | soname_offset = dynamic->d_un.d_val; 39 | else if (dynamic->d_tag == DT_STRTAB) 40 | dynstr_offset = dynamic->d_un.d_val; 41 | dynamic++; 42 | } 43 | if (soname_offset == -1 || dynstr_offset == -1) { 44 | //simple_printf("failed to find soname or dynstr offset.\n"); 45 | return NULL; 46 | } 47 | return base + soname_offset + dynstr_offset; 48 | } 49 | 50 | int check_elf_whitelist(void *base, Elf64_Ehdr *elfhdr) 51 | { 52 | typedef struct { 53 | char *soname; 54 | int len; 55 | } whitelistlib; 56 | whitelistlib whitelist[4] = { 57 | {"libffi.so.6", sizeof("libffi.so.6") - 1}, 58 | {"libfreeblpriv3.so", sizeof("libfreeblpriv3.so") - 1}, 59 | {"libavcodec.so", sizeof("libavcodec.so") - 1}, 60 | {"libcrypto.so", sizeof("libcrypto.so") - 1} 61 | }; 62 | int whitelistsize = sizeof(whitelist)/sizeof(whitelist[0]); 63 | char *soname = get_elf_soname(base, elfhdr); 64 | int idx = 0; 65 | if (soname == NULL) { 66 | /* Executable does not have SO_NAME. */ 67 | return 0; 68 | } 69 | //simple_printf("checking lib:%s\n", soname); 70 | for (idx = 0; idx < whitelistsize; idx++) { 71 | if (strncmp(soname , whitelist[idx].soname, whitelist[idx].len) == 0) { 72 | //simple_printf("Elf whitelisted ===== :%s\n", soname); 73 | return 1; 74 | } 75 | } 76 | return 0; 77 | } 78 | 79 | void implement_xom(void *base, size_t len, int prot, int flags, int fd, off_t off) 80 | { 81 | void *sectable = NULL; 82 | void *pgsectable = NULL; 83 | void *execstart = NULL; 84 | void *execend = NULL; 85 | void *pgupexecstart = NULL; 86 | void *pgdownexecstart = NULL; 87 | void *pgupexecend = NULL; 88 | void *pgdownexecend = NULL; 89 | void *pgupcodesegend = NULL; 90 | int execsize = 0; 91 | int sectablesize = 0; 92 | int pgsectablesize = 0; 93 | int pgelfmetasize = 0; 94 | int pgrodatasize = 0; 95 | int idx = 0; 96 | if (strncmp(base, ELFMAG, 4) == 0) { 97 | ;//simple_printf("matched ELF header!\n"); 98 | } else { 99 | simple_printf("did not match ELF header: %s!\n", base); 100 | return; 101 | } 102 | /* Traverse PHDR table and figure out code segment range. */ 103 | Elf64_Ehdr *elfheader = (Elf64_Ehdr *)base; 104 | Elf64_Phdr *phdr = base + elfheader->e_phoff; 105 | int phdrent = elfheader->e_phnum; 106 | for (idx = 0; idx < phdrent; idx++) { 107 | if (phdr->p_type == PT_LOAD && phdr->p_flags == (PF_X|PF_R)) { 108 | pgupcodesegend = (void *)(base + round_up_pgsize(phdr->p_memsz)); 109 | break; 110 | } 111 | phdr++; 112 | } 113 | if (pgupcodesegend == NULL) { 114 | simple_printf("code segment does not exist, return"); 115 | return; 116 | } 117 | /* Figure out section table location and mmap it into memmory. */ 118 | if (check_elf_whitelist(base, elfheader) == 1) { 119 | /* Whitelisted library make skip xom. */ 120 | return; 121 | } 122 | sectablesize = elfheader->e_shnum * elfheader->e_shentsize; 123 | pgsectablesize = round_up_pgsize(elfheader->e_shoff + sectablesize) - 124 | round_down_pgsize(elfheader->e_shoff); 125 | pgsectable = ldso_mmap(NULL, pgsectablesize, PROT_READ, 126 | MAP_PRIVATE|MAP_DENYWRITE, fd, 127 | round_down_pgsize(elfheader->e_shoff)); 128 | sectable = pgsectable + (elfheader->e_shoff - 129 | round_down_pgsize(elfheader->e_shoff)); 130 | Elf64_Shdr *sectionheader = (Elf64_Shdr *)sectable; 131 | 132 | /* Traverse section table and figure out true executable region. */ 133 | for (idx = 0; idx < elfheader->e_shnum; idx++) { 134 | if (sectionheader->sh_flags & SHF_EXECINSTR) { 135 | if (execstart == NULL) 136 | execstart = (void *)sectionheader->sh_addr; 137 | //simple_printf("executable section idx: %x\n", idx); 138 | } else if (!(sectionheader->sh_flags & SHF_EXECINSTR) && 139 | execstart != NULL && execend == NULL) { 140 | execend = (void *)sectionheader->sh_addr; 141 | break; 142 | } 143 | sectionheader++; 144 | } 145 | pgdownexecstart = (void *)((size_t)base + (size_t)round_down_pgsize((size_t)execstart)); 146 | pgupexecstart = (void *)((size_t)base + (size_t)round_up_pgsize((size_t)execstart)); 147 | pgdownexecend = (void *)((size_t)base + (size_t)round_down_pgsize((size_t)execend)); 148 | pgupexecend = (void *)((size_t)base + (size_t)round_up_pgsize((size_t)execend)); 149 | execsize = (size_t)pgdownexecend - (size_t)pgupexecstart; 150 | if (execsize > 0) { 151 | ldso_mprotect(pgupexecstart, execsize, PROT_EXEC); 152 | } 153 | /* TODO: Mark elf metadata as read-only. */ 154 | pgelfmetasize = (int)((size_t)pgdownexecstart - (size_t)base); 155 | if (pgelfmetasize > 0) 156 | ldso_mprotect((void *)base, pgelfmetasize, PROT_READ); 157 | /* TODO: Mark read-only section in code segment as read-only. */ 158 | pgrodatasize = (int)((size_t)pgupcodesegend - (size_t)pgupexecend); 159 | if(pgrodatasize > 0) 160 | ldso_mprotect((void *)pgupexecend, pgrodatasize, PROT_READ); 161 | 162 | __syscall(__NR_munmap, (void *)pgsectable, pgsectablesize); 163 | return; 164 | } 165 | -------------------------------------------------------------------------------- /src/patch/xomenable/ld.script: -------------------------------------------------------------------------------- 1 | /* Script for -z combreloc: combine and sort reloc sections */ 2 | /* Copyright (C) 2014-2017 Free Software Foundation, Inc. 3 | Copying and distribution of this script, with or without modification, 4 | are permitted in any medium without royalty provided the copyright 5 | notice and this notice are preserved. */ 6 | OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", 7 | "elf64-x86-64") 8 | OUTPUT_ARCH(i386:x86-64) 9 | ENTRY(_start) 10 | SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib"); 11 | SECTIONS 12 | { 13 | /* Read-only sections, merged into text segment: */ 14 | PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS; 15 | .interp : { *(.interp) } 16 | .note.gnu.build-id : { *(.note.gnu.build-id) } 17 | .hash : { *(.hash) } 18 | .gnu.hash : { *(.gnu.hash) } 19 | .dynsym : { *(.dynsym) } 20 | .dynstr : { *(.dynstr) } 21 | .gnu.version : { *(.gnu.version) } 22 | .gnu.version_d : { *(.gnu.version_d) } 23 | .gnu.version_r : { *(.gnu.version_r) } 24 | .rela.dyn : 25 | { 26 | *(.rela.init) 27 | *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) 28 | *(.rela.fini) 29 | *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) 30 | *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) 31 | *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) 32 | *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) 33 | *(.rela.ctors) 34 | *(.rela.dtors) 35 | *(.rela.got) 36 | *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) 37 | *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*) 38 | *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*) 39 | *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*) 40 | *(.rela.ifunc) 41 | } 42 | .rela.plt : 43 | { 44 | *(.rela.plt) 45 | PROVIDE_HIDDEN (__rela_iplt_start = .); 46 | *(.rela.iplt) 47 | PROVIDE_HIDDEN (__rela_iplt_end = .); 48 | } 49 | .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } 50 | .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } 51 | .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table 52 | .gcc_except_table.*) } 53 | .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } 54 | /* These sections are generated by the Sun/Oracle C++ compiler. */ 55 | .exception_ranges : ONLY_IF_RO { *(.exception_ranges 56 | .exception_ranges*) } 57 | .init : 58 | { 59 | KEEP (*(SORT_NONE(.init))) 60 | } 61 | .plt : { *(.plt) *(.iplt) } 62 | .plt.got : { *(.plt.got) } 63 | .plt.bnd : { *(.plt.bnd) } 64 | .fini : 65 | { 66 | KEEP (*(SORT_NONE(.fini))) 67 | } 68 | .text : 69 | { 70 | *(.text.unlikely .text.*_unlikely .text.unlikely.*) 71 | *(.text.exit .text.exit.*) 72 | *(.text.startup .text.startup.*) 73 | *(.text.hot .text.hot.*) 74 | *(.text .stub .text.* .gnu.linkonce.t.*) 75 | /* .gnu.warning sections are handled specially by elf32.em. */ 76 | *(.gnu.warning) 77 | } 78 | PROVIDE (__etext = .); 79 | PROVIDE (_etext = .); 80 | PROVIDE (etext = .); 81 | . = ALIGN(4K); 82 | .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } 83 | .rodata1 : { *(.rodata1) } 84 | /* Adjust the address for the data segment. We want to adjust up to 85 | the same address within the page on the next page up. */ 86 | . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); 87 | .data : 88 | { 89 | *(.data .data.* .gnu.linkonce.d.*) 90 | SORT(CONSTRUCTORS) 91 | } 92 | .data1 : { *(.data1) } 93 | 94 | /* Exception handling */ 95 | .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } 96 | .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } 97 | .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } 98 | .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } 99 | /* Thread Local Storage sections */ 100 | .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } 101 | .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } 102 | .preinit_array : 103 | { 104 | PROVIDE_HIDDEN (__preinit_array_start = .); 105 | KEEP (*(.preinit_array)) 106 | PROVIDE_HIDDEN (__preinit_array_end = .); 107 | } 108 | .init_array : 109 | { 110 | PROVIDE_HIDDEN (__init_array_start = .); 111 | KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) 112 | KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) 113 | PROVIDE_HIDDEN (__init_array_end = .); 114 | } 115 | .fini_array : 116 | { 117 | PROVIDE_HIDDEN (__fini_array_start = .); 118 | KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) 119 | KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) 120 | PROVIDE_HIDDEN (__fini_array_end = .); 121 | } 122 | .ctors : 123 | { 124 | /* gcc uses crtbegin.o to find the start of 125 | the constructors, so we make sure it is 126 | first. Because this is a wildcard, it 127 | doesn't matter if the user does not 128 | actually link against crtbegin.o; the 129 | linker won't look for a file to match a 130 | wildcard. The wildcard also means that it 131 | doesn't matter which directory crtbegin.o 132 | is in. */ 133 | KEEP (*crtbegin.o(.ctors)) 134 | KEEP (*crtbegin?.o(.ctors)) 135 | /* We don't want to include the .ctor section from 136 | the crtend.o file until after the sorted ctors. 137 | The .ctor section from the crtend file contains the 138 | end of ctors marker and it must be last */ 139 | KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) 140 | KEEP (*(SORT(.ctors.*))) 141 | KEEP (*(.ctors)) 142 | } 143 | .dtors : 144 | { 145 | KEEP (*crtbegin.o(.dtors)) 146 | KEEP (*crtbegin?.o(.dtors)) 147 | KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) 148 | KEEP (*(SORT(.dtors.*))) 149 | KEEP (*(.dtors)) 150 | } 151 | .jcr : { KEEP (*(.jcr)) } 152 | .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } 153 | .dynamic : { *(.dynamic) } 154 | .got : { *(.got) *(.igot) } 155 | . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .); 156 | .got.plt : { *(.got.plt) *(.igot.plt) } 157 | _edata = .; PROVIDE (edata = .); 158 | . = .; 159 | __bss_start = .; 160 | .bss : 161 | { 162 | *(.dynbss) 163 | *(.bss .bss.* .gnu.linkonce.b.*) 164 | *(COMMON) 165 | /* Align here to ensure that the .bss section occupies space up to 166 | _end. Align after .bss to ensure correct alignment even if the 167 | .bss section disappears because there are no input sections. 168 | FIXME: Why do we need it? When there is no .bss section, we don't 169 | pad the .data section. */ 170 | . = ALIGN(. != 0 ? 64 / 8 : 1); 171 | } 172 | .lbss : 173 | { 174 | *(.dynlbss) 175 | *(.lbss .lbss.* .gnu.linkonce.lb.*) 176 | *(LARGE_COMMON) 177 | } 178 | . = ALIGN(64 / 8); 179 | . = SEGMENT_START("ldata-segment", .); 180 | .lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : 181 | { 182 | *(.lrodata .lrodata.* .gnu.linkonce.lr.*) 183 | } 184 | .ldata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : 185 | { 186 | *(.ldata .ldata.* .gnu.linkonce.l.*) 187 | . = ALIGN(. != 0 ? 64 / 8 : 1); 188 | } 189 | . = ALIGN(64 / 8); 190 | _end = .; PROVIDE (end = .); 191 | . = DATA_SEGMENT_END (.); 192 | /* Stabs debugging sections. */ 193 | .stab 0 : { *(.stab) } 194 | .stabstr 0 : { *(.stabstr) } 195 | .stab.excl 0 : { *(.stab.excl) } 196 | .stab.exclstr 0 : { *(.stab.exclstr) } 197 | .stab.index 0 : { *(.stab.index) } 198 | .stab.indexstr 0 : { *(.stab.indexstr) } 199 | .comment 0 : { *(.comment) } 200 | /* DWARF debug sections. 201 | Symbols in the DWARF debugging sections are relative to the beginning 202 | of the section so we begin them at 0. */ 203 | /* DWARF 1 */ 204 | .debug 0 : { *(.debug) } 205 | .line 0 : { *(.line) } 206 | /* GNU DWARF 1 extensions */ 207 | .debug_srcinfo 0 : { *(.debug_srcinfo) } 208 | .debug_sfnames 0 : { *(.debug_sfnames) } 209 | /* DWARF 1.1 and DWARF 2 */ 210 | .debug_aranges 0 : { *(.debug_aranges) } 211 | .debug_pubnames 0 : { *(.debug_pubnames) } 212 | /* DWARF 2 */ 213 | .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } 214 | .debug_abbrev 0 : { *(.debug_abbrev) } 215 | .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end ) } 216 | .debug_frame 0 : { *(.debug_frame) } 217 | .debug_str 0 : { *(.debug_str) } 218 | .debug_loc 0 : { *(.debug_loc) } 219 | .debug_macinfo 0 : { *(.debug_macinfo) } 220 | /* SGI/MIPS DWARF 2 extensions */ 221 | .debug_weaknames 0 : { *(.debug_weaknames) } 222 | .debug_funcnames 0 : { *(.debug_funcnames) } 223 | .debug_typenames 0 : { *(.debug_typenames) } 224 | .debug_varnames 0 : { *(.debug_varnames) } 225 | /* DWARF 3 */ 226 | .debug_pubtypes 0 : { *(.debug_pubtypes) } 227 | .debug_ranges 0 : { *(.debug_ranges) } 228 | /* DWARF Extension. */ 229 | .debug_macro 0 : { *(.debug_macro) } 230 | .debug_addr 0 : { *(.debug_addr) } 231 | .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } 232 | /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } 233 | } 234 | -------------------------------------------------------------------------------- /src/patch/xomenable/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "utils.h" 10 | extern void *ldso_mmap(void *addr, size_t len, int prot, int flags, int filedes, off_t off); 11 | extern void ldso_dl_debug_vdprintf(int fd, int tag, const char *fmt, va_list arg); 12 | extern void implement_xom(void *base, size_t len, int prot, int flags, int fd, off_t off); 13 | extern int __syscall(int num, ...); 14 | void simple_printf(char *fmt, ...) 15 | { 16 | va_list ap; 17 | va_start(ap, fmt); 18 | ldso_dl_debug_vdprintf(1, 0, fmt, ap); 19 | va_end(ap); 20 | return; 21 | } 22 | 23 | void *wrapper_mmap(void *addr, size_t len, int prot, int flags, int filedes, 24 | off_t off) 25 | { 26 | void *res = ldso_mmap(addr, len, prot, flags, filedes, off); 27 | if(res != (void *)NULL && !(flags &= MAP_FIXED) && 28 | (prot == (PROT_EXEC|PROT_READ)) && filedes > 0) { 29 | //simple_printf("fd %x is used and %s at address %lx\n", filedes, 30 | // info, res); 31 | implement_xom(res, len, prot, flags, filedes, off); 32 | } 33 | return res; 34 | } 35 | int wrapper_syscall_execve(const char *filename, char *const argv[], char *const envp[]) 36 | { 37 | int ret; 38 | int idx; 39 | int argc; 40 | char ** argvptr = (char **)argv; 41 | char buf[5] = {'\0'}; 42 | for(idx = 0; argvptr[idx] != NULL; idx++); 43 | argc = idx; 44 | char *newargv[idx + 2]; 45 | int fd = __syscall(__NR_open, filename, O_CLOEXEC|O_RDONLY); 46 | __syscall(__NR_read, fd, buf, 4); 47 | if(strncmp(buf, "\177ELF", 4) == 0) { 48 | filename = "/lib64/ld-xom.so"; 49 | for(idx = 0; argv[idx] != NULL; idx++) { 50 | newargv[idx+1] = argv[idx]; 51 | } 52 | newargv[0] = (char *)filename; 53 | newargv[argc+1] = NULL; 54 | argvptr = (char **)newargv; 55 | } 56 | __asm__ volatile ("syscall" 57 | :"=a" (ret) 58 | :"a"(59), // syscall number (execve) 59 | "D"(filename), // filename 60 | "S"(argvptr), // arguments 61 | "d"(envp) // env 62 | :"rcx","r11","cc"); 63 | return ret; 64 | } 65 | -------------------------------------------------------------------------------- /src/patch/xomenable/trampoline.s: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * 3 | * The code in this file contains trampoline functions of injected binary that 4 | * help redirecting control flows in target binary (binary that is 5 | * instrumented). The following are the naming conventions being used in this 6 | * file. 7 | * 8 | * Any function starting with "_wrapper" means a proxy code stub that targeting 9 | * it real wrapper function in the target binary 10 | * 11 | * Any function name like "_wrapper_FUNCTION" means that calls in target binary 12 | * (binary to be instrumented) targeting FUNCTION should be redirected to 13 | * _wrapper_FUNCTION. 14 | * 15 | * Any function name like "_wrapper_syscall_FUNCTION" means that we intercept 16 | * the syscall instruction in the original function and redirect control here. 17 | * Since syscall has only two bytes, patching such an instruction would clobber 18 | * the next function. This is a TODO for developer. In this future, the next 19 | * instruction should be automatically identified and copied into the 20 | * "_wrapper" proxy code. 21 | * 22 | * Any function name like "LIBNAME_FUNCTION" (e.g, ldso_mmap) means a code stub 23 | * that should target the mmap function in ld.so (after instrumentation). 24 | * Similarly, libc_mmap means a code stub that should target mmap function in 25 | * libc. This type of function is to help forwarding the control flow from its 26 | * corresponding wrapper function. 27 | * 28 | *****************************************************************************/ 29 | 30 | .text 31 | .globl _start 32 | .type _start, @function 33 | _start: 34 | hlt 35 | .size _start, .-_start 36 | 37 | .text 38 | .globl __syscall 39 | .type __syscall, @function 40 | __syscall: 41 | mov %rdi,%rax 42 | mov %rsi,%rdi 43 | mov %rdx,%rsi 44 | mov %rcx,%rdx 45 | mov %r8,%r10 46 | mov %r9,%r8 47 | mov 0x8(%rsp),%r9 48 | syscall 49 | cmp $0xfffffffffffff001,%rax 50 | jae __syscall_error 51 | retq 52 | __syscall_error: 53 | neg %rax 54 | orq $0xffffffffffffffff, %rax 55 | retq 56 | .size __syscall, .-__syscall 57 | 58 | .text 59 | .globl _wrapper_mmap 60 | .type _wrapper_mmap, @function 61 | _wrapper_mmap: 62 | call wrapper_mmap 63 | ret 64 | .size _wrapper_mmap, .-_wrapper_mmap 65 | 66 | .text 67 | .globl ldso_mmap 68 | .type ldso_mmap, @function 69 | ldso_mmap: 70 | .byte 0xe9,0x0,0x0,0x0,0x0 71 | .size ldso_mmap, .-ldso_mmap 72 | 73 | .text 74 | .globl ldso_mprotect 75 | .type ldso_mprotect, @function 76 | ldso_mprotect: 77 | .byte 0xe9,0x0,0x0,0x0,0x0 78 | .size ldso_mprotect, .-ldso_mprotect 79 | 80 | .text 81 | .globl ldso_dl_debug_vdprintf 82 | .type ldso_dl_debug_vdprintf, @function 83 | ldso_dl_debug_vdprintf: 84 | .byte 0xe9,0x0,0x0,0x0,0x0 85 | .size ldso_dl_debug_vdprintf, .-ldso_dl_debug_vdprintf 86 | 87 | #TODO: supporting any instruction after syscall in general. 88 | .text 89 | .globl _wrapper_syscall_execve 90 | .type _wrapper_syscall_execve, @function 91 | _wrapper_syscall_execve: 92 | pushq %rcx 93 | popq %r10 94 | call wrapper_syscall_execve 95 | cmpq $0xfffffffffffff001,%rax 96 | post_syscall_execve: 97 | .byte 0xe9,0x0,0x0,0x0,0x0 98 | .size _wrapper_syscall_execve, .-_wrapper_syscall_execve 99 | 100 | -------------------------------------------------------------------------------- /src/patch/xomenable/utils.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | int strcmp(const char *s1, const char *s2) 4 | { 5 | unsigned char uc1, uc2; 6 | while (*s1 != '\0' && *s1 == *s2) { 7 | s1++; 8 | s2++; 9 | } 10 | uc1 = (*(unsigned char *) s1); 11 | uc2 = (*(unsigned char *) s2); 12 | return ((uc1 < uc2) ? -1 : (uc1 > uc2)); 13 | } 14 | 15 | int strncmp(const char *s1, const char *s2, size_t n) 16 | { 17 | unsigned char uc1, uc2; 18 | /* Nothing to compare? Return zero. */ 19 | if (n == 0) 20 | return 0; 21 | /* Loop, comparing bytes. */ 22 | while (n-- > 0 && *s1 == *s2) { 23 | /* If we've run out of bytes or hit a null, return zero 24 | since we already know *s1 == *s2. */ 25 | if (n == 0 || *s1 == '\0') 26 | return 0; 27 | s1++; 28 | s2++; 29 | } 30 | uc1 = (*(unsigned char *) s1); 31 | uc2 = (*(unsigned char *) s2); 32 | return ((uc1 < uc2) ? -1 : (uc1 > uc2)); 33 | } 34 | 35 | int round_up_pgsize(int size) 36 | { 37 | if (size % 0x1000 == 0) 38 | return size; 39 | return size - (size % 0x1000) + 0x1000; 40 | } 41 | 42 | int round_down_pgsize(int size) 43 | { 44 | if (size % 0x1000 == 0) 45 | return size; 46 | return size - (size % 0x1000); 47 | } 48 | 49 | -------------------------------------------------------------------------------- /src/patch/xomenable/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTILS_H_ 2 | #define _UTILS_H_ 3 | #include 4 | 5 | int strcmp(const char *s1, const char *s2); 6 | 7 | int strncmp(const char *s1, const char *s2, size_t n); 8 | 9 | int round_up_pgsize(int size); 10 | 11 | int round_down_pgsize(int size); 12 | #endif 13 | -------------------------------------------------------------------------------- /src/rewriter/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /src/rewriter/README.md: -------------------------------------------------------------------------------- 1 | ## ELF Binary Rewriter 2 | This is a baseline binary instrumentation tool for x86 ELF binaries on Linux. It works on both 64-bit and 32-bit ELF files and it works properly on Position-Independent Executable (PIE), non-PIE and shared libaries. 3 | 4 | ## Background 5 | Binary instrumentation often requires inserting new code or data into an existing binary. However, ELF files compiled by modern compilers (gcc and llvm) do not have sufficient space to put such extra information. This is because that compilers would generate binaries in a compact form for smaller memory footprint. 6 | 7 | This tool helps solving the space issue for binary instrumentation/rewriting. It works by extending an existing ELF file with extra loadable (PT_LOAD) segments, following the specification of program loaders. The binaries generated by this tool should work well with all functionality preserved. 8 | 9 | ## Getting Started 10 | To get started, check out files in examples directory. 11 | 12 | ### Writing your code in ASM and inject your code. 13 | To write your code in assembly, you need to put your code in a ".s" file and use assembler to generated an object file. For instance, you may write the following assembly code in a file called test.s: 14 | ``` 15 | .global _start 16 | _start: 17 | xorq %rax,%rax 18 | ``` 19 | After finishing code development, you may assemble it into an object file: 20 | ``` 21 | gcc -c test.s 22 | ``` 23 | Now inject your code into an existing file and execute the new file 24 | ``` 25 | ./example/inject_only_code_segment.py -f /bin/ls -i test.o -o myls 26 | ./myls 27 | ``` 28 | Note: 29 | - "/bin/ls" is the file you want to instrument; 30 | - only the ".text" section of test.o will be injected into /bin/ls 31 | 32 | ### Writing your code in C (limited support) and instrument original code. 33 | Finish the steps in [tutorial](../patch/tutorial/README.md) to see how to write your instrumentation code in C. 34 | 35 | Instrument the binary with your instrumentation code. 36 | ``` 37 | src/analysis/patch-binary.sh /bin/ls your_instrument_code ./newls 38 | ./newls 39 | ``` 40 | Note that instrumentation code written in C has the following limited support: 41 | - only local variable and constant variable definition is supported. 42 | - only system call wrapper and PLT functions are supported for hooking. 43 | - only support hooking direct jumps and direct calls. 44 | -------------------------------------------------------------------------------- /src/rewriter/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xom-switch/22cf7a855a180b450be506b8e1745b1ba770dd93/src/rewriter/__init__.py -------------------------------------------------------------------------------- /src/rewriter/base_instrumentor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import os 3 | import sys 4 | import tempfile 5 | from elf_basic import * 6 | from section_basic import * 7 | 8 | class base_instrumentor(elf_basic): 9 | def __init__(self, file, option=None): 10 | super(base_instrumentor, self).__init__(file) 11 | self.task = option 12 | self.gen_tempfile(self.get_current_file()) 13 | self.new_segment_base_addr = None 14 | self.new_segment_offset = None 15 | self.new_segment_offset_in_phdr = None 16 | self.new_segment_fsize = None 17 | self.new_segment_msize = None 18 | self.new_segment_flags = 0 19 | self.new_segment_align = None 20 | self.new_sections = [] 21 | self.new_interp_offset = None 22 | self.new_interp_vaddr = None 23 | self.new_interp_paddr = None 24 | self.prefer_inject_new_segment = 1 25 | def get_name(self): 26 | return self.instrumentor_name 27 | 28 | def get_new_segment_info(self): 29 | new_segment = dict() 30 | new_segment['current_file' ] = self.get_current_file() 31 | new_segment['new_segment_base_addr' ] = self.new_segment_base_addr 32 | new_segment['new_segment_offset' ] = self.new_segment_offset 33 | new_segment['new_segment_offset_in_phdr'] = self.new_segment_offset_in_phdr 34 | new_segment['new_segment_fsize' ] = self.new_segment_fsize 35 | new_segment['new_segment_msize' ] = self.new_segment_msize 36 | new_segment['new_segment_flags' ] = self.new_segment_flags 37 | new_segment['new_segment_align' ] = self.new_segment_align 38 | new_segment['new_sections' ] = self.new_sections 39 | return new_segment 40 | 41 | def set_new_segment_info(self, new_segment): 42 | if(new_segment == None): 43 | return 44 | self.set_current_file(new_segment['current_file']) 45 | self.new_segment_base_addr = new_segment['new_segment_base_addr' ] 46 | self.new_segment_offset = new_segment['new_segment_offset' ] 47 | self.new_segment_offset_in_phdr = new_segment['new_segment_offset_in_phdr'] 48 | self.new_segment_fsize = new_segment['new_segment_fsize' ] 49 | self.new_segment_msize = new_segment['new_segment_msize' ] 50 | self.new_segment_flags = new_segment['new_segment_flags' ] 51 | self.new_segment_align = new_segment['new_segment_align' ] 52 | self.new_sections = new_segment['new_sections' ] 53 | 54 | def get_new_segment_perm(self): 55 | return self.new_segment_flags 56 | 57 | def get_new_segment_phdr_offset(self): 58 | return self.new_segment_offset_in_phdr 59 | 60 | def get_new_segment_base(self): 61 | return self.new_segment_base_addr 62 | 63 | def get_new_segment_base_addr(self, binname): 64 | if(self.new_segment_base_addr != None): 65 | return self.new_segment_base_addr 66 | loadsegments = self.get_load_segments_info(binname) 67 | end_addr = loadsegments[-1]['vaddr'] + loadsegments[-1]['memsize'] 68 | print "end addr 0x%08x" % end_addr 69 | end_addr = (end_addr & 0xfffff000) + 0x1000 70 | self.new_segment_base_addr = end_addr 71 | print "end addr 0x%08x" % end_addr 72 | return end_addr 73 | 74 | def get_new_segment_offset(self): 75 | return self.new_segment_offset 76 | 77 | def interp_segment_exist(self, binname): 78 | if(None != self.get_segment_info(binname, "INTERP", 0, 'offset')): 79 | return True 80 | else: 81 | return False 82 | 83 | def compute_gap_to_extend_phdr(self, binname): 84 | phdrnum = self.get_elfhdr_info(binname, "Number of program headers:"); 85 | phdrsize = self.get_elfhdr_info(binname,"Size of program headers:"); 86 | phdrstart = self.get_elfhdr_info(binname,"Start of program headers:"); 87 | phdrend = phdrstart + phdrsize * phdrnum 88 | 89 | if(not self.interp_segment_exist(binname)): 90 | # Compute the distance between end of phdr and the first critical 91 | # segment after. 92 | firstdynsection = self.get_dynamic_sections_info(binname)[0][1] 93 | fisrtdynsection = self.convert_vma_to_offset(binname, 94 | firstdynsection) 95 | #print "first critical dynamic section: %x" % firstdynsection 96 | return firstdynsection - phdrend 97 | else: 98 | # Check if interp string is between end of phdr and the first critical 99 | # section recorded by .dynamic. If so, compute the distance between end 100 | # of interp str and the first critical section and the distance between 101 | # the end of phdr and beginning of interp. The function should return 102 | # whichever is larger. If the interpstr is not in between end of phdr 103 | # and 1st critical section, then return directly the distance of the 104 | # two. 105 | interpoffset = self.get_segment_info(binname, "INTERP", 0, 'offset') 106 | interpfsize = self.get_segment_info(binname, "INTERP", 0, 'fsize') 107 | interpend = interpoffset + interpfsize + 1 108 | firstdynsection = self.get_dynamic_sections_info(binname)[0][1] 109 | firstdynsecoffset = self.convert_vma_to_offset2(binname, 110 | firstdynsection) 111 | print "firstdynsection: %x" % firstdynsecoffset 112 | print "interpend: %x" % interpend 113 | print "interpoffset: %x" % interpoffset 114 | print "phdrend: %x" % phdrend 115 | if(interpoffset >= phdrend and interpend <= firstdynsecoffset): 116 | dist_1stdyn2interp = firstdynsecoffset - interpend 117 | dist_phdrend2interp = interpoffset - phdrend 118 | print "interp str is between phdrend and 1st dyn section" 119 | return max(dist_1stdyn2interp, dist_phdrend2interp) 120 | else: 121 | return firstdynsection - phdrend 122 | 123 | def relocate_interpstr(self, binname): 124 | #check if INTERP segment exists or not 125 | if(not self.interp_segment_exist(binname)): 126 | return 127 | interpoffset= self.get_segment_info(binname, "INTERP", 0, 'offset') 128 | interpvaddr = self.get_segment_info(binname, "INTERP", 0, 'vaddr') 129 | interppaddr = self.get_segment_info(binname, "INTERP", 0, 'paddr') 130 | interpfsize = self.get_segment_info(binname, "INTERP", 0, 'fsize') 131 | print "vaddr %x paddr %x " % (interpvaddr, interppaddr) 132 | #looking for the nearest critical information after interp string 133 | safeend = self.convert_vma_to_offset2(binname, self.get_dynamic_sections_info(binname)[0][1]) 134 | #figure out safe interp offset and len 135 | safestart = safeend - interpfsize - 1 136 | if(safestart <= interpoffset): 137 | print "No need to move interp string" 138 | return 139 | interpstr = self.read_data(binname, interpoffset, interpfsize) 140 | self.write_data(binname, safestart, interpstr) 141 | self.write_data(binname, safestart + interpfsize, '\0') 142 | self.write_data(binname, safestart, interpstr) 143 | #update phdr for interp 144 | diff = safestart - interpoffset 145 | newvaddr = interpvaddr + diff 146 | self.modify_phdrtab_info(binname, phdr.PT_INTERP, phdr.p_offset, safestart) 147 | self.modify_phdrtab_info(binname, phdr.PT_INTERP, phdr.p_vaddr, newvaddr) 148 | self.modify_phdrtab_info(binname, phdr.PT_INTERP, phdr.p_paddr, newvaddr) 149 | self.modify_sectiontab_info(binname, ".interp", sectiontab.s_vaddr, newvaddr) 150 | self.modify_sectiontab_info(binname, ".interp", sectiontab.s_offset, safestart) 151 | self._handle_note_sections(binname) 152 | print interpstr 153 | print self.get_current_file() 154 | self.new_interp_offset = safestart 155 | self.new_interp_vaddr = interpvaddr + diff 156 | self.new_interp_paddr = interpvaddr + diff 157 | return 158 | 159 | def _handle_note_sections(self, binname): 160 | cmd = "readelf -W -S %s|grep -o '.note\.[^ ]*'"%binname 161 | lnotes = self.get_result_lines(cmd) 162 | for sec in lnotes: 163 | self.modify_sectiontab_info(binname, sec, sectiontab.s_size, 0) 164 | 165 | def _write_to_phdr(self, binname, segment_offset, stype=phdr.PT_LOAD, offset=0, 166 | vaddr=0, paddr=0, fsize=0, msize=0, flags=0x5, 167 | align=0x1000): 168 | if(stype == phdr.PT_LOAD): 169 | align=0x1000 170 | self.write_int(binname, segment_offset + phdr.p_type , stype) 171 | self.write_int(binname, segment_offset + phdr.p_flags , flags) 172 | self.write_ptr(binname, segment_offset + phdr.p_offset, offset) 173 | self.write_ptr(binname, segment_offset + phdr.p_vaddr , vaddr) 174 | self.write_ptr(binname, segment_offset + phdr.p_paddr , paddr) 175 | self.write_ptr(binname, segment_offset + phdr.p_filesz, fsize) 176 | self.write_ptr(binname, segment_offset + phdr.p_memsz , msize) 177 | self.write_ptr(binname, segment_offset + phdr.p_align , align) 178 | 179 | def insert_new_phdr_entry(self, binname, stype=phdr.PT_LOAD, offset=0, vaddr=0, paddr=0, 180 | fsize=0, msize=0, flags=0x5, align=0x1000): 181 | gap = self.compute_gap_to_extend_phdr(binname) 182 | phdr_size = self.get_elfhdr_info(binname,"Size of program headers:"); 183 | if(gap < phdr_size): 184 | print "gap is too small to insert new segment" 185 | return None 186 | self.relocate_interpstr(binname) 187 | phdr_start = self.get_elfhdr_info(binname,"Start of program headers:"); 188 | phdr_num = self.get_elfhdr_info(binname, "Number of program headers:"); 189 | phdr_end = phdr_start + phdr_size * phdr_num 190 | phdr_num += 1 191 | self.write_short(binname, elfhdr.e_phdr_ent_cnt, phdr_num) 192 | self._write_to_phdr(binname, phdr_end, stype, offset, vaddr, paddr, 193 | fsize, msize, flags, align) 194 | print binname 195 | return phdr_end 196 | 197 | def override_existing_phdr_entry(self, binname, segtype, idx=0, stype=phdr.PT_LOAD, 198 | offset=0, vaddr=0, paddr=0, fsize=0, msize=0, 199 | flags=0x5, align=0x1000): 200 | segment_offset = self.get_segment_offset_in_phdr(binname, segtype, idx) 201 | if(segment_offset == None): 202 | print "cannot find segment to override" 203 | return None 204 | self._write_to_phdr(binname, segment_offset, stype, offset, vaddr, paddr, 205 | fsize, msize, flags, align) 206 | print self.get_current_file() 207 | return segment_offset 208 | 209 | def add_new_segment_in_phdr(self, binname, flags=0x5, stype=phdr.PT_LOAD, 210 | align=0x1000, offset=0, vaddr=0, paddr=0, 211 | fsize=0, msize=0): 212 | if(self.prefer_inject_new_segment == 0): 213 | offset_phdr = self.override_existing_phdr_entry(binname, 214 | phdr.PT_NOTE, 0, 215 | stype, offset, 216 | vaddr, paddr,fsize, 217 | msize, flags, align) 218 | if(offset_phdr != None): 219 | self.new_segment_offset_in_phdr = offset_phdr 220 | return offset_phdr 221 | print "failed to find a method to insert a new segment" 222 | raise 223 | 224 | offset_phdr = self.insert_new_phdr_entry(binname, stype, offset, vaddr, paddr, 225 | fsize, msize, flags, align) 226 | if(offset_phdr != None): 227 | self.new_segment_offset_in_phdr = offset_phdr 228 | return offset_phdr 229 | print "cannot insert new segment, trying to override existing one" 230 | offset_phdr = self.override_existing_phdr_entry(binname, phdr.PT_NOTE, 0, stype, 231 | offset, vaddr, paddr, 232 | fsize, msize, flags, align) 233 | if(offset_phdr != None): 234 | self.new_segment_offset_in_phdr = offset_phdr 235 | return offset_phdr 236 | print "failed to find a method to insert a new segment" 237 | raise 238 | 239 | def _is_new_segment_the_last(self, binname): 240 | phdr_start = self.get_elfhdr_info(binname, "Start of program headers:"); 241 | phdr_num = self.get_elfhdr_info(binname, "Number of program headers:"); 242 | phdr_size = self.get_elfhdr_info(binname, "Size of program headers:"); 243 | last_entry = phdr_start + phdr_size * (phdr_num - 1) 244 | if(last_entry == self.get_new_segment_phdr_offset()): 245 | return True 246 | else: 247 | return False 248 | 249 | def insert_new_section(self, binname, secname, filename, align, perm): 250 | sectiontable = section_table(binname) 251 | sectiontable.insert_new_section(secname, filename, binname, perm, align) 252 | sectiontable.write2disk() 253 | self.new_sections.append(secname) 254 | 255 | # FIXME: we assume ".shstrtab" is the last (geolocation) effective section in ELF file. 256 | def relocate_section(self, binname, secname, align=None): 257 | if(secname == ".shstrtab"): 258 | print "no need to relocate section string table setion" 259 | return 260 | if(align == None): 261 | align = self.get_section_info(binname, secname, sectiontab.s_align) 262 | shstr_offset = self.get_section_info(binname, ".shstrtab", "offset") 263 | shstr_size = self.get_section_info(binname, ".shstrtab", "size") 264 | shstr_align = self.get_section_info(binname, ".shstrtab", sectiontab.s_align) 265 | sec_start = self.get_section_info(binname, secname, "offset") 266 | sec_size = self.get_section_info(binname, secname, "size") 267 | newsec_start = self.compute_align(shstr_offset, align) 268 | newshstr_offset = self.compute_align(newsec_start + sec_size, shstr_align) 269 | sectiontab_entry = self.get_elfhdr_info(binname, elfhdr.e_sechdr_offset) 270 | sectiontab_ent_sz = self.get_elfhdr_info(binname, elfhdr.e_sechdr_ent_size) 271 | newsectiontab_offset = self.compute_align(newshstr_offset + shstr_size + 0x1000, sectiontab_ent_sz) 272 | # relocate sectiontab first. 273 | self.relocate_sectiontab(binname, newsectiontab_offset, elf_basic.ABSOLUTE) 274 | # relocate shstrtab then. 275 | self.modify_sectiontab_info(binname, ".shstrtab", sectiontab.s_offset, newshstr_offset) 276 | self.relocate_data(binname, shstr_offset, shstr_size, newshstr_offset) 277 | # inject section into new location and update section table 278 | self.relocate_data(binname, sec_start, sec_size, newsec_start) 279 | self.modify_sectiontab_info(binname, secname, sectiontab.s_offset, newsec_start) 280 | 281 | def validate_sectionlist(self, binname, sectionlist): 282 | if(len(sectionlist) == 0): 283 | return False 284 | for sec in sectionlist: 285 | if(self.get_section_info(binname, sec, 'offset') == None): 286 | return False 287 | return True 288 | 289 | def map_sections_to_new_segment(self, binname, align, perm): 290 | self.map_sections_to_segment(binname, self.new_sections, 291 | self.get_new_segment_phdr_offset(), align, perm) 292 | 293 | def map_sections_to_segment(self, binname, sectionlist, segment_offset, align, perm): 294 | if(self.validate_sectionlist(binname, sectionlist) == False): 295 | print "please sanitize the sectionlist and "\ 296 | "ensure that all sections are valid" 297 | return 298 | offset = self.get_section_info(binname, sectionlist[0], 'offset') 299 | fsize = \ 300 | self.get_section_info(binname, sectionlist[-1], 'offset') + \ 301 | self.get_section_info(binname, sectionlist[-1], 'size') - \ 302 | offset 303 | 304 | memsize = fsize 305 | vaddr = self.get_new_segment_base_addr(binname) 306 | paddr = vaddr 307 | align = align 308 | flags = perm 309 | self.new_segment_offset = offset 310 | self.new_segment_size = fsize 311 | self.new_segment_base_addr = vaddr 312 | self.new_segment_fsize = fsize 313 | self.new_segment_msize = memsize 314 | self.new_segment_align = align 315 | self.new_segment_flags = flags 316 | self._write_to_phdr(binname, segment_offset, phdr.PT_LOAD, offset, 317 | vaddr, paddr, fsize, memsize, flags, align) 318 | 319 | # Check if the new segment is the last'PT_LOAD' segment in phdr. 320 | # If yes: remove the segment by tweaking the segment number in elfhdr. 321 | # If no : do nothing. 322 | # Reason: objcopy does not support ELF with additional segments appended 323 | # in phdr. 324 | def _remove_new_segment(self, binname): 325 | phdr_start = self.get_elfhdr_info(binname, "Start of program headers:"); 326 | phdr_num = self.get_elfhdr_info(binname, "Number of program headers:"); 327 | phdr_size = self.get_elfhdr_info(binname, "Size of program headers:"); 328 | last_entry = phdr_start + phdr_size * (phdr_num - 1) 329 | if(last_entry == self.get_new_segment_phdr_offset()): 330 | print "removing new segment in phdr" 331 | self.write_short(binname, elfhdr.e_phdr_ent_cnt, phdr_num - 1) 332 | 333 | def _add_new_segment_back(self, binname): 334 | phdr_start = self.get_elfhdr_info(binname, "Start of program headers:"); 335 | phdr_num = self.get_elfhdr_info(binname, "Number of program headers:"); 336 | phdr_size = self.get_elfhdr_info(binname, "Size of program headers:"); 337 | phdr_end = phdr_start + phdr_size * phdr_num 338 | if(phdr_end == self.get_new_segment_phdr_offset()): 339 | print "adding new segment back in phdr" 340 | self.write_short(binname, elfhdr.e_phdr_ent_cnt, phdr_num + 1) 341 | # self._write_to_phdr(binname, 342 | # self.get_new_segment_offset_in_phdr(), 343 | # phdr.PT_LOAD, self.get_new_segment_offset(), 344 | # self.get_new_segment_base(), 345 | # self.get_new_segment_base(), 346 | # self.new_segment_fsize, 347 | # self.new_segment_msize, 348 | # self.new_segment_flags, 349 | # self.new_segment_align) 350 | 351 | def add_instrumentation_data(self, binname, filename, secname, align, perm=None): 352 | if(secname in self.new_sections): 353 | return None 354 | if(perm == None): 355 | perm = sectiontab.SHF_ALLOC | sectiontab.SHF_EXECINSTR 356 | 357 | self.insert_new_section(binname, secname, filename, align, perm) 358 | segperm = self.trans_sec2seg_perm(perm) 359 | if(self.get_new_segment_phdr_offset() == None or 360 | self.get_new_segment_base() == None): 361 | if(self.add_new_segment_in_phdr(binname, segperm) == None): 362 | return None 363 | self.map_sections_to_new_segment(binname, align, 364 | segperm | self.get_new_segment_perm()) 365 | print "segperm: %x" % segperm 366 | print "perm: %x" % perm 367 | return secname 368 | 369 | def create_file(): 370 | hexstr="\x90\x90" 371 | file = "/tmp/abc" 372 | f = open(file, 'w') 373 | f.write(hexstr) 374 | f.close() 375 | return file 376 | 377 | def main(): 378 | parser = argparse.ArgumentParser() 379 | parser.add_argument("-f", "--file", type=str, help="file to instrument", 380 | required=True) 381 | parser.add_argument("-t", "--instrument", type=str, help="instrument type", 382 | required=False, default="remap_cptr") 383 | args = parser.parse_args() 384 | instrument = base_instrumentor(args.file, args.instrument) 385 | filename = create_file() 386 | instrument.add_instrumentation_data(instrument.get_current_file(), filename, ".mytext1", 0x1000, sectiontab.SHF_EXECINSTR) 387 | #instrument.add_instrumentation_data(instrument.get_current_file(), filename, ".mytext1", 0x1000) 388 | instrument.add_instrumentation_data(instrument.get_current_file(), filename, ".mytext2", 0x1000) 389 | instrument.add_instrumentation_data(instrument.get_current_file(), filename, ".mytext3", 0x1000) 390 | 391 | print instrument.get_current_file() 392 | if __name__ == "__main__": 393 | main() 394 | -------------------------------------------------------------------------------- /src/rewriter/elf_helper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys 4 | import re 5 | import os 6 | import tempfile 7 | import struct 8 | from struct import * 9 | 10 | def static_var(varname, value): 11 | def decorate(func): 12 | setattr(func, varname, value) 13 | return func 14 | return decorate 15 | 16 | class const(object): 17 | arch32="80386" 18 | arch64="X86-64" 19 | 20 | class elf_helper(const): 21 | def __init__(self, file): 22 | self.filename = file 23 | self.ispic = None 24 | self.ptrsize = None 25 | self.cached_cmd = dict() 26 | self.set_arch() 27 | self.set_ptr_size() 28 | 29 | def set_ptr_size(self): 30 | cmd = "readelf -h " + self.filename + "|grep Class:" 31 | ll = self.get_matched_lines(cmd); 32 | if((not ll) or (not ll[0])): 33 | self.ptrsize = None 34 | raise 35 | else: 36 | if(ll[0][-1] == "ELF64"): 37 | self.ptrsize = 8 38 | elif(ll[0][-1] == "ELF32"): 39 | self.ptrsize = 4 40 | else: 41 | raise 42 | 43 | def set_arch(self): 44 | cmd = "readelf -h " + self.filename + "|grep Machine" 45 | ll = self.get_matched_lines(cmd); 46 | if((not ll) or (not ll[0])): 47 | self.archname = "unknown" 48 | raise 49 | else: 50 | self.archname = ll[0][-1] 51 | print self.archname 52 | #32bit: 80386 53 | #64bit: X86-64 54 | 55 | def isx86_32(self): 56 | if(self.archname == const.arch32): 57 | return True 58 | else: 59 | return False 60 | 61 | def isx86_64(self): 62 | if(self.archname == const.arch64): 63 | return True 64 | else: 65 | return False 66 | 67 | def is_pic(self): 68 | if(self.ispic != None): 69 | return self.ispic 70 | cmd = "readelf -h " + self.filename + "|grep Type|awk '{print $2}'" 71 | with os.popen(cmd) as file: 72 | for line in file: 73 | line = line.rstrip() 74 | if(line == "DYN"): 75 | self.ispic = True 76 | return True 77 | else: 78 | self.ispic = False 79 | return False 80 | 81 | def char_size(self): 82 | return 1 83 | 84 | def short_size(self): 85 | return 2 86 | 87 | def int_size(self): 88 | return 4 89 | 90 | def ptr_size(self): 91 | return self.ptrsize 92 | 93 | def longlong_size(self): 94 | return 8 95 | 96 | def padding(self, v, l): 97 | if(len(v) == l): 98 | return v 99 | if(len(v) < l): 100 | pad = '\x00' *(l - len(v)) 101 | newv = v + pad 102 | return newv 103 | else: 104 | raise 105 | 106 | def bytestoint(self, i): 107 | return unpack(' self.ptr_size()): 321 | print "data type '%d' is too long for a single read" % len 322 | elif(len > self.int_size() and len <= self.ptr_size()): 323 | return self.read_ptr(filename, offset, len) 324 | elif(len > self.short_size() and len <= self.int_size()): 325 | return self.read_int(filename, offset, len) 326 | elif(len > self.char_size() and len <= self.short_size()): 327 | return self.read_short(filename, offset) 328 | elif(len == self.char_size()): 329 | return self.read_char(filename, offset) 330 | else: 331 | print "ptr_size: %d" % self.ptr_size() 332 | print "unknown data type of length: %d" % len 333 | raise 334 | 335 | def readstr(self, offset): 336 | f = os.open(self.filename, os.O_RDONLY) 337 | os.lseek(f, offset, os.SEEK_SET) 338 | res = "" 339 | c = unpack('\S+)\s+" 378 | "(?P\S+)\s+" 379 | "(?P\S+)\s+" 380 | "(?P\S+)\s+" 381 | "(?P\S+)\s+" 382 | "(?P[^0]+)\s+" 383 | "(?P\S+)\s*$") 384 | cmd = "readelf -l -W " + self.filename 385 | with os.popen(cmd) as file: 386 | for line in file: 387 | line = line.rstrip() 388 | m = regex.match(line) 389 | if(m != None): 390 | start = int(m.group('offset'), 16) 391 | fsize = int(m.group('fsize'), 16) 392 | #print "start: %x\tfsize: %x" % (start, fsize) 393 | if((start <= offset) and (offset <= (start + fsize))): 394 | vbase = int(m.group('virtual'), 16) 395 | return vbase + offset - start 396 | return None 397 | 398 | def convert_vma_to_offset(self, vma): 399 | if(isinstance(vma, basestring)): 400 | vma = int(vma, 16) 401 | num = self.get_elfhdr_info(self.filename, "Number of section headers:") 402 | pattern = re.compile(r"\s*\[\s*(?P[\d]{1,2})\]\s*" 403 | "(?P[\S]+)\s*" 404 | "(?P[\S]+)\s*" 405 | "(?P[\S]+)\s*" 406 | "(?P[\S]+)\s*" 407 | "(?P[\S]+)\s*" 408 | "[^\n]*$") 409 | 410 | cmd = "readelf -W -S " + self.filename; 411 | with os.popen(cmd) as file: 412 | for line in file: 413 | line = line.strip(); 414 | m=pattern.match(line); 415 | if(m != None): 416 | vma_start = int(m.group('addr'),16) 417 | size = int(m.group('size'),16) 418 | if(((vma_start+size) <= vma) or (vma < vma_start)): 419 | continue 420 | else: 421 | if(m.group('name') == '.bss'): 422 | return int(m.group('offset'),16) 423 | else: 424 | offset_start = int(m.group('offset'), 16) 425 | offset = offset_start + (vma - vma_start) 426 | return offset 427 | return 0 428 | 429 | def compute_align_up(self, value, align): 430 | return self.compute_align(value, align) 431 | 432 | def compute_align_down(self, value, align): 433 | if(value % align == 0): 434 | return value 435 | return self.compute_align(value, align) - 4096 436 | 437 | def compute_align(self, value, align): 438 | residue = value % align 439 | if(residue == 0): 440 | return value 441 | return value - residue + align 442 | -------------------------------------------------------------------------------- /src/rewriter/examples/inject_instrumentation.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import os,sys,inspect 3 | currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) 4 | parentdir = os.path.dirname(currentdir) 5 | sys.path.insert(0, parentdir) 6 | 7 | from instrumentor import * 8 | 9 | class code_injector(instrumentor): 10 | def __init__(self, name, filename, secname, objfile): 11 | super(code_injector, self).__init__(name, filename, secname) 12 | self.obj_file = objfile 13 | self.inject_file = self.get_tempfile() 14 | self.mapping = dict() 15 | 16 | def generate_instrumentation(self, asmfile): 17 | pass 18 | 19 | def compile_inject_instrument_file(self, asmfile, objfile, injectfile, 20 | secname, align): 21 | self.extract_data(objfile, '.text', injectfile, padalignpage=True) 22 | self.add_instrumentation_data(self.get_current_file(), injectfile, 23 | secname, align) 24 | def patch_relocation(self, binname): 25 | pass 26 | 27 | class rodata_injector(instrumentor): 28 | def __init__(self, name, filename, secname, objfile): 29 | super(rodata_injector, self).__init__(name, filename, secname) 30 | self.obj_file = objfile 31 | self.inject_file = self.get_tempfile() 32 | self.mapping = dict() 33 | 34 | def generate_instrumentation(self, asmfile): 35 | pass 36 | 37 | def compile_inject_instrument_file(self, asmfile, objfile, injectfile, 38 | secname, align): 39 | self.extract_data(objfile, '.rodata', injectfile) 40 | self.add_instrumentation_data(self.get_current_file(), injectfile, 41 | secname, align) 42 | def patch_relocation(self, binname): 43 | pass 44 | 45 | def main(): 46 | parser = argparse.ArgumentParser() 47 | parser.add_argument("-f", "--file", type=str, help="ELF file to instrument", 48 | required=True) 49 | parser.add_argument("-i", "--inject", type=str, help="ELF file to be injected", 50 | required=True) 51 | parser.add_argument("-o", "--output", type=str, help="Output ELF file location", 52 | required=False) 53 | args = parser.parse_args() 54 | scheduler = instrument_scheduler() 55 | codeinjector = code_injector('instrumented_code', args.file, 56 | '.instrumented_code', args.inject) 57 | rodatainjector = rodata_injector('instrumented_data', args.file, 58 | '.instrumented_data', args.inject) 59 | codeinjector.prefer_inject_new_segment = 0 60 | rodatainjector.prefer_inject_new_segment = 0 61 | scheduler.register_instrumentor(codeinjector) 62 | scheduler.register_instrumentor(rodatainjector) 63 | scheduler.perform_instrumentation() 64 | print scheduler.get_current_file() 65 | if(args.output == None): 66 | return 67 | bname = os.path.basename(args.file) 68 | if(os.path.isdir(args.output)): 69 | os.system("mv %s %s" % (scheduler.get_current_file(), 70 | os.path.join(args.output, bname))) 71 | else: 72 | os.system("mv %s %s" % (scheduler.get_current_file(), args.output)) 73 | 74 | if __name__ == "__main__": 75 | main() 76 | -------------------------------------------------------------------------------- /src/rewriter/examples/inject_only_code_segment.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import os,sys,inspect 3 | currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) 4 | parentdir = os.path.dirname(currentdir) 5 | sys.path.insert(0, parentdir) 6 | 7 | from instrumentor import * 8 | 9 | class code_injector(instrumentor): 10 | def __init__(self, name, filename, secname, objfile): 11 | super(code_injector, self).__init__(name, filename, secname) 12 | self.obj_file = objfile 13 | self.inject_file = self.get_tempfile() 14 | self.mapping = dict() 15 | 16 | def generate_instrumentation(self, asmfile): 17 | pass 18 | 19 | def compile_inject_instrument_file(self, asmfile, objfile, injectfile, 20 | secname, align): 21 | self.extract_data(objfile, '.text', injectfile, padalignpage=False) 22 | self.add_instrumentation_data(self.get_current_file(), injectfile, 23 | secname, align) 24 | def patch_relocation(self, binname): 25 | pass 26 | 27 | 28 | def main(): 29 | parser = argparse.ArgumentParser() 30 | parser.add_argument("-f", "--file", type=str, help="ELF file to instrument", 31 | required=True) 32 | parser.add_argument("-i", "--inject", type=str, help="ELF file to be injected", 33 | required=True) 34 | parser.add_argument("-o", "--output", type=str, help="Output ELF file location", 35 | required=False) 36 | args = parser.parse_args() 37 | scheduler = instrument_scheduler() 38 | codeinjector = code_injector('instrumented_code', args.file, 39 | '.instrumented_code', args.inject) 40 | scheduler.register_instrumentor(codeinjector) 41 | scheduler.perform_instrumentation() 42 | print scheduler.get_current_file() 43 | if(args.output == None): 44 | return 45 | bname = os.path.basename(args.file) 46 | if(os.path.isdir(args.output)): 47 | os.system("mv %s %s" % (scheduler.get_current_file(), 48 | os.path.join(args.output, bname))) 49 | else: 50 | os.system("mv %s %s" % (scheduler.get_current_file(), args.output)) 51 | 52 | if __name__ == "__main__": 53 | main() 54 | -------------------------------------------------------------------------------- /src/rewriter/examples/xom_enable.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import os,sys,inspect,platform 3 | currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) 4 | prdir = os.path.dirname(currentdir) 5 | sys.path.insert(0,prdir) 6 | 7 | from instrumentor import * 8 | 9 | def main(): 10 | parser = argparse.ArgumentParser() 11 | parser.add_argument("-f", "--file", type=str, help="file to instrument", 12 | required=True) 13 | parser.add_argument("-o", "--output", type=str, help="output location", 14 | required=False) 15 | args = parser.parse_args() 16 | organizer = instrument_organizer(args.file) 17 | (exebegin, exeend) = organizer.get_exec_memory_range(organizer.get_current_file()) 18 | print "exebegin: %d" % exebegin 19 | print "exeend: %d" % exeend 20 | segbegin = organizer.get_segment_info(organizer.get_current_file(), "LOAD", 0, 'vaddr') 21 | segsize = organizer.get_segment_info(organizer.get_current_file(), "LOAD", 0, 'memsize') 22 | segend = organizer.compute_align_up(segbegin + segsize, 4096) 23 | print "segbegin: %d" % segbegin 24 | print "segend: %d" % segend 25 | 26 | assert(segbegin < exebegin) 27 | assert(exeend < segend) 28 | extendnum = 5 29 | seglist = [] 30 | ideallist = [] 31 | ideallist.append([segbegin, exebegin, phdr.PF_R]) 32 | ideallist.append([exebegin, exeend, phdr.PF_X]) 33 | ideallist.append([exeend, segend, phdr.PF_R]) 34 | curloc = ideallist[0][0] 35 | curperm = ideallist[0][2] 36 | extendnum = 0 37 | idx = 0 38 | print "idealllist: " 39 | print ideallist 40 | while(curloc < ideallist[-1][1]): 41 | if(curloc == ideallist[idx][0]): 42 | end = organizer.compute_align_down(ideallist[idx][1], 4096) 43 | if(end > curloc): 44 | seglist.append([ideallist[idx][0], end, ideallist[idx][2]]) 45 | curloc = end 46 | curperm = ideallist[idx][2] 47 | else: 48 | seglist.append([ideallist[idx][0], ideallist[idx][0] + 4096, ideallist[idx][2]|ideallist[idx +1][2]]) 49 | curloc = ideallist[idx][0] + 4096 50 | curperm = ideallist[idx +1][2] 51 | elif(curloc < ideallist[idx][0]): 52 | end = organizer.compute_align_up(ideallist[idx][0], 4096) 53 | seglist.append([curloc, end, curperm|ideallist[idx][2]]) 54 | curloc = end 55 | elif(curloc > ideallist[idx][0] and curloc < ideallist[idx][1]): 56 | curperm = ideallist[idx][2] 57 | end = organizer.compute_align_down(ideallist[idx][1], 4096) 58 | print "==end: %d" %end 59 | print "==curloc: %d" %curloc 60 | print "==idx: %d" %idx 61 | print "==%d" % ideallist[idx][1] 62 | if(end > curloc): 63 | seglist.append([curloc, end, ideallist[idx][2]]) 64 | curloc = end 65 | curperm = ideallist[idx][2] 66 | else: 67 | print "end: %d" %end 68 | print "curloc: %d" %curloc 69 | print "idx: %d" %idx 70 | seglist.append([curloc, curloc + 4096, ideallist[idx][2]|ideallist[idx+1][2]]) 71 | curloc = curloc + 4096 72 | curperm = -1 73 | if(curloc >= ideallist[idx][1]): 74 | idx += 1 75 | print seglist 76 | print seglist 77 | if(len(seglist) <= 1): 78 | print "The binary is too small in size or everything is tightly together" 79 | print "In this situation, we cannot do anything for MPK enabling" 80 | return 81 | extendnum = len(seglist) + 1 82 | # check the size of metadata 83 | # Split the code segment into three segments 84 | organizer.extend_phdr_on_demand(extendnum) 85 | 86 | #f = organizer.get_current_file() 87 | #vr = seglist[0][0] 88 | #pr = vr 89 | #off = organizer.convert_vma_to_offset2(f, vr) 90 | #fsz = seglist[0][1] - seglist[0][0] 91 | #msz = fsz 92 | #organizer.modify_phdrtab_info(f, phdr.PT_LOAD, phdr.p_vaddr, vr, 0) 93 | #organizer.modify_phdrtab_info(f, phdr.PT_LOAD, phdr.p_paddr, pr, 0) 94 | #organizer.modify_phdrtab_info(f, phdr.PT_LOAD, phdr.p_offset, off, 0) 95 | #organizer.modify_phdrtab_info(f, phdr.PT_LOAD, phdr.p_filesz, fsz, 0) 96 | #organizer.modify_phdrtab_info(f, phdr.PT_LOAD, phdr.p_memsz, msz, 0) 97 | #organizer.modify_phdrtab_info(f, phdr.PT_LOAD, phdr.p_flags, seglist[0][2], 0) 98 | #for seg in seglist[1:]: 99 | for seg in seglist: 100 | vr = seg[0] 101 | pr = vr 102 | off = organizer.convert_vma_to_offset2(organizer.get_current_file(), vr) 103 | fsz = seg[1] - seg[0] 104 | msz = fsz 105 | organizer.insert_new_phdr_entry(organizer.get_current_file(), 106 | offset = off, 107 | vaddr = vr, 108 | paddr = pr, 109 | fsize = fsz, 110 | msize = msz, 111 | flags = seg[2] 112 | ) 113 | 114 | print seglist 115 | print organizer.get_current_file() 116 | if(args.output == None): 117 | return 118 | bname = os.path.basename(args.file) 119 | if(os.path.isdir(args.output)): 120 | os.system("mv %s %s" % (organizer.get_current_file(), 121 | os.path.join(args.output, bname))) 122 | else: 123 | os.system("mv %s %s" % (organizer.get_current_file(), args.output)) 124 | 125 | if __name__ == "__main__": 126 | main() 127 | -------------------------------------------------------------------------------- /src/rewriter/hex_match.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import re 3 | import sys 4 | import argparse 5 | import elf_basic 6 | from elf_helper import * 7 | class hex_match(object): 8 | def __init__(self, filename, pattern): 9 | self.filename = filename 10 | self.vh = elf_helper(filename) 11 | self.f = open(filename, 'rb') 12 | self.data = self.f.read() 13 | self.pattern = pattern 14 | 15 | def reorder_hex(self, hexstr): 16 | hexlen=len(hexstr) 17 | byte="([0-9a-fA-F]{2})" 18 | if(hexlen == 8): 19 | pattern = "{0}{1}{2}{3}".format(byte,byte,byte,byte) 20 | regex = re.compile(pattern) 21 | m = regex.match(hexstr) 22 | if(m != None): 23 | return "\\x{0}\\x{1}\\x{2}\\x{3}".format(m.group(4), 24 | m.group(3), 25 | m.group(2), 26 | m.group(1)) 27 | elif(hexlen == 16): 28 | pattern = "{0}{1}{2}{3}{4}{5}{6}{7}".format(byte,byte,byte,byte, 29 | byte,byte,byte,byte) 30 | regex = re.compile(pattern) 31 | m = regex.match(hexstr) 32 | if(m != None): 33 | return "\\x{0}\\x{1}\\x{2}\\x{3}\\x{4}\\x{5}\\x{6}\\x{7}".\ 34 | format(m.group(8), 35 | m.group(7), 36 | m.group(6), 37 | m.group(5), 38 | m.group(4), 39 | m.group(3), 40 | m.group(2), 41 | m.group(1)) 42 | else: 43 | print "regex does not match" 44 | return None 45 | else: 46 | return None 47 | 48 | 49 | # convert hex integer sequence to hex string pattern. e.g. 50 | # '0x00100020 0xaf080009' => '\x20\x00\x10\x00\x09\x00\x08\xaf' 51 | def convert_hexints_to_hexpattern(self, strhexints): 52 | if(self.vh.isx86_32()): 53 | phexint = "([0-9a-fA-F]{8})" 54 | phexints = "[0-9a-fA-F]{8}( [0-9a-fA-F]{8})*$" 55 | else: 56 | phexint = "([0-9a-fA-F]{16})" 57 | phexints = "[0-9a-fA-F]{16}( [0-9a-fA-F]{16})*$" 58 | 59 | rhexints = re.compile(phexints) 60 | #print "phexints: {}".format(phexints) 61 | #FIXME: avoid partial match of rhexints 62 | m = re.match(phexints, strhexints) 63 | if(m != None): 64 | True #print "input pattern matched" 65 | else: 66 | print "input pattern not matched, check your syntax" 67 | return None 68 | rhexint = re.compile(phexint) 69 | pattern="" 70 | for match in rhexint.finditer(strhexints): 71 | pattern += self.reorder_hex(match.group(1)) 72 | return pattern 73 | 74 | def search_all_hexpattern(self, pattern=None): 75 | if(pattern == None): 76 | pattern = self.pattern 77 | hexpattern = self.convert_hexints_to_hexpattern(pattern) 78 | if(hexpattern == None): 79 | return None 80 | regex = re.compile(hexpattern) 81 | listLoc = [] 82 | for match_obj in regex.finditer(self.data): 83 | offset = match_obj.start() 84 | #print "offset: {0}\t{1}".format(hex(offset), hexpattern) 85 | listLoc.append(offset) 86 | #print listLoc 87 | return listLoc 88 | 89 | 90 | def main(): 91 | parser = argparse.ArgumentParser() 92 | parser.add_argument("-f", "--file", type=str, help="file to search", 93 | required=True) 94 | parser.add_argument("-p", "--pattern", type=str, help="pattern to search", 95 | required=True) 96 | args = parser.parse_args() 97 | hpmatch = hex_match(args.file, args.pattern) 98 | hpmatch.search_all_hexpattern(args.pattern) 99 | 100 | 101 | if __name__ == "__main__": 102 | main() 103 | -------------------------------------------------------------------------------- /src/rewriter/instrumentor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from base_instrumentor import * 4 | from section_basic import * 5 | 6 | # Each instrument_scheduler is responsible for one segment for all sections 7 | # that instrumentors (or base_instrumentors, section_relocators) generate. Each 8 | # instrumentor only generate one section. 9 | class instrument_scheduler(elf_basic): 10 | def __init__(self): 11 | self.linstrumentors = [] 12 | self.dinstrumentors = dict() 13 | self.new_segment = None 14 | def register_instrumentor(self, instrumentor): 15 | self.linstrumentors.append(instrumentor) 16 | self.dinstrumentors[instrumentor.get_name()] =instrumentor 17 | 18 | def perform_instrumentation(self): 19 | for instrumentor in self.linstrumentors: 20 | instrumentor.set_new_segment_info(self.new_segment) 21 | instrumentor.perform_instrumentation() 22 | self.new_segment = instrumentor.get_new_segment_info() 23 | 24 | def set_current_file(self, binname): 25 | self.linstrumentors[0].set_current_file(binname) 26 | 27 | def get_current_file(self): 28 | return self.new_segment['current_file'] 29 | 30 | def set_binname(self, binname): 31 | self.linstrumentors[0].set_binname(binname) 32 | 33 | class instrumentor(base_instrumentor): 34 | 35 | INDIRECT_BRANCH = 0 36 | DIRECT_BRANCH = 1 37 | DIRECT_BRANCH_BOTH_EDGE = 1 38 | 39 | def __init__(self, name, binname, secname, align=0x1000): 40 | super(instrumentor, self).__init__(binname) 41 | self.instrumentor_name = name 42 | self.inject_secname = secname 43 | self.inject_secalign = align 44 | self.generate_instrument_files() 45 | 46 | def generate_instrument_files(self): 47 | self.inject_file = self.get_tempfile() 48 | self.asm_file = self.inject_file + ".S" 49 | self.obj_file = self.inject_file + ".o" 50 | 51 | def compile_inject_instrument_file(self, asmfile, objfile, injectfile, 52 | secname, align): 53 | cmd = "gcc -c %s -o %s " % (asmfile, objfile) 54 | os.system(cmd) 55 | self.extract_data(objfile, '.text', injectfile) 56 | self.add_instrumentation_data(self.get_current_file(), injectfile, secname, align) 57 | 58 | 59 | #USER should implement 60 | def generate_instrumentation(self, filename): 61 | #sample: 62 | f = open(filename, "w") 63 | f.write("nop\n") 64 | f.write("nop\n") 65 | f.close() 66 | pass 67 | 68 | def intercept_code_pointers(self, asmfile, mapping): 69 | f = open(asmfile, "w") 70 | for offset in mapping: 71 | vfstr = "_ORIGPTR_%x: jmp _ORIGLOC_%x\n_ORIGNEXT_%x:\n" % \ 72 | (offset, offset, offset) 73 | f.write(vfstr) 74 | f.write("nop\n") 75 | f.close() 76 | 77 | #USER should implement 78 | def patch_relocation(self, filename, objfile=None): 79 | pass 80 | 81 | #This is the function that trigger instrumentation 82 | def perform_instrumentation(self): 83 | self.generate_instrumentation(self.asm_file) 84 | self.compile_inject_instrument_file(self.asm_file, 85 | self.obj_file, 86 | self.inject_file, 87 | self.inject_secname, 88 | self.inject_secalign) 89 | self.patch_relocation(self.obj_file) 90 | 91 | #Utility APIs start from here 92 | #FIXME: change all segment base to section base 93 | def patch_forward_edge(self, type, mapping): 94 | symtab = self.read_obj_symtable(self.obj_file) 95 | seg_base = self.get_section_info(self.get_current_file(), 96 | self.inject_secname, 'addr') 97 | if(type == instrumentor.INDIRECT_BRANCH): 98 | regex = re.compile(r'_ORIGPTR_(\S+)\s*') 99 | for idx in symtab: 100 | sym = symtab[idx] 101 | m = regex.match(sym['name']) 102 | if(m == None): 103 | continue 104 | offset = int(m.group(1), 16) 105 | value = mapping[offset] 106 | raw_value = self.read_ptr(self.get_current_file(), offset) 107 | if(raw_value != value): 108 | continue 109 | new_cptr =seg_base + sym['offset'] 110 | print "updating cptr at offset: %x to value: %x" % \ 111 | (offset, new_cptr) 112 | self.write_ptr(self.get_current_file(), offset, new_cptr) 113 | else: 114 | pass 115 | 116 | def patch_backward_edge(self, type, mapping): 117 | symtab = self.read_obj_symtable(self.obj_file) 118 | reloctable = self.read_obj_reloctable(self.obj_file) 119 | newseg_offset = self.get_section_info(self.get_current_file(), 120 | self.inject_secname, 'offset') 121 | print "new seg offset: %x" % newseg_offset 122 | print "new seg vaddr : %x" % self.get_new_segment_base() 123 | if(type == instrumentor.INDIRECT_BRANCH): 124 | rreloc = re.compile(r'_ORIGLOC_(\S+)') 125 | rsym = re.compile(r'_ORIGNEXT_(\S+)') 126 | for idx in reloctable: 127 | reloc = reloctable[idx] 128 | m = rreloc.match(reloc['name']) 129 | if(m == None): 130 | continue 131 | offset = int(m.group(1), 16) 132 | ssym = "_ORIGNEXT_%x" % offset 133 | sym = symtab[ssym] 134 | vaddr = mapping[offset] 135 | if(sym == None): 136 | print "cannot find symbol: %s" % sym 137 | sys.exit(1) 138 | value = vaddr - (sym['offset'] 139 | + self.get_section_info(self.get_current_file(), 140 | self.inject_secname, 141 | 'addr')) 142 | print "original value %x\t sym offset %x, new seg base %x" % \ 143 | (vaddr, sym['offset'], self.get_new_segment_base()) 144 | self.write_int(self.get_current_file(), 145 | reloc['offset'] + newseg_offset, 146 | value) 147 | else: 148 | pass 149 | 150 | #relocate section to the bottom of the target ELF 151 | class section_relocator(base_instrumentor): 152 | def __init__(self, name, binname, secname, align=0x1000, secperm=None): 153 | super(section_relocator, self).__init__(binname) 154 | self.instrumentor_name = name 155 | self.secname = secname 156 | self.secalign = align 157 | if(secperm==None): 158 | secperm = self.get_section_info(binname, secname, 159 | sectiontab.s_flags) 160 | self.new_section_perm = secperm 161 | 162 | # Reimplement insert_new_section in base_instrumentor 163 | def insert_new_section(self, binname, secname, filename, align, perm): 164 | self.new_sections.append(secname) 165 | 166 | def patch_relocation(self, secname): 167 | base_addr = self.get_section_info(self.get_current_file(), 168 | self.secname, 'addr') 169 | print "base addr of section %s is 0x%x" % (secname, base_addr) 170 | if(secname == '.dynamic'): 171 | base_offset = self.get_section_info(self.get_current_file(), 172 | self.secname, 'offset') 173 | self.modify_phdrtab_info(self.get_current_file(), phdr.PT_DYNAMIC, 174 | phdr.p_offset, base_offset) 175 | self.modify_phdrtab_info(self.get_current_file(), phdr.PT_DYNAMIC, 176 | phdr.p_vaddr, base_addr) 177 | self.modify_phdrtab_info(self.get_current_file(), phdr.PT_DYNAMIC, 178 | phdr.p_paddr, base_addr) 179 | 180 | elif(secname == '.rel.dyn'): 181 | print "updating dynamic option DT_REL" 182 | self.update_dynamic_option(self.get_current_file(), 183 | dynamictab.DT_REL, base_addr) 184 | elif(secname == '.rela.dyn'): 185 | print "updating dynamic option .rela.dyn" 186 | self.update_dynamic_option(self.get_current_file(), 187 | dynamictab.DT_RELA, base_addr) 188 | else: 189 | print "updating dynamic option for section %s" % secname 190 | self.update_dynamic_option(self.get_current_file(), 191 | dynamictab.dreversemap[secname], 192 | base_addr) 193 | 194 | 195 | #This is the function that trigger instrumentation 196 | def perform_instrumentation(self): 197 | if(self.validate_sectionlist(self.filename, [self.secname]) == False): 198 | print "sectionlist contain invaid section" 199 | raise 200 | 201 | sectiontable = section_table(self.get_current_file()) 202 | sectiontable.relocate_section(self.secname) 203 | sectiontable.write2disk() 204 | self.patch_relocation(self.secname) 205 | res = self.add_instrumentation_data(self.get_current_file(), 206 | None, 207 | self.secname, 208 | self.secalign, 209 | self.new_section_perm) 210 | if(res == None): 211 | print "inserting instrumentation data failed" 212 | raise 213 | 214 | 215 | # Deprecated for now 216 | 217 | class section_duplicator(base_instrumentor): 218 | def __init__(self, name, binname, srcsection, dstsecname, align=0x1000, perm=0x05): 219 | super(section_duplicator, self).__init__(binname) 220 | self.instrumentor_name = name 221 | self.inject_secname = dstsecname 222 | self.inject_secalign = align 223 | self.src_section = srcsection 224 | self.new_section_perm = perm 225 | self.generate_instrument_files() 226 | 227 | def generate_instrument_files(self): 228 | self.inject_file = self.get_tempfile() 229 | 230 | def patch_relocation(self, secname): 231 | if(secname == '.dynamic'): 232 | base_addr = self.get_section_info(self.get_current_file(), 233 | self.inject_secname, 'addr') 234 | base_offset = self.get_section_info(self.get_current_file(), 235 | self.inject_secname, 'offset') 236 | self.modify_phdrtab_info(self.get_current_file(), phdr.PT_DYNAMIC, 237 | phdr.p_offset, base_offset) 238 | self.modify_phdrtab_info(self.get_current_file(), phdr.PT_DYNAMIC, 239 | phdr.p_vaddr, base_addr) 240 | self.modify_phdrtab_info(self.get_current_file(), phdr.PT_DYNAMIC, 241 | phdr.p_paddr, base_addr) 242 | elif(secname == '.rel.dyn'): 243 | base_addr = self.get_section_info(self.get_current_file(), 244 | self.inject_secname, 'addr') 245 | print "updateing dynamic option DT_REL" 246 | print "current file is %s" % self.get_current_file() 247 | print "base address: %x" % base_addr 248 | self.update_dynamic_option(self.get_current_file(), 249 | dynamictab.DT_REL, base_addr) 250 | elif(secname == '.rela.dyn'): 251 | base_addr = self.get_section_info(self.get_current_file(), 252 | self.inject_secname, 'addr') 253 | print "updateing dynamic option" 254 | self.update_dynamic_option(self.get_current_file(), 255 | dynamictab.DT_RELA, base_addr) 256 | else: 257 | 258 | base_addr = self.get_section_info(self.get_current_file(), 259 | self.inject_secname, 'addr') 260 | print "updateing dynamic option" 261 | self.update_dynamic_option(self.get_current_file(), 262 | dynamictab.dreversemap[secname], 263 | base_addr) 264 | print "reaching here" 265 | print "base_addr %x"%base_addr 266 | #sys.exit(1) 267 | 268 | 269 | #This is the function that trigger instrumentation 270 | def perform_instrumentation(self): 271 | if(self.validate_sectionlist(self.filename, [self.src_section]) == False): 272 | print "sectionlist contain invaid section" 273 | return None 274 | self.extract_data(self.filename, self.src_section, self.inject_file) 275 | res = self.add_instrumentation_data(self.get_current_file(), 276 | self.inject_file, 277 | self.inject_secname, 278 | self.inject_secalign, 279 | self.new_section_perm) 280 | if(res == None): 281 | raise 282 | return 283 | self.patch_relocation(self.src_section) 284 | 285 | # Instrument_organizer is responsible to help successfullying injecting 286 | # multiple segments into one ELF file. Since each scheduler is responsible for 287 | # one segment, instrument_organizer observes the ELF binary layout to see if 288 | # relocating critical sections such as .gnu.hash, .dynsym or .dynstr is needed 289 | # to extend PHDR. 290 | class instrument_organizer(base_instrumentor): 291 | def __init__(self, binname): 292 | super(instrument_organizer, self).__init__(binname) 293 | self.lschedulers = [] 294 | 295 | def register_scheduler(self, scheduler): 296 | self.lschedulers.append(scheduler) 297 | 298 | def check_free_space_after_phdr(self): 299 | gap = self.compute_gap_to_extend_phdr(self.filename) 300 | phdr_size = self.get_elfhdr_info(self.filename, "Size of program headers:"); 301 | return gap/phdr_size 302 | 303 | def get_sections_to_relocate(self, dynlist, segnum): 304 | seclist = [] 305 | if(len(dynlist) == 0): 306 | raise 307 | if(segnum <=0): 308 | return seclist 309 | 310 | phdr_size = self.get_elfhdr_info(self.filename, "Size of program headers:"); 311 | size_need = phdr_size * segnum 312 | for dyn in dynlist: 313 | if(size_need <= 0): 314 | break 315 | secname = dynamictab.dnamemap[dyn[0]] 316 | if(dynamictab.dtypemap[dyn[0]] != 'data'): 317 | print "we truly don't have enough space, since we cannot move"\ 318 | " code for now" 319 | raise 320 | seclist.append(secname) 321 | size = self.get_section_info(self.filename, secname, 322 | sectiontab.s_size) 323 | size_need -= size 324 | return seclist 325 | 326 | def extend_phdr_on_number(self, extendnum): 327 | if(extendnum > 0): 328 | return self.extend_phdr_on_demand(extendnum) 329 | return False 330 | 331 | def extend_phdr_on_demand(self, extendnum=0): 332 | segnum = len(self.lschedulers) 333 | freeseg = 0 334 | # Check if there are enough space in PHDR. 335 | # Step1: skip checking existence of "NOTE" segment. 336 | if(extendnum == 0): 337 | segment_offset = self.get_segment_offset_in_phdr(self.filename, 338 | phdr.PT_NOTE, 0) 339 | if(segment_offset != None): 340 | freeseg = 1 341 | # Step2: check space after phdr. 342 | freeseg += self.check_free_space_after_phdr() 343 | print "=== freeseg: %d" % freeseg 344 | if(freeseg >= segnum): 345 | return False 346 | print "=== insufficient space to extend phdr, relocating sections" 347 | print "=== we need space for %d phdr entries" % (extendnum) 348 | extendnum = segnum - freeseg 349 | 350 | dynlist = self.get_dynamic_sections_info(self.filename) 351 | # Step3: get the critical sections. 352 | # Need space for one extra phdr entry to place relocated sections. 353 | # That's why we need 'extendnum + 1' number of extra segments. 354 | seclist = self.get_sections_to_relocate(dynlist, extendnum + 1) 355 | print "need to relocating these sections:" 356 | print seclist 357 | # Step4: relocate this section 358 | sc = instrument_scheduler() 359 | for secname in seclist: 360 | sr = section_relocator("%s_relocator"%secname, 361 | self.filename, secname) 362 | sc.register_instrumentor(sr) 363 | sc.perform_instrumentation() 364 | # Step5: setup the output file for the 1st scheduler and to itself in 365 | # case if there is no scheduler. 366 | self.set_current_file(sc.get_current_file()) 367 | if(len(self.lschedulers) > 0): 368 | self.lschedulers[0].set_current_file(sc.get_current_file()) 369 | print sr.get_current_file() 370 | return True 371 | 372 | def perform_instrumentation(self): 373 | self.extend_phdr_on_demand() 374 | total = len(self.lschedulers) 375 | for idx, scheduler in enumerate(self.lschedulers): 376 | scheduler.perform_instrumentation() 377 | if(idx < total - 1): 378 | self.lschedulers[idx + 1]\ 379 | .set_current_file(scheduler.get_current_file()) 380 | if(total > 0): 381 | self.set_current_file(self.lschedulers[-1].get_current_file()) 382 | 383 | def main(): 384 | parser = argparse.ArgumentParser() 385 | parser.add_argument("-f", "--file", type=str, help="file to instrument", 386 | required=True) 387 | args = parser.parse_args() 388 | 389 | scheduler = instrument_scheduler() 390 | scheduler2 = instrument_scheduler() 391 | scheduler3 = instrument_scheduler() 392 | 393 | organizer = instrument_organizer(args.file) 394 | organizer.register_scheduler(scheduler) 395 | organizer.register_scheduler(scheduler2) 396 | organizer.register_scheduler(scheduler3) 397 | 398 | it1 = instrumentor('mytest1', args.file, '.mytext1') 399 | it2 = instrumentor('mytest2', args.file, '.mytext2') 400 | it3 = instrumentor('mytest3', args.file, '.mytext3') 401 | scheduler.register_instrumentor(it1) 402 | scheduler.register_instrumentor(it2) 403 | scheduler.register_instrumentor(it3) 404 | 405 | it4 = instrumentor('mytest4', args.file, '.mytext4') 406 | it5 = instrumentor('mytest5', args.file, '.mytext5') 407 | it6 = instrumentor('mytest6', args.file, '.mytext6') 408 | scheduler2.register_instrumentor(it4) 409 | scheduler2.register_instrumentor(it5) 410 | scheduler2.register_instrumentor(it6) 411 | 412 | it7 = instrumentor('mytest7', args.file, '.mytext7') 413 | it8 = instrumentor('mytest8', args.file, '.mytext8') 414 | it9 = instrumentor('mytest9', args.file, '.mytext9') 415 | scheduler3.register_instrumentor(it7) 416 | scheduler3.register_instrumentor(it8) 417 | scheduler3.register_instrumentor(it9) 418 | 419 | organizer.perform_instrumentation() 420 | print organizer.get_current_file() 421 | #print scheduler2.get_current_file() 422 | if __name__ == "__main__": 423 | main() 424 | -------------------------------------------------------------------------------- /src/rewriter/section_basic.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import os,sys,inspect,platform 3 | currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) 4 | parentdir = os.path.dirname(currentdir) 5 | sys.path.insert(0,parentdir) 6 | 7 | from elf_basic import * 8 | 9 | 10 | class section_table(elf_basic): 11 | def __init__(self, binname): 12 | super(section_table, self).__init__(binname) 13 | self.sectiontable = dict(); 14 | self.stringtable = dict(); 15 | self.stringtable[0] = 1; 16 | self.strtaboffset = -1 # fail safe 17 | self.sectaboffset = -1 # fail safe 18 | self.sectabentsz = self.get_elfhdr_info(binname, elfhdr.e_sechdr_ent_size) 19 | self.dsecname2idx = dict() 20 | self.init_sections() 21 | #self.write2disk() 22 | self.secname2idx = dict() 23 | self.load_sectiontable() 24 | 25 | def load_sectiontable(self): 26 | seclist = self.get_sectionlist(self.get_binname()) 27 | self.secname2idx["NULL"] = 0 28 | secidx = 1 29 | if(len(seclist) > 0): 30 | for sec in seclist: 31 | attr = [] 32 | self.secname2idx[sec] = secidx 33 | secidx += 1 34 | for a in self.attribute_list: 35 | info = self.get_section_info(self.get_binname(), sec, a) 36 | if( a == self.sectiontab.s_link): 37 | if(info == 0): 38 | attr.append("NULL") 39 | else: 40 | attr.append(seclist[info - 1]) 41 | else: 42 | attr.append(info) 43 | self.assign_section(sec, attr) 44 | #print sec 45 | #print attr 46 | else: 47 | self.assign_section('.shstrtab', [1,3,0,0,self.strtaboffset,0,0,0,1,0]) 48 | 49 | 50 | def save_sectiontable(self): 51 | self.write2disk(self.current_file) 52 | 53 | def _expand_shstrtab(self, name): 54 | if(name == None or len(name) == 0): 55 | print "invalid string name" 56 | raise 57 | offset = self.stringtable[0] 58 | self.stringtable[offset] = name 59 | self.stringtable[0] += len(name) + 1 60 | return offset 61 | 62 | def duplicate_section(self, origsec, newsec, binname=None, 63 | align=None, tgtsec=None): 64 | if(align == None): 65 | align = self.sectiontable[origsec][self.sectiontab.s_align] 66 | if(tgtsec == None): 67 | tgtsec = self.get_last_section_in_mem() 68 | if(origsec not in self.sectiontable or \ 69 | tgtsec not in self.sectiontable): 70 | return False 71 | if(tgtsec == ".bss"): 72 | align = 0x1000 73 | secoffset = self.sectiontable[origsec][self.sectiontab.s_offset] 74 | secvaddr = self.sectiontable[origsec][self.sectiontab.s_vaddr] 75 | secsize = self.sectiontable[origsec][self.sectiontab.s_size] 76 | tgtsecoffset = self.sectiontable[tgtsec][self.sectiontab.s_offset] 77 | tgtsecsize = self.sectiontable[tgtsec][self.sectiontab.s_size] 78 | tgtsecvaddr = self.sectiontable[tgtsec][self.sectiontab.s_vaddr] 79 | tgtinjectoffset = self.compute_align(tgtsecoffset + tgtsecsize, align) 80 | tgtinjectvaddr = self.compute_align(tgtsecvaddr + tgtsecsize, align) 81 | print "location to inject 0x%x" % tgtinjectoffset 82 | if(binname == None): 83 | self.relocate_data(self.filename, secoffset, secsize, 84 | tgtinjectoffset) 85 | else: 86 | self.relocate_data(binname, secoffset, secsize, 87 | tgtinjectoffset) 88 | print "updating section table" 89 | self.assign_section(newsec, self.get_attributes(origsec).copy()) 90 | self.assign_attribute(newsec, "offset", tgtinjectoffset) 91 | self.assign_attribute(newsec, "vaddr", tgtinjectvaddr) 92 | #print self.sectiontable[newsec] 93 | 94 | def insert_new_section(self, secname, datafile, binname=None, perm=None, 95 | align=None, tgtsec=None): 96 | if(perm == None): 97 | perm = 0x6 #AX 98 | if(align == None): 99 | align = 0x1000 100 | if(tgtsec == None): 101 | tgtsec = self.get_last_section_in_mem() 102 | if(binname == None): 103 | binname = self.filename 104 | tgtsecoffset = self.sectiontable[tgtsec][self.sectiontab.s_offset] 105 | tgtsecsize = self.sectiontable[tgtsec][self.sectiontab.s_size] 106 | tgtsecvaddr = self.sectiontable[tgtsec][self.sectiontab.s_vaddr] 107 | tgtinjectoffset = self.compute_align(tgtsecoffset + tgtsecsize, align) 108 | tgtinjectvaddr = self.compute_align(tgtsecvaddr + tgtsecsize, align) 109 | self.write_data_from_file(binname, tgtinjectoffset, datafile) 110 | self.assign_section(secname, self.get_defaults('.default')) 111 | self.assign_attribute(secname, "offset", tgtinjectoffset) 112 | self.assign_attribute(secname, "vaddr", tgtinjectvaddr) 113 | self.assign_attribute(secname, "align", align) 114 | self.assign_attribute(secname, "flags", perm) 115 | self.assign_attribute(secname, "size", os.path.getsize(datafile)) 116 | 117 | 118 | def relocate_section(self, secname, binname=None, align=None, tgtsec=None): 119 | # Relocate_section() basically can be applied to all sections in 120 | # section table. If tgtsec is None, then inject to just after 121 | # ".bss". All sections behind ".bss" will be further relocated back. 122 | # Size of a section should be counted by the size and the align. 123 | if(align == None): 124 | align = self.sectiontable[secname][self.sectiontab.s_align] 125 | if(tgtsec == None): 126 | tgtsec = self.get_last_section_in_mem() 127 | if(secname not in self.sectiontable or \ 128 | tgtsec not in self.sectiontable): 129 | return False 130 | if(tgtsec == ".bss"): 131 | align = 0x1000 132 | #tgtoffset = self.sectiontable[secname][self.sectiontab.s_size] 133 | #tgtoffset = (tgtoffset + 4096 - 1) & 134 | secoffset = self.sectiontable[secname][self.sectiontab.s_offset] 135 | secvaddr = self.sectiontable[secname][self.sectiontab.s_vaddr] 136 | secsize = self.sectiontable[secname][self.sectiontab.s_size] 137 | tgtsecoffset = self.sectiontable[tgtsec][self.sectiontab.s_offset] 138 | tgtsecsize = self.sectiontable[tgtsec][self.sectiontab.s_size] 139 | tgtsecvaddr = self.sectiontable[tgtsec][self.sectiontab.s_vaddr] 140 | tgtinjectoffset = self.compute_align(tgtsecoffset + tgtsecsize, align) 141 | tgtinjectvaddr = self.compute_align(tgtsecvaddr + tgtsecsize, align) 142 | print "location to inject 0x%x" % tgtinjectoffset 143 | if(binname == None): 144 | print "offset to read: %x" % secoffset 145 | print "offset to inject: %x" % tgtinjectoffset 146 | self.relocate_data(self.filename, secoffset, secsize, 147 | tgtinjectoffset) 148 | else: 149 | self.relocate_data(binname, secoffset, secsize, 150 | tgtinjectoffset) 151 | print "updating section table" 152 | self.sectiontable[secname][self.sectiontab.s_offset] = tgtinjectoffset 153 | self.sectiontable[secname][self.sectiontab.s_vaddr] = tgtinjectvaddr 154 | 155 | 156 | def _move_section_back(self, name, size): 157 | pass 158 | 159 | def init_sections(self): 160 | 161 | self.dsecname2idx['stridx'] = sectiontab.s_str_idx 162 | self.dsecname2idx['type'] = sectiontab.s_type 163 | self.dsecname2idx['flags'] = sectiontab.s_flags 164 | self.dsecname2idx['vaddr'] = sectiontab.s_vaddr 165 | self.dsecname2idx['offset'] = sectiontab.s_offset 166 | self.dsecname2idx['size'] = sectiontab.s_size 167 | self.dsecname2idx['link'] = sectiontab.s_link 168 | self.dsecname2idx['info'] = sectiontab.s_info 169 | self.dsecname2idx['align'] = sectiontab.s_align 170 | self.dsecname2idx['entsize'] = sectiontab.s_entsize 171 | self.attribute_list = [sectiontab.s_str_idx, sectiontab.s_type, 172 | sectiontab.s_flags, sectiontab.s_vaddr, 173 | sectiontab.s_offset, sectiontab.s_size, 174 | sectiontab.s_link, sectiontab.s_info, 175 | sectiontab.s_align, sectiontab.s_entsize] 176 | # no section, then create a new one 177 | self.default_sections = dict() 178 | if(self.ptr_size() == 8): 179 | self.default_sections['.dynsym'] = [1,0xb,2,0,0,0,0,1,4,0x18] 180 | self.default_sections['.rela.dyn'] = [1,4,2,0,0,0,5,0,4,0x18] 181 | self.default_sections['.rela.plt'] = [1,4,2,0,0,0,5,0xc,4,0x18] 182 | elif(self.ptr_size() == 4): 183 | self.default_sections['.dynsym'] = [1,0xb,2,0,0,0,0,1,4,0x10] 184 | self.default_sections['.rela.dyn'] = [1,4,2,0,0,0,5,0,4,8] 185 | self.default_sections['.rela.plt'] = [1,4,2,0,0,0,5,0xc,4,8] 186 | else: 187 | raise 188 | self.default_sections['.default'] = [1,1,6,0,0,0,0,0,0x10,0] 189 | self.default_sections['.note.gnu.build-id'] = [1,7,2,0,0,0,0,0,4,0] 190 | self.default_sections['.note.ABI-tag'] = [1,7,2,0,0,0,0,0,4,0] 191 | self.default_sections['.note.hwcaps'] = [1,7,2,0,0,0,0,0,4,0] 192 | self.default_sections['.note.gold-version'] = [1,7,2,0,0,0,0,0,4,0] 193 | self.default_sections['.shstrtab'] = [1,3,0,0,0,0,0,0,1,0] 194 | self.default_sections['.data.rel.ro'] = [1,1,3,0,0,0,0,0,4,0] 195 | self.default_sections['.interp'] = [1,1,2,0,0,0,0,0,1,0] 196 | self.default_sections['.gnu.hash'] = [1,0x6ffffff6,2,0,0,0,5,0,4,4] 197 | self.default_sections['.hash'] = [1,5,2,0,0,0,5,0,4,4] 198 | self.default_sections['.tdata'] = [1,1,0x403,0,0,0,0,0,4,0] 199 | self.default_sections['.tbss'] = [1,8,0x403,0,0,0,0,0,4,0] 200 | self.default_sections['.dynstr'] = [1,3,2,0,0,0,0,0,1,0] 201 | self.default_sections['.gnu.version'] = [1,0x6fffffff,2,0,0,0,0,0,2,2] 202 | self.default_sections['.gnu.version_r'] = [1,0x6ffffffe,2,0,0,0,6,1,4,0] 203 | self.default_sections['.gnu.version_d'] = [1,0x6ffffffc,2,0,0,0,6,3,4,0] 204 | self.default_sections['.rel.dyn'] = [1,9,2,0,0,0,5,0,4,8] 205 | self.default_sections['.rel.plt'] = [1,9,2,0,0,0,5,0xc,4,8] 206 | self.default_sections['.init'] = [1,1,6,0,0,0,0,0,4,0] 207 | self.default_sections['.plt'] = [1,1,6,0,0,0,0,0,0x10,4] 208 | self.default_sections['.text'] = [1,1,6,0,0,0,0,0,0x10,0] 209 | self.default_sections['.fini'] = [1,1,6,0,0,0,0,0,4,0] 210 | self.default_sections['.rodata'] = [1,1,2,0,0,0,0,0,0x20,0] 211 | self.default_sections['.eh_frame_hdr'] = [1,1,2,0,0,0,0,0,4,0] 212 | self.default_sections['.eh_frame'] = [1,1,2,0,0,0,0,0,4,0] 213 | self.default_sections['.gcc_except_table'] = [1,1,2,0,0,0,0,0,4,0] 214 | self.default_sections['.init_array'] = [1,0xe,3,0,0,0,0,0,self.ptr_size(),0] 215 | self.default_sections['.fini_array'] = [1,0xf,3,0,0,0,0,0,self.ptr_size(),0] 216 | self.default_sections['.ctors'] = [1,1,3,0,0,0,0,0,self.ptr_size(),0] 217 | self.default_sections['.dtors'] = [1,1,3,0,0,0,0,0,self.ptr_size(),0] 218 | self.default_sections['.jcr'] = [1,1,3,0,0,0,0,0,self.ptr_size(),0] 219 | self.default_sections['.dynamic'] = [1,6,3,0,0,0,0,0,self.ptr_size(),self.ptr_size()*2] 220 | self.default_sections['.got'] = [1,1,3,0,0,0,0,0,self.ptr_size(),self.ptr_size()] 221 | self.default_sections['.got.plt'] = [1,1,3,0,0,0,0,0,self.ptr_size(),self.ptr_size()] 222 | self.default_sections['.data'] = [1,1,3,0,0,0,0,0,0x20,0] 223 | self.default_sections['.bss'] = [1,8,3,0,0,0,0,0,0x20,0] 224 | self.default_sections['.gnu_debuglink'] = [1,1,0,0,0,0,0,0,1,0] 225 | self.default_sections['.shstrtab'] = [1,3,0,0,self.strtaboffset,0,0,0,1,0] 226 | 227 | 228 | 229 | def get_defaults(self, secname): 230 | if(secname in self.default_sections): 231 | return self.default_sections[secname] 232 | else: 233 | return self.default_sections['default'] 234 | 235 | def get_attributes(self, secname): 236 | if(not secname in self.sectiontable): 237 | print "invalid section name %s" % secname 238 | return None 239 | return self.sectiontable[secname] 240 | 241 | def get_attribute(self, secname, field): 242 | if(not secname in self.sectiontable): 243 | print "invalid section name %s" % secname 244 | return None 245 | if(not field in self.dsecname2idx): 246 | print "invalid field name %s" % field 247 | return None 248 | return self.sectiontable[secname][self.dsecname2idx[field]] 249 | 250 | def get_index(self, secname): 251 | temptab1 = {k : v for k, v in self.sectiontable.iteritems() if v[sectiontab.s_vaddr] != 0} 252 | temptab1 = sorted(temptab1.items(), key=lambda x: x[1][sectiontab.s_offset]) 253 | temptab2 = {k : v for k, v in self.sectiontable.iteritems() if v[sectiontab.s_vaddr] == 0} 254 | temptab2 = sorted(temptab2.items(), key=lambda x: x[1][sectiontab.s_offset]) 255 | temptab1 = temptab1 + temptab2 256 | secidx = 1 257 | self.secname2idx["NULL"] = 0 258 | for item in temptab1: 259 | if(secname == item[0]): 260 | return secidx 261 | secidx += 1 262 | return None 263 | 264 | def get_next_known_section_location(self, cur_value, loc_type): 265 | cur_next = sys.maxint 266 | for index, sec in enumerate(sorted(self.sectiontable.items(),\ 267 | key=lambda x: x[1][self.dsecname2idx[loc_type]])): 268 | cur_loc = sec[1][self.dsecname2idx[loc_type]] 269 | if(cur_loc > cur_value): 270 | if(cur_loc < cur_next): 271 | cur_next = cur_loc 272 | return cur_next 273 | 274 | def get_section(self, secname, fieldlist): 275 | tmp = None 276 | for name in self.sectiontable: 277 | sec = self.sectiontable[name] 278 | if(name == secname): 279 | tmp = [] 280 | for field in fieldlist: 281 | if(field == 'name'): 282 | tmp.append(name) 283 | else: 284 | tmp.append(sec[self.dsecname2idx[field]]) 285 | break 286 | return tmp 287 | 288 | def get_sections(self, stypes, fieldlist): 289 | tmplist = [] 290 | for name in self.sectiontable: 291 | sec = self.sectiontable[name] 292 | for stype in stypes: 293 | if(sec[self.dsecname2idx['type']] == stype): 294 | tmp = [] 295 | for field in fieldlist: 296 | if(field == 'name'): 297 | tmp.append(name) 298 | else: 299 | tmp.append(sec[self.dsecname2idx[field]]) 300 | tmplist.append(tmp) 301 | return tmplist 302 | 303 | def assign_attribute_if_section_exists(self, secname, field, value): 304 | if(not secname in self.sectiontable): 305 | return 306 | self.assign_attribute(secname, field, value) 307 | 308 | def assign_attribute(self, secname, field, value): 309 | if(not secname in self.sectiontable): 310 | if(not secname in self.default_sections): 311 | print "invaid section name: %s" % secname 312 | return 313 | self.assign_section(secname, self.get_defaults(secname)) 314 | if(not field in self.dsecname2idx): 315 | print "invalid field name" 316 | return 317 | self.sectiontable[secname][self.dsecname2idx[field]] = value 318 | 319 | def check_section_exist(self, name): 320 | if(name in self.sectiontable): 321 | return True 322 | else: 323 | return False 324 | 325 | def remove_section(self, name): 326 | if(name == None or len(name) == 0): 327 | print "invalid section name" 328 | return False 329 | if(not name in self.sectiontable): 330 | print "section %s is not in section table" % name 331 | return False 332 | del self.sectiontable[name] 333 | 334 | def assign_section(self, name, metalist=None): 335 | if(metalist == None): 336 | metalist = self.get_defaults(name) 337 | if(name == None or len(name) == 0): 338 | print "invalid section name" 339 | return False 340 | if(name in self.sectiontable): 341 | #print "section name %s already exists" % name 342 | return False 343 | stridx = self._expand_shstrtab(name) 344 | #print "idx: %d" % stridx 345 | if(isinstance(metalist, dict)): 346 | self.sectiontable[name] = metalist 347 | self.sectiontable[name][sectiontab.s_str_idx] = stridx 348 | elif(isinstance(metalist, list)): 349 | self.sectiontable[name] = dict() 350 | self.sectiontable[name][sectiontab.s_str_idx] = stridx 351 | self.sectiontable[name][sectiontab.s_type ] = metalist[1] 352 | self.sectiontable[name][sectiontab.s_flags ] = metalist[2] 353 | self.sectiontable[name][sectiontab.s_vaddr ] = metalist[3] 354 | self.sectiontable[name][sectiontab.s_offset ] = metalist[4] 355 | self.sectiontable[name][sectiontab.s_size ] = metalist[5] 356 | self.sectiontable[name][sectiontab.s_link ] = metalist[6] 357 | self.sectiontable[name][sectiontab.s_info ] = metalist[7] 358 | self.sectiontable[name][sectiontab.s_align ] = metalist[8] 359 | self.sectiontable[name][sectiontab.s_entsize] = metalist[9] 360 | else: 361 | raise 362 | return True 363 | 364 | def get_last_section_in_mem(self): 365 | temptab = {k : v for k, v in self.sectiontable.iteritems() if v[sectiontab.s_vaddr] != 0} 366 | temptab = sorted(temptab.items(), key=lambda x: x[1][sectiontab.s_vaddr]) 367 | return temptab[-1][0] 368 | 369 | def get_elf_file_end(self): 370 | temptab = {k : v for k, v in self.sectiontable.iteritems() if v[sectiontab.s_offset] != 0} 371 | temptab = sorted(temptab.items(), key=lambda x: x[1][sectiontab.s_offset]) 372 | print "last section name: %s" % temptab[-1][0] 373 | if(temptab[-1][0] == ".shstrtab"): 374 | return temptab[-2][1][sectiontab.s_offset] + temptab[-2][1][sectiontab.s_size] 375 | return temptab[-1][1][sectiontab.s_offset] + temptab[-1][1][sectiontab.s_size] 376 | 377 | def get_updated_section_itemlist(self): 378 | temptab1 = {k : v for k, v in self.sectiontable.iteritems() if v[sectiontab.s_vaddr] != 0} 379 | temptab1 = sorted(temptab1.items(), key=lambda x: x[1][sectiontab.s_offset]) 380 | temptab2 = {k : v for k, v in self.sectiontable.iteritems() if v[sectiontab.s_vaddr] == 0} 381 | temptab2 = sorted(temptab2.items(), key=lambda x: x[1][sectiontab.s_offset]) 382 | temptab1 = temptab1 + temptab2 383 | secidx = 1 384 | self.secname2idx["NULL"] = 0 385 | for item in temptab1: 386 | secname = item[0] 387 | self.secname2idx[secname] = secidx 388 | secidx += 1 389 | return temptab1 390 | 391 | def write2disk(self, binname=None): 392 | if(binname == None): 393 | binname = self.filename 394 | #write string table 395 | print self.stringtable 396 | self.strtaboffset = self.get_elf_file_end() 397 | for offset in self.stringtable: 398 | if(offset == 0): 399 | continue 400 | self.write_str(binname, self.strtaboffset + offset, self.stringtable[offset]) 401 | print self.stringtable 402 | #write section table 403 | self.sectaboffset = self.compute_align(self.strtaboffset + self.stringtable[0], self.sectabentsz) 404 | print "string table offset: %x" % self.strtaboffset 405 | print "section table offset: %x" % self.sectaboffset 406 | 407 | offset = self.sectaboffset 408 | secnum = len(self.sectiontable) + 1 409 | self.write_single(binname, elfhdr.e_sechdr_ent_cnt, secnum, elfhdr.dsize[elfhdr.e_sechdr_ent_cnt]) 410 | self.write_single(binname, elfhdr.e_sechdr_offset, offset, elfhdr.dsize[elfhdr.e_sechdr_offset]) 411 | secindex = 1 412 | #write the first null section 413 | print "string table offset: %x" % self.strtaboffset 414 | print "section table offset: %x" % self.sectaboffset 415 | print "section size: %x" % self.sectabentsz 416 | print "try to zero out %d" % offset 417 | print "try to zero out %x" % offset 418 | self.write_zeros(binname, offset, self.sectabentsz) 419 | offset += self.sectabentsz 420 | #write the rest of the sections 421 | for item in self.get_updated_section_itemlist(): 422 | secname = item[0] 423 | section = item[1] 424 | for attribute in section: 425 | if(attribute == self.sectiontab.s_link and 426 | isinstance(section[attribute], basestring)): 427 | value = self.secname2idx[section[attribute]] 428 | else: 429 | value = section[attribute] 430 | self.write_single(binname, offset + attribute, value, sectiontab.dsize[attribute]) 431 | if(secname == ".shstrtab"): 432 | self.write_single(binname, offset + sectiontab.s_offset, 433 | self.strtaboffset, 434 | sectiontab.dsize[sectiontab.s_offset]) 435 | self.write_single(binname, offset + sectiontab.s_size, 436 | self.stringtable[0], 437 | sectiontab.dsize[sectiontab.s_size]) 438 | self.write_single(binname, elfhdr.e_sechdr_strtab, secindex, 439 | elfhdr.dsize[elfhdr.e_sechdr_strtab]) 440 | offset += self.sectabentsz 441 | secindex = secindex + 1 442 | #update the shstrtab section offset 443 | 444 | def check_if_in_gap(self, addr): 445 | for secname in self.sectiontable: 446 | if(secname == '.tbss' or secname == '.bss'): 447 | continue 448 | section = self.sectiontable[secname] 449 | secaddr = section[sectiontab.s_vaddr] 450 | secsize = section[sectiontab.s_size] 451 | if(addr >= secaddr and addr < secaddr + secsize): 452 | print "False:%s %x, %x, %x" % (secname, addr, secaddr, secaddr+secsize) 453 | return False 454 | return True 455 | 456 | def main(): 457 | parser = argparse.ArgumentParser() 458 | parser.add_argument("-f", "--file", type=str, help="file for reading section table", 459 | required=True) 460 | args = parser.parse_args() 461 | st = section_table(args.file) 462 | st.current_file = st.gen_tempfile(st.current_file) 463 | #st.duplicate_section(".gnu.hash", ".gnu.hash_new") 464 | #st.relocate_section(".gnu.hash") 465 | #st.relocate_section(".dynsym") 466 | #st.relocate_section(".dynstr") 467 | #print st.current_file 468 | #st.save_sectiontable() 469 | st.insert_new_section(".mytext", "/bin/ls", st.current_file) 470 | st.save_sectiontable() 471 | print st.current_file 472 | if __name__ == "__main__": 473 | main() 474 | --------------------------------------------------------------------------------