diff --git a/.gitattributes b/.gitattributes index 79481810d..0bead6219 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,7 +1,8 @@ docs/assets/* filter=lfs diff=lfs merge=lfs -text disassemblers/ofrak_ghidra/ofrak_ghidra_test/assets/* filter=lfs diff=lfs merge=lfs -text ofrak_patch_maker/technical_docs/vbcc.pdf filter=lfs diff=lfs merge=lfs -text -ofrak_core/test_ofrak/components/assets/* filter=lfs diff=lfs merge=lfs -text +ofrak_core/test_ofrak/components/assets/**/* filter=lfs diff=lfs merge=lfs -text +ofrak_core/test_ofrak/components/assets/cache/Makefile !filter !diff !merge text ofrak_core/test_ofrak/components/assets/README.md !filter !diff !merge text ofrak_core/test_ofrak/components/assets/kernel_address_space_build.sh !filter !diff !merge text ofrak_core/test_ofrak/components/assets/string_test.c !filter !diff !merge text @@ -11,6 +12,9 @@ ofrak_core/test_ofrak/components/assets/elf/* filter=lfs diff=lfs merge=lfs -tex ofrak_core/test_ofrak/components/assets/elf/edge-cases/* filter=lfs diff=lfs merge=lfs -text frontend/public/themes/**/* filter=lfs diff=lfs merge=lfs -text disassemblers/ofrak_angr/ofrak_angr_test/assets/* filter=lfs diff=lfs merge=lfs -text +disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/assets/**/* filter=lfs diff=lfs merge=lfs -text +disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/**/* filter=lfs diff=lfs merge=lfs -text +disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/Makefile !filter !diff !merge text ofrak_core/pytest_ofrak/elf/assets/* filter=lfs diff=lfs merge=lfs -text ofrak_core/pytest_ofrak/elf/assets/*.c !filter !diff !merge text ofrak_core/pytest_ofrak/elf/assets/Makefile !filter !diff !merge text diff --git a/.gitignore b/.gitignore index 5a50bdbc6..e5eb2d52b 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ ofrak_core/ofrak/core/entropy/entropy_c.cpython* ofrak_core/ofrak/gui/public ofrak_core/build ofrak_core/ofrak/license/license.json +disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/*_ghidra +ofrak_core/test_ofrak/components/assets/*_ghidra diff --git a/disassemblers/ofrak_cached_disassembly/Dockerstub b/disassemblers/ofrak_cached_disassembly/Dockerstub new file mode 100644 index 000000000..e69de29bb diff --git a/disassemblers/ofrak_cached_disassembly/LICENSE b/disassemblers/ofrak_cached_disassembly/LICENSE new file mode 100644 index 000000000..3819119a6 --- /dev/null +++ b/disassemblers/ofrak_cached_disassembly/LICENSE @@ -0,0 +1,426 @@ +Thank you for your interest in OFRAK (Open Firmware Reverse Analysis Konsole). +The OFRAK Community License is intended for educational use, personal +development, or just having fun. +The OFRAK Pro License is intended for individual use of OFRAK at work. For more +information, see https://ofrak.com/license. + +OFRAK COMMUNITY LICENSE AGREEMENT +Version 1.1 +Effective: July 22, 2024 + +RED BALLOON SECURITY, INC., A DELAWARE CORPORATION, WITH AN ADDRESS AT 639 11TH +AVENUE, 4TH FLOOR, NEW YORK, NY 10036, USA ("RED BALLOON") LICENSES OFRAK AND +RELATED DOCUMENTATION PURSUANT TO THE OFRAK COMMUNITY LICENSE AGREEMENT +(COLLECTIVELY WITH THE REGISTRATION FORM, THIS "AGREEMENT"). READ THIS +AGREEMENT CAREFULLY BEFORE ACCESSING, INSTALLING, COPYING AND USING OFRAK UNDER +THE OFRAK COMMUNITY AGREEMENT. BY TYPING "I AGREE" ON THE REGISTRATION FORM, OR +OTHERWISE ACCESSING, INSTALLING, COPYING OR OTHERWISE USING OFRAK, YOU +("LICENSEE") AGREE THAT THE REGISTRATION FORM SHALL BE DEEMED TO BE MUTUALLY +EXECUTED AND THE REGISTRATION FORM SHALL BE INCORPORATED INTO AND BECOME A +MATERIAL PART OF THE OFRAK COMMUNITY LICENSE AGREEMENT BETWEEN LICENSEE AND RED +BALLOON LOCATED AT https://ofrak.com/docs/license.html. YOU REPRESENT THAT YOU +ARE AUTHORIZED TO ACCEPT THIS AGREEMENT ON BEHALF OF LICENSEE. IF LICENSEE DOES +NOT AGREE TO THE FOREGOING TERMS AND CONDITIONS, DO NOT TYPE "I AGREE", OR +OTHERWISE ACCESS, INSTALL, COPY OR USE OFRAK. + +1. Definitions. + +1.1 "OFRAK" consists of (a) the source code repository for OFRAK, which can +be found at https://github.com/red-balloon-security/ofrak; (b) the following +Python packages, which are also available via PyPI, the Python Package Index: +ofrak, ofrak_components, ofrak_io, ofrak_type, ofrak_patch_maker, ofrak_angr, +ofrak_binary_ninja, ofrak_ghidra; (c) the OFRAK graphical user interface (GUI); +and (d) OFRAK documentation. OFRAK includes all updates, improvements, APIs and +add-ons provided by Red Balloon with respect thereto that Red Balloon makes +available to Licensee under this OFRAK Pro Agreement. OFRAK is presently made +available in three formats: (i) source code repository, (ii) PyPI Packages and +(iii) Docker images with dependencies preinstalled. + +1.2 "Academic Purposes" means use within a non-profit academic institution +by its then-current faculty and students for the purposes of non-profit +scholarly research, classroom and education, and not any other use (including +without limitation, directly or indirectly in connection with any commercial +activity such as, for example, sponsored research or consulting services). +Shared Use of OFRAK for an Academic Purpose is permitted only when (a) used for +educational purposes, (b) access is restricted and not provided to the general +public, (c) access is limited to employees and/or students of the same +institution involved in a specific educational activity, and (d) all users +accept and are subject to this Agreement. + +1.3 "Non-Commercial Use" means personal research, evaluation, or +development use by an individual, and not use by or on behalf of any commercial +entity or organization or directly or indirectly in connection with any +commercial activity. For clarity, you cannot make money off of redistributing +OFRAK code (including Derivatives), OFRAK analysis, OFRAK-modified binaries, or +other OFRAK outputs. Non-Commercial Use also excludes any Shared Use. + +1.4 "Commercial Use" means any use other than Academic Purposes or +Non-Commercial Use, including, without limitation, use for any commercial +purpose or by any commercial entity, including without limitation +redistributing the OFRAK code (including Derivatives), OFRAK analysis, +OFRAK-modified binaries, or other OFRAK outputs for any monetary or other +commercial consideration. + +1.5 "Derivatives" means any modifications, additions, enhancements, or +derivative works of OFRAK or any component thereof. For purposes of this +Agreement, Derivatives shall not include works that remain separable from, or +merely link to, the interfaces of OFRAK or any Derivatives. + +1.6 "Shared Use" means any use of OFRAK where the person who set up a +particular instance of OFRAK is not the same person interacting with that +instance of OFRAK, or where a single instance of OFRAK is used by more than one +person (whether on the same or different occasions). This includes, but is not +limited to, the use of OFRAK on a server that is accessible by more than one +person, or by any person other than the person who set up the use of OFRAK on +the said server. + +2. License. Subject to the terms and conditions of this Agreement, Red +Balloon grants to you a nonexclusive, nonsublicensable, nontransferable, +no-charge, royalty-free, limited license to install, use, copy, modify, create +derivative works of OFRAK only for (a) Academic Purposes and (b) Non-Commercial +Use and to share Derivatives (i) publicly within the community (via publicly +available forks on GitHub.com), (ii) for Shared Use for an Academic Purpose, +and (iii) with Red Balloon, for the purposes stated in this Agreement. For +clarity, the foregoing license does not grant to you any right or license to +commercialize, distribute or use OFRAK, Derivatives, OFRAK code, OFRAK +analysis, OFRAK-modified binaries, or other OFRAK outputs for any other purpose +whatsoever, including Commercial Use, other than Academic Purposes or +Non-Commercial Use. In the event that you wish to use OFRAK for any other +purpose, including Commercial Use, you need to contact Red Balloon and enter +into a separate OFRAK Pro License, OFRAK Enterprise License or other custom +agreement. Except for the limited rights and licenses expressly granted +hereunder, no other license is granted, no other use is permitted. + +3. Derivatives. To the extent that you prepare or create any Derivatives, +you shall and hereby grant to (a) all users of OFRAK a right and license to +such Derivatives upon the terms and conditions set forth in this Agreement and +(b) Red Balloon a perpetual, fully paid-up, royalty-free, worldwide and +irrevocable, right and license to use, copy, modify, enhance, prepare +derivative works of, distribute, with unlimited right to sublicense, make, have +made, sell, have sold, import, export and otherwise commercialize such +Derivatives. You acknowledge that Red Balloon may, but is not obligated to, +include your Derivatives in, and otherwise incorporate your Derivatives into, +the core OFRAK codebase. In the event that you create Derivatives, you must +(i) retain all copyright and other proprietary rights licenses included in the +original OFRAK code, and any other Derivatives, and (ii) make it clear that you +modified the original version of OFRAK. Red Balloon encourages you to make +your Derivatives available to the community by forking the OFRAK source code +repository on GitHub and publishing your Derivatives on your forked repository, +but you are not required to do so. You represent and warrant that you have +sufficient rights to any Derivatives and are legally entitled to grant the +above rights and licenses. If you are an individual and your +employer(s)/institution(s) have rights to intellectual property that you create +that includes your Derivatives, you represent that you have received permission +to make and contribute Derivatives on behalf of that employer/institution. + +4. Ownership; Restrictions. Except as expressly and unambiguously set +forth herein, Red Balloon and its licensors and contributors retain all right, +title and interest in and to OFRAK, Derivatives prepared or created by Red +Balloon, all copies, modifications and derivative works thereof, including +without limitation, all rights to patent, copyright, trade secret and other +proprietary or intellectual property rights related to any of the foregoing. +To the extent that Licensee creates any Derivatives, subject to the rights and +licenses granted herein, Licensee retains ownership of all right, title and +interest in and to such Derivatives prepared or created by Licensee, including +without limitation, all intellectual property rights related to any of the +foregoing. Licensee will maintain the copyright notice and any other notices +or identifications that appear on or in OFRAK and any Derivatives or any other +media or documentation that is subject to this OFRAK Pro Agreement. Licensee +will not (and will not allow any third party to): (a) use OFRAK or any +Derivatives, except as expressly permitted in this OFRAK Pro Agreement, (b) +provide, lease, lend, disclose, use for timesharing or service bureau purposes, +or otherwise use or allow others to use for the benefit of any third party, +OFRAK, (c) possess or use OFRAK, or allow the transfer, transmission, export, +or re-export of OFRAK or portion thereof in violation of any export control +laws or regulations administered by the U.S. Commerce Department, U.S. Treasury +Department’s Office of Foreign Assets Control, or any other government agency, +(d) use OFRAK in any way that violates any applicable law, rule or regulation +or for any illegal use or activity, or (e) seek any patent or other +intellectual property rights or protections over or in connection with OFRAK or +any Derivatives that Licensee prepares or creates. + +5. Feedback. In addition to Derivatives, you may, from time to time and +in your sole discretion, make suggestions for changes, modifications or +improvements to OFRAK ("Feedback"). Red Balloon shall have an irrevocable, +perpetual, worldwide, sublicenseable, transferable, full paid-up, royalty free +right and license to use, distribute and otherwise exploit all Feedback for any +purpose. + +6. No Cost License. OFRAK and any Derivatives provided pursuant to this +Agreement shall be provided during the Term at no charge to you. 7. +Services. No training or support services are provided under this Agreement. +Red Balloon may in its discretion respond to support inquiries through Red +Balloon’s support channels, such as Slack. + +8. Term and Termination. This Agreement shall commence upon the initial +download of OFRAK and shall continue until and unless terminated as set forth +herein (the "Term"). This Agreement may be terminated by Red Balloon +immediately upon notice to you in the event that you breach any term or +condition of this Agreement. Upon any termination, you shall immediately cease +all use of OFRAK. This sentence and the following provisions will survive +termination: 1, 3 - 5 and 9 - 12. Termination is not an exclusive remedy and +all other remedies will remain available. + +9. Warranty Disclaimer. The parties acknowledge that OFRAK is provided +"AS IS" and may not be functional on any machine or in any environment. +NEITHER RED BALLOON NOR ANY CONTRIBUTOR OF ANY DERIVATIVES MAKE ANY WARRANTIES, +EXPRESS OR IMPLIED, EITHER IN FACT OR BY OPERATION OF LAW, STATUTORY OR +OTHERWISE, AND RED BALLOON AND ANY CONTRIBUTOR OF ANY DERIVATIVES EXPRESSLY +EXCLUDES AND DISCLAIMS ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE, TITLE, ACCURACY, FREEDOM FROM ERRORS, FREEDOM FROM +PROGRAMMING DEFECTS, NONINTERFERENCE AND NONINFRINGEMENT, AND ALL IMPLIED +WARRANTIES ARISING OUT OF COURSE OF DEALING, COURSE OF PERFORMANCE AND USAGE OF +TRADE. THIS AGREEMENT IS NOT INTENDED FOR USE OF OFRAK IN HAZARDOUS +ENVIRONMENTS REQUIRING FAIL-SAFE PERFORMANCE WHERE THE FAILURE OF OFRAK COULD +LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SIGNIFICANT PHYSICAL OR +ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). USE OF OFRAK IN HIGH RISK +ACTIVITIES IS NOT AUTHORIZED PURSUANT TO THIS AGREEMENT. THE PARTIES AGREE +THAT THIS SECTION 9 REPRESENTS A REASONABLE ALLOCATION OF RISK AND THAT RED +BALLOON WOULD NOT PROCEED IN THE ABSENCE OF SUCH ALLOCATION. + +10. Limitations. NEITHER RED BALLOON NOR ANY CONTRIBUTOR OF DERIVATIVES +SHALL BE RESPONSIBLE OR LIABLE WITH RESPECT TO ANY SUBJECT MATTER OF THIS +AGREEMENT OR TERMS AND CONDITIONS RELATED THERETO UNDER ANY CONTRACT, +NEGLIGENCE, STRICT LIABILITY OR OTHER THEORY (A) FOR LOSS OR INACCURACY OF +DATA, OR COST OF PROCUREMENT OF SUBSTITUTE GOODS, SERVICES OR TECHNOLOGY; (B) +FOR ANY DIRECT, INDIRECT, PUNITIVE, INCIDENTAL, RELIANCE, SPECIAL, EXEMPLARY OR +CONSEQUENTIAL DAMAGES INCLUDING, BUT NOT LIMITED TO, LOSS OF REVENUES AND LOSS +OF PROFITS TO LICENSEE OR ANY THIRD PARTIES; (C) FOR ANY MATTER BEYOND ITS +REASONABLE CONTROL OR (D) FOR USE YOU OR OTHERS MAY MAKE OF OFRAK, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +11. Indemnification. You agree that (a) Red Balloon and any contributors +shall have no liability whatsoever for your use of OFRAK or any Derivatives and +(b) you shall indemnify, and hold harmless, and (upon request) defend Red +Balloon and any other user or contributor from and against any and all claims, +damages, liabilities, losses, and costs (including reasonable attorneys’ fees) +suffered or incurred by such party which arise from or relate to your (i) use +of OFRAK or Derivatives, or (ii) breach of this Agreement. + +12. Miscellaneous. Neither this Agreement nor the licenses granted +hereunder are assignable or transferable by you; any attempt to do so shall be +void. Red Balloon may assign this Agreement in whole or in part. Any notice, +report, approval or consent required or permitted hereunder shall be in +writing. The provisions hereof are for the benefit of the parties only and not +for any other person or entity. If any provision of this Agreement shall be +adjudged by any court of competent jurisdiction to be unenforceable or invalid, +that provision shall be limited or eliminated to the minimum extent necessary +so that this Agreement shall otherwise remain in full force and effect and +enforceable. This Agreement shall be deemed to have been made in, and shall be +construed pursuant to the laws of the State of New York, without regard to +conflicts of laws provisions thereof, and without regard to the United Nations +Convention on the International Sale of Goods or the Uniform Computer +Information Transactions Act. Any waivers or amendments shall be effective only +if made in writing. This Agreement is the complete and exclusive statement of +the mutual understanding of the parties and supersedes and cancels all previous +written and oral agreements and communications relating to the subject matter +of this Agreement. + + +OFRAK PRO LICENSE AGREEMENT +Version 1.0 +Effective: July 22, 2024 + +RED BALLOON SECURITY, INC., A DELAWARE CORPORATION, WITH AN ADDRESS AT 639 11TH +AVENUE, 4TH FLOOR, NEW YORK, NY 10036, USA ("RED BALLOON") LICENSES OFRAK AND +RELATED DOCUMENTATION PURSUANT TO THE OFRAK PRO LICENSE AGREEMENT (COLLECTIVELY +WITH THE REGISTRATION FORM, THIS "AGREEMENT"). READ THIS AGREEMENT CAREFULLY +BEFORE ACCESSING, INSTALLING, COPYING AND USING OFRAK UNDER THE OFRAK PRO +AGREEMENT. BY TYPING "I AGREE" ON THE REGISTRATION FORM, OR OTHERWISE +ACCESSING, INSTALLING, COPYING OR OTHERWISE USING OFRAK, YOU ("LICENSEE") AGREE +THAT THE REGISTRATION FORM SHALL BE DEEMED TO BE MUTUALLY EXECUTED AND THE +REGISTRATION FORM SHALL BE INCORPORATED INTO AND BECOME A MATERIAL PART OF THE +OFRAK PRO LICENSE AGREEMENT BETWEEN LICENSEE AND RED BALLOON LOCATED AT +https://ofrak.com/docs/license.html. YOU REPRESENT THAT YOU ARE AUTHORIZED TO +ACCEPT THIS AGREEMENT ON BEHALF OF LICENSEE. IF LICENSEE DOES NOT AGREE TO THE +FOREGOING TERMS AND CONDITIONS, DO NOT TYPE "I AGREE", OR OTHERWISE ACCESS, +INSTALL, COPY OR USE OFRAK. + +1. Definitions. + +1.1 "OFRAK" consists of (a) the source code repository for OFRAK, which can +be found at https://github.com/red-balloon-security/ofrak; (b) the following +Python packages, which are also available via PyPI, the Python Package Index: +ofrak, ofrak_components, ofrak_io, ofrak_type, ofrak_patch_maker, ofrak_angr, +ofrak_binary_ninja, ofrak_ghidra; (c) the OFRAK graphical user interface (GUI); +and (d) OFRAK documentation. OFRAK includes all updates, improvements, APIs and +add-ons provided by Red Balloon with respect thereto that Red Balloon makes +available to Licensee under this OFRAK Pro Agreement. OFRAK is presently made +available in three formats: (i) source code repository, (ii) PyPI Packages and +(iii) Docker images with dependencies preinstalled. + +1.2 "Academic Purposes" means use within a non-profit academic institution +by its then-current faculty and students for the purposes of non-profit +scholarly research, classroom and education, and not any other use (including +without limitation, directly or indirectly in connection with any commercial +activity such as, for example, sponsored research or consulting services). + +1.3 "Authorized User(s)" means any user(s) named in the license file. + +1.4 "Commercial Use" means any use other than Academic Purposes or Limited +Commercial Use, and any use other than by an Authorized User, including without +limitation redistributing the OFRAK code (including Derivatives), OFRAK +analysis, OFRAK-modified binaries, or other OFRAK outputs for use outside of a +consulting engagement of Licensee or volume redistribution of any +OFRAK-modified binaries. + +1.5 "Derivatives" means any modifications, additions, enhancements, or +derivative works of OFRAK or any component thereof. For purposes of this OFRAK +Pro Agreement, Derivatives shall not include works that remain separable from, +or merely link to, the interfaces of OFRAK or any Derivatives. + +1.6 "Limited Commercial Use" means internal business use by any Authorized +User of OFRAK during the term specified in the license file (the "Term"), which +includes use of OFRAK in exchange for monetary and other consideration (such as +security research purposes) and redistribution of (i) OFRAK analysis, (ii) +other OFRAK outputs, and (iii) OFRAK-modified binaries to an end-user for such +end user’s internal use, in each case in connection with consulting engagements +entered into between Licensee and such end user. For clarity, neither Licensee +nor the end user shall have the right to further redistribute OFRAK-modified +binaries outside of such end-user. + +2. License. Subject to the terms and conditions of this OFRAK Pro +Agreement, Red Balloon grants to Licensee a nonexclusive, nonsublicensable, +nontransferable, royalty-free, limited license during the term specified in the +license file ("Term") to install, use, copy, modify and create Derivatives of +OFRAK only by Authorized Users (a) for Academic Purposes, (b) for Limited +Commercial Use and (c) to share Derivatives (i) publicly within the community +(via publicly available forks on GitHub.com), (ii) for an Academic Purpose, and +(iii) with Red Balloon, for the purposes stated in this OFRAK Pro Agreement. +For clarity, the foregoing license does not grant to Licensee any other right +or license to commercialize, distribute or use OFRAK for any other purpose +whatsoever, including Commercial Use. In the event that Licensee wishes to use +OFRAK for any other purpose, including Commercial Use, Licensee needs to +contact Red Balloon and enter into a separate OFRAK Enterprise License or other +custom agreement. Except for the limited rights and licenses expressly granted +hereunder, no other license is granted, no other use is permitted. + +3. Derivatives. To the extent that Licensee prepares or creates any +Derivatives, Licensee shall and hereby grant to (a) all users of OFRAK a right +and license to such Derivatives upon the terms and conditions set forth in the +OFRAK Community License, located at https://ofrak.com/docs/license.html and (b) +Red Balloon a perpetual, fully paid-up, royalty-free, worldwide and +irrevocable, right and license to use, copy, modify, enhance, prepare +derivative works of, distribute, with unlimited right to sublicense, make, have +made, sell, have sold, import, export and otherwise commercialize such +Derivatives. Licensee acknowledges that Red Balloon may, but is not obligated +to, include Licensee’s Derivatives in, and otherwise incorporate Licensee’s +Derivatives into, the core OFRAK codebase. In the event that Licensee creates +Derivatives, Licensee must (i) retain all copyright and other proprietary +rights licenses included in the original OFRAK code, and any other Derivatives, +and (ii) make it clear that Licensee modified the original version of OFRAK. +Red Balloon encourages Licensee to make Licensee’s Derivatives available to the +community by forking the OFRAK source code repository on GitHub and publishing +Licensee’s Derivatives on Licensee’s forked repository, but Licensee is not +required to do so. Licensee represents and warrants that Licensee has +sufficient rights to any Derivatives and is legally entitled to grant the above +rights and licenses. + +4. Ownership; Restrictions. Except as expressly and unambiguously set +forth herein, Red Balloon and its licensors and contributors retain all right, +title and interest in and to OFRAK, Derivatives prepared or created by Red +Balloon, all copies, modifications and derivative works thereof, including +without limitation, all rights to patent, copyright, trade secret and other +proprietary or intellectual property rights related to any of the foregoing. +To the extent that Licensee creates any Derivatives, subject to the rights and +licenses granted herein, Licensee retains ownership of all right, title and +interest in and to such Derivatives prepared or created by Licensee, including +without limitation, all intellectual property rights related to any of the +foregoing. Licensee will maintain the copyright notice and any other notices +or identifications that appear on or in OFRAK and any Derivatives or any other +media or documentation that is subject to this OFRAK Pro Agreement. Licensee +will not (and will not allow any third party to): (a) use OFRAK or any +Derivatives, except as expressly permitted in this OFRAK Pro Agreement, (b) +provide, lease, lend, disclose, use for timesharing or service bureau purposes, +or otherwise use or allow others to use for the benefit of any third party, +OFRAK, (c) possess or use OFRAK, or allow the transfer, transmission, export, +or re-export of OFRAK or portion thereof in violation of any export control +laws or regulations administered by the U.S. Commerce Department, U.S. Treasury +Department’s Office of Foreign Assets Control, or any other government agency, +(d) use OFRAK in any way that violates any applicable law, rule or regulation +or for any illegal use or activity, or (e) seek any patent or other +intellectual property rights or protections over or in connection with OFRAK or +any Derivatives that Licensee prepares or creates. + +5. Feedback. In addition to Derivatives prepared or created by Licensee, +Licensee may, from time to time and in Licensee’s sole discretion, make +suggestions for changes, modifications or improvements to OFRAK ("Feedback"). +Red Balloon shall have an irrevocable, perpetual, worldwide, sublicenseable, +transferable, full paid-up, royalty free right and license to use, distribute +and otherwise exploit all Feedback for any purpose. + +6. Fees. Licensee agrees to pay Red Balloon all fees and other charges in +the amounts and at the times specified by Red Balloon in writing (without +deduction, set-off, or counterclaim). + +7. Services. No training or support services are provided under this +OFRAK Pro Agreement. Red Balloon may in its discretion respond to support +inquiries through Red Balloon’s support channels, such as Slack. + +8. Term and Termination. This OFRAK Pro Agreement shall commence upon the +date of license issue set forth in the license file and shall continue for the +Term unless terminated as set forth herein. This OFRAK Pro Agreement may be +terminated by Red Balloon immediately upon notice to Licensee in the event that +Licensee breaches any term or condition of this OFRAK Pro Agreement. Upon +expiration or any termination, Licensee shall immediately cease all use of +OFRAK. This sentence and the following provisions will survive termination: 1, +3 - 5 and 9 - 12. Termination is not an exclusive remedy and all other remedies +will remain available. + +9. Warranty Disclaimer. The parties acknowledge that OFRAK is provided +"AS IS" and may not be functional on any machine or in any environment. +NEITHER RED BALLOON NOR ANY CONTRIBUTOR OF ANY DERIVATIVES MAKE ANY WARRANTIES, +EXPRESS OR IMPLIED, EITHER IN FACT OR BY OPERATION OF LAW, STATUTORY OR +OTHERWISE, AND RED BALLOON AND ANY CONTRIBUTOR OF ANY DERIVATIVES EXPRESSLY +EXCLUDES AND DISCLAIMS ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE, TITLE, ACCURACY, FREEDOM FROM ERRORS, FREEDOM FROM +PROGRAMMING DEFECTS, NONINTERFERENCE AND NONINFRINGEMENT, AND ALL IMPLIED +WARRANTIES ARISING OUT OF COURSE OF DEALING, COURSE OF PERFORMANCE AND USAGE OF +TRADE. THIS OFRAK PRO AGREEMENT IS NOT INTENDED FOR USE OF OFRAK IN HAZARDOUS +ENVIRONMENTS REQUIRING FAIL-SAFE PERFORMANCE WHERE THE FAILURE OF OFRAK COULD +LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SIGNIFICANT PHYSICAL OR +ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). USE OF OFRAK IN HIGH RISK +ACTIVITIES IS NOT AUTHORIZED PURSUANT TO THIS OFRAK PRO AGREEMENT. THE PARTIES +AGREE THAT THIS SECTION 9 REPRESENTS A REASONABLE ALLOCATION OF RISK AND THAT +RED BALLOON WOULD NOT PROCEED IN THE ABSENCE OF SUCH ALLOCATION. +10. Limitations. NEITHER RED BALLOON NOR ANY CONTRIBUTOR OF DERIVATIVES +SHALL BE RESPONSIBLE OR LIABLE WITH RESPECT TO ANY SUBJECT MATTER OF THIS OFRAK +PRO AGREEMENT OR TERMS AND CONDITIONS RELATED THERETO UNDER ANY CONTRACT, +NEGLIGENCE, STRICT LIABILITY OR OTHER THEORY (A) FOR LOSS OR INACCURACY OF +DATA, OR COST OF PROCUREMENT OF SUBSTITUTE GOODS, SERVICES OR TECHNOLOGY; (B) +FOR ANY DIRECT, INDIRECT, PUNITIVE, INCIDENTAL, RELIANCE, SPECIAL, EXEMPLARY OR +CONSEQUENTIAL DAMAGES INCLUDING, BUT NOT LIMITED TO, LOSS OF REVENUES AND LOSS +OF PROFITS TO LICENSEE OR ANY THIRD PARTIES; (C) FOR ANY MATTER BEYOND ITS +REASONABLE CONTROL OR (D) FOR USE THAT LICENSEE OR OTHERS MAY MAKE OF OFRAK, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +11. Indemnification. Licensee agrees that (a) Red Balloon and any +contributors shall have no liability whatsoever for Licensee’s use of OFRAK or +any Derivatives and (b) Licensee shall indemnify, and hold harmless, and (upon +request) defend Red Balloon and any other user or contributor from and against +any and all claims, damages, liabilities, losses, and costs (including +reasonable attorneys’ fees) suffered or incurred by such party which arise from +or relate to Licensee’s (i) use of OFRAK or Derivatives, or (ii) breach of this +OFRAK Pro Agreement. + +12. Miscellaneous. Neither this OFRAK Pro Agreement nor the licenses +granted hereunder are assignable or transferable by Licensee; any attempt to do +so shall be void. Red Balloon may assign this OFRAK Pro Agreement in whole or +in part. Any notice, report, approval or consent required or permitted +hereunder shall be in writing. The provisions hereof are for the benefit of +the parties only and not for any other person or entity. If any provision of +this OFRAK Pro Agreement shall be adjudged by any court of competent +jurisdiction to be unenforceable or invalid, that provision shall be limited or +eliminated to the minimum extent necessary so that this OFRAK Pro Agreement +shall otherwise remain in full force and effect and enforceable. This OFRAK +Pro Agreement shall be deemed to have been made in, and shall be construed +pursuant to the laws of the State of New York, without regard to conflicts of +laws provisions thereof, and without regard to the United Nations Convention on +the International Sale of Goods or the Uniform Computer Information +Transactions Act. Any waivers or amendments shall be effective only if made in +writing. This OFRAK Pro Agreement is the complete and exclusive statement of +the mutual understanding of the parties and supersedes and cancels all previous +written and oral agreements and communications relating to the subject matter +of this OFRAK Pro Agreement. In the event of any conflict between this OFRAK +Pro License Agreement and the OFRAK Community License Agreement, located at +https://ofrak.com/docs/license.html, this OFRAK Pro Agreement shall control. diff --git a/disassemblers/ofrak_cached_disassembly/Makefile b/disassemblers/ofrak_cached_disassembly/Makefile new file mode 100644 index 000000000..19c2263ee --- /dev/null +++ b/disassemblers/ofrak_cached_disassembly/Makefile @@ -0,0 +1,12 @@ +PYTHON=python3 +PIP=pip3 + +install: + $(PIP) install . + +develop: + $(PIP) install -e .[test] + +test: + $(PYTHON) -m pytest --cov=ofrak_cached --cov-report=term-missing ofrak_cached_test + fun-coverage --cov-fail-under=100 diff --git a/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly/__init__.py b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly/components/__init__.py b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly/components/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly/components/cached_disassembly.py b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly/components/cached_disassembly.py new file mode 100644 index 000000000..67c7a6b7c --- /dev/null +++ b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly/components/cached_disassembly.py @@ -0,0 +1,34 @@ +from ofrak.core import * +import json + + +class CachedAnalysisStore: + def __init__(self): + self.analysis = dict() + self.program_attributes: Optional[ProgramAttributes] = None + + def store_analysis(self, resource_id: bytes, analysis: Union[Dict, str]): + if isinstance(analysis, str): + with open(analysis) as fh: + analysis = json.load(fh) + if resource_id not in self.analysis.keys(): + self.analysis[resource_id] = dict() + self.analysis[resource_id]["analysis"] = analysis + + def store_program_attributes(self, resource_id: bytes, program_attributes: ProgramAttributes): + if resource_id not in self.analysis.keys(): + self.analysis[resource_id] = dict() + self.analysis[resource_id]["program_attributes"] = program_attributes + + def delete_id_from_store(self, resource_id: bytes): + if resource_id in self.analysis: + del self.analysis[resource_id] + + def get_analysis(self, resource_id: bytes) -> Dict[str, Any]: + return self.analysis[resource_id]["analysis"] + + def get_program_attributes(self, resource_id: bytes) -> ProgramAttributes: + return self.analysis[resource_id]["program_attributes"] + + def id_exists(self, resource_id: bytes) -> bool: + return resource_id in self.analysis.keys() diff --git a/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly/components/cached_disassembly_unpacker.py b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly/components/cached_disassembly_unpacker.py new file mode 100644 index 000000000..25e03525f --- /dev/null +++ b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly/components/cached_disassembly_unpacker.py @@ -0,0 +1,276 @@ +from ofrak.core import * +import hashlib +from ofrak.core.code_region import CodeRegion +from ofrak.core.complex_block import ComplexBlock +from ofrak.service.component_locator_i import ( + ComponentLocatorInterface, +) +from ofrak.core.decompilation import ( + DecompilationAnalysis, + DecompilationAnalyzer, + DecompilationAnalysis, +) +from ofrak_cached_disassembly.components.cached_disassembly import CachedAnalysisStore + +_GHIDRA_AUTO_LOADABLE_FORMATS = [Elf, Ihex, Pe] + + +@dataclass +class CachedAnalysis(ResourceView): + pass + + +@dataclass +class CachedAnalysisAnalyzerConfig(ComponentConfig): + filename: str + force: Optional[bool] = False + + +class CachedAnalysisAnalyzer(Analyzer[CachedAnalysisAnalyzerConfig, CachedAnalysis]): + id = b"CachedAnalysisAnalyzer" + targets = (CachedAnalysis,) + outputs = (CachedAnalysis,) + + def __init__( + self, + resource_factory: ResourceFactory, + data_service: DataServiceInterface, + resource_service: ResourceServiceInterface, + analysis_store: CachedAnalysisStore, + ): + super().__init__(resource_factory, data_service, resource_service) + self.analysis_store = analysis_store + + async def analyze(self, resource: Resource, config: CachedAnalysisAnalyzerConfig): + await resource.identify() + if not resource.has_tag(Program) and not resource.has_attributes(ProgramAttributes): + raise AttributeError( + f"The reource with ID {resource.get_id()} is not an analyzable program format and does not have ProgramAttributes set." + ) + await resource.unpack() # Must unpack ELF to get program attributes + program_attributes = await resource.analyze(ProgramAttributes) + self.analysis_store.store_analysis(resource.get_id(), config.filename) + if not config.force: + if not await self.verify_cache_file(resource): + raise ValueError( + "MD5 recorded in cache file does not match the hash of the requested resource, use the force config option to use this cache file anyway." + ) + self.analysis_store.store_program_attributes(resource.get_id(), program_attributes) + cached_analysis_view = CachedAnalysis() + resource.add_view(cached_analysis_view) + await resource.save() + return cached_analysis_view + + async def verify_cache_file(self, resource: Resource): + data = await resource.get_data() + md5_hash = hashlib.md5(data) + return ( + md5_hash.digest().hex() + == self.analysis_store.get_analysis(resource.get_id())["metadata"]["hash"] + ) + + +class CachedProgramUnpacker(Unpacker[None]): + targets = (CachedAnalysis,) + outputs = (CodeRegion,) + + def __init__( + self, + resource_factory: ResourceFactory, + data_service: DataServiceInterface, + resource_service: ResourceServiceInterface, + analysis_store: CachedAnalysisStore, + component_locator: ComponentLocatorInterface, + ): + super().__init__(resource_factory, data_service, resource_service, component_locator) + self.analysis_store = analysis_store + + async def unpack(self, resource: Resource, config: None): + analysis = self.analysis_store.get_analysis(resource.get_id()) + for key, mem_region in analysis.items(): + if key.startswith("seg"): + await resource.create_child_from_view( + CodeRegion( + virtual_address=mem_region["virtual_address"], size=mem_region["size"] + ) + ) + + +class CachedCodeRegionModifier(Modifier[None]): + targets = (CodeRegion,) + + def __init__( + self, + resource_factory: ResourceFactory, + data_service: DataServiceInterface, + resource_service: ResourceServiceInterface, + analysis_store: CachedAnalysisStore, + ): + super().__init__(resource_factory, data_service, resource_service) + self.analysis_store = analysis_store + + async def modify(self, resource: Resource, config: None): + program_r = await resource.get_only_ancestor(ResourceFilter.with_tags(Program)) + analysis = self.analysis_store.get_analysis(program_r.get_id()) + ofrak_code_regions = await program_r.get_descendants_as_view( + v_type=CodeRegion, r_filter=ResourceFilter(tags=[CodeRegion]) + ) + backend_code_regions: List[CodeRegion] = [] + for key, mem_region in analysis.items(): + if key.startswith("seg") and mem_region["executable"]: + backend_code_regions.append( + CodeRegion( + virtual_address=mem_region["virtual_address"], size=mem_region["size"] + ) + ) + + ofrak_code_regions = sorted(ofrak_code_regions, key=lambda cr: cr.virtual_address) + backend_code_regions = sorted(backend_code_regions, key=lambda cr: cr.virtual_address) + + if len(ofrak_code_regions) > 0: + code_region = await resource.view_as(CodeRegion) + relative_va = code_region.virtual_address - ofrak_code_regions[0].virtual_address + + for backend_cr in backend_code_regions: + backend_relative_va = ( + backend_cr.virtual_address - backend_code_regions[0].virtual_address + ) + if backend_relative_va == relative_va and backend_cr.size == code_region.size: + code_region.resource.add_view( + backend_cr + ) # TODO: https://github.com/redballoonsecurity/ofrak/issues/537 + await resource.save() + + +class CachedCodeRegionUnpacker(CodeRegionUnpacker): + def __init__( + self, + resource_factory: ResourceFactory, + data_service: DataServiceInterface, + resource_service: ResourceServiceInterface, + analysis_store: CachedAnalysisStore, + component_locator: ComponentLocatorInterface, + ): + super().__init__(resource_factory, data_service, resource_service, component_locator) + self.analysis_store = analysis_store + + async def unpack(self, resource: Resource, config: None): + program_r = await resource.get_only_ancestor(ResourceFilter.with_tags(Program)) + analysis = self.analysis_store.get_analysis(program_r.get_id()) + if analysis["metadata"]["backend"] == "ghidra": + await resource.run(CachedCodeRegionModifier) + code_region_view = await resource.view_as(CodeRegion) + func_keys = analysis[f"seg_{code_region_view.virtual_address}"]["children"] + for func_key in func_keys: + complex_block = analysis[func_key] + cb = ComplexBlock( + virtual_address=complex_block["virtual_address"], + size=complex_block["size"], + name=complex_block["name"], + ) + await code_region_view.create_child_region(cb) + + +class CachedComplexBlockUnpacker(ComplexBlockUnpacker): + def __init__( + self, + resource_factory: ResourceFactory, + data_service: DataServiceInterface, + resource_service: ResourceServiceInterface, + analysis_store: CachedAnalysisStore, + component_locator: ComponentLocatorInterface, + ): + super().__init__(resource_factory, data_service, resource_service, component_locator) + self.analysis_store = analysis_store + + async def unpack(self, resource: Resource, config: None): + program_r = await resource.get_only_ancestor(ResourceFilter.with_tags(Program)) + analysis = self.analysis_store.get_analysis(program_r.get_id()) + program_attributes = self.analysis_store.get_program_attributes(program_r.get_id()) + + cb_view = await resource.view_as(ComplexBlock) + child_keys = analysis[f"func_{cb_view.virtual_address}"]["children"] + for children in child_keys: + if children.startswith("bb"): + basic_block = analysis[children] + mode = InstructionSetMode.NONE + if basic_block["mode"] == "thumb": + mode = InstructionSetMode.THUMB + elif basic_block["mode"] == "vle": + mode = InstructionSetMode.VLE + bb = BasicBlock( + virtual_address=basic_block["virtual_address"], + size=basic_block["size"], + mode=mode, + is_exit_point=basic_block["is_exit_point"], + exit_vaddr=basic_block["exit_vaddr"], + ) + await cb_view.create_child_region(bb) + elif children.startswith("dw"): + data_word = analysis[children] + fmt_string = ( + program_attributes.endianness.get_struct_flag() + data_word["format_string"] + ) + dw = DataWord( + virtual_address=data_word["virtual_address"], + size=data_word["size"], + format_string=fmt_string, + xrefs_to=tuple(data_word["xrefs_to"]), + ) + await cb_view.create_child_region(dw) + + +class CachedBasicBlockUnpacker(BasicBlockUnpacker): + def __init__( + self, + resource_factory: ResourceFactory, + data_service: DataServiceInterface, + resource_service: ResourceServiceInterface, + analysis_store: CachedAnalysisStore, + component_locator: ComponentLocatorInterface, + ): + super().__init__(resource_factory, data_service, resource_service, component_locator) + self.analysis_store = analysis_store + + async def unpack(self, resource: Resource, config: None): + program_r = await resource.get_only_ancestor(ResourceFilter.with_tags(Program)) + analysis = self.analysis_store.get_analysis(program_r.get_id()) + + bb_view = await resource.view_as(BasicBlock) + child_keys = analysis[f"bb_{bb_view.virtual_address}"]["children"] + for children in child_keys: + instruction = analysis[children] + mode = InstructionSetMode.NONE + if instruction["mode"] == "thumb": + mode = InstructionSetMode.THUMB + elif instruction["mode"] == "vle": + mode = InstructionSetMode.VLE + instr = Instruction( + virtual_address=instruction["virtual_address"], + size=instruction["size"], + disassembly=f"{instruction['mnemonic']} {instruction['operands']}", + mnemonic=instruction["mnemonic"], + operands=instruction["operands"], + mode=mode, + ) + await bb_view.create_child_region(instr) + + +class CachedDecompilationAnalyzer(DecompilationAnalyzer): + def __init__( + self, + resource_factory: ResourceFactory, + data_service: DataServiceInterface, + resource_service: ResourceServiceInterface, + analysis_store: CachedAnalysisStore, + ): + super().__init__(resource_factory, data_service, resource_service) + self.analysis_store = analysis_store + + async def analyze(self, resource: Resource, config: None) -> DecompilationAnalysis: + # Run / fetch ghidra analyzer + program_r = await resource.get_only_ancestor(ResourceFilter.with_tags(Program)) + analysis = self.analysis_store.get_analysis(program_r.get_id()) + complex_block = await resource.view_as(ComplexBlock) + decomp = analysis[f"func_{complex_block.virtual_address}"]["decompilation"] + return DecompilationAnalysis(decomp) diff --git a/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/Makefile b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/Makefile new file mode 100644 index 000000000..4aaed5f76 --- /dev/null +++ b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/Makefile @@ -0,0 +1,13 @@ +pyghidra: clean fib-pyghidra fib-thumb-pyghidra hello.x64.elf-pyghidra + +fib-pyghidra: + python3 -m ofrak_pyghidra analyze -i fib -o fib.json + +fib-thumb-pyghidra: + python3 -m ofrak_pyghidra analyze -i fib_thumb -o fib_thumb.json + +hello.x64.elf-pyghidra: + python3 -m ofrak_pyghidra analyze -i hello.x64.elf -o hello.x64.elf.json -d + +clean: + rm -f fib.json fib_thumb.json hello.x64.elf.json diff --git a/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/fib b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/fib new file mode 100755 index 000000000..f163ebaeb --- /dev/null +++ b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/fib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b382a70a95708b618c93798492c429b772fc5d08493d7b2670fde093336e83f +size 109724 diff --git a/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/fib.json b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/fib.json new file mode 100644 index 000000000..87c8842c8 --- /dev/null +++ b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/fib.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4be06b4af9cfe5cd1d45fa28110a61c421f768bca86e77eefda488052231908c +size 3346669 diff --git a/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/fib_thumb b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/fib_thumb new file mode 100755 index 000000000..bf543f14a --- /dev/null +++ b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/fib_thumb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1d986a0c86e4034d4b70b2d59cab6de7a836cc001a7d17d8da06e51c26fc419 +size 98024 diff --git a/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/fib_thumb.json b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/fib_thumb.json new file mode 100644 index 000000000..9a99319be --- /dev/null +++ b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/fib_thumb.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d3ae516795e712f4a7f23d480a2c25e44bcd34707a7ba7e97668b267ef929791 +size 3490141 diff --git a/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/hello.x64.elf b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/hello.x64.elf new file mode 100755 index 000000000..138e83be8 --- /dev/null +++ b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/hello.x64.elf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af467282f2c4cc3fd6400edae094bea7ac96dff34d925e44fa49c75024b2d67c +size 75568 diff --git a/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/hello.x64.elf.json b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/hello.x64.elf.json new file mode 100644 index 000000000..5a72486f1 --- /dev/null +++ b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/hello.x64.elf.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:40a768a1241d8f336609f0f64de71532466ecbbbaeaae497dca4a80725c5c226 +size 42698 diff --git a/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/src/Makefile b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/src/Makefile new file mode 100644 index 000000000..88e8b02ba --- /dev/null +++ b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/src/Makefile @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78a03cff955c8b53fb1d78f83d5dea279b4013068650f11ae70369e5ab3c2a0f +size 350 diff --git a/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/src/fib.c b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/src/fib.c new file mode 100644 index 000000000..3f4133494 --- /dev/null +++ b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/assets/src/fib.c @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dceb3b515ea092a5b1fdad2014d1f6cce82551b4f64f18274a166207600b424c +size 294 diff --git a/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/conftest.py b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/conftest.py new file mode 100755 index 000000000..a64982857 --- /dev/null +++ b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/conftest.py @@ -0,0 +1,21 @@ +import pytest +import ofrak_cached_disassembly + +from test_ofrak.components.hello_world_elf import hello_elf + +pytest_plugins = ["pytest_ofrak.fixtures"] + + +@pytest.fixture(scope="session") +def hello_world_elf() -> bytes: + return hello_elf() + + +@pytest.fixture(autouse=True) +def pyghidra_components(ofrak_injector): + ofrak_injector.discover(ofrak_cached_disassembly) + + +@pytest.fixture +def test_id(): + return "TEST_JOB" diff --git a/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/test_cached_components.py b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/test_cached_components.py new file mode 100644 index 000000000..b6b478245 --- /dev/null +++ b/disassemblers/ofrak_cached_disassembly/ofrak_cached_disassembly_test/test_cached_components.py @@ -0,0 +1,189 @@ +import os +from typing import Dict +import pytest +from ofrak.core import * +from ofrak.ofrak_context import OFRAKContext +from ofrak_cached_disassembly.components.cached_disassembly_unpacker import ( + CachedAnalysisAnalyzer, + CachedAnalysisAnalyzerConfig, +) +from ofrak_type import InstructionSetMode +from pytest_ofrak.patterns.code_region_unpacker import ( + CodeRegionUnpackAndVerifyPattern, + CodeRegionUnpackerTestCase, +) +from pytest_ofrak.patterns.complex_block_unpacker import ( + ComplexBlockUnpackerUnpackAndVerifyPattern, + ComplexBlockUnpackerTestCase, + TEST_PATTERN_ASSETS_DIR, +) +from pytest_ofrak.patterns.basic_block_unpacker import ( + BasicBlockUnpackerUnpackAndVerifyPattern, + BasicBlockUnpackerTestCase, +) +from ofrak.core.decompilation import DecompilationAnalysis + +import ofrak_cached_disassembly + +ASSETS_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "assets")) + + +@pytest.fixture(autouse=True) +def pyghidra_components(ofrak_injector): + ofrak_injector.discover(ofrak_cached_disassembly) + + +class TestGhidraCodeRegionUnpackAndVerify(CodeRegionUnpackAndVerifyPattern): + @pytest.fixture + async def root_resource( + self, + unpack_verify_test_case: CodeRegionUnpackerTestCase, + ofrak_context: OFRAKContext, + test_id: str, + ) -> Resource: + asset_path = os.path.join(TEST_PATTERN_ASSETS_DIR, unpack_verify_test_case.binary_filename) + with open(asset_path, "rb") as f: + binary_data = f.read() + resource = await ofrak_context.create_root_resource(test_id, binary_data, tags=(File,)) + CACHE_FILENAME = os.path.join( + os.path.join(TEST_PATTERN_ASSETS_DIR, "cache"), unpack_verify_test_case.binary_filename + ) + await resource.run( + CachedAnalysisAnalyzer, config=CachedAnalysisAnalyzerConfig(filename=CACHE_FILENAME) + ) + return resource + + +class TestCachedComplexBlockUnpackAndVerify(ComplexBlockUnpackerUnpackAndVerifyPattern): + @pytest.fixture + async def root_resource( + self, + unpack_verify_test_case: ComplexBlockUnpackerTestCase, + ofrak_context: OFRAKContext, + test_id: str, + ) -> Resource: + asset_path = os.path.join(TEST_PATTERN_ASSETS_DIR, unpack_verify_test_case.binary_filename) + with open(asset_path, "rb") as f: + binary_data = f.read() + resource = await ofrak_context.create_root_resource(test_id, binary_data, tags=(File,)) + CACHE_FILENAME = os.path.join( + os.path.join(TEST_PATTERN_ASSETS_DIR, "cache"), unpack_verify_test_case.binary_filename + ) + await resource.run( + CachedAnalysisAnalyzer, config=CachedAnalysisAnalyzerConfig(filename=CACHE_FILENAME) + ) + return resource + + @pytest.fixture + async def expected_results(self, unpack_verify_test_case: ComplexBlockUnpackerTestCase) -> Dict: + if unpack_verify_test_case.binary_md5_digest == "fc7a6b95d993f955bd92f2bef2699dd0": + return self._fixup_test_case_for_pie( + unpack_verify_test_case.expected_results, + pie_base_vaddr=0x10000, + ) + + return unpack_verify_test_case.expected_results + + @pytest.fixture + async def optional_results(self, unpack_verify_test_case: ComplexBlockUnpackerTestCase): + if unpack_verify_test_case.binary_md5_digest == "fc7a6b95d993f955bd92f2bef2699dd0": + return set( + self._fixup_test_case_for_pie( + {vaddr: [] for vaddr in unpack_verify_test_case.optional_results}, + pie_base_vaddr=0x10000, + ).keys() + ) + + return unpack_verify_test_case.optional_results + + +class TestGhidraBasicBlockUnpackAndVerify(BasicBlockUnpackerUnpackAndVerifyPattern): + @pytest.fixture + async def root_resource( + self, + unpack_verify_test_case: BasicBlockUnpackerTestCase, + ofrak_context: OFRAKContext, + test_id: str, + ) -> Resource: + asset_path = os.path.join(TEST_PATTERN_ASSETS_DIR, unpack_verify_test_case.binary_filename) + with open(asset_path, "rb") as f: + binary_data = f.read() + resource = await ofrak_context.create_root_resource(test_id, binary_data, tags=(File,)) + CACHE_FILENAME = os.path.join( + os.path.join(TEST_PATTERN_ASSETS_DIR, "cache"), unpack_verify_test_case.binary_filename + ) + await resource.run( + CachedAnalysisAnalyzer, config=CachedAnalysisAnalyzerConfig(filename=CACHE_FILENAME) + ) + return resource + + +INSTRUCTION_MODE_TEST_CASES = [ + ("fib", "fib.json", InstructionSetMode.NONE), + ("fib_thumb", "fib_thumb.json", InstructionSetMode.THUMB), +] + + +@pytest.fixture(params=INSTRUCTION_MODE_TEST_CASES, ids=lambda tc: tc[0]) +async def test_case( + pyghidra_components: None, ofrak_context: OFRAKContext, request +) -> Tuple[Resource, InstructionSetMode]: + binary_name, cache_name, mode = request.param + binary_path = os.path.join(ASSETS_DIR, binary_name) + resource = await ofrak_context.create_root_resource_from_file(binary_path) + cache_path = os.path.join(ASSETS_DIR, cache_name) + await resource.run( + CachedAnalysisAnalyzer, config=CachedAnalysisAnalyzerConfig(filename=cache_path) + ) + return resource, mode + + +async def test_instruction_mode(test_case: Tuple[Resource, InstructionSetMode]): + root_resource, mode = test_case + await root_resource.unpack_recursively() + instructions = list( + await root_resource.get_descendants_as_view( + Instruction, r_filter=ResourceFilter.with_tags(Instruction) + ) + ) + # Using "any" instead of "all" because not 100% of the basic blocks in a binary compiled with + # "-mthumb" are in THUMB mode. This is testing (de)serialization of Ghidra analysis, + # so all that matters is that we're seeing some instructions of the expected type + assert any(instruction.mode == mode for instruction in instructions), ( + f"None of the instructions in {root_resource.get_id().hex()} had the expected instruction " + f"set mode of {mode.name}." + ) + + +async def test_cached_decompilation(ofrak_context: OFRAKContext): + root_resource = await ofrak_context.create_root_resource_from_file( + os.path.join(os.path.dirname(__file__), "assets/hello.x64.elf") + ) + await root_resource.run( + CachedAnalysisAnalyzer, + config=CachedAnalysisAnalyzerConfig(filename="assets/hello.x64.elf.json"), + ) + await root_resource.unpack_recursively( + do_not_unpack=[ + ComplexBlock, + ] + ) + complex_blocks: List[ComplexBlock] = await root_resource.get_descendants_as_view( + ComplexBlock, + r_filter=ResourceFilter( + tags=[ + ComplexBlock, + ] + ), + ) + decomps = [] + for complex_block in complex_blocks: + await complex_block.resource.identify() + ghidra_resource: DecompilationAnalysis = await complex_block.resource.view_as( + DecompilationAnalysis + ) + decomps.append(ghidra_resource.decompilation) + assert len(decomps) == 14 + assert "" not in decomps + assert "main" in " ".join(decomps) + assert "print" in " ".join(decomps) diff --git a/disassemblers/ofrak_cached_disassembly/pytest.ini b/disassemblers/ofrak_cached_disassembly/pytest.ini new file mode 100644 index 000000000..2f4c80e30 --- /dev/null +++ b/disassemblers/ofrak_cached_disassembly/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +asyncio_mode = auto diff --git a/disassemblers/ofrak_cached_disassembly/requirements.txt b/disassemblers/ofrak_cached_disassembly/requirements.txt new file mode 100644 index 000000000..d8a8cabc7 --- /dev/null +++ b/disassemblers/ofrak_cached_disassembly/requirements.txt @@ -0,0 +1 @@ +importlib-resources # A workaround for https://github.com/redballoonsecurity/ofrak/issues/398 diff --git a/disassemblers/ofrak_cached_disassembly/setup.py b/disassemblers/ofrak_cached_disassembly/setup.py new file mode 100644 index 000000000..eaefb285f --- /dev/null +++ b/disassemblers/ofrak_cached_disassembly/setup.py @@ -0,0 +1,58 @@ +import setuptools +import pkg_resources +from setuptools.command.egg_info import egg_info + + +class egg_info_ex(egg_info): + """Includes license file into `.egg-info` folder.""" + + def run(self): + # don't duplicate license into `.egg-info` when building a distribution + if not self.distribution.have_run.get("install", True): + # `install` command is in progress, copy license + self.mkpath(self.egg_info) + self.copy_file("LICENSE", self.egg_info) + + egg_info.run(self) + + +with open("LICENSE") as f: + license = "".join(["\n", f.read()]) + + +# Should be the same as in build_image.py +def read_requirements(requirements_path): + with open(requirements_path) as requirements_handle: + return [ + str(requirement) + for requirement in pkg_resources.parse_requirements(requirements_handle) + ] + + +setuptools.setup( + name="ofrak_cached_disassembly", + version="0.1.0", + author="Red Balloon Security", + author_email="ofrak@redballoonsecurity.com", + description="OFRAK Disassembler Components for Cached Results", + url="", # TODO + packages=setuptools.find_packages(), + package_data={"ofrak_cached_disassembly": ["py.typed"]}, + classifiers=[ + "Programming Language :: Python :: 3", + "Operating System :: OS Independent", + ], + install_requires=read_requirements("requirements.txt"), + extras_require={ + "test": [ + "fun-coverage==0.2.0", + "pytest", + "pytest-cov", + ] + }, + python_requires=">=3.9", + include_package_data=True, + license=license, + cmdclass={"egg_info": egg_info_ex}, + entry_points={"ofrak.packages": ["ofrak_cached_disassembly_pkg = ofrak_cached_disassembly"]}, +) diff --git a/disassemblers/ofrak_pyghidra/Dockerstub b/disassemblers/ofrak_pyghidra/Dockerstub new file mode 100644 index 000000000..c751c4148 --- /dev/null +++ b/disassemblers/ofrak_pyghidra/Dockerstub @@ -0,0 +1,22 @@ +# Download & install java and supervisor +RUN wget https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.deb && \ + dpkg -i jdk-21_linux-x64_bin.deb && \ + rm -f jdk-21_linux-x64_bin.debd + + + +RUN git clone https://github.com/NationalSecurityAgency/ghidra.git && \ + cd ghidra && \ + git checkout 5b7b12e7d016700eb0834d7421d266dd527a7355 && \ + ./gradlew -I gradle/support/fetchDependencies.gradle && \ + ./gradlew buildGhidra --info && \ + cd build/dist/ && \ + unzip ghidra*.zip > /dev/null && \ + mv ghidra_11.3_DEV /opt/rbs/ghidra_11.3_DEV && \ + cd ../../../ && \ + rm -rf ghidra && \ + cd /opt/rbs/ghidra_11.3_DEV/Ghidra/Features/PyGhidra/pypkg/ && \ + python3 -m pip install -e . + +ENV GHIDRA_INSTALL_DIR=/opt/rbs/ghidra_11.3_DEV/ +RUN export GHIDRA_INSTALL_DIR=/opt/rbs/ghidra_11.3_DEV/ diff --git a/disassemblers/ofrak_pyghidra/LICENSE b/disassemblers/ofrak_pyghidra/LICENSE new file mode 100644 index 000000000..3819119a6 --- /dev/null +++ b/disassemblers/ofrak_pyghidra/LICENSE @@ -0,0 +1,426 @@ +Thank you for your interest in OFRAK (Open Firmware Reverse Analysis Konsole). +The OFRAK Community License is intended for educational use, personal +development, or just having fun. +The OFRAK Pro License is intended for individual use of OFRAK at work. For more +information, see https://ofrak.com/license. + +OFRAK COMMUNITY LICENSE AGREEMENT +Version 1.1 +Effective: July 22, 2024 + +RED BALLOON SECURITY, INC., A DELAWARE CORPORATION, WITH AN ADDRESS AT 639 11TH +AVENUE, 4TH FLOOR, NEW YORK, NY 10036, USA ("RED BALLOON") LICENSES OFRAK AND +RELATED DOCUMENTATION PURSUANT TO THE OFRAK COMMUNITY LICENSE AGREEMENT +(COLLECTIVELY WITH THE REGISTRATION FORM, THIS "AGREEMENT"). READ THIS +AGREEMENT CAREFULLY BEFORE ACCESSING, INSTALLING, COPYING AND USING OFRAK UNDER +THE OFRAK COMMUNITY AGREEMENT. BY TYPING "I AGREE" ON THE REGISTRATION FORM, OR +OTHERWISE ACCESSING, INSTALLING, COPYING OR OTHERWISE USING OFRAK, YOU +("LICENSEE") AGREE THAT THE REGISTRATION FORM SHALL BE DEEMED TO BE MUTUALLY +EXECUTED AND THE REGISTRATION FORM SHALL BE INCORPORATED INTO AND BECOME A +MATERIAL PART OF THE OFRAK COMMUNITY LICENSE AGREEMENT BETWEEN LICENSEE AND RED +BALLOON LOCATED AT https://ofrak.com/docs/license.html. YOU REPRESENT THAT YOU +ARE AUTHORIZED TO ACCEPT THIS AGREEMENT ON BEHALF OF LICENSEE. IF LICENSEE DOES +NOT AGREE TO THE FOREGOING TERMS AND CONDITIONS, DO NOT TYPE "I AGREE", OR +OTHERWISE ACCESS, INSTALL, COPY OR USE OFRAK. + +1. Definitions. + +1.1 "OFRAK" consists of (a) the source code repository for OFRAK, which can +be found at https://github.com/red-balloon-security/ofrak; (b) the following +Python packages, which are also available via PyPI, the Python Package Index: +ofrak, ofrak_components, ofrak_io, ofrak_type, ofrak_patch_maker, ofrak_angr, +ofrak_binary_ninja, ofrak_ghidra; (c) the OFRAK graphical user interface (GUI); +and (d) OFRAK documentation. OFRAK includes all updates, improvements, APIs and +add-ons provided by Red Balloon with respect thereto that Red Balloon makes +available to Licensee under this OFRAK Pro Agreement. OFRAK is presently made +available in three formats: (i) source code repository, (ii) PyPI Packages and +(iii) Docker images with dependencies preinstalled. + +1.2 "Academic Purposes" means use within a non-profit academic institution +by its then-current faculty and students for the purposes of non-profit +scholarly research, classroom and education, and not any other use (including +without limitation, directly or indirectly in connection with any commercial +activity such as, for example, sponsored research or consulting services). +Shared Use of OFRAK for an Academic Purpose is permitted only when (a) used for +educational purposes, (b) access is restricted and not provided to the general +public, (c) access is limited to employees and/or students of the same +institution involved in a specific educational activity, and (d) all users +accept and are subject to this Agreement. + +1.3 "Non-Commercial Use" means personal research, evaluation, or +development use by an individual, and not use by or on behalf of any commercial +entity or organization or directly or indirectly in connection with any +commercial activity. For clarity, you cannot make money off of redistributing +OFRAK code (including Derivatives), OFRAK analysis, OFRAK-modified binaries, or +other OFRAK outputs. Non-Commercial Use also excludes any Shared Use. + +1.4 "Commercial Use" means any use other than Academic Purposes or +Non-Commercial Use, including, without limitation, use for any commercial +purpose or by any commercial entity, including without limitation +redistributing the OFRAK code (including Derivatives), OFRAK analysis, +OFRAK-modified binaries, or other OFRAK outputs for any monetary or other +commercial consideration. + +1.5 "Derivatives" means any modifications, additions, enhancements, or +derivative works of OFRAK or any component thereof. For purposes of this +Agreement, Derivatives shall not include works that remain separable from, or +merely link to, the interfaces of OFRAK or any Derivatives. + +1.6 "Shared Use" means any use of OFRAK where the person who set up a +particular instance of OFRAK is not the same person interacting with that +instance of OFRAK, or where a single instance of OFRAK is used by more than one +person (whether on the same or different occasions). This includes, but is not +limited to, the use of OFRAK on a server that is accessible by more than one +person, or by any person other than the person who set up the use of OFRAK on +the said server. + +2. License. Subject to the terms and conditions of this Agreement, Red +Balloon grants to you a nonexclusive, nonsublicensable, nontransferable, +no-charge, royalty-free, limited license to install, use, copy, modify, create +derivative works of OFRAK only for (a) Academic Purposes and (b) Non-Commercial +Use and to share Derivatives (i) publicly within the community (via publicly +available forks on GitHub.com), (ii) for Shared Use for an Academic Purpose, +and (iii) with Red Balloon, for the purposes stated in this Agreement. For +clarity, the foregoing license does not grant to you any right or license to +commercialize, distribute or use OFRAK, Derivatives, OFRAK code, OFRAK +analysis, OFRAK-modified binaries, or other OFRAK outputs for any other purpose +whatsoever, including Commercial Use, other than Academic Purposes or +Non-Commercial Use. In the event that you wish to use OFRAK for any other +purpose, including Commercial Use, you need to contact Red Balloon and enter +into a separate OFRAK Pro License, OFRAK Enterprise License or other custom +agreement. Except for the limited rights and licenses expressly granted +hereunder, no other license is granted, no other use is permitted. + +3. Derivatives. To the extent that you prepare or create any Derivatives, +you shall and hereby grant to (a) all users of OFRAK a right and license to +such Derivatives upon the terms and conditions set forth in this Agreement and +(b) Red Balloon a perpetual, fully paid-up, royalty-free, worldwide and +irrevocable, right and license to use, copy, modify, enhance, prepare +derivative works of, distribute, with unlimited right to sublicense, make, have +made, sell, have sold, import, export and otherwise commercialize such +Derivatives. You acknowledge that Red Balloon may, but is not obligated to, +include your Derivatives in, and otherwise incorporate your Derivatives into, +the core OFRAK codebase. In the event that you create Derivatives, you must +(i) retain all copyright and other proprietary rights licenses included in the +original OFRAK code, and any other Derivatives, and (ii) make it clear that you +modified the original version of OFRAK. Red Balloon encourages you to make +your Derivatives available to the community by forking the OFRAK source code +repository on GitHub and publishing your Derivatives on your forked repository, +but you are not required to do so. You represent and warrant that you have +sufficient rights to any Derivatives and are legally entitled to grant the +above rights and licenses. If you are an individual and your +employer(s)/institution(s) have rights to intellectual property that you create +that includes your Derivatives, you represent that you have received permission +to make and contribute Derivatives on behalf of that employer/institution. + +4. Ownership; Restrictions. Except as expressly and unambiguously set +forth herein, Red Balloon and its licensors and contributors retain all right, +title and interest in and to OFRAK, Derivatives prepared or created by Red +Balloon, all copies, modifications and derivative works thereof, including +without limitation, all rights to patent, copyright, trade secret and other +proprietary or intellectual property rights related to any of the foregoing. +To the extent that Licensee creates any Derivatives, subject to the rights and +licenses granted herein, Licensee retains ownership of all right, title and +interest in and to such Derivatives prepared or created by Licensee, including +without limitation, all intellectual property rights related to any of the +foregoing. Licensee will maintain the copyright notice and any other notices +or identifications that appear on or in OFRAK and any Derivatives or any other +media or documentation that is subject to this OFRAK Pro Agreement. Licensee +will not (and will not allow any third party to): (a) use OFRAK or any +Derivatives, except as expressly permitted in this OFRAK Pro Agreement, (b) +provide, lease, lend, disclose, use for timesharing or service bureau purposes, +or otherwise use or allow others to use for the benefit of any third party, +OFRAK, (c) possess or use OFRAK, or allow the transfer, transmission, export, +or re-export of OFRAK or portion thereof in violation of any export control +laws or regulations administered by the U.S. Commerce Department, U.S. Treasury +Department’s Office of Foreign Assets Control, or any other government agency, +(d) use OFRAK in any way that violates any applicable law, rule or regulation +or for any illegal use or activity, or (e) seek any patent or other +intellectual property rights or protections over or in connection with OFRAK or +any Derivatives that Licensee prepares or creates. + +5. Feedback. In addition to Derivatives, you may, from time to time and +in your sole discretion, make suggestions for changes, modifications or +improvements to OFRAK ("Feedback"). Red Balloon shall have an irrevocable, +perpetual, worldwide, sublicenseable, transferable, full paid-up, royalty free +right and license to use, distribute and otherwise exploit all Feedback for any +purpose. + +6. No Cost License. OFRAK and any Derivatives provided pursuant to this +Agreement shall be provided during the Term at no charge to you. 7. +Services. No training or support services are provided under this Agreement. +Red Balloon may in its discretion respond to support inquiries through Red +Balloon’s support channels, such as Slack. + +8. Term and Termination. This Agreement shall commence upon the initial +download of OFRAK and shall continue until and unless terminated as set forth +herein (the "Term"). This Agreement may be terminated by Red Balloon +immediately upon notice to you in the event that you breach any term or +condition of this Agreement. Upon any termination, you shall immediately cease +all use of OFRAK. This sentence and the following provisions will survive +termination: 1, 3 - 5 and 9 - 12. Termination is not an exclusive remedy and +all other remedies will remain available. + +9. Warranty Disclaimer. The parties acknowledge that OFRAK is provided +"AS IS" and may not be functional on any machine or in any environment. +NEITHER RED BALLOON NOR ANY CONTRIBUTOR OF ANY DERIVATIVES MAKE ANY WARRANTIES, +EXPRESS OR IMPLIED, EITHER IN FACT OR BY OPERATION OF LAW, STATUTORY OR +OTHERWISE, AND RED BALLOON AND ANY CONTRIBUTOR OF ANY DERIVATIVES EXPRESSLY +EXCLUDES AND DISCLAIMS ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE, TITLE, ACCURACY, FREEDOM FROM ERRORS, FREEDOM FROM +PROGRAMMING DEFECTS, NONINTERFERENCE AND NONINFRINGEMENT, AND ALL IMPLIED +WARRANTIES ARISING OUT OF COURSE OF DEALING, COURSE OF PERFORMANCE AND USAGE OF +TRADE. THIS AGREEMENT IS NOT INTENDED FOR USE OF OFRAK IN HAZARDOUS +ENVIRONMENTS REQUIRING FAIL-SAFE PERFORMANCE WHERE THE FAILURE OF OFRAK COULD +LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SIGNIFICANT PHYSICAL OR +ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). USE OF OFRAK IN HIGH RISK +ACTIVITIES IS NOT AUTHORIZED PURSUANT TO THIS AGREEMENT. THE PARTIES AGREE +THAT THIS SECTION 9 REPRESENTS A REASONABLE ALLOCATION OF RISK AND THAT RED +BALLOON WOULD NOT PROCEED IN THE ABSENCE OF SUCH ALLOCATION. + +10. Limitations. NEITHER RED BALLOON NOR ANY CONTRIBUTOR OF DERIVATIVES +SHALL BE RESPONSIBLE OR LIABLE WITH RESPECT TO ANY SUBJECT MATTER OF THIS +AGREEMENT OR TERMS AND CONDITIONS RELATED THERETO UNDER ANY CONTRACT, +NEGLIGENCE, STRICT LIABILITY OR OTHER THEORY (A) FOR LOSS OR INACCURACY OF +DATA, OR COST OF PROCUREMENT OF SUBSTITUTE GOODS, SERVICES OR TECHNOLOGY; (B) +FOR ANY DIRECT, INDIRECT, PUNITIVE, INCIDENTAL, RELIANCE, SPECIAL, EXEMPLARY OR +CONSEQUENTIAL DAMAGES INCLUDING, BUT NOT LIMITED TO, LOSS OF REVENUES AND LOSS +OF PROFITS TO LICENSEE OR ANY THIRD PARTIES; (C) FOR ANY MATTER BEYOND ITS +REASONABLE CONTROL OR (D) FOR USE YOU OR OTHERS MAY MAKE OF OFRAK, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +11. Indemnification. You agree that (a) Red Balloon and any contributors +shall have no liability whatsoever for your use of OFRAK or any Derivatives and +(b) you shall indemnify, and hold harmless, and (upon request) defend Red +Balloon and any other user or contributor from and against any and all claims, +damages, liabilities, losses, and costs (including reasonable attorneys’ fees) +suffered or incurred by such party which arise from or relate to your (i) use +of OFRAK or Derivatives, or (ii) breach of this Agreement. + +12. Miscellaneous. Neither this Agreement nor the licenses granted +hereunder are assignable or transferable by you; any attempt to do so shall be +void. Red Balloon may assign this Agreement in whole or in part. Any notice, +report, approval or consent required or permitted hereunder shall be in +writing. The provisions hereof are for the benefit of the parties only and not +for any other person or entity. If any provision of this Agreement shall be +adjudged by any court of competent jurisdiction to be unenforceable or invalid, +that provision shall be limited or eliminated to the minimum extent necessary +so that this Agreement shall otherwise remain in full force and effect and +enforceable. This Agreement shall be deemed to have been made in, and shall be +construed pursuant to the laws of the State of New York, without regard to +conflicts of laws provisions thereof, and without regard to the United Nations +Convention on the International Sale of Goods or the Uniform Computer +Information Transactions Act. Any waivers or amendments shall be effective only +if made in writing. This Agreement is the complete and exclusive statement of +the mutual understanding of the parties and supersedes and cancels all previous +written and oral agreements and communications relating to the subject matter +of this Agreement. + + +OFRAK PRO LICENSE AGREEMENT +Version 1.0 +Effective: July 22, 2024 + +RED BALLOON SECURITY, INC., A DELAWARE CORPORATION, WITH AN ADDRESS AT 639 11TH +AVENUE, 4TH FLOOR, NEW YORK, NY 10036, USA ("RED BALLOON") LICENSES OFRAK AND +RELATED DOCUMENTATION PURSUANT TO THE OFRAK PRO LICENSE AGREEMENT (COLLECTIVELY +WITH THE REGISTRATION FORM, THIS "AGREEMENT"). READ THIS AGREEMENT CAREFULLY +BEFORE ACCESSING, INSTALLING, COPYING AND USING OFRAK UNDER THE OFRAK PRO +AGREEMENT. BY TYPING "I AGREE" ON THE REGISTRATION FORM, OR OTHERWISE +ACCESSING, INSTALLING, COPYING OR OTHERWISE USING OFRAK, YOU ("LICENSEE") AGREE +THAT THE REGISTRATION FORM SHALL BE DEEMED TO BE MUTUALLY EXECUTED AND THE +REGISTRATION FORM SHALL BE INCORPORATED INTO AND BECOME A MATERIAL PART OF THE +OFRAK PRO LICENSE AGREEMENT BETWEEN LICENSEE AND RED BALLOON LOCATED AT +https://ofrak.com/docs/license.html. YOU REPRESENT THAT YOU ARE AUTHORIZED TO +ACCEPT THIS AGREEMENT ON BEHALF OF LICENSEE. IF LICENSEE DOES NOT AGREE TO THE +FOREGOING TERMS AND CONDITIONS, DO NOT TYPE "I AGREE", OR OTHERWISE ACCESS, +INSTALL, COPY OR USE OFRAK. + +1. Definitions. + +1.1 "OFRAK" consists of (a) the source code repository for OFRAK, which can +be found at https://github.com/red-balloon-security/ofrak; (b) the following +Python packages, which are also available via PyPI, the Python Package Index: +ofrak, ofrak_components, ofrak_io, ofrak_type, ofrak_patch_maker, ofrak_angr, +ofrak_binary_ninja, ofrak_ghidra; (c) the OFRAK graphical user interface (GUI); +and (d) OFRAK documentation. OFRAK includes all updates, improvements, APIs and +add-ons provided by Red Balloon with respect thereto that Red Balloon makes +available to Licensee under this OFRAK Pro Agreement. OFRAK is presently made +available in three formats: (i) source code repository, (ii) PyPI Packages and +(iii) Docker images with dependencies preinstalled. + +1.2 "Academic Purposes" means use within a non-profit academic institution +by its then-current faculty and students for the purposes of non-profit +scholarly research, classroom and education, and not any other use (including +without limitation, directly or indirectly in connection with any commercial +activity such as, for example, sponsored research or consulting services). + +1.3 "Authorized User(s)" means any user(s) named in the license file. + +1.4 "Commercial Use" means any use other than Academic Purposes or Limited +Commercial Use, and any use other than by an Authorized User, including without +limitation redistributing the OFRAK code (including Derivatives), OFRAK +analysis, OFRAK-modified binaries, or other OFRAK outputs for use outside of a +consulting engagement of Licensee or volume redistribution of any +OFRAK-modified binaries. + +1.5 "Derivatives" means any modifications, additions, enhancements, or +derivative works of OFRAK or any component thereof. For purposes of this OFRAK +Pro Agreement, Derivatives shall not include works that remain separable from, +or merely link to, the interfaces of OFRAK or any Derivatives. + +1.6 "Limited Commercial Use" means internal business use by any Authorized +User of OFRAK during the term specified in the license file (the "Term"), which +includes use of OFRAK in exchange for monetary and other consideration (such as +security research purposes) and redistribution of (i) OFRAK analysis, (ii) +other OFRAK outputs, and (iii) OFRAK-modified binaries to an end-user for such +end user’s internal use, in each case in connection with consulting engagements +entered into between Licensee and such end user. For clarity, neither Licensee +nor the end user shall have the right to further redistribute OFRAK-modified +binaries outside of such end-user. + +2. License. Subject to the terms and conditions of this OFRAK Pro +Agreement, Red Balloon grants to Licensee a nonexclusive, nonsublicensable, +nontransferable, royalty-free, limited license during the term specified in the +license file ("Term") to install, use, copy, modify and create Derivatives of +OFRAK only by Authorized Users (a) for Academic Purposes, (b) for Limited +Commercial Use and (c) to share Derivatives (i) publicly within the community +(via publicly available forks on GitHub.com), (ii) for an Academic Purpose, and +(iii) with Red Balloon, for the purposes stated in this OFRAK Pro Agreement. +For clarity, the foregoing license does not grant to Licensee any other right +or license to commercialize, distribute or use OFRAK for any other purpose +whatsoever, including Commercial Use. In the event that Licensee wishes to use +OFRAK for any other purpose, including Commercial Use, Licensee needs to +contact Red Balloon and enter into a separate OFRAK Enterprise License or other +custom agreement. Except for the limited rights and licenses expressly granted +hereunder, no other license is granted, no other use is permitted. + +3. Derivatives. To the extent that Licensee prepares or creates any +Derivatives, Licensee shall and hereby grant to (a) all users of OFRAK a right +and license to such Derivatives upon the terms and conditions set forth in the +OFRAK Community License, located at https://ofrak.com/docs/license.html and (b) +Red Balloon a perpetual, fully paid-up, royalty-free, worldwide and +irrevocable, right and license to use, copy, modify, enhance, prepare +derivative works of, distribute, with unlimited right to sublicense, make, have +made, sell, have sold, import, export and otherwise commercialize such +Derivatives. Licensee acknowledges that Red Balloon may, but is not obligated +to, include Licensee’s Derivatives in, and otherwise incorporate Licensee’s +Derivatives into, the core OFRAK codebase. In the event that Licensee creates +Derivatives, Licensee must (i) retain all copyright and other proprietary +rights licenses included in the original OFRAK code, and any other Derivatives, +and (ii) make it clear that Licensee modified the original version of OFRAK. +Red Balloon encourages Licensee to make Licensee’s Derivatives available to the +community by forking the OFRAK source code repository on GitHub and publishing +Licensee’s Derivatives on Licensee’s forked repository, but Licensee is not +required to do so. Licensee represents and warrants that Licensee has +sufficient rights to any Derivatives and is legally entitled to grant the above +rights and licenses. + +4. Ownership; Restrictions. Except as expressly and unambiguously set +forth herein, Red Balloon and its licensors and contributors retain all right, +title and interest in and to OFRAK, Derivatives prepared or created by Red +Balloon, all copies, modifications and derivative works thereof, including +without limitation, all rights to patent, copyright, trade secret and other +proprietary or intellectual property rights related to any of the foregoing. +To the extent that Licensee creates any Derivatives, subject to the rights and +licenses granted herein, Licensee retains ownership of all right, title and +interest in and to such Derivatives prepared or created by Licensee, including +without limitation, all intellectual property rights related to any of the +foregoing. Licensee will maintain the copyright notice and any other notices +or identifications that appear on or in OFRAK and any Derivatives or any other +media or documentation that is subject to this OFRAK Pro Agreement. Licensee +will not (and will not allow any third party to): (a) use OFRAK or any +Derivatives, except as expressly permitted in this OFRAK Pro Agreement, (b) +provide, lease, lend, disclose, use for timesharing or service bureau purposes, +or otherwise use or allow others to use for the benefit of any third party, +OFRAK, (c) possess or use OFRAK, or allow the transfer, transmission, export, +or re-export of OFRAK or portion thereof in violation of any export control +laws or regulations administered by the U.S. Commerce Department, U.S. Treasury +Department’s Office of Foreign Assets Control, or any other government agency, +(d) use OFRAK in any way that violates any applicable law, rule or regulation +or for any illegal use or activity, or (e) seek any patent or other +intellectual property rights or protections over or in connection with OFRAK or +any Derivatives that Licensee prepares or creates. + +5. Feedback. In addition to Derivatives prepared or created by Licensee, +Licensee may, from time to time and in Licensee’s sole discretion, make +suggestions for changes, modifications or improvements to OFRAK ("Feedback"). +Red Balloon shall have an irrevocable, perpetual, worldwide, sublicenseable, +transferable, full paid-up, royalty free right and license to use, distribute +and otherwise exploit all Feedback for any purpose. + +6. Fees. Licensee agrees to pay Red Balloon all fees and other charges in +the amounts and at the times specified by Red Balloon in writing (without +deduction, set-off, or counterclaim). + +7. Services. No training or support services are provided under this +OFRAK Pro Agreement. Red Balloon may in its discretion respond to support +inquiries through Red Balloon’s support channels, such as Slack. + +8. Term and Termination. This OFRAK Pro Agreement shall commence upon the +date of license issue set forth in the license file and shall continue for the +Term unless terminated as set forth herein. This OFRAK Pro Agreement may be +terminated by Red Balloon immediately upon notice to Licensee in the event that +Licensee breaches any term or condition of this OFRAK Pro Agreement. Upon +expiration or any termination, Licensee shall immediately cease all use of +OFRAK. This sentence and the following provisions will survive termination: 1, +3 - 5 and 9 - 12. Termination is not an exclusive remedy and all other remedies +will remain available. + +9. Warranty Disclaimer. The parties acknowledge that OFRAK is provided +"AS IS" and may not be functional on any machine or in any environment. +NEITHER RED BALLOON NOR ANY CONTRIBUTOR OF ANY DERIVATIVES MAKE ANY WARRANTIES, +EXPRESS OR IMPLIED, EITHER IN FACT OR BY OPERATION OF LAW, STATUTORY OR +OTHERWISE, AND RED BALLOON AND ANY CONTRIBUTOR OF ANY DERIVATIVES EXPRESSLY +EXCLUDES AND DISCLAIMS ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE, TITLE, ACCURACY, FREEDOM FROM ERRORS, FREEDOM FROM +PROGRAMMING DEFECTS, NONINTERFERENCE AND NONINFRINGEMENT, AND ALL IMPLIED +WARRANTIES ARISING OUT OF COURSE OF DEALING, COURSE OF PERFORMANCE AND USAGE OF +TRADE. THIS OFRAK PRO AGREEMENT IS NOT INTENDED FOR USE OF OFRAK IN HAZARDOUS +ENVIRONMENTS REQUIRING FAIL-SAFE PERFORMANCE WHERE THE FAILURE OF OFRAK COULD +LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SIGNIFICANT PHYSICAL OR +ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). USE OF OFRAK IN HIGH RISK +ACTIVITIES IS NOT AUTHORIZED PURSUANT TO THIS OFRAK PRO AGREEMENT. THE PARTIES +AGREE THAT THIS SECTION 9 REPRESENTS A REASONABLE ALLOCATION OF RISK AND THAT +RED BALLOON WOULD NOT PROCEED IN THE ABSENCE OF SUCH ALLOCATION. +10. Limitations. NEITHER RED BALLOON NOR ANY CONTRIBUTOR OF DERIVATIVES +SHALL BE RESPONSIBLE OR LIABLE WITH RESPECT TO ANY SUBJECT MATTER OF THIS OFRAK +PRO AGREEMENT OR TERMS AND CONDITIONS RELATED THERETO UNDER ANY CONTRACT, +NEGLIGENCE, STRICT LIABILITY OR OTHER THEORY (A) FOR LOSS OR INACCURACY OF +DATA, OR COST OF PROCUREMENT OF SUBSTITUTE GOODS, SERVICES OR TECHNOLOGY; (B) +FOR ANY DIRECT, INDIRECT, PUNITIVE, INCIDENTAL, RELIANCE, SPECIAL, EXEMPLARY OR +CONSEQUENTIAL DAMAGES INCLUDING, BUT NOT LIMITED TO, LOSS OF REVENUES AND LOSS +OF PROFITS TO LICENSEE OR ANY THIRD PARTIES; (C) FOR ANY MATTER BEYOND ITS +REASONABLE CONTROL OR (D) FOR USE THAT LICENSEE OR OTHERS MAY MAKE OF OFRAK, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +11. Indemnification. Licensee agrees that (a) Red Balloon and any +contributors shall have no liability whatsoever for Licensee’s use of OFRAK or +any Derivatives and (b) Licensee shall indemnify, and hold harmless, and (upon +request) defend Red Balloon and any other user or contributor from and against +any and all claims, damages, liabilities, losses, and costs (including +reasonable attorneys’ fees) suffered or incurred by such party which arise from +or relate to Licensee’s (i) use of OFRAK or Derivatives, or (ii) breach of this +OFRAK Pro Agreement. + +12. Miscellaneous. Neither this OFRAK Pro Agreement nor the licenses +granted hereunder are assignable or transferable by Licensee; any attempt to do +so shall be void. Red Balloon may assign this OFRAK Pro Agreement in whole or +in part. Any notice, report, approval or consent required or permitted +hereunder shall be in writing. The provisions hereof are for the benefit of +the parties only and not for any other person or entity. If any provision of +this OFRAK Pro Agreement shall be adjudged by any court of competent +jurisdiction to be unenforceable or invalid, that provision shall be limited or +eliminated to the minimum extent necessary so that this OFRAK Pro Agreement +shall otherwise remain in full force and effect and enforceable. This OFRAK +Pro Agreement shall be deemed to have been made in, and shall be construed +pursuant to the laws of the State of New York, without regard to conflicts of +laws provisions thereof, and without regard to the United Nations Convention on +the International Sale of Goods or the Uniform Computer Information +Transactions Act. Any waivers or amendments shall be effective only if made in +writing. This OFRAK Pro Agreement is the complete and exclusive statement of +the mutual understanding of the parties and supersedes and cancels all previous +written and oral agreements and communications relating to the subject matter +of this OFRAK Pro Agreement. In the event of any conflict between this OFRAK +Pro License Agreement and the OFRAK Community License Agreement, located at +https://ofrak.com/docs/license.html, this OFRAK Pro Agreement shall control. diff --git a/disassemblers/ofrak_pyghidra/Makefile b/disassemblers/ofrak_pyghidra/Makefile new file mode 100644 index 000000000..d0a7ca34e --- /dev/null +++ b/disassemblers/ofrak_pyghidra/Makefile @@ -0,0 +1,12 @@ +PYTHON=python3 +PIP=pip3 + +install: + $(PIP) install . + +develop: + $(PIP) install -e .[test] + +test: + $(PYTHON) -m pytest --cov=ofrak_pyghidra --cov-report=term-missing ofrak_pyghidra_test + fun-coverage --cov-fail-under=100 diff --git a/disassemblers/ofrak_pyghidra/ofrak_pyghidra/__init__.py b/disassemblers/ofrak_pyghidra/ofrak_pyghidra/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/disassemblers/ofrak_pyghidra/ofrak_pyghidra/__main__.py b/disassemblers/ofrak_pyghidra/ofrak_pyghidra/__main__.py new file mode 100644 index 000000000..41c3fe452 --- /dev/null +++ b/disassemblers/ofrak_pyghidra/ofrak_pyghidra/__main__.py @@ -0,0 +1,40 @@ +import argparse +import time +import json + +from ofrak_pyghidra.standalone.pyghidra_analysis import unpack + + +def _analyze_binary(args): + start = time.time() + res = unpack(args.infile, args.decompile) + with open(args.outfile, "w") as fh: + json.dump(res, fh, indent=4) + print(f"PyGhidra analysis took {time.time() - start} seconds") + + +parser = argparse.ArgumentParser(description="Run PyGhidra scripts and OFRAK Components.") +command_parser = parser.add_subparsers() + +start_parser = command_parser.add_parser("analyze", description="Start the OFRAK Ghidra server") +start_parser.set_defaults(func=_analyze_binary) +start_parser.add_argument( + "--infile", "-i", type=str, required=True, help="The binary to be analyzed." +) +start_parser.add_argument("--outfile", "-o", type=str, required=True, help="The output json file.") +start_parser.add_argument( + "--decompile", + "-d", + action="store_true", + required=False, + default=False, + help="decompile functions in cache", +) + + +if __name__ == "__main__": + args = parser.parse_args() + if hasattr(args, "func"): + args.func(args) + else: + parser.print_usage() diff --git a/disassemblers/ofrak_pyghidra/ofrak_pyghidra/components/__init__.py b/disassemblers/ofrak_pyghidra/ofrak_pyghidra/components/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/disassemblers/ofrak_pyghidra/ofrak_pyghidra/components/pyghidra_components.py b/disassemblers/ofrak_pyghidra/ofrak_pyghidra/components/pyghidra_components.py new file mode 100644 index 000000000..f48d09115 --- /dev/null +++ b/disassemblers/ofrak_pyghidra/ofrak_pyghidra/components/pyghidra_components.py @@ -0,0 +1,112 @@ +from ofrak.core import * +from tempfile import TemporaryDirectory +import os + + +from ofrak_cached_disassembly.components.cached_disassembly import CachedAnalysisStore +from ofrak_cached_disassembly.components.cached_disassembly_unpacker import ( + CachedCodeRegionUnpacker, + CachedComplexBlockUnpacker, + CachedBasicBlockUnpacker, + CachedCodeRegionModifier, + CachedDecompilationAnalyzer, +) +from ofrak_pyghidra.standalone.pyghidra_analysis import unpack + + +_GHIDRA_AUTO_LOADABLE_FORMATS = [Elf, Ihex, Pe] + + +@dataclass +class PyGhidraAutoLoadProject(ResourceView): + pass + + +@dataclass +class PyGhidraProject(ResourceView): + pass + + +class PyGhidraAnalysisIdentifier(Identifier): + """ + Component to identify resources to analyze with Ghidra. If this component is discovered, + it will tag all [Program][ofrak.core.program.Program]s as GhidraProjects + """ + + id = b"GhidraAnalysisIdentifier" + targets = (Program,) + + async def identify(self, resource: Resource, config=None): + for tag in _GHIDRA_AUTO_LOADABLE_FORMATS: + if resource.has_tag(tag): + resource.add_tag(PyGhidraAutoLoadProject) + + +@dataclass +class PyGhidraUnpackerConfig(ComponentConfig): + unpack_complex_blocks: bool + + +class PyGhidraAnalysisStore(CachedAnalysisStore): + pass + + +class CachedCodeRegionModifier(CachedCodeRegionModifier): + pass + + +class PyGhidraAutoAnalyzer(Analyzer[None, PyGhidraProject]): + id = b"PyGhidraAutoAnalyzer" + + targets = (PyGhidraAutoLoadProject,) + outputs = (PyGhidraProject,) + + def __init__( + self, + resource_factory: ResourceFactory, + data_service: DataServiceInterface, + resource_service: ResourceServiceInterface, + analysis_store: PyGhidraAnalysisStore, + ): + super().__init__(resource_factory, data_service, resource_service) + self.analysis_store = analysis_store + + async def analyze(self, resource: Resource, config=None): + with TemporaryDirectory() as tempdir: + program_file = os.path.join(tempdir, "program") + await resource.flush_data_to_disk(program_file) + self.analysis_store.store_analysis(resource.get_id(), unpack(program_file, False)) + program_attributes = await resource.analyze(ProgramAttributes) + self.analysis_store.store_program_attributes(resource.get_id(), program_attributes) + return PyGhidraProject() + + +class PyGhidraCodeRegionUnpacker(CachedCodeRegionUnpacker): + id = b"PyGhidraCodeRegionUnpacker" + + async def unpack(self, resource: Resource, config=None): + program_r = await resource.get_only_ancestor(ResourceFilter.with_tags(Program)) + if not self.analysis_store.id_exists(program_r.get_id()): + await program_r.run(PyGhidraAutoAnalyzer) + return await super().unpack(resource, config) + + +class PyGhidraComplexBlockUnpacker(CachedComplexBlockUnpacker): + id = b"PyGhidraComplexBlockUnpacker" + + +class PyGhidraBasicBlockUnpacker(CachedBasicBlockUnpacker): + id = b"PyGhidraBasicBlockUnpacker" + + +class PyGhidraDecompilationAnalyzer(CachedDecompilationAnalyzer): + id = b"PyGhidraDecompilationAnalyzer" + + async def analyze(self, resource: Resource, config=None): + program_r = await resource.get_only_ancestor(ResourceFilter.with_tags(Program)) + if not self.analysis_store.get_analysis(program_r.get_id())["metadata"]["decompiled"]: + with TemporaryDirectory() as tempdir: + program_file = os.path.join(tempdir, "program") + await program_r.flush_data_to_disk(program_file) + self.analysis_store.store_analysis(program_r.get_id(), unpack(program_file, True)) + return await super().analyze(resource, config) diff --git a/disassemblers/ofrak_pyghidra/ofrak_pyghidra/standalone/pyghidra_analysis.py b/disassemblers/ofrak_pyghidra/ofrak_pyghidra/standalone/pyghidra_analysis.py new file mode 100644 index 000000000..9170f5858 --- /dev/null +++ b/disassemblers/ofrak_pyghidra/ofrak_pyghidra/standalone/pyghidra_analysis.py @@ -0,0 +1,372 @@ +import logging + +import hashlib +import pyghidra +import argparse +import time +import re +import json + + +def _parse_offset(java_object): + """ + This parses the offset as a big int + """ + return int(str(java_object.getOffsetAsBigInteger())) + + +def unpack(program_file, decompiled): + with pyghidra.open_program(program_file) as flat_api: + main_dictionary = {} + code_regions = _unpack_program(flat_api) + main_dictionary["metadata"] = {} + main_dictionary["metadata"]["backend"] = "ghidra" + main_dictionary["metadata"]["decompiled"] = decompiled + with open(program_file, "rb") as fh: + data = fh.read() + md5_hash = hashlib.md5(data) + main_dictionary["metadata"]["hash"] = md5_hash.digest().hex() + for code_region in code_regions: + seg_key = f"seg_{code_region['virtual_address']}" + main_dictionary[seg_key] = code_region + func_cbs = _unpack_code_region(code_region, flat_api) + code_region["children"] = [] + for func, cb in func_cbs: + cb_key = f"func_{cb['virtual_address']}" + code_region["children"].append(cb_key) + if decompiled: + decompilation = _decompile(func, flat_api) + cb["decompilation"] = decompilation + basic_blocks, data_words = _unpack_complex_block(func, flat_api) + cb["children"] = [] + for block, bb in basic_blocks: + if bb["size"] == 0: + raise Exception(f"Basic block 0x{bb['virtual_address']:x} has no size") + + if ( + bb["virtual_address"] < cb["virtual_address"] + or (bb["virtual_address"] + bb["size"]) > cb["virtual_address"] + cb["size"] + ): + logging.warning( + f"Basic Block 0x{bb['virtual_address']:x} does not fall within " + f"complex block {hex(cb['virtual_address'])}-{hex(cb['virtual_address'] + cb['size'])}" + ) + continue + bb_key = f"bb_{bb['virtual_address']}" + instructions = _unpack_basic_block(block, flat_api) + bb["children"] = [] + for instruction in instructions: + instr_key = f"instr_{instruction['virtual_address']}" + bb["children"].append(instr_key) + main_dictionary[instr_key] = instruction + cb["children"].append(bb_key) + main_dictionary[bb_key] = bb + for dw in data_words: + if ( + dw["virtual_address"] < cb["virtual_address"] + or (dw["virtual_address"] + dw["size"]) > cb["virtual_address"] + cb["size"] + ): + logging.warning( + f"Data Word 0x{dw['virtual_address']:x} does not fall within " + f"complex block {hex(cb['virtual_address'])}-{hex(cb['virtual_address'] + cb['size'])}" + ) + continue + dw_key = f"dw_{dw['virtual_address']}" + cb["children"].append(dw_key) + main_dictionary[dw_key] = dw + main_dictionary[cb_key] = cb + return main_dictionary + + +def _unpack_program(flat_api): + ghidra_code_regions = [] + for memory_block in flat_api.getMemoryBlocks(): + is_execute = False + if memory_block.isExecute(): + is_execute = True + vaddr = _parse_offset(memory_block.getStart()) + ghidra_code_regions.append( + {"virtual_address": vaddr, "size": memory_block.getSize(), "executable": is_execute} + ) + return ghidra_code_regions + + +def _unpack_code_region(code_region, flat_api): + functions = [] + start_address = ( + flat_api.getAddressFactory() + .getDefaultAddressSpace() + .getAddress(hex(code_region["virtual_address"])) + ) + end_address = ( + flat_api.getAddressFactory() + .getDefaultAddressSpace() + .getAddress(hex(code_region["virtual_address"] + code_region["size"])) + ) + func = flat_api.getFunctionAt(start_address) + if func is None: + func = flat_api.getFunctionAfter(start_address) + if func is None: + return functions + + while func is not None and end_address.subtract(func.getEntryPoint()) > 0: + virtual_address = _parse_offset(func.getEntryPoint()) + start = _parse_offset(func.getEntryPoint()) + end, _ = _get_last_address(func, flat_api) + if end is not None: + cb = { + "virtual_address": virtual_address, + "size": end - start, + "name": func.getName(), + } + functions.append((func, cb)) + func = flat_api.getFunctionAfter(func) + return functions + + +def _unpack_complex_block(func, flat_api): + from ghidra.program.model.block import BasicBlockModel + + bb_model = BasicBlockModel(flat_api.getCurrentProgram()) + bbs = [] + bb_iter = bb_model.getCodeBlocksContaining(func.getBody(), flat_api.monitor) + for block in bb_iter: + address_range = block.getAddressRanges().next() + start = _parse_offset(address_range.getMinAddress()) + size = address_range.getLength() + exit_vaddr = None + is_exit_point = True + iterator = block.getDestinations(flat_api.monitor) + ghidra_block = block + while block is not None: + block = iterator.next() + if block is None: + break + successor_bb = block.getDestinationBlock() + successor_bb_address_range = successor_bb.getAddressRanges().next() + if _parse_offset(successor_bb_address_range.getMinAddress()) >= _parse_offset( + func.getBody().getMinAddress() + ) and _parse_offset(successor_bb_address_range.getMaxAddress()) <= _parse_offset( + func.getBody().getMaxAddress() + ): + is_exit_point = False + if ( + exit_vaddr is None + or _parse_offset(successor_bb_address_range.getMinAddress()) + == _parse_offset(address_range.getMaxAddress()) + 1 + ): + exit_vaddr = _parse_offset(successor_bb_address_range.getMinAddress()) + from java.math import BigInteger + + instruction_mode = "none" + tmode_register = flat_api.getCurrentProgram().getRegister("TMode") + if tmode_register is not None: + function_mode = ( + flat_api.getCurrentProgram() + .getProgramContext() + .getRegisterValue(tmode_register, address_range.getMinAddress()) + ) + if function_mode.getUnsignedValueIgnoreMask().equals(BigInteger.ONE): + instruction_mode = "thumb" + vle_register = flat_api.getCurrentProgram().getRegister("vle") + if vle_register is not None: + function_mode = ( + flat_api.getCurrentProgram() + .getProgramContext() + .getRegisterValue(vle_register, address_range.getMinAddress()) + ) + if function_mode.getUnsignedValueIgnoreMask().equals(BigInteger.ONE): + instruction_mode = "vle" + if is_exit_point: + exit_vaddr = None + + bb = { + "virtual_address": start, + "size": size, + "mode": instruction_mode, + "is_exit_point": is_exit_point, + "exit_vaddr": exit_vaddr, + } + bbs.append((ghidra_block, bb)) + + end_data_addr, end_code_addr = _get_last_address(func, flat_api) + + dws = [] + data = flat_api.getDataAt(end_code_addr) + while data is not None and _parse_offset(data.getAddress()) <= end_data_addr: + num_words = 1 + word_size = data.getLength() + if word_size == 1: + size_flag = "B" + elif word_size == 2: + size_flag = "H" + elif word_size == 4: + size_flag = "L" + elif word_size == 8: + size_flag = "Q" + else: + size_flag = "B" + num_words = word_size + word_size = 1 + + refs = [ + _parse_offset(ref.getFromAddress()) + for ref in flat_api.getReferencesTo(data.getAddress()) + ] + for word in range(num_words): + dws.append( + { + "virtual_address": _parse_offset(data.getAddress()) + word, + "size": data.getLength(), + "format_string": size_flag, + "xrefs_to": tuple(refs), + } + ) + data = flat_api.getDataAfter(data) + + return bbs, dws + + +def _unpack_basic_block(block, flat_api): + from java.math import BigInteger + from ghidra.program.model.symbol import RefType + + instructions = [] + address_range = block.getAddressRanges().next() + start = _parse_offset(address_range.getMinAddress()) + size = int(address_range.getLength()) + end = start + size + instr = flat_api.getInstructionAt(address_range.getMinAddress()) + while instr is not None and _parse_offset(instr.getAddress()) < end: + res = [] + ops = [] + regs_read = [] + regs_written = [] + results_objects = instr.getResultObjects() + instr_offset = instr.getAddress() + instr_size = instr.getLength() + mnem = instr.getMnemonicString() + + thumb_register = instr.getRegister("TMode") + instruction_mode = "none" + if thumb_register is not None: + thumb_val = instr.getValue(thumb_register, False) + if thumb_val.equals(BigInteger.ONE): + instruction_mode = "thumb" + else: + vle_register = instr.getRegister("vle") + if vle_register is not None: + vle_val = instr.getValue(vle_register, False) + if vle_val.equals(BigInteger.ONE): + instruction_mode = "vle" + for i in range(int(instr.getNumOperands())): + ops.append(instr.getDefaultOperandRepresentation(i)) + if i != instr.getNumOperands() - 1: + ops.append(", ") + if instr.getOperandRefType(i) == RefType.READ: + regs_read.append(instr.getOpObjects(i)[instr.getOpObjects(i).length - 1].toString()) + if i != instr.getNumOperands() - 1: + regs_read.append(", ") + + if instr.getOperandRefType(i) == RefType.WRITE: + regs_written.append( + instr.getOpObjects(i)[instr.getOpObjects(i).length - 1].toString() + ) + if i != instr.getNumOperands() - 1: + regs_written.append(", ") + + if instr.getOperandRefType(i) == RefType.READ_WRITE: + regs_read.append(instr.getOpObjects(i)[instr.getOpObjects(i).length - 1].toString()) + regs_written.append( + instr.getOpObjects(i)[instr.getOpObjects(i).length - 1].toString() + ) + if i != instr.getNumOperands() - 1: + regs_read.append(", ") + regs_written.append(", ") + results_objects = instr.getResultObjects() + for i in range(len(results_objects)): + res.append(results_objects[i]) + if i != len(results_objects) - 1: + res.append(", ") + vaddr = _parse_offset(instr_offset) + size = int(instr_size) + ops = [op.lower() for op in ops] + operands = "".join(ops) + mnem = str(mnem).lower() + mnem = re.sub("cpy", "mov", mnem) + operands = re.sub("0x[0]+([0-9])", lambda match: f"0x{match.group(1)}", operands) + operands = re.sub(r" \+ -", " - ", operands) + operands = re.sub(r",([^\s])", lambda match: f", {match.group(1)}", operands) + disasm = f"{mnem} {operands}" + instructions.append( + { + "virtual_address": vaddr, + "size": size, + "disassembly": disasm, + "mnemonic": mnem, + "operands": operands, + "mode": instruction_mode, + } + ) + instr = flat_api.getInstructionAfter(instr_offset) + return instructions + + +def _decompile(func, flat_api): + from ghidra.app.decompiler import DecompInterface + from ghidra.util.task import TaskMonitor + + ifc = DecompInterface() + ifc.openProgram(flat_api.getCurrentProgram()) + res = ifc.decompileFunction(func, 0, TaskMonitor.DUMMY) + if not res.decompileCompleted(): + decomp = "Unable to decompile :(" + return + decomp = res.getDecompiledFunction().getC() + return decomp + + +def _get_last_address(func, flat_api): + end_addr = None + address_iter = func.getBody().getAddressRanges() + nextFunc = flat_api.getFunctionAfter(func) + if nextFunc is None: + nextFuncAddr = func.getBody().getMaxAddress() + else: + nextFuncAddr = nextFunc.getEntryPoint() + + while address_iter.hasNext(): + range = address_iter.next() + if range.getMaxAddress().subtract(nextFuncAddr) > 0: + break + end_addr = range.getMaxAddress() + last_insn = flat_api.getInstructionAt(end_addr) + if last_insn is None: + last_insn = flat_api.getInstructionBefore(end_addr) + if last_insn is None: + end_addr = end_addr.add(1) + elif func.equals(flat_api.getFunctionContaining(last_insn.getAddress())): + end_addr = last_insn.getAddress().add(last_insn.getLength()) + end_code_addr = end_addr + data = flat_api.getDataAt(end_addr) + while data is not None and nextFuncAddr.subtract(data.getAddress()) > 0: + end_addr = data.getAddress().add(data.getLength()) + data = flat_api.getDataAfter(data) + return _parse_offset(end_addr), end_code_addr + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument( + "--infile", "-i", type=str, required=True, help="The binary to be analyzed." + ) + parser.add_argument("--outfile", "-o", type=str, required=True, help="The output json file.") + parser.add_argument( + "--decompile", "-d", type=bool, default=False, help="decompile functions in cache" + ) + args = parser.parse_args() + start = time.time() + res = unpack(args.infile, args.decompile) + with open(args.outfile, "w") as fh: + json.dump(res, fh, indent=4) + print(f"PyGhidra analysis took {time.time() - start} seconds") diff --git a/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/assets/fib b/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/assets/fib new file mode 100755 index 000000000..f163ebaeb --- /dev/null +++ b/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/assets/fib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b382a70a95708b618c93798492c429b772fc5d08493d7b2670fde093336e83f +size 109724 diff --git a/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/assets/fib_thumb b/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/assets/fib_thumb new file mode 100755 index 000000000..bf543f14a --- /dev/null +++ b/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/assets/fib_thumb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1d986a0c86e4034d4b70b2d59cab6de7a836cc001a7d17d8da06e51c26fc419 +size 98024 diff --git a/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/assets/hello.x64.elf b/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/assets/hello.x64.elf new file mode 100755 index 000000000..138e83be8 --- /dev/null +++ b/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/assets/hello.x64.elf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af467282f2c4cc3fd6400edae094bea7ac96dff34d925e44fa49c75024b2d67c +size 75568 diff --git a/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/assets/src/Makefile b/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/assets/src/Makefile new file mode 100644 index 000000000..88e8b02ba --- /dev/null +++ b/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/assets/src/Makefile @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78a03cff955c8b53fb1d78f83d5dea279b4013068650f11ae70369e5ab3c2a0f +size 350 diff --git a/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/assets/src/fib.c b/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/assets/src/fib.c new file mode 100644 index 000000000..3f4133494 --- /dev/null +++ b/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/assets/src/fib.c @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dceb3b515ea092a5b1fdad2014d1f6cce82551b4f64f18274a166207600b424c +size 294 diff --git a/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/conftest.py b/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/conftest.py new file mode 100755 index 000000000..113be758c --- /dev/null +++ b/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/conftest.py @@ -0,0 +1,21 @@ +import pytest +import ofrak_pyghidra + +from test_ofrak.components.hello_world_elf import hello_elf + +pytest_plugins = ["pytest_ofrak.fixtures"] + + +@pytest.fixture(scope="session") +def hello_world_elf() -> bytes: + return hello_elf() + + +@pytest.fixture(autouse=True) +def pyghidra_components(ofrak_injector): + ofrak_injector.discover(ofrak_pyghidra) + + +@pytest.fixture +def test_id(): + return "TEST_JOB" diff --git a/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/test_pyghidra_components.py b/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/test_pyghidra_components.py new file mode 100644 index 000000000..0f62e107c --- /dev/null +++ b/disassemblers/ofrak_pyghidra/ofrak_pyghidra_test/test_pyghidra_components.py @@ -0,0 +1,116 @@ +import os +from typing import Dict +from ofrak.ofrak_context import OFRAKContext +from ofrak_type import InstructionSetMode +import pytest +from ofrak.core import * +from pytest_ofrak.patterns.code_region_unpacker import CodeRegionUnpackAndVerifyPattern +from pytest_ofrak.patterns.complex_block_unpacker import ( + ComplexBlockUnpackerUnpackAndVerifyPattern, + ComplexBlockUnpackerTestCase, +) +from ofrak.core.decompilation import DecompilationAnalysis +from pytest_ofrak.patterns.basic_block_unpacker import BasicBlockUnpackerUnpackAndVerifyPattern +import ofrak_pyghidra + +ASSETS_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "assets")) + + +@pytest.fixture(autouse=True) +def pyghidra_components(ofrak_injector): + ofrak_injector.discover(ofrak_pyghidra) + + +class TestGhidraCodeRegionUnpackAndVerify(CodeRegionUnpackAndVerifyPattern): + pass + + +class TestGhidraComplexBlockUnpackAndVerify(ComplexBlockUnpackerUnpackAndVerifyPattern): + @pytest.fixture + async def expected_results(self, unpack_verify_test_case: ComplexBlockUnpackerTestCase) -> Dict: + if unpack_verify_test_case.binary_md5_digest == "fc7a6b95d993f955bd92f2bef2699dd0": + return self._fixup_test_case_for_pie( + unpack_verify_test_case.expected_results, + pie_base_vaddr=0x10000, + ) + + return unpack_verify_test_case.expected_results + + @pytest.fixture + async def optional_results(self, unpack_verify_test_case: ComplexBlockUnpackerTestCase): + if unpack_verify_test_case.binary_md5_digest == "fc7a6b95d993f955bd92f2bef2699dd0": + return set( + self._fixup_test_case_for_pie( + {vaddr: [] for vaddr in unpack_verify_test_case.optional_results}, + pie_base_vaddr=0x10000, + ).keys() + ) + + return unpack_verify_test_case.optional_results + + +class TestGhidraBasicBlockUnpackAndVerify(BasicBlockUnpackerUnpackAndVerifyPattern): + pass + + +INSTRUCTION_MODE_TEST_CASES = [ + ("fib", InstructionSetMode.NONE), + ("fib_thumb", InstructionSetMode.THUMB), +] + + +@pytest.fixture(params=INSTRUCTION_MODE_TEST_CASES, ids=lambda tc: tc[0]) +async def test_case( + pyghidra_components: None, ofrak_context: OFRAKContext, request +) -> Tuple[Resource, InstructionSetMode]: + binary_name, mode = request.param + binary_path = os.path.join(ASSETS_DIR, binary_name) + resource = await ofrak_context.create_root_resource_from_file(binary_path) + return resource, mode + + +async def test_instruction_mode(test_case: Tuple[Resource, InstructionSetMode]): + root_resource, mode = test_case + await root_resource.unpack_recursively() + instructions = list( + await root_resource.get_descendants_as_view( + Instruction, r_filter=ResourceFilter.with_tags(Instruction) + ) + ) + # Using "any" instead of "all" because not 100% of the basic blocks in a binary compiled with + # "-mthumb" are in THUMB mode. This is testing (de)serialization of Ghidra analysis, + # so all that matters is that we're seeing some instructions of the expected type + assert any(instruction.mode == mode for instruction in instructions), ( + f"None of the instructions in {root_resource.get_id().hex()} had the expected instruction " + f"set mode of {mode.name}." + ) + + +async def test_cached_decompilation(ofrak_context: OFRAKContext): + root_resource = await ofrak_context.create_root_resource_from_file( + os.path.join(os.path.dirname(__file__), "assets/hello.x64.elf") + ) + await root_resource.unpack_recursively( + do_not_unpack=[ + ComplexBlock, + ] + ) + complex_blocks: List[ComplexBlock] = await root_resource.get_descendants_as_view( + ComplexBlock, + r_filter=ResourceFilter( + tags=[ + ComplexBlock, + ] + ), + ) + decomps = [] + for complex_block in complex_blocks: + await complex_block.resource.identify() + ghidra_resource: DecompilationAnalysis = await complex_block.resource.view_as( + DecompilationAnalysis + ) + decomps.append(ghidra_resource.decompilation) + assert len(decomps) == 14 + assert "" not in decomps + assert "main" in " ".join(decomps) + assert "print" in " ".join(decomps) diff --git a/disassemblers/ofrak_pyghidra/pytest.ini b/disassemblers/ofrak_pyghidra/pytest.ini new file mode 100644 index 000000000..2f4c80e30 --- /dev/null +++ b/disassemblers/ofrak_pyghidra/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +asyncio_mode = auto diff --git a/disassemblers/ofrak_pyghidra/requirements.txt b/disassemblers/ofrak_pyghidra/requirements.txt new file mode 100644 index 000000000..d8a8cabc7 --- /dev/null +++ b/disassemblers/ofrak_pyghidra/requirements.txt @@ -0,0 +1 @@ +importlib-resources # A workaround for https://github.com/redballoonsecurity/ofrak/issues/398 diff --git a/disassemblers/ofrak_pyghidra/setup.py b/disassemblers/ofrak_pyghidra/setup.py new file mode 100644 index 000000000..bc1112d4a --- /dev/null +++ b/disassemblers/ofrak_pyghidra/setup.py @@ -0,0 +1,59 @@ +import setuptools +import pkg_resources +from setuptools.command.egg_info import egg_info + + +class egg_info_ex(egg_info): + """Includes license file into `.egg-info` folder.""" + + def run(self): + # don't duplicate license into `.egg-info` when building a distribution + if not self.distribution.have_run.get("install", True): + # `install` command is in progress, copy license + self.mkpath(self.egg_info) + self.copy_file("LICENSE", self.egg_info) + + egg_info.run(self) + + +with open("LICENSE") as f: + license = "".join(["\n", f.read()]) + + +# Should be the same as in build_image.py +def read_requirements(requirements_path): + with open(requirements_path) as requirements_handle: + return [ + str(requirement) + for requirement in pkg_resources.parse_requirements(requirements_handle) + ] + + +setuptools.setup( + name="ofrak_pyghidra", + version="0.1.0", + author="Red Balloon Security", + author_email="ofrak@redballoonsecurity.com", + description="OFRAK PyGhidra Components", + url="", # TODO + packages=setuptools.find_packages(), + package_data={"ofrak_pyghidra": ["py.typed"]}, + install_requires=["ofrak", "ofrak_cached_disassembly"], + classifiers=[ + "Programming Language :: Python :: 3", + "Operating System :: OS Independent", + ], + install_requires=read_requirements("requirements.txt"), + extras_require={ + "test": [ + "fun-coverage==0.2.0", + "pytest", + "pytest-cov", + ] + }, + python_requires=">=3.9", + include_package_data=True, + license=license, + cmdclass={"egg_info": egg_info_ex}, + entry_points={"ofrak.packages": ["ofrak_pyghidra_pkg = ofrak_pyghidra"]}, +) diff --git a/ofrak-pyghidra.yml b/ofrak-pyghidra.yml new file mode 100644 index 000000000..5310f6fa3 --- /dev/null +++ b/ofrak-pyghidra.yml @@ -0,0 +1,14 @@ +registry: "redballoonsecurity/ofrak" +base_image_name: "pyghidra-base" +image_name: "pyghidra" +python_image: "python:3.9-bullseye" +packages_paths: + [ + "ofrak_type", + "ofrak_io", + "ofrak_patch_maker", + "ofrak_core", + "disassemblers/ofrak_pyghidra", + "disassemblers/ofrak_cached_disassembly", + "frontend", + ] diff --git a/ofrak_core/CHANGELOG.md b/ofrak_core/CHANGELOG.md index 1ab1d8d70..cc9d39cdc 100644 --- a/ofrak_core/CHANGELOG.md +++ b/ofrak_core/CHANGELOG.md @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Add support for running on Windows to the `Filesystem` component. ([#521](https://github.com/redballoonsecurity/ofrak/pull/521)) - Add `JavaArchive` resource tag ([#492](https://github.com/redballoonsecurity/ofrak/pull/492)) - Add new method for allocating `.bss` sections using free space ranges that aren't mapped to data ranges. ([#505](https://github.com/redballoonsecurity/ofrak/pull/505)) +- Add PyGhidra support along with a disassembly backend to pull disassembler analysis from a json. ([#556](https://github.com/redballoonsecurity/ofrak/pull/556)) - Add `JavaArchive` resource tag ([#492](https://github.com/redballoonsecurity/ofrak/pull/492)) ### Fixed diff --git a/ofrak_core/pytest_ofrak/patterns/basic_block_unpacker.py b/ofrak_core/pytest_ofrak/patterns/basic_block_unpacker.py index 7914f47c4..fe549f64e 100644 --- a/ofrak_core/pytest_ofrak/patterns/basic_block_unpacker.py +++ b/ofrak_core/pytest_ofrak/patterns/basic_block_unpacker.py @@ -1461,6 +1461,14 @@ class BasicBlockUnpackerTestCase( operands="sp!, {r3 lr}", mode=InstructionSetMode.NONE, ), + Instruction( + virtual_address=0x8038, + size=4, + disassembly="stmdb sp!, {r3, lr}", + mnemonic="stmdb", + operands="sp!, {r3, lr}", + mode=InstructionSetMode.NONE, + ), Instruction( virtual_address=0x8038, size=4, @@ -1525,6 +1533,14 @@ class BasicBlockUnpackerTestCase( operands="sp!, {r3 lr}", mode=InstructionSetMode.NONE, ), + Instruction( + virtual_address=0x8058, + size=4, + disassembly="ldmia sp!, {r3, lr}", + mnemonic="ldmia", + operands="sp!, {r3, lr}", + mode=InstructionSetMode.NONE, + ), Instruction( virtual_address=0x8058, size=4, diff --git a/ofrak_core/test_ofrak/components/assets/cache/Makefile b/ofrak_core/test_ofrak/components/assets/cache/Makefile new file mode 100644 index 000000000..2d0dbdfba --- /dev/null +++ b/ofrak_core/test_ofrak/components/assets/cache/Makefile @@ -0,0 +1,10 @@ +pyghidra: clean + python3 -m ofrak_pyghidra analyze -i ../simple_pie_thumb_bin -o simple_pie_thumb_bin + python3 -m ofrak_pyghidra analyze -i ../simple_arm_gcc.o.elf -o simple_arm_gcc.o.elf + python3 -m ofrak_pyghidra analyze -i ../hello.out -o hello.out + python3 -m ofrak_pyghidra analyze -i ../hello_nosections.out -o hello_nosections.out + python3 -m ofrak_pyghidra analyze -i ../kernel_address_space.out -o kernel_address_space.out + python3 -m ofrak_pyghidra analyze -i ../arm_reloc_relocated.elf -o arm_reloc_relocated.elf + +clean: + rm -f simple_pie_thumb_bin simple_arm_gcc.o.elf hello.out hello_nosections.out kernel_address_space.out arm_reloc_relocated.elf diff --git a/ofrak_core/test_ofrak/components/assets/cache/arm_reloc_relocated.elf b/ofrak_core/test_ofrak/components/assets/cache/arm_reloc_relocated.elf new file mode 100644 index 000000000..66dc2ff73 --- /dev/null +++ b/ofrak_core/test_ofrak/components/assets/cache/arm_reloc_relocated.elf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:578d7ccb5c9054d12bca8ec19dca6b8211b5537533aa765e9848af0df675292f +size 26858 diff --git a/ofrak_core/test_ofrak/components/assets/cache/hello.out b/ofrak_core/test_ofrak/components/assets/cache/hello.out new file mode 100644 index 000000000..e2f8a0830 --- /dev/null +++ b/ofrak_core/test_ofrak/components/assets/cache/hello.out @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f154a4d1ed1a55ee192be2dd343fd49a97572d9500586049c3bf8f53a45414ea +size 44816 diff --git a/ofrak_core/test_ofrak/components/assets/cache/hello_nosections.out b/ofrak_core/test_ofrak/components/assets/cache/hello_nosections.out new file mode 100644 index 000000000..9e257d316 --- /dev/null +++ b/ofrak_core/test_ofrak/components/assets/cache/hello_nosections.out @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d81b6d296747ae97e91cde70bf48a0fb0f6ac5cd85ad4d74c3c50cf461331dbb +size 40905 diff --git a/ofrak_core/test_ofrak/components/assets/cache/kernel_address_space.out b/ofrak_core/test_ofrak/components/assets/cache/kernel_address_space.out new file mode 100644 index 000000000..c022f6db3 --- /dev/null +++ b/ofrak_core/test_ofrak/components/assets/cache/kernel_address_space.out @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ada0d37a40a3d8a37208869c4296cf413c4b9421ac3f7367d0e73752c42edeca +size 2305 diff --git a/ofrak_core/test_ofrak/components/assets/cache/simple_arm_gcc.o.elf b/ofrak_core/test_ofrak/components/assets/cache/simple_arm_gcc.o.elf new file mode 100644 index 000000000..903bfe1fd --- /dev/null +++ b/ofrak_core/test_ofrak/components/assets/cache/simple_arm_gcc.o.elf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f8224a60424dc321c957fc3de6ae3eef016d97f7d1eeefc370ae9620c5908cf0 +size 13780 diff --git a/ofrak_core/test_ofrak/components/assets/cache/simple_pie_thumb_bin b/ofrak_core/test_ofrak/components/assets/cache/simple_pie_thumb_bin new file mode 100644 index 000000000..aff37f584 --- /dev/null +++ b/ofrak_core/test_ofrak/components/assets/cache/simple_pie_thumb_bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:baa585b3ddad6f59d3436fbd28a084d5cb575970130c19d01f1b4459238f75e1 +size 20885