├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── LICENSE-docs ├── README.md ├── README.zh-CN.md ├── build_libs.sh ├── clean_libs.sh ├── docs ├── overview │ ├── android_plt_hook_overview.zh-CN.md │ ├── code │ │ ├── .gitignore │ │ ├── build.sh │ │ ├── clean.sh │ │ ├── libtest │ │ │ └── jni │ │ │ │ ├── Android.mk │ │ │ │ ├── Application.mk │ │ │ │ ├── test.c │ │ │ │ └── test.h │ │ ├── main │ │ │ └── jni │ │ │ │ ├── Android.mk │ │ │ │ ├── Application.mk │ │ │ │ └── main.c │ │ └── run.sh │ └── res │ │ ├── elfheader.png │ │ ├── elfpltgot.png │ │ └── elfview.png ├── xhooklogo.png └── xhooklogo.sketch ├── install_libs.sh ├── libbiz └── jni │ ├── Android.mk │ ├── Application.mk │ └── biz.c ├── libtest └── jni │ ├── Android.mk │ ├── Application.mk │ └── test.c ├── libxhook └── jni │ ├── Android.mk │ ├── Application.mk │ ├── queue.h │ ├── tree.h │ ├── xh_core.c │ ├── xh_core.h │ ├── xh_elf.c │ ├── xh_elf.h │ ├── xh_errno.h │ ├── xh_jni.c │ ├── xh_log.c │ ├── xh_log.h │ ├── xh_util.c │ ├── xh_util.h │ ├── xh_version.c │ ├── xh_version.h │ ├── xhook.c │ └── xhook.h └── xhookwrapper ├── .gitignore ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── qiyi │ │ ├── test │ │ ├── NativeHandler.java │ │ └── Test.java │ │ └── xhookwrapper │ │ └── MainActivity.java │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ └── activity_main.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── biz ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── qiyi │ │ └── biz │ │ ├── Biz.java │ │ └── NativeHandler.java │ └── res │ └── values │ └── strings.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── xhook ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src └── main ├── AndroidManifest.xml ├── java └── com │ └── qiyi │ └── xhook │ ├── NativeHandler.java │ └── XHook.java └── res └── values └── strings.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | libxhook/libs/ 4 | libxhook/obj/ 5 | libbiz/libs/ 6 | libbiz/obj/ 7 | libtest/libs/ 8 | libtest/obj/ 9 | 10 | #for xhookwrapper 11 | build/ 12 | .gradle/ 13 | .idea/ 14 | local.properties 15 | *.iml 16 | *.log 17 | *.so 18 | xhookwrapper/xhook/libs/ 19 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to xHook 2 | 3 | Welcome to the xHook project. Read on to learn more about our development process and how to propose bug fixes and improvements. 4 | 5 | ## Issues 6 | 7 | We use GitHub issues to track public bugs and feature requests. Before creating an issue, please note the following: 8 | 9 | 1. Please search existing issues before creating a new one. 10 | 2. Please ensure your description is clear and has sufficient instructions to be able to reproduce the issue. The more information the better. 11 | 12 | 13 | ## Branch Management 14 | 15 | There are 2 main branches: 16 | 17 | 1. `master` branch 18 | 19 | * It's the latest (pre-)release branch. We use `master` for tags. 20 | * **Please do NOT submit any PR on `master` branch.** 21 | 22 | 2. `dev` branch 23 | 24 | * It's our stable developing branch. 25 | * Once `dev` has passed iQIYI's internal tests, it will be merged to `master` branch for the next release. 26 | * **Please always submit PR on `dev` branch.** 27 | 28 | 29 | ## Pull Requests 30 | 31 | Please make sure the following is done when submitting a pull request: 32 | 33 | 1. Fork the repo and create your branch from `master`. 34 | 2. Add the copyright notice to the top of any new files you've added. 35 | 3. Try your best to test your code. 36 | 4. Squash all of your commits into one meaningful commit. 37 | 38 | 39 | ## Code Style Guide 40 | 41 | 1. 4 spaces for indentation rather than tabs. 42 | 2. Follow the C code style already in place. 43 | 44 | 45 | ## License 46 | 47 | By contributing to xHook, you agree that your contributions will be licensed under its [MIT LICENSE](LICENSE). 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. 2 | 3 | Most source code in xhook are MIT licensed. Some other source code 4 | have BSD-style licenses. 5 | 6 | A copy of the MIT License is included in this file. 7 | 8 | 9 | Source code Licensed under the BSD 2-Clause License 10 | =================================================== 11 | 12 | tree.h 13 | 14 | Copyright 2002 Niels Provos 15 | All rights reserved. 16 | 17 | Redistribution and use in source and binary forms, with or without 18 | modification, are permitted provided that the following conditions 19 | are met: 20 | 1. Redistributions of source code must retain the above copyright 21 | notice, this list of conditions and the following disclaimer. 22 | 2. Redistributions in binary form must reproduce the above copyright 23 | notice, this list of conditions and the following disclaimer in the 24 | documentation and/or other materials provided with the distribution. 25 | 26 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 27 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 28 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 29 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 30 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 31 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | 37 | 38 | Source code Licensed under the BSD 3-Clause License 39 | =================================================== 40 | 41 | queue.h 42 | 43 | Copyright (c) 1991, 1993 The Regents of the University of California. 44 | All rights reserved. 45 | 46 | Redistribution and use in source and binary forms, with or without 47 | modification, are permitted provided that the following conditions 48 | are met: 49 | 1. Redistributions of source code must retain the above copyright 50 | notice, this list of conditions and the following disclaimer. 51 | 2. Redistributions in binary form must reproduce the above copyright 52 | notice, this list of conditions and the following disclaimer in the 53 | documentation and/or other materials provided with the distribution. 54 | 3. Neither the name of the University nor the names of its contributors 55 | may be used to endorse or promote products derived from this software 56 | without specific prior written permission. 57 | 58 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 59 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 60 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 61 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 62 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 63 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 64 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 65 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 66 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 67 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 68 | SUCH DAMAGE. 69 | 70 | 71 | Terms of the MIT License 72 | ======================== 73 | 74 | Permission is hereby granted, free of charge, to any person obtaining a copy 75 | of this software and associated documentation files (the "Software"), to deal 76 | in the Software without restriction, including without limitation the rights 77 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 78 | copies of the Software, and to permit persons to whom the Software is 79 | furnished to do so, subject to the following conditions: 80 | 81 | The above copyright notice and this permission notice shall be included in all 82 | copies or substantial portions of the Software. 83 | 84 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 85 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 86 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 87 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 88 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 89 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 90 | SOFTWARE. 91 | -------------------------------------------------------------------------------- /LICENSE-docs: -------------------------------------------------------------------------------- 1 | Attribution 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More_considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution 4.0 International Public License 58 | 59 | By exercising the Licensed Rights (defined below), You accept and agree 60 | to be bound by the terms and conditions of this Creative Commons 61 | Attribution 4.0 International Public License ("Public License"). To the 62 | extent this Public License may be interpreted as a contract, You are 63 | granted the Licensed Rights in consideration of Your acceptance of 64 | these terms and conditions, and the Licensor grants You such rights in 65 | consideration of benefits the Licensor receives from making the 66 | Licensed Material available under these terms and conditions. 67 | 68 | 69 | Section 1 -- Definitions. 70 | 71 | a. Adapted Material means material subject to Copyright and Similar 72 | Rights that is derived from or based upon the Licensed Material 73 | and in which the Licensed Material is translated, altered, 74 | arranged, transformed, or otherwise modified in a manner requiring 75 | permission under the Copyright and Similar Rights held by the 76 | Licensor. For purposes of this Public License, where the Licensed 77 | Material is a musical work, performance, or sound recording, 78 | Adapted Material is always produced where the Licensed Material is 79 | synched in timed relation with a moving image. 80 | 81 | b. Adapter's License means the license You apply to Your Copyright 82 | and Similar Rights in Your contributions to Adapted Material in 83 | accordance with the terms and conditions of this Public License. 84 | 85 | c. Copyright and Similar Rights means copyright and/or similar rights 86 | closely related to copyright including, without limitation, 87 | performance, broadcast, sound recording, and Sui Generis Database 88 | Rights, without regard to how the rights are labeled or 89 | categorized. For purposes of this Public License, the rights 90 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 91 | Rights. 92 | 93 | d. Effective Technological Measures means those measures that, in the 94 | absence of proper authority, may not be circumvented under laws 95 | fulfilling obligations under Article 11 of the WIPO Copyright 96 | Treaty adopted on December 20, 1996, and/or similar international 97 | agreements. 98 | 99 | e. Exceptions and Limitations means fair use, fair dealing, and/or 100 | any other exception or limitation to Copyright and Similar Rights 101 | that applies to Your use of the Licensed Material. 102 | 103 | f. Licensed Material means the artistic or literary work, database, 104 | or other material to which the Licensor applied this Public 105 | License. 106 | 107 | g. Licensed Rights means the rights granted to You subject to the 108 | terms and conditions of this Public License, which are limited to 109 | all Copyright and Similar Rights that apply to Your use of the 110 | Licensed Material and that the Licensor has authority to license. 111 | 112 | h. Licensor means the individual(s) or entity(ies) granting rights 113 | under this Public License. 114 | 115 | i. Share means to provide material to the public by any means or 116 | process that requires permission under the Licensed Rights, such 117 | as reproduction, public display, public performance, distribution, 118 | dissemination, communication, or importation, and to make material 119 | available to the public including in ways that members of the 120 | public may access the material from a place and at a time 121 | individually chosen by them. 122 | 123 | j. Sui Generis Database Rights means rights other than copyright 124 | resulting from Directive 96/9/EC of the European Parliament and of 125 | the Council of 11 March 1996 on the legal protection of databases, 126 | as amended and/or succeeded, as well as other essentially 127 | equivalent rights anywhere in the world. 128 | 129 | k. You means the individual or entity exercising the Licensed Rights 130 | under this Public License. Your has a corresponding meaning. 131 | 132 | 133 | Section 2 -- Scope. 134 | 135 | a. License grant. 136 | 137 | 1. Subject to the terms and conditions of this Public License, 138 | the Licensor hereby grants You a worldwide, royalty-free, 139 | non-sublicensable, non-exclusive, irrevocable license to 140 | exercise the Licensed Rights in the Licensed Material to: 141 | 142 | a. reproduce and Share the Licensed Material, in whole or 143 | in part; and 144 | 145 | b. produce, reproduce, and Share Adapted Material. 146 | 147 | 2. Exceptions and Limitations. For the avoidance of doubt, where 148 | Exceptions and Limitations apply to Your use, this Public 149 | License does not apply, and You do not need to comply with 150 | its terms and conditions. 151 | 152 | 3. Term. The term of this Public License is specified in Section 153 | 6(a). 154 | 155 | 4. Media and formats; technical modifications allowed. The 156 | Licensor authorizes You to exercise the Licensed Rights in 157 | all media and formats whether now known or hereafter created, 158 | and to make technical modifications necessary to do so. The 159 | Licensor waives and/or agrees not to assert any right or 160 | authority to forbid You from making technical modifications 161 | necessary to exercise the Licensed Rights, including 162 | technical modifications necessary to circumvent Effective 163 | Technological Measures. For purposes of this Public License, 164 | simply making modifications authorized by this Section 2(a) 165 | (4) never produces Adapted Material. 166 | 167 | 5. Downstream recipients. 168 | 169 | a. Offer from the Licensor -- Licensed Material. Every 170 | recipient of the Licensed Material automatically 171 | receives an offer from the Licensor to exercise the 172 | Licensed Rights under the terms and conditions of this 173 | Public License. 174 | 175 | b. No downstream restrictions. You may not offer or impose 176 | any additional or different terms or conditions on, or 177 | apply any Effective Technological Measures to, the 178 | Licensed Material if doing so restricts exercise of the 179 | Licensed Rights by any recipient of the Licensed 180 | Material. 181 | 182 | 6. No endorsement. Nothing in this Public License constitutes or 183 | may be construed as permission to assert or imply that You 184 | are, or that Your use of the Licensed Material is, connected 185 | with, or sponsored, endorsed, or granted official status by, 186 | the Licensor or others designated to receive attribution as 187 | provided in Section 3(a)(1)(A)(i). 188 | 189 | b. Other rights. 190 | 191 | 1. Moral rights, such as the right of integrity, are not 192 | licensed under this Public License, nor are publicity, 193 | privacy, and/or other similar personality rights; however, to 194 | the extent possible, the Licensor waives and/or agrees not to 195 | assert any such rights held by the Licensor to the limited 196 | extent necessary to allow You to exercise the Licensed 197 | Rights, but not otherwise. 198 | 199 | 2. Patent and trademark rights are not licensed under this 200 | Public License. 201 | 202 | 3. To the extent possible, the Licensor waives any right to 203 | collect royalties from You for the exercise of the Licensed 204 | Rights, whether directly or through a collecting society 205 | under any voluntary or waivable statutory or compulsory 206 | licensing scheme. In all other cases the Licensor expressly 207 | reserves any right to collect such royalties. 208 | 209 | 210 | Section 3 -- License Conditions. 211 | 212 | Your exercise of the Licensed Rights is expressly made subject to the 213 | following conditions. 214 | 215 | a. Attribution. 216 | 217 | 1. If You Share the Licensed Material (including in modified 218 | form), You must: 219 | 220 | a. retain the following if it is supplied by the Licensor 221 | with the Licensed Material: 222 | 223 | i. identification of the creator(s) of the Licensed 224 | Material and any others designated to receive 225 | attribution, in any reasonable manner requested by 226 | the Licensor (including by pseudonym if 227 | designated); 228 | 229 | ii. a copyright notice; 230 | 231 | iii. a notice that refers to this Public License; 232 | 233 | iv. a notice that refers to the disclaimer of 234 | warranties; 235 | 236 | v. a URI or hyperlink to the Licensed Material to the 237 | extent reasonably practicable; 238 | 239 | b. indicate if You modified the Licensed Material and 240 | retain an indication of any previous modifications; and 241 | 242 | c. indicate the Licensed Material is licensed under this 243 | Public License, and include the text of, or the URI or 244 | hyperlink to, this Public License. 245 | 246 | 2. You may satisfy the conditions in Section 3(a)(1) in any 247 | reasonable manner based on the medium, means, and context in 248 | which You Share the Licensed Material. For example, it may be 249 | reasonable to satisfy the conditions by providing a URI or 250 | hyperlink to a resource that includes the required 251 | information. 252 | 253 | 3. If requested by the Licensor, You must remove any of the 254 | information required by Section 3(a)(1)(A) to the extent 255 | reasonably practicable. 256 | 257 | 4. If You Share Adapted Material You produce, the Adapter's 258 | License You apply must not prevent recipients of the Adapted 259 | Material from complying with this Public License. 260 | 261 | 262 | Section 4 -- Sui Generis Database Rights. 263 | 264 | Where the Licensed Rights include Sui Generis Database Rights that 265 | apply to Your use of the Licensed Material: 266 | 267 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 268 | to extract, reuse, reproduce, and Share all or a substantial 269 | portion of the contents of the database; 270 | 271 | b. if You include all or a substantial portion of the database 272 | contents in a database in which You have Sui Generis Database 273 | Rights, then the database in which You have Sui Generis Database 274 | Rights (but not its individual contents) is Adapted Material; and 275 | 276 | c. You must comply with the conditions in Section 3(a) if You Share 277 | all or a substantial portion of the contents of the database. 278 | 279 | For the avoidance of doubt, this Section 4 supplements and does not 280 | replace Your obligations under this Public License where the Licensed 281 | Rights include other Copyright and Similar Rights. 282 | 283 | 284 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 285 | 286 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 287 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 288 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 289 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 290 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 291 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 292 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 293 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 294 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 295 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 296 | 297 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 298 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 299 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 300 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 301 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 302 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 303 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 304 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 305 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 306 | 307 | c. The disclaimer of warranties and limitation of liability provided 308 | above shall be interpreted in a manner that, to the extent 309 | possible, most closely approximates an absolute disclaimer and 310 | waiver of all liability. 311 | 312 | 313 | Section 6 -- Term and Termination. 314 | 315 | a. This Public License applies for the term of the Copyright and 316 | Similar Rights licensed here. However, if You fail to comply with 317 | this Public License, then Your rights under this Public License 318 | terminate automatically. 319 | 320 | b. Where Your right to use the Licensed Material has terminated under 321 | Section 6(a), it reinstates: 322 | 323 | 1. automatically as of the date the violation is cured, provided 324 | it is cured within 30 days of Your discovery of the 325 | violation; or 326 | 327 | 2. upon express reinstatement by the Licensor. 328 | 329 | For the avoidance of doubt, this Section 6(b) does not affect any 330 | right the Licensor may have to seek remedies for Your violations 331 | of this Public License. 332 | 333 | c. For the avoidance of doubt, the Licensor may also offer the 334 | Licensed Material under separate terms or conditions or stop 335 | distributing the Licensed Material at any time; however, doing so 336 | will not terminate this Public License. 337 | 338 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 339 | License. 340 | 341 | 342 | Section 7 -- Other Terms and Conditions. 343 | 344 | a. The Licensor shall not be bound by any additional or different 345 | terms or conditions communicated by You unless expressly agreed. 346 | 347 | b. Any arrangements, understandings, or agreements regarding the 348 | Licensed Material not stated herein are separate from and 349 | independent of the terms and conditions of this Public License. 350 | 351 | 352 | Section 8 -- Interpretation. 353 | 354 | a. For the avoidance of doubt, this Public License does not, and 355 | shall not be interpreted to, reduce, limit, restrict, or impose 356 | conditions on any use of the Licensed Material that could lawfully 357 | be made without permission under this Public License. 358 | 359 | b. To the extent possible, if any provision of this Public License is 360 | deemed unenforceable, it shall be automatically reformed to the 361 | minimum extent necessary to make it enforceable. If the provision 362 | cannot be reformed, it shall be severed from this Public License 363 | without affecting the enforceability of the remaining terms and 364 | conditions. 365 | 366 | c. No term or condition of this Public License will be waived and no 367 | failure to comply consented to unless expressly agreed to by the 368 | Licensor. 369 | 370 | d. Nothing in this Public License constitutes or may be interpreted 371 | as a limitation upon, or waiver of, any privileges and immunities 372 | that apply to the Licensor or You, including from the legal 373 | processes of any jurisdiction or authority. 374 | 375 | 376 | ======================================================================= 377 | 378 | Creative Commons is not a party to its public licenses. 379 | Notwithstanding, Creative Commons may elect to apply one of its public 380 | licenses to material it publishes and in those instances will be 381 | considered the "Licensor." Except for the limited purpose of indicating 382 | that material is shared under a Creative Commons public license or as 383 | otherwise permitted by the Creative Commons policies published at 384 | creativecommons.org/policies, Creative Commons does not authorize the 385 | use of the trademark "Creative Commons" or any other trademark or logo 386 | of Creative Commons without its prior written consent including, 387 | without limitation, in connection with any unauthorized modifications 388 | to any of its public licenses or any other arrangements, 389 | understandings, or agreements concerning use of licensed material. For 390 | the avoidance of doubt, this paragraph does not form part of the public 391 | licenses. 392 | 393 | Creative Commons may be contacted at creativecommons.org. 394 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

xhook

