├── .gitignore ├── LICENSE ├── README.md ├── androguard ├── LICENCE-2.0 ├── __init__.py └── core │ ├── __init__.py │ ├── analysis │ ├── __init__.py │ └── analysis.py │ ├── bytecode.py │ └── bytecodes │ ├── __init__.py │ ├── dvm.py │ ├── dvm_types.py │ └── mutf8.py ├── dcc.cfg ├── dcc.py ├── dex2c ├── __init__.py ├── basic_blocks.py ├── compiler.py ├── graph.py ├── instruction.py ├── opcode_ins.py ├── util.py └── writer.py ├── filter.txt ├── keystore └── debug.keystore ├── loader └── DccApplication.smali ├── project └── jni │ ├── Android.mk │ ├── Application.mk │ └── nc │ ├── Dex2C.cpp │ ├── Dex2C.h │ ├── DynamicRegister.h │ ├── ScopedLocalRef.h │ ├── ScopedPthreadMutexLock.h │ ├── obfuscate.h │ ├── well_known_classes.cpp │ └── well_known_classes.h ├── requirements.txt └── tools ├── APKEditor.jar ├── apksigner.jar └── manifest-editor.jar /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/python 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=python 3 | 4 | ### Python ### 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # test apk, zip & signing files 11 | *.apk 12 | *.idsig 13 | *.zip 14 | tools/apktool.jar 15 | 16 | # C extensions 17 | *.so 18 | 19 | # Distribution / packaging 20 | .Python 21 | build/ 22 | develop-eggs/ 23 | dist/ 24 | downloads/ 25 | eggs/ 26 | .eggs/ 27 | lib/ 28 | lib64/ 29 | parts/ 30 | sdist/ 31 | var/ 32 | wheels/ 33 | share/python-wheels/ 34 | *.egg-info/ 35 | .installed.cfg 36 | *.egg 37 | MANIFEST 38 | 39 | # PyInstaller 40 | # Usually these files are written by a python script from a template 41 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 42 | *.manifest 43 | *.spec 44 | 45 | # Installer logs 46 | pip-log.txt 47 | pip-delete-this-directory.txt 48 | 49 | # Unit test / coverage reports 50 | htmlcov/ 51 | .tox/ 52 | .nox/ 53 | .coverage 54 | .coverage.* 55 | .cache 56 | nosetests.xml 57 | coverage.xml 58 | *.cover 59 | *.py,cover 60 | .hypothesis/ 61 | .pytest_cache/ 62 | cover/ 63 | 64 | # Translations 65 | *.mo 66 | *.pot 67 | 68 | # Django stuff: 69 | *.log 70 | local_settings.py 71 | db.sqlite3 72 | db.sqlite3-journal 73 | 74 | # Flask stuff: 75 | instance/ 76 | .webassets-cache 77 | 78 | # Scrapy stuff: 79 | .scrapy 80 | 81 | # Sphinx documentation 82 | docs/_build/ 83 | 84 | # PyBuilder 85 | .pybuilder/ 86 | target/ 87 | 88 | # Jupyter Notebook 89 | .ipynb_checkpoints 90 | 91 | # IPython 92 | profile_default/ 93 | ipython_config.py 94 | 95 | # pyenv 96 | # For a library or package, you might want to ignore these files since the code is 97 | # intended to run in multiple environments; otherwise, check them in: 98 | # .python-version 99 | 100 | # pipenv 101 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 102 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 103 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 104 | # install all needed dependencies. 105 | #Pipfile.lock 106 | 107 | # poetry 108 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 109 | # This is especially recommended for binary packages to ensure reproducibility, and is more 110 | # commonly ignored for libraries. 111 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 112 | #poetry.lock 113 | 114 | # pdm 115 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 116 | #pdm.lock 117 | analyzer.py 118 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 119 | # in version control. 120 | # https://pdm.fming.dev/#use-with-ide 121 | .pdm.toml 122 | 123 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 124 | __pypackages__/ 125 | 126 | # Celery stuff 127 | celerybeat-schedule 128 | celerybeat.pid 129 | 130 | # SageMath parsed files 131 | *.sage.py 132 | 133 | # Environments 134 | .env 135 | .venv 136 | env/ 137 | venv/ 138 | ENV/ 139 | env.bak/ 140 | venv.bak/ 141 | 142 | # Spyder project settings 143 | .spyderproject 144 | .spyproject 145 | 146 | # Rope project settings 147 | .ropeproject 148 | 149 | # mkdocs documentation 150 | /site 151 | 152 | # mypy 153 | .mypy_cache/ 154 | .dmypy.json 155 | dmypy.json 156 | 157 | # Pyre type checker 158 | .pyre/ 159 | 160 | # pytype static type analyzer 161 | .pytype/ 162 | 163 | # Cython debug symbols 164 | cython_debug/ 165 | 166 | # PyCharm 167 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 168 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 169 | # and can be added to the global gitignore or merged into this file. For a more nuclear 170 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 171 | #.idea/ 172 | 173 | ### Python Patch ### 174 | # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration 175 | poetry.toml 176 | 177 | # ruff 178 | .ruff_cache/ 179 | 180 | # LSP config files 181 | pyrightconfig.json 182 | 183 | # End of https://www.toptal.com/developers/gitignore/api/python 184 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

𝐃𝐞𝐱𝟐𝐂

3 |

4 | Method-based AOT compiler that can wrap Dalvik bytecode with JNI native code. 5 |