2 | 3 | # xHook 4 | 5 | ![](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat) 6 | ![](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat) 7 | ![](https://img.shields.io/badge/release-1.2.0-red.svg?style=flat) 8 | ![](https://img.shields.io/badge/Android-4.0%20--%2010-blue.svg?style=flat) 9 | ![](https://img.shields.io/badge/arch-armeabi%20%7C%20armeabi--v7a%20%7C%20arm64--v8a%20%7C%20x86%20%7C%20x86__64-blue.svg?style=flat) 10 | 11 | [README 中文版](README.zh-CN.md) 12 | 13 | [Android PLT hook 概述 中文版](docs/overview/android_plt_hook_overview.zh-CN.md) 14 | 15 | xHook is a PLT (Procedure Linkage Table) hook library for Android native ELF (executable and shared libraries). 16 | 17 | xHook has been keeping optimized for stability and compatibility. 18 | 19 | 20 | ## Features 21 | 22 | * Support Android 4.0 - 10 (API level 14 - 29). 23 | * Support armeabi, armeabi-v7a, arm64-v8a, x86 and x86_64. 24 | * Support **ELF HASH** and **GNU HASH** indexed symbols. 25 | * Support **SLEB128** encoded relocation info. 26 | * Support setting hook info via regular expressions. 27 | * Do not require root permission or any system permissions. 28 | * Do not depends on any third-party shared libraries. 29 | 30 | 31 | ## Build 32 | 33 | * Download [Android NDK r16b](https://developer.android.com/ndk/downloads/revision_history.html), set environment PATH. (support for armeabi has been removed since r17) 34 | 35 | * Build and install the native libraries. 36 | 37 | ``` 38 | ./build_libs.sh 39 | ./install_libs.sh 40 | ``` 41 | 42 | 43 | ## Demo 44 | 45 | ``` 46 | cd ./xhookwrapper/ 47 | ./gradlew assembleDebug 48 | adb install ./app/build/outputs/apk/debug/app-debug.apk 49 | ``` 50 | 51 | 52 | ## API 53 | 54 | External API header file: `libxhook/jni/xhook.h` 55 | 56 | ### 1. Register hook info 57 | 58 | ```c 59 | int xhook_register(const char *pathname_regex_str, 60 | const char *symbol, 61 | void *new_func, 62 | void **old_func); 63 | ``` 64 | 65 | In current process's memory space, in every loaded ELF which pathname matches regular expression `pathname_regex_str`, every PLT entries to `symbol` will be **replaced with** `new_func`. The original one will be saved in `old_func`. 66 | 67 | The `new_func` **must** have the same function declaration as the original one. 68 | 69 | Return zero if successful, non-zero otherwise. 70 | 71 | The regular expression for `pathname_regex_str` only support **POSIX BRE (Basic Regular Expression)**. 72 | 73 | ### 2. Ignore some hook info 74 | 75 | ```c 76 | int xhook_ignore(const char *pathname_regex_str, 77 | const char *symbol); 78 | ``` 79 | 80 | Ignore some hook info according to `pathname_regex_str` and `symbol`, from registered hooks by `xhook_register`. If `symbol` is `NULL`, xhook will ignore all symbols from ELF which pathname matches `pathname_regex_str`. 81 | 82 | Return zero if successful, non-zero otherwise. 83 | 84 | The regular expression for `pathname_regex_str` only support **POSIX BRE**. 85 | 86 | ### 3. Do hook 87 | 88 | ```c 89 | int xhook_refresh(int async); 90 | ``` 91 | 92 | Do the real hook operations according to the registered hook info. 93 | 94 | Pass `1` to `async` for asynchronous hook. Pass `0` to `async` for synchronous hook. 95 | 96 | Return zero if successful, non-zero otherwise. 97 | 98 | xhook will keep a global cache for saving the last ELF loading info from `/proc/self/maps`. This cache will also be updated in `xhook_refresh`. With this cache, `xhook_refresh` can determine which ELF is newly loaded. We only need to do hook in these newly loaded ELF. 99 | 100 | ### 4. Clear cache 101 | 102 | ```c 103 | void xhook_clear(); 104 | ``` 105 | 106 | Clear all cache owned by xhook, reset all global flags to default value. 107 | 108 | If you confirm that all PLT entries you want have been hooked, you could call this function to save some memory. 109 | 110 | ### 5. Enable/Disable debug info 111 | 112 | ```c 113 | void xhook_enable_debug(int flag); 114 | ``` 115 | 116 | Pass `1` to `flag` for enable debug info. Pass `0` to `flag` for disable. (**disabled** by default) 117 | 118 | Debug info will be sent to logcat with tag `xhook`. 119 | 120 | ### 6. Enable/Disable SFP (segmentation fault protection) 121 | 122 | ```c 123 | void xhook_enable_sigsegv_protection(int flag); 124 | ``` 125 | 126 | Pass `1` to `flag` for enable SFP. Pass `0` to `flag` for disable. (**enabled** by default) 127 | 128 | xhook is NOT a compliant business layer library. We have to calculate the value of some pointers directly. Reading or writing the memory pointed to by these pointers will cause a segmentation fault in some unusual situations and environment. The APP crash rate increased which caused by xhook is about one ten-millionth (0.0000001) according to our test. (The increased crash rate is also related to the ELFs and symbols you need to hook). Finally, we have to use some trick to prevent this harmless crashing. We called it SFP (segmentation fault protection) which consists of: `sigaction()`, `SIGSEGV`, `siglongjmp()` and `sigsetjmp()`. 129 | 130 | **You should always enable SFP for release-APP, this will prevent your app from crashing. On the other hand, you should always disable SFP for debug-APP, so you can't miss any common coding mistakes that should be fixed.** 131 | 132 | 133 | ## Examples 134 | 135 | ```c 136 | //detect memory leaks 137 | xhook_register(".*\\.so$", "malloc", my_malloc, NULL); 138 | xhook_register(".*\\.so$", "calloc", my_calloc, NULL); 139 | xhook_register(".*\\.so$", "realloc", my_realloc, NULL); 140 | xhook_register(".*\\.so$", "free", my_free, NULL); 141 | 142 | //inspect sockets lifecycle 143 | xhook_register(".*\\.so$", "getaddrinfo", my_getaddrinfo, NULL); 144 | xhook_register(".*\\.so$", "socket", my_socket, NULL); 145 | xhook_register(".*\\.so$", "setsockopt" my_setsockopt, NULL); 146 | xhook_register(".*\\.so$", "bind", my_bind, NULL); 147 | xhook_register(".*\\.so$", "listen", my_listen, NULL); 148 | xhook_register(".*\\.so$", "connect", my_connect, NULL); 149 | xhook_register(".*\\.so$", "shutdown", my_shutdown, NULL); 150 | xhook_register(".*\\.so$", "close", my_close, NULL); 151 | 152 | //filter off and save some android log to local file 153 | xhook_register(".*\\.so$", "__android_log_write", my_log_write, NULL); 154 | xhook_register(".*\\.so$", "__android_log_print", my_log_print, NULL); 155 | xhook_register(".*\\.so$", "__android_log_vprint", my_log_vprint, NULL); 156 | xhook_register(".*\\.so$", "__android_log_assert", my_log_assert, NULL); 157 | 158 | //tracking (ignore linker and linker64) 159 | xhook_register("^/system/.*$", "mmap", my_mmap, NULL); 160 | xhook_register("^/vendor/.*$", "munmap", my_munmap, NULL); 161 | xhook_ignore (".*/linker$", "mmap"); 162 | xhook_ignore (".*/linker$", "munmap"); 163 | xhook_ignore (".*/linker64$", "mmap"); 164 | xhook_ignore (".*/linker64$", "munmap"); 165 | 166 | //defense to some injection attacks 167 | xhook_register(".*com\\.hacker.*\\.so$", "malloc", my_malloc_always_return_NULL, NULL); 168 | xhook_register(".*/libhacker\\.so$", "connect", my_connect_with_recorder, NULL); 169 | 170 | //fix some system bug 171 | xhook_register(".*some_vendor.*/libvictim\\.so$", "bad_func", my_nice_func, NULL); 172 | 173 | //ignore all hooks in libwebviewchromium.so 174 | xhook_ignore(".*/libwebviewchromium.so$", NULL); 175 | 176 | //hook now! 177 | xhook_refresh(1); 178 | ``` 179 | 180 | 181 | ## Support 182 | 183 | * [GitHub Issues](https://github.com/iqiyi/xHook/issues) 184 | * [GitHub Discussions](https://github.com/iqiyi/xHook/discussions) 185 | * [Telegram Public Group](https://t.me/android_native_geeks) 186 | 187 | 188 | ## Contributing 189 | 190 | See [xHook Contributing Guide](CONTRIBUTING.md). 191 | 192 | 193 | ## License 194 | 195 | xHook is MIT licensed, as found in the [LICENSE](LICENSE) file. 196 | 197 | xHook documentation is Creative Commons licensed, as found in the [LICENSE-docs](LICENSE-docs) file. 198 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 |

xhook