6 |
7 | 8 | ### Requierements 9 | 1. Python3 10 | 2. lxml python package. 11 | ```bash 12 | pip3 install -U --user 'lxml>=4.3.0' 13 | ``` 14 | on Termux: 15 | ```bash 16 | pkg install libxml2 libxslt 17 | CFLAGS="-O0" pip install -U lxml 18 | ``` 19 | 3. JRE/JDK 20 | 4. Android NDK 21 | 22 | ### Installation 23 | ```bash 24 | git clone https://github.com/Kirlif/d2c 25 | ``` 26 | - configure dcc.cfg with your NDK path 27 | 28 | ### Major updates 29 | - changed Apktool for APKEditor 30 | - cleaned androguard from useless parts and removed dependencies 31 | - DEX file support: creates a ZIP archive of the built libraries and dex file 32 | - skip constructors by default 33 | - the application class is the loader ; if not defined or absent from dex, the custom loader is used 34 | - --allow-init option: do not skip constructors 35 | - --lib-name option: edit the library name, default: stub 36 | - --output default value is « output.apk » for an APK else « output.zip » for a DEX 37 | - --input only is required 38 | - all options can be configured in dcc.cfg ; options passed to the command line have priority 39 | - adjust APP_PLATFORM automatically 40 | 41 | ### Settings 42 | 43 | | Cli | Config | Default | 44 | | ----- | -------- | -------- | 45 | |-i, --input||| 46 | | -o, --output|output|output.(apk\|zip)| 47 | |-p, --obfuscate|obfuscate|false| 48 | |-d, --dynamic-register|dynamic_register|false| 49 | |-s, --skip-synthetic|skip_synthetic|false| 50 | |-a, --allow-init|allow_init|false| 51 | |-k, --force-keep-libs|force_keep_libs|false| 52 | |-b, --no-build|no_build|false| 53 | |-f, --filter|filter|filter.txt| 54 | |-c, --custom-loader|custom_loader|amimo.dcc.DccApplication| 55 | |-r, --force-custom-loader|force_custom_loader|false 56 | |-l, --lib-name|lib_name|stub| 57 | |-e, --source-dir|source_dir|| 58 | |-z, --project-archive|project_archive|project-source.zip| 59 | -------------------------------------------------------------------------------- /androguard/LICENCE-2.0: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /androguard/__init__.py: -------------------------------------------------------------------------------- 1 | # The current version of Androguard 2 | # Please use only this variable in any scripts, 3 | # to keep the version number the same everywhere. 4 | __version__ = "3.3.5" 5 | -------------------------------------------------------------------------------- /androguard/core/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /androguard/core/analysis/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /androguard/core/analysis/analysis.py: -------------------------------------------------------------------------------- 1 | import re 2 | import warnings 3 | from androguard.core.bytecodes import dvm 4 | import logging 5 | 6 | log = logging.getLogger("androguard.analysis") 7 | 8 | BasicOPCODES = [] 9 | for i in dvm.BRANCH_DVM_OPCODES: 10 | BasicOPCODES.append(re.compile(i)) 11 | 12 | 13 | class DVMBasicBlock: 14 | """ 15 | A simple basic block of a dalvik method 16 | """ 17 | 18 | def __init__(self, start, vm, method, context): 19 | self.__vm = vm 20 | self.method = method 21 | self.context = context 22 | 23 | self.last_length = 0 24 | self.nb_instructions = 0 25 | 26 | self.fathers = [] 27 | self.childs = [] 28 | 29 | self.start = start 30 | self.end = self.start 31 | 32 | self.special_ins = {} 33 | 34 | self.name = "%s-BB@0x%x" % (self.method.get_name(), self.start) 35 | self.exception_analysis = None 36 | 37 | self.__cached_instructions = None 38 | 39 | def get_instructions(self): 40 | """ 41 | Get all instructions from a basic block. 42 | 43 | :rtype: Return all instructions in the current basic block 44 | """ 45 | tmp_ins = [] 46 | idx = 0 47 | for i in self.method.get_instructions(): 48 | if self.start <= idx < self.end: 49 | yield i 50 | idx += i.get_length() 51 | 52 | def get_nb_instructions(self): 53 | return self.nb_instructions 54 | 55 | def get_name(self): 56 | return "%s-BB@0x%x" % (self.method.get_name(), self.start) 57 | 58 | def get_start(self): 59 | return self.start 60 | 61 | def get_end(self): 62 | return self.end 63 | 64 | def get_last(self): 65 | return self.get_instructions()[-1] 66 | 67 | def get_next(self): 68 | """ 69 | Get next basic blocks 70 | 71 | :rtype: a list of the next basic blocks 72 | """ 73 | return self.childs 74 | 75 | def get_prev(self): 76 | """ 77 | Get previous basic blocks 78 | 79 | :rtype: a list of the previous basic blocks 80 | """ 81 | return self.fathers 82 | 83 | def set_fathers(self, f): 84 | self.fathers.append(f) 85 | 86 | def get_last_length(self): 87 | return self.last_length 88 | 89 | def set_childs(self, values): 90 | # print self, self.start, self.end, values 91 | if not values: 92 | next_block = self.context.get_basic_block(self.end + 1) 93 | if next_block is not None: 94 | self.childs.append((self.end - self.get_last_length(), self.end, 95 | next_block)) 96 | else: 97 | for i in values: 98 | if i != -1: 99 | next_block = self.context.get_basic_block(i) 100 | if next_block is not None: 101 | self.childs.append((self.end - self.get_last_length(), 102 | i, next_block)) 103 | 104 | for c in self.childs: 105 | if c[2] is not None: 106 | c[2].set_fathers((c[1], c[0], self)) 107 | 108 | def push(self, i): 109 | self.nb_instructions += 1 110 | idx = self.end 111 | self.last_length = i.get_length() 112 | self.end += self.last_length 113 | 114 | op_value = i.get_op_value() 115 | 116 | if op_value == 0x26 or (0x2b <= op_value <= 0x2c): 117 | code = self.method.get_code().get_bc() 118 | self.special_ins[idx] = code.get_ins_off(idx + i.get_ref_off() * 2) 119 | 120 | def get_special_ins(self, idx): 121 | """ 122 | Return the associated instruction to a specific instruction (for example a packed/sparse switch) 123 | 124 | :param idx: the index of the instruction 125 | 126 | :rtype: None or an Instruction 127 | """ 128 | if idx in self.special_ins: 129 | return self.special_ins[idx] 130 | else: 131 | return None 132 | 133 | def get_exception_analysis(self): 134 | return self.exception_analysis 135 | 136 | def set_exception_analysis(self, exception_analysis): 137 | self.exception_analysis = exception_analysis 138 | 139 | class BasicBlocks: 140 | """ 141 | This class represents all basic blocks of a method 142 | """ 143 | 144 | def __init__(self, _vm): 145 | self.__vm = _vm 146 | self.bb = [] 147 | 148 | def push(self, bb): 149 | self.bb.append(bb) 150 | 151 | def pop(self, idx): 152 | return self.bb.pop(idx) 153 | 154 | def get_basic_block(self, idx): 155 | for i in self.bb: 156 | if i.get_start() <= idx < i.get_end(): 157 | return i 158 | return None 159 | 160 | def get(self): 161 | """ 162 | :rtype: return each basic block (:class:`DVMBasicBlock` object) 163 | """ 164 | for i in self.bb: 165 | yield i 166 | 167 | def gets(self): 168 | """ 169 | :rtype: a list of basic blocks (:class:`DVMBasicBlock` objects) 170 | """ 171 | return self.bb 172 | 173 | def get_basic_block_pos(self, idx): 174 | return self.bb[idx] 175 | 176 | 177 | class ExceptionAnalysis: 178 | def __init__(self, exception, bb): 179 | self.start = exception[0] 180 | self.end = exception[1] 181 | 182 | self.exceptions = exception[2:] 183 | 184 | for i in self.exceptions: 185 | i.append(bb.get_basic_block(i[1])) 186 | 187 | def show_buff(self): 188 | buff = "%x:%x\n" % (self.start, self.end) 189 | 190 | for i in self.exceptions: 191 | if i[2] is None: 192 | buff += "\t(%s -> %x %s)\n" % (i[0], i[1], i[2]) 193 | else: 194 | buff += "\t(%s -> %x %s)\n" % (i[0], i[1], i[2].get_name()) 195 | 196 | return buff[:-1] 197 | 198 | def get(self): 199 | d = {"start": self.start, "end": self.end, "list": []} 200 | 201 | for i in self.exceptions: 202 | d["list"].append({"name": i[0], "idx": i[1], "bb": i[2].get_name()}) 203 | 204 | return d 205 | 206 | 207 | class Exceptions: 208 | def __init__(self, _vm): 209 | self.__vm = _vm 210 | self.exceptions = [] 211 | 212 | def add(self, exceptions, basic_blocks): 213 | for i in exceptions: 214 | self.exceptions.append(ExceptionAnalysis(i, basic_blocks)) 215 | 216 | def get_exception(self, addr_start, addr_end): 217 | for i in self.exceptions: 218 | if i.start >= addr_start and i.end <= addr_end: 219 | return i 220 | 221 | elif addr_end <= i.end and addr_start >= i.start: 222 | return i 223 | 224 | return None 225 | 226 | def gets(self): 227 | return self.exceptions 228 | 229 | def get(self): 230 | for i in self.exceptions: 231 | yield i 232 | 233 | 234 | class MethodAnalysis: 235 | def __init__(self, vm, method): 236 | """ 237 | This class analyses in details a method of a class/dex file 238 | It is a wrapper around a :class:`EncodedMethod` and enhances it 239 | by using multiple :class:`BasicBlock`. 240 | 241 | :type vm: a :class:`DalvikVMFormat` object 242 | :type method: a :class:`EncodedMethod` object 243 | """ 244 | self.__vm = vm 245 | self.method = method 246 | 247 | self.basic_blocks = BasicBlocks(self.__vm) 248 | self.exceptions = Exceptions(self.__vm) 249 | 250 | self.code = self.method.get_code() 251 | if self.code: 252 | self._create_basic_block() 253 | 254 | def _create_basic_block(self): 255 | current_basic = DVMBasicBlock(0, self.__vm, self.method, self.basic_blocks) 256 | self.basic_blocks.push(current_basic) 257 | 258 | bc = self.code.get_bc() 259 | l = [] 260 | h = {} 261 | idx = 0 262 | 263 | log.debug("Parsing instructions") 264 | for i in bc.get_instructions(): 265 | for j in BasicOPCODES: 266 | if j.match(i.get_name()) is not None: 267 | v = dvm.determineNext(i, idx, self.method) 268 | h[idx] = v 269 | l.extend(v) 270 | break 271 | 272 | idx += i.get_length() 273 | 274 | log.debug("Parsing exceptions") 275 | excepts = dvm.determineException(self.__vm, self.method) 276 | for i in excepts: 277 | l.extend([i[0]]) 278 | for handler in i[2:]: 279 | l.append(handler[1]) 280 | 281 | log.debug("Creating basic blocks in %s" % self.method) 282 | idx = 0 283 | for i in bc.get_instructions(): 284 | # index is a destination 285 | if idx in l: 286 | if current_basic.get_nb_instructions() != 0: 287 | current_basic = DVMBasicBlock(current_basic.get_end(), self.__vm, self.method, self.basic_blocks) 288 | self.basic_blocks.push(current_basic) 289 | 290 | current_basic.push(i) 291 | 292 | # index is a branch instruction 293 | if idx in h: 294 | current_basic = DVMBasicBlock(current_basic.get_end(), self.__vm, self.method, self.basic_blocks) 295 | self.basic_blocks.push(current_basic) 296 | 297 | idx += i.get_length() 298 | 299 | if current_basic.get_nb_instructions() == 0: 300 | self.basic_blocks.pop(-1) 301 | 302 | log.debug("Settings basic blocks childs") 303 | 304 | for i in self.basic_blocks.get(): 305 | try: 306 | i.set_childs(h[i.end - i.get_last_length()]) 307 | except KeyError: 308 | i.set_childs([]) 309 | 310 | log.debug("Creating exceptions") 311 | 312 | # Create exceptions 313 | self.exceptions.add(excepts, self.basic_blocks) 314 | 315 | for i in self.basic_blocks.get(): 316 | # setup exception by basic block 317 | i.set_exception_analysis(self.exceptions.get_exception(i.start, i.end - 1)) 318 | 319 | def get_basic_blocks(self): 320 | """ 321 | :rtype: a :class:`BasicBlocks` object 322 | """ 323 | return self.basic_blocks 324 | 325 | def get_length(self): 326 | """ 327 | :rtype: an integer which is the length of the code 328 | """ 329 | return self.code.get_length() if self.code else 0 330 | 331 | def get_method(self): 332 | return self.method 333 | 334 | def get_vm(self): 335 | return self.__vm 336 | 337 | class MethodClassAnalysis: 338 | def __init__(self, method): 339 | """ 340 | MethodClassAnalysis contains the XREFs for a given method. 341 | 342 | Both referneces to other methods (XREF_TO) as well as methods calling 343 | this method (XREF_FROM) are saved. 344 | 345 | :param androguard.core.bytecodes.dvm.EncodedMethod method: the DVM Method object 346 | """ 347 | self.method = method 348 | 349 | 350 | @property 351 | def name(self): 352 | """Returns the name of this method""" 353 | return self.method.get_name() 354 | 355 | @property 356 | def descriptor(self): 357 | """Returns the type descriptor for this method""" 358 | return self.method.get_descriptor() 359 | 360 | @property 361 | def access(self): 362 | """Returns the access flags to the method as a string""" 363 | return self.method.get_access_flags_string() 364 | 365 | 366 | class FieldClassAnalysis: 367 | def __init__(self, field): 368 | """ 369 | FieldClassAnalysis contains the XREFs for a class field. 370 | 371 | Instead of using XREF_FROM/XREF_TO, this object has methods for READ and 372 | WRITE access to the field. 373 | 374 | That means, that it will show you, where the field is read or written. 375 | 376 | :param androguard.core.bytecodes.dvm.EncodedField field: `dvm.EncodedField` 377 | """ 378 | self.field = field 379 | 380 | @property 381 | def name(self): 382 | return self.field.get_name() 383 | 384 | class ClassAnalysis: 385 | def __init__(self, classobj): 386 | """ 387 | It is also used to wrap :class:`~androguard.core.bytecode.dvm.ClassDefItem`, which 388 | contain the actual class content like bytecode. 389 | 390 | :param classobj: class:`~androguard.core.bytecode.dvm.ClassDefItem` 391 | """ 392 | 393 | self.orig_class = classobj 394 | self._methods = {} 395 | self._fields = {} 396 | 397 | @property 398 | def implements(self): 399 | """ 400 | Get a list of interfaces which are implemented by this class 401 | 402 | :return: a list of Interface names 403 | """ 404 | return self.orig_class.get_interfaces() 405 | 406 | @property 407 | def extends(self): 408 | """ 409 | Return the parent class 410 | 411 | :return: a string of the parent class name 412 | """ 413 | return self.orig_class.get_superclassname() 414 | 415 | @property 416 | def name(self): 417 | """ 418 | Return the class name 419 | 420 | :return: 421 | """ 422 | return self.orig_class.get_name() 423 | 424 | def get_vm_class(self): 425 | return self.orig_class 426 | 427 | 428 | class Analysis: 429 | def __init__(self, vm=None): 430 | """ 431 | Analysis Object 432 | 433 | The Analysis contains a lot of information about (multiple) DalvikVMFormat objects 434 | Features are for example XREFs between Classes, Methods, Fields and Strings. 435 | 436 | Multiple DalvikVMFormat Objects can be added using the function `add` 437 | 438 | :param vm: inital DalvikVMFormat object (default None) 439 | """ 440 | 441 | # Contains DalvikVMFormat objects 442 | self.vms = [] 443 | # A dict of {classname: ClassAnalysis}, populated on add(vm) 444 | self.classes = {} 445 | # A dict of {EncodedMethod: MethodAnalysis}, populated on add(vm) 446 | self.methods = {} 447 | 448 | if vm: 449 | self.add(vm) 450 | 451 | def add(self, vm): 452 | """ 453 | Add a DalvikVMFormat to this Analysis 454 | 455 | :param vm: :class:`dvm.DalvikVMFormat` to add to this Analysis 456 | """ 457 | self.vms.append(vm) 458 | for current_class in vm.get_classes(): 459 | self.classes[current_class.get_name()] = ClassAnalysis(current_class) 460 | 461 | for method in vm.get_methods(): 462 | self.methods[method] = MethodAnalysis(vm, method) 463 | 464 | def get_method(self, method): 465 | """ 466 | Get the :class:`MethodAnalysis` object for a given :class:`EncodedMethod`. 467 | This Analysis object is used to enhance EncodedMethods. 468 | 469 | :param method: :class:`EncodedMethod` to search for 470 | :return: :class:`MethodAnalysis` object for the given method, or None if method was not found 471 | """ 472 | if method in self.methods: 473 | return self.methods[method] 474 | else: 475 | return None 476 | -------------------------------------------------------------------------------- /androguard/core/bytecode.py: -------------------------------------------------------------------------------- 1 | from struct import unpack, pack 2 | 3 | 4 | # Print arg into a correct format 5 | def _Print(name, arg): 6 | buff = name + " " 7 | 8 | if type(arg).__name__ == 'int': 9 | buff += "0x%x" % arg 10 | elif type(arg).__name__ == 'long': 11 | buff += "0x%x" % arg 12 | elif type(arg).__name__ == 'str': 13 | buff += "%s" % arg 14 | elif isinstance(arg, SV): 15 | buff += "0x%x" % arg.get_value() 16 | print(buff) 17 | 18 | class SV(object): 19 | 20 | def __init__(self, size, buff): 21 | self.__size = size 22 | self.__value = unpack(self.__size, buff)[0] 23 | 24 | def _get(self): 25 | return pack(self.__size, self.__value) 26 | 27 | def __str__(self): 28 | return "0x%x" % self.__value 29 | 30 | def __int__(self): 31 | return self.__value 32 | 33 | def get_value_buff(self): 34 | return self._get() 35 | 36 | def get_value(self): 37 | return self.__value 38 | 39 | def set_value(self, attr): 40 | self.__value = attr 41 | 42 | def object_to_bytes(obj): 43 | """ 44 | Convert a object to a bytearray or call get_raw() of the object 45 | if no useful type was found. 46 | """ 47 | if isinstance(obj, str): 48 | return bytearray(obj, "UTF-8") 49 | elif isinstance(obj, bool): 50 | return bytearray() 51 | elif isinstance(obj, int): 52 | return pack("= len(self.__buff) 200 | 201 | def get_buff(self): 202 | """ 203 | Return the whole buffer 204 | 205 | :rtype: bytearray 206 | """ 207 | return self.__buff 208 | 209 | def set_buff(self, buff): 210 | """ 211 | Overwrite the current buffer with the content of `buff` 212 | 213 | :param bytearray buff: the new buffer 214 | """ 215 | self.__buff = buff 216 | 217 | 218 | 219 | class Buff(object): 220 | def __init__(self, offset, buff): 221 | self.offset = offset 222 | self.buff = buff 223 | 224 | self.size = len(buff) 225 | 226 | 227 | # Here for legacy reasons. Might get removed some day... 228 | _Bytecode = BuffHandle 229 | 230 | 231 | class Node(object): 232 | def __init__(self, n, s): 233 | self.id = n 234 | self.title = s 235 | self.children = [] 236 | 237 | -------------------------------------------------------------------------------- /androguard/core/bytecodes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kirlif/d2c/80305482722efd38781d9213a7cc20b7bc036eed/androguard/core/bytecodes/__init__.py -------------------------------------------------------------------------------- /androguard/core/bytecodes/dvm_types.py: -------------------------------------------------------------------------------- 1 | # This file contains dictionaries used in the Dalvik Format. 2 | 3 | # https://source.android.com/devices/tech/dalvik/dex-format#type-codes 4 | TYPE_MAP_ITEM = { 5 | 0x0: "TYPE_HEADER_ITEM", 6 | 0x1: "TYPE_STRING_ID_ITEM", 7 | 0x2: "TYPE_TYPE_ID_ITEM", 8 | 0x3: "TYPE_PROTO_ID_ITEM", 9 | 0x4: "TYPE_FIELD_ID_ITEM", 10 | 0x5: "TYPE_METHOD_ID_ITEM", 11 | 0x6: "TYPE_CLASS_DEF_ITEM", 12 | 0x1000: "TYPE_MAP_LIST", 13 | 0x1001: "TYPE_TYPE_LIST", 14 | 0x1002: "TYPE_ANNOTATION_SET_REF_LIST", 15 | 0x1003: "TYPE_ANNOTATION_SET_ITEM", 16 | 0x2000: "TYPE_CLASS_DATA_ITEM", 17 | 0x2001: "TYPE_CODE_ITEM", 18 | 0x2002: "TYPE_STRING_DATA_ITEM", 19 | 0x2003: "TYPE_DEBUG_INFO_ITEM", 20 | 0x2004: "TYPE_ANNOTATION_ITEM", 21 | 0x2005: "TYPE_ENCODED_ARRAY_ITEM", 22 | 0x2006: "TYPE_ANNOTATIONS_DIRECTORY_ITEM", 23 | } 24 | 25 | # https://source.android.com/devices/tech/dalvik/dex-format#access-flags 26 | ACCESS_FLAGS = { 27 | 0x1: 'public', 28 | 0x2: 'private', 29 | 0x4: 'protected', 30 | 0x8: 'static', 31 | 0x10: 'final', 32 | 0x20: 'synchronized', 33 | 0x40: 'bridge', 34 | 0x80: 'varargs', 35 | 0x100: 'native', 36 | 0x200: 'interface', 37 | 0x400: 'abstract', 38 | 0x800: 'strictfp', 39 | 0x1000: 'synthetic', 40 | 0x4000: 'enum', 41 | 0x8000: 'unused', 42 | 0x10000: 'constructor', 43 | 0x20000: 'synchronized', 44 | } 45 | 46 | # https://source.android.com/devices/tech/dalvik/dex-format#typedescriptor 47 | TYPE_DESCRIPTOR = { 48 | 'V': 'void', 49 | 'Z': 'boolean', 50 | 'B': 'byte', 51 | 'S': 'short', 52 | 'C': 'char', 53 | 'I': 'int', 54 | 'J': 'long', 55 | 'F': 'float', 56 | 'D': 'double', 57 | } 58 | 59 | -------------------------------------------------------------------------------- /androguard/core/bytecodes/mutf8.py: -------------------------------------------------------------------------------- 1 | import builtins 2 | from builtins import str 3 | import struct 4 | 5 | 6 | def chr(val): 7 | """ 8 | Patched Version of builtins.chr, to work with narrow python builds 9 | In those versions, the function unichr does not work with inputs >0x10000 10 | 11 | This seems to be a problem usually on older windows builds. 12 | 13 | :param val: integer value of character 14 | :return: character 15 | """ 16 | try: 17 | return builtins.chr(val) 18 | except ValueError as e: 19 | if "(narrow Python build)" in str(e): 20 | return struct.pack('i', val).decode('utf-32') 21 | else: 22 | raise e 23 | 24 | 25 | def decode(b): 26 | """ 27 | Decode bytes as MUTF-8 28 | See https://docs.oracle.com/javase/6/docs/api/java/io/DataInput.html#modified-utf-8 29 | for more information 30 | 31 | Surrogates will be returned as two 16 bit characters. 32 | 33 | :param b: bytes to decode 34 | :rtype: unicode (py2), str (py3) of 16bit chars 35 | :raises: UnicodeDecodeError if string is not decodable 36 | """ 37 | res = u"" 38 | 39 | b = iter(bytearray(b)) 40 | 41 | for x in b: 42 | if x >> 7 == 0: 43 | # Single char: 44 | res += chr(x & 0x7f) 45 | elif x >> 5 == 0b110: 46 | # 2 byte Multichar 47 | b2 = next(b) 48 | if b2 >> 6 != 0b10: 49 | raise UnicodeDecodeError("Second byte of 2 byte sequence does not looks right.") 50 | 51 | res += chr((x & 0x1f) << 6 | b2 & 0x3f) 52 | elif x >> 4 == 0b1110: 53 | # 3 byte Multichar 54 | b2 = next(b) 55 | b3 = next(b) 56 | if b2 >> 6 != 0b10: 57 | raise UnicodeDecodeError("Second byte of 3 byte sequence does not looks right.") 58 | if b3 >> 6 != 0b10: 59 | raise UnicodeDecodeError("Third byte of 3 byte sequence does not looks right.") 60 | 61 | res += chr((x & 0xf) << 12 | (b2 & 0x3f) << 6 | b3 & 0x3f) 62 | else: 63 | raise UnicodeDecodeError("Could not decode byte") 64 | 65 | return res 66 | 67 | 68 | class PeekIterator: 69 | """ 70 | A quick'n'dirty variant of an Iterator that has a special function 71 | peek, which will return the next object but not consume it. 72 | """ 73 | idx = 0 74 | 75 | def __init__(self, s): 76 | self.s = s 77 | 78 | def __iter__(self): 79 | return self 80 | 81 | def __next__(self): 82 | if self.idx == len(self.s): 83 | raise StopIteration() 84 | self.idx = self.idx + 1 85 | return self.s[self.idx - 1] 86 | 87 | def next(self): 88 | # py2 compliance 89 | return self.__next__() 90 | 91 | def peek(self): 92 | if self.idx == len(self.s): 93 | return None 94 | return self.s[self.idx] 95 | 96 | 97 | def patch_string(s): 98 | """ 99 | Reorganize a String in such a way that surrogates are printable 100 | and lonely surrogates are escaped. 101 | 102 | :param s: input string 103 | :return: string with escaped lonely surrogates and 32bit surrogates 104 | """ 105 | res = u'' 106 | it = PeekIterator(s) 107 | for c in it: 108 | if (ord(c) >> 10) == 0b110110: 109 | # High surrogate 110 | # Check for the next 111 | n = it.peek() 112 | if n and (ord(n) >> 10) == 0b110111: 113 | # Next is a low surrogate! Merge them together 114 | res += chr(((ord(c) & 0x3ff) << 10 | (ord(n) & 0x3ff)) + 0x10000) 115 | # Skip next char, as we already consumed it 116 | next(it) 117 | else: 118 | # Lonely high surrogate 119 | res += u"\\u{:04x}".format(ord(c)) 120 | elif (ord(c) >> 10) == 0b110111: 121 | # Lonely low surrogate 122 | res += u"\\u{:04x}".format(ord(c)) 123 | else: 124 | # Looks like a normal char... 125 | res += c 126 | return res 127 | 128 | -------------------------------------------------------------------------------- /dcc.cfg: -------------------------------------------------------------------------------- 1 | { 2 | "ndk_dir": "path/to/ndkdir", 3 | "signature": { 4 | "keystore_path": "keystore/debug.keystore", 5 | "alias": "androiddebugkey", 6 | "keystore_pass": "android", 7 | "store_pass": "android", 8 | "v1_enabled": true, 9 | "v2_enabled": true, 10 | "v3_enabled": true 11 | }, 12 | "output": "", 13 | "obfuscate": false, 14 | "dynamic_register": false, 15 | "skip_synthetic": false, 16 | "allow_init": false, 17 | "force_keep_libs": false, 18 | "no_build": false, 19 | "filter": "filter.txt", 20 | "custom_loader": "amimo.dcc.DccApplication", 21 | "force_custom_loader": false, 22 | "lib_name": "stub", 23 | "source_dir": "", 24 | "project_archive": "project-source.zip" 25 | } 26 | -------------------------------------------------------------------------------- /dex2c/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kirlif/d2c/80305482722efd38781d9213a7cc20b7bc036eed/dex2c/__init__.py -------------------------------------------------------------------------------- /dex2c/basic_blocks.py: -------------------------------------------------------------------------------- 1 | # encoding=utf8 2 | # 3 | # Copyright (c) 2012 Geoffroy Gueguen 4 | # All Rights Reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS-IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | import logging 19 | from builtins import object 20 | from collections import OrderedDict 21 | 22 | from dex2c.instruction import MoveResultExpression 23 | from dex2c.opcode_ins import INSTRUCTION_SET 24 | 25 | logger = logging.getLogger('dex2c.basic_blocks') 26 | 27 | 28 | class IrBasicBlock(object): 29 | def __init__(self, dvm_basicblock): 30 | self.dvm_basicblock = dvm_basicblock 31 | self.instr_list = [] 32 | 33 | # MoveParam指令会给参数生成一个局部引用(local reference), 使用局部引用引用参数 34 | # 不能将它放入instr_list, 因为第一个基本块可以是循环 35 | self.move_param_insns = [] 36 | self.var_to_declare = list() 37 | self.class_to_declare = list() 38 | self.field_to_declare = list() 39 | self.method_to_declare = list() 40 | self.num = -1 # 基本块编号 41 | 42 | # 处理异常相关 43 | self.in_catch = False 44 | self.catch_type = None 45 | 46 | # 构建SSA IR使用数据结构 47 | # Ref: <> by Matthias Braun 48 | self.filled = False 49 | self.sealed = False 50 | self.current_definitions = {} 51 | self.incomplete_phis = set() 52 | self.phis = set() 53 | 54 | self.catch_successors = set() 55 | 56 | if dvm_basicblock: 57 | self.start = self.dvm_basicblock.get_start() 58 | else: 59 | self.start = -1 60 | 61 | def get_instr_list(self): 62 | return self.instr_list 63 | 64 | def get_name(self): 65 | return self.label 66 | 67 | def add_ins_before(self, new_ins, before_ins): 68 | pos = self.instr_list.index(before_ins) 69 | self.instr_list = self.instr_list[:pos] + [new_ins] + self.instr_list[pos:] 70 | 71 | def remove_ins(self, ins): 72 | self.instr_list.remove(ins) 73 | 74 | def add_ins(self, new_ins): 75 | self.instr_list.append(new_ins) 76 | 77 | def add_variable_declaration(self, variable): 78 | self.var_to_declare.append(variable) 79 | 80 | def set_catch_type(self, _type): 81 | self.catch_type = _type 82 | 83 | def update_current_definition(self, register, var): 84 | self.current_definitions[register] = var 85 | 86 | def read_current_definition(self, register): 87 | return self.current_definitions[register] if register in self.current_definitions else None 88 | 89 | def visit(self, visitor): 90 | # pass 91 | visitor.visit_statement_node(self) 92 | 93 | def add_incomplete_phi(self, phi): 94 | self.incomplete_phis.add(phi) 95 | 96 | def get_incomplete_phis(self): 97 | return self.incomplete_phis 98 | 99 | def clear_incomplete_phis(self): 100 | self.incomplete_phis.clear() 101 | 102 | def add_phi(self, phi): 103 | self.phis.add(phi) 104 | 105 | def remove_phi(self, phi): 106 | if phi in self.phis: 107 | self.phis.remove(phi) 108 | 109 | def clear_phis(self): 110 | self.phis.clear() 111 | 112 | def add_catch_successor(self, node): 113 | if node not in self.catch_successors: 114 | self.catch_successors.add(node) 115 | 116 | @property 117 | def label(self): 118 | if self.dvm_basicblock: 119 | return 'BB_%x' % self.dvm_basicblock.start 120 | else: 121 | return 'BB_%d' % self.num 122 | 123 | def __str__(self): 124 | s = [self.label] 125 | for phi in self.phis: 126 | s.append(phi.print()) 127 | 128 | for ins in self.instr_list: 129 | s.append(str(ins)) 130 | return '\n'.join(s) 131 | 132 | 133 | class LandingPad(IrBasicBlock): 134 | def __init__(self, source): 135 | super(LandingPad, self).__init__(None) 136 | self.node = source 137 | self.handles = OrderedDict() 138 | 139 | def add_catch_handle(self, atype, node): 140 | if atype in self.handles and atype != 'Ljava/lang/Throwable;': 141 | raise Exception("duplicate catch handle for %s" % atype) 142 | 143 | if atype not in self.handles: 144 | self.handles[atype] = node 145 | 146 | @property 147 | def label(self): 148 | return 'EX_LandingPad_%d' % self.node.num 149 | 150 | def __str__(self): 151 | return '%d' % self.node.num 152 | 153 | 154 | def fill_node_from_block(irbuilder, node): 155 | block = node.dvm_basicblock 156 | idx = block.get_start() 157 | current_node = node 158 | for ins in block.get_instructions(): 159 | opcode = ins.get_op_value() 160 | if opcode == -1: # FIXME? or opcode in (0x0300, 0x0200, 0x0100): 161 | idx += ins.get_length() 162 | continue 163 | try: 164 | _ins = INSTRUCTION_SET[opcode] 165 | except IndexError: 166 | raise Exception('Unknown instruction : %s.', ins.get_name().lower()) 167 | # fill-array-data 168 | if opcode == 0x26: 169 | fillarray = block.get_special_ins(idx) 170 | lastins = _ins(ins, irbuilder, fillarray) 171 | current_node.add_ins(lastins) 172 | # invoke-kind[/range] 173 | elif 0x6e <= opcode <= 0x72 or 0x74 <= opcode <= 0x78: 174 | lastins = _ins(ins, irbuilder) 175 | current_node.add_ins(lastins) 176 | elif 0x24 <= opcode <= 0x25: 177 | lastins = _ins(ins, irbuilder) 178 | current_node.add_ins(lastins) 179 | # move-result* 180 | elif 0xa <= opcode <= 0xc: 181 | lastins = _ins(ins, irbuilder) 182 | current_node.add_ins(lastins) 183 | # move-exception 184 | elif opcode == 0xd: 185 | lastins = _ins(ins, irbuilder, current_node.catch_type) 186 | current_node.add_ins(lastins) 187 | # {packed,sparse}-switch 188 | elif 0x2b <= opcode <= 0x2c: 189 | values = block.get_special_ins(idx) 190 | lastins = _ins(ins, irbuilder, values) 191 | current_node.add_ins(lastins) 192 | else: 193 | lastins = _ins(ins, irbuilder) 194 | current_node.add_ins(lastins) 195 | # aget-object v1_y, v1_x, v0 196 | # 如果v1_x在该指令后不再使用,我们会将v1_x释放,但是v1_x,v1_y分配到同一个寄存器(v1),导致v1_y被释放. 197 | # 解决办法是,我们将其转化为两条指令 198 | # aget-object vTmp, v1, v0 199 | # move-result-object v1, vTmp 200 | # 类似iget-object 201 | if opcode == 0x46 or opcode == 0x54: 202 | vtmp = irbuilder.write_result_variable() 203 | val = lastins.get_value() 204 | lastins.set_value(vtmp) 205 | move_ins = MoveResultExpression(val, vtmp) 206 | move_ins.parent = current_node # 变量活跃分析会使用 207 | current_node.add_ins(move_ins) 208 | lastins.offset = idx 209 | lastins.parent = current_node 210 | if lastins.get_value(): 211 | lastins.get_value().definition = lastins 212 | 213 | idx += ins.get_length() 214 | lastins.next_offset = idx 215 | lastins.dvm_instr = ins 216 | 217 | current_node.filled = True 218 | return current_node 219 | -------------------------------------------------------------------------------- /dex2c/compiler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # 4 | # Copyright (c) 2012 Geoffroy Gueguen 5 | # All Rights Reserved. 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS-IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | from dex2c.basic_blocks import fill_node_from_block 19 | 20 | import logging 21 | from collections import defaultdict 22 | import dex2c.util as util 23 | from androguard.core.analysis import analysis 24 | from dex2c.graph import construct 25 | from dex2c.instruction import Param, ThisParam, MoveParam, Phi, Variable, LoadConstant 26 | from dex2c.writer import Writer 27 | 28 | DEBUG = False 29 | # DEBUG = True 30 | 31 | logger = logging.getLogger("dex2c.compiler") 32 | 33 | 34 | class RegisterAllocator(object): 35 | def __init__(self, vars): 36 | self.slots = {} 37 | self.next_name = 0 38 | for var in vars: 39 | self.allocate(var) 40 | 41 | def allocate(self, var): 42 | register = var.get_register() 43 | assert var.get_type() 44 | atype = util.get_cdecl_type(var.get_type()) 45 | if (register, atype) not in self.slots: 46 | logger.debug("%s -> v%s:%s" % (var, self.next_name, atype)) 47 | self.slots[(register, atype)] = self.next_name 48 | self.next_name += 1 49 | else: 50 | logger.debug("%s -> v%s:%s" % (var, self.slots[(register, atype)], atype)) 51 | return self.slots[(register, atype)] 52 | 53 | 54 | class IrMethod(object): 55 | def __init__(self, graph, method): 56 | self.method = method 57 | 58 | self.graph = graph 59 | self.entry = graph.entry 60 | self.offset_to_node = graph.offset_to_node 61 | self.node_to_landing_pad = graph.node_to_landing_pad 62 | self.landing_pads = graph.landing_pads 63 | self.irblocks = self.graph.compute_block_order() 64 | 65 | self.cls_name = method.get_class_name() 66 | self.name = method.get_name() 67 | self.ra = RegisterAllocator(self.entry.var_to_declare).allocate 68 | self.writer = None 69 | 70 | self.rtype = None 71 | self.params = [] 72 | self.params_type = [] 73 | 74 | def show_source(self): 75 | print(self.get_source()) 76 | 77 | def get_source(self): 78 | if self.writer: 79 | return str(self.writer) 80 | return "" 81 | 82 | def get_prototype(self): 83 | if self.writer: 84 | return self.writer.get_prototype() 85 | return '' 86 | 87 | def __repr__(self): 88 | return "class IrMethod(object): %s" % self.name 89 | 90 | 91 | class IrBuilder(object): 92 | def __init__(self, methanalysis, obfus, dynamic_register): 93 | method = methanalysis.get_method() 94 | self.method = method 95 | self.irmethod = None 96 | self.start_block = next(methanalysis.get_basic_blocks().get(), None) 97 | self.cls_name = method.get_class_name() 98 | self.name = method.get_name() 99 | self.lparams = [] 100 | self.var_to_name = defaultdict() 101 | self.offset_to_node = {} 102 | self.graph = None 103 | self.dynamic_register = dynamic_register 104 | 105 | self.access = util.get_access_method(method.get_access_flags()) 106 | 107 | desc = method.get_descriptor() 108 | self.type = desc.split(")")[-1] 109 | self.params_type = util.get_params_type(desc) 110 | self.triple = method.get_triple() 111 | 112 | self.exceptions = methanalysis.exceptions.exceptions 113 | self.curret_block = None 114 | 115 | self.var_versions = defaultdict(int) 116 | 117 | self.obfus = obfus 118 | 119 | code = self.method.get_code() 120 | if code: 121 | start = code.registers_size - code.ins_size 122 | if "static" not in self.access: 123 | self.lparams.append(start) 124 | start += 1 125 | num_param = 0 126 | for ptype in self.params_type: 127 | param = start + num_param 128 | self.lparams.append(param) 129 | num_param += util.get_type_size(ptype) 130 | 131 | def get_return_type(self): 132 | return self.type 133 | 134 | def process(self): 135 | logger.debug("METHOD : %s", self.name) 136 | 137 | # Native methods... no blocks. 138 | if self.start_block is None: 139 | return 140 | 141 | graph = construct(self.start_block) 142 | self.graph = graph 143 | 144 | self.build() 145 | 146 | irmethod = IrMethod(graph, self.method) 147 | irmethod.rtype = self.get_return_type() 148 | irmethod.params = self.lparams 149 | irmethod.params_type = self.params_type 150 | 151 | writer = Writer(irmethod, self.dynamic_register) 152 | writer.write_method() 153 | irmethod.writer = writer 154 | return irmethod 155 | 156 | def build(self): 157 | self.define_params() 158 | 159 | self.graph.entry.sealed = True # Entry has Predecessor? 160 | 161 | todo = [self.graph.entry] 162 | while todo: 163 | node = todo.pop(0) 164 | if node.filled: 165 | continue 166 | self.curret_block = node 167 | node = fill_node_from_block(self, node) 168 | self.try_seal_block(node) 169 | todo.extend(self.graph.all_sucs(node)) 170 | 171 | self.remove_trivial_phi() 172 | 173 | self.hack_polymorphic_constant() 174 | 175 | self.infer_type() 176 | self.fix_const_type() 177 | self.verify_operand_type() 178 | self.verify_phi_operand_type() 179 | 180 | # self.dump_type() 181 | 182 | self.add_var_to_decl() 183 | 184 | def remove_trivial_phi(self): 185 | Changed = True 186 | while Changed: 187 | Changed = False 188 | for node in self.graph.rpo: 189 | phis = list(node.phis) 190 | for phi in phis: 191 | Changed |= phi.remove_trivial_phi() 192 | 193 | def verify_operand_type(self): 194 | nodes = self.graph.compute_block_order() 195 | for node in nodes: 196 | for ins in node.get_instr_list(): 197 | var = ins.get_value() 198 | if var and var.get_type() is None: 199 | raise Exception("unkonw type %s" % var) 200 | 201 | def verify_phi_operand_type(self): 202 | for node in self.graph.rpo: 203 | phis = list(node.phis) 204 | for phi in phis: 205 | same_type = phi.get_type() 206 | for operand in phi.get_operands().values(): 207 | op_type = operand.get_type() 208 | if util.is_int(same_type) and util.is_int(op_type): 209 | continue 210 | elif util.is_float(same_type) and util.is_float(op_type): 211 | continue 212 | elif util.is_ref(same_type) and util.is_ref(op_type): 213 | continue 214 | else: 215 | raise Exception( 216 | "inconsistency phi operand type %s %s %s" 217 | % (phi, same_type, op_type) 218 | ) 219 | 220 | def hack_polymorphic_constant(self): 221 | nodes = self.graph.compute_block_order() 222 | todo_list = [] 223 | for node in nodes: 224 | for ins in node.get_instr_list(): 225 | if isinstance(ins, LoadConstant) and ins.get_value_type() is None: 226 | todo_list.append(ins) 227 | while todo_list: 228 | ins = todo_list.pop(0) 229 | bb = ins.parent 230 | for user in ins.get_users(): 231 | new_val = self.write_variable(ins.get_value().get_register()) 232 | new_ins = LoadConstant(new_val, ins.get_cst()) 233 | bb.add_ins_before(new_ins, ins) 234 | # 每处引用不同的常量拷贝,解决常量多态问题.如0,可以当NULL,整型0,false使用. 235 | user.replase_use_of_with(ins.get_value(), new_val) 236 | ins.parent.remove_ins(ins) 237 | 238 | def infer_type(self): 239 | Changed = True 240 | nodes = self.graph.compute_block_order() 241 | max = 500 242 | while Changed: 243 | # self.dump_type() 244 | max -= 1 245 | if max == 0: 246 | raise Exception("type infer failed") 247 | 248 | Changed = False 249 | for node in nodes: 250 | for phi in node.phis: 251 | Changed |= phi.resolve_type() 252 | for ins in node.get_instr_list(): 253 | Changed |= ins.resolve_type() 254 | 255 | # 无法推导出的常量类型,根据其大小,设置类型 256 | def fix_const_type(self): 257 | nodes = self.graph.rpo 258 | Changed = False 259 | for node in nodes: 260 | for ins in node.get_instr_list(): 261 | if isinstance(ins, LoadConstant) and ins.get_value_type() is None: 262 | if -2147483648 <= ins.get_cst().get_constant() <= 2147483647: 263 | Changed = True 264 | logger.debug("Set constant type to int: %s" % ins) 265 | ins.set_value_type("I") 266 | else: 267 | Changed = True 268 | logger.debug("Set constant type to long: %s" % ins) 269 | ins.set_value_type("J") 270 | 271 | if Changed: 272 | self.infer_type() 273 | 274 | def dump_type(self): 275 | nodes = self.graph.compute_block_order() 276 | for node in nodes: 277 | for ins in node.get_instr_list(): 278 | var = ins.get_value() 279 | if var is not None: 280 | print("Type: %s %s" % (var, var.get_type())) 281 | 282 | def add_var_to_decl(self): 283 | entry = self.graph.entry 284 | nodes = self.graph.compute_block_order() 285 | for node in nodes: 286 | for ins in node.get_instr_list(): 287 | var = ins.get_value() 288 | if var is not None: 289 | entry.var_to_declare.append(var) 290 | 291 | clz = ins.get_class() 292 | if clz: 293 | entry.class_to_declare.append(clz) 294 | 295 | field = ins.get_field() 296 | if field: 297 | entry.field_to_declare.append(field) 298 | 299 | method = ins.get_call_method() 300 | if method: 301 | entry.method_to_declare.append(method) 302 | 303 | def try_seal_block(self, block): 304 | sucs = self.graph.all_sucs(block) 305 | 306 | if len(sucs) == 0: 307 | return 308 | 309 | for suc in sucs: 310 | preds = self.graph.all_preds(suc) 311 | filled = list(filter(lambda pred: pred.filled, preds)) 312 | if len(filled) == len(preds): 313 | self.seal_block(suc) 314 | 315 | def seal_block(self, block): 316 | preds = self.graph.all_preds(block) 317 | block.sealed = True 318 | for phi in block.get_incomplete_phis(): 319 | for pred in preds: 320 | incoming = self.read_variable(phi.get_register(), pred) 321 | phi.add_operand(pred, incoming) 322 | block.add_phi(phi) 323 | block.clear_incomplete_phis() 324 | 325 | def set_current_block(self, block): 326 | self.curret_block = block 327 | 328 | def get_current_block(self): 329 | return self.curret_block 330 | 331 | def read_result_variable(self): 332 | var = self.read_variable(-1, self.curret_block) 333 | if isinstance(var, Phi): 334 | raise Exception("invoke result is a Phi node") 335 | return var 336 | 337 | def write_result_variable(self): 338 | return self.write_variable(-1) 339 | 340 | def write_variable(self, register): 341 | value = self.new_ssa_variable(register) 342 | self.curret_block.update_current_definition(register, value) 343 | return value 344 | 345 | def read_variable(self, register, block=None): 346 | if block is None: 347 | block = self.curret_block 348 | value = block.read_current_definition(register) 349 | if value is None: 350 | return self.read_variable_recursive(register, block) 351 | else: 352 | return value 353 | 354 | def read_variable_recursive(self, register, block): 355 | preds = self.graph.all_preds(block) 356 | if not block.sealed: 357 | value = self.new_ssa_variable(register, True) 358 | block.add_incomplete_phi(value) 359 | value.set_block(block) 360 | elif len(preds) == 1: 361 | value = self.read_variable(register, preds[0]) 362 | else: 363 | value = self.new_ssa_variable(register, True) 364 | block.update_current_definition(register, value) 365 | block.add_phi(value) 366 | value.set_block(block) 367 | for pred in preds: 368 | incoming = self.read_variable(register, pred) 369 | value.add_operand(pred, incoming) 370 | block.update_current_definition(register, value) 371 | return value 372 | 373 | def define_params(self): 374 | entry = self.graph.entry 375 | code = self.method.get_code() 376 | if code: 377 | start = code.registers_size - code.ins_size 378 | if "static" not in self.access: 379 | param = self.new_ssa_variable(start) 380 | param.set_type(self.cls_name) 381 | entry.move_param_insns.append(MoveParam(ThisParam(param))) 382 | entry.update_current_definition(start, param) 383 | entry.var_to_declare.append(param) 384 | start += 1 385 | num_param = 0 386 | for ptype in self.params_type: 387 | register = start + num_param 388 | param = self.new_ssa_variable(register) 389 | param.set_type(ptype) 390 | entry.move_param_insns.append(MoveParam(Param(param))) 391 | entry.update_current_definition(register, param) 392 | entry.var_to_declare.append(param) 393 | num_param += util.get_type_size(ptype) 394 | 395 | def new_ssa_variable(self, register, phi=False): 396 | ver = self.var_versions.get(register, 0) 397 | new_var = Phi(register, ver) if phi else Variable(register, ver) 398 | 399 | ver += 1 400 | self.var_versions[register] = ver 401 | return new_var 402 | 403 | def __repr__(self): 404 | # return 'Method %s' % self.name 405 | return "class DvMethod(object): %s" % self.name 406 | 407 | 408 | class DvClass(object): 409 | def __init__(self, dvclass, vma): 410 | name = dvclass.get_name() 411 | if name.find("/") > 0: 412 | pckg, name = name.rsplit("/", 1) 413 | else: 414 | pckg, name = "", name 415 | self.package = pckg[1:].replace("/", ".") 416 | self.name = name[:-1] 417 | 418 | self.vma = vma 419 | self.methods = dvclass.get_methods() 420 | self.fields = dvclass.get_fields() 421 | self.code = [] 422 | self.inner = False 423 | 424 | access = dvclass.get_access_flags() 425 | # If interface we remove the class and abstract keywords 426 | if 0x200 & access: 427 | prototype = "%s %s" 428 | if access & 0x400: 429 | access -= 0x400 430 | else: 431 | prototype = "%s class %s" 432 | 433 | self.access = util.get_access_class(access) 434 | self.prototype = prototype % (" ".join(self.access), self.name) 435 | 436 | self.interfaces = dvclass.get_interfaces() 437 | self.superclass = dvclass.get_superclassname() 438 | self.thisclass = dvclass.get_name() 439 | 440 | logger.info("Class : %s", self.name) 441 | logger.info("Methods added :") 442 | for meth in self.methods: 443 | logger.info("%s (%s, %s)", meth.get_method_idx(), self.name, meth.name) 444 | logger.info("") 445 | 446 | def get_methods(self): 447 | return self.methods 448 | 449 | def process_method(self, num): 450 | method = self.methods[num] 451 | if not isinstance(method, IrMethod): 452 | irbuilder = IrBuilder(self.vma.get_method(method)) 453 | self.methods[num] = irbuilder.process() 454 | else: 455 | method.process() 456 | 457 | def process(self): 458 | for i in range(len(self.methods)): 459 | try: 460 | self.process_method(i) 461 | except Exception as e: 462 | logger.warning("Error decompiling method %s: %s", self.methods[i], e) 463 | 464 | def get_source(self): 465 | source = [] 466 | for method in self.methods: 467 | if isinstance(method, IrMethod): 468 | source.append(method.get_source()) 469 | return "".join(source) 470 | 471 | def show_source(self): 472 | print(self.get_source()) 473 | 474 | def __repr__(self): 475 | return "Class(%s)" % self.name 476 | 477 | 478 | class Dex2C: 479 | def __init__(self, vm, vmx, obfus, dynamic_register): 480 | self.vm = vm 481 | self.vmx = vmx 482 | self.obfus = obfus 483 | self.dynamic_register = dynamic_register 484 | 485 | 486 | def get_source_method(self, m): 487 | mx = self.vmx.get_method(m) 488 | z = IrBuilder(mx, self.obfus, self.dynamic_register) 489 | irmethod = z.process() 490 | if irmethod: 491 | return (irmethod.get_source(), irmethod.get_prototype()) 492 | else: 493 | return (None, None) 494 | 495 | def get_source_class(self, _class): 496 | c = DvClass(_class, self.vmx) 497 | c.process() 498 | return c.get_source() 499 | -------------------------------------------------------------------------------- /dex2c/graph.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2012 Geoffroy Gueguen 3 | # All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS-IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | from collections import defaultdict 17 | 18 | from dex2c.basic_blocks import (IrBasicBlock, LandingPad) 19 | from dex2c.instruction import * 20 | 21 | logger = logging.getLogger('dex2c.graph') 22 | 23 | 24 | class Graph(object): 25 | def __init__(self): 26 | self.entry = None 27 | self.exit = None 28 | self.nodes = list() 29 | self.landing_pads = list() 30 | self.rpo = [] 31 | self.edges = defaultdict(list) 32 | self.catch_edges = defaultdict(list) 33 | self.reverse_edges = defaultdict(list) 34 | self.reverse_catch_edges = defaultdict(list) 35 | self.loc_to_ins = None 36 | self.loc_to_node = None 37 | self.offset_to_node = {} 38 | self.node_to_landing_pad = {} 39 | 40 | def sucs(self, node): 41 | return self.edges.get(node, [])[:] 42 | 43 | def all_sucs(self, node): 44 | return self.edges.get(node, []) + self.catch_edges.get(node, []) 45 | 46 | def all_catches(self, node): 47 | return self.catch_edges.get(node, [])[:] 48 | 49 | def preds(self, node): 50 | return [n for n in self.reverse_edges.get(node, []) if not n.in_catch] 51 | 52 | def all_preds(self, node): 53 | return (self.reverse_edges.get(node, []) + self.reverse_catch_edges.get( 54 | node, [])) 55 | 56 | def add_node(self, node): 57 | self.nodes.append(node) 58 | 59 | def add_landing_pad(self, pad): 60 | self.landing_pads.append(pad) 61 | 62 | def add_edge(self, e1, e2): 63 | lsucs = self.edges[e1] 64 | if e2 not in lsucs: 65 | lsucs.append(e2) 66 | lpreds = self.reverse_edges[e2] 67 | if e1 not in lpreds: 68 | lpreds.append(e1) 69 | 70 | def remove_edge(self, e1, e2): 71 | lsucs = self.edges[e1] 72 | if e2 in lsucs: 73 | lsucs.remove(e2) 74 | 75 | lpreds = self.reverse_edges[e2] 76 | if e1 in lpreds: 77 | lpreds.remove(e1) 78 | 79 | def add_catch_edge(self, e1, e2): 80 | lsucs = self.catch_edges[e1] 81 | if e2 not in lsucs: 82 | lsucs.append(e2) 83 | lpreds = self.reverse_catch_edges[e2] 84 | if e1 not in lpreds: 85 | lpreds.append(e1) 86 | 87 | def remove_node(self, node): 88 | preds = self.reverse_edges.get(node, []) 89 | for pred in preds: 90 | self.edges[pred].remove(node) 91 | 92 | succs = self.edges.get(node, []) 93 | for suc in succs: 94 | self.reverse_edges[suc].remove(node) 95 | 96 | exc_preds = self.reverse_catch_edges.pop(node, []) 97 | for pred in exc_preds: 98 | self.catch_edges[pred].remove(node) 99 | 100 | exc_succs = self.catch_edges.pop(node, []) 101 | for suc in exc_succs: 102 | self.reverse_catch_edges[suc].remove(node) 103 | 104 | self.nodes.remove(node) 105 | if node in self.rpo: 106 | self.rpo.remove(node) 107 | del node 108 | 109 | def number_ins(self): 110 | self.loc_to_ins = {} 111 | self.loc_to_node = {} 112 | num = 0 113 | for node in self.rpo: 114 | start_node = num 115 | num = node.number_ins(num) 116 | end_node = num - 1 117 | self.loc_to_ins.update(node.get_loc_with_ins()) 118 | self.loc_to_node[start_node, end_node] = node 119 | 120 | def get_ins_from_loc(self, loc): 121 | return self.loc_to_ins.get(loc) 122 | 123 | def get_node_from_loc(self, loc): 124 | for (start, end), node in self.loc_to_node.items(): 125 | if start <= loc <= end: 126 | return node 127 | 128 | def remove_ins(self, loc): 129 | ins = self.get_ins_from_loc(loc) 130 | self.get_node_from_loc(loc).remove_ins(loc, ins) 131 | self.loc_to_ins.pop(loc) 132 | 133 | def compute_rpo(self): 134 | """ 135 | Number the nodes in reverse post order. 136 | An RPO traversal visit as many predecessors of a node as possible 137 | before visiting the node itself. 138 | """ 139 | nb = len(self.nodes) + 1 140 | for node in self.post_order(): 141 | node.num = nb - node.po 142 | self.rpo = sorted(self.nodes, key=lambda n: n.num) 143 | 144 | def compute_block_order(self): 145 | list = sorted(self.nodes, key=lambda n: n.start) 146 | for num, node in enumerate(list): 147 | node.num = num 148 | return list 149 | 150 | def post_order(self): 151 | """ 152 | Return the nodes of the graph in post-order i.e we visit all the 153 | children of a node before visiting the node itself. 154 | """ 155 | 156 | def _visit(n, cnt): 157 | visited.add(n) 158 | for suc in self.all_sucs(n): 159 | if suc not in visited: 160 | for cnt, s in _visit(suc, cnt): 161 | yield cnt, s 162 | n.po = cnt 163 | yield cnt + 1, n 164 | 165 | visited = set() 166 | for _, node in _visit(self.entry, 1): 167 | yield node 168 | 169 | def immediate_dominators(self): 170 | return dom_lt(self) 171 | 172 | def __len__(self): 173 | return len(self.nodes) 174 | 175 | def __repr__(self): 176 | return str(self.nodes) 177 | 178 | def __iter__(self): 179 | for node in self.nodes: 180 | yield node 181 | 182 | 183 | def dom_lt(graph): 184 | """Dominator algorithm from Lengaeur-Tarjan""" 185 | 186 | def _dfs(v, n): 187 | semi[v] = n = n + 1 188 | vertex[n] = label[v] = v 189 | ancestor[v] = 0 190 | for w in graph.all_sucs(v): 191 | if not semi[w]: 192 | parent[w] = v 193 | n = _dfs(w, n) 194 | pred[w].add(v) 195 | return n 196 | 197 | def _compress(v): 198 | u = ancestor[v] 199 | if ancestor[u]: 200 | _compress(u) 201 | if semi[label[u]] < semi[label[v]]: 202 | label[v] = label[u] 203 | ancestor[v] = ancestor[u] 204 | 205 | def _eval(v): 206 | if ancestor[v]: 207 | _compress(v) 208 | return label[v] 209 | return v 210 | 211 | def _link(v, w): 212 | ancestor[w] = v 213 | 214 | parent, ancestor, vertex = {}, {}, {} 215 | label, dom = {}, {} 216 | pred, bucket = defaultdict(set), defaultdict(set) 217 | 218 | # Step 1: 219 | semi = {v: 0 for v in graph.nodes} 220 | n = _dfs(graph.entry, 0) 221 | for i in range(n, 1, -1): 222 | w = vertex[i] 223 | # Step 2: 224 | for v in pred[w]: 225 | u = _eval(v) 226 | y = semi[w] = min(semi[w], semi[u]) 227 | bucket[vertex[y]].add(w) 228 | pw = parent[w] 229 | _link(pw, w) 230 | # Step 3: 231 | bpw = bucket[pw] 232 | while bpw: 233 | v = bpw.pop() 234 | u = _eval(v) 235 | dom[v] = u if semi[u] < semi[v] else pw 236 | # Step 4: 237 | for i in range(2, n + 1): 238 | w = vertex[i] 239 | dw = dom[w] 240 | if dw != vertex[semi[w]]: 241 | dom[w] = dom[dw] 242 | dom[graph.entry] = None 243 | return dom 244 | 245 | 246 | def bfs(start): 247 | to_visit = [start] 248 | visited = set([start]) 249 | while to_visit: 250 | node = to_visit.pop(0) 251 | yield node 252 | if node.exception_analysis: 253 | for _, _, exception in node.exception_analysis.exceptions: 254 | if exception not in visited: 255 | to_visit.append(exception) 256 | visited.add(exception) 257 | for _, _, child in node.childs: 258 | if child not in visited: 259 | to_visit.append(child) 260 | visited.add(child) 261 | 262 | 263 | def construct(start_block): 264 | bfs_blocks = bfs(start_block) 265 | 266 | graph = Graph() 267 | 268 | block_to_node = {} 269 | node_to_landing_pad = {} 270 | 271 | # 每个异常exception_analysis 对应一个分发异常的landingping_pad, 不同的block可以有相同的异常处理 272 | exception_to_landing_pad = {} 273 | 274 | for block in bfs_blocks: 275 | node = block_to_node.get(block) 276 | if node is None: 277 | node = IrBasicBlock(block) 278 | block_to_node[block] = node 279 | graph.add_node(node) 280 | 281 | if block.exception_analysis: 282 | if block.exception_analysis in exception_to_landing_pad: 283 | landing_pad = exception_to_landing_pad[block.exception_analysis] 284 | else: 285 | # 初始化 LandingPad 286 | landing_pad = LandingPad(node) 287 | graph.add_landing_pad(landing_pad) 288 | exception_to_landing_pad[block.exception_analysis] = landing_pad 289 | for _type, _, exception_target in block.exception_analysis.exceptions: 290 | catch_node = block_to_node.get(exception_target) 291 | if catch_node is None: 292 | catch_node = IrBasicBlock(exception_target) 293 | block_to_node[exception_target] = catch_node 294 | 295 | catch_node.set_catch_type(_type) 296 | catch_node.in_catch = True 297 | 298 | landing_pad.add_catch_handle(_type, catch_node) 299 | 300 | # 将catch 节点加入到node后继 301 | for _type, _, exception_target in block.exception_analysis.exceptions: 302 | catch_node = block_to_node.get(exception_target) 303 | assert catch_node is not None 304 | node.add_catch_successor(catch_node) 305 | graph.add_catch_edge(node, catch_node) 306 | # 更新节点对应的LandingPad 307 | node_to_landing_pad[node] = landing_pad 308 | 309 | for _, _, child_block in block.childs: 310 | child_node = block_to_node.get(child_block) 311 | if child_node is None: 312 | child_node = IrBasicBlock(child_block) 313 | block_to_node[child_block] = child_node 314 | graph.add_edge(node, child_node) 315 | 316 | graph.entry = block_to_node[start_block] 317 | 318 | graph.compute_rpo() 319 | 320 | offset_to_node = {} 321 | for node in graph.rpo: 322 | if node.start >= 0: 323 | offset_to_node[node.start] = node 324 | 325 | graph.node_to_landing_pad = node_to_landing_pad 326 | graph.offset_to_node = offset_to_node 327 | for node in graph.rpo: 328 | preds = [pred for pred in graph.all_preds(node) if pred.num < node.num] 329 | if preds and all(pred.in_catch for pred in preds): 330 | node.in_catch = True 331 | 332 | return graph 333 | 334 | 335 | -------------------------------------------------------------------------------- /dex2c/instruction.py: -------------------------------------------------------------------------------- 1 | # encoding=utf8 2 | # 3 | # Copyright (C) 2012, Geoffroy Gueguen 4 | # All rights reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS-IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | import logging 19 | from builtins import object 20 | from typing import List, Set 21 | from dex2c import util 22 | 23 | logger = logging.getLogger('dex2c.instruction') 24 | 25 | 26 | class Value(object): 27 | def __init__(self): 28 | self.var_type = None 29 | self.type_sealed = False 30 | 31 | self.definition = None 32 | self.uses = set() 33 | self.is_const = False 34 | 35 | def set_type(self, vtype: str): 36 | if vtype is None or self.type_sealed: 37 | return False 38 | 39 | self.type_sealed = True 40 | if self.var_type is None: 41 | self.var_type = vtype 42 | return True 43 | 44 | if self.var_type == vtype: 45 | return False 46 | else: 47 | self.var_type = vtype 48 | return True 49 | 50 | def refine_type(self, vtype: str): 51 | 52 | if vtype is None or self.type_sealed or self.var_type == vtype: 53 | return False 54 | 55 | if self.var_type is None: 56 | self.var_type = vtype 57 | return True 58 | 59 | if util.is_java_lang_object(vtype) and util.is_ref(self.var_type): 60 | return False 61 | 62 | if util.is_java_lang_object(self.var_type) and util.is_ref(vtype): 63 | return False 64 | 65 | if (util.is_int(self.var_type) and util.is_int(vtype)) or \ 66 | (util.is_float(self.var_type) and util.is_float(vtype)): 67 | new_type = util.get_smaller_type(self.var_type, vtype) 68 | if new_type != self.var_type: 69 | self.var_type = new_type 70 | return True 71 | else: 72 | return False 73 | elif util.is_int(self.var_type) and (util.is_float(vtype) or util.is_ref(vtype)): 74 | # 常量转换成浮点,提升为浮点类型,或者引用类型 0->NULL, 0->int_to_float() 75 | if self.is_const: 76 | # 常量转换成浮点,需要调用相应的帮助函数 77 | self.var_type = vtype 78 | return True 79 | else: 80 | if util.is_float(vtype): 81 | # 可以无损转化成浮点类型 82 | return False 83 | else: 84 | raise Exception("unable to refine type %s %s" % (self.var_type, vtype)) 85 | else: 86 | new_type = util.merge_type(self.var_type, vtype) 87 | if new_type is None: 88 | raise Exception("unable to refine type %s %s" % (self.var_type, vtype)) 89 | if self.var_type != new_type: 90 | self.var_type = new_type 91 | return True 92 | else: 93 | return False 94 | 95 | def get_type(self): 96 | return self.var_type 97 | 98 | def use_empty(self): 99 | return len(self.uses) == 0 100 | 101 | def get_uses(self): 102 | return self.uses 103 | 104 | def get_users(self): 105 | users = set() 106 | for use in self.uses: 107 | users.add(use.get_user()) 108 | return list(users) 109 | 110 | def add_user(self, instr): 111 | for use in self.uses: 112 | if use.get_user() == instr: 113 | return 114 | else: 115 | use = Use(self, instr) 116 | self.uses.add(use) 117 | 118 | def visit_decl(self, visitor): 119 | return visitor.visit_decl(self) 120 | 121 | def remove_user(self, user): 122 | new_uses = set() 123 | for use in self.uses: 124 | if user != use.get_user(): 125 | new_uses.add(use) 126 | self.uses = new_uses 127 | 128 | def replace_all_uses_with(self, new_value): 129 | for use in self.uses: 130 | user = use.get_user() 131 | user.replase_use_of_with(use.get_value(), new_value) 132 | self.uses.clear() 133 | 134 | 135 | class Use(object): 136 | def __init__(self, value: Value, user): 137 | self.value = value 138 | self.user = user 139 | 140 | def get_user(self): 141 | return self.user 142 | 143 | def get_value(self): 144 | return self.value 145 | 146 | 147 | class Constant(Value): 148 | def __init__(self, value, vtype=None): 149 | super(Constant, self).__init__() 150 | self.constant = value 151 | self.is_const = True 152 | if vtype: 153 | self.refine_type(vtype) 154 | 155 | def get_constant(self): 156 | if self.var_type and self.var_type[0] == 'L': 157 | return util.hex_escape_string(self.constant) 158 | else: 159 | return self.constant 160 | 161 | def visit(self, visitor): 162 | return visitor.visit_constant(self) 163 | 164 | def visit_decl(self, visitor): 165 | return visitor.visit_decl(self) 166 | 167 | def __str__(self): 168 | atype = self.var_type 169 | if atype == 'F': 170 | return 'd2c_bitcast_to_float(%r)' % self.constant 171 | elif atype == 'D': 172 | return 'd2c_bitcast_to_double(%r)' % self.constant 173 | elif util.is_int(atype) or util.is_long(atype): 174 | return '%r' % self.constant 175 | else: 176 | return '%s' % (self.get_constant()) 177 | 178 | 179 | class Variable(Value): 180 | def __init__(self, register, version): 181 | super(Variable, self).__init__() 182 | self.register = register 183 | self.version = version 184 | 185 | def set_type(self, vtype: str): 186 | return super(Variable, self).set_type(vtype) 187 | 188 | def refine_type(self, vtype: str): 189 | return super(Variable, self).refine_type(vtype) 190 | 191 | def get_register(self): 192 | return self.register 193 | 194 | def visit(self, visitor): 195 | return visitor.visit_variable(self) 196 | 197 | def visit_decl(self, visitor): 198 | return visitor.visit_decl(self) 199 | 200 | def __str__(self): 201 | return 'v%s_%s' % (self.register if self.register >= 0 else 'Result', self.version) 202 | 203 | 204 | class Phi(Variable): 205 | def __init__(self, register, version): 206 | super(Phi, self).__init__(register, version) 207 | self.operands = {} 208 | self.block = None 209 | self.phi_users = set() 210 | 211 | def get_operands(self): 212 | return self.operands 213 | 214 | def set_block(self, block): 215 | self.block = block 216 | 217 | def get_block(self): 218 | return self.block 219 | 220 | def add_operand(self, pred, value): 221 | self.operands[pred] = value 222 | value.add_user(self) 223 | 224 | def get_incoming_blocks(self, value): 225 | result = [] 226 | for k, v in self.operands.items(): 227 | if v == value: 228 | result.append(k) 229 | return result 230 | 231 | def remove_operand(self, pred): 232 | self.operands.pop(pred) 233 | 234 | def replase_use_of_with(self, old: Value, new: Value): 235 | for pred, value in self.operands.items(): 236 | if value == old: 237 | value.remove_user(self) 238 | new.add_user(self) 239 | self.operands[pred] = new 240 | 241 | def resolve_type(self): 242 | same_op_type = None 243 | 244 | for op in self.operands.values(): 245 | if isinstance(op, Constant): 246 | continue 247 | op_type = op.get_type() 248 | if same_op_type and op_type != same_op_type: 249 | op_type = util.merge_type(same_op_type, op_type) 250 | same_op_type = op_type 251 | 252 | if same_op_type: 253 | new_type = same_op_type 254 | else: 255 | new_type = self.var_type 256 | 257 | Changed = False 258 | if new_type: 259 | if self.var_type != new_type: 260 | Changed |= self.refine_type(new_type) 261 | for op in self.operands.values(): 262 | if isinstance(op, Constant): 263 | continue 264 | if op.get_type() != new_type: 265 | Changed |= op.refine_type(new_type) 266 | return Changed 267 | 268 | def remove_trivial_phi(self): 269 | # return 270 | same = None 271 | for op in self.operands.values(): 272 | if op == same or op == self: 273 | continue 274 | 275 | if not same is None: 276 | return False 277 | same = op 278 | 279 | for op in self.operands.values(): 280 | op.remove_user(self) 281 | 282 | self.replace_all_uses_with(same) 283 | self.block.remove_phi(self) 284 | assert len(self.uses) == 0 285 | return True 286 | 287 | def print(self): 288 | s = 'v%s_%s = Phi(' % (self.register, self.version) 289 | vars = [] 290 | for n, v in self.operands.items(): 291 | vars.append(str(v)) 292 | s += ','.join(vars) 293 | s += ')' 294 | return s 295 | 296 | 297 | class Instruction(object): 298 | def __init__(self): 299 | self.value: Value = None 300 | self.operands: List[Value] = [] 301 | self.parent = None # bb 302 | self.offset = -1 # current instruction's offset, used by branch instruction 303 | self.next_offset = -1 # next instruction's offset, used to get next basicblock 304 | self.dvm_instr = None 305 | 306 | # 活跃信息,如果一个引用类型变量在live_in但不在live_out,需要释放引用 307 | self.live_in: Set[Value] = set() 308 | self.live_out: Set[Value] = set() 309 | 310 | def set_value(self, value): 311 | self.value = value 312 | 313 | def set_value_type(self, vtype): 314 | return self.value.set_type(vtype) 315 | 316 | def get_value_type(self): 317 | return self.value.get_type() 318 | 319 | def set_operand_type(self, index, vtype): 320 | self.operands[index].refine_type(vtype) 321 | 322 | def get_operand_type(self, index): 323 | self.operands[index].get_type() 324 | 325 | def get_operans_number(self): 326 | return len(self.operands) 327 | 328 | def resolve_type(self): 329 | return False 330 | 331 | def is_const(self): 332 | return False 333 | 334 | def get_value(self): 335 | return self.value 336 | 337 | def get_users(self): 338 | return list(self.value.get_users()) 339 | 340 | def add_user(self, instr): 341 | self.value.add_user(instr) 342 | 343 | def remove_user(self, user): 344 | self.value.remove_user(user) 345 | 346 | def replase_use_of_with(self, old_var: Value, new_var: Value): 347 | for idx, operand in enumerate(self.operands): 348 | if old_var == operand: 349 | self.operands[idx] = new_var 350 | new_var.add_user(self) 351 | old_var.remove_user(self) 352 | 353 | def visit(self, visitor): 354 | pass 355 | 356 | def get_class(self): 357 | return None 358 | 359 | def get_field(self): 360 | return None 361 | 362 | def get_call_method(self): 363 | return None 364 | 365 | def dump(self): 366 | if self.dvm_instr is not None: 367 | return '%x:%s %s' % ( 368 | self.offset, self.dvm_instr.get_name(), util.hex_escape_string(self.dvm_instr.get_output())) 369 | else: 370 | return '' 371 | 372 | 373 | class LoadConstant(Instruction): 374 | def __init__(self, value, cst, obfus=False): 375 | super(LoadConstant, self).__init__() 376 | self.value = value 377 | self.value.refine_type(cst.get_type()) 378 | self.value.is_const = True 379 | self.operands.append(cst) 380 | self.obfus = obfus 381 | 382 | @property 383 | def constant(self): 384 | return self.operands[0] 385 | 386 | def get_cst(self): 387 | return self.operands[0] 388 | 389 | def get_class(self): 390 | cst_type = self.get_cst().get_type() 391 | if cst_type == 'Ljava/lang/Class;': 392 | return self.get_cst().get_constant() 393 | else: 394 | return None 395 | 396 | def resolve_type(self): 397 | return False 398 | 399 | def visit(self, visitor): 400 | return visitor.visit_load_constant(self, self.obfus) 401 | 402 | def __str__(self): 403 | return '%s = %s' % (str(self.value), str(self.operands[0])) 404 | 405 | 406 | class Param(object): 407 | def __init__(self, value): 408 | self.declared = True 409 | self.value = value 410 | self.this = False 411 | 412 | def get_value(self): 413 | return self.value 414 | 415 | def visit(self, visitor): 416 | return visitor.visit_param(self.value) 417 | 418 | def __str__(self): 419 | return 'PARAM_%s' % self.name 420 | 421 | 422 | class ThisParam(Param): 423 | def __init__(self, value): 424 | super(ThisParam, self).__init__(value) 425 | self.this = True 426 | 427 | def visit(self, visitor): 428 | return visitor.visit_this() 429 | 430 | def __str__(self): 431 | return 'THIS' 432 | 433 | 434 | class MoveParam(Instruction): 435 | def __init__(self, param): 436 | super(MoveParam, self).__init__() 437 | self.param = param 438 | 439 | def visit(self, visitor): 440 | return visitor.visit_move_param(self) 441 | 442 | def get_value(self): 443 | return self.param.get_value() 444 | 445 | def get_param(self): 446 | return self.param 447 | 448 | def __str__(self): 449 | return 'MoveParam(%s)' % self.param.get_value() 450 | 451 | 452 | class MoveExpression(Instruction): 453 | def __init__(self, lhs, rhs): 454 | super(MoveExpression, self).__init__() 455 | self.value = lhs 456 | self.operands.append(rhs) 457 | rhs.add_user(self) 458 | 459 | def visit(self, visitor): 460 | return visitor.visit_move(self.value, self.operands[0]) 461 | 462 | def resolve_type(self): 463 | op_type = self.operands[0].get_type() 464 | value_type = self.value.get_type() 465 | 466 | new_type = util.merge_type(op_type, value_type) 467 | if new_type is None: 468 | return False 469 | 470 | Changed = False 471 | if self.operands[0].type_sealed: 472 | Changed |= self.set_value_type(new_type) 473 | else: 474 | Changed |= self.value.refine_type(new_type) 475 | Changed |= self.operands[0].refine_type(new_type) 476 | 477 | return Changed 478 | 479 | def __str__(self): 480 | return '%s = %s' % (self.value, self.operands[0]) 481 | 482 | 483 | class MoveResultExpression(MoveExpression): 484 | def __init__(self, lhs, rhs): 485 | super(MoveResultExpression, self).__init__(lhs, rhs) 486 | 487 | def visit(self, visitor): 488 | return visitor.visit_move_result(self, self.value, self.operands[0]) 489 | 490 | def __str__(self): 491 | return '%s = %s' % (self.value, self.operands[0]) 492 | 493 | 494 | class ArrayStoreInstruction(Instruction): 495 | def __init__(self, rhs, array, index, _type): 496 | super(ArrayStoreInstruction, self).__init__() 497 | rhs.add_user(self) 498 | array.add_user(self) 499 | index.add_user(self) 500 | self.operands.append(rhs) 501 | self.operands.append(array) 502 | self.operands.append(index) 503 | self.elem_type = _type 504 | 505 | @property 506 | def array(self): 507 | return self.operands[1] 508 | 509 | @property 510 | def index(self): 511 | return self.operands[2] 512 | 513 | def resolve_type(self): 514 | elem_type = self.get_elem_type() 515 | Changed = self.index.set_type('I') 516 | if elem_type: 517 | Changed |= self.operands[0].refine_type(elem_type) 518 | Changed |= self.array.refine_type('[' + elem_type) 519 | return Changed 520 | 521 | def get_elem_type(self): 522 | if self.elem_type: 523 | return self.elem_type 524 | else: 525 | if util.is_array(self.array.get_type()): 526 | type1 = self.array.get_type()[1:] 527 | else: 528 | type1 = None 529 | type2 = self.operands[0].get_type() 530 | return util.merge_type(type1, type2) 531 | 532 | def visit(self, visitor): 533 | return visitor.visit_astore(self, self.array, 534 | self.index, 535 | self.operands[0]) 536 | 537 | def __str__(self): 538 | return '%s[%s] = %s' % (self.array, self.index, self.operands[0]) 539 | 540 | 541 | class StaticInstruction(Instruction): 542 | def __init__(self, rhs, klass, ftype, name): 543 | super(StaticInstruction, self).__init__() 544 | self.cls = util.get_type(klass) 545 | self.ftype = ftype 546 | self.name = name 547 | self.clsdesc = klass 548 | rhs.add_user(self) 549 | self.operands.append(rhs) 550 | 551 | def get_class(self): 552 | return self.clsdesc 553 | 554 | def get_field(self): 555 | return '%s.%s' % (self.clsdesc, self.name) 556 | 557 | def resolve_type(self): 558 | return self.operands[0].refine_type(self.ftype) 559 | 560 | def visit(self, visitor): 561 | return visitor.visit_put_static(self, self.clsdesc, self.name, self.ftype, self.operands[0]) 562 | 563 | def __str__(self): 564 | return '%s.%s = %s' % (self.cls, self.name, self.operands[0]) 565 | 566 | 567 | class InstanceInstruction(Instruction): 568 | def __init__(self, rhs, lhs, klass, atype, name): 569 | super(InstanceInstruction, self).__init__() 570 | self.atype = atype 571 | self.cls = util.get_type(klass) 572 | self.name = name 573 | self.clsdesc = klass 574 | 575 | lhs.add_user(self) 576 | rhs.add_user(self) 577 | self.operands.append(lhs) 578 | self.operands.append(rhs) 579 | 580 | def get_class(self): 581 | return self.clsdesc 582 | 583 | def get_field(self): 584 | return '%s.%s' % (self.clsdesc, self.name) 585 | 586 | def resolve_type(self): 587 | Changed = False 588 | Changed |= self.operands[0].refine_type(self.clsdesc) 589 | Changed |= self.operands[1].refine_type(self.atype) 590 | return Changed 591 | 592 | def visit(self, visitor): 593 | return visitor.visit_put_instance(self, self.operands[0], self.operands[1], self.atype, self.clsdesc, self.name) 594 | 595 | def __str__(self): 596 | return '%s.%s = %s' % (self.operands[0], self.name, self.operands[1]) 597 | 598 | 599 | class NewInstance(Instruction): 600 | def __init__(self, lhs, ins_type): 601 | super(NewInstance, self).__init__() 602 | self.value = lhs 603 | self.value.refine_type(ins_type) 604 | self.type = ins_type 605 | 606 | def get_type(self): 607 | return self.type 608 | 609 | def get_class(self): 610 | return self.type 611 | 612 | def resolve_type(self): 613 | return self.set_value_type(self.type) 614 | 615 | def visit(self, visitor): 616 | return visitor.visit_new(self, self.value, self.type) 617 | 618 | def __str__(self): 619 | return '%s = NEW(%s)' % (self.value, self.type) 620 | 621 | 622 | class InvokeInstruction(Instruction): 623 | def __init__(self, invoke_type, clsname, name, thiz, result, rtype, ptype, args, triple): 624 | super(InvokeInstruction, self).__init__() 625 | self.invoke_type = invoke_type 626 | self.is_static = True if invoke_type == 'static' else False 627 | self.clsdesc = clsname 628 | self.value = result 629 | self.name = name 630 | 631 | if not self.is_static: 632 | self.operands.append(thiz) 633 | thiz.add_user(self) 634 | 635 | if result: 636 | result.refine_type(rtype) 637 | 638 | self.rtype = rtype 639 | self.ptype = ptype 640 | self.args = [] 641 | for arg in args: 642 | arg.add_user(self) 643 | self.operands.append(arg) 644 | 645 | self.triple = triple 646 | assert (triple[1] == name) 647 | 648 | @property 649 | def thiz(self): 650 | if self.is_static: 651 | return None 652 | else: 653 | return self.operands[0] 654 | 655 | def get_class(self): 656 | return self.clsdesc 657 | 658 | def get_call_method(self): 659 | return '%s->%s(%s)' % (self.clsdesc, self.name, ''.join(self.ptype)) 660 | 661 | def resolve_type(self): 662 | Changed = False 663 | if self.is_static: 664 | assert self.thiz is None 665 | args = self.operands 666 | else: 667 | args = self.operands[1:] 668 | 669 | if self.thiz and self.thiz.get_type() != self.clsdesc: 670 | Changed |= self.thiz.refine_type(self.clsdesc) 671 | 672 | for idx, arg in enumerate(args): 673 | Changed |= arg.refine_type(self.ptype[idx]) 674 | 675 | if self.value: 676 | Changed |= self.set_value_type(self.rtype) 677 | return Changed 678 | 679 | def visit(self, visitor): 680 | if self.is_static: 681 | assert self.thiz is None 682 | args = self.operands 683 | else: 684 | args = self.operands[1:] 685 | return visitor.visit_invoke(self, self.invoke_type, self.name, self.thiz, self.ptype, 686 | self.rtype, args, self.clsdesc) 687 | 688 | def __str__(self): 689 | args = [] 690 | if not self.is_static: 691 | args.append(self.thiz) 692 | args.extend(self.args) 693 | return '%s.%s(%s)' % (self.clsdesc, self.name, 694 | ', '.join('%s' % i for i in args)) 695 | 696 | 697 | class InvokeRangeInstruction(InvokeInstruction): 698 | def __init__(self, clsname, name, rtype, ptype, args, triple): 699 | base = args.pop(0) 700 | super(InvokeRangeInstruction, self).__init__(clsname, name, base, rtype, 701 | ptype, args, triple) 702 | 703 | 704 | class InvokeDirectInstruction(InvokeInstruction): 705 | def __init__(self, clsname, name, base, rtype, ptype, args, triple): 706 | super(InvokeDirectInstruction, self).__init__( 707 | clsname, name, base, rtype, ptype, args, triple) 708 | 709 | 710 | class InvokeStaticInstruction(InvokeInstruction): 711 | def __init__(self, clsname, name, base, rtype, ptype, args, triple): 712 | super(InvokeStaticInstruction, self).__init__( 713 | clsname, name, base, rtype, ptype, args, triple) 714 | 715 | 716 | class InvokeSuperInstruction(InvokeInstruction): 717 | def __init__(self, clsname, name, base, rtype, ptype, args, triple): 718 | super(InvokeSuperInstruction, self).__init__(clsname, name, base, rtype, 719 | ptype, args, triple) 720 | 721 | 722 | class ReturnInstruction(Instruction): 723 | def __init__(self, arg, rtype=None): 724 | super(ReturnInstruction, self).__init__() 725 | self.rtype = rtype 726 | if arg: 727 | arg.add_user(self) 728 | self.operands.append(arg) 729 | 730 | @property 731 | def retval(self): 732 | if self.rtype: 733 | return self.operands[0] 734 | else: 735 | return None 736 | 737 | def visit(self, visitor): 738 | if self.rtype is None: 739 | return visitor.visit_return_void() 740 | else: 741 | return visitor.visit_return(self.retval) 742 | 743 | def resolve_type(self): 744 | if self.rtype: 745 | return self.retval.refine_type(self.rtype) 746 | else: 747 | return False 748 | 749 | def __str__(self): 750 | if self.retval is not None: 751 | return 'RETURN(%s)' % self.retval 752 | else: 753 | return 'RETURN' 754 | 755 | 756 | class NopExpression(Instruction): 757 | def __init__(self): 758 | super(NopExpression, self).__init__() 759 | pass 760 | 761 | def visit(self, visitor): 762 | return visitor.visit_nop() 763 | 764 | 765 | class GotoInst(Instruction): 766 | def __init__(self, target): 767 | super(GotoInst, self).__init__() 768 | self.target = target 769 | 770 | def visit(self, visitor): 771 | return visitor.visit_goto((self.offset // 2 + self.target) * 2) 772 | 773 | def __str__(self): 774 | return "GOTO(%x)" % (self.target) 775 | 776 | 777 | class SwitchExpression(Instruction): 778 | def __init__(self, src, cases): 779 | super(SwitchExpression, self).__init__() 780 | # src.set_type('I') 781 | src.add_user(self) 782 | self.cases = cases 783 | self.operands.append(src) 784 | 785 | def visit(self, visitor): 786 | return visitor.visit_switch_node(self, self.operands[0], self.cases) 787 | 788 | def resolve_type(self): 789 | return self.operands[0].refine_type('I') 790 | 791 | def __str__(self): 792 | return 'SWITCH(%s)' % (self.operands[0]) 793 | 794 | 795 | class CheckCastExpression(Instruction): 796 | def __init__(self, arg, _type, descriptor=None): 797 | super(CheckCastExpression, self).__init__() 798 | self.type = descriptor 799 | self.clsdesc = descriptor 800 | arg.add_user(self) 801 | self.operands.append(arg) 802 | 803 | def get_type(self): 804 | return self.type 805 | 806 | def get_class(self): 807 | return self.clsdesc 808 | 809 | def resolve_type(self): 810 | return self.operands[0].refine_type(self.type) 811 | 812 | def visit(self, visitor): 813 | return visitor.visit_check_cast(self, self.operands[0], self.clsdesc) 814 | 815 | def __str__(self): 816 | return 'CAST(%s) %s' % (self.type, self.operands[0]) 817 | 818 | 819 | class InstanceOfExpression(Instruction): 820 | def __init__(self, result, obj, clsdesc): 821 | super(InstanceOfExpression, self).__init__() 822 | self.clsdesc = clsdesc 823 | self.value = result 824 | obj.add_user(self) 825 | self.operands.append(obj) 826 | 827 | def get_class(self): 828 | return self.clsdesc 829 | 830 | def resolve_type(self): 831 | Changed = self.operands[0].refine_type(self.clsdesc) 832 | Changed |= self.set_value_type('Z') 833 | return Changed 834 | 835 | def visit(self, visitor): 836 | return visitor.visit_instanceof(self, self.value, self.operands[0], self.clsdesc) 837 | 838 | def __str__(self): 839 | return '(%s instanceof %s)' % (self.value, self.clsdesc) 840 | 841 | 842 | class ArrayExpression(Instruction): 843 | def __init__(self): 844 | super(ArrayExpression, self).__init__() 845 | 846 | 847 | class ArrayLoadExpression(ArrayExpression): 848 | def __init__(self, result, arg, index, _type): 849 | super(ArrayLoadExpression, self).__init__() 850 | self.value = result 851 | arg.add_user(self) 852 | index.add_user(self) 853 | self.operands.append(arg) 854 | self.operands.append(index) 855 | self.elem_type = _type 856 | 857 | @property 858 | def array(self): 859 | return self.operands[0] 860 | 861 | @property 862 | def idx(self): 863 | return self.operands[1] 864 | 865 | def resolve_type(self): 866 | Changed = False 867 | elem_type = self.get_elem_type() 868 | if elem_type: 869 | Changed |= self.value.refine_type(elem_type) 870 | Changed |= self.array.refine_type('[' + elem_type) 871 | Changed |= self.idx.refine_type("I") 872 | return Changed 873 | 874 | def visit(self, visitor): 875 | return visitor.visit_aload(self, self.value, self.array, self.idx) 876 | 877 | def get_elem_type(self): 878 | if self.elem_type: 879 | return self.elem_type 880 | else: 881 | if util.is_array(self.array.get_type()): 882 | type1 = self.array.get_type()[1:] 883 | else: 884 | type1 = None 885 | type2 = self.get_value_type() 886 | return util.merge_type(type1, type2) 887 | 888 | def __str__(self): 889 | return '%s = ARRAYLOAD(%s, %s)' % (self.value, self.array, self.idx) 890 | 891 | 892 | class ArrayLengthExpression(ArrayExpression): 893 | def __init__(self, result, array): 894 | super(ArrayLengthExpression, self).__init__() 895 | self.value = result 896 | array.add_user(self) 897 | self.operands.append(array) 898 | 899 | @property 900 | def array(self): 901 | return self.operands[0] 902 | 903 | def resolve_type(self): 904 | return self.set_value_type('I') 905 | 906 | def visit(self, visitor): 907 | return visitor.visit_alength(self, self.value, self.array) 908 | 909 | def __str__(self): 910 | return 'ARRAYLEN(%s)' % self.array 911 | 912 | 913 | class NewArrayExpression(ArrayExpression): 914 | def __init__(self, result, asize, atype): 915 | super(NewArrayExpression, self).__init__() 916 | self.value = result 917 | asize.add_user(self) 918 | self.type = atype 919 | self.elem_type = atype[1:] 920 | self.operands.append(asize) 921 | 922 | def get_size(self): 923 | return self.operands[0] 924 | 925 | def get_elem_type(self): 926 | return self.elem_type 927 | 928 | def get_class(self): 929 | return self.elem_type 930 | 931 | def resolve_type(self): 932 | Changed = False 933 | Changed |= self.set_value_type(self.type) 934 | Changed |= self.operands[0].set_type('I') 935 | return Changed 936 | 937 | def visit(self, visitor): 938 | return visitor.visit_new_array(self, self.value, self.type, self.get_size()) 939 | 940 | def __str__(self): 941 | return '%s = NEWARRAY_%s[%s]' % (self.value, self.type, self.get_size()) 942 | 943 | 944 | class FilledArrayExpression(ArrayExpression): 945 | def __init__(self, result, asize, atype, args): 946 | super(FilledArrayExpression, self).__init__() 947 | self.value = result 948 | result.refine_type(atype) 949 | self.size = asize 950 | self.type = atype 951 | self.elem_type = atype[1:] 952 | for arg in args: 953 | arg.add_user(self) 954 | self.operands.append(arg) 955 | 956 | def get_class(self): 957 | return self.elem_type 958 | 959 | def resolve_type(self): 960 | Changed = False 961 | Changed |= self.set_value_type(self.type) 962 | for arg in self.operands: 963 | Changed |= arg.refine_type(self.elem_type) 964 | return Changed 965 | 966 | def visit(self, visitor): 967 | return visitor.visit_filled_new_array(self, self.value, self.type, self.size, self.operands) 968 | 969 | 970 | class FillArrayExpression(ArrayExpression): 971 | def __init__(self, reg, value): 972 | super(FillArrayExpression, self).__init__() 973 | self.filldata = value 974 | self.operands.append(reg) 975 | self.reg.add_user(self) 976 | 977 | @property 978 | def reg(self): 979 | return self.operands[0] 980 | 981 | def resolve_type(self): 982 | return False 983 | 984 | def visit(self, visitor): 985 | return visitor.visit_fill_array(self, self.reg, self.filldata) 986 | 987 | 988 | class MoveExceptionExpression(Instruction): 989 | def __init__(self, value, _type): 990 | super(MoveExceptionExpression, self).__init__() 991 | self.value = value 992 | self.value.refine_type(_type) 993 | self.type = _type 994 | 995 | def resolve_type(self): 996 | return self.value.refine_type(self.type) 997 | 998 | def visit(self, visitor): 999 | return visitor.visit_move_exception(self, self.value) 1000 | 1001 | def __str__(self): 1002 | return 'MOVE_EXCEPT %s(%s)' % (self.value, self.value.get_type()) 1003 | 1004 | 1005 | class MonitorEnterExpression(Instruction): 1006 | def __init__(self, ref): 1007 | super(MonitorEnterExpression, self).__init__() 1008 | ref.add_user(self) 1009 | self.operands.append(ref) 1010 | 1011 | def resolve_type(self): 1012 | return self.operands[0].refine_type('Ljava/lang/Object;') 1013 | 1014 | def visit(self, visitor): 1015 | return visitor.visit_monitor_enter(self, self.operands[0]) 1016 | 1017 | def __str__(self): 1018 | return 'MonitorEnter (%s)' % self.operands[0] 1019 | 1020 | 1021 | class MonitorExitExpression(Instruction): 1022 | def __init__(self, ref): 1023 | super(MonitorExitExpression, self).__init__() 1024 | ref.add_user(self) 1025 | self.operands.append(ref) 1026 | 1027 | def resolve_type(self): 1028 | return self.operands[0].refine_type('Ljava/lang/Object;') 1029 | 1030 | def visit(self, visitor): 1031 | return visitor.visit_monitor_exit(self, self.operands[0]) 1032 | 1033 | def __str__(self): 1034 | return 'MonitorExit (%s)' % self.operands[0] 1035 | 1036 | 1037 | class ThrowExpression(Instruction): 1038 | def __init__(self, ref): 1039 | super(ThrowExpression, self).__init__() 1040 | ref.add_user(self) 1041 | self.operands.append(ref) 1042 | 1043 | def resolve_type(self): 1044 | return self.operands[0].set_type('Ljava/lang/Throwable;') 1045 | 1046 | def visit(self, visitor): 1047 | return visitor.visit_throw(self, self.operands[0]) 1048 | 1049 | def __str__(self): 1050 | return 'Throw %s' % self.operands[0] 1051 | 1052 | 1053 | class BinaryExpression(Instruction): 1054 | def __init__(self, op, result, arg1, arg2, vtype): 1055 | super(BinaryExpression, self).__init__() 1056 | self.value = result 1057 | self.op = op 1058 | 1059 | self.operands.append(arg1) 1060 | self.operands.append(arg2) 1061 | 1062 | self.op_type = vtype 1063 | 1064 | arg1.add_user(self) 1065 | arg2.add_user(self) 1066 | 1067 | def resolve_type(self): 1068 | Changed = False 1069 | Changed |= self.value.refine_type(self.op_type) 1070 | for op in self.operands: 1071 | Changed |= op.refine_type(self.op_type) 1072 | return Changed 1073 | 1074 | def visit(self, visitor): 1075 | return visitor.visit_binary_expression(self, self.value, self.op, self.operands[0], self.operands[1]) 1076 | 1077 | def __str__(self): 1078 | return '%s = %s %s %s' % (self.value, self.operands[0], self.op, self.operands[1]) 1079 | 1080 | 1081 | class BinaryCompExpression(BinaryExpression): 1082 | def __init__(self, op, result, arg1, arg2, _type): 1083 | super(BinaryCompExpression, self).__init__(op, result, arg1, arg2, _type) 1084 | result.refine_type('I') 1085 | 1086 | def resolve_type(self): 1087 | Changed = False 1088 | Changed |= self.value.refine_type('I') 1089 | for op in self.operands: 1090 | Changed |= op.refine_type(self.op_type) 1091 | return Changed 1092 | 1093 | 1094 | class BinaryExpression2Addr(BinaryExpression): 1095 | def __init__(self, result, op, dest, arg, _type): 1096 | super(BinaryExpression2Addr, self).__init__(result, op, dest, arg, _type) 1097 | 1098 | 1099 | class BinaryExpressionLit(BinaryExpression): 1100 | def __init__(self, op, result, arg1, arg2): 1101 | super(BinaryExpressionLit, self).__init__(op, result, arg1, arg2, 'I') 1102 | 1103 | 1104 | class UnaryExpression(Instruction): 1105 | def __init__(self, result, op, arg, _type): 1106 | super(UnaryExpression, self).__init__() 1107 | result.refine_type(_type) 1108 | self.value = result 1109 | self.op = op 1110 | self.type = _type 1111 | self.operands.append(arg) 1112 | arg.add_user(self) 1113 | 1114 | def resolve_type(self): 1115 | return self.set_value_type(self.type) 1116 | 1117 | def visit(self, visitor): 1118 | return visitor.visit_unary_expression(self, self.value, self.op, self.operands[0]) 1119 | 1120 | def __str__(self): 1121 | return '(%s, %s)' % (self.op, self.operands[0]) 1122 | 1123 | 1124 | class CastExpression(Instruction): 1125 | def __init__(self, result, op, dest_type, arg, src_type): 1126 | super(CastExpression, self).__init__() 1127 | self.type = dest_type 1128 | self.src_type = src_type 1129 | self.op = op 1130 | self.value = result 1131 | self.value.refine_type(dest_type) 1132 | self.operands.append(arg) 1133 | arg.add_user(self) 1134 | 1135 | def get_type(self): 1136 | return self.type 1137 | 1138 | def resolve_type(self): 1139 | Changed = self.value.refine_type(self.type) 1140 | Changed |= self.operands[0].refine_type(self.src_type) 1141 | return Changed 1142 | 1143 | def visit(self, visitor): 1144 | return visitor.visit_cast(self.value, self.op, self.operands[0]) 1145 | 1146 | def __str__(self): 1147 | return '%s = %s(%s)' % (self.value, self.op, self.operands[0]) 1148 | 1149 | 1150 | CONDS = {'==': '!=', '!=': '==', '<': '>=', '<=': '>', '>=': '<', '>': '<=', } 1151 | 1152 | 1153 | class ConditionalExpression(Instruction): 1154 | def __init__(self, op, arg1, arg2, target): 1155 | super(ConditionalExpression, self).__init__() 1156 | self.op = op 1157 | self.target = target 1158 | arg1.add_user(self) 1159 | arg2.add_user(self) 1160 | self.operands.append(arg1) 1161 | self.operands.append(arg2) 1162 | 1163 | def get_target(self): 1164 | return self.target 1165 | 1166 | def resolve_type(self): 1167 | Changed = False 1168 | type1 = self.operands[0].get_type() 1169 | type2 = self.operands[1].get_type() 1170 | 1171 | # 引用类型可以比较 1172 | if (util.is_ref(type1) or util.is_array(type1)) and (util.is_ref(type2) or util.is_array(type2)): 1173 | return False 1174 | 1175 | new_type = util.merge_type(type1, type2) 1176 | Changed |= self.operands[0].refine_type(new_type) 1177 | Changed |= self.operands[1].refine_type(new_type) 1178 | return Changed 1179 | 1180 | def visit(self, visitor): 1181 | return visitor.visit_cond_expression(self, self.op, self.operands[0], 1182 | self.operands[1], (self.offset // 2 + self.target) * 2, 1183 | self.next_offset) 1184 | 1185 | def __str__(self): 1186 | return 'IF %s %s %s' % (self.operands[0], self.op, self.operands[1]) 1187 | 1188 | 1189 | class ConditionalZExpression(Instruction): 1190 | def __init__(self, op, arg, target): 1191 | super(ConditionalZExpression, self).__init__() 1192 | self.op = op 1193 | self.target = target 1194 | arg.add_user(self) 1195 | self.operands.append(arg) 1196 | 1197 | # 无法使用该指令无法推断出操作类型 1198 | def resolve_type(self): 1199 | return False 1200 | 1201 | def visit(self, visitor): 1202 | return visitor.visit_condz_expression(self, self.op, self.operands[0], (self.offset // 2 + self.target) * 2, 1203 | self.next_offset) 1204 | 1205 | def __str__(self): 1206 | return 'IF %s %s 0' % (self.operands[0], self.op) 1207 | 1208 | 1209 | class InstanceExpression(Instruction): 1210 | def __init__(self, result, arg, klass, ftype, name): 1211 | super(InstanceExpression, self).__init__() 1212 | self.value = result 1213 | self.cls = util.get_type(klass) 1214 | self.ftype = ftype 1215 | self.name = name 1216 | self.clsdesc = klass 1217 | 1218 | arg.add_user(self) 1219 | self.operands.append(arg) 1220 | 1221 | def get_class(self): 1222 | return self.clsdesc 1223 | 1224 | def get_field(self): 1225 | return '%s.%s' % (self.clsdesc, self.name) 1226 | 1227 | def resolve_type(self): 1228 | Changed = self.set_value_type(self.ftype) 1229 | Changed |= self.operands[0].refine_type(self.clsdesc) 1230 | return Changed 1231 | 1232 | def get_type(self): 1233 | return self.ftype 1234 | 1235 | def visit(self, visitor): 1236 | return visitor.visit_get_instance( 1237 | self, 1238 | self.value, 1239 | self.operands[0], 1240 | self.ftype, 1241 | self.clsdesc, 1242 | self.name) 1243 | 1244 | def __str__(self): 1245 | return '%s = %s.%s' % (self.value, self.operands[0], self.name) 1246 | 1247 | 1248 | class StaticExpression(Instruction): 1249 | def __init__(self, result, cls_name, field_type, field_name): 1250 | super(StaticExpression, self).__init__() 1251 | self.value = result 1252 | self.cls = util.get_type(cls_name) 1253 | self.ftype = field_type 1254 | self.name = field_name 1255 | self.clsdesc = cls_name 1256 | 1257 | def get_class(self): 1258 | return self.clsdesc 1259 | 1260 | def get_field(self): 1261 | return '%s.%s' % (self.clsdesc, self.name) 1262 | 1263 | def resolve_type(self): 1264 | return self.set_value_type(self.ftype) 1265 | 1266 | def visit(self, visitor): 1267 | return visitor.visit_get_static(self, self.value, self.ftype, self.clsdesc, self.name) 1268 | 1269 | def __str__(self): 1270 | return '%s = %s.%s' % (self.value, self.cls, self.name) 1271 | -------------------------------------------------------------------------------- /dex2c/util.py: -------------------------------------------------------------------------------- 1 | # encoding=utf8 2 | # 3 | # Copyright (c) 2012 Geoffroy Gueguen 4 | # All Rights Reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS-IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | import logging 19 | 20 | logger = logging.getLogger('dex2c.util') 21 | 22 | TYPE_DESCRIPTOR = { 23 | 'V': 'Void', 24 | 'Z': 'Boolean', 25 | 'B': 'Byte', 26 | 'S': 'Short', 27 | 'C': 'Char', 28 | 'I': 'Int', 29 | 'J': 'Long', 30 | 'F': 'Float', 31 | 'D': 'Double', 32 | } 33 | 34 | DECL_JNI_TYPE = { 35 | 'Z': 'jboolean', # jboolean 36 | 'B': 'jbyte', 37 | 'S': 'jshort', 38 | 'C': 'jchar', 39 | 'I': 'jint', 40 | 'J': 'jlong', 41 | 'F': 'jfloat', 42 | 'D': 'jdouble', 43 | 'V': 'void', 44 | } 45 | 46 | PRIMITIVE_TYPE_ORDER = { 47 | 'Z': 1, 48 | 'B': 2, 49 | 'S': 3, # signed 50 | 'C': 3, # unsigned 51 | 'I': 5, 52 | 'J': 6, 53 | 'F': 8, 54 | 'D': 9, 55 | } 56 | 57 | ACCESS_FLAGS_CLASSES = { 58 | 0x1: 'public', 59 | 0x2: 'private', 60 | 0x4: 'protected', 61 | 0x8: 'static', 62 | 0x10: 'final', 63 | 0x200: 'interface', 64 | 0x400: 'abstract', 65 | 0x1000: 'synthetic', 66 | 0x2000: 'annotation', 67 | 0x4000: 'enum', 68 | } 69 | 70 | ACCESS_FLAGS_FIELDS = { 71 | 0x1: 'public', 72 | 0x2: 'private', 73 | 0x4: 'protected', 74 | 0x8: 'static', 75 | 0x10: 'final', 76 | 0x40: 'volatile', 77 | 0x80: 'transient', 78 | 0x1000: 'synthetic', 79 | 0x4000: 'enum', 80 | } 81 | 82 | ACCESS_FLAGS_METHODS = { 83 | 0x1: 'public', 84 | 0x2: 'private', 85 | 0x4: 'protected', 86 | 0x8: 'static', 87 | 0x10: 'final', 88 | 0x20: 'synchronized', 89 | 0x40: 'bridge', 90 | 0x80: 'varargs', 91 | 0x100: 'native', 92 | 0x400: 'abstract', 93 | 0x800: 'strictfp', 94 | 0x1000: 'synthetic', 95 | 0x10000: 'constructor', 96 | 0x20000: 'declared_synchronized', 97 | } 98 | 99 | ACCESS_ORDER = [0x1, 0x4, 0x2, 0x400, 0x8, 0x10, 0x80, 0x40, 0x20, 0x100, 0x800, 100 | 0x200, 0x1000, 0x2000, 0x4000, 0x10000, 0x20000] 101 | 102 | TYPE_LEN = {'J': 2, 'D': 2, } 103 | 104 | 105 | def get_access_class(access): 106 | sorted_access = [i for i in ACCESS_ORDER if i & access] 107 | return [ACCESS_FLAGS_CLASSES.get(flag, 'unkn_%d' % flag) 108 | for flag in sorted_access] 109 | 110 | 111 | def get_access_method(access): 112 | sorted_access = [i for i in ACCESS_ORDER if i & access] 113 | return [ACCESS_FLAGS_METHODS.get(flag, 'unkn_%d' % flag) 114 | for flag in sorted_access] 115 | 116 | 117 | def get_access_field(access): 118 | sorted_access = [i for i in ACCESS_ORDER if i & access] 119 | return [ACCESS_FLAGS_FIELDS.get(flag, 'unkn_%d' % flag) 120 | for flag in sorted_access] 121 | 122 | 123 | def build_path(graph, node1, node2, path=None): 124 | """ 125 | Build the path from node1 to node2. 126 | The path is composed of all the nodes between node1 and node2, 127 | node1 excluded. Although if there is a loop starting from node1, it will be 128 | included in the path. 129 | """ 130 | if path is None: 131 | path = [] 132 | if node1 is node2: 133 | return path 134 | path.append(node2) 135 | for pred in graph.all_preds(node2): 136 | if pred in path: 137 | continue 138 | build_path(graph, node1, pred, path) 139 | return path 140 | 141 | 142 | def common_dom(idom, cur, pred): 143 | if not (cur and pred): 144 | return cur or pred 145 | while cur is not pred: 146 | while cur.num < pred.num: 147 | pred = idom[pred] 148 | while cur.num > pred.num: 149 | cur = idom[cur] 150 | return cur 151 | 152 | 153 | def merge_inner(clsdict): 154 | """ 155 | Merge the inner class(es) of a class: 156 | e.g class A { ... } class A$foo{ ... } class A$bar{ ... } 157 | ==> class A { class foo{...} class bar{...} ... } 158 | """ 159 | samelist = False 160 | done = {} 161 | while not samelist: 162 | samelist = True 163 | classlist = list(clsdict.keys()) 164 | for classname in classlist: 165 | parts_name = classname.rsplit('$', 1) 166 | if len(parts_name) > 1: 167 | mainclass, innerclass = parts_name 168 | innerclass = innerclass[:-1] # remove ';' of the name 169 | mainclass += ';' 170 | if mainclass in clsdict: 171 | clsdict[mainclass].add_subclass(innerclass, 172 | clsdict[classname]) 173 | clsdict[classname].name = innerclass 174 | done[classname] = clsdict[classname] 175 | del clsdict[classname] 176 | samelist = False 177 | elif mainclass in done: 178 | cls = done[mainclass] 179 | cls.add_subclass(innerclass, clsdict[classname]) 180 | clsdict[classname].name = innerclass 181 | done[classname] = done[mainclass] 182 | del clsdict[classname] 183 | samelist = False 184 | 185 | 186 | def get_type_size(param): 187 | """ 188 | Return the number of register needed by the type @param 189 | """ 190 | return TYPE_LEN.get(param, 1) 191 | 192 | 193 | def get_type(atype, size=None): 194 | """ 195 | Retrieve the java type of a descriptor (e.g : I) 196 | """ 197 | res = TYPE_DESCRIPTOR.get(atype) 198 | if res is None: 199 | if atype[0] == 'L': 200 | res = atype[1:-1] 201 | elif atype[0] == '[': 202 | res = atype 203 | else: 204 | res = atype 205 | logger.debug('Unknown descriptor: "%s".', atype) 206 | return res 207 | 208 | 209 | def get_fully_qualified_class_name(signature): 210 | res = TYPE_DESCRIPTOR.get(signature) 211 | if res is None: 212 | if signature[0] == 'L': 213 | res = signature[1:-1] 214 | else: 215 | res = signature 216 | logger.debug('Unknown descriptor: "%s".', signature) 217 | return res 218 | 219 | 220 | def get_type_descriptor(atype): 221 | res = TYPE_DESCRIPTOR.get(atype) 222 | if res is None: 223 | res = 'Object' 224 | return res 225 | 226 | 227 | def is_primitive_type(atype): 228 | return atype and atype in PRIMITIVE_TYPE_ORDER 229 | 230 | 231 | def get_params_type(descriptor): 232 | """ 233 | Return the parameters type of a descriptor (e.g (IC)V) 234 | """ 235 | params = descriptor.split(')')[0][1:].split() 236 | if params: 237 | return [param for param in params] 238 | return [] 239 | 240 | 241 | def get_method_triple(method, return_type=True): 242 | method_triple = method.get_triple() 243 | cls_name = method.class_name 244 | _, name, proto = method_triple 245 | if return_type: 246 | return cls_name, name, proto 247 | else: 248 | index = proto.find(')') 249 | proto = proto[:index + 1] 250 | return cls_name, name, proto 251 | 252 | 253 | def get_native_type(jtype): 254 | res = DECL_JNI_TYPE.get(jtype) 255 | if res is None: 256 | if jtype == 'Ljava/lang/String;': 257 | res = 'jstring' 258 | elif jtype[0] == 'L': 259 | res = 'jobject' 260 | elif jtype[0] == '[': 261 | res = 'jarray' 262 | else: 263 | res = 'jobject' 264 | logger.debug('Unknown descriptor: "%s".', jtype) 265 | return res 266 | 267 | 268 | def is_int(atype): 269 | return atype and atype in 'ZBCSIJ' 270 | 271 | 272 | def is_long(atype): 273 | return atype == 'J' 274 | 275 | 276 | def is_float(atype): 277 | return atype and atype in 'FD' 278 | 279 | 280 | def is_ref(atype): 281 | return atype and (atype[0] == 'L' or atype[0] == '[') 282 | 283 | 284 | def is_array(atype): 285 | return atype and atype[0] == '[' 286 | 287 | 288 | def is_java_lang_object(atype): 289 | return atype and atype == 'Ljava/lang/Object;' 290 | 291 | 292 | def is_java_lang_object_array(atype): 293 | return is_array(atype) and atype.endswith('Ljava/lang/Object;') 294 | 295 | 296 | # Use for variable declaration 297 | def get_cdecl_type(atype): 298 | if atype in 'ZBCSI': 299 | return 'jint' 300 | elif atype == 'J': 301 | return 'jlong' 302 | elif atype == 'F': 303 | return 'jfloat' 304 | elif atype == 'D': 305 | return 'jdouble' 306 | else: 307 | return 'jobject' 308 | 309 | 310 | # art/runtime/utils.cc 311 | def MangleForJni(name): 312 | result = '' 313 | for ch in name: 314 | if ('A' <= ch <= 'Z') or ('a' <= ch <= 'z') or ('0' <= ch <= '9'): 315 | result += ch 316 | elif ch == '.' or ch == '/': 317 | result += "_" 318 | elif ch == '_': 319 | result += "_1" 320 | elif ch == ';': 321 | result += "_2" 322 | elif ch == '[': 323 | result += "_3" 324 | else: 325 | result += '_0%04x' % (ord(ch)) 326 | return result 327 | 328 | 329 | def JniShortName(cls_name, method_name): 330 | assert cls_name[0] == 'L' 331 | assert cls_name[-1] == ';' 332 | cls_name = cls_name[1:-1] 333 | short_name = 'Java_' 334 | short_name += MangleForJni(cls_name) 335 | short_name += '_' 336 | short_name += MangleForJni(method_name) 337 | return short_name 338 | 339 | 340 | def JniLongName(cls_name, method_name, signature): 341 | long_name = '' 342 | long_name += JniShortName(cls_name, method_name) 343 | long_name += "__" 344 | signature = signature[1:] 345 | index = signature.find(')') 346 | long_name += MangleForJni(signature[:index]) 347 | return long_name 348 | 349 | 350 | def compare_primitive_type(type1, type2): 351 | if type1 is None and type2 is None: 352 | return 0 353 | if type1 is None: 354 | # type1 < type2 355 | return -1 356 | elif type2 is None: 357 | # type1 > type2 358 | return 1 359 | 360 | assert is_primitive_type(type1) 361 | assert is_primitive_type(type2) 362 | 363 | o1 = PRIMITIVE_TYPE_ORDER.get(type1) 364 | o2 = PRIMITIVE_TYPE_ORDER.get(type2) 365 | return o1 - o2 366 | 367 | 368 | def get_smaller_type(type1, type2): 369 | return type1 if compare_primitive_type(type1, type2) < 0 else type2 370 | 371 | 372 | def get_bigger_type(type1, type2): 373 | return type1 if compare_primitive_type(type1, type2) > 0 else type2 374 | 375 | 376 | def merge_array_type(type1, type2): 377 | assert is_array(type1) or is_array(type2) 378 | if is_java_lang_object(type2): 379 | return type2 380 | elif is_java_lang_object(type1): 381 | return type1 382 | if is_array(type1): 383 | if is_array(type2): 384 | new_type = merge_type(type1[1:], type2[1:]) 385 | if new_type: 386 | return '[' + new_type 387 | else: 388 | return None 389 | else: 390 | return 'Ljava/lang/Object;' 391 | else: 392 | return merge_array_type(type2, type1) 393 | 394 | 395 | # return bigger type 396 | def merge_reference_type(type1, type2): 397 | assert is_ref(type1) and is_ref(type2) 398 | if type1 == type2: 399 | return type1 400 | elif is_java_lang_object(type1) and is_ref(type2): 401 | return type1 402 | elif is_java_lang_object(type2) and is_ref(type1): 403 | return type2 404 | else: 405 | return 'Ljava/lang/Object;' 406 | 407 | 408 | def merge_type(type1, type2): 409 | if type1 is None and type2 is None: 410 | return None 411 | if type1 is None: 412 | return type2 413 | if type2 is None: 414 | return type1 415 | 416 | if (is_int(type1) and is_int(type2)) or \ 417 | (is_float(type1) and is_float(type2)): 418 | return get_bigger_type(type1, type2) 419 | elif is_array(type1) or is_array(type2): 420 | new_type = merge_array_type(type1, type2) 421 | if new_type is None: 422 | return 'Ljava/lang/Object;' 423 | else: 424 | return new_type 425 | elif is_ref(type1) or is_ref(type2): 426 | return merge_reference_type(type1, type2) 427 | else: 428 | return None 429 | 430 | 431 | def is_synthetic_method(method): 432 | return method.get_access_flags() & 0x1000 433 | 434 | 435 | def is_native_method(method): 436 | return method.get_access_flags() & 0x100 437 | 438 | 439 | def hex_escape_string(s): 440 | result = '' 441 | s = s.encode('utf8') 442 | for c in s: 443 | result += '\\x%02x' % c 444 | return result 445 | # ERROR: hex escape sequence out of range 446 | # result = '' 447 | # s = s.encode('utf8') 448 | # for c in s: 449 | # if 0x20 <= c < 0x7f: 450 | # if c in (0x22, 0x27, 0x5c): # " ' \ 451 | # result += '\\' 452 | # result += chr(c) 453 | # continue 454 | # if c < 0x20: 455 | # if c == 0x9: 456 | # result += '\\t' 457 | # elif c == 0xa: 458 | # result += '\\n' 459 | # elif c == 0xd: 460 | # result += '\\r' 461 | # else: 462 | # result += '\\x%02x' % c 463 | # continue 464 | # result += '\\x%02x' % c 465 | # return result 466 | 467 | 468 | def get_cdecl_name(clsname): 469 | assert is_ref(clsname) 470 | return clsname[1:-1].replace('/', '_').repace('[', '_') 471 | 472 | 473 | # ERROR: universal character name refers to a control character 474 | def string(s): 475 | """ 476 | Convert a string to a escaped ASCII representation including quotation marks 477 | :param s: a string 478 | :return: ASCII escaped string 479 | """ 480 | ret = [] 481 | for c in s: 482 | if ' ' <= c < '\x7f': 483 | if c == "'" or c == '"' or c == '\\': 484 | ret.append('\\') 485 | ret.append(c) 486 | continue 487 | elif c <= '\x7f': 488 | if c in ('\r', '\n', '\t'): 489 | # unicode-escape produces bytes 490 | ret.append(c.encode('unicode-escape').decode("ascii")) 491 | continue 492 | i = ord(c) 493 | ret.append('\\u') 494 | ret.append('%x' % (i >> 12)) 495 | ret.append('%x' % ((i >> 8) & 0x0f)) 496 | ret.append('%x' % ((i >> 4) & 0x0f)) 497 | ret.append('%x' % (i & 0x0f)) 498 | return ''.join(ret) 499 | -------------------------------------------------------------------------------- /dex2c/writer.py: -------------------------------------------------------------------------------- 1 | # encoding=utf8 2 | # 3 | # Copyright (c) 2012 Geoffroy Gueguen 4 | # All Rights Reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS-IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | import logging 19 | from builtins import object 20 | from builtins import range 21 | from builtins import zip 22 | from struct import unpack 23 | 24 | from dex2c import util 25 | from dex2c.instruction import BinaryCompExpression, Constant 26 | from dex2c.opcode_ins import Op 27 | from dex2c.util import get_type_descriptor, get_native_type, JniLongName, is_primitive_type, \ 28 | get_cdecl_type, get_type 29 | 30 | logger = logging.getLogger('dex2c.writer') 31 | 32 | 33 | class TmpnameAllocator(object): 34 | def __init__(self, vars, prefix=''): 35 | self.numbering = {} 36 | self.next_name = 0 37 | self.prefix = prefix 38 | for item in vars: 39 | assert item is not None 40 | if item is None: 41 | raise Exception("Unable to allocate tmpname for None.") 42 | if item in self.numbering: 43 | continue 44 | else: 45 | self.numbering[item] = self.next_name 46 | self.next_name += 1 47 | 48 | def get_name(self, item): 49 | assert item and item in self.numbering 50 | logger.debug("%s - > %s%s" % (item, self.prefix, self.numbering[item])) 51 | return '%s%s' % (self.prefix, self.numbering[item]) 52 | 53 | 54 | class Writer(object): 55 | def __init__(self, irmethod, dynamic_register): 56 | self.graph = irmethod.graph 57 | self.method = irmethod.method 58 | self.irmethod = irmethod 59 | self.visited_nodes = set() 60 | self.buffer = [] 61 | self.dynamic_register = dynamic_register 62 | self.prototype = [] 63 | 64 | entry = irmethod.entry 65 | self.ra = irmethod.ra 66 | self.ca = TmpnameAllocator(entry.class_to_declare, 'cls').get_name 67 | self.fa = TmpnameAllocator(entry.field_to_declare, 'fld').get_name 68 | self.ma = TmpnameAllocator(entry.method_to_declare, 'mth').get_name 69 | 70 | def __str__(self): 71 | return ''.join(self.buffer) 72 | 73 | def get_prototype(self): 74 | return ''.join(self.prototype) 75 | 76 | def write_trace(self, ins): 77 | s = ins.dump() 78 | if s: 79 | self.write('LOGD("%s");\n' % s) 80 | 81 | def write(self, s): 82 | self.buffer.append(s) 83 | 84 | def end_ins(self): 85 | self.write(';\n') 86 | 87 | def visit_ins(self, ins): 88 | ins.visit(self) 89 | 90 | def visit_move_param(self, ins): 91 | param = ins.get_param() 92 | value = param.get_value() 93 | self.write('v%s = (%s)' % (self.ra(value), get_cdecl_type(value.get_type()))) 94 | param.visit(self) 95 | self.write(";\n") 96 | 97 | def write_method(self): 98 | access = util.get_access_method(self.method.get_access_flags()) 99 | class_name = self.method.get_class_name() 100 | _, name, proto = self.method.get_triple() 101 | 102 | jni_name = JniLongName(class_name, name, proto) 103 | 104 | self.write("\n/* %s->%s%s */\n" % (class_name, name, proto)) 105 | 106 | if self.dynamic_register: 107 | self.write(get_native_type(self.irmethod.rtype) + ' ') 108 | self.prototype.append(get_native_type(self.irmethod.rtype) + ' ') 109 | self.prototype.append(jni_name) 110 | else: 111 | self.write('extern "C" JNIEXPORT %s JNICALL\n' % get_native_type(self.irmethod.rtype)) 112 | 113 | self.write(jni_name) 114 | params = self.irmethod.params 115 | if 'static' not in access: 116 | params = params[1:] 117 | proto = '' 118 | if self.irmethod.params_type: 119 | proto = ', '.join(['%s p%s' % (get_native_type(p_type), param) for p_type, 120 | param in 121 | zip(self.irmethod.params_type, params)]) 122 | if proto: 123 | self.write('(JNIEnv *env, jobject thiz, %s)' % proto) 124 | if self.dynamic_register: 125 | self.prototype.append('(JNIEnv *env, jobject thiz, %s)' % proto) 126 | else: 127 | self.write('(JNIEnv *env, jobject thiz)') 128 | if self.dynamic_register: 129 | self.prototype.append('(JNIEnv *env, jobject thiz)') 130 | self.write('{\n') 131 | nodes = self.irmethod.irblocks 132 | for node in nodes: 133 | self.visit_node(node) 134 | 135 | for lp in self.irmethod.landing_pads: 136 | self.write('\n') 137 | self.visit_landing_pad(lp) 138 | 139 | return_type = self.irmethod.rtype 140 | if return_type[0] != 'V': 141 | if return_type[0] == 'L': 142 | self.write("EX_UnwindBlock: return NULL;\n") 143 | else: 144 | self.write("EX_UnwindBlock: return (%s)0;\n" % (get_native_type(return_type))) 145 | else: 146 | self.write("EX_UnwindBlock: return;\n") 147 | 148 | self.write('}\n') 149 | 150 | def visit_node(self, node): 151 | if node in self.visited_nodes: 152 | return 153 | self.visited_nodes.add(node) 154 | var_declared = set() 155 | for var in node.var_to_declare: 156 | var_type = var.get_type() 157 | r = self.ra(var) 158 | if r in var_declared: 159 | continue 160 | var_declared.add(r) 161 | self.write('%s v%s' % (get_cdecl_type(var_type), r)) 162 | if util.is_ref(var_type): 163 | self.write(' = NULL') 164 | self.write(";\n") 165 | 166 | if node.var_to_declare and self.irmethod.landing_pads: 167 | self.write("jthrowable exception;\n") 168 | 169 | declared = set() 170 | to_declare = [] 171 | for jclass in node.class_to_declare: 172 | if jclass in declared: 173 | continue 174 | declared.add(jclass) 175 | to_declare.append('%s = NULL' % (self.ca(jclass))) 176 | if to_declare: 177 | self.write('jclass %s;\n' % (','.join(to_declare))) 178 | 179 | declared.clear() 180 | to_declare.clear() 181 | for jfield in node.field_to_declare: 182 | if jfield in declared: 183 | continue 184 | declared.add(jfield) 185 | to_declare.append('%s = NULL' % (self.fa(jfield))) 186 | if to_declare: 187 | self.write('jfieldID %s;\n' % (','.join(to_declare))) 188 | 189 | declared.clear() 190 | to_declare.clear() 191 | for jmethod in node.method_to_declare: 192 | if jmethod in declared: 193 | continue 194 | declared.add(jmethod) 195 | to_declare.append('%s = NULL' % (self.ma(jmethod))) 196 | if to_declare: 197 | self.write('jmethodID %s;\n' % (', '.join(to_declare))) 198 | 199 | for ins in node.move_param_insns: 200 | ins.visit(self) 201 | node.visit(self) 202 | 203 | def visit_landing_pad(self, landing_pad): 204 | self.write("%s:\n" % (landing_pad.label)) 205 | self.write("D2C_GET_PENDING_EX\n") 206 | for atype, handle in landing_pad.handles.items(): 207 | self.write('if(d2c_is_instance_of(env, exception, "%s")) {\n' % (get_type(atype))) 208 | self.write('goto L%d;\n' % handle.num) 209 | self.write('}\n') 210 | self.write("D2C_GOTO_UNWINDBLOCK\n") 211 | 212 | def write_delete_dead_local_reference(self, val): 213 | if val.use_empty() and val.get_register() < 0: 214 | self.write_kill_local_reference(val, True) 215 | 216 | def write_kill_local_reference(self, val, tmp=False): 217 | if val is None: 218 | return 219 | 220 | if get_native_type(val.get_type()) in ('jarray', 'jobject', 'jstring'): 221 | if tmp or val.get_register() >= 0: 222 | reg = self.ra(val) 223 | self.write('if (v%s) {\n' % (reg)) 224 | self.write('LOGD("env->DeleteLocalRef(%%p):v%s", v%s);\n' % (reg, reg)) 225 | self.write('env->DeleteLocalRef(v%s);\n' % reg) 226 | self.write('}\n') 227 | 228 | def visit_switch_node(self, ins, switch, cases): 229 | self.write('switch (') 230 | switch.visit(self) 231 | self.write(') {\n') 232 | for case, offset in cases: 233 | node = self.irmethod.offset_to_node[offset + ins.offset] 234 | self.write('case %d: goto L%d;\n' % (case, node.num)) 235 | self.write('}\n') 236 | 237 | def visit_statement_node(self, stmt): 238 | if stmt.num >= 0: 239 | self.write("L%d:\n" % stmt.num) 240 | for ins in stmt.get_instr_list(): 241 | self.visit_ins(ins) 242 | 243 | def visit_return_node(self, ret): 244 | for ins in ret.get_ins(): 245 | self.visit_ins(ins) 246 | 247 | def visit_load_constant(self, ins, obfus=False): 248 | val = ins.get_value() 249 | atype = val.get_type() 250 | self.write_trace(ins) 251 | self.write_kill_local_reference(val) 252 | cst = ins.get_cst().get_constant() 253 | cst_type = ins.get_cst().get_type() 254 | if atype == 'F': 255 | self.write('v%s = d2c_bitcast_to_float(%r);\n' % (self.ra(val), cst)) 256 | elif atype == 'D': 257 | self.write('v%s = d2c_bitcast_to_double(%r);\n' % (self.ra(val), cst)) 258 | elif cst_type == 'Ljava/lang/String;': 259 | c = 'v%s = (%s) env->NewStringUTF(AY_OBFUSCATE("%s"));\n' if obfus else 'v%s = (%s) env->NewStringUTF("%s");\n' 260 | self.write( 261 | c % (self.ra(val), get_native_type(atype), cst)) 262 | elif cst_type == 'Ljava/lang/Class;': 263 | self.write('{\n') 264 | self.write_define_ex_handle(ins) 265 | self.write('jclass &clz = %s;\n' % (self.ca(ins.get_class()))) 266 | self.write('D2C_RESOLVE_CLASS(clz,"%s");\n' % (cst)) 267 | self.write('v%s = env->NewLocalRef(clz);\n' % (self.ra(val))) 268 | self.write_undefine_ex_handle(ins) 269 | self.write('}\n') 270 | else: 271 | self.write('v%s = %r;\n' % (self.ra(val), cst)) 272 | 273 | def visit_constant(self, cst): 274 | self.write('%s' % cst) 275 | 276 | def visit_variable(self, var): 277 | self.write('v%s' % self.ra(var)) 278 | 279 | def visit_param(self, param): 280 | decl_type = get_native_type(param.get_type()) 281 | if decl_type in ('jstring', 'jobject', 'jarray'): 282 | self.write('env->NewLocalRef(p%s)' % (param.get_register())) 283 | else: 284 | self.write('p%s' % param.get_register()) 285 | 286 | def visit_this(self): 287 | self.write('env->NewLocalRef(thiz)') 288 | 289 | def visit_move_result(self, ins, lhs, rhs): 290 | self.write_trace(ins) 291 | self.write_kill_local_reference(ins.get_value()) 292 | self.write('v%s = (%s) %s;\n' % (self.ra(lhs), get_cdecl_type(lhs.get_type()), self.get_variable_or_const(rhs))) 293 | 294 | def visit_move(self, lhs, rhs): 295 | self.write_kill_local_reference(lhs) 296 | self.write('v%s = ' % (self.ra(lhs))) 297 | if is_primitive_type(rhs.get_type()): 298 | self.write('%s' % (self.get_variable_or_const(rhs))) 299 | else: 300 | self.write('(%s) env->NewLocalRef(v%s)' % (get_cdecl_type(lhs.get_type()), self.ra(rhs))) 301 | self.write(';\n') 302 | 303 | def visit_astore(self, ins, array, index, rhs): 304 | self.write_trace(ins) 305 | self.write('{\n') 306 | self.write_define_ex_handle(ins) 307 | self.write_not_null(array) 308 | elem_type = ins.get_elem_type() 309 | if is_primitive_type(elem_type): 310 | self.write('{%s val = %s;' % (get_native_type(elem_type), self.get_variable_or_const(rhs))) 311 | self.write('env->Set%sArrayRegion((%sArray) v%s, (jint) %s, 1, &val);' % 312 | (get_type_descriptor(elem_type), get_native_type(elem_type), 313 | self.ra(array), 314 | self.get_variable_or_const(index))) 315 | self.write('}\n') 316 | else: 317 | self.write('env->SetObjectArrayElement((jobjectArray) v%s, (jint) %s, v%s);' % 318 | (self.ra(array), 319 | self.get_variable_or_const(index), 320 | self.ra(rhs))) 321 | self.write_undefine_ex_handle(ins) 322 | self.write('}\n') 323 | 324 | def visit_put_static(self, ins, clsdesc, name, ftype, rhs): 325 | self.write_trace(ins) 326 | self.write('{\n') 327 | self.write_define_ex_handle(ins) 328 | self.write('jclass &clz = %s;\n' % (self.ca(ins.get_class()))) 329 | self.write('jfieldID &fld = %s;\n' % (self.fa(ins.get_field()))) 330 | self.write('D2C_RESOLVE_STATIC_FIELD(clz, fld, "%s", "%s", "%s");\n' % (get_type(clsdesc), name, ftype)) 331 | self.write('env->SetStatic%sField(clz,fld,(%s) %s);\n' % ( 332 | get_type_descriptor(ftype), get_native_type(ftype), self.get_variable_or_const(rhs))) 333 | self.write_undefine_ex_handle(ins) 334 | self.write('}\n') 335 | 336 | def write_not_null(self, var): 337 | self.write('D2C_NOT_NULL(%s);\n' % (self.get_variable_or_const(var))) 338 | 339 | def visit_put_instance(self, ins, lhs, rhs, ftype, clsdesc, name): 340 | self.write_trace(ins) 341 | self.write('{\n') 342 | self.write_define_ex_handle(ins) 343 | self.write_not_null(lhs) 344 | self.write('jclass &clz = %s;\n' % (self.ca(ins.get_class()))) 345 | self.write('jfieldID &fld = %s;\n' % (self.fa(ins.get_field()))) 346 | self.write('D2C_RESOLVE_FIELD(clz, fld, "%s", "%s", "%s");\n' % (get_type(clsdesc), name, ftype)) 347 | self.write('env->Set%sField(v%s,fld,(%s) %s);\n' % ( 348 | get_type_descriptor(ftype), self.ra(lhs), get_native_type(ftype), self.get_variable_or_const(rhs))) 349 | self.write_undefine_ex_handle(ins) 350 | self.write('}\n') 351 | 352 | def visit_new(self, ins, result, atype): 353 | self.write_trace(ins) 354 | self.write('{\n') 355 | self.write_define_ex_handle(ins) 356 | # should kill local reference after D2C_RESOLVE_CLASS, since D2C_RESOLVE_CLASS may throw exception, 357 | # so this ref may be double killed in exception handle. 358 | self.write_kill_local_reference(ins.get_value()) 359 | self.write('jclass &clz = %s;\n' % (self.ca(ins.get_class()))) 360 | self.write('D2C_RESOLVE_CLASS(clz,"%s");\n' % (get_type(atype))) 361 | self.write('v%s = (%s) env->AllocObject(clz);\n' % (self.ra(result), get_native_type(result.get_type()))) 362 | self.write_undefine_ex_handle(ins) 363 | self.write('}\n') 364 | 365 | def _invoke_common(self, ins, invoke_type, name, base, ptype, rtype, args, clsdesc): 366 | self.write('{\n') 367 | self.write_define_ex_handle(ins) 368 | if invoke_type != 'static': 369 | self.write("D2C_NOT_NULL(v%s);\n" % (self.ra(base))) 370 | self.write('jclass &clz = %s;\n' % (self.ca(ins.get_class()))) 371 | self.write('jmethodID &mid = %s;\n' % (self.ma(ins.get_call_method()))) 372 | if invoke_type != 'static': 373 | self.write('D2C_RESOLVE_METHOD(clz, mid, "%s", "%s", "(%s)%s");\n' % (get_type(clsdesc), name, ''.join(ptype), rtype)) 374 | else: 375 | self.write('D2C_RESOLVE_STATIC_METHOD(clz, mid, "%s", "%s", "(%s)%s");\n' % (get_type(clsdesc), name, ''.join(ptype), rtype)) 376 | self.write('jvalue args[] = {') 377 | vars = [] 378 | for arg, atype in zip(args, ptype): 379 | if atype[0] == 'L' or atype[0] == '[': 380 | vars.append('{.l = %s}' % (self.get_variable_or_const(arg))) 381 | elif atype[0] == 'Z': 382 | vars.append('{.z = (jboolean) %s}' % (self.get_variable_or_const(arg))) 383 | elif atype[0] == 'B': 384 | vars.append('{.b = (jbyte) %s}' % (self.get_variable_or_const(arg))) 385 | elif atype[0] == 'C': 386 | vars.append('{.c = (jchar) %s}' % (self.get_variable_or_const(arg))) 387 | elif atype[0] == 'S': 388 | vars.append('{.s = (jshort) %s}' % (self.get_variable_or_const(arg))) 389 | elif atype[0] == 'I': 390 | vars.append('{.i = %s}' % (self.get_variable_or_const(arg))) 391 | elif atype[0] == 'J': 392 | vars.append('{.j = (jlong) %s}' % (self.get_variable_or_const(arg))) 393 | elif atype[0] == 'F': 394 | vars.append('{.f = %s}' % (self.get_variable_or_const(arg))) 395 | elif atype[0] == 'D': 396 | vars.append('{.d = %s}' % (self.get_variable_or_const(arg))) 397 | else: 398 | raise Exception("Unknow signuare type %s" % (atype)) 399 | self.write(','.join(vars)) 400 | self.write('};\n') 401 | if rtype != 'V': 402 | self.write_kill_local_reference(ins.get_value()) 403 | ins.get_value().visit(self) 404 | self.write(' = ') 405 | self.write("(%s) " % (get_native_type(ins.get_value().get_type()))) 406 | 407 | if invoke_type == 'super': 408 | self.write( 409 | 'env->CallNonvirtual%sMethodA(v%s, clz, mid, args);\n' % (get_type_descriptor(rtype), self.ra(base))) 410 | elif invoke_type == 'static': 411 | self.write('env->CallStatic%sMethodA(clz, mid, args);\n' % (get_type_descriptor(rtype))) 412 | else: 413 | self.write('env->Call%sMethodA(v%s, mid, args);\n' % (get_type_descriptor(rtype), self.ra(base))) 414 | 415 | self.write_undefine_ex_handle(ins) 416 | self.write('}\n') 417 | if rtype != 'V': 418 | self.write_delete_dead_local_reference(ins.get_value()) 419 | 420 | def visit_invoke(self, ins, invoke_type, name, base, ptype, rtype, args, clsdesc): 421 | self.write_trace(ins) 422 | return self._invoke_common(ins, invoke_type, name, base, ptype, rtype, args, clsdesc) 423 | 424 | def visit_return_void(self): 425 | self.write('return') 426 | self.end_ins() 427 | 428 | def visit_return(self, arg): 429 | return_type = self.irmethod.rtype 430 | if return_type[0] != 'V': 431 | self.write("return (%s) %s;\n" % (get_native_type(return_type), self.get_variable_or_const(arg))) 432 | else: 433 | self.write('return;') 434 | 435 | def visit_nop(self): 436 | pass 437 | 438 | def visit_goto(self, target): 439 | self.write('goto L%d' % (self.irmethod.offset_to_node[target].num)) 440 | self.end_ins() 441 | 442 | def visit_check_cast(self, ins, arg, atype): 443 | self.write_trace(ins) 444 | self.write('{\n') 445 | self.write_define_ex_handle(ins) 446 | self.write('jclass &clz = %s;\n' % (self.ca(ins.get_class()))) 447 | self.write('D2C_RESOLVE_CLASS(clz,"%s");\n' % (get_type(atype))) 448 | self.write('D2C_CHECK_CAST(%s, clz, "%s");\n' % (self.get_variable_or_const(arg), get_type(atype))) 449 | self.write_undefine_ex_handle(ins) 450 | self.write('}\n') 451 | 452 | def visit_instanceof(self, ins, result, arg, atype): 453 | self.write_trace(ins) 454 | self.write('{\n') 455 | self.write_define_ex_handle(ins) 456 | self.write('jclass &clz = %s;\n' % (self.ca(ins.get_class()))) 457 | self.write('D2C_RESOLVE_CLASS(clz,"%s");\n' % (get_type(atype))) 458 | self.write('v%s = d2c_is_instance_of(env, v%s, clz);\n' % (self.ra(result), self.ra(arg))) 459 | self.write_undefine_ex_handle(ins) 460 | self.write('}\n') 461 | 462 | def visit_aload(self, ins, result, array, index): 463 | self.write_trace(ins) 464 | self.write('{\n') 465 | self.write_define_ex_handle(ins) 466 | self.write_not_null(array) 467 | elem_type = ins.get_elem_type() 468 | if is_primitive_type(elem_type): 469 | self.write('{%s val;' % (get_native_type(elem_type))) 470 | self.write('env->Get%sArrayRegion((%sArray) v%s, (jint) %s, 1, &val);' % 471 | (get_type_descriptor(elem_type), get_native_type(elem_type), self.ra(array), 472 | self.get_variable_or_const(index))) 473 | self.write('v%s = val;}' % (self.ra(result))) 474 | else: 475 | self.write_kill_local_reference(result) 476 | self.write('v%s = (%s) env->GetObjectArrayElement((jobjectArray) v%s, (jint) %s);' % 477 | (self.ra(result), get_native_type(result.get_type()), 478 | self.ra(array), 479 | self.get_variable_or_const(index))) 480 | self.write('\n') 481 | self.write_undefine_ex_handle(ins) 482 | self.write('}\n') 483 | 484 | def visit_alength(self, ins, result, array): 485 | self.write_trace(ins) 486 | self.write('{\n') 487 | self.write_define_ex_handle(ins) 488 | self.write_not_null(array) 489 | self.write('v%s = env->GetArrayLength((jarray) v%s);\n' % (self.ra(result), self.ra(array))) 490 | self.write_undefine_ex_handle(ins) 491 | self.write('}\n') 492 | 493 | def write_check_array_size(self, size): 494 | self.write('if (%s < 0) {\n' 495 | 'd2c_throw_exception(env, "java/lang/NegativeArraySizeException", "negative array size");\n' 496 | % self.get_variable_or_const(size)) 497 | self.write('goto EX_HANDLE;\n') 498 | self.write('}\n') 499 | 500 | def visit_new_array(self, ins, result, atype, size): 501 | self.write_trace(ins) 502 | elem_type = ins.get_elem_type() 503 | self.write('{\n') 504 | self.write_define_ex_handle(ins) 505 | self.write_check_array_size(size) 506 | self.write_kill_local_reference(ins.get_value()) 507 | if is_primitive_type(elem_type): 508 | result.visit(self) 509 | self.write( 510 | ' = (%s) env->New%sArray((jint) %s);\n' % ( 511 | get_native_type(result.get_type()), get_type_descriptor(elem_type), self.get_variable_or_const(size))) 512 | else: 513 | self.write('jclass &clz = %s;\n' % (self.ca(ins.get_class()))) 514 | self.write('D2C_RESOLVE_CLASS(clz,"%s");\n' % (get_type(elem_type))) 515 | result.visit(self) 516 | self.write(' = env->NewObjectArray((jint) %s, clz, NULL);\n' % (self.get_variable_or_const(size))) 517 | self.write_undefine_ex_handle(ins) 518 | self.write('}\n') 519 | 520 | def visit_filled_new_array(self, ins, result, atype, size, args): 521 | self.write_trace(ins) 522 | params = [] 523 | for arg in args: 524 | p = '%s' % (self.get_variable_or_const(arg)) 525 | params.append(p) 526 | self.write('{\n') 527 | self.write_define_ex_handle(ins) 528 | self.write_kill_local_reference(ins.get_value()) 529 | elem_type = atype[1:] 530 | assert elem_type != 'D' and elem_type != 'J' 531 | if is_primitive_type(elem_type): 532 | result.visit(self) 533 | self.write(' = env->New%sArray((jint) %r);\n' % (get_type_descriptor(elem_type), size)) 534 | else: 535 | self.write('jclass &clz = %s;\n' % (self.ca(ins.get_class()))) 536 | self.write('D2C_RESOLVE_CLASS(clz,"%s");\n' % (get_type(elem_type))) 537 | result.visit(self) 538 | self.write(' = env->NewObjectArray((jint) %r, clz, NULL);\n' % (size)) 539 | self.write('d2c_filled_new_array(env, (jarray) v%s, "%s", %d, %s);\n' 540 | % (self.ra(result), elem_type, len(params), ', '.join(params))) 541 | self.write_undefine_ex_handle(ins) 542 | self.write('}\n') 543 | 544 | def visit_fill_array(self, ins, array, value): 545 | self.write_trace(ins) 546 | self.write('{\n') 547 | self.write('static const unsigned char data[] = {') 548 | data = value.get_data() 549 | tab = [] 550 | elem_id = 'B' 551 | elem_size = 1 552 | 553 | for i in range(0, value.size * value.element_width, elem_size): 554 | tab.append('%s' % unpack(elem_id, data[i:i + elem_size])[0]) 555 | self.write(', '.join(tab)) 556 | self.write('};\n') 557 | 558 | elem_type = array.get_type()[1:] 559 | array_size = value.size 560 | 561 | self.write( 562 | 'env->Set%sArrayRegion((%sArray) v%s, 0, %d, (const %s *) data);\n' % (get_type_descriptor(elem_type), \ 563 | get_native_type(elem_type), 564 | self.ra(array), \ 565 | array_size, 566 | get_native_type(elem_type))) 567 | self.write('}\n') 568 | 569 | def visit_move_exception(self, ins, var): 570 | self.write_trace(ins) 571 | self.write_kill_local_reference(var) 572 | var.declared = True 573 | var.visit(self) 574 | self.write(' = exception;\n') 575 | 576 | def visit_monitor_enter(self, ins, ref): 577 | self.write_trace(ins) 578 | self.write('{\n') 579 | self.write_define_ex_handle(ins) 580 | self.write_not_null(ref) 581 | self.write('env->MonitorEnter(%s);\n' % (self.get_variable_or_const(ref))) 582 | self.write_undefine_ex_handle(ins) 583 | self.write('}\n') 584 | 585 | def visit_monitor_exit(self, ins, ref): 586 | self.write('{\n') 587 | self.write_define_ex_handle(ins) 588 | self.write('if (env->MonitorExit(%s) != JNI_OK) {\n' % (self.get_variable_or_const(ref))) 589 | self.write_undefine_ex_handle(ins) 590 | self.write('}\n') 591 | self.write('}\n') 592 | 593 | def visit_throw(self, ins, ref): 594 | self.write_trace(ins) 595 | self.write('{\n') 596 | self.write_define_ex_handle(ins) 597 | self.write_not_null(ref) 598 | self.write('env->Throw((jthrowable) v%s);\n' % self.ra(ref)) 599 | self.write_undefine_ex_handle(ins) 600 | self.write('}\n') 601 | 602 | def write_div_expression(self, ins, result, op, arg1, arg2): 603 | self.write('{\n') 604 | if result.get_type() not in 'FD': 605 | self.write_define_ex_handle(ins) 606 | self.write('if (%s == 0) {\n' 607 | 'd2c_throw_exception(env, "java/lang/ArithmeticException", "divide by zero");\n' 608 | % self.get_variable_or_const(arg2)) 609 | self.write('goto EX_HANDLE;\n') 610 | self.write('}\n') 611 | self.write('#undef EX_HANDLE\n') 612 | self.write('v%s = ' % (self.ra(result))) 613 | arg1.visit(self) 614 | self.write(' %s ' % (op)) 615 | arg2.visit(self) 616 | self.write(';\n') 617 | self.write('}\n') 618 | 619 | def visit_binary_expression(self, ins, result, op, arg1, arg2): 620 | self.write_trace(ins) 621 | if op == Op.DIV or op == Op.MOD: 622 | self.write_div_expression(ins, result, op, arg1, arg2) 623 | return 624 | 625 | self.write('v%s = ' % self.ra(result)) 626 | # 浮点数有个NaN值,任何值与他比较都不成立,包括NaN. 627 | if op == Op.CMP: 628 | self.write('(') 629 | arg1.visit(self) 630 | self.write(' == ') 631 | arg2.visit(self) 632 | self.write(') ? 0 :') 633 | self.write(' (') 634 | arg1.visit(self) 635 | self.write(' > ') 636 | arg2.visit(self) 637 | self.write(') ? 1 : -1;\n') 638 | elif op == Op.CMPL: 639 | self.write('(') 640 | arg1.visit(self) 641 | self.write(' == ') 642 | arg2.visit(self) 643 | self.write(') ? 0 :') 644 | self.write(' (') 645 | arg1.visit(self) 646 | self.write(' > ') 647 | arg2.visit(self) 648 | self.write(') ? 1 : -1;\n') 649 | elif op == Op.CMPG: 650 | self.write('(') 651 | arg1.visit(self) 652 | self.write(' == ') 653 | arg2.visit(self) 654 | self.write(') ? 0 :') 655 | self.write(' (') 656 | arg1.visit(self) 657 | self.write(' < ') 658 | arg2.visit(self) 659 | self.write(') ? -1 : 1;\n') 660 | elif op == Op.INTSHR: 661 | self.write('(%s) >> (' % (self.get_variable_or_const(arg1))) 662 | arg2.visit(self) 663 | self.write(' & 0x1f);\n') 664 | elif op == Op.INTSHL: 665 | self.write('(%s) << (' % (self.get_variable_or_const(arg1))) 666 | arg2.visit(self) 667 | self.write(' & 0x1f);\n') 668 | elif op == Op.INTUSHR: 669 | self.write('((uint32_t) %s) >> (' % (self.get_variable_or_const(arg1))) 670 | arg2.visit(self) 671 | self.write(' & 0x1f);\n') 672 | elif op == Op.LONGSHR: 673 | self.write('(v%s) >> (' % (self.ra(arg1))) 674 | arg2.visit(self) 675 | self.write(' & 0x3f);\n') 676 | elif op == Op.LONGSHL: 677 | self.write('(%s) << (' % (self.get_variable_or_const(arg1))) 678 | arg2.visit(self) 679 | self.write(' & 0x3f);\n') 680 | elif op == Op.LONGUSHR: 681 | self.write('((uint64_t) v%s) >> (' % (self.ra(arg1))) 682 | arg2.visit(self) 683 | self.write(' & 0x3f);\n') 684 | elif op == Op.MODF: 685 | self.write('fmodf(v%s, v%s);\n' % (self.ra(arg1), self.ra(arg2))) 686 | elif op == Op.MODD: 687 | self.write('fmod(v%s, v%s);\n' % (self.ra(arg1), self.ra(arg2))) 688 | else: 689 | self.write('(') 690 | arg1.visit(self) 691 | self.write(' %s ' % op) 692 | arg2.visit(self) 693 | self.write(');\n') 694 | 695 | def visit_unary_expression(self, ins, result, op, arg): 696 | self.write_trace(ins) 697 | self.write('v%s = (%s %s);\n' % (self.ra(result), op, self.get_variable_or_const(arg))) 698 | 699 | def visit_cast(self, lhs, op, arg): 700 | type1 = arg.get_type() 701 | type2 = lhs.get_type() 702 | # double to long 703 | if type1 == 'D' and type2 == 'J': 704 | self.write('v%s = d2c_double_to_long(v%s);\n' % (self.ra(lhs), self.ra(arg))) 705 | # double to int 706 | elif type1 == 'D' and type2 == 'I': 707 | self.write('v%s = d2c_double_to_int(v%s);\n' % (self.ra(lhs), self.ra(arg))) 708 | # float to long 709 | elif type1 == 'F' and type2 == 'J': 710 | self.write('v%s = d2c_float_to_long(v%s);\n' % (self.ra(lhs), self.ra(arg))) 711 | elif type1 == 'F' and type2 == 'I': 712 | self.write('v%s = d2c_float_to_int(v%s);\n' % (self.ra(lhs), self.ra(arg))) 713 | else: 714 | self.write('v%s = %s(%s);\n' % (self.ra(lhs), op, self.get_variable_or_const(arg))) 715 | 716 | def visit_cond_expression(self, ins, op, arg1, arg2, true_target, false_target): 717 | self.write_trace(ins) 718 | true_target_node = self.irmethod.offset_to_node[true_target] 719 | false_target_node = self.irmethod.offset_to_node[false_target] 720 | self.write('if(') 721 | if is_primitive_type(arg1.get_type()): 722 | arg1.visit(self) 723 | self.write(' %s ' % op) 724 | arg2.visit(self) 725 | else: 726 | if op == '!=': 727 | self.write('!') 728 | self.write('d2c_is_same_object(env,') 729 | arg1.visit(self) 730 | self.write(',') 731 | arg2.visit(self) 732 | self.write(')') 733 | self.write(') ') 734 | self.write('{\n') 735 | self.write('goto L%d;\n' % true_target_node.num) 736 | self.write('}\n') 737 | self.write('else {\n') 738 | self.write('goto L%d;\n' % false_target_node.num) 739 | self.write('}\n') 740 | 741 | def visit_condz_expression(self, ins, op, arg, true_target, false_target): 742 | self.write_trace(ins) 743 | true_target_node = self.irmethod.offset_to_node[true_target] 744 | false_target_node = self.irmethod.offset_to_node[false_target] 745 | if isinstance(arg, BinaryCompExpression): 746 | arg.op = op 747 | return arg.visit(self) 748 | atype = arg.get_type() 749 | self.write('if(') 750 | arg.visit(self) 751 | if atype in 'ZBSCIJFD': 752 | self.write(' %s 0' % op) 753 | else: 754 | self.write(' %s NULL' % op) 755 | self.write(')') 756 | self.write('{\n') 757 | self.write('goto L%d;\n' % (true_target_node.num)) 758 | self.write('}\n') 759 | self.write('else {\n') 760 | self.write('goto L%d;\n' % (false_target_node.num)) 761 | self.write('}\n') 762 | 763 | def visit_get_instance(self, ins, result, arg, ftype, clsdesc, name): 764 | self.write_trace(ins) 765 | self.write('{\n') 766 | self.write_define_ex_handle(ins) 767 | self.write_not_null(arg) 768 | self.write_kill_local_reference(result) 769 | self.write('jclass &clz = %s;\n' % (self.ca(ins.get_class()))) 770 | self.write('jfieldID &fld = %s;\n' % (self.fa(ins.get_field()))) 771 | self.write('D2C_RESOLVE_FIELD(clz, fld, "%s", "%s", "%s");\n' % (get_type(clsdesc), name, ftype)) 772 | result.visit(self) 773 | self.write( 774 | ' = (%s) env->Get%sField(v%s,fld);\n' % ( 775 | get_native_type(result.get_type()), get_type_descriptor(ftype), self.ra(arg))) 776 | self.write_undefine_ex_handle(ins) 777 | self.write('}\n') 778 | 779 | def write_define_ex_handle(self, ins): 780 | bb = ins.parent 781 | if bb in self.irmethod.node_to_landing_pad: 782 | landing_pad = self.irmethod.node_to_landing_pad[ins.parent].label 783 | else: 784 | landing_pad = 'EX_UnwindBlock' 785 | self.write("#define EX_HANDLE %s\n" % (landing_pad)) 786 | 787 | def write_undefine_ex_handle(self, ins): 788 | self.write('D2C_CHECK_PENDING_EX;\n') 789 | self.write('#undef EX_HANDLE\n') 790 | 791 | def visit_get_static(self, ins, result, ftype, clsdesc, name): 792 | self.write_trace(ins) 793 | self.write('{\n') 794 | self.write_define_ex_handle(ins) 795 | self.write_kill_local_reference(result) 796 | self.write('jclass &clz = %s;\n' % (self.ca(ins.get_class()))) 797 | self.write('jfieldID &fld = %s;\n' % (self.fa(ins.get_field()))) 798 | self.write('D2C_RESOLVE_STATIC_FIELD(clz, fld, "%s", "%s", "%s");\n' % (get_type(clsdesc), name, ftype)) 799 | result.visit(self) 800 | self.write(' = (%s) env->GetStatic%sField(clz,fld);\n' % ( 801 | get_native_type(result.get_type()), get_type_descriptor(ftype))) 802 | self.write_undefine_ex_handle(ins) 803 | self.write('}\n') 804 | 805 | def get_variable_or_const(self, var): 806 | if isinstance(var, Constant): 807 | return '%s' % var 808 | else: 809 | return 'v%s' % self.ra(var) 810 | -------------------------------------------------------------------------------- /filter.txt: -------------------------------------------------------------------------------- 1 | # Rules are made using regex patterns 2 | 3 | # Protect all methods in all classes 4 | # Not recommended for real world applications. 5 | #.* 6 | -------------------------------------------------------------------------------- /keystore/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kirlif/d2c/80305482722efd38781d9213a7cc20b7bc036eed/keystore/debug.keystore -------------------------------------------------------------------------------- /loader/DccApplication.smali: -------------------------------------------------------------------------------- 1 | .class public Lamimo/dcc/DccApplication; 2 | .super Landroid/app/Application; 3 | 4 | 5 | # direct methods 6 | .method static final constructor ()V 7 | .locals 1 8 | 9 | const-string v0, "stub" 10 | 11 | invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V 12 | 13 | return-void 14 | .end method 15 | 16 | .method public constructor ()V 17 | .locals 0 18 | 19 | invoke-direct {p0}, Landroid/app/Application;->()V 20 | 21 | return-void 22 | .end method 23 | 24 | 25 | -------------------------------------------------------------------------------- /project/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_MODULE := stub 5 | LOCAL_LDLIBS := -llog 6 | 7 | SOURCES:= $(wildcard $(LOCAL_PATH)/nc/*.cpp) 8 | LOCAL_C_INCLUDES := $(LOCAL_PATH)/nc 9 | 10 | LOCAL_SRC_FILES := $(SOURCES:$(LOCAL_PATH)/%=%) 11 | 12 | include $(BUILD_SHARED_LIBRARY) 13 | -------------------------------------------------------------------------------- /project/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_STL := c++_static 2 | APP_CPPFLAGS += -fvisibility=hidden 3 | APP_PLATFORM := 21 4 | APP_ABI := armeabi-v7a arm64-v8a # x86 x86_64 5 | 6 | -------------------------------------------------------------------------------- /project/jni/nc/Dex2C.cpp: -------------------------------------------------------------------------------- 1 | #include "Dex2C.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "ScopedLocalRef.h" 9 | #include "ScopedPthreadMutexLock.h" 10 | #include "well_known_classes.h" 11 | #include "DynamicRegister.h" 12 | 13 | struct MemberTriple { 14 | MemberTriple(const char *cls_name, const char *name, const char *sig) : class_name_(cls_name), 15 | member_name_(name), 16 | signautre_(sig) {} 17 | 18 | const char *class_name_; 19 | const char *member_name_; 20 | const char *signautre_; 21 | 22 | bool operator<(const MemberTriple &member) const { 23 | if (class_name_ != member.class_name_) return class_name_ < member.class_name_; 24 | if (member_name_ != member.member_name_) return member_name_ < member.member_name_; 25 | if (signautre_ != member.signautre_) 26 | return signautre_ < member.signautre_; 27 | else 28 | return false; 29 | } 30 | }; 31 | 32 | static std::map resvoled_fields; 33 | static std::map resvoled_methods; 34 | static std::map resvoled_classes; 35 | static pthread_mutex_t resovle_method_mutex = PTHREAD_MUTEX_INITIALIZER; 36 | static pthread_mutex_t resovle_field_mutex = PTHREAD_MUTEX_INITIALIZER; 37 | static pthread_mutex_t resovle_class_mutex = PTHREAD_MUTEX_INITIALIZER; 38 | 39 | static const int max_global_reference = 1500; 40 | 41 | static void cache_well_known_classes(JNIEnv *env) { 42 | d2c::WellKnownClasses::Init(env); 43 | 44 | resvoled_classes[MemberTriple("Int", NULL, NULL)] = d2c::WellKnownClasses::primitive_int; 45 | resvoled_classes[MemberTriple("Long", NULL, NULL)] = d2c::WellKnownClasses::primitive_long; 46 | resvoled_classes[MemberTriple("Short", NULL, NULL)] = d2c::WellKnownClasses::primitive_short; 47 | resvoled_classes[MemberTriple("Char", NULL, NULL)] = d2c::WellKnownClasses::primitive_char; 48 | resvoled_classes[MemberTriple("Byte", NULL, NULL)] = d2c::WellKnownClasses::primitive_byte; 49 | resvoled_classes[MemberTriple("Boolean", NULL, 50 | NULL)] = d2c::WellKnownClasses::primitive_boolean; 51 | resvoled_classes[MemberTriple("Float", NULL, NULL)] = d2c::WellKnownClasses::primitive_float; 52 | resvoled_classes[MemberTriple("Double", NULL, NULL)] = d2c::WellKnownClasses::primitive_double; 53 | } 54 | 55 | void d2c_throw_exception(JNIEnv *env, const char *class_name, const char *message) { 56 | LOGD("d2c_throw_exception %s %s", class_name, message); 57 | ScopedLocalRef c(env, env->FindClass(class_name)); 58 | if (c.get()) { 59 | env->ThrowNew(c.get(), message); 60 | } 61 | } 62 | 63 | void d2c_filled_new_array(JNIEnv *env, jarray array, const char *type, jint count, ...) { 64 | va_list args; 65 | va_start(args, count); 66 | char ty = type[0]; 67 | bool ref = ty == '[' || ty == 'L'; 68 | for (int i = 0; i < count; i++) { 69 | if (ref) { 70 | env->SetObjectArrayElement((jobjectArray) array, i, (jobject) va_arg(args, long)); 71 | } else { 72 | int val = va_arg(args, jint); 73 | LOGD("idx = %d, val = %d", i, val); 74 | env->SetIntArrayRegion((jintArray) array, i, 1, &val); 75 | } 76 | } 77 | va_end(args); 78 | } 79 | 80 | int64_t d2c_double_to_long(double val) { 81 | int64_t result; 82 | if (val != val) { // NaN 83 | result = 0; 84 | } else if (val > static_cast(INT64_MAX)) { 85 | result = INT64_MAX; 86 | } else if (val < static_cast(INT64_MIN)) { 87 | result = INT64_MIN; 88 | } else { 89 | result = static_cast(val); 90 | } 91 | return result; 92 | } 93 | 94 | int64_t d2c_float_to_long(float val) { 95 | int64_t result; 96 | if (val != val) { // NaN 97 | result = 0; 98 | } else if (val > static_cast(INT64_MAX)) { 99 | result = INT64_MAX; 100 | } else if (val < static_cast(INT64_MIN)) { 101 | result = INT64_MIN; 102 | } else { 103 | result = static_cast(val); 104 | } 105 | return result; 106 | } 107 | 108 | int32_t d2c_double_to_int(double val) { 109 | int32_t result; 110 | if (val != val) { 111 | result = 0; 112 | } else if (val > static_cast(INT32_MAX)) { 113 | result = INT32_MAX; 114 | } else if (val < static_cast(INT32_MIN)) { 115 | result = INT32_MIN; 116 | } else { 117 | result = static_cast(val); 118 | } 119 | return result; 120 | } 121 | 122 | int32_t d2c_float_to_int(float val) { 123 | int32_t result; 124 | if (val != val) { 125 | result = 0; 126 | } else if (val > static_cast(INT32_MAX)) { 127 | result = INT32_MAX; 128 | } else if (val < static_cast(INT32_MIN)) { 129 | result = INT32_MIN; 130 | } else { 131 | result = static_cast(val); 132 | } 133 | return result; 134 | } 135 | 136 | bool d2c_is_instance_of(JNIEnv *env, jobject instance, const char *class_name) { 137 | if (instance == NULL) { 138 | return false; 139 | } 140 | 141 | ScopedLocalRef c(env, env->FindClass(class_name)); 142 | if (c.get()) { 143 | return env->IsInstanceOf(instance, c.get()); 144 | } else { 145 | return false; 146 | } 147 | } 148 | 149 | bool d2c_check_cast(JNIEnv *env, jobject instance, jclass clz, const char *class_name) { 150 | if (env->IsInstanceOf(instance, clz)) { 151 | return false; 152 | } else { 153 | d2c_throw_exception(env, "java/lang/ClassCastException", class_name); 154 | return true; 155 | } 156 | } 157 | 158 | bool d2c_resolve_class(JNIEnv *env, jclass *cached_class, const char *class_name) { 159 | if (*cached_class) { 160 | return false; 161 | } 162 | 163 | MemberTriple triple(class_name, NULL, NULL); 164 | 165 | if (max_global_reference > 0) { 166 | ScopedPthreadMutexLock lock(&resovle_class_mutex); 167 | 168 | auto iter = resvoled_classes.find(triple); 169 | if (iter != resvoled_classes.end()) { 170 | *cached_class = (jclass) iter->second; 171 | return false; 172 | } 173 | } 174 | 175 | jclass clz = env->FindClass(class_name); 176 | if (clz) { 177 | LOGD("resvoled class %s %zd", class_name, resvoled_classes.size()); 178 | if (max_global_reference > 0 && resvoled_classes.size() < max_global_reference) { 179 | ScopedPthreadMutexLock lock(&resovle_class_mutex); 180 | *cached_class = (jclass) env->NewGlobalRef(clz); 181 | resvoled_classes[triple] = *cached_class; 182 | env->DeleteLocalRef(clz); 183 | } else { 184 | *cached_class = clz; 185 | } 186 | return false; 187 | } else { 188 | return true; 189 | } 190 | } 191 | 192 | bool d2c_resolve_method(JNIEnv *env, jclass *cached_class, jmethodID *cached_method, bool is_static, 193 | const char *class_name, const char *method_name, const char *signature) { 194 | if (*cached_method) { 195 | return false; 196 | } 197 | 198 | if (d2c_resolve_class(env, cached_class, class_name)) { 199 | return true; 200 | } 201 | 202 | MemberTriple triple(class_name, method_name, signature); 203 | { 204 | ScopedPthreadMutexLock lock(&resovle_method_mutex); 205 | 206 | auto iter = resvoled_methods.find(triple); 207 | if (iter != resvoled_methods.end()) { 208 | *cached_method = iter->second; 209 | return false; 210 | } 211 | } 212 | 213 | if (is_static) { 214 | *cached_method = env->GetStaticMethodID(*cached_class, method_name, signature); 215 | } else { 216 | *cached_method = env->GetMethodID(*cached_class, method_name, signature); 217 | } 218 | 219 | if (*cached_method) { 220 | ScopedPthreadMutexLock lock(&resovle_method_mutex); 221 | resvoled_methods[triple] = *cached_method; 222 | } 223 | 224 | return *cached_method == NULL; 225 | } 226 | 227 | bool d2c_resolve_field(JNIEnv *env, jclass *cached_class, jfieldID *cached_field, bool is_static, 228 | const char *class_name, const char *field_name, const char *signature) { 229 | if (*cached_field) { 230 | return false; 231 | } 232 | 233 | if (d2c_resolve_class(env, cached_class, class_name)) { 234 | return true; 235 | } 236 | 237 | MemberTriple triple(class_name, field_name, signature); 238 | { 239 | ScopedPthreadMutexLock lock(&resovle_field_mutex); 240 | 241 | auto iter = resvoled_fields.find(triple); 242 | if (iter != resvoled_fields.end()) { 243 | *cached_field = iter->second; 244 | return false; 245 | } 246 | } 247 | 248 | if (is_static) { 249 | *cached_field = env->GetStaticFieldID(*cached_class, field_name, signature); 250 | } else { 251 | *cached_field = env->GetFieldID(*cached_class, field_name, signature); 252 | } 253 | 254 | if (*cached_field) { 255 | ScopedPthreadMutexLock lock(&resovle_field_mutex); 256 | resvoled_fields[triple] = *cached_field; 257 | } 258 | 259 | return *cached_field == NULL; 260 | } 261 | 262 | JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { 263 | JNIEnv *env; 264 | 265 | if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) { 266 | return JNI_ERR; 267 | } 268 | cache_well_known_classes(env); 269 | const char *result = dynamic_register_compile_methods(env); 270 | if (result != nullptr) 271 | { 272 | LOGD("d2c_throw_exception %s", result); 273 | return JNI_ERR; 274 | } 275 | return JNI_VERSION_1_6; 276 | } 277 | -------------------------------------------------------------------------------- /project/jni/nc/Dex2C.h: -------------------------------------------------------------------------------- 1 | #ifndef _DEX2C_H_ 2 | #define _DEX2C_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | //#define DEBUG 12 | 13 | #define D2C_RESOLVE_CLASS(cached_class, class_name) \ 14 | if (cached_class == NULL && d2c_resolve_class(env, &cached_class, class_name)) { \ 15 | goto EX_HANDLE; \ 16 | } 17 | 18 | #define D2C_RESOLVE_METHOD(cached_class, cached_method, class_name, method_name, signature) \ 19 | if (cached_method == NULL && d2c_resolve_method(env, &cached_class, &cached_method, false, class_name, method_name, signature)) { \ 20 | goto EX_HANDLE; \ 21 | } 22 | 23 | #define D2C_RESOLVE_STATIC_METHOD(cached_class, cached_method, class_name, method_name, signature) \ 24 | if (cached_method == NULL && d2c_resolve_method(env, &cached_class, &cached_method, true, class_name, method_name, signature)) { \ 25 | goto EX_HANDLE; \ 26 | } 27 | 28 | #define D2C_RESOLVE_FIELD(cached_class, cached_field, class_name, field_name, signature) \ 29 | if (cached_field == NULL && d2c_resolve_field(env, &cached_class, &cached_field, false, class_name, field_name, signature)) { \ 30 | goto EX_HANDLE; \ 31 | } 32 | 33 | #define D2C_RESOLVE_STATIC_FIELD(cached_class, cached_field, class_name, field_name, signature) \ 34 | if (cached_field == NULL && d2c_resolve_field(env, &cached_class, &cached_field, true, class_name, field_name, signature)) { \ 35 | goto EX_HANDLE; \ 36 | } 37 | 38 | #define D2C_CHECK_PENDING_EX \ 39 | if (env->ExceptionCheck()) { \ 40 | goto EX_HANDLE; \ 41 | } 42 | 43 | #define D2C_GET_PENDING_EX \ 44 | exception = env->ExceptionOccurred(); \ 45 | env->ExceptionClear(); \ 46 | 47 | #define D2C_GOTO_UNWINDBLOCK \ 48 | env->Throw(exception); \ 49 | env->DeleteLocalRef(exception); \ 50 | goto EX_UnwindBlock; 51 | 52 | #define D2C_NOT_NULL(obj) \ 53 | if (obj == NULL) { \ 54 | d2c_throw_exception(env, "java/lang/NullPointerException", "NullPointerException"); \ 55 | goto EX_HANDLE; \ 56 | } 57 | 58 | #define D2C_CHECK_CAST(obj, clz, class_name) \ 59 | if (d2c_check_cast(env, obj, clz, class_name)) { \ 60 | goto EX_HANDLE; \ 61 | } 62 | 63 | #ifdef DEBUG 64 | #define LOGD(...) \ 65 | __android_log_print(ANDROID_LOG_DEBUG, "Dex2C", __VA_ARGS__) 66 | #else 67 | #define LOGD(...) (0) 68 | #endif 69 | 70 | inline jdouble d2c_bitcast_to_double(uint64_t val) { 71 | union { 72 | double dest; 73 | uint64_t src; 74 | } conv; 75 | conv.src = val; 76 | return conv.dest; 77 | } 78 | 79 | inline jfloat d2c_bitcast_to_float(uint32_t val) { 80 | union { 81 | float dest; 82 | uint32_t src; 83 | } conv; 84 | conv.src = val; 85 | return conv.dest; 86 | } 87 | 88 | inline double d2c_long_to_double(int64_t l) { 89 | return static_cast(l); 90 | } 91 | 92 | inline float d2c_long_to_float(int64_t l) { 93 | return static_cast(l); 94 | } 95 | 96 | int64_t d2c_double_to_long(double val); 97 | 98 | int32_t d2c_double_to_int(double val); 99 | 100 | int64_t d2c_float_to_long(float val); 101 | 102 | int32_t d2c_float_to_int(float val); 103 | 104 | void d2c_filled_new_array(JNIEnv *env, jarray array, const char *type, jint count, ...); 105 | 106 | void d2c_throw_exception(JNIEnv *env, const char *name, const char *msg); 107 | 108 | inline bool d2c_is_instance_of(JNIEnv *env, jobject instance, jclass clz) { 109 | if (instance) { 110 | return env->IsInstanceOf(instance, clz); 111 | } else { 112 | return false; 113 | } 114 | } 115 | 116 | bool d2c_is_instance_of(JNIEnv *env, jobject instance, const char *class_name); 117 | 118 | inline bool d2c_is_same_object(JNIEnv *env, jobject obj1, jobject obj2) { 119 | if (obj1 == obj2) { 120 | return true; 121 | } 122 | if (obj1 && obj2) { 123 | return env->IsSameObject(obj1, obj2); 124 | } else { 125 | return false; 126 | } 127 | } 128 | 129 | /* The following functions return true if exception occurred */ 130 | bool d2c_check_cast(JNIEnv *env, jobject instance, jclass clz, const char *class_name); 131 | 132 | bool d2c_resolve_class(JNIEnv *env, jclass *cached_class, const char *class_name); 133 | 134 | bool d2c_resolve_method(JNIEnv *env, jclass *cached_class, jmethodID *cached_method, bool is_static, 135 | const char *class_name, const char *method_name, const char *signature); 136 | 137 | bool d2c_resolve_field(JNIEnv *env, jclass *cached_class, jfieldID *cached_field, bool is_static, 138 | const char *class_name, const char *field_name, const char *signature); 139 | 140 | #endif 141 | -------------------------------------------------------------------------------- /project/jni/nc/DynamicRegister.h: -------------------------------------------------------------------------------- 1 | #ifndef _DYNAMIC_REGISTER_H_ // !_DYNAMIC_REGISTER_H_ 2 | #define _DYNAMIC_REGISTER_H_ 3 | 4 | #include 5 | 6 | const char *dynamic_register_compile_methods(JNIEnv *env); 7 | 8 | #endif // !_DYNAMIC_REGISTER_H_ -------------------------------------------------------------------------------- /project/jni/nc/ScopedLocalRef.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef SCOPED_LOCAL_REF_H_included 18 | #define SCOPED_LOCAL_REF_H_included 19 | 20 | #include "jni.h" 21 | 22 | #include 23 | 24 | // A smart pointer that deletes a JNI local reference when it goes out of scope. 25 | template 26 | class ScopedLocalRef { 27 | public: 28 | ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) { 29 | } 30 | 31 | ~ScopedLocalRef() { 32 | reset(); 33 | } 34 | 35 | void reset(T ptr = NULL) { 36 | if (ptr != mLocalRef) { 37 | if (mLocalRef != NULL) { 38 | mEnv->DeleteLocalRef(mLocalRef); 39 | } 40 | mLocalRef = ptr; 41 | } 42 | } 43 | 44 | T release() __attribute__((warn_unused_result)) { 45 | T localRef = mLocalRef; 46 | mLocalRef = NULL; 47 | return localRef; 48 | } 49 | 50 | T get() const { 51 | return mLocalRef; 52 | } 53 | 54 | private: 55 | JNIEnv* mEnv; 56 | T mLocalRef; 57 | 58 | // Disallow copy and assignment. 59 | ScopedLocalRef(const ScopedLocalRef&); 60 | void operator=(const ScopedLocalRef&); 61 | }; 62 | 63 | #endif // SCOPED_LOCAL_REF_H_included 64 | -------------------------------------------------------------------------------- /project/jni/nc/ScopedPthreadMutexLock.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef SCOPED_PTHREAD_MUTEX_LOCK_H_included 18 | #define SCOPED_PTHREAD_MUTEX_LOCK_H_included 19 | 20 | #include 21 | 22 | /** 23 | * Locks and unlocks a pthread_mutex_t as it goes in and out of scope. 24 | */ 25 | class ScopedPthreadMutexLock { 26 | public: 27 | explicit ScopedPthreadMutexLock(pthread_mutex_t* mutex) : mMutexPtr(mutex) { 28 | pthread_mutex_lock(mMutexPtr); 29 | } 30 | 31 | ~ScopedPthreadMutexLock() { 32 | pthread_mutex_unlock(mMutexPtr); 33 | } 34 | 35 | private: 36 | pthread_mutex_t* mMutexPtr; 37 | 38 | // Disallow copy and assignment. 39 | ScopedPthreadMutexLock(const ScopedPthreadMutexLock&); 40 | void operator=(const ScopedPthreadMutexLock&); 41 | }; 42 | 43 | #endif // SCOPED_PTHREAD_MUTEX_LOCK_H_included 44 | -------------------------------------------------------------------------------- /project/jni/nc/obfuscate.h: -------------------------------------------------------------------------------- 1 | /* --------------------------------- ABOUT ------------------------------------- 2 | 3 | Original Author: Adam Yaxley 4 | Website: https://github.com/adamyaxley 5 | License: See end of file 6 | 7 | Obfuscate 8 | Guaranteed compile-time string literal obfuscation library for C++14 9 | 10 | Usage: 11 | Pass string literals into the AY_OBFUSCATE macro to obfuscate them at compile 12 | time. AY_OBFUSCATE returns a reference to an ay::obfuscated_data object with the 13 | following traits: 14 | - Guaranteed obfuscation of string 15 | The passed string is encrypted with a simple XOR cipher at compile-time to 16 | prevent it being viewable in the binary image 17 | - Global lifetime 18 | The actual instantiation of the ay::obfuscated_data takes place inside a 19 | lambda as a function level static 20 | - Implicitly convertible to a char* 21 | This means that you can pass it directly into functions that would normally 22 | take a char* or a const char* 23 | 24 | Example: 25 | const char* obfuscated_string = AY_OBFUSCATE("Hello World"); 26 | std::cout << obfuscated_string << std::endl; 27 | 28 | ----------------------------------------------------------------------------- */ 29 | 30 | #pragma once 31 | #if __cplusplus >= 202002L 32 | #define AY_CONSTEVAL consteval 33 | #else 34 | #define AY_CONSTEVAL constexpr 35 | #endif 36 | 37 | // Workaround for __LINE__ not being constexpr when /ZI (Edit and Continue) is enabled in Visual Studio 38 | // See: https://developercommunity.visualstudio.com/t/-line-cannot-be-used-as-an-argument-for-constexpr/195665 39 | #ifdef _MSC_VER 40 | #define AY_CAT(X,Y) AY_CAT2(X,Y) 41 | #define AY_CAT2(X,Y) X##Y 42 | #define AY_LINE int(AY_CAT(__LINE__,U)) 43 | #else 44 | #define AY_LINE __LINE__ 45 | #endif 46 | 47 | #ifndef AY_OBFUSCATE_DEFAULT_KEY 48 | // The default 64 bit key to obfuscate strings with. 49 | // This can be user specified by defining AY_OBFUSCATE_DEFAULT_KEY before 50 | // including obfuscate.h 51 | #define AY_OBFUSCATE_DEFAULT_KEY ay::generate_key(AY_LINE) 52 | #endif 53 | 54 | namespace ay 55 | { 56 | using size_type = unsigned long long; 57 | using key_type = unsigned long long; 58 | 59 | // libstdc++ has std::remove_cvref_t since C++20, but because not every user will be 60 | // able or willing to link to the STL, we prefer to do this functionality ourselves here. 61 | template 62 | struct remove_const_ref { 63 | using type = T; 64 | }; 65 | 66 | template 67 | struct remove_const_ref { 68 | using type = T; 69 | }; 70 | 71 | template 72 | struct remove_const_ref { 73 | using type = T; 74 | }; 75 | 76 | template 77 | struct remove_const_ref { 78 | using type = T; 79 | }; 80 | 81 | template 82 | using char_type = typename remove_const_ref::type; 83 | 84 | // Generate a pseudo-random key that spans all 8 bytes 85 | AY_CONSTEVAL key_type generate_key(key_type seed) 86 | { 87 | // Use the MurmurHash3 64-bit finalizer to hash our seed 88 | key_type key = seed; 89 | key ^= (key >> 33); 90 | key *= 0xff51afd7ed558ccd; 91 | key ^= (key >> 33); 92 | key *= 0xc4ceb9fe1a85ec53; 93 | key ^= (key >> 33); 94 | 95 | // Make sure that a bit in each byte is set 96 | key |= 0x0101010101010101ull; 97 | 98 | return key; 99 | } 100 | 101 | // Obfuscates or deobfuscates data with key 102 | template 103 | constexpr void cipher(CHAR_TYPE* data, size_type size, key_type key) 104 | { 105 | // Obfuscate with a simple XOR cipher based on key 106 | for (size_type i = 0; i < size; i++) 107 | { 108 | data[i] ^= CHAR_TYPE((key >> ((i % 8) * 8)) & 0xFF); 109 | } 110 | } 111 | 112 | // Obfuscates a string at compile time 113 | template 114 | class obfuscator 115 | { 116 | public: 117 | // Obfuscates the string 'data' on construction 118 | AY_CONSTEVAL obfuscator(const CHAR_TYPE* data) 119 | { 120 | // Copy data 121 | for (size_type i = 0; i < N; i++) 122 | { 123 | m_data[i] = data[i]; 124 | } 125 | 126 | // On construction each of the characters in the string is 127 | // obfuscated with an XOR cipher based on key 128 | cipher(m_data, N, KEY); 129 | } 130 | 131 | constexpr const CHAR_TYPE* data() const 132 | { 133 | return &m_data[0]; 134 | } 135 | 136 | AY_CONSTEVAL size_type size() const 137 | { 138 | return N; 139 | } 140 | 141 | AY_CONSTEVAL key_type key() const 142 | { 143 | return KEY; 144 | } 145 | 146 | private: 147 | 148 | CHAR_TYPE m_data[N]{}; 149 | }; 150 | 151 | // Handles decryption and re-encryption of an encrypted string at runtime 152 | template 153 | class obfuscated_data 154 | { 155 | public: 156 | obfuscated_data(const obfuscator& obfuscator) 157 | { 158 | // Copy obfuscated data 159 | for (size_type i = 0; i < N; i++) 160 | { 161 | m_data[i] = obfuscator.data()[i]; 162 | } 163 | } 164 | 165 | ~obfuscated_data() 166 | { 167 | // Zero m_data to remove it from memory 168 | for (size_type i = 0; i < N; i++) 169 | { 170 | m_data[i] = 0; 171 | } 172 | } 173 | 174 | // Returns a pointer to the plain text string, decrypting it if 175 | // necessary 176 | operator CHAR_TYPE* () 177 | { 178 | decrypt(); 179 | return m_data; 180 | } 181 | 182 | // Manually decrypt the string 183 | void decrypt() 184 | { 185 | if (m_encrypted) 186 | { 187 | cipher(m_data, N, KEY); 188 | m_encrypted = false; 189 | } 190 | } 191 | 192 | // Manually re-encrypt the string 193 | void encrypt() 194 | { 195 | if (!m_encrypted) 196 | { 197 | cipher(m_data, N, KEY); 198 | m_encrypted = true; 199 | } 200 | } 201 | 202 | // Returns true if this string is currently encrypted, false otherwise. 203 | bool is_encrypted() const 204 | { 205 | return m_encrypted; 206 | } 207 | 208 | private: 209 | 210 | // Local storage for the string. Call is_encrypted() to check whether or 211 | // not the string is currently obfuscated. 212 | CHAR_TYPE m_data[N]; 213 | 214 | // Whether data is currently encrypted 215 | bool m_encrypted{ true }; 216 | }; 217 | 218 | // This function exists purely to extract the number of elements 'N' in the 219 | // array 'data' 220 | template 221 | AY_CONSTEVAL auto make_obfuscator(const CHAR_TYPE(&data)[N]) 222 | { 223 | return obfuscator(data); 224 | } 225 | } 226 | 227 | // Obfuscates the string 'data' at compile-time and returns a reference to a 228 | // ay::obfuscated_data object with global lifetime that has functions for 229 | // decrypting the string and is also implicitly convertable to a char* 230 | #define AY_OBFUSCATE(data) AY_OBFUSCATE_KEY(data, AY_OBFUSCATE_DEFAULT_KEY) 231 | 232 | // Obfuscates the string 'data' with 'key' at compile-time and returns a 233 | // reference to a ay::obfuscated_data object with global lifetime that has 234 | // functions for decrypting the string and is also implicitly convertable to a 235 | // char* 236 | #define AY_OBFUSCATE_KEY(data, key) \ 237 | []() -> ay::obfuscated_data>& { \ 238 | static_assert(sizeof(decltype(key)) == sizeof(ay::key_type), "key must be a 64 bit unsigned integer"); \ 239 | static_assert((key) >= (1ull << 56), "key must span all 8 bytes"); \ 240 | using char_type = ay::char_type; \ 241 | constexpr auto n = sizeof(data)/sizeof(data[0]); \ 242 | constexpr auto obfuscator = ay::make_obfuscator(data); \ 243 | thread_local auto obfuscated_data = ay::obfuscated_data(obfuscator); \ 244 | return obfuscated_data; \ 245 | }() 246 | 247 | /* -------------------------------- LICENSE ------------------------------------ 248 | 249 | Public Domain (http://www.unlicense.org) 250 | 251 | This is free and unencumbered software released into the public domain. 252 | 253 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 254 | software, either in source code form or as a compiled binary, for any purpose, 255 | commercial or non-commercial, and by any means. 256 | 257 | In jurisdictions that recognize copyright laws, the author or authors of this 258 | software dedicate any and all copyright interest in the software to the public 259 | domain. We make this dedication for the benefit of the public at large and to 260 | the detriment of our heirs and successors. We intend this dedication to be an 261 | overt act of relinquishment in perpetuity of all present and future rights to 262 | this software under copyright law. 263 | 264 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 265 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 266 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE 267 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 268 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 269 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 270 | 271 | ----------------------------------------------------------------------------- */ 272 | -------------------------------------------------------------------------------- /project/jni/nc/well_known_classes.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "well_known_classes.h" 22 | #include "ScopedLocalRef.h" 23 | 24 | #define LOG_FATAL(...) __android_log_print(ANDROID_LOG_FATAL, "WellKnownClasses", __VA_ARGS__) 25 | 26 | 27 | namespace d2c { 28 | 29 | jclass WellKnownClasses::java_lang_Double; 30 | jclass WellKnownClasses::java_lang_Float; 31 | jclass WellKnownClasses::java_lang_Long; 32 | jclass WellKnownClasses::java_lang_Integer; 33 | jclass WellKnownClasses::java_lang_Short; 34 | jclass WellKnownClasses::java_lang_Character; 35 | jclass WellKnownClasses::java_lang_Byte; 36 | jclass WellKnownClasses::java_lang_Boolean; 37 | 38 | jclass WellKnownClasses::primitive_double; 39 | jclass WellKnownClasses::primitive_float; 40 | jclass WellKnownClasses::primitive_long; 41 | jclass WellKnownClasses::primitive_int; 42 | jclass WellKnownClasses::primitive_short; 43 | jclass WellKnownClasses::primitive_char; 44 | jclass WellKnownClasses::primitive_byte; 45 | jclass WellKnownClasses::primitive_boolean; 46 | 47 | static jobject CachePrimitiveClass(JNIEnv *env, jclass c, const char *name, const char *signature) { 48 | jfieldID fid = env->GetStaticFieldID(c, name, signature); 49 | if (fid == NULL) { 50 | LOG_FATAL ( "Couldn't find field \"%s\" with signature \"%s\"", name, signature); 51 | } 52 | jobject val = env->GetStaticObjectField(c, fid); 53 | return env->NewGlobalRef(val); 54 | } 55 | 56 | jclass CacheClass(JNIEnv* env, const char* jni_class_name) { 57 | ScopedLocalRef c(env, env->FindClass(jni_class_name)); 58 | if (c.get() == NULL) { 59 | LOG_FATAL ("Couldn't find class: %s", jni_class_name); 60 | } 61 | return reinterpret_cast(env->NewGlobalRef(c.get())); 62 | } 63 | 64 | jfieldID CacheField(JNIEnv* env, jclass c, bool is_static, const char* name, const char* signature) { 65 | jfieldID fid = is_static ? env->GetStaticFieldID(c, name, signature) : env->GetFieldID(c, name, signature); 66 | if (fid == NULL) { 67 | LOG_FATAL ( "Couldn't find field \"%s\" with signature \"%s\"", name, signature); 68 | } 69 | return fid; 70 | } 71 | 72 | jmethodID CacheMethod(JNIEnv* env, jclass c, bool is_static, const char* name, const char* signature) { 73 | jmethodID mid = is_static ? env->GetStaticMethodID(c, name, signature) : env->GetMethodID(c, name, signature); 74 | if (mid == NULL) { 75 | LOG_FATAL ( "Couldn't find method \"%s\" with signature \"%s\"", name, signature); 76 | } 77 | return mid; 78 | } 79 | 80 | 81 | void WellKnownClasses::Init(JNIEnv* env) { 82 | java_lang_Double = CacheClass(env, "java/lang/Double"); 83 | java_lang_Float = CacheClass(env, "java/lang/Float"); 84 | java_lang_Long = CacheClass(env, "java/lang/Long"); 85 | java_lang_Integer = CacheClass(env, "java/lang/Integer"); 86 | java_lang_Short = CacheClass(env, "java/lang/Short"); 87 | java_lang_Character = CacheClass(env, "java/lang/Character"); 88 | java_lang_Byte = CacheClass(env, "java/lang/Byte"); 89 | java_lang_Boolean = CacheClass(env, "java/lang/Boolean"); 90 | 91 | primitive_double = static_cast(CachePrimitiveClass(env, java_lang_Double, "TYPE", 92 | "Ljava/lang/Class;")); 93 | primitive_float = static_cast(CachePrimitiveClass(env, java_lang_Float, "TYPE", 94 | "Ljava/lang/Class;")); 95 | primitive_long = static_cast(CachePrimitiveClass(env, java_lang_Long, "TYPE", 96 | "Ljava/lang/Class;")); 97 | primitive_int = static_cast(CachePrimitiveClass(env, java_lang_Integer, "TYPE", 98 | "Ljava/lang/Class;")); 99 | primitive_short = static_cast(CachePrimitiveClass(env, java_lang_Short, "TYPE", 100 | "Ljava/lang/Class;")); 101 | primitive_char = static_cast(CachePrimitiveClass(env, java_lang_Character, "TYPE", 102 | "Ljava/lang/Class;")); 103 | primitive_byte = static_cast(CachePrimitiveClass(env, java_lang_Byte, "TYPE", 104 | "Ljava/lang/Class;")); 105 | primitive_boolean = static_cast(CachePrimitiveClass(env, java_lang_Boolean, "TYPE", 106 | "Ljava/lang/Class;")); 107 | 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /project/jni/nc/well_known_classes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef ART_RUNTIME_WELL_KNOWN_CLASSES_H_ 18 | #define ART_RUNTIME_WELL_KNOWN_CLASSES_H_ 19 | 20 | #include 21 | #include 22 | 23 | namespace d2c { 24 | 25 | // Various classes used in JNI. We cache them so we don't have to keep looking 26 | // them up. Similar to libcore's JniConstants (except there's no overlap, so 27 | // we keep them separate). 28 | 29 | jmethodID CacheMethod(JNIEnv* env, jclass c, bool is_static, const char* name, const char* signature); 30 | jclass CacheClass(JNIEnv* env, const char* jni_class_name); 31 | jfieldID CacheField(JNIEnv* env, jclass c, bool is_static, const char* name, const char* signature); 32 | 33 | struct WellKnownClasses { 34 | public: 35 | static void Init(JNIEnv* env); // Run before native methods are registered. 36 | 37 | static jclass java_lang_Double; 38 | static jclass java_lang_Float; 39 | static jclass java_lang_Long; 40 | static jclass java_lang_Integer; 41 | static jclass java_lang_Short; 42 | static jclass java_lang_Character; 43 | static jclass java_lang_Byte; 44 | static jclass java_lang_Boolean; 45 | 46 | static jclass primitive_double; 47 | static jclass primitive_float; 48 | static jclass primitive_long; 49 | static jclass primitive_int; 50 | static jclass primitive_short; 51 | static jclass primitive_char; 52 | static jclass primitive_byte; 53 | static jclass primitive_boolean; 54 | }; 55 | 56 | } // namespace art 57 | 58 | #endif // ART_RUNTIME_WELL_KNOWN_CLASSES_H_ 59 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | lxml>=4.3.0 2 | -------------------------------------------------------------------------------- /tools/APKEditor.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kirlif/d2c/80305482722efd38781d9213a7cc20b7bc036eed/tools/APKEditor.jar -------------------------------------------------------------------------------- /tools/apksigner.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kirlif/d2c/80305482722efd38781d9213a7cc20b7bc036eed/tools/apksigner.jar -------------------------------------------------------------------------------- /tools/manifest-editor.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kirlif/d2c/80305482722efd38781d9213a7cc20b7bc036eed/tools/manifest-editor.jar --------------------------------------------------------------------------------