2 | 3 | # xHook 4 | 5 | ![](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat) 6 | ![](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat) 7 | ![](https://img.shields.io/badge/release-1.2.0-red.svg?style=flat) 8 | ![](https://img.shields.io/badge/Android-4.0%20--%2010-blue.svg?style=flat) 9 | ![](https://img.shields.io/badge/arch-armeabi%20%7C%20armeabi--v7a%20%7C%20arm64--v8a%20%7C%20x86%20%7C%20x86__64-blue.svg?style=flat) 10 | 11 | [README English Version](README.md) 12 | 13 | [Android PLT hook 概述 中文版](docs/overview/android_plt_hook_overview.zh-CN.md) 14 | 15 | xHook 是一个针对 Android 平台 ELF (可执行文件和动态库) 的 PLT (Procedure Linkage Table) hook 库。 16 | 17 | xHook 一直在稳定性和兼容性方面做着持续的优化。 18 | 19 | 20 | ## 特征 21 | 22 | * 支持 Android 4.0 - 10(API level 14 - 29)。 23 | * 支持 armeabi,armeabi-v7a,arm64-v8a,x86 和 x86_64。 24 | * 支持 **ELF HASH** 和 **GNU HASH** 索引的符号。 25 | * 支持 **SLEB128** 编码的重定位信息。 26 | * 支持通过正则表达式批量设置 hook 信息。 27 | * 不需要 root 权限或任何系统权限。 28 | * 不依赖于任何的第三方动态库。 29 | 30 | 31 | ## 编译 32 | 33 | * 下载 [Android NDK r16b](https://developer.android.com/ndk/downloads/revision_history.html),设置 PATH 环境变量。(对 armeabi 的支持,从 r17 版本开始被移除了) 34 | 35 | * 编译和安装 native 库。 36 | 37 | ``` 38 | ./build_libs.sh 39 | ./install_libs.sh 40 | ``` 41 | 42 | 43 | ## Demo 44 | 45 | ``` 46 | cd ./xhookwrapper/ 47 | ./gradlew assembleDebug 48 | adb install ./app/build/outputs/apk/debug/app-debug.apk 49 | ``` 50 | 51 | 52 | ## API 53 | 54 | 外部 API 头文件: `libxhook/jni/xhook.h` 55 | 56 | ### 1. 注册 hook 信息 57 | 58 | ```c 59 | int xhook_register(const char *pathname_regex_str, 60 | const char *symbol, 61 | void *new_func, 62 | void **old_func); 63 | ``` 64 | 65 | 在当前进程的内存空间中,在每一个符合正则表达式 `pathname_regex_str` 的已加载ELF中,每一个调用 `symbol` 的 PLT 入口点的地址值都将给替换成 `new_func`。之前的 PLT 入口点的地址值将被保存在 `old_func` 中。 66 | 67 | `new_func` 必须具有和原函数同样的函数声明。 68 | 69 | 成功返回 0,失败返回 非0。 70 | 71 | `pathname_regex_str` 只支持 **POSIX BRE (Basic Regular Expression)** 定义的正则表达式语法。 72 | 73 | ### 2. 忽略部分 hook 信息 74 | 75 | ```c 76 | int xhook_ignore(const char *pathname_regex_str, 77 | const char *symbol); 78 | ``` 79 | 80 | 根据 `pathname_regex_str` 和 `symbol`,从已经通过 `xhook_register` 注册的 hook 信息中,忽略一部分 hook 信息。如果 `symbol` 为 `NULL`,xhook 将忽略所有路径名符合正则表达式 `pathname_regex_str` 的 ELF。 81 | 82 | 成功返回 0,失败返回 非0。 83 | 84 | `pathname_regex_str` 只支持 **POSIX BRE** 定义的正则表达式语法。 85 | 86 | ### 3. 执行 hook 87 | 88 | ```c 89 | int xhook_refresh(int async); 90 | ``` 91 | 92 | 根据前面注册的 hook 信息,执行真正的 hook 操作。 93 | 94 | 给 `async` 参数传 `1` 表示执行异步的 hook 操作,传 `0` 表示执行同步的 hook 操作。 95 | 96 | 成功返回 0,失败返回 非0。 97 | 98 | xhook 在内部维护了一个全局的缓存,用于保存最后一次从 `/proc/self/maps` 读取到的 ELF 加载信息。每次一调用 `xhook_refresh` 函数,这个缓存都将被更新。xhook 使用这个缓存来判断哪些 ELF 是这次新被加载到内存中的。我们每次只需要针对这些新加载的 ELF 做 hook 就可以了。 99 | 100 | ### 4. 清除缓存 101 | 102 | ```c 103 | void xhook_clear(); 104 | ``` 105 | 106 | 清除 xhook 的缓存,重置所有的全局标示。 107 | 108 | 如果你确定你需要的所有 PLT 入口点都已经被替换了,你可以调用这个函数来释放和节省一些内存空间。 109 | 110 | ### 5. 启用/禁用 调试信息 111 | 112 | ```c 113 | void xhook_enable_debug(int flag); 114 | ``` 115 | 116 | 给 `flag` 参数传 `1` 表示启用调试信息,传 `0` 表示禁用调试信息。 (默认为:**禁用**) 117 | 118 | 调试信息将被输出到 logcat,对应的 TAG 为:`xhook`。 119 | 120 | ### 6. 启用/禁用 SFP (段错误保护) 121 | 122 | ```c 123 | void xhook_enable_sigsegv_protection(int flag); 124 | ``` 125 | 126 | 给 `flag` 参数传 `1` 表示启用 SFP,传 `0` 表示禁用 SFP。 (默认为:**启用**) 127 | 128 | xhook 并不是一个常规的业务层的动态库。在 xhook 中,我们不得不直接计算一些内存指针的值。在一些极端的情况和环境下,读或者写这些指针指向的内存会发生段错误。根据我们的测试,xhook 的行为将导致 APP 崩溃率增加 “一千万分之一” (0.0000001)。(具体崩溃率可能会增加多少,也和你想要 hook 的库和符号有关)。最终,我们不得不使用某些方法来防止这些无害的崩溃。我们叫它SFP (段错误保护),它是由这些调用和值组成的:`sigaction()`, `SIGSEGV`, `siglongjmp()` 和 `sigsetjmp()`。 129 | 130 | **在 release 版本的 APP 中,你应该始终启用 SFP,这能防止你的 APP 因为 xhook 而崩溃。在 debug 版本的 APP 中,你应该始终禁用 SFP,这样你就不会丢失那些一般性的编码失误导致的段错误,这些段错误是应该被修复的。** 131 | 132 | 133 | ## 例子 134 | 135 | ```c 136 | //监测内存泄露 137 | xhook_register(".*\\.so$", "malloc", my_malloc, NULL); 138 | xhook_register(".*\\.so$", "calloc", my_calloc, NULL); 139 | xhook_register(".*\\.so$", "realloc", my_realloc, NULL); 140 | xhook_register(".*\\.so$", "free", my_free, NULL); 141 | 142 | //监控 sockets 生命周期 143 | xhook_register(".*\\.so$", "getaddrinfo", my_getaddrinfo, NULL); 144 | xhook_register(".*\\.so$", "socket", my_socket, NULL); 145 | xhook_register(".*\\.so$", "setsockopt" my_setsockopt, NULL); 146 | xhook_register(".*\\.so$", "bind", my_bind, NULL); 147 | xhook_register(".*\\.so$", "listen", my_listen, NULL); 148 | xhook_register(".*\\.so$", "connect", my_connect, NULL); 149 | xhook_register(".*\\.so$", "shutdown", my_shutdown, NULL); 150 | xhook_register(".*\\.so$", "close", my_close, NULL); 151 | 152 | //过滤出和保存部分安卓 log 到本地文件 153 | xhook_register(".*\\.so$", "__android_log_write", my_log_write, NULL); 154 | xhook_register(".*\\.so$", "__android_log_print", my_log_print, NULL); 155 | xhook_register(".*\\.so$", "__android_log_vprint", my_log_vprint, NULL); 156 | xhook_register(".*\\.so$", "__android_log_assert", my_log_assert, NULL); 157 | 158 | //追踪某些调用 (忽略 linker 和 linker64) 159 | xhook_register("^/system/.*$", "mmap", my_mmap, NULL); 160 | xhook_register("^/vendor/.*$", "munmap", my_munmap, NULL); 161 | xhook_ignore (".*/linker$", "mmap"); 162 | xhook_ignore (".*/linker$", "munmap"); 163 | xhook_ignore (".*/linker64$", "mmap"); 164 | xhook_ignore (".*/linker64$", "munmap"); 165 | 166 | //防御某些注入攻击 167 | xhook_register(".*com\\.hacker.*\\.so$", "malloc", my_malloc_always_return_NULL, NULL); 168 | xhook_register(".*/libhacker\\.so$", "connect", my_connect_with_recorder, NULL); 169 | 170 | //修复某些系统 bug 171 | xhook_register(".*some_vendor.*/libvictim\\.so$", "bad_func", my_nice_func, NULL); 172 | 173 | //忽略 libwebviewchromium.so 的所有 hook 信息 174 | xhook_ignore(".*/libwebviewchromium.so$", NULL); 175 | 176 | //现在执行 hook! 177 | xhook_refresh(1); 178 | ``` 179 | 180 | 181 | ## 技术支持 182 | 183 | * [GitHub Issues](https://github.com/iqiyi/xHook/issues) 184 | * [GitHub Discussions](https://github.com/iqiyi/xHook/discussions) 185 | * [Telegram Public Group](https://t.me/android_native_geeks) 186 | 187 | 188 | ## 贡献 189 | 190 | 请阅读 [xHook Contributing Guide](CONTRIBUTING.md)。 191 | 192 | 193 | ## 许可证 194 | 195 | xHook 使用 [MIT 许可证](LICENSE)。 196 | 197 | xHook 的文档使用 [Creative Commons 许可证](LICENSE-docs)。 198 | -------------------------------------------------------------------------------- /build_libs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ndk-build -C ./libxhook/jni 4 | ndk-build -C ./libbiz/jni 5 | ndk-build -C ./libtest/jni 6 | -------------------------------------------------------------------------------- /clean_libs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ndk-build -C ./libbiz/jni clean 4 | ndk-build -C ./libxhook/jni clean 5 | ndk-build -C ./libtest/jni clean 6 | -------------------------------------------------------------------------------- /docs/overview/android_plt_hook_overview.zh-CN.md: -------------------------------------------------------------------------------- 1 | # Android PLT hook 概述 2 | 3 | 4 | ## 获取代码和资源 5 | 6 | 你始终可以从 [这里](https://github.com/iqiyi/xHook/blob/master/docs/overview/android_plt_hook_overview.zh-CN.md) 访问本文的最新版本。 7 | 8 | 文中使用的示例代码可以从 [这里](https://github.com/iqiyi/xHook/tree/master/docs/overview/code) 获取。文中提到的 xhook 开源项目可以从 [这里](https://github.com/iqiyi/xHook) 获取。 9 | 10 | 11 | ## 开始 12 | 13 | 14 | ### 新的动态库 15 | 16 | 我们有一个新的动态库:libtest.so。 17 | 18 | > 头文件 test.h 19 | 20 | ```c 21 | #ifndef TEST_H 22 | #define TEST_H 1 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | void say_hello(); 29 | 30 | #ifdef __cplusplus 31 | } 32 | #endif 33 | 34 | #endif 35 | ``` 36 | 37 | > 源文件 test.c 38 | 39 | ```c 40 | #include 41 | #include 42 | 43 | void say_hello() 44 | { 45 | char *buf = malloc(1024); 46 | if(NULL != buf) 47 | { 48 | snprintf(buf, 1024, "%s", "hello\n"); 49 | printf("%s", buf); 50 | } 51 | } 52 | ``` 53 | 54 | `say_hello` 的功能是在终端打印出 `hello\n` 这6个字符(包括结尾的 `\n`)。 55 | 56 | 我们需要一个测试程序:main。 57 | 58 | > 源文件 main.c 59 | 60 | ```c 61 | #include 62 | 63 | int main() 64 | { 65 | say_hello(); 66 | return 0; 67 | } 68 | ``` 69 | 70 | 编译它们分别生成 libtest.so 和 main。运行一下: 71 | 72 | ``` 73 | caikelun@debian:~$ adb push ./libtest.so ./main /data/local/tmp 74 | caikelun@debian:~$ adb shell "chmod +x /data/local/tmp/main" 75 | caikelun@debian:~$ adb shell "export LD_LIBRARY_PATH=/data/local/tmp; /data/local/tmp/main" 76 | hello 77 | caikelun@debian:~$ 78 | ``` 79 | 80 | 太棒了!libtest.so 的代码虽然看上去有些愚蠢,但是它居然可以正确的工作,那还有什么可抱怨的呢?赶紧在新版 APP 中开始使用它吧! 81 | 82 | 遗憾的是,正如你可能已经发现的,libtest.so 存在严重的内存泄露问题,每调用一次 `say_hello` 函数,就会泄露 1024 字节的内存。新版 APP 上线后崩溃率开始上升,各种诡异的崩溃信息和报障信息跌撞而至。 83 | 84 | 85 | ### 面临的问题 86 | 87 | 幸运的是,我们修复了 libtest.so 的问题。可是以后怎么办呢?我们面临 2 个问题: 88 | 89 | 1. 当测试覆盖不足时,如何及时发现和准确定位线上 APP 的此类问题? 90 | 2. 如果 libtest.so 是某些机型的系统库,或者第三方的闭源库,我们如何修复它?如果监控它的行为? 91 | 92 | 93 | ### 怎么做? 94 | 95 | 如果我们能对动态库中的函数调用做 hook(替换,拦截,窃听,或者你觉得任何正确的描述方式),那就能够做到很多我们想做的事情。比如 hook `malloc`,`calloc`,`realloc` 和 `free`,我们就能统计出各个动态库分配了多少内存,哪些内存一直被占用没有释放。 96 | 97 | 这真的能做到吗?答案是:hook 我们自己的进程是完全可以的。hook 其他进程需要 root 权限(对于其他进程,没有 root 权限就没法修改它的内存空间,也没法注入代码)。幸运的是,我们只要 hook 自己就够了。 98 | 99 | 100 | ## ELF 101 | 102 | 103 | ### 概述 104 | 105 | ELF(Executable and Linkable Format)是一种行业标准的二进制数据封装格式,主要用于封装可执行文件、动态库、object 文件和 core dumps 文件。 106 | 107 | 使用 google NDK 对源代码进行编译和链接,生成的动态库或可执行文件都是 ELF 格式的。用 readelf 可以查看 ELF 文件的基本信息,用 objdump 可以查看 ELF 文件的反汇编输出。 108 | 109 | ELF 格式的概述可以参考 [这里](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format),完整定义可以参考 [这里](http://refspecs.linuxbase.org/elf/elf.pdf)。其中最重要的部分是:ELF 文件头、SHT(section header table)、PHT(program header table)。 110 | 111 | 112 | ### ELF 文件头 113 | 114 | ELF 文件的起始处,有一个固定格式的定长的文件头(32 位架构为 52 字节,64 位架构为 64 字节)。ELF 文件头以 magic number `0x7F 0x45 0x4C 0x46` 开始(其中后 3 个字节分别对应可见字符 `E` `L` `F`)。 115 | 116 | libtest.so 的 ELF 文件头信息: 117 | 118 | ``` 119 | caikelun@debian:~$ arm-linux-androideabi-readelf -h ./libtest.so 120 | 121 | ELF Header: 122 | Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 123 | Class: ELF32 124 | Data: 2's complement, little endian 125 | Version: 1 (current) 126 | OS/ABI: UNIX - System V 127 | ABI Version: 0 128 | Type: DYN (Shared object file) 129 | Machine: ARM 130 | Version: 0x1 131 | Entry point address: 0x0 132 | Start of program headers: 52 (bytes into file) 133 | Start of section headers: 12744 (bytes into file) 134 | Flags: 0x5000200, Version5 EABI, soft-float ABI 135 | Size of this header: 52 (bytes) 136 | Size of program headers: 32 (bytes) 137 | Number of program headers: 8 138 | Size of section headers: 40 (bytes) 139 | Number of section headers: 25 140 | Section header string table index: 24 141 | ``` 142 | 143 | ELF 文件头中包含了 SHT 和 PHT 在当前 ELF 文件中的起始位置和长度。例如,libtest.so 的 SHT 起始位置为 12744,长度 40 字节;PHT 起始位置为 52,长度 32字节。 144 | 145 | ![](https://raw.githubusercontent.com/iqiyi/xHook/master/docs/overview/res/elfheader.png) 146 | 147 | 148 | ### SHT(section header table) 149 | 150 | ELF 以 section 为单位来组织和管理各种信息。ELF 使用 SHT 来记录所有 section 的基本信息。主要包括:section 的类型、在文件中的偏移量、大小、加载到内存后的虚拟内存相对地址、内存中字节的对齐方式等。 151 | 152 | libtest.so 的 SHT: 153 | 154 | ``` 155 | caikelun@debian:~$ arm-linux-androideabi-readelf -S ./libtest.so 156 | 157 | There are 25 section headers, starting at offset 0x31c8: 158 | 159 | Section Headers: 160 | [Nr] Name Type Addr Off Size ES Flg Lk Inf Al 161 | [ 0] NULL 00000000 000000 000000 00 0 0 0 162 | [ 1] .note.android.ide NOTE 00000134 000134 000098 00 A 0 0 4 163 | [ 2] .note.gnu.build-i NOTE 000001cc 0001cc 000024 00 A 0 0 4 164 | [ 3] .dynsym DYNSYM 000001f0 0001f0 0003a0 10 A 4 1 4 165 | [ 4] .dynstr STRTAB 00000590 000590 0004b1 00 A 0 0 1 166 | [ 5] .hash HASH 00000a44 000a44 000184 04 A 3 0 4 167 | [ 6] .gnu.version VERSYM 00000bc8 000bc8 000074 02 A 3 0 2 168 | [ 7] .gnu.version_d VERDEF 00000c3c 000c3c 00001c 00 A 4 1 4 169 | [ 8] .gnu.version_r VERNEED 00000c58 000c58 000020 00 A 4 1 4 170 | [ 9] .rel.dyn REL 00000c78 000c78 000040 08 A 3 0 4 171 | [10] .rel.plt REL 00000cb8 000cb8 0000f0 08 AI 3 18 4 172 | [11] .plt PROGBITS 00000da8 000da8 00017c 00 AX 0 0 4 173 | [12] .text PROGBITS 00000f24 000f24 0015a4 00 AX 0 0 4 174 | [13] .ARM.extab PROGBITS 000024c8 0024c8 00003c 00 A 0 0 4 175 | [14] .ARM.exidx ARM_EXIDX 00002504 002504 000100 08 AL 12 0 4 176 | [15] .fini_array FINI_ARRAY 00003e3c 002e3c 000008 04 WA 0 0 4 177 | [16] .init_array INIT_ARRAY 00003e44 002e44 000004 04 WA 0 0 1 178 | [17] .dynamic DYNAMIC 00003e48 002e48 000118 08 WA 4 0 4 179 | [18] .got PROGBITS 00003f60 002f60 0000a0 00 WA 0 0 4 180 | [19] .data PROGBITS 00004000 003000 000004 00 WA 0 0 4 181 | [20] .bss NOBITS 00004004 003004 000000 00 WA 0 0 1 182 | [21] .comment PROGBITS 00000000 003004 000065 01 MS 0 0 1 183 | [22] .note.gnu.gold-ve NOTE 00000000 00306c 00001c 00 0 0 4 184 | [23] .ARM.attributes ARM_ATTRIBUTES 00000000 003088 00003b 00 0 0 1 185 | [24] .shstrtab STRTAB 00000000 0030c3 000102 00 0 0 1 186 | Key to Flags: 187 | W (write), A (alloc), X (execute), M (merge), S (strings), I (info), 188 | L (link order), O (extra OS processing required), G (group), T (TLS), 189 | C (compressed), x (unknown), o (OS specific), E (exclude), 190 | y (noread), p (processor specific) 191 | ``` 192 | 193 | 比较重要,且和 hook 关系比较大的几个 section 是: 194 | 195 | * `.dynstr`:保存了所有的字符串常量信息。 196 | * `.dynsym`:保存了符号(symbol)的信息(符号的类型、起始地址、大小、符号名称在 `.dynstr` 中的索引编号等)。函数也是一种符号。 197 | * `.text`:程序代码经过编译后生成的机器指令。 198 | * `.dynamic`:供动态链接器使用的各项信息,记录了当前 ELF 的外部依赖,以及其他各个重要 section 的起始位置等信息。 199 | * `.got`:Global Offset Table。用于记录外部调用的入口地址。动态链接器(linker)执行重定位(relocate)操作时,这里会被填入真实的外部调用的绝对地址。 200 | * `.plt`:Procedure Linkage Table。外部调用的跳板,主要用于支持 lazy binding 方式的外部调用重定位。(Android 目前只有 MIPS 架构支持 lazy binding) 201 | * `.rel.plt`:对外部函数直接调用的重定位信息。 202 | * `.rel.dyn`:除 `.rel.plt` 以外的重定位信息。(比如通过全局函数指针来调用外部函数) 203 | 204 | ![](https://raw.githubusercontent.com/iqiyi/xHook/master/docs/overview/res/elfpltgot.png) 205 | 206 | 207 | ### PHT(program header table) 208 | 209 | ELF 被加载到内存时,是以 segment 为单位的。一个 segment 包含了一个或多个 section。ELF 使用 PHT 来记录所有 segment 的基本信息。主要包括:segment 的类型、在文件中的偏移量、大小、加载到内存后的虚拟内存相对地址、内存中字节的对齐方式等。 210 | 211 | libtest.so 的 PHT: 212 | 213 | ``` 214 | caikelun@debian:~$ arm-linux-androideabi-readelf -l ./libtest.so 215 | 216 | Elf file type is DYN (Shared object file) 217 | Entry point 0x0 218 | There are 8 program headers, starting at offset 52 219 | 220 | Program Headers: 221 | Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align 222 | PHDR 0x000034 0x00000034 0x00000034 0x00100 0x00100 R 0x4 223 | LOAD 0x000000 0x00000000 0x00000000 0x02604 0x02604 R E 0x1000 224 | LOAD 0x002e3c 0x00003e3c 0x00003e3c 0x001c8 0x001c8 RW 0x1000 225 | DYNAMIC 0x002e48 0x00003e48 0x00003e48 0x00118 0x00118 RW 0x4 226 | NOTE 0x000134 0x00000134 0x00000134 0x000bc 0x000bc R 0x4 227 | GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10 228 | EXIDX 0x002504 0x00002504 0x00002504 0x00100 0x00100 R 0x4 229 | GNU_RELRO 0x002e3c 0x00003e3c 0x00003e3c 0x001c4 0x001c4 RW 0x4 230 | 231 | Section to Segment mapping: 232 | Segment Sections... 233 | 00 234 | 01 .note.android.ident .note.gnu.build-id .dynsym .dynstr .hash .gnu.version .gnu.version_d .gnu.version_r .rel.dyn .rel.plt .plt .text .ARM.extab .ARM.exidx 235 | 02 .fini_array .init_array .dynamic .got .data 236 | 03 .dynamic 237 | 04 .note.android.ident .note.gnu.build-id 238 | 05 239 | 06 .ARM.exidx 240 | 07 .fini_array .init_array .dynamic .got 241 | ``` 242 | 243 | 所有类型为 `PT_LOAD` 的 segment 都会被动态链接器(linker)映射(mmap)到内存中。 244 | 245 | 246 | ### 连接视图(Linking View)和执行视图(Execution View) 247 | 248 | * 连接视图:ELF 未被加载到内存执行前,以 section 为单位的数据组织形式。 249 | * 执行视图:ELF 被加载到内存后,以 segment 为单位的数据组织形式。 250 | 251 | 我们关心的 hook 操作,属于动态形式的内存操作,因此主要关心的是执行视图,即 ELF 被加载到内存后,ELF 中的数据是如何组织和存放的。 252 | 253 | ![](https://raw.githubusercontent.com/iqiyi/xHook/master/docs/overview/res/elfview.png) 254 | 255 | 256 | ### .dynamic section 257 | 258 | 这是一个十分重要和特殊的 section,其中包含了 ELF 中其他各个 section 的内存位置等信息。在执行视图中,总是会存在一个类型为 `PT_DYNAMIC` 的 segment,这个 segment 就包含了 .dynamic section 的内容。 259 | 260 | 无论是执行 hook 操作时,还是动态链接器执行动态链接时,都需要通过 `PT_DYNAMIC` segment 来找到 .dynamic section 的内存位置,再进一步读取其他各项 section 的信息。 261 | 262 | libtest.so 的 .dynamic section: 263 | 264 | ``` 265 | caikelun@debian:~$ arm-linux-androideabi-readelf -d ./libtest.so 266 | 267 | Dynamic section at offset 0x2e48 contains 30 entries: 268 | Tag Type Name/Value 269 | 0x00000003 (PLTGOT) 0x3f7c 270 | 0x00000002 (PLTRELSZ) 240 (bytes) 271 | 0x00000017 (JMPREL) 0xcb8 272 | 0x00000014 (PLTREL) REL 273 | 0x00000011 (REL) 0xc78 274 | 0x00000012 (RELSZ) 64 (bytes) 275 | 0x00000013 (RELENT) 8 (bytes) 276 | 0x6ffffffa (RELCOUNT) 3 277 | 0x00000006 (SYMTAB) 0x1f0 278 | 0x0000000b (SYMENT) 16 (bytes) 279 | 0x00000005 (STRTAB) 0x590 280 | 0x0000000a (STRSZ) 1201 (bytes) 281 | 0x00000004 (HASH) 0xa44 282 | 0x00000001 (NEEDED) Shared library: [libc.so] 283 | 0x00000001 (NEEDED) Shared library: [libm.so] 284 | 0x00000001 (NEEDED) Shared library: [libstdc++.so] 285 | 0x00000001 (NEEDED) Shared library: [libdl.so] 286 | 0x0000000e (SONAME) Library soname: [libtest.so] 287 | 0x0000001a (FINI_ARRAY) 0x3e3c 288 | 0x0000001c (FINI_ARRAYSZ) 8 (bytes) 289 | 0x00000019 (INIT_ARRAY) 0x3e44 290 | 0x0000001b (INIT_ARRAYSZ) 4 (bytes) 291 | 0x0000001e (FLAGS) BIND_NOW 292 | 0x6ffffffb (FLAGS_1) Flags: NOW 293 | 0x6ffffff0 (VERSYM) 0xbc8 294 | 0x6ffffffc (VERDEF) 0xc3c 295 | 0x6ffffffd (VERDEFNUM) 1 296 | 0x6ffffffe (VERNEED) 0xc58 297 | 0x6fffffff (VERNEEDNUM) 1 298 | 0x00000000 (NULL) 0x0 299 | ``` 300 | 301 | 302 | ## 动态链接器(linker) 303 | 304 | 安卓中的动态链接器程序是 linker。源码在 [这里](https://android.googlesource.com/platform/bionic/+/master/linker/)。 305 | 306 | 动态链接(比如执行 dlopen)的大致步骤是: 307 | 308 | 1. 检查已加载的 ELF 列表。(如果 libtest.so 已经加载,就不再重复加载了,仅把 libtest.so 的引用计数加一,然后直接返回。) 309 | 2. 从 libtest.so 的 .dynamic section 中读取 libtest.so 的外部依赖的 ELF 列表,从此列表中剔除已加载的 ELF,最后得到本次需要加载的 ELF 完整列表(包括 libtest.so 自身)。 310 | 3. 逐个加载列表中的 ELF。加载步骤: 311 | * 用 `mmap` 预留一块足够大的内存,用于后续映射 ELF。(`MAP_PRIVATE` 方式) 312 | * 读 ELF 的 PHT,用 `mmap` 把所有类型为 `PT_LOAD` 的 segment 依次映射到内存中。 313 | * 从 .dynamic segment 中读取各信息项,主要是各个 section 的虚拟内存相对地址,然后计算并保存各个 section 的虚拟内存绝对地址。 314 | * 执行重定位操作(relocate),这是最关键的一步。重定位信息可能存在于下面的一个或多个 secion 中:`.rel.plt`, `.rela.plt`, `.rel.dyn`, `.rela.dyn`, `.rel.android`, `.rela.android`。动态链接器需要逐个处理这些 `.relxxx` section 中的重定位诉求。根据已加载的 ELF 的信息,动态链接器查找所需符号的地址(比如 libtest.so 的符号 `malloc`),找到后,将地址值填入 `.relxxx` 中指明的**目标地址**中,这些“**目标地址**”一般存在于`.got` 或 `.data` 中。 315 | * ELF 的引用计数加一。 316 | 4. 逐个调用列表中 ELF 的构造函数(constructor),这些构造函数的地址是之前从 .dynamic segment 中读取到的(类型为 `DT_INIT` 和 `DT_INIT_ARRAY`)。各 ELF 的构造函数是按照依赖关系逐层调用的,先调用被依赖 ELF 的构造函数,最后调用 libtest.so 自己的构造函数。(ELF 也可以定义自己的析构函数(destructor),在 ELF 被 unload 的时候会被自动调用) 317 | 318 | 等一下!我们似乎发现了什么!再看一遍重定位操作(relocate)的部分。难道我们只要从这些 `.relxxx` 中获取到“**目标地址**”,然后在“**目标地址**”中重新填上一个新的函数地址,这样就完成 hook 了吗?也许吧。 319 | 320 | 321 | ## 追踪 322 | 323 | 静态分析验证一下还是很容易的。以 armeabi-v7a 架构的 libtest.so 为例。先看一下 say_hello 函数对应的汇编代码吧。 324 | 325 | ``` 326 | caikelun@debian:~/$ arm-linux-androideabi-readelf -s ./libtest.so 327 | 328 | Symbol table '.dynsym' contains 58 entries: 329 | Num: Value Size Type Bind Vis Ndx Name 330 | 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 331 | 1: 00000000 0 FUNC GLOBAL DEFAULT UND __cxa_finalize@LIBC (2) 332 | 2: 00000000 0 FUNC GLOBAL DEFAULT UND snprintf@LIBC (2) 333 | 3: 00000000 0 FUNC GLOBAL DEFAULT UND malloc@LIBC (2) 334 | 4: 00000000 0 FUNC GLOBAL DEFAULT UND __cxa_atexit@LIBC (2) 335 | 5: 00000000 0 FUNC GLOBAL DEFAULT UND printf@LIBC (2) 336 | 6: 00000f61 60 FUNC GLOBAL DEFAULT 12 say_hello 337 | ............... 338 | ............... 339 | ``` 340 | 341 | 找到了!`say_hello` 在地址 `f61`,对应的汇编指令体积为 `60`(10 进制)字节。用 objdump 查看 `say_hello` 的反汇编输出。 342 | 343 | ``` 344 | caikelun@debian:~$ arm-linux-androideabi-objdump -D ./libtest.so 345 | ............... 346 | ............... 347 | 00000f60 : 348 | f60: b5b0 push {r4, r5, r7, lr} 349 | f62: af02 add r7, sp, #8 350 | f64: f44f 6080 mov.w r0, #1024 ; 0x400 351 | f68: f7ff ef34 blx dd4 352 | f6c: 4604 mov r4, r0 353 | f6e: b16c cbz r4, f8c 354 | f70: a507 add r5, pc, #28 ; (adr r5, f90 ) 355 | f72: a308 add r3, pc, #32 ; (adr r3, f94 ) 356 | f74: 4620 mov r0, r4 357 | f76: f44f 6180 mov.w r1, #1024 ; 0x400 358 | f7a: 462a mov r2, r5 359 | f7c: f7ff ef30 blx de0 360 | f80: 4628 mov r0, r5 361 | f82: 4621 mov r1, r4 362 | f84: e8bd 40b0 ldmia.w sp!, {r4, r5, r7, lr} 363 | f88: f001 ba96 b.w 24b8 <_Unwind_GetTextRelBase@@Base+0x8> 364 | f8c: bdb0 pop {r4, r5, r7, pc} 365 | f8e: bf00 nop 366 | f90: 7325 strb r5, [r4, #12] 367 | f92: 0000 movs r0, r0 368 | f94: 6568 str r0, [r5, #84] ; 0x54 369 | f96: 6c6c ldr r4, [r5, #68] ; 0x44 370 | f98: 0a6f lsrs r7, r5, #9 371 | f9a: 0000 movs r0, r0 372 | ............... 373 | ............... 374 | ``` 375 | 376 | 对 `malloc` 函数的调用对应于指令 `blx dd4`。跳转到了地址 `dd4`。看看这个地址里有什么吧: 377 | 378 | ``` 379 | caikelun@debian:~$ arm-linux-androideabi-objdump -D ./libtest.so 380 | ............... 381 | ............... 382 | 00000dd4 : 383 | dd4: e28fc600 add ip, pc, #0, 12 384 | dd8: e28cca03 add ip, ip, #12288 ; 0x3000 385 | ddc: e5bcf1b4 ldr pc, [ip, #436]! ; 0x1b4 386 | ............... 387 | ............... 388 | ``` 389 | 390 | 果然,跳转到了 `.plt` 中,经过了几次地址计算,最后跳转到了地址 `3f90` 中的值指向的地址处,`3f90` 是个函数指针。 391 | 392 | 稍微解释一下:因为 arm 处理器使用 3 级流水线,所以第一条指令取到的 `pc` 的值是当前执行的指令地址 + `8`。 393 | 于是:`dd4` + `8` + `3000` + `1b4` = `3f90`。 394 | 395 | 地址 `3f90` 在哪里呢: 396 | 397 | ``` 398 | caikelun@debian:~$ arm-linux-androideabi-objdump -D ./libtest.so 399 | ............... 400 | ............... 401 | 00003f60 <.got>: 402 | ... 403 | 3f70: 00002604 andeq r2, r0, r4, lsl #12 404 | 3f74: 00002504 andeq r2, r0, r4, lsl #10 405 | ... 406 | 3f88: 00000da8 andeq r0, r0, r8, lsr #27 407 | 3f8c: 00000da8 andeq r0, r0, r8, lsr #27 408 | 3f90: 00000da8 andeq r0, r0, r8, lsr #27 409 | ............... 410 | ............... 411 | ``` 412 | 413 | 果然,在 `.got` 里。 414 | 415 | 顺便再看一下 `.rel.plt`: 416 | 417 | ``` 418 | caikelun@debian:~$ arm-linux-androideabi-readelf -r ./libtest.so 419 | 420 | Relocation section '.rel.plt' at offset 0xcb8 contains 30 entries: 421 | Offset Info Type Sym.Value Sym. Name 422 | 00003f88 00000416 R_ARM_JUMP_SLOT 00000000 __cxa_atexit@LIBC 423 | 00003f8c 00000116 R_ARM_JUMP_SLOT 00000000 __cxa_finalize@LIBC 424 | 00003f90 00000316 R_ARM_JUMP_SLOT 00000000 malloc@LIBC 425 | ............... 426 | ............... 427 | ``` 428 | 429 | `malloc` 的地址居然正好存放在 `3f90` 里,这绝对不是巧合啊!还等什么,赶紧改代码吧。我们的 main.c 应该改成这样: 430 | 431 | ```c 432 | #include 433 | 434 | void *my_malloc(size_t size) 435 | { 436 | printf("%zu bytes memory are allocated by libtest.so\n", size); 437 | return malloc(size); 438 | } 439 | 440 | int main() 441 | { 442 | void **p = (void **)0x3f90; 443 | *p = (void *)my_malloc; // do hook 444 | 445 | say_hello(); 446 | return 0; 447 | } 448 | ``` 449 | 450 | 编译运行一下: 451 | 452 | ``` 453 | caikelun@debian:~$ adb push ./main /data/local/tmp 454 | caikelun@debian:~$ adb shell "chmod +x /data/local/tmp/main" 455 | caikelun@debian:~$ adb shell "export LD_LIBRARY_PATH=/data/local/tmp; /data/local/tmp/main" 456 | Segmentation fault 457 | caikelun@debian:~$ 458 | ``` 459 | 460 | 思路是正确的。但之所以还是失败了,是因为这段代码存在下面的 3 个问题: 461 | 462 | 1. `3f90` 是个相对内存地址,需要把它换算成绝对地址。 463 | 2. `3f90` 对应的绝对地址很可能没有写入权限,直接对这个地址赋值会引起段错误。 464 | 3. 新的函数地址即使赋值成功了,`my_malloc` 也不会被执行,因为处理器有指令缓存(instruction cache)。 465 | 466 | 我们需要解决这些问题。 467 | 468 | 469 | ## 内存 470 | 471 | 472 | ### 基地址 473 | 474 | 在进程的内存空间中,各种 ELF 的加载地址是随机的,只有在运行时才能拿到加载地址,也就是**基地址**。我们需要知道 ELF 的基地址,才能将相对地址换算成绝对地址。 475 | 476 | 没有错,熟悉 Linux 开发的聪明的你一定知道,我们可以直接调用 `dl_iterate_phdr`。详细的定义见 [这里](http://man7.org/linux/man-pages/man3/dl_iterate_phdr.3.html)。 477 | 478 | 嗯,先等等,多年的 Android 开发被坑经历告诉我们,还是再看一眼 NDK 里的 `linker.h` 头文件吧: 479 | 480 | ```c 481 | #if defined(__arm__) 482 | 483 | #if __ANDROID_API__ >= 21 484 | int dl_iterate_phdr(int (*__callback)(struct dl_phdr_info*, size_t, void*), void* __data) __INTRODUCED_IN(21); 485 | #endif /* __ANDROID_API__ >= 21 */ 486 | 487 | #else 488 | int dl_iterate_phdr(int (*__callback)(struct dl_phdr_info*, size_t, void*), void* __data); 489 | #endif 490 | ``` 491 | 492 | 为什么?!ARM 架构的 Android 5.0 以下版本居然不支持 `dl_iterate_phdr`!我们的 APP 可是要支持 Android 4.0 以上的所有版本啊。特别是 ARM,怎么能不支持呢?!这还让不让人写代码啦! 493 | 494 | 幸运的是,我们想到了,我们还可以解析 `/proc/self/maps`: 495 | 496 | ``` 497 | root@android:/ # ps | grep main 498 | ps | grep main 499 | shell 7884 7882 2616 1016 hrtimer_na b6e83824 S /data/local/tmp/main 500 | 501 | root@android:/ # cat /proc/7884/maps 502 | cat /proc/7884/maps 503 | 504 | address perms offset dev inode pathname 505 | --------------------------------------------------------------------- 506 | ........... 507 | ........... 508 | b6e42000-b6eb5000 r-xp 00000000 b3:17 57457 /system/lib/libc.so 509 | b6eb5000-b6eb9000 r--p 00072000 b3:17 57457 /system/lib/libc.so 510 | b6eb9000-b6ebc000 rw-p 00076000 b3:17 57457 /system/lib/libc.so 511 | b6ec6000-b6ec9000 r-xp 00000000 b3:19 753708 /data/local/tmp/libtest.so 512 | b6ec9000-b6eca000 r--p 00002000 b3:19 753708 /data/local/tmp/libtest.so 513 | b6eca000-b6ecb000 rw-p 00003000 b3:19 753708 /data/local/tmp/libtest.so 514 | b6f03000-b6f20000 r-xp 00000000 b3:17 32860 /system/bin/linker 515 | b6f20000-b6f21000 r--p 0001c000 b3:17 32860 /system/bin/linker 516 | b6f21000-b6f23000 rw-p 0001d000 b3:17 32860 /system/bin/linker 517 | b6f25000-b6f26000 r-xp 00000000 b3:19 753707 /data/local/tmp/main 518 | b6f26000-b6f27000 r--p 00000000 b3:19 753707 /data/local/tmp/main 519 | becd5000-becf6000 rw-p 00000000 00:00 0 [stack] 520 | ffff0000-ffff1000 r-xp 00000000 00:00 0 [vectors] 521 | ........... 522 | ........... 523 | ``` 524 | 525 | maps 返回的是指定进程的内存空间中 `mmap` 的映射信息,包括各种动态库、可执行文件(如:linker),栈空间,堆空间,甚至还包括字体文件。maps 格式的详细说明见 [这里](http://man7.org/linux/man-pages/man5/proc.5.html)。 526 | 527 | 我们的 libtest.so 在 maps 中有 3 行记录。offset 为 `0` 的第一行的起始地址 `b6ec6000` 在**绝大多数情况下**就是我们寻找的**基地址**。 528 | 529 | 530 | ### 内存访问权限 531 | 532 | maps 返回的信息中已经包含了权限访问信息。如果要执行 hook,就需要写入的权限,可以使用 `mprotect` 来完成: 533 | 534 | ```c 535 | #include 536 | 537 | int mprotect(void *addr, size_t len, int prot); 538 | ``` 539 | 540 | 注意修改内存访问权限时,只能以“页”为单位。`mprotect` 的详细说明见 [这里](http://man7.org/linux/man-pages/man2/mprotect.2.html)。 541 | 542 | 543 | ### 指令缓存 544 | 545 | 注意 `.got` 和 `.data` 的 section 类型是 `PROGBITS`,也就是执行代码。处理器可能会对这部分数据做缓存。修改内存地址后,我们需要清除处理器的指令缓存,让处理器重新从内存中读取这部分指令。方法是调用 `__builtin___clear_cache`: 546 | 547 | ```c 548 | void __builtin___clear_cache (char *begin, char *end); 549 | ``` 550 | 551 | 注意清除指令缓存时,也只能以“页”为单位。`__builtin___clear_cache` 的详细说明见 [这里](https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html)。 552 | 553 | 554 | ## 验证 555 | 556 | 557 | ### 修改 main.c 558 | 559 | 我们把 `main.c` 修改为: 560 | 561 | ```c 562 | #include 563 | #include 564 | #include 565 | #include 566 | #include 567 | #include 568 | 569 | #define PAGE_START(addr) ((addr) & PAGE_MASK) 570 | #define PAGE_END(addr) (PAGE_START(addr) + PAGE_SIZE) 571 | 572 | void *my_malloc(size_t size) 573 | { 574 | printf("%zu bytes memory are allocated by libtest.so\n", size); 575 | return malloc(size); 576 | } 577 | 578 | void hook() 579 | { 580 | char line[512]; 581 | FILE *fp; 582 | uintptr_t base_addr = 0; 583 | uintptr_t addr; 584 | 585 | //find base address of libtest.so 586 | if(NULL == (fp = fopen("/proc/self/maps", "r"))) return; 587 | while(fgets(line, sizeof(line), fp)) 588 | { 589 | if(NULL != strstr(line, "libtest.so") && 590 | sscanf(line, "%"PRIxPTR"-%*lx %*4s 00000000", &base_addr) == 1) 591 | break; 592 | } 593 | fclose(fp); 594 | if(0 == base_addr) return; 595 | 596 | //the absolute address 597 | addr = base_addr + 0x3f90; 598 | 599 | //add write permission 600 | mprotect((void *)PAGE_START(addr), PAGE_SIZE, PROT_READ | PROT_WRITE); 601 | 602 | //replace the function address 603 | *(void **)addr = my_malloc; 604 | 605 | //clear instruction cache 606 | __builtin___clear_cache((void *)PAGE_START(addr), (void *)PAGE_END(addr)); 607 | } 608 | 609 | int main() 610 | { 611 | hook(); 612 | 613 | say_hello(); 614 | return 0; 615 | } 616 | ``` 617 | 618 | 重新编译运行: 619 | 620 | ``` 621 | caikelun@debian:~$ adb push ./main /data/local/tmp 622 | caikelun@debian:~$ adb shell "chmod +x /data/local/tmp/main" 623 | caikelun@debian:~$ adb shell "export LD_LIBRARY_PATH=/data/local/tmp; /data/local/tmp/main" 624 | 1024 bytes memory are allocated by libtest.so 625 | hello 626 | caikelun@debian:~$ 627 | ``` 628 | 629 | 是的,成功了!我们并没有修改 libtest.so 的代码,甚至没有重新编译它。我们仅仅修改了 main 程序。 630 | 631 | libtest.so 和 main 的源码放在 github 上,可以从 [这里](https://github.com/iqiyi/xhook/tree/master/docs/overview/code) 获取到。(根据你使用的编译器不同,或者编译器的版本不同,生成的 libtest.so 中,也许 `malloc` 对应的地址不再是 `0x3f90`,这时你需要先用 readelf 确认,然后再到 `main.c` 中修改。) 632 | 633 | 634 | ### 使用 xhook 635 | 636 | 当然,我们已经开源了一个叫 xhook 的工具库。使用 xhook,你可以更优雅的完成对 libtest.so 的 hook 操作,也不必担心硬编码 `0x3f90` 导致的兼容性问题。 637 | 638 | ```c 639 | #include 640 | #include 641 | #include 642 | #include 643 | 644 | void *my_malloc(size_t size) 645 | { 646 | printf("%zu bytes memory are allocated by libtest.so\n", size); 647 | return malloc(size); 648 | } 649 | 650 | int main() 651 | { 652 | xhook_register(".*/libtest\\.so$", "malloc", my_malloc, NULL); 653 | xhook_refresh(0); 654 | 655 | say_hello(); 656 | return 0; 657 | } 658 | ``` 659 | 660 | xhook 支持 armeabi, armeabi-v7a 和 arm64-v8a。支持 Android 4.0 (含) 以上版本 (API level >= 14)。经过了产品级的稳定性和兼容性验证。可以在 [这里](https://github.com/iqiyi/xhook) 获取 `xhook`。 661 | 662 | 总结一下 xhook 中执行 PLT hook 的流程: 663 | 664 | 1. 读 maps,获取 ELF 的内存首地址(start address)。 665 | 2. 验证 ELF 头信息。 666 | 3. 从 PHT 中找到类型为 `PT_LOAD` 且 offset 为 `0` 的 segment。计算 ELF 基地址。 667 | 4. 从 PHT 中找到类型为 `PT_DYNAMIC` 的 segment,从中获取到 `.dynamic` section,从 `.dynamic` section中获取其他各项 section 对应的内存地址。 668 | 5. 在 `.dynstr` section 中找到需要 hook 的 symbol 对应的 index 值。 669 | 6. 遍历所有的 `.relxxx` section(重定位 section),查找 symbol index 和 symbol type 都匹配的项,对于这项重定位项,执行 hook 操作。hook 流程如下: 670 | * 读 maps,确认当前 hook 地址的内存访问权限。 671 | * 如果权限不是可读也可写,则用 `mprotect` 修改访问权限为可读也可写。 672 | * 如果调用方需要,就保留 hook 地址当前的值,用于返回。 673 | * 将 hook 地址的值替换为新的值。(执行 hook) 674 | * 如果之前用 `mprotect` 修改过内存访问权限,现在还原到之前的权限。 675 | * 清除 hook 地址所在内存页的处理器指令缓存。 676 | 677 | 678 | ## FAQ 679 | 680 | 681 | ### 可以直接从文件中读取 ELF 信息吗? 682 | 683 | 可以。而且对于格式解析来说,读文件是最稳妥的方式,因为 ELF 在运行时,原理上有很多 section 不需要一直保留在内存中,可以在加载完之后就从内存中丢弃,这样可以节省少量的内存。但是从实践的角度出发,各种平台的动态链接器和加载器,都不会这么做,可能它们认为增加的复杂度得不偿失。所以我们从内存中读取各种 ELF 信息就可以了,读文件反而增加了性能损耗。另外,某些系统库 ELF 文件,APP 也不一定有访问权限。 684 | 685 | 686 | ### 计算基地址的精确方法是什么? 687 | 688 | 正如你已经注意到的,前面介绍 libtest.so 基地址获取时,为了简化概念和编码方便,用了“**绝大多数情况下**”这种不应该出现的描述方式。对于 hook 来说,精确的基地址计算流程是: 689 | 690 | 1. 在 maps 中找到找到 offset 为 `0`,且 `pathname` 为目标 ELF 的行。保存该行的 start address 为 `p0`。 691 | 2. 找出 ELF 的 PHT 中第一个类型为 `PT_LOAD` 且 offset 为 `0` 的 segment,保存该 segment 的虚拟内存相对地址(`p_vaddr`)为 `p1`。 692 | 3. `p0` - `p1` 即为该 ELF 当前的基地址。 693 | 694 | 绝大多数的 ELF 第一个 `PT_LOAD` segment 的 `p_vaddr` 都是 `0`。 695 | 696 | 另外,之所以要在 maps 里找 offset 为 `0` 的行,是因为我们在执行 hook 之前,希望对内存中的 ELF 文件头进行校验,确保当前操作的是一个有效的 ELF,而这种 ELF 文件头只能出现在 offset 为 `0` 的 mmap 区域。 697 | 698 | 可以在 Android linker 的源码中搜索“load_bias”,可以找到很多详细的注释说明,也可以参考 linker 中对 `load_bias_` 变量的赋值程序逻辑。 699 | 700 | 701 | ### 目标 ELF 使用的编译选项对 hook 有什么影响? 702 | 703 | 会有一些影响。 704 | 705 | 对于外部函数的调用,可以分为 3 中情况: 706 | 707 | 1. 直接调用。无论编译选项如何,都可以被 hook 到。外部函数地址始终保存在 `.got` 中。 708 | 2. 通过全局函数指针调用。无论编译选项如何,都可以被 hook 到。外部函数地址始终保存在 `.data` 中。 709 | 3. 通过局部函数指针调用。如果编译选项为 -O2(默认值),调用将被优化为直接调用(同情况 1)。如果编译选项为 -O0,则在执行 hook 前已经被赋值到临时变量中的外部函数的指针,通过 PLT 方式无法 hook;对于执行 hook 之后才被赋值的,可以通过 PLT 方式 hook。 710 | 711 | 一般情况下,产品级的 ELF 很少会使用 -O0 进行编译,所以也不必太纠结。但是如果你希望你的 ELF 尽量不被别人 PLT hook,那可以试试使用 -O0 来编译,然后尽量早的将外部函数的指针赋值给局部函数指针变量,之后一直使用这些局部函数指针来访问外部函数。 712 | 713 | 总之,查看 C/C++ 的源代码对这个问题的理解没有意义,需要查看使用不同的编译选项后,生成的 ELF 的反汇编输出,比较它们的区别,才能知道哪些情况由于什么原因导致无法被 PLT hook。 714 | 715 | 716 | ### hook 时遇到偶发的段错误是什么原因?如何处理? 717 | 718 | 我们有时会遇到这样的问题: 719 | 720 | * 读取 `/proc/self/maps` 后发现某个内存区域的访问权限为**可读**,当我们读取该区域的内容做 ELF 文件头校验时,发生了段错误(sig: SIGSEGV, code: SEGV_ACCERR)。 721 | * 已经用 `mprotect()` 修改了某个内存区域的访问权限为**可写**,`mprotect()` 返回修改成功,然后再次读取 `/proc/self/maps` 确认对应内存区域的访问权限确实为**可写**,执行写入操作(替换函数指针,执行 hook)时发生段错误(sig: SIGSEGV, code: SEGV_ACCERR)。 722 | * 读取和验证 ELF 文件头成功了,根据 ELF 头中的相对地址值,进一步读取 PHT 或者 `.dynamic` section 时发生段错误(sig: SIGSEGV, code: SEGV_ACCERR 或 SEGV_MAPERR)。 723 | 724 | 可能的原因是: 725 | 726 | * 进程的内存空间是多线程共享的,我们在执行 hook 时,其他线程(甚至 linker)可能正在执行 `dlclose()`,或者正在用 `mprotect()` 修改这块内存区域的访问权限。 727 | * 不同厂家、机型、版本的 Android ROM 可能有未公开的行为,比如在某些情况下对某些内存区域存在**写保护**或者**读保护**机制,而这些保护机制并不反应在 `/proc/self/maps` 的内容中。 728 | 729 | 问题分析: 730 | 731 | * 读内存时发生段错误其实是无害的。 732 | * 我在 hook 执行的流程中,需要直接通过计算内存地址的方式来写入数据的地方只有一处:即替换函数指针的最关键的那一行。只要其他地方的逻辑没有错误,这里就算写入失败了,也不会对其他内存区域造成破坏。 733 | * 加载运行安卓平台的 APP 进程时,加载器已经向我们注入了 signal handler 的注册逻辑,以便 APP 崩溃时与系统的 `debuggerd` 守护进程通讯,`debuggerd` 使用 `ptrace` 调试崩溃进程,获取需要的崩溃现场信息,记录到 tombstone 文件中,然后 APP 自杀。 734 | * 系统会精确的把段错误信号发送给“发生段错误的线程”。 735 | * 我们希望能有一种隐秘的,且可控的方式来避免段错误引起 APP 崩溃。 736 | 737 | 先明确一个观点:不要只从应用层程序开发的角度来看待段错误,段错误不是洪水猛兽,它只是内核与用户进程的一种正常的交流方式。当用户进程访问了无权限或未 mmap 的虚拟内存地址时,内核向用户进程发送 SIGSEGV 信号,来通知用户进程,仅此而已。只要段错误的发生位置是可控的,我们就可以在用户进程中处理它。 738 | 739 | 解决方案: 740 | 741 | * 当 hook 逻辑进入我们认为的危险区域(直接计算内存地址进行读写)之前,通过一个全局 `flag` 来进行标记,离开危险区域后将 `flag` 复位。 742 | * 注册我们自己的 signal handler,只捕获段错误。在 signal handler 中,通过判断 `flag` 的值,来判断当前线程逻辑是否在危险区域中。如果是,就用 `siglongjmp` 跳出 signal handler,直接跳到我们预先设置好的“危险区域以外的下一行代码处”;如果不是,就恢复之前加载器向我们注入的 signal handler,然后直接返回,这时系统会再次向我们的线程发送段错误信号,由于已经恢复了之前的 signal handler,这时会进入默认的系统 signal handler 中走正常逻辑。 743 | * 我们把这种机制简称为:SFP (segmentation fault protection,段错误保护) 744 | * 注意:SFP需要一个开关,让我们随时能够开启和关闭它。在 APP 开发调试阶段,SFP 应该始终被关闭,这样就不会错过由于编码失误导致的段错误,这些错误是应该被修复的;在正式上线后 SFP 应该被开启,这样能保证 APP 不会崩溃。(当然,以采样的形式部分关闭 SFP,用以观察和分析 hook 机制本身导致的崩溃,也是可以考虑的) 745 | 746 | 具体代码可以参考 `xhook` 中的实现,在源码中搜索 `siglongjmp` 和 `sigsetjmp`。 747 | 748 | 749 | ### ELF 内部函数之间的调用能 hook 吗? 750 | 751 | 752 | 我们这里介绍的 hook 方式为 PLT hook,不能做 ELF 内部函数之间调用的 hook。 753 | 754 | inline hook 可以做到,你需要先知道想要 hook 的内部函数符号名(symbol name)或者地址,然后可以 hook。 755 | 756 | 有很多开源和非开源的 inline hook 实现,比如: 757 | 758 | * substrate:http://www.cydiasubstrate.com/ 759 | * frida:https://www.frida.re/ 760 | 761 | inline hook 方案强大的同时可能带来以下的问题: 762 | 763 | * 由于需要直接解析和修改 ELF 中的机器指令(汇编码),对于不同架构的处理器、处理器指令集、编译器优化选项、操作系统版本可能存在不同的兼容性和稳定性问题。 764 | * 发生问题后可能难以分析和定位,一些知名的 inline hook 方案是闭源的。 765 | * 实现起来相对复杂,难度也较大。 766 | * 未知的坑相对较多,这个可以自行 google。 767 | 768 | 建议如果 PLT hook 够用的话,就不必尝试 inline hook 了。 769 | 770 | 771 | ## 联系作者 772 | 773 | 774 | * caikelun@gmail.com 775 | * https://github.com/caikelun 776 | 777 | 778 | ## 许可证 779 | 780 | 781 | Copyright (c) 2018, 爱奇艺, Inc. All rights reserved. 782 | 783 | 本文使用 [Creative Commons 许可证](https://creativecommons.org/licenses/by/4.0/legalcode) 授权。 784 | -------------------------------------------------------------------------------- /docs/overview/code/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | main/libs/ 4 | main/obj/ 5 | libtest/libs/ 6 | libtest/obj/ 7 | -------------------------------------------------------------------------------- /docs/overview/code/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ndk-build -C ./libtest/jni 4 | ndk-build -C ./main/jni 5 | -------------------------------------------------------------------------------- /docs/overview/code/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ndk-build -C ./main/jni clean 4 | ndk-build -C ./libtest/jni clean 5 | -------------------------------------------------------------------------------- /docs/overview/code/libtest/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_MODULE := test 5 | LOCAL_SRC_FILES := test.c 6 | LOCAL_CFLAGS := -Wall -Wextra -Werror #-O0 7 | LOCAL_CONLYFLAGS := -std=c11 8 | include $(BUILD_SHARED_LIBRARY) 9 | -------------------------------------------------------------------------------- /docs/overview/code/libtest/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi-v7a 2 | APP_PLATFORM := android-14 3 | -------------------------------------------------------------------------------- /docs/overview/code/libtest/jni/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void say_hello() 5 | { 6 | char *buf = malloc(1024); 7 | if(NULL != buf) 8 | { 9 | snprintf(buf, 1024, "%s", "hello\n"); 10 | printf("%s", buf); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /docs/overview/code/libtest/jni/test.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_H 2 | #define TEST_H 1 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | void say_hello(); 9 | 10 | #ifdef __cplusplus 11 | } 12 | #endif 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /docs/overview/code/main/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_MODULE := test 5 | LOCAL_SRC_FILES := $(LOCAL_PATH)/../../libtest/libs/$(TARGET_ARCH_ABI)/libtest.so 6 | LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../libtest/jni 7 | include $(PREBUILT_SHARED_LIBRARY) 8 | 9 | include $(CLEAR_VARS) 10 | LOCAL_MODULE := main 11 | LOCAL_SRC_FILES := main.c 12 | LOCAL_SHARED_LIBRARIES := test 13 | LOCAL_CFLAGS := -Wall -Wextra -Werror -fPIE 14 | LOCAL_CONLYFLAGS := -std=c11 15 | LOCAL_LDLIBS += -fPIE -pie 16 | include $(BUILD_EXECUTABLE) 17 | -------------------------------------------------------------------------------- /docs/overview/code/main/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi-v7a 2 | APP_PLATFORM := android-14 3 | -------------------------------------------------------------------------------- /docs/overview/code/main/jni/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define PAGE_START(addr) ((addr) & PAGE_MASK) 9 | #define PAGE_END(addr) (PAGE_START(addr) + PAGE_SIZE) 10 | 11 | void *my_malloc(size_t size) 12 | { 13 | printf("%zu bytes memory are allocated by libtest.so\n", size); 14 | return malloc(size); 15 | } 16 | 17 | void hook() 18 | { 19 | char line[512]; 20 | FILE *fp; 21 | uintptr_t base_addr = 0; 22 | uintptr_t addr; 23 | 24 | //find base address of libtest.so 25 | if(NULL == (fp = fopen("/proc/self/maps", "r"))) return; 26 | while(fgets(line, sizeof(line), fp)) 27 | { 28 | if(NULL != strstr(line, "libtest.so") && 29 | sscanf(line, "%"PRIxPTR"-%*lx %*4s 00000000", &base_addr) == 1) 30 | break; 31 | } 32 | fclose(fp); 33 | if(0 == base_addr) return; 34 | 35 | //the absolute address 36 | addr = base_addr + 0x3f90; 37 | 38 | //add write permission 39 | mprotect((void *)PAGE_START(addr), PAGE_SIZE, PROT_READ | PROT_WRITE); 40 | 41 | //replace the function address 42 | *(void **)addr = my_malloc; 43 | 44 | //clear instruction cache 45 | __builtin___clear_cache((void *)PAGE_START(addr), (void *)PAGE_END(addr)); 46 | } 47 | 48 | int main() 49 | { 50 | hook(); 51 | 52 | say_hello(); 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /docs/overview/code/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | adb push ./main/libs/armeabi-v7a/libtest.so ./main/libs/armeabi-v7a/main /data/local/tmp 4 | adb shell "chmod +x /data/local/tmp/main" 5 | adb shell "export LD_LIBRARY_PATH=/data/local/tmp; /data/local/tmp/main" 6 | -------------------------------------------------------------------------------- /docs/overview/res/elfheader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iqiyi/xHook/e59285034feadfdd4ba9b65e1eea1d381da83ed3/docs/overview/res/elfheader.png -------------------------------------------------------------------------------- /docs/overview/res/elfpltgot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iqiyi/xHook/e59285034feadfdd4ba9b65e1eea1d381da83ed3/docs/overview/res/elfpltgot.png -------------------------------------------------------------------------------- /docs/overview/res/elfview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iqiyi/xHook/e59285034feadfdd4ba9b65e1eea1d381da83ed3/docs/overview/res/elfview.png -------------------------------------------------------------------------------- /docs/xhooklogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iqiyi/xHook/e59285034feadfdd4ba9b65e1eea1d381da83ed3/docs/xhooklogo.png -------------------------------------------------------------------------------- /docs/xhooklogo.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iqiyi/xHook/e59285034feadfdd4ba9b65e1eea1d381da83ed3/docs/xhooklogo.sketch -------------------------------------------------------------------------------- /install_libs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p ./xhookwrapper/xhook/libs/armeabi 4 | mkdir -p ./xhookwrapper/xhook/libs/armeabi-v7a 5 | mkdir -p ./xhookwrapper/xhook/libs/arm64-v8a 6 | mkdir -p ./xhookwrapper/xhook/libs/x86 7 | mkdir -p ./xhookwrapper/xhook/libs/x86_64 8 | 9 | cp -f ./libxhook/libs/armeabi/libxhook.so ./xhookwrapper/xhook/libs/armeabi/ 10 | cp -f ./libxhook/libs/armeabi-v7a/libxhook.so ./xhookwrapper/xhook/libs/armeabi-v7a/ 11 | cp -f ./libxhook/libs/arm64-v8a/libxhook.so ./xhookwrapper/xhook/libs/arm64-v8a/ 12 | cp -f ./libxhook/libs/x86/libxhook.so ./xhookwrapper/xhook/libs/x86/ 13 | cp -f ./libxhook/libs/x86_64/libxhook.so ./xhookwrapper/xhook/libs/x86_64/ 14 | 15 | mkdir -p ./xhookwrapper/biz/libs/armeabi 16 | mkdir -p ./xhookwrapper/biz/libs/armeabi-v7a 17 | mkdir -p ./xhookwrapper/biz/libs/arm64-v8a 18 | mkdir -p ./xhookwrapper/biz/libs/x86 19 | mkdir -p ./xhookwrapper/biz/libs/x86_64 20 | 21 | cp -f ./libbiz/libs/armeabi/libbiz.so ./xhookwrapper/biz/libs/armeabi/ 22 | cp -f ./libbiz/libs/armeabi-v7a/libbiz.so ./xhookwrapper/biz/libs/armeabi-v7a/ 23 | cp -f ./libbiz/libs/arm64-v8a/libbiz.so ./xhookwrapper/biz/libs/arm64-v8a/ 24 | cp -f ./libbiz/libs/x86/libbiz.so ./xhookwrapper/biz/libs/x86/ 25 | cp -f ./libbiz/libs/x86_64/libbiz.so ./xhookwrapper/biz/libs/x86_64/ 26 | 27 | mkdir -p ./xhookwrapper/app/libs/armeabi 28 | mkdir -p ./xhookwrapper/app/libs/armeabi-v7a 29 | mkdir -p ./xhookwrapper/app/libs/arm64-v8a 30 | mkdir -p ./xhookwrapper/app/libs/x86 31 | mkdir -p ./xhookwrapper/app/libs/x86_64 32 | 33 | cp -f ./libtest/libs/armeabi/libtest.so ./xhookwrapper/app/libs/armeabi/ 34 | cp -f ./libtest/libs/armeabi-v7a/libtest.so ./xhookwrapper/app/libs/armeabi-v7a/ 35 | cp -f ./libtest/libs/arm64-v8a/libtest.so ./xhookwrapper/app/libs/arm64-v8a/ 36 | cp -f ./libtest/libs/x86/libtest.so ./xhookwrapper/app/libs/x86/ 37 | cp -f ./libtest/libs/x86_64/libtest.so ./xhookwrapper/app/libs/x86_64/ 38 | -------------------------------------------------------------------------------- /libbiz/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_MODULE := xhook 5 | LOCAL_SRC_FILES := $(LOCAL_PATH)/../../libxhook/libs/$(TARGET_ARCH_ABI)/libxhook.so 6 | LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../libxhook/jni 7 | include $(PREBUILT_SHARED_LIBRARY) 8 | 9 | include $(CLEAR_VARS) 10 | LOCAL_MODULE := biz 11 | LOCAL_SRC_FILES := biz.c 12 | LOCAL_SHARED_LIBRARIES := xhook 13 | LOCAL_CFLAGS := -Wall -Wextra -Werror 14 | LOCAL_CONLYFLAGS := -std=c11 15 | LOCAL_LDLIBS := -llog 16 | include $(BUILD_SHARED_LIBRARY) 17 | -------------------------------------------------------------------------------- /libbiz/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi armeabi-v7a arm64-v8a x86 x86_64 2 | APP_PLATFORM := android-14 3 | -------------------------------------------------------------------------------- /libbiz/jni/biz.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "xhook.h" 7 | 8 | static int my_system_log_print(int prio, const char* tag, const char* fmt, ...) 9 | { 10 | va_list ap; 11 | char buf[1024]; 12 | int r; 13 | 14 | snprintf(buf, sizeof(buf), "[%s] %s", (NULL == tag ? "" : tag), (NULL == fmt ? "" : fmt)); 15 | 16 | va_start(ap, fmt); 17 | r = __android_log_vprint(prio, "xhook_system", buf, ap); 18 | va_end(ap); 19 | return r; 20 | } 21 | 22 | static int my_libtest_log_print(int prio, const char* tag, const char* fmt, ...) 23 | { 24 | va_list ap; 25 | char buf[1024]; 26 | int r; 27 | 28 | snprintf(buf, sizeof(buf), "[%s] %s", (NULL == tag ? "" : tag), (NULL == fmt ? "" : fmt)); 29 | 30 | va_start(ap, fmt); 31 | r = __android_log_vprint(prio, "xhook_libtest", buf, ap); 32 | va_end(ap); 33 | return r; 34 | } 35 | 36 | void Java_com_qiyi_biz_NativeHandler_start(JNIEnv* env, jobject obj) 37 | { 38 | (void)env; 39 | (void)obj; 40 | 41 | xhook_register("^/system/.*\\.so$", "__android_log_print", my_system_log_print, NULL); 42 | xhook_register("^/vendor/.*\\.so$", "__android_log_print", my_system_log_print, NULL); 43 | xhook_register(".*/libtest\\.so$", "__android_log_print", my_libtest_log_print, NULL); 44 | 45 | //just for testing 46 | xhook_ignore(".*/liblog\\.so$", "__android_log_print"); //ignore __android_log_print in liblog.so 47 | xhook_ignore(".*/libjavacore\\.so$", NULL); //ignore all hooks in libjavacore.so 48 | } 49 | -------------------------------------------------------------------------------- /libtest/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_MODULE := test 5 | LOCAL_SRC_FILES := test.c 6 | LOCAL_CFLAGS := -Wall -Wextra -Werror -O0 7 | LOCAL_CONLYFLAGS := -std=c11 8 | LOCAL_LDLIBS := -llog 9 | include $(BUILD_SHARED_LIBRARY) 10 | -------------------------------------------------------------------------------- /libtest/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi armeabi-v7a arm64-v8a x86 x86_64 2 | APP_PLATFORM := android-14 3 | -------------------------------------------------------------------------------- /libtest/jni/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | typedef int (*my_log_t)(int prio, const char* tag, const char* fmt, ...); 7 | my_log_t my_global_log_ptr = (my_log_t)__android_log_print; 8 | 9 | static void *new_thread_func(void *arg) 10 | { 11 | (void)arg; 12 | my_log_t my_local_log_ptr2 = (my_log_t)__android_log_print; 13 | unsigned int i = 0; 14 | 15 | while(1) 16 | { 17 | my_log_t my_local_log_ptr = (my_log_t)__android_log_print; 18 | __android_log_print(ANDROID_LOG_DEBUG, "mytest", "call directly. %u\n", i); 19 | my_global_log_ptr(ANDROID_LOG_DEBUG, "mytest", "call from global ptr. %u\n", i); 20 | my_local_log_ptr(ANDROID_LOG_DEBUG, "mytest", "call from local ptr. %u\n", i); 21 | my_local_log_ptr2(ANDROID_LOG_DEBUG, "mytest", "call from local ptr2. %u (definitely failed when compiled with -O0)\n", i); 22 | i++; 23 | sleep(1); 24 | } 25 | 26 | return NULL; 27 | } 28 | 29 | void Java_com_qiyi_test_NativeHandler_start(JNIEnv* env, jobject obj) 30 | { 31 | (void)env; 32 | (void)obj; 33 | 34 | pthread_t tid; 35 | pthread_create(&tid, NULL, &new_thread_func, NULL); 36 | } 37 | -------------------------------------------------------------------------------- /libxhook/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_MODULE := xhook 5 | LOCAL_SRC_FILES := xhook.c \ 6 | xh_core.c \ 7 | xh_elf.c \ 8 | xh_jni.c \ 9 | xh_log.c \ 10 | xh_util.c \ 11 | xh_version.c 12 | LOCAL_C_INCLUDES := $(LOCAL_PATH) 13 | LOCAL_CFLAGS := -Wall -Wextra -Werror -fvisibility=hidden 14 | LOCAL_CONLYFLAGS := -std=c11 15 | LOCAL_LDLIBS := -llog 16 | include $(BUILD_SHARED_LIBRARY) 17 | -------------------------------------------------------------------------------- /libxhook/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi armeabi-v7a arm64-v8a x86 x86_64 2 | APP_PLATFORM := android-14 3 | -------------------------------------------------------------------------------- /libxhook/jni/queue.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 1991, 1993 3 | * The Regents of the University of California. All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the University nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * @(#)queue.h 8.5 (Berkeley) 8/20/94 30 | * $FreeBSD: stable/9/sys/sys/queue.h 252365 2013-06-29 04:25:40Z lstewart $ 31 | */ 32 | 33 | #ifndef QUEUE_H 34 | #define QUEUE_H 35 | 36 | /* #include */ 37 | #define __containerof(ptr, type, field) ((type *)((char *)(ptr) - ((char *)&((type *)0)->field))) 38 | 39 | /* 40 | * This file defines four types of data structures: singly-linked lists, 41 | * singly-linked tail queues, lists and tail queues. 42 | * 43 | * A singly-linked list is headed by a single forward pointer. The elements 44 | * are singly linked for minimum space and pointer manipulation overhead at 45 | * the expense of O(n) removal for arbitrary elements. New elements can be 46 | * added to the list after an existing element or at the head of the list. 47 | * Elements being removed from the head of the list should use the explicit 48 | * macro for this purpose for optimum efficiency. A singly-linked list may 49 | * only be traversed in the forward direction. Singly-linked lists are ideal 50 | * for applications with large datasets and few or no removals or for 51 | * implementing a LIFO queue. 52 | * 53 | * A singly-linked tail queue is headed by a pair of pointers, one to the 54 | * head of the list and the other to the tail of the list. The elements are 55 | * singly linked for minimum space and pointer manipulation overhead at the 56 | * expense of O(n) removal for arbitrary elements. New elements can be added 57 | * to the list after an existing element, at the head of the list, or at the 58 | * end of the list. Elements being removed from the head of the tail queue 59 | * should use the explicit macro for this purpose for optimum efficiency. 60 | * A singly-linked tail queue may only be traversed in the forward direction. 61 | * Singly-linked tail queues are ideal for applications with large datasets 62 | * and few or no removals or for implementing a FIFO queue. 63 | * 64 | * A list is headed by a single forward pointer (or an array of forward 65 | * pointers for a hash table header). The elements are doubly linked 66 | * so that an arbitrary element can be removed without a need to 67 | * traverse the list. New elements can be added to the list before 68 | * or after an existing element or at the head of the list. A list 69 | * may be traversed in either direction. 70 | * 71 | * A tail queue is headed by a pair of pointers, one to the head of the 72 | * list and the other to the tail of the list. The elements are doubly 73 | * linked so that an arbitrary element can be removed without a need to 74 | * traverse the list. New elements can be added to the list before or 75 | * after an existing element, at the head of the list, or at the end of 76 | * the list. A tail queue may be traversed in either direction. 77 | * 78 | * For details on the use of these macros, see the queue(3) manual page. 79 | * 80 | * SLIST LIST STAILQ TAILQ 81 | * _HEAD + + + + 82 | * _HEAD_INITIALIZER + + + + 83 | * _ENTRY + + + + 84 | * _INIT + + + + 85 | * _EMPTY + + + + 86 | * _FIRST + + + + 87 | * _NEXT + + + + 88 | * _PREV - + - + 89 | * _LAST - - + + 90 | * _FOREACH + + + + 91 | * _FOREACH_FROM + + + + 92 | * _FOREACH_SAFE + + + + 93 | * _FOREACH_FROM_SAFE + + + + 94 | * _FOREACH_REVERSE - - - + 95 | * _FOREACH_REVERSE_FROM - - - + 96 | * _FOREACH_REVERSE_SAFE - - - + 97 | * _FOREACH_REVERSE_FROM_SAFE - - - + 98 | * _INSERT_HEAD + + + + 99 | * _INSERT_BEFORE - + - + 100 | * _INSERT_AFTER + + + + 101 | * _INSERT_TAIL - - + + 102 | * _CONCAT - - + + 103 | * _REMOVE_AFTER + - + - 104 | * _REMOVE_HEAD + - + - 105 | * _REMOVE + + + + 106 | * _SWAP + + + + 107 | * 108 | */ 109 | 110 | /* 111 | * Singly-linked List declarations. 112 | */ 113 | #define SLIST_HEAD(name, type, qual) \ 114 | struct name { \ 115 | struct type *qual slh_first; /* first element */ \ 116 | } 117 | 118 | #define SLIST_HEAD_INITIALIZER(head) \ 119 | { NULL } 120 | 121 | #define SLIST_ENTRY(type, qual) \ 122 | struct { \ 123 | struct type *qual sle_next; /* next element */ \ 124 | } 125 | 126 | /* 127 | * Singly-linked List functions. 128 | */ 129 | #define SLIST_INIT(head) do { \ 130 | SLIST_FIRST((head)) = NULL; \ 131 | } while (0) 132 | 133 | #define SLIST_EMPTY(head) ((head)->slh_first == NULL) 134 | 135 | #define SLIST_FIRST(head) ((head)->slh_first) 136 | 137 | #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) 138 | 139 | #define SLIST_FOREACH(var, head, field) \ 140 | for ((var) = SLIST_FIRST((head)); \ 141 | (var); \ 142 | (var) = SLIST_NEXT((var), field)) 143 | 144 | #define SLIST_FOREACH_FROM(var, head, field) \ 145 | for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ 146 | (var); \ 147 | (var) = SLIST_NEXT((var), field)) 148 | 149 | #define SLIST_FOREACH_SAFE(var, head, field, tvar) \ 150 | for ((var) = SLIST_FIRST((head)); \ 151 | (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ 152 | (var) = (tvar)) 153 | 154 | #define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ 155 | for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ 156 | (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ 157 | (var) = (tvar)) 158 | 159 | #define SLIST_INSERT_HEAD(head, elm, field) do { \ 160 | SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ 161 | SLIST_FIRST((head)) = (elm); \ 162 | } while (0) 163 | 164 | #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ 165 | SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ 166 | SLIST_NEXT((slistelm), field) = (elm); \ 167 | } while (0) 168 | 169 | #define SLIST_REMOVE_AFTER(elm, field) do { \ 170 | SLIST_NEXT(elm, field) = \ 171 | SLIST_NEXT(SLIST_NEXT(elm, field), field); \ 172 | } while (0) 173 | 174 | #define SLIST_REMOVE_HEAD(head, field) do { \ 175 | SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ 176 | } while (0) 177 | 178 | #define SLIST_REMOVE(head, elm, type, field) do { \ 179 | if (SLIST_FIRST((head)) == (elm)) { \ 180 | SLIST_REMOVE_HEAD((head), field); \ 181 | } \ 182 | else { \ 183 | struct type *curelm = SLIST_FIRST((head)); \ 184 | while (SLIST_NEXT(curelm, field) != (elm)) \ 185 | curelm = SLIST_NEXT(curelm, field); \ 186 | SLIST_REMOVE_AFTER(curelm, field); \ 187 | } \ 188 | } while (0) 189 | 190 | #define SLIST_SWAP(head1, head2, type) do { \ 191 | struct type *swap_first = SLIST_FIRST(head1); \ 192 | SLIST_FIRST(head1) = SLIST_FIRST(head2); \ 193 | SLIST_FIRST(head2) = swap_first; \ 194 | } while (0) 195 | 196 | /* 197 | * List declarations. 198 | */ 199 | #define LIST_HEAD(name, type, qual) \ 200 | struct name { \ 201 | struct type *qual lh_first; /* first element */ \ 202 | } 203 | 204 | #define LIST_HEAD_INITIALIZER(head) \ 205 | { NULL } 206 | 207 | #define LIST_ENTRY(type, qual) \ 208 | struct { \ 209 | struct type *qual le_next; /* next element */ \ 210 | struct type *qual *le_prev; /* address of previous next element */ \ 211 | } 212 | 213 | /* 214 | * List functions. 215 | */ 216 | #define LIST_INIT(head) do { \ 217 | LIST_FIRST((head)) = NULL; \ 218 | } while (0) 219 | 220 | #define LIST_EMPTY(head) ((head)->lh_first == NULL) 221 | 222 | #define LIST_FIRST(head) ((head)->lh_first) 223 | 224 | #define LIST_NEXT(elm, field) ((elm)->field.le_next) 225 | 226 | #define LIST_PREV(elm, head, type, field) \ 227 | ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \ 228 | __containerof((elm)->field.le_prev, struct type, field.le_next)) 229 | 230 | #define LIST_FOREACH(var, head, field) \ 231 | for ((var) = LIST_FIRST((head)); \ 232 | (var); \ 233 | (var) = LIST_NEXT((var), field)) 234 | 235 | #define LIST_FOREACH_FROM(var, head, field) \ 236 | for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ 237 | (var); \ 238 | (var) = LIST_NEXT((var), field)) 239 | 240 | #define LIST_FOREACH_SAFE(var, head, field, tvar) \ 241 | for ((var) = LIST_FIRST((head)); \ 242 | (var) && ((tvar) = LIST_NEXT((var), field), 1); \ 243 | (var) = (tvar)) 244 | 245 | #define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ 246 | for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ 247 | (var) && ((tvar) = LIST_NEXT((var), field), 1); \ 248 | (var) = (tvar)) 249 | 250 | #define LIST_INSERT_HEAD(head, elm, field) do { \ 251 | if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ 252 | LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field); \ 253 | LIST_FIRST((head)) = (elm); \ 254 | (elm)->field.le_prev = &LIST_FIRST((head)); \ 255 | } while (0) 256 | 257 | #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ 258 | (elm)->field.le_prev = (listelm)->field.le_prev; \ 259 | LIST_NEXT((elm), field) = (listelm); \ 260 | *(listelm)->field.le_prev = (elm); \ 261 | (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ 262 | } while (0) 263 | 264 | #define LIST_INSERT_AFTER(listelm, elm, field) do { \ 265 | if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL) \ 266 | LIST_NEXT((listelm), field)->field.le_prev = \ 267 | &LIST_NEXT((elm), field); \ 268 | LIST_NEXT((listelm), field) = (elm); \ 269 | (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ 270 | } while (0) 271 | 272 | #define LIST_REMOVE(elm, field) do { \ 273 | if (LIST_NEXT((elm), field) != NULL) \ 274 | LIST_NEXT((elm), field)->field.le_prev = \ 275 | (elm)->field.le_prev; \ 276 | *(elm)->field.le_prev = LIST_NEXT((elm), field); \ 277 | } while (0) 278 | 279 | #define LIST_SWAP(head1, head2, type, field) do { \ 280 | struct type *swap_tmp = LIST_FIRST((head1)); \ 281 | LIST_FIRST((head1)) = LIST_FIRST((head2)); \ 282 | LIST_FIRST((head2)) = swap_tmp; \ 283 | if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ 284 | swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ 285 | if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ 286 | swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ 287 | } while (0) 288 | 289 | /* 290 | * Singly-linked Tail queue declarations. 291 | */ 292 | #define STAILQ_HEAD(name, type, qual) \ 293 | struct name { \ 294 | struct type *qual stqh_first;/* first element */ \ 295 | struct type *qual *stqh_last;/* addr of last next element */ \ 296 | } 297 | 298 | #define STAILQ_HEAD_INITIALIZER(head) \ 299 | { NULL, &(head).stqh_first } 300 | 301 | #define STAILQ_ENTRY(type, qual) \ 302 | struct { \ 303 | struct type *qual stqe_next; /* next element */ \ 304 | } 305 | 306 | /* 307 | * Singly-linked Tail queue functions. 308 | */ 309 | #define STAILQ_INIT(head) do { \ 310 | STAILQ_FIRST((head)) = NULL; \ 311 | (head)->stqh_last = &STAILQ_FIRST((head)); \ 312 | } while (0) 313 | 314 | #define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) 315 | 316 | #define STAILQ_FIRST(head) ((head)->stqh_first) 317 | 318 | #define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) 319 | 320 | #define STAILQ_LAST(head, type, field) \ 321 | (STAILQ_EMPTY((head)) ? NULL : \ 322 | __containerof((head)->stqh_last, struct type, field.stqe_next)) 323 | 324 | #define STAILQ_FOREACH(var, head, field) \ 325 | for((var) = STAILQ_FIRST((head)); \ 326 | (var); \ 327 | (var) = STAILQ_NEXT((var), field)) 328 | 329 | #define STAILQ_FOREACH_FROM(var, head, field) \ 330 | for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ 331 | (var); \ 332 | (var) = STAILQ_NEXT((var), field)) 333 | 334 | #define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ 335 | for ((var) = STAILQ_FIRST((head)); \ 336 | (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ 337 | (var) = (tvar)) 338 | 339 | #define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ 340 | for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ 341 | (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ 342 | (var) = (tvar)) 343 | 344 | #define STAILQ_INSERT_HEAD(head, elm, field) do { \ 345 | if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ 346 | (head)->stqh_last = &STAILQ_NEXT((elm), field); \ 347 | STAILQ_FIRST((head)) = (elm); \ 348 | } while (0) 349 | 350 | #define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ 351 | if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL) \ 352 | (head)->stqh_last = &STAILQ_NEXT((elm), field); \ 353 | STAILQ_NEXT((tqelm), field) = (elm); \ 354 | } while (0) 355 | 356 | #define STAILQ_INSERT_TAIL(head, elm, field) do { \ 357 | STAILQ_NEXT((elm), field) = NULL; \ 358 | *(head)->stqh_last = (elm); \ 359 | (head)->stqh_last = &STAILQ_NEXT((elm), field); \ 360 | } while (0) 361 | 362 | #define STAILQ_CONCAT(head1, head2) do { \ 363 | if (!STAILQ_EMPTY((head2))) { \ 364 | *(head1)->stqh_last = (head2)->stqh_first; \ 365 | (head1)->stqh_last = (head2)->stqh_last; \ 366 | STAILQ_INIT((head2)); \ 367 | } \ 368 | } while (0) 369 | 370 | #define STAILQ_REMOVE_AFTER(head, elm, field) do { \ 371 | if ((STAILQ_NEXT(elm, field) = \ 372 | STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ 373 | (head)->stqh_last = &STAILQ_NEXT((elm), field); \ 374 | } while (0) 375 | 376 | #define STAILQ_REMOVE_HEAD(head, field) do { \ 377 | if ((STAILQ_FIRST((head)) = \ 378 | STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ 379 | (head)->stqh_last = &STAILQ_FIRST((head)); \ 380 | } while (0) 381 | 382 | #define STAILQ_REMOVE(head, elm, type, field) do { \ 383 | if (STAILQ_FIRST((head)) == (elm)) { \ 384 | STAILQ_REMOVE_HEAD((head), field); \ 385 | } \ 386 | else { \ 387 | struct type *curelm = STAILQ_FIRST((head)); \ 388 | while (STAILQ_NEXT(curelm, field) != (elm)) \ 389 | curelm = STAILQ_NEXT(curelm, field); \ 390 | STAILQ_REMOVE_AFTER(head, curelm, field); \ 391 | } \ 392 | } while (0) 393 | 394 | #define STAILQ_SWAP(head1, head2, type) do { \ 395 | struct type *swap_first = STAILQ_FIRST(head1); \ 396 | struct type **swap_last = (head1)->stqh_last; \ 397 | STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ 398 | (head1)->stqh_last = (head2)->stqh_last; \ 399 | STAILQ_FIRST(head2) = swap_first; \ 400 | (head2)->stqh_last = swap_last; \ 401 | if (STAILQ_EMPTY(head1)) \ 402 | (head1)->stqh_last = &STAILQ_FIRST(head1); \ 403 | if (STAILQ_EMPTY(head2)) \ 404 | (head2)->stqh_last = &STAILQ_FIRST(head2); \ 405 | } while (0) 406 | 407 | /* 408 | * Tail queue declarations. 409 | */ 410 | #define TAILQ_HEAD(name, type, qual) \ 411 | struct name { \ 412 | struct type *qual tqh_first; /* first element */ \ 413 | struct type *qual *tqh_last; /* addr of last next element */ \ 414 | } 415 | 416 | #define TAILQ_HEAD_INITIALIZER(head) \ 417 | { NULL, &(head).tqh_first } 418 | 419 | #define TAILQ_ENTRY(type, qual) \ 420 | struct { \ 421 | struct type *qual tqe_next; /* next element */ \ 422 | struct type *qual *tqe_prev; /* address of previous next element */ \ 423 | } 424 | 425 | /* 426 | * Tail queue functions. 427 | */ 428 | #define TAILQ_INIT(head) do { \ 429 | TAILQ_FIRST((head)) = NULL; \ 430 | (head)->tqh_last = &TAILQ_FIRST((head)); \ 431 | } while (0) 432 | 433 | #define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) 434 | 435 | #define TAILQ_FIRST(head) ((head)->tqh_first) 436 | 437 | #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) 438 | 439 | #define TAILQ_PREV(elm, headname, field) \ 440 | (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) 441 | 442 | #define TAILQ_LAST(head, headname) \ 443 | (*(((struct headname *)((head)->tqh_last))->tqh_last)) 444 | 445 | #define TAILQ_FOREACH(var, head, field) \ 446 | for ((var) = TAILQ_FIRST((head)); \ 447 | (var); \ 448 | (var) = TAILQ_NEXT((var), field)) 449 | 450 | #define TAILQ_FOREACH_FROM(var, head, field) \ 451 | for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ 452 | (var); \ 453 | (var) = TAILQ_NEXT((var), field)) 454 | 455 | #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ 456 | for ((var) = TAILQ_FIRST((head)); \ 457 | (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ 458 | (var) = (tvar)) 459 | 460 | #define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ 461 | for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ 462 | (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ 463 | (var) = (tvar)) 464 | 465 | #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ 466 | for ((var) = TAILQ_LAST((head), headname); \ 467 | (var); \ 468 | (var) = TAILQ_PREV((var), headname, field)) 469 | 470 | #define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \ 471 | for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ 472 | (var); \ 473 | (var) = TAILQ_PREV((var), headname, field)) 474 | 475 | #define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ 476 | for ((var) = TAILQ_LAST((head), headname); \ 477 | (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ 478 | (var) = (tvar)) 479 | 480 | #define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \ 481 | for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ 482 | (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ 483 | (var) = (tvar)) 484 | 485 | #define TAILQ_INSERT_HEAD(head, elm, field) do { \ 486 | if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ 487 | TAILQ_FIRST((head))->field.tqe_prev = \ 488 | &TAILQ_NEXT((elm), field); \ 489 | else \ 490 | (head)->tqh_last = &TAILQ_NEXT((elm), field); \ 491 | TAILQ_FIRST((head)) = (elm); \ 492 | (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ 493 | } while (0) 494 | 495 | #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ 496 | (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ 497 | TAILQ_NEXT((elm), field) = (listelm); \ 498 | *(listelm)->field.tqe_prev = (elm); \ 499 | (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ 500 | } while (0) 501 | 502 | #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ 503 | if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL) \ 504 | TAILQ_NEXT((elm), field)->field.tqe_prev = \ 505 | &TAILQ_NEXT((elm), field); \ 506 | else \ 507 | (head)->tqh_last = &TAILQ_NEXT((elm), field); \ 508 | TAILQ_NEXT((listelm), field) = (elm); \ 509 | (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ 510 | } while (0) 511 | 512 | #define TAILQ_INSERT_TAIL(head, elm, field) do { \ 513 | TAILQ_NEXT((elm), field) = NULL; \ 514 | (elm)->field.tqe_prev = (head)->tqh_last; \ 515 | *(head)->tqh_last = (elm); \ 516 | (head)->tqh_last = &TAILQ_NEXT((elm), field); \ 517 | } while (0) 518 | 519 | #define TAILQ_CONCAT(head1, head2, field) do { \ 520 | if (!TAILQ_EMPTY(head2)) { \ 521 | *(head1)->tqh_last = (head2)->tqh_first; \ 522 | (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ 523 | (head1)->tqh_last = (head2)->tqh_last; \ 524 | TAILQ_INIT((head2)); \ 525 | } \ 526 | } while (0) 527 | 528 | #define TAILQ_REMOVE(head, elm, field) do { \ 529 | if ((TAILQ_NEXT((elm), field)) != NULL) \ 530 | TAILQ_NEXT((elm), field)->field.tqe_prev = \ 531 | (elm)->field.tqe_prev; \ 532 | else \ 533 | (head)->tqh_last = (elm)->field.tqe_prev; \ 534 | *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ 535 | } while (0) 536 | 537 | #define TAILQ_SWAP(head1, head2, type, field) do { \ 538 | struct type *swap_first = (head1)->tqh_first; \ 539 | struct type **swap_last = (head1)->tqh_last; \ 540 | (head1)->tqh_first = (head2)->tqh_first; \ 541 | (head1)->tqh_last = (head2)->tqh_last; \ 542 | (head2)->tqh_first = swap_first; \ 543 | (head2)->tqh_last = swap_last; \ 544 | if ((swap_first = (head1)->tqh_first) != NULL) \ 545 | swap_first->field.tqe_prev = &(head1)->tqh_first; \ 546 | else \ 547 | (head1)->tqh_last = &(head1)->tqh_first; \ 548 | if ((swap_first = (head2)->tqh_first) != NULL) \ 549 | swap_first->field.tqe_prev = &(head2)->tqh_first; \ 550 | else \ 551 | (head2)->tqh_last = &(head2)->tqh_first; \ 552 | } while (0) 553 | 554 | #endif 555 | -------------------------------------------------------------------------------- /libxhook/jni/xh_core.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2018-04-11. 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include "queue.h" 35 | #include "tree.h" 36 | #include "xh_errno.h" 37 | #include "xh_log.h" 38 | #include "xh_elf.h" 39 | #include "xh_version.h" 40 | #include "xh_core.h" 41 | 42 | #define XH_CORE_DEBUG 0 43 | 44 | //registered hook info collection 45 | typedef struct xh_core_hook_info 46 | { 47 | #if XH_CORE_DEBUG 48 | char *pathname_regex_str; 49 | #endif 50 | regex_t pathname_regex; 51 | char *symbol; 52 | void *new_func; 53 | void **old_func; 54 | TAILQ_ENTRY(xh_core_hook_info,) link; 55 | } xh_core_hook_info_t; 56 | typedef TAILQ_HEAD(xh_core_hook_info_queue, xh_core_hook_info,) xh_core_hook_info_queue_t; 57 | 58 | //ignored hook info collection 59 | typedef struct xh_core_ignore_info 60 | { 61 | #if XH_CORE_DEBUG 62 | char *pathname_regex_str; 63 | #endif 64 | regex_t pathname_regex; 65 | char *symbol; //NULL meaning for all symbols 66 | TAILQ_ENTRY(xh_core_ignore_info,) link; 67 | } xh_core_ignore_info_t; 68 | typedef TAILQ_HEAD(xh_core_ignore_info_queue, xh_core_ignore_info,) xh_core_ignore_info_queue_t; 69 | 70 | //required info from /proc/self/maps 71 | typedef struct xh_core_map_info 72 | { 73 | char *pathname; 74 | uintptr_t base_addr; 75 | xh_elf_t elf; 76 | RB_ENTRY(xh_core_map_info) link; 77 | } xh_core_map_info_t; 78 | static __inline__ int xh_core_map_info_cmp(xh_core_map_info_t *a, xh_core_map_info_t *b) 79 | { 80 | return strcmp(a->pathname, b->pathname); 81 | } 82 | typedef RB_HEAD(xh_core_map_info_tree, xh_core_map_info) xh_core_map_info_tree_t; 83 | RB_GENERATE_STATIC(xh_core_map_info_tree, xh_core_map_info, link, xh_core_map_info_cmp) 84 | 85 | //signal handler for SIGSEGV 86 | //for xh_elf_init(), xh_elf_hook(), xh_elf_check_elfheader() 87 | static int xh_core_sigsegv_enable = 1; //enable by default 88 | static struct sigaction xh_core_sigsegv_act_old; 89 | static volatile int xh_core_sigsegv_flag = 0; 90 | static sigjmp_buf xh_core_sigsegv_env; 91 | static void xh_core_sigsegv_handler(int sig) 92 | { 93 | (void)sig; 94 | 95 | if(xh_core_sigsegv_flag) 96 | siglongjmp(xh_core_sigsegv_env, 1); 97 | else 98 | sigaction(SIGSEGV, &xh_core_sigsegv_act_old, NULL); 99 | } 100 | static int xh_core_add_sigsegv_handler() 101 | { 102 | struct sigaction act; 103 | 104 | if(!xh_core_sigsegv_enable) return 0; 105 | 106 | if(0 != sigemptyset(&act.sa_mask)) return (0 == errno ? XH_ERRNO_UNKNOWN : errno); 107 | act.sa_handler = xh_core_sigsegv_handler; 108 | 109 | if(0 != sigaction(SIGSEGV, &act, &xh_core_sigsegv_act_old)) 110 | return (0 == errno ? XH_ERRNO_UNKNOWN : errno); 111 | 112 | return 0; 113 | } 114 | static void xh_core_del_sigsegv_handler() 115 | { 116 | if(!xh_core_sigsegv_enable) return; 117 | 118 | sigaction(SIGSEGV, &xh_core_sigsegv_act_old, NULL); 119 | } 120 | 121 | 122 | static xh_core_hook_info_queue_t xh_core_hook_info = TAILQ_HEAD_INITIALIZER(xh_core_hook_info); 123 | static xh_core_ignore_info_queue_t xh_core_ignore_info = TAILQ_HEAD_INITIALIZER(xh_core_ignore_info); 124 | static xh_core_map_info_tree_t xh_core_map_info = RB_INITIALIZER(&xh_core_map_info); 125 | static pthread_mutex_t xh_core_mutex = PTHREAD_MUTEX_INITIALIZER; 126 | static pthread_cond_t xh_core_cond = PTHREAD_COND_INITIALIZER; 127 | static volatile int xh_core_inited = 0; 128 | static volatile int xh_core_init_ok = 0; 129 | static volatile int xh_core_async_inited = 0; 130 | static volatile int xh_core_async_init_ok = 0; 131 | static pthread_mutex_t xh_core_refresh_mutex = PTHREAD_MUTEX_INITIALIZER; 132 | static pthread_t xh_core_refresh_thread_tid; 133 | static volatile int xh_core_refresh_thread_running = 0; 134 | static volatile int xh_core_refresh_thread_do = 0; 135 | 136 | 137 | int xh_core_register(const char *pathname_regex_str, const char *symbol, 138 | void *new_func, void **old_func) 139 | { 140 | xh_core_hook_info_t *hi; 141 | regex_t regex; 142 | 143 | if(NULL == pathname_regex_str || NULL == symbol || NULL == new_func) return XH_ERRNO_INVAL; 144 | 145 | if(xh_core_inited) 146 | { 147 | XH_LOG_ERROR("do not register hook after refresh(): %s, %s", pathname_regex_str, symbol); 148 | return XH_ERRNO_INVAL; 149 | } 150 | 151 | if(0 != regcomp(®ex, pathname_regex_str, REG_NOSUB)) return XH_ERRNO_INVAL; 152 | 153 | if(NULL == (hi = malloc(sizeof(xh_core_hook_info_t)))) return XH_ERRNO_NOMEM; 154 | if(NULL == (hi->symbol = strdup(symbol))) 155 | { 156 | free(hi); 157 | return XH_ERRNO_NOMEM; 158 | } 159 | #if XH_CORE_DEBUG 160 | if(NULL == (hi->pathname_regex_str = strdup(pathname_regex_str))) 161 | { 162 | free(hi->symbol); 163 | free(hi); 164 | return XH_ERRNO_NOMEM; 165 | } 166 | #endif 167 | hi->pathname_regex = regex; 168 | hi->new_func = new_func; 169 | hi->old_func = old_func; 170 | 171 | pthread_mutex_lock(&xh_core_mutex); 172 | TAILQ_INSERT_TAIL(&xh_core_hook_info, hi, link); 173 | pthread_mutex_unlock(&xh_core_mutex); 174 | 175 | return 0; 176 | } 177 | 178 | int xh_core_ignore(const char *pathname_regex_str, const char *symbol) 179 | { 180 | xh_core_ignore_info_t *ii; 181 | regex_t regex; 182 | 183 | if(NULL == pathname_regex_str) return XH_ERRNO_INVAL; 184 | 185 | if(xh_core_inited) 186 | { 187 | XH_LOG_ERROR("do not ignore hook after refresh(): %s, %s", pathname_regex_str, symbol ? symbol : "ALL"); 188 | return XH_ERRNO_INVAL; 189 | } 190 | 191 | if(0 != regcomp(®ex, pathname_regex_str, REG_NOSUB)) return XH_ERRNO_INVAL; 192 | 193 | if(NULL == (ii = malloc(sizeof(xh_core_ignore_info_t)))) return XH_ERRNO_NOMEM; 194 | if(NULL != symbol) 195 | { 196 | if(NULL == (ii->symbol = strdup(symbol))) 197 | { 198 | free(ii); 199 | return XH_ERRNO_NOMEM; 200 | } 201 | } 202 | else 203 | { 204 | ii->symbol = NULL; //ignore all symbols 205 | } 206 | #if XH_CORE_DEBUG 207 | if(NULL == (ii->pathname_regex_str = strdup(pathname_regex_str))) 208 | { 209 | free(ii->symbol); 210 | free(ii); 211 | return XH_ERRNO_NOMEM; 212 | } 213 | #endif 214 | ii->pathname_regex = regex; 215 | 216 | pthread_mutex_lock(&xh_core_mutex); 217 | TAILQ_INSERT_TAIL(&xh_core_ignore_info, ii, link); 218 | pthread_mutex_unlock(&xh_core_mutex); 219 | 220 | return 0; 221 | } 222 | 223 | static int xh_core_check_elf_header(uintptr_t base_addr, const char *pathname) 224 | { 225 | if(!xh_core_sigsegv_enable) 226 | { 227 | return xh_elf_check_elfheader(base_addr); 228 | } 229 | else 230 | { 231 | int ret = XH_ERRNO_UNKNOWN; 232 | 233 | xh_core_sigsegv_flag = 1; 234 | if(0 == sigsetjmp(xh_core_sigsegv_env, 1)) 235 | { 236 | ret = xh_elf_check_elfheader(base_addr); 237 | } 238 | else 239 | { 240 | ret = XH_ERRNO_SEGVERR; 241 | XH_LOG_WARN("catch SIGSEGV when check_elfheader: %s", pathname); 242 | } 243 | xh_core_sigsegv_flag = 0; 244 | return ret; 245 | } 246 | } 247 | 248 | static void xh_core_hook_impl(xh_core_map_info_t *mi) 249 | { 250 | //init 251 | if(0 != xh_elf_init(&(mi->elf), mi->base_addr, mi->pathname)) return; 252 | 253 | //hook 254 | xh_core_hook_info_t *hi; 255 | xh_core_ignore_info_t *ii; 256 | int ignore; 257 | TAILQ_FOREACH(hi, &xh_core_hook_info, link) //find hook info 258 | { 259 | if(0 == regexec(&(hi->pathname_regex), mi->pathname, 0, NULL, 0)) 260 | { 261 | ignore = 0; 262 | TAILQ_FOREACH(ii, &xh_core_ignore_info, link) //find ignore info 263 | { 264 | if(0 == regexec(&(ii->pathname_regex), mi->pathname, 0, NULL, 0)) 265 | { 266 | if(NULL == ii->symbol) //ignore all symbols 267 | return; 268 | 269 | if(0 == strcmp(ii->symbol, hi->symbol)) //ignore the current symbol 270 | { 271 | ignore = 1; 272 | break; 273 | } 274 | } 275 | } 276 | 277 | if(0 == ignore) 278 | xh_elf_hook(&(mi->elf), hi->symbol, hi->new_func, hi->old_func); 279 | } 280 | } 281 | } 282 | 283 | static void xh_core_hook(xh_core_map_info_t *mi) 284 | { 285 | if(!xh_core_sigsegv_enable) 286 | { 287 | xh_core_hook_impl(mi); 288 | } 289 | else 290 | { 291 | xh_core_sigsegv_flag = 1; 292 | if(0 == sigsetjmp(xh_core_sigsegv_env, 1)) 293 | { 294 | xh_core_hook_impl(mi); 295 | } 296 | else 297 | { 298 | XH_LOG_WARN("catch SIGSEGV when init or hook: %s", mi->pathname); 299 | } 300 | xh_core_sigsegv_flag = 0; 301 | } 302 | } 303 | 304 | static void xh_core_refresh_impl() 305 | { 306 | char line[512]; 307 | FILE *fp; 308 | uintptr_t base_addr; 309 | uintptr_t prev_base_addr = 0; 310 | char perm[5]; 311 | char prev_perm[5] = "---p"; 312 | unsigned long offset; 313 | unsigned long prev_offset = 0; 314 | int pathname_pos; 315 | char *pathname; 316 | char prev_pathname[512] = {0}; 317 | size_t pathname_len; 318 | xh_core_map_info_t *mi, *mi_tmp; 319 | xh_core_map_info_t mi_key; 320 | xh_core_hook_info_t *hi; 321 | xh_core_ignore_info_t *ii; 322 | int match; 323 | xh_core_map_info_tree_t map_info_refreshed = RB_INITIALIZER(&map_info_refreshed); 324 | 325 | if(NULL == (fp = fopen("/proc/self/maps", "r"))) 326 | { 327 | XH_LOG_ERROR("fopen /proc/self/maps failed"); 328 | return; 329 | } 330 | 331 | while(fgets(line, sizeof(line), fp)) 332 | { 333 | if(sscanf(line, "%"PRIxPTR"-%*lx %4s %lx %*x:%*x %*d%n", &base_addr, perm, &offset, &pathname_pos) != 3) continue; 334 | 335 | // do not touch the shared memory 336 | if (perm[3] != 'p') continue; 337 | 338 | // Ignore permission PROT_NONE maps 339 | if (perm[0] == '-' && perm[1] == '-' && perm[2] == '-') 340 | continue; 341 | 342 | //get pathname 343 | while(isspace(line[pathname_pos]) && pathname_pos < (int)(sizeof(line) - 1)) 344 | pathname_pos += 1; 345 | if(pathname_pos >= (int)(sizeof(line) - 1)) continue; 346 | pathname = line + pathname_pos; 347 | pathname_len = strlen(pathname); 348 | if(0 == pathname_len) continue; 349 | if(pathname[pathname_len - 1] == '\n') 350 | { 351 | pathname[pathname_len - 1] = '\0'; 352 | pathname_len -= 1; 353 | } 354 | if(0 == pathname_len) continue; 355 | if('[' == pathname[0]) continue; 356 | 357 | // Find non-executable map, we need record it. Because so maps can begin with 358 | // an non-executable map. 359 | if (perm[2] != 'x') { 360 | prev_offset = offset; 361 | prev_base_addr = base_addr; 362 | memcpy(prev_perm, perm, sizeof(prev_perm)); 363 | strcpy(prev_pathname, pathname); 364 | continue; 365 | } 366 | 367 | // Find executable map if offset == 0, it OK, 368 | // or we need check previous map for base address. 369 | if (offset != 0) { 370 | if (strcmp(prev_pathname, pathname) || prev_offset != 0 || prev_perm[0] != 'r') { 371 | continue; 372 | } 373 | // The previous map is real begin map 374 | base_addr = prev_base_addr; 375 | } 376 | 377 | //check pathname 378 | //if we need to hook this elf? 379 | match = 0; 380 | TAILQ_FOREACH(hi, &xh_core_hook_info, link) //find hook info 381 | { 382 | if(0 == regexec(&(hi->pathname_regex), pathname, 0, NULL, 0)) 383 | { 384 | TAILQ_FOREACH(ii, &xh_core_ignore_info, link) //find ignore info 385 | { 386 | if(0 == regexec(&(ii->pathname_regex), pathname, 0, NULL, 0)) 387 | { 388 | if(NULL == ii->symbol) 389 | goto check_finished; 390 | 391 | if(0 == strcmp(ii->symbol, hi->symbol)) 392 | goto check_continue; 393 | } 394 | } 395 | 396 | match = 1; 397 | check_continue: 398 | break; 399 | } 400 | } 401 | check_finished: 402 | if(0 == match) continue; 403 | 404 | //check elf header format 405 | //We are trying to do ELF header checking as late as possible. 406 | if(0 != xh_core_check_elf_header(base_addr, pathname)) continue; 407 | 408 | //check existed map item 409 | mi_key.pathname = pathname; 410 | if(NULL != (mi = RB_FIND(xh_core_map_info_tree, &xh_core_map_info, &mi_key))) 411 | { 412 | //exist 413 | RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi); 414 | 415 | //repeated? 416 | //We only keep the first one, that is the real base address 417 | if(NULL != RB_INSERT(xh_core_map_info_tree, &map_info_refreshed, mi)) 418 | { 419 | #if XH_CORE_DEBUG 420 | XH_LOG_DEBUG("repeated map info when update: %s", line); 421 | #endif 422 | free(mi->pathname); 423 | free(mi); 424 | continue; 425 | } 426 | 427 | //re-hook if base_addr changed 428 | if(mi->base_addr != base_addr) 429 | { 430 | mi->base_addr = base_addr; 431 | xh_core_hook(mi); 432 | } 433 | } 434 | else 435 | { 436 | //not exist, create a new map info 437 | if(NULL == (mi = (xh_core_map_info_t *)malloc(sizeof(xh_core_map_info_t)))) continue; 438 | if(NULL == (mi->pathname = strdup(pathname))) 439 | { 440 | free(mi); 441 | continue; 442 | } 443 | mi->base_addr = base_addr; 444 | 445 | //repeated? 446 | //We only keep the first one, that is the real base address 447 | if(NULL != RB_INSERT(xh_core_map_info_tree, &map_info_refreshed, mi)) 448 | { 449 | #if XH_CORE_DEBUG 450 | XH_LOG_DEBUG("repeated map info when create: %s", line); 451 | #endif 452 | free(mi->pathname); 453 | free(mi); 454 | continue; 455 | } 456 | 457 | //hook 458 | xh_core_hook(mi); //hook 459 | } 460 | } 461 | fclose(fp); 462 | 463 | //free all missing map item, maybe dlclosed? 464 | RB_FOREACH_SAFE(mi, xh_core_map_info_tree, &xh_core_map_info, mi_tmp) 465 | { 466 | #if XH_CORE_DEBUG 467 | XH_LOG_DEBUG("remove missing map info: %s", mi->pathname); 468 | #endif 469 | RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi); 470 | if(mi->pathname) free(mi->pathname); 471 | free(mi); 472 | } 473 | 474 | //save the new refreshed map info tree 475 | xh_core_map_info = map_info_refreshed; 476 | 477 | XH_LOG_INFO("map refreshed"); 478 | 479 | #if XH_CORE_DEBUG 480 | RB_FOREACH(mi, xh_core_map_info_tree, &xh_core_map_info) 481 | XH_LOG_DEBUG(" %"PRIxPTR" %s\n", mi->base_addr, mi->pathname); 482 | #endif 483 | } 484 | 485 | static void *xh_core_refresh_thread_func(void *arg) 486 | { 487 | (void)arg; 488 | 489 | pthread_setname_np(pthread_self(), "xh_refresh_loop"); 490 | 491 | while(xh_core_refresh_thread_running) 492 | { 493 | //waiting for a refresh task or exit 494 | pthread_mutex_lock(&xh_core_mutex); 495 | while(!xh_core_refresh_thread_do && xh_core_refresh_thread_running) 496 | { 497 | pthread_cond_wait(&xh_core_cond, &xh_core_mutex); 498 | } 499 | if(!xh_core_refresh_thread_running) 500 | { 501 | pthread_mutex_unlock(&xh_core_mutex); 502 | break; 503 | } 504 | xh_core_refresh_thread_do = 0; 505 | pthread_mutex_unlock(&xh_core_mutex); 506 | 507 | //refresh 508 | pthread_mutex_lock(&xh_core_refresh_mutex); 509 | xh_core_refresh_impl(); 510 | pthread_mutex_unlock(&xh_core_refresh_mutex); 511 | } 512 | 513 | return NULL; 514 | } 515 | 516 | static void xh_core_init_once() 517 | { 518 | if(xh_core_inited) return; 519 | 520 | pthread_mutex_lock(&xh_core_mutex); 521 | 522 | if(xh_core_inited) goto end; 523 | 524 | xh_core_inited = 1; 525 | 526 | //dump debug info 527 | XH_LOG_INFO("%s\n", xh_version_str_full()); 528 | #if XH_CORE_DEBUG 529 | xh_core_hook_info_t *hi; 530 | TAILQ_FOREACH(hi, &xh_core_hook_info, link) 531 | XH_LOG_INFO(" hook: %s @ %s, (%p, %p)\n", hi->symbol, hi->pathname_regex_str, 532 | hi->new_func, hi->old_func); 533 | xh_core_ignore_info_t *ii; 534 | TAILQ_FOREACH(ii, &xh_core_ignore_info, link) 535 | XH_LOG_INFO(" ignore: %s @ %s\n", ii->symbol ? ii->symbol : "ALL ", 536 | ii->pathname_regex_str); 537 | #endif 538 | 539 | //register signal handler 540 | if(0 != xh_core_add_sigsegv_handler()) goto end; 541 | 542 | //OK 543 | xh_core_init_ok = 1; 544 | 545 | end: 546 | pthread_mutex_unlock(&xh_core_mutex); 547 | } 548 | 549 | static void xh_core_init_async_once() 550 | { 551 | if(xh_core_async_inited) return; 552 | 553 | pthread_mutex_lock(&xh_core_mutex); 554 | 555 | if(xh_core_async_inited) goto end; 556 | 557 | xh_core_async_inited = 1; 558 | 559 | //create async refresh thread 560 | xh_core_refresh_thread_running = 1; 561 | if(0 != pthread_create(&xh_core_refresh_thread_tid, NULL, &xh_core_refresh_thread_func, NULL)) 562 | { 563 | xh_core_refresh_thread_running = 0; 564 | goto end; 565 | } 566 | 567 | //OK 568 | xh_core_async_init_ok = 1; 569 | 570 | end: 571 | pthread_mutex_unlock(&xh_core_mutex); 572 | } 573 | 574 | int xh_core_refresh(int async) 575 | { 576 | //init 577 | xh_core_init_once(); 578 | if(!xh_core_init_ok) return XH_ERRNO_UNKNOWN; 579 | 580 | if(async) 581 | { 582 | //init for async 583 | xh_core_init_async_once(); 584 | if(!xh_core_async_init_ok) return XH_ERRNO_UNKNOWN; 585 | 586 | //refresh async 587 | pthread_mutex_lock(&xh_core_mutex); 588 | xh_core_refresh_thread_do = 1; 589 | pthread_cond_signal(&xh_core_cond); 590 | pthread_mutex_unlock(&xh_core_mutex); 591 | } 592 | else 593 | { 594 | //refresh sync 595 | pthread_mutex_lock(&xh_core_refresh_mutex); 596 | xh_core_refresh_impl(); 597 | pthread_mutex_unlock(&xh_core_refresh_mutex); 598 | } 599 | 600 | return 0; 601 | } 602 | 603 | void xh_core_clear() 604 | { 605 | //stop the async refresh thread 606 | if(xh_core_async_init_ok) 607 | { 608 | pthread_mutex_lock(&xh_core_mutex); 609 | xh_core_refresh_thread_running = 0; 610 | pthread_cond_signal(&xh_core_cond); 611 | pthread_mutex_unlock(&xh_core_mutex); 612 | 613 | pthread_join(xh_core_refresh_thread_tid, NULL); 614 | xh_core_async_init_ok = 0; 615 | } 616 | xh_core_async_inited = 0; 617 | 618 | //unregister the sig handler 619 | if(xh_core_init_ok) 620 | { 621 | xh_core_del_sigsegv_handler(); 622 | xh_core_init_ok = 0; 623 | } 624 | xh_core_inited = 0; 625 | 626 | pthread_mutex_lock(&xh_core_mutex); 627 | pthread_mutex_lock(&xh_core_refresh_mutex); 628 | 629 | //free all map info 630 | xh_core_map_info_t *mi, *mi_tmp; 631 | RB_FOREACH_SAFE(mi, xh_core_map_info_tree, &xh_core_map_info, mi_tmp) 632 | { 633 | RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi); 634 | if(mi->pathname) free(mi->pathname); 635 | free(mi); 636 | } 637 | 638 | //free all hook info 639 | xh_core_hook_info_t *hi, *hi_tmp; 640 | TAILQ_FOREACH_SAFE(hi, &xh_core_hook_info, link, hi_tmp) 641 | { 642 | TAILQ_REMOVE(&xh_core_hook_info, hi, link); 643 | #if XH_CORE_DEBUG 644 | free(hi->pathname_regex_str); 645 | #endif 646 | regfree(&(hi->pathname_regex)); 647 | free(hi->symbol); 648 | free(hi); 649 | } 650 | 651 | //free all ignore info 652 | xh_core_ignore_info_t *ii, *ii_tmp; 653 | TAILQ_FOREACH_SAFE(ii, &xh_core_ignore_info, link, ii_tmp) 654 | { 655 | TAILQ_REMOVE(&xh_core_ignore_info, ii, link); 656 | #if XH_CORE_DEBUG 657 | free(ii->pathname_regex_str); 658 | #endif 659 | regfree(&(ii->pathname_regex)); 660 | free(ii->symbol); 661 | free(ii); 662 | } 663 | 664 | pthread_mutex_unlock(&xh_core_refresh_mutex); 665 | pthread_mutex_unlock(&xh_core_mutex); 666 | } 667 | 668 | void xh_core_enable_debug(int flag) 669 | { 670 | xh_log_priority = (flag ? ANDROID_LOG_DEBUG : ANDROID_LOG_WARN); 671 | } 672 | 673 | void xh_core_enable_sigsegv_protection(int flag) 674 | { 675 | xh_core_sigsegv_enable = (flag ? 1 : 0); 676 | } 677 | -------------------------------------------------------------------------------- /libxhook/jni/xh_core.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2018-04-11. 23 | 24 | #ifndef XH_CORE_H 25 | #define XH_CORE_H 1 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | int xh_core_register(const char *pathname_regex_str, const char *symbol, 32 | void *new_func, void **old_func); 33 | 34 | int xh_core_ignore(const char *pathname_regex_str, const char *symbol); 35 | 36 | int xh_core_refresh(int async); 37 | 38 | void xh_core_clear(); 39 | 40 | void xh_core_enable_debug(int flag); 41 | 42 | void xh_core_enable_sigsegv_protection(int flag); 43 | 44 | #ifdef __cplusplus 45 | } 46 | #endif 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /libxhook/jni/xh_elf.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2018-04-11. 23 | 24 | #ifndef XH_ELF_H 25 | #define XH_ELF_H 1 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | typedef struct 36 | { 37 | const char *pathname; 38 | 39 | ElfW(Addr) base_addr; 40 | ElfW(Addr) bias_addr; 41 | 42 | ElfW(Ehdr) *ehdr; 43 | ElfW(Phdr) *phdr; 44 | 45 | ElfW(Dyn) *dyn; //.dynamic 46 | ElfW(Word) dyn_sz; 47 | 48 | const char *strtab; //.dynstr (string-table) 49 | ElfW(Sym) *symtab; //.dynsym (symbol-index to string-table's offset) 50 | 51 | ElfW(Addr) relplt; //.rel.plt or .rela.plt 52 | ElfW(Word) relplt_sz; 53 | 54 | ElfW(Addr) reldyn; //.rel.dyn or .rela.dyn 55 | ElfW(Word) reldyn_sz; 56 | 57 | ElfW(Addr) relandroid; //android compressed rel or rela 58 | ElfW(Word) relandroid_sz; 59 | 60 | //for ELF hash 61 | uint32_t *bucket; 62 | uint32_t bucket_cnt; 63 | uint32_t *chain; 64 | uint32_t chain_cnt; //invalid for GNU hash 65 | 66 | //append for GNU hash 67 | uint32_t symoffset; 68 | ElfW(Addr) *bloom; 69 | uint32_t bloom_sz; 70 | uint32_t bloom_shift; 71 | 72 | int is_use_rela; 73 | int is_use_gnu_hash; 74 | } xh_elf_t; 75 | 76 | int xh_elf_init(xh_elf_t *self, uintptr_t base_addr, const char *pathname); 77 | int xh_elf_hook(xh_elf_t *self, const char *symbol, void *new_func, void **old_func); 78 | 79 | int xh_elf_check_elfheader(uintptr_t base_addr); 80 | 81 | #ifdef __cplusplus 82 | } 83 | #endif 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /libxhook/jni/xh_errno.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2018-04-11. 23 | 24 | #ifndef XH_ERRNO_H 25 | #define XH_ERRNO_H 1 26 | 27 | #define XH_ERRNO_UNKNOWN 1001 28 | #define XH_ERRNO_INVAL 1002 29 | #define XH_ERRNO_NOMEM 1003 30 | #define XH_ERRNO_REPEAT 1004 31 | #define XH_ERRNO_NOTFND 1005 32 | #define XH_ERRNO_BADMAPS 1006 33 | #define XH_ERRNO_FORMAT 1007 34 | #define XH_ERRNO_ELFINIT 1008 35 | #define XH_ERRNO_SEGVERR 1009 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /libxhook/jni/xh_jni.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2018-04-11. 23 | 24 | #include 25 | #include "xhook.h" 26 | 27 | #define JNI_API_DEF(f) Java_com_qiyi_xhook_NativeHandler_##f 28 | 29 | JNIEXPORT jint JNI_API_DEF(refresh)(JNIEnv *env, jobject obj, jboolean async) 30 | { 31 | (void)env; 32 | (void)obj; 33 | 34 | return xhook_refresh(async ? 1 : 0); 35 | } 36 | 37 | JNIEXPORT void JNI_API_DEF(clear)(JNIEnv *env, jobject obj) 38 | { 39 | (void)env; 40 | (void)obj; 41 | 42 | xhook_clear(); 43 | } 44 | 45 | JNIEXPORT void JNI_API_DEF(enableDebug)(JNIEnv *env, jobject obj, jboolean flag) 46 | { 47 | (void)env; 48 | (void)obj; 49 | 50 | xhook_enable_debug(flag ? 1 : 0); 51 | } 52 | 53 | JNIEXPORT void JNI_API_DEF(enableSigSegvProtection)(JNIEnv *env, jobject obj, jboolean flag) 54 | { 55 | (void)env; 56 | (void)obj; 57 | 58 | xhook_enable_sigsegv_protection(flag ? 1 : 0); 59 | } 60 | -------------------------------------------------------------------------------- /libxhook/jni/xh_log.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2018-04-11. 23 | 24 | #include 25 | #include "xh_log.h" 26 | 27 | android_LogPriority xh_log_priority = ANDROID_LOG_WARN; 28 | -------------------------------------------------------------------------------- /libxhook/jni/xh_log.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2018-04-11. 23 | 24 | #ifndef XH_LOG_H 25 | #define XH_LOG_H 1 26 | 27 | #include 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | extern android_LogPriority xh_log_priority; 34 | 35 | #define XH_LOG_TAG "xhook" 36 | #define XH_LOG_DEBUG(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_DEBUG) __android_log_print(ANDROID_LOG_DEBUG, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0) 37 | #define XH_LOG_INFO(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_INFO) __android_log_print(ANDROID_LOG_INFO, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0) 38 | #define XH_LOG_WARN(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_WARN) __android_log_print(ANDROID_LOG_WARN, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0) 39 | #define XH_LOG_ERROR(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_ERROR) __android_log_print(ANDROID_LOG_ERROR, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0) 40 | 41 | #ifdef __cplusplus 42 | } 43 | #endif 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /libxhook/jni/xh_util.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2018-04-11. 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include "xh_util.h" 37 | #include "xh_errno.h" 38 | #include "xh_log.h" 39 | 40 | #define PAGE_START(addr) ((addr) & PAGE_MASK) 41 | #define PAGE_END(addr) (PAGE_START(addr + sizeof(uintptr_t) - 1) + PAGE_SIZE) 42 | #define PAGE_COVER(addr) (PAGE_END(addr) - PAGE_START(addr)) 43 | 44 | int xh_util_get_mem_protect(uintptr_t addr, size_t len, const char *pathname, unsigned int *prot) 45 | { 46 | uintptr_t start_addr = addr; 47 | uintptr_t end_addr = addr + len; 48 | FILE *fp; 49 | char line[512]; 50 | uintptr_t start, end; 51 | char perm[5]; 52 | int load0 = 1; 53 | int found_all = 0; 54 | 55 | *prot = 0; 56 | 57 | if(NULL == (fp = fopen("/proc/self/maps", "r"))) return XH_ERRNO_BADMAPS; 58 | 59 | while(fgets(line, sizeof(line), fp)) 60 | { 61 | if(NULL != pathname) 62 | if(NULL == strstr(line, pathname)) continue; 63 | 64 | if(sscanf(line, "%"PRIxPTR"-%"PRIxPTR" %4s ", &start, &end, perm) != 3) continue; 65 | 66 | if(perm[3] != 'p') continue; 67 | 68 | if(start_addr >= start && start_addr < end) 69 | { 70 | if(load0) 71 | { 72 | //first load segment 73 | if(perm[0] == 'r') *prot |= PROT_READ; 74 | if(perm[1] == 'w') *prot |= PROT_WRITE; 75 | if(perm[2] == 'x') *prot |= PROT_EXEC; 76 | load0 = 0; 77 | } 78 | else 79 | { 80 | //others 81 | if(perm[0] != 'r') *prot &= ~PROT_READ; 82 | if(perm[1] != 'w') *prot &= ~PROT_WRITE; 83 | if(perm[2] != 'x') *prot &= ~PROT_EXEC; 84 | } 85 | 86 | if(end_addr <= end) 87 | { 88 | found_all = 1; 89 | break; //finished 90 | } 91 | else 92 | { 93 | start_addr = end; //try to find the next load segment 94 | } 95 | } 96 | } 97 | 98 | fclose(fp); 99 | 100 | if(!found_all) return XH_ERRNO_SEGVERR; 101 | 102 | return 0; 103 | } 104 | 105 | int xh_util_get_addr_protect(uintptr_t addr, const char *pathname, unsigned int *prot) 106 | { 107 | return xh_util_get_mem_protect(addr, sizeof(addr), pathname, prot); 108 | } 109 | 110 | int xh_util_set_addr_protect(uintptr_t addr, unsigned int prot) 111 | { 112 | if(0 != mprotect((void *)PAGE_START(addr), PAGE_COVER(addr), (int)prot)) 113 | return 0 == errno ? XH_ERRNO_UNKNOWN : errno; 114 | 115 | return 0; 116 | } 117 | 118 | void xh_util_flush_instruction_cache(uintptr_t addr) 119 | { 120 | __builtin___clear_cache((void *)PAGE_START(addr), (void *)PAGE_END(addr)); 121 | } 122 | -------------------------------------------------------------------------------- /libxhook/jni/xh_util.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2018-04-11. 23 | 24 | #ifndef XH_UTILS_H 25 | #define XH_UTILS_H 1 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | #if defined(__LP64__) 32 | #define XH_UTIL_FMT_LEN "16" 33 | #define XH_UTIL_FMT_X "llx" 34 | #else 35 | #define XH_UTIL_FMT_LEN "8" 36 | #define XH_UTIL_FMT_X "x" 37 | #endif 38 | 39 | #define XH_UTIL_FMT_FIXED_X XH_UTIL_FMT_LEN XH_UTIL_FMT_X 40 | #define XH_UTIL_FMT_FIXED_S XH_UTIL_FMT_LEN "s" 41 | 42 | int xh_util_get_mem_protect(uintptr_t addr, size_t len, const char *pathname, unsigned int *prot); 43 | int xh_util_get_addr_protect(uintptr_t addr, const char *pathname, unsigned int *prot); 44 | int xh_util_set_addr_protect(uintptr_t addr, unsigned int prot); 45 | void xh_util_flush_instruction_cache(uintptr_t addr); 46 | 47 | #ifdef __cplusplus 48 | } 49 | #endif 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /libxhook/jni/xh_version.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2018-04-11. 23 | 24 | #include "xh_version.h" 25 | 26 | #define XH_VERSION_MAJOR 1 27 | #define XH_VERSION_MINOR 2 28 | #define XH_VERSION_EXTRA 0 29 | 30 | #define XH_VERSION ((XH_VERSION_MAJOR << 16) | (XH_VERSION_MINOR << 8) | (XH_VERSION_EXTRA)) 31 | 32 | #define XH_VERSION_TO_STR_HELPER(x) #x 33 | #define XH_VERSION_TO_STR(x) XH_VERSION_TO_STR_HELPER(x) 34 | 35 | #define XH_VERSION_STR XH_VERSION_TO_STR(XH_VERSION_MAJOR) "." \ 36 | XH_VERSION_TO_STR(XH_VERSION_MINOR) "." \ 37 | XH_VERSION_TO_STR(XH_VERSION_EXTRA) 38 | 39 | #if defined(__arm__) 40 | #define XH_VERSION_ARCH "arm" 41 | #elif defined(__aarch64__) 42 | #define XH_VERSION_ARCH "aarch64" 43 | #elif defined(__i386__) 44 | #define XH_VERSION_ARCH "x86" 45 | #elif defined(__x86_64__) 46 | #define XH_VERSION_ARCH "x86_64" 47 | #else 48 | #define XH_VERSION_ARCH "unknown" 49 | #endif 50 | 51 | #define XH_VERSION_STR_FULL "libxhook "XH_VERSION_STR" ("XH_VERSION_ARCH")" 52 | 53 | unsigned int xh_version() 54 | { 55 | return XH_VERSION; 56 | } 57 | 58 | const char *xh_version_str() 59 | { 60 | return XH_VERSION_STR; 61 | } 62 | 63 | const char *xh_version_str_full() 64 | { 65 | return XH_VERSION_STR_FULL; 66 | } 67 | -------------------------------------------------------------------------------- /libxhook/jni/xh_version.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2018-04-11. 23 | 24 | #ifndef XH_VERSION_H 25 | #define XH_VERSION_H 1 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | unsigned int xh_version(); 32 | 33 | const char *xh_version_str(); 34 | 35 | const char *xh_version_str_full(); 36 | 37 | #ifdef __cplusplus 38 | } 39 | #endif 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /libxhook/jni/xhook.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2018-04-11. 23 | 24 | #include "xh_core.h" 25 | #include "xhook.h" 26 | 27 | int xhook_register(const char *pathname_regex_str, const char *symbol, 28 | void *new_func, void **old_func) 29 | { 30 | return xh_core_register(pathname_regex_str, symbol, new_func, old_func); 31 | } 32 | 33 | int xhook_ignore(const char *pathname_regex_str, const char *symbol) 34 | { 35 | return xh_core_ignore(pathname_regex_str, symbol); 36 | } 37 | 38 | int xhook_refresh(int async) 39 | { 40 | return xh_core_refresh(async); 41 | } 42 | 43 | void xhook_clear() 44 | { 45 | return xh_core_clear(); 46 | } 47 | 48 | void xhook_enable_debug(int flag) 49 | { 50 | return xh_core_enable_debug(flag); 51 | } 52 | 53 | void xhook_enable_sigsegv_protection(int flag) 54 | { 55 | return xh_core_enable_sigsegv_protection(flag); 56 | } 57 | -------------------------------------------------------------------------------- /libxhook/jni/xhook.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2018-04-11. 23 | 24 | #ifndef XHOOK_H 25 | #define XHOOK_H 1 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | #define XHOOK_EXPORT __attribute__((visibility("default"))) 32 | 33 | int xhook_register(const char *pathname_regex_str, const char *symbol, 34 | void *new_func, void **old_func) XHOOK_EXPORT; 35 | 36 | int xhook_ignore(const char *pathname_regex_str, const char *symbol) XHOOK_EXPORT; 37 | 38 | int xhook_refresh(int async) XHOOK_EXPORT; 39 | 40 | void xhook_clear() XHOOK_EXPORT; 41 | 42 | void xhook_enable_debug(int flag) XHOOK_EXPORT; 43 | 44 | void xhook_enable_sigsegv_protection(int flag) XHOOK_EXPORT; 45 | 46 | #ifdef __cplusplus 47 | } 48 | #endif 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /xhookwrapper/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /xhookwrapper/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /xhookwrapper/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | defaultConfig { 6 | applicationId "com.qiyi.xhookwrapper" 7 | minSdkVersion 14 8 | targetSdkVersion 29 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | sourceSets { 20 | main { 21 | jniLibs.srcDirs = ['libs'] 22 | } 23 | } 24 | } 25 | 26 | dependencies { 27 | implementation fileTree(include: ['*.jar'], dir: 'libs') 28 | implementation project(':xhook') 29 | implementation project(':biz') 30 | implementation 'androidx.appcompat:appcompat:1.1.0' 31 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 32 | } 33 | -------------------------------------------------------------------------------- /xhookwrapper/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/java/com/qiyi/test/NativeHandler.java: -------------------------------------------------------------------------------- 1 | package com.qiyi.test; 2 | 3 | /** 4 | * Created by caikelun on 18/01/2018. 5 | */ 6 | 7 | public class NativeHandler { 8 | private static final NativeHandler ourInstance = new NativeHandler(); 9 | 10 | public static NativeHandler getInstance() { 11 | return ourInstance; 12 | } 13 | 14 | private NativeHandler() { 15 | } 16 | 17 | public native void start(); 18 | } 19 | -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/java/com/qiyi/test/Test.java: -------------------------------------------------------------------------------- 1 | package com.qiyi.test; 2 | 3 | /** 4 | * Created by caikelun on 18/01/2018. 5 | */ 6 | 7 | public class Test { 8 | private static final Test ourInstance = new Test(); 9 | 10 | public static Test getInstance() { 11 | return ourInstance; 12 | } 13 | 14 | private Test() { 15 | } 16 | 17 | public synchronized void init() { 18 | System.loadLibrary("test"); 19 | } 20 | 21 | public synchronized void start() { 22 | com.qiyi.test.NativeHandler.getInstance().start(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/java/com/qiyi/xhookwrapper/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.qiyi.xhookwrapper; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.util.Log; 6 | 7 | public class MainActivity extends AppCompatActivity { 8 | 9 | @Override 10 | protected void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | setContentView(R.layout.activity_main); 13 | 14 | //load xhook 15 | com.qiyi.xhook.XHook.getInstance().init(this.getApplicationContext()); 16 | if(!com.qiyi.xhook.XHook.getInstance().isInited()) { 17 | return; 18 | } 19 | //com.qiyi.xhook.XHook.getInstance().enableDebug(true); //default is false 20 | //com.qiyi.xhook.XHook.getInstance().enableSigSegvProtection(false); //default is true 21 | 22 | //load and run your biz lib (for register hook points) 23 | com.qiyi.biz.Biz.getInstance().init(); 24 | com.qiyi.biz.Biz.getInstance().start(); 25 | 26 | //xhook do refresh 27 | com.qiyi.xhook.XHook.getInstance().refresh(false); 28 | 29 | //load and run the target lib 30 | com.qiyi.test.Test.getInstance().init(); 31 | com.qiyi.test.Test.getInstance().start(); 32 | try { 33 | Thread.sleep(200); 34 | } catch (InterruptedException e) { 35 | e.printStackTrace(); 36 | } 37 | 38 | //xhook do refresh again 39 | com.qiyi.xhook.XHook.getInstance().refresh(false); 40 | 41 | //xhook do refresh again for some reason, 42 | //maybe called after some System.loadLibrary() and System.load() 43 | //* 44 | new Thread(new Runnable() { 45 | @Override 46 | public void run() { 47 | while(true) 48 | { 49 | com.qiyi.xhook.XHook.getInstance().refresh(true); 50 | 51 | try { 52 | Thread.sleep(5000); 53 | } catch (InterruptedException e) { 54 | e.printStackTrace(); 55 | } 56 | } 57 | } 58 | }).start(); 59 | //*/ 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iqiyi/xHook/e59285034feadfdd4ba9b65e1eea1d381da83ed3/xhookwrapper/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iqiyi/xHook/e59285034feadfdd4ba9b65e1eea1d381da83ed3/xhookwrapper/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iqiyi/xHook/e59285034feadfdd4ba9b65e1eea1d381da83ed3/xhookwrapper/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iqiyi/xHook/e59285034feadfdd4ba9b65e1eea1d381da83ed3/xhookwrapper/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iqiyi/xHook/e59285034feadfdd4ba9b65e1eea1d381da83ed3/xhookwrapper/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iqiyi/xHook/e59285034feadfdd4ba9b65e1eea1d381da83ed3/xhookwrapper/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iqiyi/xHook/e59285034feadfdd4ba9b65e1eea1d381da83ed3/xhookwrapper/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iqiyi/xHook/e59285034feadfdd4ba9b65e1eea1d381da83ed3/xhookwrapper/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iqiyi/xHook/e59285034feadfdd4ba9b65e1eea1d381da83ed3/xhookwrapper/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iqiyi/xHook/e59285034feadfdd4ba9b65e1eea1d381da83ed3/xhookwrapper/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | xhookwrapper 3 | 4 | -------------------------------------------------------------------------------- /xhookwrapper/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /xhookwrapper/biz/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /xhookwrapper/biz/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 29 5 | defaultConfig { 6 | minSdkVersion 14 7 | targetSdkVersion 29 8 | versionCode 1 9 | versionName "1.0" 10 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 11 | } 12 | buildTypes { 13 | release { 14 | minifyEnabled false 15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 16 | } 17 | } 18 | sourceSets { 19 | main { 20 | jniLibs.srcDirs = ['libs'] 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(dir: 'libs', include: ['*.jar']) 27 | 28 | implementation 'androidx.appcompat:appcompat:1.1.0' 29 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 30 | } 31 | -------------------------------------------------------------------------------- /xhookwrapper/biz/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /xhookwrapper/biz/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /xhookwrapper/biz/src/main/java/com/qiyi/biz/Biz.java: -------------------------------------------------------------------------------- 1 | package com.qiyi.biz; 2 | 3 | /** 4 | * Created by caikelun on 18/01/2018. 5 | */ 6 | 7 | public class Biz { 8 | private static final Biz ourInstance = new Biz(); 9 | 10 | public static Biz getInstance() { 11 | return ourInstance; 12 | } 13 | 14 | private Biz() { 15 | } 16 | 17 | public synchronized void init() { 18 | System.loadLibrary("biz"); 19 | } 20 | 21 | public synchronized void start() { 22 | com.qiyi.biz.NativeHandler.getInstance().start(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /xhookwrapper/biz/src/main/java/com/qiyi/biz/NativeHandler.java: -------------------------------------------------------------------------------- 1 | package com.qiyi.biz; 2 | 3 | /** 4 | * Created by caikelun on 18/01/2018. 5 | */ 6 | 7 | public class NativeHandler { 8 | private static final NativeHandler ourInstance = new NativeHandler(); 9 | 10 | public static NativeHandler getInstance() { 11 | return ourInstance; 12 | } 13 | 14 | private NativeHandler() { 15 | } 16 | 17 | public native void start(); 18 | } 19 | -------------------------------------------------------------------------------- /xhookwrapper/biz/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | biz 3 | 4 | -------------------------------------------------------------------------------- /xhookwrapper/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.3.2' 11 | 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | jcenter() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /xhookwrapper/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /xhookwrapper/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iqiyi/xHook/e59285034feadfdd4ba9b65e1eea1d381da83ed3/xhookwrapper/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /xhookwrapper/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Mar 26 19:19:54 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip 7 | -------------------------------------------------------------------------------- /xhookwrapper/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /xhookwrapper/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /xhookwrapper/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':xhook', ':biz' 2 | -------------------------------------------------------------------------------- /xhookwrapper/xhook/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /xhookwrapper/xhook/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'maven' 3 | 4 | def version = "1.2.0" 5 | 6 | android { 7 | compileSdkVersion 29 8 | buildToolsVersion '29.0.2' 9 | defaultConfig { 10 | minSdkVersion 14 11 | targetSdkVersion 29 12 | } 13 | compileOptions { 14 | sourceCompatibility JavaVersion.VERSION_1_6 15 | targetCompatibility JavaVersion.VERSION_1_6 16 | } 17 | buildTypes { 18 | debug { 19 | minifyEnabled false 20 | useProguard false 21 | } 22 | release { 23 | minifyEnabled false 24 | useProguard false 25 | } 26 | } 27 | sourceSets { 28 | main { 29 | jniLibs.srcDirs = ['libs'] 30 | } 31 | } 32 | uploadArchives { 33 | repositories { 34 | mavenDeployer { 35 | repository(url: "") { 36 | authentication(userName: "", password: "") 37 | } 38 | pom.version = "$version" 39 | pom.artifactId = "xhook" 40 | pom.groupId = "com.qiyi.xhook" 41 | } 42 | } 43 | } 44 | } 45 | 46 | dependencies { 47 | implementation fileTree(dir: 'libs', include: ['*.jar']) 48 | } 49 | -------------------------------------------------------------------------------- /xhookwrapper/xhook/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /xhookwrapper/xhook/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /xhookwrapper/xhook/src/main/java/com/qiyi/xhook/NativeHandler.java: -------------------------------------------------------------------------------- 1 | package com.qiyi.xhook; 2 | 3 | /** 4 | * Created by caikelun on 18/01/2018. 5 | */ 6 | 7 | public class NativeHandler { 8 | private static final NativeHandler ourInstance = new NativeHandler(); 9 | 10 | public static NativeHandler getInstance() { 11 | return ourInstance; 12 | } 13 | 14 | private NativeHandler() { 15 | } 16 | 17 | public native int refresh(boolean async); 18 | 19 | public native void clear(); 20 | 21 | public native void enableDebug(boolean flag); 22 | 23 | public native void enableSigSegvProtection(boolean flag); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /xhookwrapper/xhook/src/main/java/com/qiyi/xhook/XHook.java: -------------------------------------------------------------------------------- 1 | package com.qiyi.xhook; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | /** 7 | * Created by caikelun on 18/01/2018. 8 | */ 9 | 10 | public class XHook { 11 | private static final XHook ourInstance = new XHook(); 12 | private static boolean inited = false; 13 | 14 | public static XHook getInstance() { 15 | return ourInstance; 16 | } 17 | 18 | private XHook() { 19 | } 20 | 21 | /** 22 | * Check if xhook has inited. 23 | * @return true if xhook has inited, false otherwise. 24 | */ 25 | public synchronized boolean isInited() { 26 | return inited; 27 | } 28 | 29 | /** 30 | * Init xhook. 31 | * @param ctx The application context. 32 | * @return true if successful, false otherwise. 33 | */ 34 | public synchronized boolean init(Context ctx) { 35 | if(inited) { 36 | return true; 37 | } 38 | 39 | try { 40 | System.loadLibrary("xhook"); 41 | inited = true; 42 | } catch (Throwable e) { 43 | try { 44 | System.load(ctx.getFilesDir().getParent() + "/lib/libxhook.so"); 45 | inited = true; 46 | } catch (Throwable ex) { 47 | ex.printStackTrace(); 48 | Log.e("xhook", "load libxhook.so failed"); 49 | } 50 | } 51 | return inited; 52 | } 53 | 54 | /** 55 | * Re-hook after System.loadLibrary() and System.load(). 56 | * @param async true if to refresh in async mode; otherwise, refresh in sync mode. 57 | * @return 0 if successful, false otherwise. 58 | */ 59 | public synchronized void refresh(boolean async) { 60 | if(!inited) { 61 | return; 62 | } 63 | 64 | try { 65 | com.qiyi.xhook.NativeHandler.getInstance().refresh(async); 66 | } catch (Throwable ex) { 67 | ex.printStackTrace(); 68 | Log.e("xhook", "xhook native refresh failed"); 69 | } 70 | } 71 | 72 | /** 73 | * Clear all cache. 74 | */ 75 | public synchronized void clear() { 76 | if(!inited) { 77 | return; 78 | } 79 | 80 | try { 81 | com.qiyi.xhook.NativeHandler.getInstance().clear(); 82 | } catch (Throwable ex) { 83 | ex.printStackTrace(); 84 | Log.e("xhook", "xhook native clear failed"); 85 | } 86 | } 87 | 88 | /** 89 | * Enable/disable the debug log to logcat. (disabled by default) 90 | * @param flag the bool flag. 91 | */ 92 | public synchronized void enableDebug(boolean flag) { 93 | if(!inited) { 94 | return; 95 | } 96 | 97 | try { 98 | com.qiyi.xhook.NativeHandler.getInstance().enableDebug(flag); 99 | } catch (Throwable ex) { 100 | ex.printStackTrace(); 101 | Log.e("xhook", "xhook native enableDebug failed"); 102 | } 103 | } 104 | 105 | /** 106 | * Enable/disable the segmentation fault protection. (enabled by default) 107 | * @param flag the bool flag. 108 | */ 109 | public synchronized void enableSigSegvProtection(boolean flag) { 110 | if (!inited) { 111 | return; 112 | } 113 | 114 | try { 115 | com.qiyi.xhook.NativeHandler.getInstance().enableSigSegvProtection(flag); 116 | } catch (Throwable ex) { 117 | ex.printStackTrace(); 118 | Log.e("xhook", "xhook native enableSigSegvProtection failed"); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /xhookwrapper/xhook/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | xhook 3 | 4 | --------------------------------------------------------------------------------