[
  {
    "path": ".gitignore",
    "content": ".DS_Store\n\nlibxhook/libs/\nlibxhook/obj/\nlibbiz/libs/\nlibbiz/obj/\nlibtest/libs/\nlibtest/obj/\n\n#for xhookwrapper\nbuild/\n.gradle/\n.idea/\nlocal.properties\n*.iml\n*.log\n*.so\nxhookwrapper/xhook/libs/\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to xHook\n\nWelcome to the xHook project. Read on to learn more about our development process and how to propose bug fixes and improvements.\n\n## Issues\n\nWe use GitHub issues to track public bugs and feature requests. Before creating an issue, please note the following:\n\n1. Please search existing issues before creating a new one.\n2. Please ensure your description is clear and has sufficient instructions to be able to reproduce the issue. The more information the better.\n\n\n## Branch Management\n\nThere are 2 main branches:\n\n1. `master` branch\n\n    * It's the latest (pre-)release branch. We use `master` for tags.\n    * **Please do NOT submit any PR on `master` branch.**\n\n2. `dev` branch\n\n    * It's our stable developing branch.\n    * Once `dev` has passed iQIYI's internal tests, it will be merged to `master` branch for the next release.\n    * **Please always submit PR on `dev` branch.**\n\n\n## Pull Requests\n\nPlease make sure the following is done when submitting a pull request:\n\n1. Fork the repo and create your branch from `master`.\n2. Add the copyright notice to the top of any new files you've added.\n3. Try your best to test your code.\n4. Squash all of your commits into one meaningful commit.\n\n\n## Code Style Guide\n\n1. 4 spaces for indentation rather than tabs.\n2. Follow the C code style already in place.\n\n\n## License\n\nBy contributing to xHook, you agree that your contributions will be licensed under its [MIT LICENSE](LICENSE).\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.\n\nMost source code in xhook are MIT licensed. Some other source code\nhave BSD-style licenses.\n\nA copy of the MIT License is included in this file.\n\n\nSource code Licensed under the BSD 2-Clause License\n===================================================\n\ntree.h\n\nCopyright 2002 Niels Provos <provos@citi.umich.edu>\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n1. Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright\n   notice, this list of conditions and the following disclaimer in the\n   documentation and/or other materials provided with the distribution.\n   \nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\nIMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\nOF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\nIN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\nINCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\nNOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\nTHIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n\nSource code Licensed under the BSD 3-Clause License\n===================================================\n\nqueue.h\n\nCopyright (c) 1991, 1993 The Regents of the University of California.\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n1. Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright\n   notice, this list of conditions and the following disclaimer in the\n   documentation and/or other materials provided with the distribution.\n3. Neither the name of the University nor the names of its contributors\n   may be used to endorse or promote products derived from this software\n   without specific prior written permission.\n   \nTHIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\nOR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\nHOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\nLIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\nOUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGE.\n\n\nTerms of the MIT License\n========================\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "LICENSE-docs",
    "content": "Attribution 4.0 International\n\n=======================================================================\n\nCreative Commons Corporation (\"Creative Commons\") is not a law firm and\ndoes not provide legal services or legal advice. Distribution of\nCreative Commons public licenses does not create a lawyer-client or\nother relationship. Creative Commons makes its licenses and related\ninformation available on an \"as-is\" basis. Creative Commons gives no\nwarranties regarding its licenses, any material licensed under their\nterms and conditions, or any related information. Creative Commons\ndisclaims all liability for damages resulting from their use to the\nfullest extent possible.\n\nUsing Creative Commons Public Licenses\n\nCreative Commons public licenses provide a standard set of terms and\nconditions that creators and other rights holders may use to share\noriginal works of authorship and other material subject to copyright\nand certain other rights specified in the public license below. The\nfollowing considerations are for informational purposes only, are not\nexhaustive, and do not form part of our licenses.\n\n     Considerations for licensors: Our public licenses are\n     intended for use by those authorized to give the public\n     permission to use material in ways otherwise restricted by\n     copyright and certain other rights. Our licenses are\n     irrevocable. Licensors should read and understand the terms\n     and conditions of the license they choose before applying it.\n     Licensors should also secure all rights necessary before\n     applying our licenses so that the public can reuse the\n     material as expected. Licensors should clearly mark any\n     material not subject to the license. This includes other CC-\n     licensed material, or material used under an exception or\n     limitation to copyright. More considerations for licensors:\n  wiki.creativecommons.org/Considerations_for_licensors\n\n     Considerations for the public: By using one of our public\n     licenses, a licensor grants the public permission to use the\n     licensed material under specified terms and conditions. If\n     the licensor's permission is not necessary for any reason--for\n     example, because of any applicable exception or limitation to\n     copyright--then that use is not regulated by the license. Our\n     licenses grant only permissions under copyright and certain\n     other rights that a licensor has authority to grant. Use of\n     the licensed material may still be restricted for other\n     reasons, including because others have copyright or other\n     rights in the material. A licensor may make special requests,\n     such as asking that all changes be marked or described.\n     Although not required by our licenses, you are encouraged to\n     respect those requests where reasonable. More_considerations\n     for the public:\n  wiki.creativecommons.org/Considerations_for_licensees\n\n=======================================================================\n\nCreative Commons Attribution 4.0 International Public License\n\nBy exercising the Licensed Rights (defined below), You accept and agree\nto be bound by the terms and conditions of this Creative Commons\nAttribution 4.0 International Public License (\"Public License\"). To the\nextent this Public License may be interpreted as a contract, You are\ngranted the Licensed Rights in consideration of Your acceptance of\nthese terms and conditions, and the Licensor grants You such rights in\nconsideration of benefits the Licensor receives from making the\nLicensed Material available under these terms and conditions.\n\n\nSection 1 -- Definitions.\n\n  a. Adapted Material means material subject to Copyright and Similar\n     Rights that is derived from or based upon the Licensed Material\n     and in which the Licensed Material is translated, altered,\n     arranged, transformed, or otherwise modified in a manner requiring\n     permission under the Copyright and Similar Rights held by the\n     Licensor. For purposes of this Public License, where the Licensed\n     Material is a musical work, performance, or sound recording,\n     Adapted Material is always produced where the Licensed Material is\n     synched in timed relation with a moving image.\n\n  b. Adapter's License means the license You apply to Your Copyright\n     and Similar Rights in Your contributions to Adapted Material in\n     accordance with the terms and conditions of this Public License.\n\n  c. Copyright and Similar Rights means copyright and/or similar rights\n     closely related to copyright including, without limitation,\n     performance, broadcast, sound recording, and Sui Generis Database\n     Rights, without regard to how the rights are labeled or\n     categorized. For purposes of this Public License, the rights\n     specified in Section 2(b)(1)-(2) are not Copyright and Similar\n     Rights.\n\n  d. Effective Technological Measures means those measures that, in the\n     absence of proper authority, may not be circumvented under laws\n     fulfilling obligations under Article 11 of the WIPO Copyright\n     Treaty adopted on December 20, 1996, and/or similar international\n     agreements.\n\n  e. Exceptions and Limitations means fair use, fair dealing, and/or\n     any other exception or limitation to Copyright and Similar Rights\n     that applies to Your use of the Licensed Material.\n\n  f. Licensed Material means the artistic or literary work, database,\n     or other material to which the Licensor applied this Public\n     License.\n\n  g. Licensed Rights means the rights granted to You subject to the\n     terms and conditions of this Public License, which are limited to\n     all Copyright and Similar Rights that apply to Your use of the\n     Licensed Material and that the Licensor has authority to license.\n\n  h. Licensor means the individual(s) or entity(ies) granting rights\n     under this Public License.\n\n  i. Share means to provide material to the public by any means or\n     process that requires permission under the Licensed Rights, such\n     as reproduction, public display, public performance, distribution,\n     dissemination, communication, or importation, and to make material\n     available to the public including in ways that members of the\n     public may access the material from a place and at a time\n     individually chosen by them.\n\n  j. Sui Generis Database Rights means rights other than copyright\n     resulting from Directive 96/9/EC of the European Parliament and of\n     the Council of 11 March 1996 on the legal protection of databases,\n     as amended and/or succeeded, as well as other essentially\n     equivalent rights anywhere in the world.\n\n  k. You means the individual or entity exercising the Licensed Rights\n     under this Public License. Your has a corresponding meaning.\n\n\nSection 2 -- Scope.\n\n  a. License grant.\n\n       1. Subject to the terms and conditions of this Public License,\n          the Licensor hereby grants You a worldwide, royalty-free,\n          non-sublicensable, non-exclusive, irrevocable license to\n          exercise the Licensed Rights in the Licensed Material to:\n\n            a. reproduce and Share the Licensed Material, in whole or\n               in part; and\n\n            b. produce, reproduce, and Share Adapted Material.\n\n       2. Exceptions and Limitations. For the avoidance of doubt, where\n          Exceptions and Limitations apply to Your use, this Public\n          License does not apply, and You do not need to comply with\n          its terms and conditions.\n\n       3. Term. The term of this Public License is specified in Section\n          6(a).\n\n       4. Media and formats; technical modifications allowed. The\n          Licensor authorizes You to exercise the Licensed Rights in\n          all media and formats whether now known or hereafter created,\n          and to make technical modifications necessary to do so. The\n          Licensor waives and/or agrees not to assert any right or\n          authority to forbid You from making technical modifications\n          necessary to exercise the Licensed Rights, including\n          technical modifications necessary to circumvent Effective\n          Technological Measures. For purposes of this Public License,\n          simply making modifications authorized by this Section 2(a)\n          (4) never produces Adapted Material.\n\n       5. Downstream recipients.\n\n            a. Offer from the Licensor -- Licensed Material. Every\n               recipient of the Licensed Material automatically\n               receives an offer from the Licensor to exercise the\n               Licensed Rights under the terms and conditions of this\n               Public License.\n\n            b. No downstream restrictions. You may not offer or impose\n               any additional or different terms or conditions on, or\n               apply any Effective Technological Measures to, the\n               Licensed Material if doing so restricts exercise of the\n               Licensed Rights by any recipient of the Licensed\n               Material.\n\n       6. No endorsement. Nothing in this Public License constitutes or\n          may be construed as permission to assert or imply that You\n          are, or that Your use of the Licensed Material is, connected\n          with, or sponsored, endorsed, or granted official status by,\n          the Licensor or others designated to receive attribution as\n          provided in Section 3(a)(1)(A)(i).\n\n  b. Other rights.\n\n       1. Moral rights, such as the right of integrity, are not\n          licensed under this Public License, nor are publicity,\n          privacy, and/or other similar personality rights; however, to\n          the extent possible, the Licensor waives and/or agrees not to\n          assert any such rights held by the Licensor to the limited\n          extent necessary to allow You to exercise the Licensed\n          Rights, but not otherwise.\n\n       2. Patent and trademark rights are not licensed under this\n          Public License.\n\n       3. To the extent possible, the Licensor waives any right to\n          collect royalties from You for the exercise of the Licensed\n          Rights, whether directly or through a collecting society\n          under any voluntary or waivable statutory or compulsory\n          licensing scheme. In all other cases the Licensor expressly\n          reserves any right to collect such royalties.\n\n\nSection 3 -- License Conditions.\n\nYour exercise of the Licensed Rights is expressly made subject to the\nfollowing conditions.\n\n  a. Attribution.\n\n       1. If You Share the Licensed Material (including in modified\n          form), You must:\n\n            a. retain the following if it is supplied by the Licensor\n               with the Licensed Material:\n\n                 i. identification of the creator(s) of the Licensed\n                    Material and any others designated to receive\n                    attribution, in any reasonable manner requested by\n                    the Licensor (including by pseudonym if\n                    designated);\n\n                ii. a copyright notice;\n\n               iii. a notice that refers to this Public License;\n\n                iv. a notice that refers to the disclaimer of\n                    warranties;\n\n                 v. a URI or hyperlink to the Licensed Material to the\n                    extent reasonably practicable;\n\n            b. indicate if You modified the Licensed Material and\n               retain an indication of any previous modifications; and\n\n            c. indicate the Licensed Material is licensed under this\n               Public License, and include the text of, or the URI or\n               hyperlink to, this Public License.\n\n       2. You may satisfy the conditions in Section 3(a)(1) in any\n          reasonable manner based on the medium, means, and context in\n          which You Share the Licensed Material. For example, it may be\n          reasonable to satisfy the conditions by providing a URI or\n          hyperlink to a resource that includes the required\n          information.\n\n       3. If requested by the Licensor, You must remove any of the\n          information required by Section 3(a)(1)(A) to the extent\n          reasonably practicable.\n\n       4. If You Share Adapted Material You produce, the Adapter's\n          License You apply must not prevent recipients of the Adapted\n          Material from complying with this Public License.\n\n\nSection 4 -- Sui Generis Database Rights.\n\nWhere the Licensed Rights include Sui Generis Database Rights that\napply to Your use of the Licensed Material:\n\n  a. for the avoidance of doubt, Section 2(a)(1) grants You the right\n     to extract, reuse, reproduce, and Share all or a substantial\n     portion of the contents of the database;\n\n  b. if You include all or a substantial portion of the database\n     contents in a database in which You have Sui Generis Database\n     Rights, then the database in which You have Sui Generis Database\n     Rights (but not its individual contents) is Adapted Material; and\n\n  c. You must comply with the conditions in Section 3(a) if You Share\n     all or a substantial portion of the contents of the database.\n\nFor the avoidance of doubt, this Section 4 supplements and does not\nreplace Your obligations under this Public License where the Licensed\nRights include other Copyright and Similar Rights.\n\n\nSection 5 -- Disclaimer of Warranties and Limitation of Liability.\n\n  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE\n     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS\n     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF\n     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,\n     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,\n     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR\n     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,\n     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT\n     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT\n     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.\n\n  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE\n     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,\n     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,\n     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,\n     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR\n     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN\n     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR\n     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR\n     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.\n\n  c. The disclaimer of warranties and limitation of liability provided\n     above shall be interpreted in a manner that, to the extent\n     possible, most closely approximates an absolute disclaimer and\n     waiver of all liability.\n\n\nSection 6 -- Term and Termination.\n\n  a. This Public License applies for the term of the Copyright and\n     Similar Rights licensed here. However, if You fail to comply with\n     this Public License, then Your rights under this Public License\n     terminate automatically.\n\n  b. Where Your right to use the Licensed Material has terminated under\n     Section 6(a), it reinstates:\n\n       1. automatically as of the date the violation is cured, provided\n          it is cured within 30 days of Your discovery of the\n          violation; or\n\n       2. upon express reinstatement by the Licensor.\n\n     For the avoidance of doubt, this Section 6(b) does not affect any\n     right the Licensor may have to seek remedies for Your violations\n     of this Public License.\n\n  c. For the avoidance of doubt, the Licensor may also offer the\n     Licensed Material under separate terms or conditions or stop\n     distributing the Licensed Material at any time; however, doing so\n     will not terminate this Public License.\n\n  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public\n     License.\n\n\nSection 7 -- Other Terms and Conditions.\n\n  a. The Licensor shall not be bound by any additional or different\n     terms or conditions communicated by You unless expressly agreed.\n\n  b. Any arrangements, understandings, or agreements regarding the\n     Licensed Material not stated herein are separate from and\n     independent of the terms and conditions of this Public License.\n\n\nSection 8 -- Interpretation.\n\n  a. For the avoidance of doubt, this Public License does not, and\n     shall not be interpreted to, reduce, limit, restrict, or impose\n     conditions on any use of the Licensed Material that could lawfully\n     be made without permission under this Public License.\n\n  b. To the extent possible, if any provision of this Public License is\n     deemed unenforceable, it shall be automatically reformed to the\n     minimum extent necessary to make it enforceable. If the provision\n     cannot be reformed, it shall be severed from this Public License\n     without affecting the enforceability of the remaining terms and\n     conditions.\n\n  c. No term or condition of this Public License will be waived and no\n     failure to comply consented to unless expressly agreed to by the\n     Licensor.\n\n  d. Nothing in this Public License constitutes or may be interpreted\n     as a limitation upon, or waiver of, any privileges and immunities\n     that apply to the Licensor or You, including from the legal\n     processes of any jurisdiction or authority.\n\n\n=======================================================================\n\nCreative Commons is not a party to its public licenses.\nNotwithstanding, Creative Commons may elect to apply one of its public\nlicenses to material it publishes and in those instances will be\nconsidered the \"Licensor.\" Except for the limited purpose of indicating\nthat material is shared under a Creative Commons public license or as\notherwise permitted by the Creative Commons policies published at\ncreativecommons.org/policies, Creative Commons does not authorize the\nuse of the trademark \"Creative Commons\" or any other trademark or logo\nof Creative Commons without its prior written consent including,\nwithout limitation, in connection with any unauthorized modifications\nto any of its public licenses or any other arrangements,\nunderstandings, or agreements concerning use of licensed material. For\nthe avoidance of doubt, this paragraph does not form part of the public\nlicenses.\n\nCreative Commons may be contacted at creativecommons.org.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\"><img src=\"https://github.com/iqiyi/xHook/blob/master/docs/xhooklogo.png?raw=true\" alt=\"xhook\" width=\"50%\"></p>\n\n# xHook\n\n![](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)\n![](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)\n![](https://img.shields.io/badge/release-1.2.0-red.svg?style=flat)\n![](https://img.shields.io/badge/Android-4.0%20--%2010-blue.svg?style=flat)\n![](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)\n\n[README 中文版](README.zh-CN.md)\n\n[Android PLT hook 概述 中文版](docs/overview/android_plt_hook_overview.zh-CN.md)\n\nxHook is a PLT (Procedure Linkage Table) hook library for Android native ELF (executable and shared libraries).\n\nxHook has been keeping optimized for stability and compatibility.\n\n\n## Features\n\n* Support Android 4.0 - 10 (API level 14 - 29).\n* Support armeabi, armeabi-v7a, arm64-v8a, x86 and x86_64.\n* Support **ELF HASH** and **GNU HASH** indexed symbols.\n* Support **SLEB128** encoded relocation info.\n* Support setting hook info via regular expressions.\n* Do not require root permission or any system permissions.\n* Do not depends on any third-party shared libraries.\n\n\n## Build\n\n* Download [Android NDK r16b](https://developer.android.com/ndk/downloads/revision_history.html), set environment PATH. (support for armeabi has been removed since r17)\n\n* Build and install the native libraries.\n\n```\n./build_libs.sh\n./install_libs.sh\n```\n\n\n## Demo\n\n```\ncd ./xhookwrapper/\n./gradlew assembleDebug\nadb install ./app/build/outputs/apk/debug/app-debug.apk\n```\n\n\n## API\n\nExternal API header file: `libxhook/jni/xhook.h`\n\n### 1. Register hook info\n\n```c\nint xhook_register(const char  *pathname_regex_str,  \n                   const char  *symbol,  \n                   void        *new_func,  \n                   void       **old_func);\n```\n\nIn 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`.\n\nThe `new_func` **must** have the same function declaration as the original one.\n\nReturn zero if successful, non-zero otherwise.\n\nThe regular expression for `pathname_regex_str` only support **POSIX BRE (Basic Regular Expression)**.\n\n### 2. Ignore some hook info\n\n```c\nint xhook_ignore(const char *pathname_regex_str,  \n                 const char *symbol);\n```\n\nIgnore 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`.\n\nReturn zero if successful, non-zero otherwise.\n\nThe regular expression for `pathname_regex_str` only support **POSIX BRE**.\n\n### 3. Do hook\n\n```c\nint xhook_refresh(int async);\n```\n\nDo the real hook operations according to the registered hook info.\n\nPass `1` to `async` for asynchronous hook. Pass `0` to `async` for synchronous hook.\n\nReturn zero if successful, non-zero otherwise.\n\nxhook 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.\n\n### 4. Clear cache\n\n```c\nvoid xhook_clear();\n```\n\nClear all cache owned by xhook, reset all global flags to default value.\n\nIf you confirm that all PLT entries you want have been hooked, you could call this function to save some memory.\n\n### 5. Enable/Disable debug info\n\n```c\nvoid xhook_enable_debug(int flag);\n```\n\nPass `1` to `flag` for enable debug info. Pass `0` to `flag` for disable. (**disabled** by default)\n\nDebug info will be sent to logcat with tag `xhook`.\n\n### 6. Enable/Disable SFP (segmentation fault protection)\n\n```c\nvoid xhook_enable_sigsegv_protection(int flag);\n```\n\nPass `1` to `flag` for enable SFP. Pass `0` to `flag` for disable. (**enabled** by default) \n\nxhook 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()`.\n\n**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.**\n\n\n## Examples\n\n```c\n//detect memory leaks\nxhook_register(\".*\\\\.so$\", \"malloc\",  my_malloc,  NULL);\nxhook_register(\".*\\\\.so$\", \"calloc\",  my_calloc,  NULL);\nxhook_register(\".*\\\\.so$\", \"realloc\", my_realloc, NULL);\nxhook_register(\".*\\\\.so$\", \"free\",    my_free,    NULL);\n\n//inspect sockets lifecycle\nxhook_register(\".*\\\\.so$\", \"getaddrinfo\", my_getaddrinfo, NULL);\nxhook_register(\".*\\\\.so$\", \"socket\",      my_socket,      NULL);\nxhook_register(\".*\\\\.so$\", \"setsockopt\"   my_setsockopt,  NULL);\nxhook_register(\".*\\\\.so$\", \"bind\",        my_bind,        NULL);\nxhook_register(\".*\\\\.so$\", \"listen\",      my_listen,      NULL);\nxhook_register(\".*\\\\.so$\", \"connect\",     my_connect,     NULL);\nxhook_register(\".*\\\\.so$\", \"shutdown\",    my_shutdown,    NULL);\nxhook_register(\".*\\\\.so$\", \"close\",       my_close,       NULL);\n\n//filter off and save some android log to local file\nxhook_register(\".*\\\\.so$\", \"__android_log_write\",  my_log_write,  NULL);\nxhook_register(\".*\\\\.so$\", \"__android_log_print\",  my_log_print,  NULL);\nxhook_register(\".*\\\\.so$\", \"__android_log_vprint\", my_log_vprint, NULL);\nxhook_register(\".*\\\\.so$\", \"__android_log_assert\", my_log_assert, NULL);\n\n//tracking (ignore linker and linker64)\nxhook_register(\"^/system/.*$\", \"mmap\",   my_mmap,   NULL);\nxhook_register(\"^/vendor/.*$\", \"munmap\", my_munmap, NULL);\nxhook_ignore  (\".*/linker$\",   \"mmap\");\nxhook_ignore  (\".*/linker$\",   \"munmap\");\nxhook_ignore  (\".*/linker64$\", \"mmap\");\nxhook_ignore  (\".*/linker64$\", \"munmap\");\n\n//defense to some injection attacks\nxhook_register(\".*com\\\\.hacker.*\\\\.so$\", \"malloc\",  my_malloc_always_return_NULL, NULL);\nxhook_register(\".*/libhacker\\\\.so$\",     \"connect\", my_connect_with_recorder,     NULL);\n\n//fix some system bug\nxhook_register(\".*some_vendor.*/libvictim\\\\.so$\", \"bad_func\", my_nice_func, NULL);\n\n//ignore all hooks in libwebviewchromium.so\nxhook_ignore(\".*/libwebviewchromium.so$\", NULL);\n\n//hook now!\nxhook_refresh(1);\n```\n\n\n## Support\n\n* [GitHub Issues](https://github.com/iqiyi/xHook/issues)\n* [GitHub Discussions](https://github.com/iqiyi/xHook/discussions)\n\n\n## Contributing\n\nSee [xHook Contributing Guide](CONTRIBUTING.md).\n\n\n## License\n\nxHook is MIT licensed, as found in the [LICENSE](LICENSE) file.\n\nxHook documentation is Creative Commons licensed, as found in the [LICENSE-docs](LICENSE-docs) file.\n"
  },
  {
    "path": "README.zh-CN.md",
    "content": "<p align=\"center\"><img src=\"https://github.com/iqiyi/xHook/blob/master/docs/xhooklogo.png?raw=true\" alt=\"xhook\" width=\"50%\"></p>\n\n# xHook\n\n![](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)\n![](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)\n![](https://img.shields.io/badge/release-1.2.0-red.svg?style=flat)\n![](https://img.shields.io/badge/Android-4.0%20--%2010-blue.svg?style=flat)\n![](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)\n\n[README English Version](README.md)\n\n[Android PLT hook 概述 中文版](docs/overview/android_plt_hook_overview.zh-CN.md)\n\nxHook 是一个针对 Android 平台 ELF (可执行文件和动态库) 的 PLT (Procedure Linkage Table) hook 库。\n\nxHook 一直在稳定性和兼容性方面做着持续的优化。\n\n\n## 特征\n\n* 支持 Android 4.0 - 10（API level 14 - 29）。\n* 支持 armeabi，armeabi-v7a，arm64-v8a，x86 和 x86_64。\n* 支持 **ELF HASH** 和 **GNU HASH** 索引的符号。\n* 支持 **SLEB128** 编码的重定位信息。\n* 支持通过正则表达式批量设置 hook 信息。\n* 不需要 root 权限或任何系统权限。\n* 不依赖于任何的第三方动态库。\n\n\n## 编译\n\n* 下载 [Android NDK r16b](https://developer.android.com/ndk/downloads/revision_history.html)，设置 PATH 环境变量。（对 armeabi 的支持，从 r17 版本开始被移除了）\n\n* 编译和安装 native 库。\n\n```\n./build_libs.sh\n./install_libs.sh\n```\n\n\n## Demo\n\n```\ncd ./xhookwrapper/\n./gradlew assembleDebug\nadb install ./app/build/outputs/apk/debug/app-debug.apk\n```\n\n\n## API\n\n外部 API 头文件: `libxhook/jni/xhook.h`\n\n### 1. 注册 hook 信息\n\n```c\nint xhook_register(const char  *pathname_regex_str,  \n                   const char  *symbol,  \n                   void        *new_func,  \n                   void       **old_func);\n```\n\n在当前进程的内存空间中，在每一个符合正则表达式 `pathname_regex_str` 的已加载ELF中，每一个调用 `symbol` 的 PLT 入口点的地址值都将给替换成 `new_func`。之前的 PLT 入口点的地址值将被保存在 `old_func` 中。\n\n`new_func` 必须具有和原函数同样的函数声明。\n\n成功返回 0，失败返回 非0。\n\n`pathname_regex_str` 只支持 **POSIX BRE (Basic Regular Expression)** 定义的正则表达式语法。\n\n### 2. 忽略部分 hook 信息\n\n```c\nint xhook_ignore(const char *pathname_regex_str,  \n                 const char *symbol);\n```\n\n根据 `pathname_regex_str` 和 `symbol`，从已经通过 `xhook_register` 注册的 hook 信息中，忽略一部分 hook 信息。如果 `symbol` 为 `NULL`，xhook 将忽略所有路径名符合正则表达式 `pathname_regex_str` 的 ELF。\n\n成功返回 0，失败返回 非0。\n\n`pathname_regex_str` 只支持 **POSIX BRE** 定义的正则表达式语法。\n\n### 3. 执行 hook\n\n```c\nint xhook_refresh(int async);\n```\n\n根据前面注册的 hook 信息，执行真正的 hook 操作。\n\n给 `async` 参数传 `1` 表示执行异步的 hook 操作，传 `0` 表示执行同步的 hook 操作。\n\n成功返回 0，失败返回 非0。\n\nxhook 在内部维护了一个全局的缓存，用于保存最后一次从 `/proc/self/maps` 读取到的 ELF 加载信息。每次一调用 `xhook_refresh` 函数，这个缓存都将被更新。xhook 使用这个缓存来判断哪些 ELF 是这次新被加载到内存中的。我们每次只需要针对这些新加载的 ELF 做 hook 就可以了。\n\n### 4. 清除缓存\n\n```c\nvoid xhook_clear();\n```\n\n清除 xhook 的缓存，重置所有的全局标示。\n\n如果你确定你需要的所有 PLT 入口点都已经被替换了，你可以调用这个函数来释放和节省一些内存空间。\n\n### 5. 启用/禁用 调试信息\n\n```c\nvoid xhook_enable_debug(int flag);\n```\n\n给 `flag` 参数传 `1` 表示启用调试信息，传 `0` 表示禁用调试信息。 (默认为：**禁用**)\n\n调试信息将被输出到 logcat，对应的 TAG 为：`xhook`。\n\n### 6. 启用/禁用 SFP (段错误保护)\n\n```c\nvoid xhook_enable_sigsegv_protection(int flag);\n```\n\n给 `flag` 参数传 `1` 表示启用 SFP，传 `0` 表示禁用 SFP。 (默认为：**启用**)\n\nxhook 并不是一个常规的业务层的动态库。在 xhook 中，我们不得不直接计算一些内存指针的值。在一些极端的情况和环境下，读或者写这些指针指向的内存会发生段错误。根据我们的测试，xhook 的行为将导致 APP 崩溃率增加 “一千万分之一” (0.0000001)。（具体崩溃率可能会增加多少，也和你想要 hook 的库和符号有关）。最终，我们不得不使用某些方法来防止这些无害的崩溃。我们叫它SFP (段错误保护)，它是由这些调用和值组成的：`sigaction()`， `SIGSEGV`， `siglongjmp()` 和 `sigsetjmp()`。\n\n**在 release 版本的 APP 中，你应该始终启用 SFP，这能防止你的 APP 因为 xhook 而崩溃。在 debug 版本的 APP 中，你应该始终禁用 SFP，这样你就不会丢失那些一般性的编码失误导致的段错误，这些段错误是应该被修复的。**\n\n\n## 例子\n\n```c\n//监测内存泄露\nxhook_register(\".*\\\\.so$\", \"malloc\",  my_malloc,  NULL);\nxhook_register(\".*\\\\.so$\", \"calloc\",  my_calloc,  NULL);\nxhook_register(\".*\\\\.so$\", \"realloc\", my_realloc, NULL);\nxhook_register(\".*\\\\.so$\", \"free\",    my_free,    NULL);\n\n//监控 sockets 生命周期\nxhook_register(\".*\\\\.so$\", \"getaddrinfo\", my_getaddrinfo, NULL);\nxhook_register(\".*\\\\.so$\", \"socket\",      my_socket,      NULL);\nxhook_register(\".*\\\\.so$\", \"setsockopt\"   my_setsockopt,  NULL);\nxhook_register(\".*\\\\.so$\", \"bind\",        my_bind,        NULL);\nxhook_register(\".*\\\\.so$\", \"listen\",      my_listen,      NULL);\nxhook_register(\".*\\\\.so$\", \"connect\",     my_connect,     NULL);\nxhook_register(\".*\\\\.so$\", \"shutdown\",    my_shutdown,    NULL);\nxhook_register(\".*\\\\.so$\", \"close\",       my_close,       NULL);\n\n//过滤出和保存部分安卓 log 到本地文件\nxhook_register(\".*\\\\.so$\", \"__android_log_write\",  my_log_write,  NULL);\nxhook_register(\".*\\\\.so$\", \"__android_log_print\",  my_log_print,  NULL);\nxhook_register(\".*\\\\.so$\", \"__android_log_vprint\", my_log_vprint, NULL);\nxhook_register(\".*\\\\.so$\", \"__android_log_assert\", my_log_assert, NULL);\n\n//追踪某些调用 (忽略 linker 和 linker64)\nxhook_register(\"^/system/.*$\", \"mmap\",   my_mmap,   NULL);\nxhook_register(\"^/vendor/.*$\", \"munmap\", my_munmap, NULL);\nxhook_ignore  (\".*/linker$\",   \"mmap\");\nxhook_ignore  (\".*/linker$\",   \"munmap\");\nxhook_ignore  (\".*/linker64$\", \"mmap\");\nxhook_ignore  (\".*/linker64$\", \"munmap\");\n\n//防御某些注入攻击\nxhook_register(\".*com\\\\.hacker.*\\\\.so$\", \"malloc\",  my_malloc_always_return_NULL, NULL);\nxhook_register(\".*/libhacker\\\\.so$\",     \"connect\", my_connect_with_recorder,     NULL);\n\n//修复某些系统 bug\nxhook_register(\".*some_vendor.*/libvictim\\\\.so$\", \"bad_func\", my_nice_func, NULL);\n\n//忽略 libwebviewchromium.so 的所有 hook 信息\nxhook_ignore(\".*/libwebviewchromium.so$\", NULL);\n\n//现在执行 hook!\nxhook_refresh(1);\n```\n\n\n## 技术支持\n\n* [GitHub Issues](https://github.com/iqiyi/xHook/issues)\n* [GitHub Discussions](https://github.com/iqiyi/xHook/discussions)\n\n\n## 贡献\n\n请阅读 [xHook Contributing Guide](CONTRIBUTING.md)。\n\n\n## 许可证\n\nxHook 使用 [MIT 许可证](LICENSE)。\n\nxHook 的文档使用 [Creative Commons 许可证](LICENSE-docs)。\n"
  },
  {
    "path": "build_libs.sh",
    "content": "#!/bin/bash\n\nndk-build -C ./libxhook/jni\nndk-build -C ./libbiz/jni\nndk-build -C ./libtest/jni\n"
  },
  {
    "path": "clean_libs.sh",
    "content": "#!/bin/bash\n\nndk-build -C ./libbiz/jni clean\nndk-build -C ./libxhook/jni clean\nndk-build -C ./libtest/jni clean\n"
  },
  {
    "path": "docs/overview/android_plt_hook_overview.zh-CN.md",
    "content": "# Android PLT hook 概述\n\n\n## 获取代码和资源\n\n你始终可以从 [这里](https://github.com/iqiyi/xHook/blob/master/docs/overview/android_plt_hook_overview.zh-CN.md) 访问本文的最新版本。\n\n文中使用的示例代码可以从 [这里](https://github.com/iqiyi/xHook/tree/master/docs/overview/code) 获取。文中提到的 xhook 开源项目可以从 [这里](https://github.com/iqiyi/xHook) 获取。 \n\n\n## 开始\n\n\n### 新的动态库\n\n我们有一个新的动态库：libtest.so。\n\n> 头文件 test.h\n\n```c\n#ifndef TEST_H\n#define TEST_H 1\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid say_hello();\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n```\n\n> 源文件 test.c\n\n```c\n#include <stdlib.h>\n#include <stdio.h>\n\nvoid say_hello()\n{\n    char *buf = malloc(1024);\n    if(NULL != buf)\n    {\n        snprintf(buf, 1024, \"%s\", \"hello\\n\");\n        printf(\"%s\", buf);\n    }\n}\n```\n\n`say_hello` 的功能是在终端打印出 `hello\\n` 这6个字符（包括结尾的 `\\n`）。\n\n我们需要一个测试程序：main。\n\n> 源文件 main.c\n\n```c\n#include <test.h>\n\nint main()\n{\n    say_hello();\n    return 0;\n}\n```\n\n编译它们分别生成 libtest.so 和 main。运行一下：\n\n```\ncaikelun@debian:~$ adb push ./libtest.so ./main /data/local/tmp\ncaikelun@debian:~$ adb shell \"chmod +x /data/local/tmp/main\"\ncaikelun@debian:~$ adb shell \"export LD_LIBRARY_PATH=/data/local/tmp; /data/local/tmp/main\"\nhello\ncaikelun@debian:~$\n```\n\n太棒了！libtest.so 的代码虽然看上去有些愚蠢，但是它居然可以正确的工作，那还有什么可抱怨的呢？赶紧在新版 APP 中开始使用它吧！\n\n遗憾的是，正如你可能已经发现的，libtest.so 存在严重的内存泄露问题，每调用一次 `say_hello` 函数，就会泄露 1024 字节的内存。新版 APP 上线后崩溃率开始上升，各种诡异的崩溃信息和报障信息跌撞而至。\n\n\n### 面临的问题\n\n幸运的是，我们修复了 libtest.so 的问题。可是以后怎么办呢？我们面临 2 个问题：\n\n1. 当测试覆盖不足时，如何及时发现和准确定位线上 APP 的此类问题？\n2. 如果 libtest.so 是某些机型的系统库，或者第三方的闭源库，我们如何修复它？如果监控它的行为？\n\n\n### 怎么做？\n\n如果我们能对动态库中的函数调用做 hook（替换，拦截，窃听，或者你觉得任何正确的描述方式），那就能够做到很多我们想做的事情。比如 hook `malloc`，`calloc`，`realloc` 和 `free`，我们就能统计出各个动态库分配了多少内存，哪些内存一直被占用没有释放。\n\n这真的能做到吗？答案是：hook 我们自己的进程是完全可以的。hook 其他进程需要 root 权限（对于其他进程，没有 root 权限就没法修改它的内存空间，也没法注入代码）。幸运的是，我们只要 hook 自己就够了。\n\n\n## ELF\n\n\n### 概述\n\nELF（Executable and Linkable Format）是一种行业标准的二进制数据封装格式，主要用于封装可执行文件、动态库、object 文件和 core dumps 文件。\n\n使用 google NDK 对源代码进行编译和链接，生成的动态库或可执行文件都是 ELF 格式的。用 readelf 可以查看 ELF 文件的基本信息，用 objdump 可以查看 ELF 文件的反汇编输出。\n\nELF 格式的概述可以参考 [这里](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）。\n\n\n### ELF 文件头\n\nELF 文件的起始处，有一个固定格式的定长的文件头（32 位架构为 52 字节，64 位架构为 64 字节）。ELF 文件头以 magic number `0x7F 0x45 0x4C 0x46` 开始（其中后 3 个字节分别对应可见字符 `E` `L` `F`）。\n\nlibtest.so 的 ELF 文件头信息：\n\n```\ncaikelun@debian:~$ arm-linux-androideabi-readelf -h ./libtest.so\n \nELF Header:\n  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00\n  Class:                             ELF32\n  Data:                              2's complement, little endian\n  Version:                           1 (current)\n  OS/ABI:                            UNIX - System V\n  ABI Version:                       0\n  Type:                              DYN (Shared object file)\n  Machine:                           ARM\n  Version:                           0x1\n  Entry point address:               0x0\n  Start of program headers:          52 (bytes into file)\n  Start of section headers:          12744 (bytes into file)\n  Flags:                             0x5000200, Version5 EABI, soft-float ABI\n  Size of this header:               52 (bytes)\n  Size of program headers:           32 (bytes)\n  Number of program headers:         8\n  Size of section headers:           40 (bytes)\n  Number of section headers:         25\n  Section header string table index: 24\n```\n\nELF 文件头中包含了 SHT 和 PHT 在当前 ELF 文件中的起始位置和长度。例如，libtest.so 的 SHT 起始位置为 12744，长度 40 字节；PHT 起始位置为 52，长度 32字节。\n\n![](https://raw.githubusercontent.com/iqiyi/xHook/master/docs/overview/res/elfheader.png)\n\n\n### SHT（section header table）\n\nELF 以 section 为单位来组织和管理各种信息。ELF 使用 SHT 来记录所有 section 的基本信息。主要包括：section 的类型、在文件中的偏移量、大小、加载到内存后的虚拟内存相对地址、内存中字节的对齐方式等。\n\nlibtest.so 的 SHT：\n\n```\ncaikelun@debian:~$ arm-linux-androideabi-readelf -S ./libtest.so\n \nThere are 25 section headers, starting at offset 0x31c8:\n\nSection Headers:\n  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al\n  [ 0]                   NULL            00000000 000000 000000 00      0   0  0\n  [ 1] .note.android.ide NOTE            00000134 000134 000098 00   A  0   0  4\n  [ 2] .note.gnu.build-i NOTE            000001cc 0001cc 000024 00   A  0   0  4\n  [ 3] .dynsym           DYNSYM          000001f0 0001f0 0003a0 10   A  4   1  4\n  [ 4] .dynstr           STRTAB          00000590 000590 0004b1 00   A  0   0  1\n  [ 5] .hash             HASH            00000a44 000a44 000184 04   A  3   0  4\n  [ 6] .gnu.version      VERSYM          00000bc8 000bc8 000074 02   A  3   0  2\n  [ 7] .gnu.version_d    VERDEF          00000c3c 000c3c 00001c 00   A  4   1  4\n  [ 8] .gnu.version_r    VERNEED         00000c58 000c58 000020 00   A  4   1  4\n  [ 9] .rel.dyn          REL             00000c78 000c78 000040 08   A  3   0  4\n  [10] .rel.plt          REL             00000cb8 000cb8 0000f0 08  AI  3  18  4\n  [11] .plt              PROGBITS        00000da8 000da8 00017c 00  AX  0   0  4\n  [12] .text             PROGBITS        00000f24 000f24 0015a4 00  AX  0   0  4\n  [13] .ARM.extab        PROGBITS        000024c8 0024c8 00003c 00   A  0   0  4\n  [14] .ARM.exidx        ARM_EXIDX       00002504 002504 000100 08  AL 12   0  4\n  [15] .fini_array       FINI_ARRAY      00003e3c 002e3c 000008 04  WA  0   0  4\n  [16] .init_array       INIT_ARRAY      00003e44 002e44 000004 04  WA  0   0  1\n  [17] .dynamic          DYNAMIC         00003e48 002e48 000118 08  WA  4   0  4\n  [18] .got              PROGBITS        00003f60 002f60 0000a0 00  WA  0   0  4\n  [19] .data             PROGBITS        00004000 003000 000004 00  WA  0   0  4\n  [20] .bss              NOBITS          00004004 003004 000000 00  WA  0   0  1\n  [21] .comment          PROGBITS        00000000 003004 000065 01  MS  0   0  1\n  [22] .note.gnu.gold-ve NOTE            00000000 00306c 00001c 00      0   0  4\n  [23] .ARM.attributes   ARM_ATTRIBUTES  00000000 003088 00003b 00      0   0  1\n  [24] .shstrtab         STRTAB          00000000 0030c3 000102 00      0   0  1\nKey to Flags:\n  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),\n  L (link order), O (extra OS processing required), G (group), T (TLS),\n  C (compressed), x (unknown), o (OS specific), E (exclude),\n  y (noread), p (processor specific)\n```\n\n比较重要，且和 hook 关系比较大的几个 section 是：\n\n* `.dynstr`：保存了所有的字符串常量信息。\n* `.dynsym`：保存了符号（symbol）的信息（符号的类型、起始地址、大小、符号名称在 `.dynstr` 中的索引编号等）。函数也是一种符号。\n* `.text`：程序代码经过编译后生成的机器指令。\n* `.dynamic`：供动态链接器使用的各项信息，记录了当前 ELF 的外部依赖，以及其他各个重要 section 的起始位置等信息。\n* `.got`：Global Offset Table。用于记录外部调用的入口地址。动态链接器（linker）执行重定位（relocate）操作时，这里会被填入真实的外部调用的绝对地址。\n* `.plt`：Procedure Linkage Table。外部调用的跳板，主要用于支持 lazy binding 方式的外部调用重定位。（Android 目前只有 MIPS 架构支持 lazy binding）\n* `.rel.plt`：对外部函数直接调用的重定位信息。\n* `.rel.dyn`：除 `.rel.plt` 以外的重定位信息。（比如通过全局函数指针来调用外部函数）\n\n![](https://raw.githubusercontent.com/iqiyi/xHook/master/docs/overview/res/elfpltgot.png)\n\n\n### PHT（program header table）\n\nELF 被加载到内存时，是以 segment 为单位的。一个 segment 包含了一个或多个 section。ELF 使用 PHT 来记录所有 segment 的基本信息。主要包括：segment 的类型、在文件中的偏移量、大小、加载到内存后的虚拟内存相对地址、内存中字节的对齐方式等。\n\nlibtest.so 的 PHT：\n\n```\ncaikelun@debian:~$ arm-linux-androideabi-readelf -l ./libtest.so \n\nElf file type is DYN (Shared object file)\nEntry point 0x0\nThere are 8 program headers, starting at offset 52\n\nProgram Headers:\n  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align\n  PHDR           0x000034 0x00000034 0x00000034 0x00100 0x00100 R   0x4\n  LOAD           0x000000 0x00000000 0x00000000 0x02604 0x02604 R E 0x1000\n  LOAD           0x002e3c 0x00003e3c 0x00003e3c 0x001c8 0x001c8 RW  0x1000\n  DYNAMIC        0x002e48 0x00003e48 0x00003e48 0x00118 0x00118 RW  0x4\n  NOTE           0x000134 0x00000134 0x00000134 0x000bc 0x000bc R   0x4\n  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10\n  EXIDX          0x002504 0x00002504 0x00002504 0x00100 0x00100 R   0x4\n  GNU_RELRO      0x002e3c 0x00003e3c 0x00003e3c 0x001c4 0x001c4 RW  0x4\n\n Section to Segment mapping:\n  Segment Sections...\n   00     \n   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 \n   02     .fini_array .init_array .dynamic .got .data \n   03     .dynamic \n   04     .note.android.ident .note.gnu.build-id \n   05     \n   06     .ARM.exidx \n   07     .fini_array .init_array .dynamic .got\n```\n\n所有类型为 `PT_LOAD` 的 segment 都会被动态链接器（linker）映射（mmap）到内存中。\n\n\n### 连接视图（Linking View）和执行视图（Execution View）\n\n* 连接视图：ELF 未被加载到内存执行前，以 section 为单位的数据组织形式。\n* 执行视图：ELF 被加载到内存后，以 segment 为单位的数据组织形式。\n\n我们关心的 hook 操作，属于动态形式的内存操作，因此主要关心的是执行视图，即 ELF 被加载到内存后，ELF 中的数据是如何组织和存放的。\n\n![](https://raw.githubusercontent.com/iqiyi/xHook/master/docs/overview/res/elfview.png)\n\n\n### .dynamic section\n\n这是一个十分重要和特殊的 section，其中包含了 ELF 中其他各个 section 的内存位置等信息。在执行视图中，总是会存在一个类型为 `PT_DYNAMIC` 的 segment，这个 segment 就包含了 .dynamic section 的内容。\n\n无论是执行 hook 操作时，还是动态链接器执行动态链接时，都需要通过 `PT_DYNAMIC` segment 来找到 .dynamic section 的内存位置，再进一步读取其他各项 section 的信息。\n\nlibtest.so 的 .dynamic section：\n\n```\ncaikelun@debian:~$ arm-linux-androideabi-readelf -d ./libtest.so \n\nDynamic section at offset 0x2e48 contains 30 entries:\n  Tag        Type                         Name/Value\n 0x00000003 (PLTGOT)                     0x3f7c\n 0x00000002 (PLTRELSZ)                   240 (bytes)\n 0x00000017 (JMPREL)                     0xcb8\n 0x00000014 (PLTREL)                     REL\n 0x00000011 (REL)                        0xc78\n 0x00000012 (RELSZ)                      64 (bytes)\n 0x00000013 (RELENT)                     8 (bytes)\n 0x6ffffffa (RELCOUNT)                   3\n 0x00000006 (SYMTAB)                     0x1f0\n 0x0000000b (SYMENT)                     16 (bytes)\n 0x00000005 (STRTAB)                     0x590\n 0x0000000a (STRSZ)                      1201 (bytes)\n 0x00000004 (HASH)                       0xa44\n 0x00000001 (NEEDED)                     Shared library: [libc.so]\n 0x00000001 (NEEDED)                     Shared library: [libm.so]\n 0x00000001 (NEEDED)                     Shared library: [libstdc++.so]\n 0x00000001 (NEEDED)                     Shared library: [libdl.so]\n 0x0000000e (SONAME)                     Library soname: [libtest.so]\n 0x0000001a (FINI_ARRAY)                 0x3e3c\n 0x0000001c (FINI_ARRAYSZ)               8 (bytes)\n 0x00000019 (INIT_ARRAY)                 0x3e44\n 0x0000001b (INIT_ARRAYSZ)               4 (bytes)\n 0x0000001e (FLAGS)                      BIND_NOW\n 0x6ffffffb (FLAGS_1)                    Flags: NOW\n 0x6ffffff0 (VERSYM)                     0xbc8\n 0x6ffffffc (VERDEF)                     0xc3c\n 0x6ffffffd (VERDEFNUM)                  1\n 0x6ffffffe (VERNEED)                    0xc58\n 0x6fffffff (VERNEEDNUM)                 1\n 0x00000000 (NULL)                       0x0\n```\n\n\n## 动态链接器（linker）\n\n安卓中的动态链接器程序是 linker。源码在 [这里](https://android.googlesource.com/platform/bionic/+/master/linker/)。\n\n动态链接（比如执行 dlopen）的大致步骤是：\n\n1. 检查已加载的 ELF 列表。（如果 libtest.so 已经加载，就不再重复加载了，仅把 libtest.so 的引用计数加一，然后直接返回。）\n2. 从 libtest.so 的 .dynamic section 中读取 libtest.so 的外部依赖的 ELF 列表，从此列表中剔除已加载的 ELF，最后得到本次需要加载的 ELF 完整列表（包括 libtest.so 自身）。\n3. 逐个加载列表中的 ELF。加载步骤：\n    * 用 `mmap` 预留一块足够大的内存，用于后续映射 ELF。（`MAP_PRIVATE` 方式）\n    * 读 ELF 的 PHT，用 `mmap` 把所有类型为 `PT_LOAD` 的 segment 依次映射到内存中。\n    * 从 .dynamic segment 中读取各信息项，主要是各个 section 的虚拟内存相对地址，然后计算并保存各个 section 的虚拟内存绝对地址。\n    * 执行重定位操作（relocate），这是最关键的一步。重定位信息可能存在于下面的一个或多个 secion 中：`.rel.plt`, `.rela.plt`, `.rel.dyn`, `.rela.dyn`, `.rel.android`, `.rela.android`。动态链接器需要逐个处理这些 `.relxxx` section 中的重定位诉求。根据已加载的 ELF 的信息，动态链接器查找所需符号的地址（比如 libtest.so 的符号 `malloc`），找到后，将地址值填入 `.relxxx` 中指明的**目标地址**中，这些“**目标地址**”一般存在于`.got` 或 `.data` 中。\n    * ELF 的引用计数加一。\n4. 逐个调用列表中 ELF 的构造函数（constructor），这些构造函数的地址是之前从 .dynamic segment 中读取到的（类型为 `DT_INIT` 和 `DT_INIT_ARRAY`）。各 ELF 的构造函数是按照依赖关系逐层调用的，先调用被依赖 ELF 的构造函数，最后调用 libtest.so 自己的构造函数。（ELF 也可以定义自己的析构函数（destructor），在 ELF 被 unload 的时候会被自动调用）\n\n等一下！我们似乎发现了什么！再看一遍重定位操作（relocate）的部分。难道我们只要从这些 `.relxxx` 中获取到“**目标地址**”，然后在“**目标地址**”中重新填上一个新的函数地址，这样就完成 hook 了吗？也许吧。\n\n\n## 追踪\n\n静态分析验证一下还是很容易的。以 armeabi-v7a 架构的 libtest.so 为例。先看一下 say_hello 函数对应的汇编代码吧。\n\n```\ncaikelun@debian:~/$ arm-linux-androideabi-readelf -s ./libtest.so\n\nSymbol table '.dynsym' contains 58 entries:\n   Num:    Value  Size Type    Bind   Vis      Ndx Name\n     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND \n     1: 00000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_finalize@LIBC (2)\n     2: 00000000     0 FUNC    GLOBAL DEFAULT  UND snprintf@LIBC (2)\n     3: 00000000     0 FUNC    GLOBAL DEFAULT  UND malloc@LIBC (2)\n     4: 00000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_atexit@LIBC (2)\n     5: 00000000     0 FUNC    GLOBAL DEFAULT  UND printf@LIBC (2)\n     6: 00000f61    60 FUNC    GLOBAL DEFAULT   12 say_hello\n...............\n...............\n```\n\n找到了！`say_hello` 在地址 `f61`，对应的汇编指令体积为 `60`（10 进制）字节。用 objdump 查看 `say_hello` 的反汇编输出。\n\n```\ncaikelun@debian:~$ arm-linux-androideabi-objdump -D ./libtest.so\n...............\n...............\n00000f60 <say_hello@@Base>:\n     f60:   b5b0        push    {r4, r5, r7, lr}\n     f62:   af02        add r7, sp, #8\n     f64:   f44f 6080   mov.w   r0, #1024   ; 0x400\n     f68:   f7ff ef34   blx dd4 <malloc@plt>\n     f6c:   4604        mov r4, r0\n     f6e:   b16c        cbz r4, f8c <say_hello@@Base+0x2c>\n     f70:   a507        add r5, pc, #28 ; (adr r5, f90 <say_hello@@Base+0x30>)\n     f72:   a308        add r3, pc, #32 ; (adr r3, f94 <say_hello@@Base+0x34>)\n     f74:   4620        mov r0, r4\n     f76:   f44f 6180   mov.w   r1, #1024   ; 0x400\n     f7a:   462a        mov r2, r5\n     f7c:   f7ff ef30   blx de0 <snprintf@plt>\n     f80:   4628        mov r0, r5\n     f82:   4621        mov r1, r4\n     f84:   e8bd 40b0   ldmia.w sp!, {r4, r5, r7, lr}\n     f88:   f001 ba96   b.w 24b8 <_Unwind_GetTextRelBase@@Base+0x8>\n     f8c:   bdb0        pop {r4, r5, r7, pc}\n     f8e:   bf00        nop\n     f90:   7325        strb    r5, [r4, #12]\n     f92:   0000        movs    r0, r0\n     f94:   6568        str r0, [r5, #84]   ; 0x54\n     f96:   6c6c        ldr r4, [r5, #68]   ; 0x44\n     f98:   0a6f        lsrs    r7, r5, #9\n     f9a:   0000        movs    r0, r0\n...............\n...............\n```\n\n对 `malloc` 函数的调用对应于指令 `blx dd4`。跳转到了地址 `dd4`。看看这个地址里有什么吧：\n\n```\ncaikelun@debian:~$ arm-linux-androideabi-objdump -D ./libtest.so\n...............\n...............\n00000dd4 <malloc@plt>:\n dd4:   e28fc600    add ip, pc, #0, 12\n dd8:   e28cca03    add ip, ip, #12288  ; 0x3000\n ddc:   e5bcf1b4    ldr pc, [ip, #436]! ; 0x1b4\n...............\n...............\n```\n\n果然，跳转到了 `.plt` 中，经过了几次地址计算，最后跳转到了地址 `3f90` 中的值指向的地址处，`3f90` 是个函数指针。\n\n稍微解释一下：因为 arm 处理器使用 3 级流水线，所以第一条指令取到的 `pc` 的值是当前执行的指令地址 + `8`。\n于是：`dd4` + `8` + `3000` + `1b4` = `3f90`。\n\n地址 `3f90` 在哪里呢：\n\n```\ncaikelun@debian:~$ arm-linux-androideabi-objdump -D ./libtest.so\n...............\n...............\n00003f60 <.got>:\n    ...\n    3f70:   00002604    andeq   r2, r0, r4, lsl #12\n    3f74:   00002504    andeq   r2, r0, r4, lsl #10\n    ...\n    3f88:   00000da8    andeq   r0, r0, r8, lsr #27\n    3f8c:   00000da8    andeq   r0, r0, r8, lsr #27\n    3f90:   00000da8    andeq   r0, r0, r8, lsr #27\n...............\n...............\n```\n\n果然，在 `.got` 里。\n\n顺便再看一下 `.rel.plt`：\n\n```\ncaikelun@debian:~$ arm-linux-androideabi-readelf -r ./libtest.so\n\nRelocation section '.rel.plt' at offset 0xcb8 contains 30 entries:\n Offset     Info    Type            Sym.Value  Sym. Name\n00003f88  00000416 R_ARM_JUMP_SLOT   00000000   __cxa_atexit@LIBC\n00003f8c  00000116 R_ARM_JUMP_SLOT   00000000   __cxa_finalize@LIBC\n00003f90  00000316 R_ARM_JUMP_SLOT   00000000   malloc@LIBC\n...............\n...............\n```\n\n`malloc` 的地址居然正好存放在 `3f90` 里，这绝对不是巧合啊！还等什么，赶紧改代码吧。我们的 main.c 应该改成这样：\n\n```c\n#include <test.h>\n\nvoid *my_malloc(size_t size)\n{\n    printf(\"%zu bytes memory are allocated by libtest.so\\n\", size);\n    return malloc(size);\n}\n\nint main()\n{\n    void **p = (void **)0x3f90;\n    *p = (void *)my_malloc; // do hook\n    \n    say_hello();\n    return 0;\n}\n```\n\n编译运行一下：\n\n```\ncaikelun@debian:~$ adb push ./main /data/local/tmp\ncaikelun@debian:~$ adb shell \"chmod +x /data/local/tmp/main\"\ncaikelun@debian:~$ adb shell \"export LD_LIBRARY_PATH=/data/local/tmp; /data/local/tmp/main\"\nSegmentation fault\ncaikelun@debian:~$\n```\n\n思路是正确的。但之所以还是失败了，是因为这段代码存在下面的 3 个问题：\n\n1. `3f90` 是个相对内存地址，需要把它换算成绝对地址。\n2. `3f90` 对应的绝对地址很可能没有写入权限，直接对这个地址赋值会引起段错误。\n3. 新的函数地址即使赋值成功了，`my_malloc` 也不会被执行，因为处理器有指令缓存（instruction cache）。\n\n我们需要解决这些问题。\n\n\n## 内存\n\n\n### 基地址\n\n在进程的内存空间中，各种 ELF 的加载地址是随机的，只有在运行时才能拿到加载地址，也就是**基地址**。我们需要知道 ELF 的基地址，才能将相对地址换算成绝对地址。\n\n没有错，熟悉 Linux 开发的聪明的你一定知道，我们可以直接调用 `dl_iterate_phdr`。详细的定义见 [这里](http://man7.org/linux/man-pages/man3/dl_iterate_phdr.3.html)。\n\n嗯，先等等，多年的 Android 开发被坑经历告诉我们，还是再看一眼 NDK 里的 `linker.h` 头文件吧：\n\n```c\n#if defined(__arm__)\n\n#if __ANDROID_API__ >= 21\nint dl_iterate_phdr(int (*__callback)(struct dl_phdr_info*, size_t, void*), void* __data) __INTRODUCED_IN(21);\n#endif /* __ANDROID_API__ >= 21 */\n\n#else\nint dl_iterate_phdr(int (*__callback)(struct dl_phdr_info*, size_t, void*), void* __data);\n#endif\n```\n\n为什么？！ARM 架构的 Android 5.0 以下版本居然不支持 `dl_iterate_phdr`！我们的 APP 可是要支持 Android 4.0 以上的所有版本啊。特别是 ARM，怎么能不支持呢？！这还让不让人写代码啦！\n\n幸运的是，我们想到了，我们还可以解析 `/proc/self/maps`:\n\n```\nroot@android:/ # ps | grep main\nps | grep main\nshell     7884  7882  2616   1016  hrtimer_na b6e83824 S /data/local/tmp/main\n\nroot@android:/ # cat /proc/7884/maps\ncat /proc/7884/maps\n\naddress           perms offset  dev   inode       pathname\n---------------------------------------------------------------------\n...........\n...........\nb6e42000-b6eb5000 r-xp 00000000 b3:17 57457      /system/lib/libc.so\nb6eb5000-b6eb9000 r--p 00072000 b3:17 57457      /system/lib/libc.so\nb6eb9000-b6ebc000 rw-p 00076000 b3:17 57457      /system/lib/libc.so\nb6ec6000-b6ec9000 r-xp 00000000 b3:19 753708     /data/local/tmp/libtest.so\nb6ec9000-b6eca000 r--p 00002000 b3:19 753708     /data/local/tmp/libtest.so\nb6eca000-b6ecb000 rw-p 00003000 b3:19 753708     /data/local/tmp/libtest.so\nb6f03000-b6f20000 r-xp 00000000 b3:17 32860      /system/bin/linker\nb6f20000-b6f21000 r--p 0001c000 b3:17 32860      /system/bin/linker\nb6f21000-b6f23000 rw-p 0001d000 b3:17 32860      /system/bin/linker\nb6f25000-b6f26000 r-xp 00000000 b3:19 753707     /data/local/tmp/main\nb6f26000-b6f27000 r--p 00000000 b3:19 753707     /data/local/tmp/main\nbecd5000-becf6000 rw-p 00000000 00:00 0          [stack]\nffff0000-ffff1000 r-xp 00000000 00:00 0          [vectors]\n...........\n...........\n```\n\nmaps 返回的是指定进程的内存空间中 `mmap` 的映射信息，包括各种动态库、可执行文件（如：linker），栈空间，堆空间，甚至还包括字体文件。maps 格式的详细说明见 [这里](http://man7.org/linux/man-pages/man5/proc.5.html)。\n\n我们的 libtest.so 在 maps 中有 3 行记录。offset 为 `0` 的第一行的起始地址 `b6ec6000` 在**绝大多数情况下**就是我们寻找的**基地址**。\n\n\n### 内存访问权限\n\nmaps 返回的信息中已经包含了权限访问信息。如果要执行 hook，就需要写入的权限，可以使用 `mprotect` 来完成：\n\n```c\n#include <sys/mman.h>\n\nint mprotect(void *addr, size_t len, int prot);\n```\n\n注意修改内存访问权限时，只能以“页”为单位。`mprotect` 的详细说明见 [这里](http://man7.org/linux/man-pages/man2/mprotect.2.html)。\n\n\n### 指令缓存\n\n注意 `.got` 和 `.data` 的 section 类型是 `PROGBITS`，也就是执行代码。处理器可能会对这部分数据做缓存。修改内存地址后，我们需要清除处理器的指令缓存，让处理器重新从内存中读取这部分指令。方法是调用 `__builtin___clear_cache`：\n\n```c\nvoid __builtin___clear_cache (char *begin, char *end);\n```\n\n注意清除指令缓存时，也只能以“页”为单位。`__builtin___clear_cache` 的详细说明见 [这里](https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html)。\n\n\n## 验证\n\n\n### 修改 main.c\n\n我们把 `main.c` 修改为：\n\n```c\n#include <inttypes.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <sys/mman.h>\n#include <test.h>\n\n#define PAGE_START(addr) ((addr) & PAGE_MASK)\n#define PAGE_END(addr)   (PAGE_START(addr) + PAGE_SIZE)\n\nvoid *my_malloc(size_t size)\n{\n    printf(\"%zu bytes memory are allocated by libtest.so\\n\", size);\n    return malloc(size);\n}\n\nvoid hook()\n{\n    char       line[512];\n    FILE      *fp;\n    uintptr_t  base_addr = 0;\n    uintptr_t  addr;\n\n    //find base address of libtest.so\n    if(NULL == (fp = fopen(\"/proc/self/maps\", \"r\"))) return;\n    while(fgets(line, sizeof(line), fp))\n    {\n        if(NULL != strstr(line, \"libtest.so\") &&\n           sscanf(line, \"%\"PRIxPTR\"-%*lx %*4s 00000000\", &base_addr) == 1)\n            break;\n    }\n    fclose(fp);\n    if(0 == base_addr) return;\n\n    //the absolute address\n    addr = base_addr + 0x3f90;\n    \n    //add write permission\n    mprotect((void *)PAGE_START(addr), PAGE_SIZE, PROT_READ | PROT_WRITE);\n\n    //replace the function address\n    *(void **)addr = my_malloc;\n\n    //clear instruction cache\n    __builtin___clear_cache((void *)PAGE_START(addr), (void *)PAGE_END(addr));\n}\n\nint main()\n{\n    hook();\n    \n    say_hello();\n    return 0;\n}\n```\n\n重新编译运行：\n\n```\ncaikelun@debian:~$ adb push ./main /data/local/tmp\ncaikelun@debian:~$ adb shell \"chmod +x /data/local/tmp/main\"\ncaikelun@debian:~$ adb shell \"export LD_LIBRARY_PATH=/data/local/tmp; /data/local/tmp/main\"\n1024 bytes memory are allocated by libtest.so\nhello\ncaikelun@debian:~$\n```\n\n是的，成功了！我们并没有修改 libtest.so 的代码，甚至没有重新编译它。我们仅仅修改了 main 程序。\n\nlibtest.so 和 main 的源码放在 github 上，可以从 [这里](https://github.com/iqiyi/xhook/tree/master/docs/overview/code) 获取到。（根据你使用的编译器不同，或者编译器的版本不同，生成的 libtest.so 中，也许 `malloc` 对应的地址不再是 `0x3f90`，这时你需要先用 readelf 确认，然后再到 `main.c` 中修改。）\n\n\n### 使用 xhook\n\n当然，我们已经开源了一个叫 xhook 的工具库。使用 xhook，你可以更优雅的完成对 libtest.so 的 hook 操作，也不必担心硬编码 `0x3f90` 导致的兼容性问题。\n\n```c\n#include <stdlib.h>\n#include <stdio.h>\n#include <test.h>\n#include <xhook.h>\n\nvoid *my_malloc(size_t size)\n{\n    printf(\"%zu bytes memory are allocated by libtest.so\\n\", size);\n    return malloc(size);\n}\n\nint main()\n{\n    xhook_register(\".*/libtest\\\\.so$\", \"malloc\", my_malloc, NULL);\n    xhook_refresh(0);\n    \n    say_hello();\n    return 0;\n}\n```\n\nxhook 支持 armeabi, armeabi-v7a 和 arm64-v8a。支持 Android 4.0 (含) 以上版本 (API level >= 14)。经过了产品级的稳定性和兼容性验证。可以在 [这里](https://github.com/iqiyi/xhook) 获取 `xhook`。\n\n总结一下 xhook 中执行 PLT hook 的流程：\n\n1. 读 maps，获取 ELF 的内存首地址（start address）。\n2. 验证 ELF 头信息。\n3. 从 PHT 中找到类型为 `PT_LOAD` 且 offset 为 `0` 的 segment。计算 ELF 基地址。\n4. 从 PHT 中找到类型为 `PT_DYNAMIC` 的 segment，从中获取到 `.dynamic` section，从 `.dynamic` section中获取其他各项 section 对应的内存地址。\n5. 在 `.dynstr` section 中找到需要 hook 的 symbol 对应的 index 值。\n6. 遍历所有的 `.relxxx` section（重定位 section），查找 symbol index 和 symbol type 都匹配的项，对于这项重定位项，执行 hook 操作。hook 流程如下：\n    * 读 maps，确认当前 hook 地址的内存访问权限。\n    * 如果权限不是可读也可写，则用 `mprotect` 修改访问权限为可读也可写。\n    * 如果调用方需要，就保留 hook 地址当前的值，用于返回。\n    * 将 hook 地址的值替换为新的值。（执行 hook）\n    * 如果之前用 `mprotect` 修改过内存访问权限，现在还原到之前的权限。\n    * 清除 hook 地址所在内存页的处理器指令缓存。\n\n\n## FAQ\n\n\n### 可以直接从文件中读取 ELF 信息吗？\n\n可以。而且对于格式解析来说，读文件是最稳妥的方式，因为 ELF 在运行时，原理上有很多 section 不需要一直保留在内存中，可以在加载完之后就从内存中丢弃，这样可以节省少量的内存。但是从实践的角度出发，各种平台的动态链接器和加载器，都不会这么做，可能它们认为增加的复杂度得不偿失。所以我们从内存中读取各种 ELF 信息就可以了，读文件反而增加了性能损耗。另外，某些系统库 ELF 文件，APP 也不一定有访问权限。\n\n\n### 计算基地址的精确方法是什么？\n\n正如你已经注意到的，前面介绍 libtest.so 基地址获取时，为了简化概念和编码方便，用了“**绝大多数情况下**”这种不应该出现的描述方式。对于 hook 来说，精确的基地址计算流程是：\n\n1. 在 maps 中找到找到 offset 为 `0`，且 `pathname` 为目标 ELF 的行。保存该行的 start address 为 `p0`。\n2. 找出 ELF 的 PHT 中第一个类型为 `PT_LOAD` 且 offset 为 `0` 的 segment，保存该 segment 的虚拟内存相对地址（`p_vaddr`）为 `p1`。\n3. `p0` - `p1` 即为该 ELF 当前的基地址。\n\n绝大多数的 ELF 第一个 `PT_LOAD` segment 的 `p_vaddr` 都是 `0`。\n\n另外，之所以要在 maps 里找 offset 为 `0` 的行，是因为我们在执行 hook 之前，希望对内存中的 ELF 文件头进行校验，确保当前操作的是一个有效的 ELF，而这种 ELF 文件头只能出现在 offset 为 `0` 的 mmap 区域。\n\n可以在 Android linker 的源码中搜索“load_bias”，可以找到很多详细的注释说明，也可以参考 linker 中对 `load_bias_` 变量的赋值程序逻辑。\n\n\n### 目标 ELF 使用的编译选项对 hook 有什么影响？\n\n会有一些影响。\n\n对于外部函数的调用，可以分为 3 中情况：\n\n1. 直接调用。无论编译选项如何，都可以被 hook 到。外部函数地址始终保存在 `.got` 中。\n2. 通过全局函数指针调用。无论编译选项如何，都可以被 hook 到。外部函数地址始终保存在 `.data` 中。\n3. 通过局部函数指针调用。如果编译选项为 -O2（默认值），调用将被优化为直接调用（同情况 1）。如果编译选项为 -O0，则在执行 hook 前已经被赋值到临时变量中的外部函数的指针，通过 PLT 方式无法 hook；对于执行 hook 之后才被赋值的，可以通过 PLT 方式 hook。\n\n一般情况下，产品级的 ELF 很少会使用 -O0 进行编译，所以也不必太纠结。但是如果你希望你的 ELF 尽量不被别人 PLT hook，那可以试试使用 -O0 来编译，然后尽量早的将外部函数的指针赋值给局部函数指针变量，之后一直使用这些局部函数指针来访问外部函数。\n\n总之，查看 C/C++ 的源代码对这个问题的理解没有意义，需要查看使用不同的编译选项后，生成的 ELF 的反汇编输出，比较它们的区别，才能知道哪些情况由于什么原因导致无法被 PLT hook。\n\n\n### hook 时遇到偶发的段错误是什么原因？如何处理？\n\n我们有时会遇到这样的问题：\n\n* 读取 `/proc/self/maps` 后发现某个内存区域的访问权限为**可读**，当我们读取该区域的内容做 ELF 文件头校验时，发生了段错误（sig: SIGSEGV, code: SEGV_ACCERR）。\n* 已经用 `mprotect()` 修改了某个内存区域的访问权限为**可写**，`mprotect()` 返回修改成功，然后再次读取 `/proc/self/maps` 确认对应内存区域的访问权限确实为**可写**，执行写入操作（替换函数指针，执行 hook）时发生段错误（sig: SIGSEGV, code: SEGV_ACCERR）。\n* 读取和验证 ELF 文件头成功了，根据 ELF 头中的相对地址值，进一步读取 PHT 或者 `.dynamic` section 时发生段错误（sig: SIGSEGV, code: SEGV_ACCERR 或 SEGV_MAPERR）。\n\n可能的原因是：\n\n* 进程的内存空间是多线程共享的，我们在执行 hook 时，其他线程（甚至 linker）可能正在执行 `dlclose()`，或者正在用 `mprotect()` 修改这块内存区域的访问权限。\n* 不同厂家、机型、版本的 Android ROM 可能有未公开的行为，比如在某些情况下对某些内存区域存在**写保护**或者**读保护**机制，而这些保护机制并不反应在 `/proc/self/maps` 的内容中。\n\n问题分析：\n\n* 读内存时发生段错误其实是无害的。\n* 我在 hook 执行的流程中，需要直接通过计算内存地址的方式来写入数据的地方只有一处：即替换函数指针的最关键的那一行。只要其他地方的逻辑没有错误，这里就算写入失败了，也不会对其他内存区域造成破坏。\n* 加载运行安卓平台的 APP 进程时，加载器已经向我们注入了 signal handler 的注册逻辑，以便 APP 崩溃时与系统的 `debuggerd` 守护进程通讯，`debuggerd` 使用 `ptrace` 调试崩溃进程，获取需要的崩溃现场信息，记录到 tombstone 文件中，然后 APP 自杀。\n* 系统会精确的把段错误信号发送给“发生段错误的线程”。\n* 我们希望能有一种隐秘的，且可控的方式来避免段错误引起 APP 崩溃。\n\n先明确一个观点：不要只从应用层程序开发的角度来看待段错误，段错误不是洪水猛兽，它只是内核与用户进程的一种正常的交流方式。当用户进程访问了无权限或未 mmap 的虚拟内存地址时，内核向用户进程发送 SIGSEGV 信号，来通知用户进程，仅此而已。只要段错误的发生位置是可控的，我们就可以在用户进程中处理它。\n\n解决方案：\n\n* 当 hook 逻辑进入我们认为的危险区域（直接计算内存地址进行读写）之前，通过一个全局 `flag` 来进行标记，离开危险区域后将 `flag` 复位。\n* 注册我们自己的 signal handler，只捕获段错误。在 signal handler 中，通过判断 `flag` 的值，来判断当前线程逻辑是否在危险区域中。如果是，就用 `siglongjmp` 跳出 signal handler，直接跳到我们预先设置好的“危险区域以外的下一行代码处”；如果不是，就恢复之前加载器向我们注入的 signal handler，然后直接返回，这时系统会再次向我们的线程发送段错误信号，由于已经恢复了之前的 signal handler，这时会进入默认的系统 signal handler 中走正常逻辑。\n* 我们把这种机制简称为：SFP (segmentation fault protection，段错误保护)\n* 注意：SFP需要一个开关，让我们随时能够开启和关闭它。在 APP 开发调试阶段，SFP 应该始终被关闭，这样就不会错过由于编码失误导致的段错误，这些错误是应该被修复的；在正式上线后 SFP 应该被开启，这样能保证 APP 不会崩溃。（当然，以采样的形式部分关闭 SFP，用以观察和分析 hook 机制本身导致的崩溃，也是可以考虑的）\n\n具体代码可以参考 `xhook` 中的实现，在源码中搜索 `siglongjmp` 和 `sigsetjmp`。\n\n\n### ELF 内部函数之间的调用能 hook 吗？\n\n\n我们这里介绍的 hook 方式为 PLT hook，不能做 ELF 内部函数之间调用的 hook。\n\ninline hook 可以做到，你需要先知道想要 hook 的内部函数符号名（symbol name）或者地址，然后可以 hook。\n\n有很多开源和非开源的 inline hook 实现，比如：\n\n* substrate：http://www.cydiasubstrate.com/\n* frida：https://www.frida.re/\n\ninline hook 方案强大的同时可能带来以下的问题：\n\n* 由于需要直接解析和修改 ELF 中的机器指令（汇编码），对于不同架构的处理器、处理器指令集、编译器优化选项、操作系统版本可能存在不同的兼容性和稳定性问题。\n* 发生问题后可能难以分析和定位，一些知名的 inline hook 方案是闭源的。\n* 实现起来相对复杂，难度也较大。\n* 未知的坑相对较多，这个可以自行 google。\n\n建议如果 PLT hook 够用的话，就不必尝试 inline hook 了。\n\n\n## 联系作者\n\n\n* caikelun@gmail.com\n* https://github.com/caikelun\n\n\n## 许可证\n\n\nCopyright (c) 2018, 爱奇艺, Inc. All rights reserved.\n\n本文使用 [Creative Commons 许可证](https://creativecommons.org/licenses/by/4.0/legalcode) 授权。\n"
  },
  {
    "path": "docs/overview/code/.gitignore",
    "content": ".DS_Store\n\nmain/libs/\nmain/obj/\nlibtest/libs/\nlibtest/obj/\n"
  },
  {
    "path": "docs/overview/code/build.sh",
    "content": "#!/bin/bash\n\nndk-build -C ./libtest/jni\nndk-build -C ./main/jni\n"
  },
  {
    "path": "docs/overview/code/clean.sh",
    "content": "#!/bin/bash\n\nndk-build -C ./main/jni clean\nndk-build -C ./libtest/jni clean\n"
  },
  {
    "path": "docs/overview/code/libtest/jni/Android.mk",
    "content": "LOCAL_PATH := $(call my-dir)\n\ninclude $(CLEAR_VARS)\nLOCAL_MODULE     := test\nLOCAL_SRC_FILES  := test.c\nLOCAL_CFLAGS     := -Wall -Wextra -Werror #-O0\nLOCAL_CONLYFLAGS := -std=c11\ninclude $(BUILD_SHARED_LIBRARY)\n"
  },
  {
    "path": "docs/overview/code/libtest/jni/Application.mk",
    "content": "APP_ABI      := armeabi-v7a\nAPP_PLATFORM := android-14\n"
  },
  {
    "path": "docs/overview/code/libtest/jni/test.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n\nvoid say_hello()\n{\n    char *buf = malloc(1024);\n    if(NULL != buf)\n    {\n        snprintf(buf, 1024, \"%s\", \"hello\\n\");\n        printf(\"%s\", buf);\n    }\n}\n"
  },
  {
    "path": "docs/overview/code/libtest/jni/test.h",
    "content": "#ifndef TEST_H\n#define TEST_H 1\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid say_hello();\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "docs/overview/code/main/jni/Android.mk",
    "content": "LOCAL_PATH := $(call my-dir)\n\ninclude $(CLEAR_VARS)\nLOCAL_MODULE            := test\nLOCAL_SRC_FILES         := $(LOCAL_PATH)/../../libtest/libs/$(TARGET_ARCH_ABI)/libtest.so\nLOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../libtest/jni\ninclude $(PREBUILT_SHARED_LIBRARY)\n\ninclude $(CLEAR_VARS)\nLOCAL_MODULE            := main\nLOCAL_SRC_FILES         := main.c\nLOCAL_SHARED_LIBRARIES  := test\nLOCAL_CFLAGS            := -Wall -Wextra -Werror -fPIE\nLOCAL_CONLYFLAGS        := -std=c11\nLOCAL_LDLIBS            += -fPIE -pie\ninclude $(BUILD_EXECUTABLE)\n"
  },
  {
    "path": "docs/overview/code/main/jni/Application.mk",
    "content": "APP_ABI      := armeabi-v7a\nAPP_PLATFORM := android-14\n"
  },
  {
    "path": "docs/overview/code/main/jni/main.c",
    "content": "#include <inttypes.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <sys/mman.h>\n#include <test.h>\n\n#define PAGE_START(addr) ((addr) & PAGE_MASK)\n#define PAGE_END(addr)   (PAGE_START(addr) + PAGE_SIZE)\n\nvoid *my_malloc(size_t size)\n{\n    printf(\"%zu bytes memory are allocated by libtest.so\\n\", size);\n    return malloc(size);\n}\n\nvoid hook()\n{\n    char       line[512];\n    FILE      *fp;\n    uintptr_t  base_addr = 0;\n    uintptr_t  addr;\n\n    //find base address of libtest.so\n    if(NULL == (fp = fopen(\"/proc/self/maps\", \"r\"))) return;\n    while(fgets(line, sizeof(line), fp))\n    {\n        if(NULL != strstr(line, \"libtest.so\") &&\n           sscanf(line, \"%\"PRIxPTR\"-%*lx %*4s 00000000\", &base_addr) == 1)\n            break;\n    }\n    fclose(fp);\n    if(0 == base_addr) return;\n\n    //the absolute address\n    addr = base_addr + 0x3f90;\n    \n    //add write permission\n    mprotect((void *)PAGE_START(addr), PAGE_SIZE, PROT_READ | PROT_WRITE);\n\n    //replace the function address\n    *(void **)addr = my_malloc;\n\n    //clear instruction cache\n    __builtin___clear_cache((void *)PAGE_START(addr), (void *)PAGE_END(addr));\n}\n\nint main()\n{\n    hook();\n    \n    say_hello();\n    return 0;\n}\n"
  },
  {
    "path": "docs/overview/code/run.sh",
    "content": "#!/bin/bash\n\nadb push ./main/libs/armeabi-v7a/libtest.so ./main/libs/armeabi-v7a/main /data/local/tmp\nadb shell \"chmod +x /data/local/tmp/main\"\nadb shell \"export LD_LIBRARY_PATH=/data/local/tmp; /data/local/tmp/main\"\n"
  },
  {
    "path": "install_libs.sh",
    "content": "#!/bin/bash\n\nmkdir -p ./xhookwrapper/xhook/libs/armeabi\nmkdir -p ./xhookwrapper/xhook/libs/armeabi-v7a\nmkdir -p ./xhookwrapper/xhook/libs/arm64-v8a\nmkdir -p ./xhookwrapper/xhook/libs/x86\nmkdir -p ./xhookwrapper/xhook/libs/x86_64\n\ncp -f ./libxhook/libs/armeabi/libxhook.so     ./xhookwrapper/xhook/libs/armeabi/\ncp -f ./libxhook/libs/armeabi-v7a/libxhook.so ./xhookwrapper/xhook/libs/armeabi-v7a/\ncp -f ./libxhook/libs/arm64-v8a/libxhook.so   ./xhookwrapper/xhook/libs/arm64-v8a/\ncp -f ./libxhook/libs/x86/libxhook.so         ./xhookwrapper/xhook/libs/x86/\ncp -f ./libxhook/libs/x86_64/libxhook.so      ./xhookwrapper/xhook/libs/x86_64/\n\nmkdir -p ./xhookwrapper/biz/libs/armeabi\nmkdir -p ./xhookwrapper/biz/libs/armeabi-v7a\nmkdir -p ./xhookwrapper/biz/libs/arm64-v8a\nmkdir -p ./xhookwrapper/biz/libs/x86\nmkdir -p ./xhookwrapper/biz/libs/x86_64\n\ncp -f ./libbiz/libs/armeabi/libbiz.so         ./xhookwrapper/biz/libs/armeabi/\ncp -f ./libbiz/libs/armeabi-v7a/libbiz.so     ./xhookwrapper/biz/libs/armeabi-v7a/\ncp -f ./libbiz/libs/arm64-v8a/libbiz.so       ./xhookwrapper/biz/libs/arm64-v8a/\ncp -f ./libbiz/libs/x86/libbiz.so             ./xhookwrapper/biz/libs/x86/\ncp -f ./libbiz/libs/x86_64/libbiz.so          ./xhookwrapper/biz/libs/x86_64/\n\nmkdir -p ./xhookwrapper/app/libs/armeabi\nmkdir -p ./xhookwrapper/app/libs/armeabi-v7a\nmkdir -p ./xhookwrapper/app/libs/arm64-v8a\nmkdir -p ./xhookwrapper/app/libs/x86\nmkdir -p ./xhookwrapper/app/libs/x86_64\n\ncp -f ./libtest/libs/armeabi/libtest.so       ./xhookwrapper/app/libs/armeabi/\ncp -f ./libtest/libs/armeabi-v7a/libtest.so   ./xhookwrapper/app/libs/armeabi-v7a/\ncp -f ./libtest/libs/arm64-v8a/libtest.so     ./xhookwrapper/app/libs/arm64-v8a/\ncp -f ./libtest/libs/x86/libtest.so           ./xhookwrapper/app/libs/x86/\ncp -f ./libtest/libs/x86_64/libtest.so        ./xhookwrapper/app/libs/x86_64/\n"
  },
  {
    "path": "libbiz/jni/Android.mk",
    "content": "LOCAL_PATH := $(call my-dir)\n\ninclude $(CLEAR_VARS)\nLOCAL_MODULE            := xhook\nLOCAL_SRC_FILES         := $(LOCAL_PATH)/../../libxhook/libs/$(TARGET_ARCH_ABI)/libxhook.so\nLOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../libxhook/jni\ninclude $(PREBUILT_SHARED_LIBRARY)\n\ninclude $(CLEAR_VARS)\nLOCAL_MODULE            := biz\nLOCAL_SRC_FILES         := biz.c\nLOCAL_SHARED_LIBRARIES  := xhook\nLOCAL_CFLAGS            := -Wall -Wextra -Werror\nLOCAL_CONLYFLAGS        := -std=c11\nLOCAL_LDLIBS            := -llog\ninclude $(BUILD_SHARED_LIBRARY)\n"
  },
  {
    "path": "libbiz/jni/Application.mk",
    "content": "APP_ABI      := armeabi armeabi-v7a arm64-v8a x86 x86_64\nAPP_PLATFORM := android-14\n"
  },
  {
    "path": "libbiz/jni/biz.c",
    "content": "#include <stdio.h>\n#include <unistd.h>\n#include <pthread.h>\n#include <jni.h>\n#include <android/log.h>\n#include \"xhook.h\"\n\nstatic int my_system_log_print(int prio, const char* tag, const char* fmt, ...)\n{\n    va_list ap;\n    char buf[1024];\n    int r;\n    \n    snprintf(buf, sizeof(buf), \"[%s] %s\", (NULL == tag ? \"\" : tag), (NULL == fmt ? \"\" : fmt));\n\n    va_start(ap, fmt);\n    r = __android_log_vprint(prio, \"xhook_system\", buf, ap);\n    va_end(ap);\n    return r;\n}\n\nstatic int my_libtest_log_print(int prio, const char* tag, const char* fmt, ...)\n{\n    va_list ap;\n    char buf[1024];\n    int r;\n    \n    snprintf(buf, sizeof(buf), \"[%s] %s\", (NULL == tag ? \"\" : tag), (NULL == fmt ? \"\" : fmt));\n\n    va_start(ap, fmt);\n    r = __android_log_vprint(prio, \"xhook_libtest\", buf, ap);\n    va_end(ap);\n    return r;\n}\n\nvoid Java_com_qiyi_biz_NativeHandler_start(JNIEnv* env, jobject obj)\n{\n    (void)env;\n    (void)obj;\n\n    xhook_register(\"^/system/.*\\\\.so$\",  \"__android_log_print\", my_system_log_print,  NULL);\n    xhook_register(\"^/vendor/.*\\\\.so$\",  \"__android_log_print\", my_system_log_print,  NULL);\n    xhook_register(\".*/libtest\\\\.so$\", \"__android_log_print\", my_libtest_log_print, NULL);\n\n    //just for testing\n    xhook_ignore(\".*/liblog\\\\.so$\", \"__android_log_print\"); //ignore __android_log_print in liblog.so\n    xhook_ignore(\".*/libjavacore\\\\.so$\", NULL); //ignore all hooks in libjavacore.so\n}\n"
  },
  {
    "path": "libtest/jni/Android.mk",
    "content": "LOCAL_PATH := $(call my-dir)\n\ninclude $(CLEAR_VARS)\nLOCAL_MODULE     := test\nLOCAL_SRC_FILES  := test.c\nLOCAL_CFLAGS     := -Wall -Wextra -Werror -O0\nLOCAL_CONLYFLAGS := -std=c11\nLOCAL_LDLIBS     := -llog\ninclude $(BUILD_SHARED_LIBRARY)\n"
  },
  {
    "path": "libtest/jni/Application.mk",
    "content": "APP_ABI      := armeabi armeabi-v7a arm64-v8a x86 x86_64\nAPP_PLATFORM := android-14\n"
  },
  {
    "path": "libtest/jni/test.c",
    "content": "#include <unistd.h>\n#include <pthread.h>\n#include <jni.h>\n#include <android/log.h>\n\ntypedef int (*my_log_t)(int prio, const char* tag, const char* fmt, ...);\nmy_log_t my_global_log_ptr = (my_log_t)__android_log_print;\n\nstatic void *new_thread_func(void *arg)\n{\n    (void)arg;\n    my_log_t my_local_log_ptr2 = (my_log_t)__android_log_print;\n    unsigned int i = 0;\n    \n    while(1)\n    {\n        my_log_t my_local_log_ptr = (my_log_t)__android_log_print;\n        __android_log_print(ANDROID_LOG_DEBUG, \"mytest\", \"call directly. %u\\n\", i);\n        my_global_log_ptr(ANDROID_LOG_DEBUG, \"mytest\", \"call from global ptr. %u\\n\", i);\n        my_local_log_ptr(ANDROID_LOG_DEBUG, \"mytest\", \"call from local ptr. %u\\n\", i);\n        my_local_log_ptr2(ANDROID_LOG_DEBUG, \"mytest\", \"call from local ptr2. %u (definitely failed when compiled with -O0)\\n\", i);\n        i++;\n        sleep(1);\n    }\n    \n    return NULL;\n}\n\nvoid Java_com_qiyi_test_NativeHandler_start(JNIEnv* env, jobject obj)\n{\n    (void)env;\n    (void)obj;\n    \n    pthread_t tid;\n    pthread_create(&tid, NULL, &new_thread_func, NULL);\n}\n"
  },
  {
    "path": "libxhook/jni/Android.mk",
    "content": "LOCAL_PATH := $(call my-dir)\n\ninclude $(CLEAR_VARS)\nLOCAL_MODULE     := xhook\nLOCAL_SRC_FILES  := xhook.c \\\n                    xh_core.c \\\n                    xh_elf.c \\\n                    xh_jni.c \\\n                    xh_log.c \\\n                    xh_util.c \\\n                    xh_version.c\nLOCAL_C_INCLUDES := $(LOCAL_PATH)\nLOCAL_CFLAGS     := -Wall -Wextra -Werror -fvisibility=hidden\nLOCAL_CONLYFLAGS := -std=c11\nLOCAL_LDLIBS     := -llog\ninclude $(BUILD_SHARED_LIBRARY)\n"
  },
  {
    "path": "libxhook/jni/Application.mk",
    "content": "APP_ABI      := armeabi armeabi-v7a arm64-v8a x86 x86_64\nAPP_PLATFORM := android-14\n"
  },
  {
    "path": "libxhook/jni/queue.h",
    "content": "/*-\n * Copyright (c) 1991, 1993\n *      The Regents of the University of California.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the University nor the names of its contributors\n *    may be used to endorse or promote products derived from this software\n *    without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n *\n *      @(#)queue.h     8.5 (Berkeley) 8/20/94\n * $FreeBSD: stable/9/sys/sys/queue.h 252365 2013-06-29 04:25:40Z lstewart $\n */\n\n#ifndef QUEUE_H\n#define QUEUE_H\n\n/* #include <sys/cdefs.h> */\n#define __containerof(ptr, type, field) ((type *)((char *)(ptr) - ((char *)&((type *)0)->field)))\n\n/*\n * This file defines four types of data structures: singly-linked lists,\n * singly-linked tail queues, lists and tail queues.\n *\n * A singly-linked list is headed by a single forward pointer. The elements\n * are singly linked for minimum space and pointer manipulation overhead at\n * the expense of O(n) removal for arbitrary elements. New elements can be\n * added to the list after an existing element or at the head of the list.\n * Elements being removed from the head of the list should use the explicit\n * macro for this purpose for optimum efficiency. A singly-linked list may\n * only be traversed in the forward direction.  Singly-linked lists are ideal\n * for applications with large datasets and few or no removals or for\n * implementing a LIFO queue.\n *\n * A singly-linked tail queue is headed by a pair of pointers, one to the\n * head of the list and the other to the tail of the list. The elements are\n * singly linked for minimum space and pointer manipulation overhead at the\n * expense of O(n) removal for arbitrary elements. New elements can be added\n * to the list after an existing element, at the head of the list, or at the\n * end of the list. Elements being removed from the head of the tail queue\n * should use the explicit macro for this purpose for optimum efficiency.\n * A singly-linked tail queue may only be traversed in the forward direction.\n * Singly-linked tail queues are ideal for applications with large datasets\n * and few or no removals or for implementing a FIFO queue.\n *\n * A list is headed by a single forward pointer (or an array of forward\n * pointers for a hash table header). The elements are doubly linked\n * so that an arbitrary element can be removed without a need to\n * traverse the list. New elements can be added to the list before\n * or after an existing element or at the head of the list. A list\n * may be traversed in either direction.\n *\n * A tail queue is headed by a pair of pointers, one to the head of the\n * list and the other to the tail of the list. The elements are doubly\n * linked so that an arbitrary element can be removed without a need to\n * traverse the list. New elements can be added to the list before or\n * after an existing element, at the head of the list, or at the end of\n * the list. A tail queue may be traversed in either direction.\n *\n * For details on the use of these macros, see the queue(3) manual page.\n *\n *                              SLIST   LIST    STAILQ  TAILQ\n * _HEAD                        +       +       +       +\n * _HEAD_INITIALIZER            +       +       +       +\n * _ENTRY                       +       +       +       +\n * _INIT                        +       +       +       +\n * _EMPTY                       +       +       +       +\n * _FIRST                       +       +       +       +\n * _NEXT                        +       +       +       +\n * _PREV                        -       +       -       +\n * _LAST                        -       -       +       +\n * _FOREACH                     +       +       +       +\n * _FOREACH_FROM                +       +       +       +\n * _FOREACH_SAFE                +       +       +       +\n * _FOREACH_FROM_SAFE           +       +       +       +\n * _FOREACH_REVERSE             -       -       -       +\n * _FOREACH_REVERSE_FROM        -       -       -       +\n * _FOREACH_REVERSE_SAFE        -       -       -       +\n * _FOREACH_REVERSE_FROM_SAFE   -       -       -       +\n * _INSERT_HEAD                 +       +       +       +\n * _INSERT_BEFORE               -       +       -       +\n * _INSERT_AFTER                +       +       +       +\n * _INSERT_TAIL                 -       -       +       +\n * _CONCAT                      -       -       +       +\n * _REMOVE_AFTER                +       -       +       -\n * _REMOVE_HEAD                 +       -       +       -\n * _REMOVE                      +       +       +       +\n * _SWAP                        +       +       +       +\n *\n */\n\n/*\n * Singly-linked List declarations.\n */\n#define SLIST_HEAD(name, type, qual)                                    \\\n    struct name {                                                       \\\n        struct type *qual slh_first; /* first element */                \\\n    }\n\n#define SLIST_HEAD_INITIALIZER(head)                                    \\\n    { NULL }\n\n#define SLIST_ENTRY(type, qual)                                         \\\n    struct {                                                            \\\n        struct type *qual sle_next;  /* next element */                 \\\n    }\n\n/*\n * Singly-linked List functions.\n */\n#define SLIST_INIT(head) do {                                           \\\n        SLIST_FIRST((head)) = NULL;                                     \\\n    } while (0)\n\n#define SLIST_EMPTY(head) ((head)->slh_first == NULL)\n\n#define SLIST_FIRST(head) ((head)->slh_first)\n\n#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)\n\n#define SLIST_FOREACH(var, head, field)                                 \\\n    for ((var) = SLIST_FIRST((head));                                   \\\n         (var);                                                         \\\n         (var) = SLIST_NEXT((var), field))\n\n#define SLIST_FOREACH_FROM(var, head, field)                            \\\n    for ((var) = ((var) ? (var) : SLIST_FIRST((head)));                 \\\n         (var);                                                         \\\n         (var) = SLIST_NEXT((var), field))\n\n#define SLIST_FOREACH_SAFE(var, head, field, tvar)                      \\\n    for ((var) = SLIST_FIRST((head));                                   \\\n         (var) && ((tvar) = SLIST_NEXT((var), field), 1);               \\\n         (var) = (tvar))\n\n#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar)                 \\\n    for ((var) = ((var) ? (var) : SLIST_FIRST((head)));                 \\\n         (var) && ((tvar) = SLIST_NEXT((var), field), 1);               \\\n         (var) = (tvar))\n\n#define SLIST_INSERT_HEAD(head, elm, field) do {                        \\\n        SLIST_NEXT((elm), field) = SLIST_FIRST((head));                 \\\n        SLIST_FIRST((head)) = (elm);                                    \\\n    } while (0)\n\n#define SLIST_INSERT_AFTER(slistelm, elm, field) do {                   \\\n        SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field);       \\\n        SLIST_NEXT((slistelm), field) = (elm);                          \\\n    } while (0)\n\n#define SLIST_REMOVE_AFTER(elm, field) do {                             \\\n        SLIST_NEXT(elm, field) =                                        \\\n            SLIST_NEXT(SLIST_NEXT(elm, field), field);                  \\\n    } while (0)\n\n#define SLIST_REMOVE_HEAD(head, field) do {                             \\\n        SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field);   \\\n    } while (0)\n\n#define SLIST_REMOVE(head, elm, type, field) do {                       \\\n        if (SLIST_FIRST((head)) == (elm)) {                             \\\n            SLIST_REMOVE_HEAD((head), field);                           \\\n        }                                                               \\\n        else {                                                          \\\n            struct type *curelm = SLIST_FIRST((head));                  \\\n            while (SLIST_NEXT(curelm, field) != (elm))                  \\\n                curelm = SLIST_NEXT(curelm, field);                     \\\n            SLIST_REMOVE_AFTER(curelm, field);                          \\\n        }                                                               \\\n    } while (0)\n\n#define SLIST_SWAP(head1, head2, type) do {                             \\\n        struct type *swap_first = SLIST_FIRST(head1);                   \\\n        SLIST_FIRST(head1) = SLIST_FIRST(head2);                        \\\n        SLIST_FIRST(head2) = swap_first;                                \\\n    } while (0)\n\n/*\n * List declarations.\n */\n#define LIST_HEAD(name, type, qual)                                     \\\n    struct name {                                                       \\\n        struct type *qual lh_first;  /* first element */                \\\n    }\n\n#define LIST_HEAD_INITIALIZER(head)                                     \\\n    { NULL }\n\n#define LIST_ENTRY(type, qual)                                          \\\n    struct {                                                            \\\n        struct type *qual le_next;   /* next element */                 \\\n        struct type *qual *le_prev;  /* address of previous next element */ \\\n    }\n\n/*\n * List functions.\n */\n#define LIST_INIT(head) do {                                            \\\n        LIST_FIRST((head)) = NULL;                                      \\\n    } while (0)\n\n#define LIST_EMPTY(head) ((head)->lh_first == NULL)\n\n#define LIST_FIRST(head) ((head)->lh_first)\n\n#define LIST_NEXT(elm, field) ((elm)->field.le_next)\n\n#define LIST_PREV(elm, head, type, field)                               \\\n    ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL :               \\\n     __containerof((elm)->field.le_prev, struct type, field.le_next))\n\n#define LIST_FOREACH(var, head, field)                                  \\\n    for ((var) = LIST_FIRST((head));                                    \\\n         (var);                                                         \\\n         (var) = LIST_NEXT((var), field))\n\n#define LIST_FOREACH_FROM(var, head, field)                             \\\n    for ((var) = ((var) ? (var) : LIST_FIRST((head)));                  \\\n         (var);                                                         \\\n         (var) = LIST_NEXT((var), field))\n\n#define LIST_FOREACH_SAFE(var, head, field, tvar)                       \\\n    for ((var) = LIST_FIRST((head));                                    \\\n         (var) && ((tvar) = LIST_NEXT((var), field), 1);                \\\n         (var) = (tvar))\n\n#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar)                  \\\n    for ((var) = ((var) ? (var) : LIST_FIRST((head)));                  \\\n         (var) && ((tvar) = LIST_NEXT((var), field), 1);                \\\n         (var) = (tvar))\n\n#define LIST_INSERT_HEAD(head, elm, field) do {                         \\\n        if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL)     \\\n            LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field); \\\n        LIST_FIRST((head)) = (elm);                                     \\\n        (elm)->field.le_prev = &LIST_FIRST((head));                     \\\n    } while (0)\n\n#define LIST_INSERT_BEFORE(listelm, elm, field) do {                    \\\n        (elm)->field.le_prev = (listelm)->field.le_prev;                \\\n        LIST_NEXT((elm), field) = (listelm);                            \\\n        *(listelm)->field.le_prev = (elm);                              \\\n        (listelm)->field.le_prev = &LIST_NEXT((elm), field);            \\\n    } while (0)\n\n#define LIST_INSERT_AFTER(listelm, elm, field) do {                     \\\n        if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL) \\\n            LIST_NEXT((listelm), field)->field.le_prev =                \\\n                &LIST_NEXT((elm), field);                               \\\n        LIST_NEXT((listelm), field) = (elm);                            \\\n        (elm)->field.le_prev = &LIST_NEXT((listelm), field);            \\\n    } while (0)\n\n#define LIST_REMOVE(elm, field) do {                                    \\\n        if (LIST_NEXT((elm), field) != NULL)                            \\\n            LIST_NEXT((elm), field)->field.le_prev =                    \\\n                (elm)->field.le_prev;                                   \\\n        *(elm)->field.le_prev = LIST_NEXT((elm), field);                \\\n    } while (0)\n\n#define LIST_SWAP(head1, head2, type, field) do {                       \\\n        struct type *swap_tmp = LIST_FIRST((head1));                    \\\n        LIST_FIRST((head1)) = LIST_FIRST((head2));                      \\\n        LIST_FIRST((head2)) = swap_tmp;                                 \\\n        if ((swap_tmp = LIST_FIRST((head1))) != NULL)                   \\\n            swap_tmp->field.le_prev = &LIST_FIRST((head1));             \\\n        if ((swap_tmp = LIST_FIRST((head2))) != NULL)                   \\\n            swap_tmp->field.le_prev = &LIST_FIRST((head2));             \\\n    } while (0)\n\n/*\n * Singly-linked Tail queue declarations.\n */\n#define STAILQ_HEAD(name, type, qual)                                   \\\n    struct name {                                                       \\\n        struct type *qual stqh_first;/* first element */                \\\n        struct type *qual *stqh_last;/* addr of last next element */    \\\n    }\n\n#define STAILQ_HEAD_INITIALIZER(head)                                   \\\n    { NULL, &(head).stqh_first }\n\n#define STAILQ_ENTRY(type, qual)                                        \\\n    struct {                                                            \\\n        struct type *qual stqe_next; /* next element */                 \\\n    }\n\n/*\n * Singly-linked Tail queue functions.\n */\n#define STAILQ_INIT(head) do {                                          \\\n        STAILQ_FIRST((head)) = NULL;                                    \\\n        (head)->stqh_last = &STAILQ_FIRST((head));                      \\\n    } while (0)\n\n#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)\n\n#define STAILQ_FIRST(head) ((head)->stqh_first)\n\n#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)\n\n#define STAILQ_LAST(head, type, field)                                  \\\n    (STAILQ_EMPTY((head)) ? NULL :                                      \\\n     __containerof((head)->stqh_last, struct type, field.stqe_next))\n\n#define STAILQ_FOREACH(var, head, field)                                \\\n    for((var) = STAILQ_FIRST((head));                                   \\\n        (var);                                                          \\\n        (var) = STAILQ_NEXT((var), field))\n\n#define STAILQ_FOREACH_FROM(var, head, field)                           \\\n    for ((var) = ((var) ? (var) : STAILQ_FIRST((head)));                \\\n         (var);                                                         \\\n         (var) = STAILQ_NEXT((var), field))\n\n#define STAILQ_FOREACH_SAFE(var, head, field, tvar)                     \\\n    for ((var) = STAILQ_FIRST((head));                                  \\\n         (var) && ((tvar) = STAILQ_NEXT((var), field), 1);              \\\n         (var) = (tvar))\n\n#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar)                \\\n    for ((var) = ((var) ? (var) : STAILQ_FIRST((head)));                \\\n         (var) && ((tvar) = STAILQ_NEXT((var), field), 1);              \\\n         (var) = (tvar))\n\n#define STAILQ_INSERT_HEAD(head, elm, field) do {                       \\\n        if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \\\n            (head)->stqh_last = &STAILQ_NEXT((elm), field);             \\\n        STAILQ_FIRST((head)) = (elm);                                   \\\n    } while (0)\n\n#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do {               \\\n        if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL) \\\n            (head)->stqh_last = &STAILQ_NEXT((elm), field);             \\\n        STAILQ_NEXT((tqelm), field) = (elm);                            \\\n    } while (0)\n\n#define STAILQ_INSERT_TAIL(head, elm, field) do {                       \\\n        STAILQ_NEXT((elm), field) = NULL;                               \\\n        *(head)->stqh_last = (elm);                                     \\\n        (head)->stqh_last = &STAILQ_NEXT((elm), field);                 \\\n    } while (0)\n\n#define STAILQ_CONCAT(head1, head2) do {                                \\\n        if (!STAILQ_EMPTY((head2))) {                                   \\\n            *(head1)->stqh_last = (head2)->stqh_first;                  \\\n            (head1)->stqh_last = (head2)->stqh_last;                    \\\n            STAILQ_INIT((head2));                                       \\\n        }                                                               \\\n    } while (0)\n\n#define STAILQ_REMOVE_AFTER(head, elm, field) do {                      \\\n        if ((STAILQ_NEXT(elm, field) =                                  \\\n             STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL)      \\\n            (head)->stqh_last = &STAILQ_NEXT((elm), field);             \\\n    } while (0)\n\n#define STAILQ_REMOVE_HEAD(head, field) do {                            \\\n        if ((STAILQ_FIRST((head)) =                                     \\\n             STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL)         \\\n            (head)->stqh_last = &STAILQ_FIRST((head));                  \\\n    } while (0)\n\n#define STAILQ_REMOVE(head, elm, type, field) do {                      \\\n        if (STAILQ_FIRST((head)) == (elm)) {                            \\\n            STAILQ_REMOVE_HEAD((head), field);                          \\\n        }                                                               \\\n        else {                                                          \\\n            struct type *curelm = STAILQ_FIRST((head));                 \\\n            while (STAILQ_NEXT(curelm, field) != (elm))                 \\\n                curelm = STAILQ_NEXT(curelm, field);                    \\\n            STAILQ_REMOVE_AFTER(head, curelm, field);                   \\\n        }                                                               \\\n    } while (0)\n\n#define STAILQ_SWAP(head1, head2, type) do {                            \\\n        struct type *swap_first = STAILQ_FIRST(head1);                  \\\n        struct type **swap_last = (head1)->stqh_last;                   \\\n        STAILQ_FIRST(head1) = STAILQ_FIRST(head2);                      \\\n        (head1)->stqh_last = (head2)->stqh_last;                        \\\n        STAILQ_FIRST(head2) = swap_first;                               \\\n        (head2)->stqh_last = swap_last;                                 \\\n        if (STAILQ_EMPTY(head1))                                        \\\n            (head1)->stqh_last = &STAILQ_FIRST(head1);                  \\\n        if (STAILQ_EMPTY(head2))                                        \\\n            (head2)->stqh_last = &STAILQ_FIRST(head2);                  \\\n    } while (0)\n\n/*\n * Tail queue declarations.\n */\n#define TAILQ_HEAD(name, type, qual)                                    \\\n    struct name {                                                       \\\n        struct type *qual tqh_first; /* first element */                \\\n        struct type *qual *tqh_last; /* addr of last next element */    \\\n}\n\n#define TAILQ_HEAD_INITIALIZER(head)                                    \\\n    { NULL, &(head).tqh_first }\n\n#define TAILQ_ENTRY(type, qual)                                         \\\n    struct {                                                            \\\n        struct type *qual tqe_next;  /* next element */                 \\\n        struct type *qual *tqe_prev; /* address of previous next element */ \\\n    }\n\n/*\n * Tail queue functions.\n */\n#define TAILQ_INIT(head) do {                                           \\\n        TAILQ_FIRST((head)) = NULL;                                     \\\n        (head)->tqh_last = &TAILQ_FIRST((head));                        \\\n    } while (0)\n\n#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)\n\n#define TAILQ_FIRST(head) ((head)->tqh_first)\n\n#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)\n\n#define TAILQ_PREV(elm, headname, field)                                \\\n        (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))\n\n#define TAILQ_LAST(head, headname)                                      \\\n    (*(((struct headname *)((head)->tqh_last))->tqh_last))\n\n#define TAILQ_FOREACH(var, head, field)                                 \\\n    for ((var) = TAILQ_FIRST((head));                                   \\\n         (var);                                                         \\\n         (var) = TAILQ_NEXT((var), field))\n\n#define TAILQ_FOREACH_FROM(var, head, field)                            \\\n    for ((var) = ((var) ? (var) : TAILQ_FIRST((head)));                 \\\n         (var);                                                         \\\n         (var) = TAILQ_NEXT((var), field))\n\n#define TAILQ_FOREACH_SAFE(var, head, field, tvar)                      \\\n    for ((var) = TAILQ_FIRST((head));                                   \\\n         (var) && ((tvar) = TAILQ_NEXT((var), field), 1);               \\\n         (var) = (tvar))\n\n#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar)                 \\\n    for ((var) = ((var) ? (var) : TAILQ_FIRST((head)));                 \\\n         (var) && ((tvar) = TAILQ_NEXT((var), field), 1);               \\\n         (var) = (tvar))\n\n#define TAILQ_FOREACH_REVERSE(var, head, headname, field)               \\\n    for ((var) = TAILQ_LAST((head), headname);                          \\\n         (var);                                                         \\\n         (var) = TAILQ_PREV((var), headname, field))\n\n#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field)          \\\n    for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname));        \\\n         (var);                                                         \\\n         (var) = TAILQ_PREV((var), headname, field))\n\n#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar)    \\\n    for ((var) = TAILQ_LAST((head), headname);                          \\\n         (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1);     \\\n         (var) = (tvar))\n\n#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \\\n    for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname));        \\\n         (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1);     \\\n         (var) = (tvar))\n\n#define TAILQ_INSERT_HEAD(head, elm, field) do {                        \\\n        if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL)   \\\n            TAILQ_FIRST((head))->field.tqe_prev =                       \\\n                &TAILQ_NEXT((elm), field);                              \\\n        else                                                            \\\n            (head)->tqh_last = &TAILQ_NEXT((elm), field);               \\\n        TAILQ_FIRST((head)) = (elm);                                    \\\n        (elm)->field.tqe_prev = &TAILQ_FIRST((head));                   \\\n    } while (0)\n\n#define TAILQ_INSERT_BEFORE(listelm, elm, field) do {                   \\\n        (elm)->field.tqe_prev = (listelm)->field.tqe_prev;              \\\n        TAILQ_NEXT((elm), field) = (listelm);                           \\\n        *(listelm)->field.tqe_prev = (elm);                             \\\n        (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field);          \\\n    } while (0)\n\n#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do {              \\\n        if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL) \\\n            TAILQ_NEXT((elm), field)->field.tqe_prev =                  \\\n                &TAILQ_NEXT((elm), field);                              \\\n        else                                                            \\\n            (head)->tqh_last = &TAILQ_NEXT((elm), field);               \\\n        TAILQ_NEXT((listelm), field) = (elm);                           \\\n        (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field);          \\\n    } while (0)\n\n#define TAILQ_INSERT_TAIL(head, elm, field) do {                        \\\n        TAILQ_NEXT((elm), field) = NULL;                                \\\n        (elm)->field.tqe_prev = (head)->tqh_last;                       \\\n        *(head)->tqh_last = (elm);                                      \\\n        (head)->tqh_last = &TAILQ_NEXT((elm), field);                   \\\n    } while (0)\n\n#define TAILQ_CONCAT(head1, head2, field) do {                          \\\n        if (!TAILQ_EMPTY(head2)) {                                      \\\n            *(head1)->tqh_last = (head2)->tqh_first;                    \\\n            (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last;     \\\n            (head1)->tqh_last = (head2)->tqh_last;                      \\\n            TAILQ_INIT((head2));                                        \\\n        }                                                               \\\n    } while (0)\n\n#define TAILQ_REMOVE(head, elm, field) do {                             \\\n        if ((TAILQ_NEXT((elm), field)) != NULL)                         \\\n            TAILQ_NEXT((elm), field)->field.tqe_prev =                  \\\n                (elm)->field.tqe_prev;                                  \\\n        else                                                            \\\n            (head)->tqh_last = (elm)->field.tqe_prev;                   \\\n        *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field);              \\\n    } while (0)\n\n#define TAILQ_SWAP(head1, head2, type, field) do {                      \\\n        struct type *swap_first = (head1)->tqh_first;                   \\\n        struct type **swap_last = (head1)->tqh_last;                    \\\n        (head1)->tqh_first = (head2)->tqh_first;                        \\\n        (head1)->tqh_last = (head2)->tqh_last;                          \\\n        (head2)->tqh_first = swap_first;                                \\\n        (head2)->tqh_last = swap_last;                                  \\\n        if ((swap_first = (head1)->tqh_first) != NULL)                  \\\n            swap_first->field.tqe_prev = &(head1)->tqh_first;           \\\n        else                                                            \\\n            (head1)->tqh_last = &(head1)->tqh_first;                    \\\n        if ((swap_first = (head2)->tqh_first) != NULL)                  \\\n            swap_first->field.tqe_prev = &(head2)->tqh_first;           \\\n        else                                                            \\\n            (head2)->tqh_last = &(head2)->tqh_first;                    \\\n    } while (0)\n\n#endif\n"
  },
  {
    "path": "libxhook/jni/tree.h",
    "content": "/*      $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $  */\n/*      $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $    */\n/* $FreeBSD: stable/9/sys/sys/tree.h 189204 2009-03-01 04:57:23Z bms $ */\n\n/*-\n * Copyright 2002 Niels Provos <provos@citi.umich.edu>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef TREE_H\n#define TREE_H\n\n/* #include <sys/cdefs.h> */\n#ifndef __unused\n#define __unused __attribute__((__unused__))\n#endif\n\n/*\n * This file defines data structures for different types of trees:\n * splay trees and red-black trees.\n *\n * A splay tree is a self-organizing data structure.  Every operation\n * on the tree causes a splay to happen.  The splay moves the requested\n * node to the root of the tree and partly rebalances it.\n *\n * This has the benefit that request locality causes faster lookups as\n * the requested nodes move to the top of the tree.  On the other hand,\n * every lookup causes memory writes.\n *\n * The Balance Theorem bounds the total access time for m operations\n * and n inserts on an initially empty tree as O((m + n)lg n).  The\n * amortized cost for a sequence of m accesses to a splay tree is O(lg n);\n *\n * A red-black tree is a binary search tree with the node color as an\n * extra attribute.  It fulfills a set of conditions:\n *      - every search path from the root to a leaf consists of the\n *        same number of black nodes,\n *      - each red node (except for the root) has a black parent,\n *      - each leaf node is black.\n *\n * Every operation on a red-black tree is bounded as O(lg n).\n * The maximum height of a red-black tree is 2lg (n+1).\n */\n\n#define SPLAY_HEAD(name, type)                                          \\\nstruct name {                                                           \\\n        struct type *sph_root; /* root of the tree */                   \\\n}\n\n#define SPLAY_INITIALIZER(root)                                         \\\n        { NULL }\n\n#define SPLAY_INIT(root) do {                                           \\\n        (root)->sph_root = NULL;                                        \\\n} while (/*CONSTCOND*/ 0)\n\n#define SPLAY_ENTRY(type)                                               \\\nstruct {                                                                \\\n        struct type *spe_left; /* left element */                       \\\n        struct type *spe_right; /* right element */                     \\\n}\n\n#define SPLAY_LEFT(elm, field)          (elm)->field.spe_left\n#define SPLAY_RIGHT(elm, field)         (elm)->field.spe_right\n#define SPLAY_ROOT(head)                (head)->sph_root\n#define SPLAY_EMPTY(head)               (SPLAY_ROOT(head) == NULL)\n\n/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */\n#define SPLAY_ROTATE_RIGHT(head, tmp, field) do {                       \\\n        SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field);  \\\n        SPLAY_RIGHT(tmp, field) = (head)->sph_root;                     \\\n        (head)->sph_root = tmp;                                         \\\n} while (/*CONSTCOND*/ 0)\n        \n#define SPLAY_ROTATE_LEFT(head, tmp, field) do {                        \\\n        SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field);  \\\n        SPLAY_LEFT(tmp, field) = (head)->sph_root;                      \\\n        (head)->sph_root = tmp;                                         \\\n} while (/*CONSTCOND*/ 0)\n\n#define SPLAY_LINKLEFT(head, tmp, field) do {                           \\\n        SPLAY_LEFT(tmp, field) = (head)->sph_root;                      \\\n        tmp = (head)->sph_root;                                         \\\n        (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);         \\\n} while (/*CONSTCOND*/ 0)\n\n#define SPLAY_LINKRIGHT(head, tmp, field) do {                          \\\n        SPLAY_RIGHT(tmp, field) = (head)->sph_root;                     \\\n        tmp = (head)->sph_root;                                         \\\n        (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);        \\\n} while (/*CONSTCOND*/ 0)\n\n#define SPLAY_ASSEMBLE(head, node, left, right, field) do {             \\\n        SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \\\n        SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\\\n        SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \\\n        SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \\\n} while (/*CONSTCOND*/ 0)\n\n/* Generates prototypes and inline functions */\n\n#define SPLAY_PROTOTYPE(name, type, field, cmp)                         \\\nvoid name##_SPLAY(struct name *, struct type *);                        \\\nvoid name##_SPLAY_MINMAX(struct name *, int);                           \\\nstruct type *name##_SPLAY_INSERT(struct name *, struct type *);         \\\nstruct type *name##_SPLAY_REMOVE(struct name *, struct type *);         \\\n                                                                        \\\n/* Finds the node with the same key as elm */                           \\\nstatic __inline struct type *                                           \\\nname##_SPLAY_FIND(struct name *head, struct type *elm)                  \\\n{                                                                       \\\n        if (SPLAY_EMPTY(head))                                          \\\n                return(NULL);                                           \\\n        name##_SPLAY(head, elm);                                        \\\n        if ((cmp)(elm, (head)->sph_root) == 0)                          \\\n                return (head->sph_root);                                \\\n        return (NULL);                                                  \\\n}                                                                       \\\n                                                                        \\\nstatic __inline struct type *                                          \\\nname##_SPLAY_NEXT(struct name *head, struct type *elm)                 \\\n{                                                                       \\\n        name##_SPLAY(head, elm);                                        \\\n        if (SPLAY_RIGHT(elm, field) != NULL) {                          \\\n                elm = SPLAY_RIGHT(elm, field);                          \\\n                while (SPLAY_LEFT(elm, field) != NULL) {                \\\n                        elm = SPLAY_LEFT(elm, field);                   \\\n                }                                                       \\\n        } else                                                          \\\n                elm = NULL;                                             \\\n        return (elm);                                                   \\\n}                                                                       \\\n                                                                        \\\nstatic __inline struct type *                                           \\\nname##_SPLAY_MIN_MAX(struct name *head, int val)                        \\\n{                                                                       \\\n        name##_SPLAY_MINMAX(head, val);                                 \\\n        return (SPLAY_ROOT(head));                                      \\\n}\n\n/* Main splay operation.\n * Moves node close to the key of elm to top\n */\n#define SPLAY_GENERATE(name, type, field, cmp)                          \\\nstruct type *                                                           \\\nname##_SPLAY_INSERT(struct name *head, struct type *elm)                \\\n{                                                                       \\\n    if (SPLAY_EMPTY(head)) {                                            \\\n            SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL;    \\\n    } else {                                                            \\\n            int __comp;                                                 \\\n            name##_SPLAY(head, elm);                                    \\\n            __comp = (cmp)(elm, (head)->sph_root);                      \\\n            if(__comp < 0) {                                            \\\n                    SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\\\n                    SPLAY_RIGHT(elm, field) = (head)->sph_root;         \\\n                    SPLAY_LEFT((head)->sph_root, field) = NULL;         \\\n            } else if (__comp > 0) {                                    \\\n                    SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\\\n                    SPLAY_LEFT(elm, field) = (head)->sph_root;          \\\n                    SPLAY_RIGHT((head)->sph_root, field) = NULL;        \\\n            } else                                                      \\\n                    return ((head)->sph_root);                          \\\n    }                                                                   \\\n    (head)->sph_root = (elm);                                           \\\n    return (NULL);                                                      \\\n}                                                                       \\\n                                                                        \\\nstruct type *                                                           \\\nname##_SPLAY_REMOVE(struct name *head, struct type *elm)                \\\n{                                                                       \\\n        struct type *__tmp;                                             \\\n        if (SPLAY_EMPTY(head))                                          \\\n                return (NULL);                                          \\\n        name##_SPLAY(head, elm);                                        \\\n        if ((cmp)(elm, (head)->sph_root) == 0) {                        \\\n                if (SPLAY_LEFT((head)->sph_root, field) == NULL) {      \\\n                        (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\\\n                } else {                                                \\\n                        __tmp = SPLAY_RIGHT((head)->sph_root, field);   \\\n                        (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\\\n                        name##_SPLAY(head, elm);                        \\\n                        SPLAY_RIGHT((head)->sph_root, field) = __tmp;   \\\n                }                                                       \\\n                return (elm);                                           \\\n        }                                                               \\\n        return (NULL);                                                  \\\n}                                                                       \\\n                                                                        \\\nvoid                                                                    \\\nname##_SPLAY(struct name *head, struct type *elm)                       \\\n{                                                                       \\\n        struct type __node, *__left, *__right, *__tmp;                  \\\n        int __comp;                                                     \\\n\\\n        SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\\\n        __left = __right = &__node;                                     \\\n\\\n        while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) {          \\\n                if (__comp < 0) {                                       \\\n                        __tmp = SPLAY_LEFT((head)->sph_root, field);    \\\n                        if (__tmp == NULL)                              \\\n                                break;                                  \\\n                        if ((cmp)(elm, __tmp) < 0){                     \\\n                                SPLAY_ROTATE_RIGHT(head, __tmp, field); \\\n                                if (SPLAY_LEFT((head)->sph_root, field) == NULL)\\\n                                        break;                          \\\n                        }                                               \\\n                        SPLAY_LINKLEFT(head, __right, field);           \\\n                } else if (__comp > 0) {                                \\\n                        __tmp = SPLAY_RIGHT((head)->sph_root, field);   \\\n                        if (__tmp == NULL)                              \\\n                                break;                                  \\\n                        if ((cmp)(elm, __tmp) > 0){                     \\\n                                SPLAY_ROTATE_LEFT(head, __tmp, field);  \\\n                                if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\\\n                                        break;                          \\\n                        }                                               \\\n                        SPLAY_LINKRIGHT(head, __left, field);           \\\n                }                                                       \\\n        }                                                               \\\n        SPLAY_ASSEMBLE(head, &__node, __left, __right, field);          \\\n}                                                                       \\\n                                                                        \\\n/* Splay with either the minimum or the maximum element                 \\\n * Used to find minimum or maximum element in tree.                     \\\n */                                                                     \\\nvoid name##_SPLAY_MINMAX(struct name *head, int __comp) \\\n{                                                                       \\\n        struct type __node, *__left, *__right, *__tmp;                  \\\n\\\n        SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\\\n        __left = __right = &__node;                                     \\\n\\\n        while (1) {                                                     \\\n                if (__comp < 0) {                                       \\\n                        __tmp = SPLAY_LEFT((head)->sph_root, field);    \\\n                        if (__tmp == NULL)                              \\\n                                break;                                  \\\n                        if (__comp < 0){                                \\\n                                SPLAY_ROTATE_RIGHT(head, __tmp, field); \\\n                                if (SPLAY_LEFT((head)->sph_root, field) == NULL)\\\n                                        break;                          \\\n                        }                                               \\\n                        SPLAY_LINKLEFT(head, __right, field);           \\\n                } else if (__comp > 0) {                                \\\n                        __tmp = SPLAY_RIGHT((head)->sph_root, field);   \\\n                        if (__tmp == NULL)                              \\\n                                break;                                  \\\n                        if (__comp > 0) {                               \\\n                                SPLAY_ROTATE_LEFT(head, __tmp, field);  \\\n                                if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\\\n                                        break;                          \\\n                        }                                               \\\n                        SPLAY_LINKRIGHT(head, __left, field);           \\\n                }                                                       \\\n        }                                                               \\\n        SPLAY_ASSEMBLE(head, &__node, __left, __right, field);          \\\n}\n\n#define SPLAY_NEGINF    -1\n#define SPLAY_INF       1\n\n#define SPLAY_INSERT(name, x, y)        name##_SPLAY_INSERT(x, y)\n#define SPLAY_REMOVE(name, x, y)        name##_SPLAY_REMOVE(x, y)\n#define SPLAY_FIND(name, x, y)          name##_SPLAY_FIND(x, y)\n#define SPLAY_NEXT(name, x, y)          name##_SPLAY_NEXT(x, y)\n#define SPLAY_MIN(name, x)              (SPLAY_EMPTY(x) ? NULL  \\\n                                        : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))\n#define SPLAY_MAX(name, x)              (SPLAY_EMPTY(x) ? NULL  \\\n                                        : name##_SPLAY_MIN_MAX(x, SPLAY_INF))\n\n#define SPLAY_FOREACH(x, name, head)                                    \\\n        for ((x) = SPLAY_MIN(name, head);                               \\\n             (x) != NULL;                                               \\\n             (x) = SPLAY_NEXT(name, head, x))\n\n/* Macros that define a red-black tree */\n#define RB_HEAD(name, type)                                             \\\nstruct name {                                                           \\\n        struct type *rbh_root; /* root of the tree */                   \\\n}\n\n#define RB_INITIALIZER(root)                                            \\\n        { NULL }\n\n#define RB_INIT(root) do {                                              \\\n        (root)->rbh_root = NULL;                                        \\\n} while (/*CONSTCOND*/ 0)\n\n#define RB_BLACK        0\n#define RB_RED          1\n#define RB_ENTRY(type)                                                  \\\nstruct {                                                                \\\n        struct type *rbe_left;          /* left element */              \\\n        struct type *rbe_right;         /* right element */             \\\n        struct type *rbe_parent;        /* parent element */            \\\n        int rbe_color;                  /* node color */                \\\n}\n\n#define RB_LEFT(elm, field)             (elm)->field.rbe_left\n#define RB_RIGHT(elm, field)            (elm)->field.rbe_right\n#define RB_PARENT(elm, field)           (elm)->field.rbe_parent\n#define RB_COLOR(elm, field)            (elm)->field.rbe_color\n#define RB_ROOT(head)                   (head)->rbh_root\n#define RB_EMPTY(head)                  (RB_ROOT(head) == NULL)\n\n#define RB_SET(elm, parent, field) do {                                 \\\n        RB_PARENT(elm, field) = parent;                                 \\\n        RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL;              \\\n        RB_COLOR(elm, field) = RB_RED;                                  \\\n} while (/*CONSTCOND*/ 0)\n\n#define RB_SET_BLACKRED(black, red, field) do {                         \\\n        RB_COLOR(black, field) = RB_BLACK;                              \\\n        RB_COLOR(red, field) = RB_RED;                                  \\\n} while (/*CONSTCOND*/ 0)\n\n#ifndef RB_AUGMENT\n#define RB_AUGMENT(x)   do {} while (0)\n#endif\n\n#define RB_ROTATE_LEFT(head, elm, tmp, field) do {                      \\\n        (tmp) = RB_RIGHT(elm, field);                                   \\\n        if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) {     \\\n                RB_PARENT(RB_LEFT(tmp, field), field) = (elm);          \\\n        }                                                               \\\n        RB_AUGMENT(elm);                                                \\\n        if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) {  \\\n                if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))     \\\n                        RB_LEFT(RB_PARENT(elm, field), field) = (tmp);  \\\n                else                                                    \\\n                        RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \\\n        } else                                                          \\\n                (head)->rbh_root = (tmp);                               \\\n        RB_LEFT(tmp, field) = (elm);                                    \\\n        RB_PARENT(elm, field) = (tmp);                                  \\\n        RB_AUGMENT(tmp);                                                \\\n        if ((RB_PARENT(tmp, field)))                                    \\\n                RB_AUGMENT(RB_PARENT(tmp, field));                      \\\n} while (/*CONSTCOND*/ 0)\n\n#define RB_ROTATE_RIGHT(head, elm, tmp, field) do {                     \\\n        (tmp) = RB_LEFT(elm, field);                                    \\\n        if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) {     \\\n                RB_PARENT(RB_RIGHT(tmp, field), field) = (elm);         \\\n        }                                                               \\\n        RB_AUGMENT(elm);                                                \\\n        if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) {  \\\n                if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))     \\\n                        RB_LEFT(RB_PARENT(elm, field), field) = (tmp);  \\\n                else                                                    \\\n                        RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \\\n        } else                                                          \\\n                (head)->rbh_root = (tmp);                               \\\n        RB_RIGHT(tmp, field) = (elm);                                   \\\n        RB_PARENT(elm, field) = (tmp);                                  \\\n        RB_AUGMENT(tmp);                                                \\\n        if ((RB_PARENT(tmp, field)))                                    \\\n                RB_AUGMENT(RB_PARENT(tmp, field));                      \\\n} while (/*CONSTCOND*/ 0)\n\n/* Generates prototypes and inline functions */\n#define RB_PROTOTYPE(name, type, field, cmp)                            \\\n        RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)\n#define RB_PROTOTYPE_STATIC(name, type, field, cmp)                     \\\n        RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static)\n#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr)             \\\nattr void name##_RB_INSERT_COLOR(struct name *, struct type *);         \\\nattr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\\\nattr struct type *name##_RB_REMOVE(struct name *, struct type *);       \\\nattr struct type *name##_RB_INSERT(struct name *, struct type *);       \\\nattr struct type *name##_RB_FIND(struct name *, struct type *);         \\\nattr struct type *name##_RB_NFIND(struct name *, struct type *);        \\\nattr struct type *name##_RB_NEXT(struct type *);                        \\\nattr struct type *name##_RB_PREV(struct type *);                        \\\nattr struct type *name##_RB_MINMAX(struct name *, int);                 \\\n                                                                        \\\n\n/* Main rb operation.\n * Moves node close to the key of elm to top\n */\n#define RB_GENERATE(name, type, field, cmp)                             \\\n        RB_GENERATE_INTERNAL(name, type, field, cmp,)\n#define RB_GENERATE_STATIC(name, type, field, cmp)                      \\\n        RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static)\n#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr)              \\\nattr void                                                               \\\nname##_RB_INSERT_COLOR(struct name *head, struct type *elm)             \\\n{                                                                       \\\n        struct type *parent, *gparent, *tmp;                            \\\n        while ((parent = RB_PARENT(elm, field)) != NULL &&              \\\n            RB_COLOR(parent, field) == RB_RED) {                        \\\n                gparent = RB_PARENT(parent, field);                     \\\n                if (parent == RB_LEFT(gparent, field)) {                \\\n                        tmp = RB_RIGHT(gparent, field);                 \\\n                        if (tmp && RB_COLOR(tmp, field) == RB_RED) {    \\\n                                RB_COLOR(tmp, field) = RB_BLACK;        \\\n                                RB_SET_BLACKRED(parent, gparent, field);\\\n                                elm = gparent;                          \\\n                                continue;                               \\\n                        }                                               \\\n                        if (RB_RIGHT(parent, field) == elm) {           \\\n                                RB_ROTATE_LEFT(head, parent, tmp, field);\\\n                                tmp = parent;                           \\\n                                parent = elm;                           \\\n                                elm = tmp;                              \\\n                        }                                               \\\n                        RB_SET_BLACKRED(parent, gparent, field);        \\\n                        RB_ROTATE_RIGHT(head, gparent, tmp, field);     \\\n                } else {                                                \\\n                        tmp = RB_LEFT(gparent, field);                  \\\n                        if (tmp && RB_COLOR(tmp, field) == RB_RED) {    \\\n                                RB_COLOR(tmp, field) = RB_BLACK;        \\\n                                RB_SET_BLACKRED(parent, gparent, field);\\\n                                elm = gparent;                          \\\n                                continue;                               \\\n                        }                                               \\\n                        if (RB_LEFT(parent, field) == elm) {            \\\n                                RB_ROTATE_RIGHT(head, parent, tmp, field);\\\n                                tmp = parent;                           \\\n                                parent = elm;                           \\\n                                elm = tmp;                              \\\n                        }                                               \\\n                        RB_SET_BLACKRED(parent, gparent, field);        \\\n                        RB_ROTATE_LEFT(head, gparent, tmp, field);      \\\n                }                                                       \\\n        }                                                               \\\n        RB_COLOR(head->rbh_root, field) = RB_BLACK;                     \\\n}                                                                       \\\n                                                                        \\\nattr void                                                               \\\nname##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \\\n{                                                                       \\\n        struct type *tmp;                                               \\\n        while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) &&     \\\n            elm != RB_ROOT(head)) {                                     \\\n                if (RB_LEFT(parent, field) == elm) {                    \\\n                        tmp = RB_RIGHT(parent, field);                  \\\n                        if (RB_COLOR(tmp, field) == RB_RED) {           \\\n                                RB_SET_BLACKRED(tmp, parent, field);    \\\n                                RB_ROTATE_LEFT(head, parent, tmp, field);\\\n                                tmp = RB_RIGHT(parent, field);          \\\n                        }                                               \\\n                        if ((RB_LEFT(tmp, field) == NULL ||             \\\n                            RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\\\n                            (RB_RIGHT(tmp, field) == NULL ||            \\\n                            RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\\\n                                RB_COLOR(tmp, field) = RB_RED;          \\\n                                elm = parent;                           \\\n                                parent = RB_PARENT(elm, field);         \\\n                        } else {                                        \\\n                                if (RB_RIGHT(tmp, field) == NULL ||     \\\n                                    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\\\n                                        struct type *oleft;             \\\n                                        if ((oleft = RB_LEFT(tmp, field)) \\\n                                            != NULL)                    \\\n                                                RB_COLOR(oleft, field) = RB_BLACK;\\\n                                        RB_COLOR(tmp, field) = RB_RED;  \\\n                                        RB_ROTATE_RIGHT(head, tmp, oleft, field);\\\n                                        tmp = RB_RIGHT(parent, field);  \\\n                                }                                       \\\n                                RB_COLOR(tmp, field) = RB_COLOR(parent, field);\\\n                                RB_COLOR(parent, field) = RB_BLACK;     \\\n                                if (RB_RIGHT(tmp, field))               \\\n                                        RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\\\n                                RB_ROTATE_LEFT(head, parent, tmp, field);\\\n                                elm = RB_ROOT(head);                    \\\n                                break;                                  \\\n                        }                                               \\\n                } else {                                                \\\n                        tmp = RB_LEFT(parent, field);                   \\\n                        if (RB_COLOR(tmp, field) == RB_RED) {           \\\n                                RB_SET_BLACKRED(tmp, parent, field);    \\\n                                RB_ROTATE_RIGHT(head, parent, tmp, field);\\\n                                tmp = RB_LEFT(parent, field);           \\\n                        }                                               \\\n                        if ((RB_LEFT(tmp, field) == NULL ||             \\\n                            RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\\\n                            (RB_RIGHT(tmp, field) == NULL ||            \\\n                            RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\\\n                                RB_COLOR(tmp, field) = RB_RED;          \\\n                                elm = parent;                           \\\n                                parent = RB_PARENT(elm, field);         \\\n                        } else {                                        \\\n                                if (RB_LEFT(tmp, field) == NULL ||      \\\n                                    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\\\n                                        struct type *oright;            \\\n                                        if ((oright = RB_RIGHT(tmp, field)) \\\n                                            != NULL)                    \\\n                                                RB_COLOR(oright, field) = RB_BLACK;\\\n                                        RB_COLOR(tmp, field) = RB_RED;  \\\n                                        RB_ROTATE_LEFT(head, tmp, oright, field);\\\n                                        tmp = RB_LEFT(parent, field);   \\\n                                }                                       \\\n                                RB_COLOR(tmp, field) = RB_COLOR(parent, field);\\\n                                RB_COLOR(parent, field) = RB_BLACK;     \\\n                                if (RB_LEFT(tmp, field))                \\\n                                        RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\\\n                                RB_ROTATE_RIGHT(head, parent, tmp, field);\\\n                                elm = RB_ROOT(head);                    \\\n                                break;                                  \\\n                        }                                               \\\n                }                                                       \\\n        }                                                               \\\n        if (elm)                                                        \\\n                RB_COLOR(elm, field) = RB_BLACK;                        \\\n}                                                                       \\\n                                                                        \\\nattr struct type *                                                      \\\nname##_RB_REMOVE(struct name *head, struct type *elm)                   \\\n{                                                                       \\\n        struct type *child, *parent, *old = elm;                        \\\n        int color;                                                      \\\n        if (RB_LEFT(elm, field) == NULL)                                \\\n                child = RB_RIGHT(elm, field);                           \\\n        else if (RB_RIGHT(elm, field) == NULL)                          \\\n                child = RB_LEFT(elm, field);                            \\\n        else {                                                          \\\n                struct type *left;                                      \\\n                elm = RB_RIGHT(elm, field);                             \\\n                while ((left = RB_LEFT(elm, field)) != NULL)            \\\n                        elm = left;                                     \\\n                child = RB_RIGHT(elm, field);                           \\\n                parent = RB_PARENT(elm, field);                         \\\n                color = RB_COLOR(elm, field);                           \\\n                if (child)                                              \\\n                        RB_PARENT(child, field) = parent;               \\\n                if (parent) {                                           \\\n                        if (RB_LEFT(parent, field) == elm)              \\\n                                RB_LEFT(parent, field) = child;         \\\n                        else                                            \\\n                                RB_RIGHT(parent, field) = child;        \\\n                        RB_AUGMENT(parent);                             \\\n                } else                                                  \\\n                        RB_ROOT(head) = child;                          \\\n                if (RB_PARENT(elm, field) == old)                       \\\n                        parent = elm;                                   \\\n                (elm)->field = (old)->field;                            \\\n                if (RB_PARENT(old, field)) {                            \\\n                        if (RB_LEFT(RB_PARENT(old, field), field) == old)\\\n                                RB_LEFT(RB_PARENT(old, field), field) = elm;\\\n                        else                                            \\\n                                RB_RIGHT(RB_PARENT(old, field), field) = elm;\\\n                        RB_AUGMENT(RB_PARENT(old, field));              \\\n                } else                                                  \\\n                        RB_ROOT(head) = elm;                            \\\n                RB_PARENT(RB_LEFT(old, field), field) = elm;            \\\n                if (RB_RIGHT(old, field))                               \\\n                        RB_PARENT(RB_RIGHT(old, field), field) = elm;   \\\n                if (parent) {                                           \\\n                        left = parent;                                  \\\n                        do {                                            \\\n                                RB_AUGMENT(left);                       \\\n                        } while ((left = RB_PARENT(left, field)) != NULL); \\\n                }                                                       \\\n                goto color;                                             \\\n        }                                                               \\\n        parent = RB_PARENT(elm, field);                                 \\\n        color = RB_COLOR(elm, field);                                   \\\n        if (child)                                                      \\\n                RB_PARENT(child, field) = parent;                       \\\n        if (parent) {                                                   \\\n                if (RB_LEFT(parent, field) == elm)                      \\\n                        RB_LEFT(parent, field) = child;                 \\\n                else                                                    \\\n                        RB_RIGHT(parent, field) = child;                \\\n                RB_AUGMENT(parent);                                     \\\n        } else                                                          \\\n                RB_ROOT(head) = child;                                  \\\ncolor:                                                                  \\\n        if (color == RB_BLACK)                                          \\\n                name##_RB_REMOVE_COLOR(head, parent, child);            \\\n        return (old);                                                   \\\n}                                                                       \\\n                                                                        \\\n/* Inserts a node into the RB tree */                                   \\\nattr struct type *                                                      \\\nname##_RB_INSERT(struct name *head, struct type *elm)                   \\\n{                                                                       \\\n        struct type *tmp;                                               \\\n        struct type *parent = NULL;                                     \\\n        int comp = 0;                                                   \\\n        tmp = RB_ROOT(head);                                            \\\n        while (tmp) {                                                   \\\n                parent = tmp;                                           \\\n                comp = (cmp)(elm, parent);                              \\\n                if (comp < 0)                                           \\\n                        tmp = RB_LEFT(tmp, field);                      \\\n                else if (comp > 0)                                      \\\n                        tmp = RB_RIGHT(tmp, field);                     \\\n                else                                                    \\\n                        return (tmp);                                   \\\n        }                                                               \\\n        RB_SET(elm, parent, field);                                     \\\n        if (parent != NULL) {                                           \\\n                if (comp < 0)                                           \\\n                        RB_LEFT(parent, field) = elm;                   \\\n                else                                                    \\\n                        RB_RIGHT(parent, field) = elm;                  \\\n                RB_AUGMENT(parent);                                     \\\n        } else                                                          \\\n                RB_ROOT(head) = elm;                                    \\\n        name##_RB_INSERT_COLOR(head, elm);                              \\\n        return (NULL);                                                  \\\n}                                                                       \\\n                                                                        \\\n/* Finds the node with the same key as elm */                           \\\nattr struct type *                                                      \\\nname##_RB_FIND(struct name *head, struct type *elm)                     \\\n{                                                                       \\\n        struct type *tmp = RB_ROOT(head);                               \\\n        int comp;                                                       \\\n        while (tmp) {                                                   \\\n                comp = cmp(elm, tmp);                                   \\\n                if (comp < 0)                                           \\\n                        tmp = RB_LEFT(tmp, field);                      \\\n                else if (comp > 0)                                      \\\n                        tmp = RB_RIGHT(tmp, field);                     \\\n                else                                                    \\\n                        return (tmp);                                   \\\n        }                                                               \\\n        return (NULL);                                                  \\\n}                                                                       \\\n                                                                        \\\n/* Finds the first node greater than or equal to the search key */      \\\nattr struct type *                                                      \\\nname##_RB_NFIND(struct name *head, struct type *elm)                    \\\n{                                                                       \\\n        struct type *tmp = RB_ROOT(head);                               \\\n        struct type *res = NULL;                                        \\\n        int comp;                                                       \\\n        while (tmp) {                                                   \\\n                comp = cmp(elm, tmp);                                   \\\n                if (comp < 0) {                                         \\\n                        res = tmp;                                      \\\n                        tmp = RB_LEFT(tmp, field);                      \\\n                }                                                       \\\n                else if (comp > 0)                                      \\\n                        tmp = RB_RIGHT(tmp, field);                     \\\n                else                                                    \\\n                        return (tmp);                                   \\\n        }                                                               \\\n        return (res);                                                   \\\n}                                                                       \\\n                                                                        \\\n/* ARGSUSED */                                                          \\\nattr struct type *                                                      \\\nname##_RB_NEXT(struct type *elm)                                        \\\n{                                                                       \\\n        if (RB_RIGHT(elm, field)) {                                     \\\n                elm = RB_RIGHT(elm, field);                             \\\n                while (RB_LEFT(elm, field))                             \\\n                        elm = RB_LEFT(elm, field);                      \\\n        } else {                                                        \\\n                if (RB_PARENT(elm, field) &&                            \\\n                    (elm == RB_LEFT(RB_PARENT(elm, field), field)))     \\\n                        elm = RB_PARENT(elm, field);                    \\\n                else {                                                  \\\n                        while (RB_PARENT(elm, field) &&                 \\\n                            (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\\\n                                elm = RB_PARENT(elm, field);            \\\n                        elm = RB_PARENT(elm, field);                    \\\n                }                                                       \\\n        }                                                               \\\n        return (elm);                                                   \\\n}                                                                       \\\n                                                                        \\\n/* ARGSUSED */                                                          \\\nattr struct type *                                                      \\\nname##_RB_PREV(struct type *elm)                                        \\\n{                                                                       \\\n        if (RB_LEFT(elm, field)) {                                      \\\n                elm = RB_LEFT(elm, field);                              \\\n                while (RB_RIGHT(elm, field))                            \\\n                        elm = RB_RIGHT(elm, field);                     \\\n        } else {                                                        \\\n                if (RB_PARENT(elm, field) &&                            \\\n                    (elm == RB_RIGHT(RB_PARENT(elm, field), field)))    \\\n                        elm = RB_PARENT(elm, field);                    \\\n                else {                                                  \\\n                        while (RB_PARENT(elm, field) &&                 \\\n                            (elm == RB_LEFT(RB_PARENT(elm, field), field)))\\\n                                elm = RB_PARENT(elm, field);            \\\n                        elm = RB_PARENT(elm, field);                    \\\n                }                                                       \\\n        }                                                               \\\n        return (elm);                                                   \\\n}                                                                       \\\n                                                                        \\\nattr struct type *                                                      \\\nname##_RB_MINMAX(struct name *head, int val)                            \\\n{                                                                       \\\n        struct type *tmp = RB_ROOT(head);                               \\\n        struct type *parent = NULL;                                     \\\n        while (tmp) {                                                   \\\n                parent = tmp;                                           \\\n                if (val < 0)                                            \\\n                        tmp = RB_LEFT(tmp, field);                      \\\n                else                                                    \\\n                        tmp = RB_RIGHT(tmp, field);                     \\\n        }                                                               \\\n        return (parent);                                                \\\n}\n\n#define RB_NEGINF       -1\n#define RB_INF  1\n\n#define RB_INSERT(name, x, y)   name##_RB_INSERT(x, y)\n#define RB_REMOVE(name, x, y)   name##_RB_REMOVE(x, y)\n#define RB_FIND(name, x, y)     name##_RB_FIND(x, y)\n#define RB_NFIND(name, x, y)    name##_RB_NFIND(x, y)\n#define RB_NEXT(name, x, y)     name##_RB_NEXT(y)\n#define RB_PREV(name, x, y)     name##_RB_PREV(y)\n#define RB_MIN(name, x)         name##_RB_MINMAX(x, RB_NEGINF)\n#define RB_MAX(name, x)         name##_RB_MINMAX(x, RB_INF)\n\n#define RB_FOREACH(x, name, head)                                       \\\n        for ((x) = RB_MIN(name, head);                                  \\\n             (x) != NULL;                                               \\\n             (x) = name##_RB_NEXT(x))\n\n#define RB_FOREACH_FROM(x, name, y)                                     \\\n        for ((x) = (y);                                                 \\\n            ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL);    \\\n             (x) = (y))\n\n#define RB_FOREACH_SAFE(x, name, head, y)                               \\\n        for ((x) = RB_MIN(name, head);                                  \\\n            ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL);    \\\n             (x) = (y))\n\n#define RB_FOREACH_REVERSE(x, name, head)                               \\\n        for ((x) = RB_MAX(name, head);                                  \\\n             (x) != NULL;                                               \\\n             (x) = name##_RB_PREV(x))\n\n#define RB_FOREACH_REVERSE_FROM(x, name, y)                             \\\n        for ((x) = (y);                                                 \\\n            ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL);    \\\n             (x) = (y))\n\n#define RB_FOREACH_REVERSE_SAFE(x, name, head, y)                       \\\n        for ((x) = RB_MAX(name, head);                                  \\\n            ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL);    \\\n             (x) = (y))\n\n#endif\n"
  },
  {
    "path": "libxhook/jni/xh_core.c",
    "content": "// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n//\n\n// Created by caikelun on 2018-04-11.\n\n#include <inttypes.h>\n#include <stdio.h>\n#include <stdint.h>\n#include <ctype.h>\n#include <stdlib.h>\n#include <string.h>\n#include <pthread.h>\n#include <regex.h>\n#include <setjmp.h>\n#include <errno.h>\n#include \"queue.h\"\n#include \"tree.h\"\n#include \"xh_errno.h\"\n#include \"xh_log.h\"\n#include \"xh_elf.h\"\n#include \"xh_version.h\"\n#include \"xh_core.h\"\n\n#define XH_CORE_DEBUG 0\n\n//registered hook info collection\ntypedef struct xh_core_hook_info\n{\n#if XH_CORE_DEBUG\n    char     *pathname_regex_str;\n#endif\n    regex_t   pathname_regex;\n    char     *symbol;\n    void     *new_func;\n    void    **old_func;\n    TAILQ_ENTRY(xh_core_hook_info,) link;\n} xh_core_hook_info_t;\ntypedef TAILQ_HEAD(xh_core_hook_info_queue, xh_core_hook_info,) xh_core_hook_info_queue_t;\n\n//ignored hook info collection\ntypedef struct xh_core_ignore_info\n{\n#if XH_CORE_DEBUG\n    char     *pathname_regex_str;\n#endif\n    regex_t   pathname_regex;\n    char     *symbol; //NULL meaning for all symbols\n    TAILQ_ENTRY(xh_core_ignore_info,) link;\n} xh_core_ignore_info_t;\ntypedef TAILQ_HEAD(xh_core_ignore_info_queue, xh_core_ignore_info,) xh_core_ignore_info_queue_t;\n\n//required info from /proc/self/maps\ntypedef struct xh_core_map_info\n{\n    char      *pathname;\n    uintptr_t  base_addr;\n    xh_elf_t   elf;\n    RB_ENTRY(xh_core_map_info) link;\n} xh_core_map_info_t;\nstatic __inline__ int xh_core_map_info_cmp(xh_core_map_info_t *a, xh_core_map_info_t *b)\n{\n    return strcmp(a->pathname, b->pathname);\n}\ntypedef RB_HEAD(xh_core_map_info_tree, xh_core_map_info) xh_core_map_info_tree_t;\nRB_GENERATE_STATIC(xh_core_map_info_tree, xh_core_map_info, link, xh_core_map_info_cmp)\n\n//signal handler for SIGSEGV\n//for xh_elf_init(), xh_elf_hook(), xh_elf_check_elfheader()\nstatic int              xh_core_sigsegv_enable = 1; //enable by default\nstatic struct sigaction xh_core_sigsegv_act_old;\nstatic volatile int     xh_core_sigsegv_flag = 0;\nstatic sigjmp_buf       xh_core_sigsegv_env;\nstatic void xh_core_sigsegv_handler(int sig)\n{\n    (void)sig;\n    \n    if(xh_core_sigsegv_flag)\n        siglongjmp(xh_core_sigsegv_env, 1);\n    else\n        sigaction(SIGSEGV, &xh_core_sigsegv_act_old, NULL);\n}\nstatic int xh_core_add_sigsegv_handler()\n{\n    struct sigaction act;\n\n    if(!xh_core_sigsegv_enable) return 0;\n    \n    if(0 != sigemptyset(&act.sa_mask)) return (0 == errno ? XH_ERRNO_UNKNOWN : errno);\n    act.sa_handler = xh_core_sigsegv_handler;\n    \n    if(0 != sigaction(SIGSEGV, &act, &xh_core_sigsegv_act_old))\n        return (0 == errno ? XH_ERRNO_UNKNOWN : errno);\n\n    return 0;\n}\nstatic void xh_core_del_sigsegv_handler()\n{\n    if(!xh_core_sigsegv_enable) return;\n    \n    sigaction(SIGSEGV, &xh_core_sigsegv_act_old, NULL);\n}\n\n\nstatic xh_core_hook_info_queue_t   xh_core_hook_info   = TAILQ_HEAD_INITIALIZER(xh_core_hook_info);\nstatic xh_core_ignore_info_queue_t xh_core_ignore_info = TAILQ_HEAD_INITIALIZER(xh_core_ignore_info);\nstatic xh_core_map_info_tree_t     xh_core_map_info    = RB_INITIALIZER(&xh_core_map_info);\nstatic pthread_mutex_t             xh_core_mutex       = PTHREAD_MUTEX_INITIALIZER;\nstatic pthread_cond_t              xh_core_cond        = PTHREAD_COND_INITIALIZER;\nstatic volatile int                xh_core_inited      = 0;\nstatic volatile int                xh_core_init_ok     = 0;\nstatic volatile int                xh_core_async_inited  = 0;\nstatic volatile int                xh_core_async_init_ok = 0;\nstatic pthread_mutex_t             xh_core_refresh_mutex = PTHREAD_MUTEX_INITIALIZER;\nstatic pthread_t                   xh_core_refresh_thread_tid;\nstatic volatile int                xh_core_refresh_thread_running = 0;\nstatic volatile int                xh_core_refresh_thread_do = 0;\n\n\nint xh_core_register(const char *pathname_regex_str, const char *symbol,\n                     void *new_func, void **old_func)\n{\n    xh_core_hook_info_t *hi;\n    regex_t              regex;\n\n    if(NULL == pathname_regex_str || NULL == symbol || NULL == new_func) return XH_ERRNO_INVAL;\n\n    if(xh_core_inited)\n    {\n        XH_LOG_ERROR(\"do not register hook after refresh(): %s, %s\", pathname_regex_str, symbol);\n        return XH_ERRNO_INVAL;\n    }\n\n    if(0 != regcomp(&regex, pathname_regex_str, REG_NOSUB)) return XH_ERRNO_INVAL;\n\n    if(NULL == (hi = malloc(sizeof(xh_core_hook_info_t)))) return XH_ERRNO_NOMEM;\n    if(NULL == (hi->symbol = strdup(symbol)))\n    {\n        free(hi);\n        return XH_ERRNO_NOMEM;\n    }\n#if XH_CORE_DEBUG\n    if(NULL == (hi->pathname_regex_str = strdup(pathname_regex_str)))\n    {\n        free(hi->symbol);\n        free(hi);\n        return XH_ERRNO_NOMEM;\n    }\n#endif\n    hi->pathname_regex = regex;\n    hi->new_func = new_func;\n    hi->old_func = old_func;\n    \n    pthread_mutex_lock(&xh_core_mutex);\n    TAILQ_INSERT_TAIL(&xh_core_hook_info, hi, link);\n    pthread_mutex_unlock(&xh_core_mutex);\n\n    return 0;\n}\n\nint xh_core_ignore(const char *pathname_regex_str, const char *symbol)\n{\n    xh_core_ignore_info_t *ii;\n    regex_t                regex;\n\n    if(NULL == pathname_regex_str) return XH_ERRNO_INVAL;\n\n    if(xh_core_inited)\n    {\n        XH_LOG_ERROR(\"do not ignore hook after refresh(): %s, %s\", pathname_regex_str, symbol ? symbol : \"ALL\");\n        return XH_ERRNO_INVAL;\n    }\n\n    if(0 != regcomp(&regex, pathname_regex_str, REG_NOSUB)) return XH_ERRNO_INVAL;\n\n    if(NULL == (ii = malloc(sizeof(xh_core_ignore_info_t)))) return XH_ERRNO_NOMEM;\n    if(NULL != symbol)\n    {\n        if(NULL == (ii->symbol = strdup(symbol)))\n        {\n            free(ii);\n            return XH_ERRNO_NOMEM;\n        }\n    }\n    else\n    {\n        ii->symbol = NULL; //ignore all symbols\n    }\n#if XH_CORE_DEBUG\n    if(NULL == (ii->pathname_regex_str = strdup(pathname_regex_str)))\n    {\n        free(ii->symbol);\n        free(ii);\n        return XH_ERRNO_NOMEM;\n    }\n#endif\n    ii->pathname_regex = regex;\n\n    pthread_mutex_lock(&xh_core_mutex);\n    TAILQ_INSERT_TAIL(&xh_core_ignore_info, ii, link);\n    pthread_mutex_unlock(&xh_core_mutex);\n\n    return 0;\n}\n\nstatic int xh_core_check_elf_header(uintptr_t base_addr, const char *pathname)\n{\n    if(!xh_core_sigsegv_enable)\n    {\n        return xh_elf_check_elfheader(base_addr);\n    }\n    else\n    {\n        int ret = XH_ERRNO_UNKNOWN;\n        \n        xh_core_sigsegv_flag = 1;\n        if(0 == sigsetjmp(xh_core_sigsegv_env, 1))\n        {\n            ret = xh_elf_check_elfheader(base_addr);\n        }\n        else\n        {\n            ret = XH_ERRNO_SEGVERR;\n            XH_LOG_WARN(\"catch SIGSEGV when check_elfheader: %s\", pathname);\n        }\n        xh_core_sigsegv_flag = 0;\n        return ret;\n    }\n}\n\nstatic void xh_core_hook_impl(xh_core_map_info_t *mi)\n{\n    //init\n    if(0 != xh_elf_init(&(mi->elf), mi->base_addr, mi->pathname)) return;\n    \n    //hook\n    xh_core_hook_info_t   *hi;\n    xh_core_ignore_info_t *ii;\n    int ignore;\n    TAILQ_FOREACH(hi, &xh_core_hook_info, link) //find hook info\n    {\n        if(0 == regexec(&(hi->pathname_regex), mi->pathname, 0, NULL, 0))\n        {\n            ignore = 0;\n            TAILQ_FOREACH(ii, &xh_core_ignore_info, link) //find ignore info\n            {\n                if(0 == regexec(&(ii->pathname_regex), mi->pathname, 0, NULL, 0))\n                {\n                    if(NULL == ii->symbol) //ignore all symbols\n                        return;\n\n                    if(0 == strcmp(ii->symbol, hi->symbol)) //ignore the current symbol\n                    {\n                        ignore = 1;\n                        break;\n                    }\n                }\n            }\n\n            if(0 == ignore)\n                xh_elf_hook(&(mi->elf), hi->symbol, hi->new_func, hi->old_func);\n        }\n    }\n}\n\nstatic void xh_core_hook(xh_core_map_info_t *mi)\n{\n    if(!xh_core_sigsegv_enable)\n    {\n        xh_core_hook_impl(mi);\n    }\n    else\n    {    \n        xh_core_sigsegv_flag = 1;\n        if(0 == sigsetjmp(xh_core_sigsegv_env, 1))\n        {\n            xh_core_hook_impl(mi);\n        }\n        else\n        {\n            XH_LOG_WARN(\"catch SIGSEGV when init or hook: %s\", mi->pathname);\n        }\n        xh_core_sigsegv_flag = 0;\n    }\n}\n\nstatic void xh_core_refresh_impl()\n{\n    char                     line[512];\n    FILE                    *fp;\n    uintptr_t                base_addr;\n    uintptr_t                prev_base_addr = 0;\n    char                     perm[5];\n    char                     prev_perm[5] = \"---p\";\n    unsigned long            offset;\n    unsigned long            prev_offset = 0;\n    int                      pathname_pos;\n    char                    *pathname;\n    char                     prev_pathname[512] = {0};\n    size_t                   pathname_len;\n    xh_core_map_info_t      *mi, *mi_tmp;\n    xh_core_map_info_t       mi_key;\n    xh_core_hook_info_t     *hi;\n    xh_core_ignore_info_t   *ii;\n    int                      match;\n    xh_core_map_info_tree_t  map_info_refreshed = RB_INITIALIZER(&map_info_refreshed);\n\n    if(NULL == (fp = fopen(\"/proc/self/maps\", \"r\")))\n    {\n        XH_LOG_ERROR(\"fopen /proc/self/maps failed\");\n        return;\n    }\n\n    while(fgets(line, sizeof(line), fp))\n    {\n        if(sscanf(line, \"%\"PRIxPTR\"-%*lx %4s %lx %*x:%*x %*d%n\", &base_addr, perm, &offset, &pathname_pos) != 3) continue;\n\n         // do not touch the shared memory\n        if (perm[3] != 'p') continue;\n\n        // Ignore permission PROT_NONE maps\n        if (perm[0] == '-' && perm[1] == '-' && perm[2] == '-')\n            continue;\n\n        //get pathname\n        while(isspace(line[pathname_pos]) && pathname_pos < (int)(sizeof(line) - 1))\n            pathname_pos += 1;\n        if(pathname_pos >= (int)(sizeof(line) - 1)) continue;\n        pathname = line + pathname_pos;\n        pathname_len = strlen(pathname);\n        if(0 == pathname_len) continue;\n        if(pathname[pathname_len - 1] == '\\n')\n        {\n            pathname[pathname_len - 1] = '\\0';\n            pathname_len -= 1;\n        }\n        if(0 == pathname_len) continue;\n        if('[' == pathname[0]) continue;\n\n        // Find non-executable map, we need record it. Because so maps can begin with\n        // an non-executable map.\n        if (perm[2] != 'x') {\n            prev_offset = offset;\n            prev_base_addr = base_addr;\n            memcpy(prev_perm, perm, sizeof(prev_perm));\n            strcpy(prev_pathname, pathname);\n            continue;\n        }\n\n        // Find executable map if offset == 0, it OK,\n        // or we need check previous map for base address.\n        if (offset != 0) {\n            if (strcmp(prev_pathname, pathname) || prev_offset != 0 || prev_perm[0] != 'r') {\n                continue;\n            }\n            // The previous map is real begin map\n            base_addr = prev_base_addr;\n        }\n\n        //check pathname\n        //if we need to hook this elf?\n        match = 0;\n        TAILQ_FOREACH(hi, &xh_core_hook_info, link) //find hook info\n        {\n            if(0 == regexec(&(hi->pathname_regex), pathname, 0, NULL, 0))\n            {\n                TAILQ_FOREACH(ii, &xh_core_ignore_info, link) //find ignore info\n                {\n                    if(0 == regexec(&(ii->pathname_regex), pathname, 0, NULL, 0))\n                    {\n                        if(NULL == ii->symbol)\n                            goto check_finished;\n\n                        if(0 == strcmp(ii->symbol, hi->symbol))\n                            goto check_continue;\n                    }\n                }\n\n                match = 1;\n            check_continue:\n                break;\n            }\n        }\n    check_finished:\n        if(0 == match) continue;\n\n        //check elf header format\n        //We are trying to do ELF header checking as late as possible.\n        if(0 != xh_core_check_elf_header(base_addr, pathname)) continue;\n        \n        //check existed map item\n        mi_key.pathname = pathname;\n        if(NULL != (mi = RB_FIND(xh_core_map_info_tree, &xh_core_map_info, &mi_key)))\n        {\n            //exist\n            RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi);\n            \n            //repeated?\n            //We only keep the first one, that is the real base address\n            if(NULL != RB_INSERT(xh_core_map_info_tree, &map_info_refreshed, mi))\n            {\n#if XH_CORE_DEBUG\n                XH_LOG_DEBUG(\"repeated map info when update: %s\", line);\n#endif\n                free(mi->pathname);\n                free(mi);\n                continue;\n            }\n\n            //re-hook if base_addr changed\n            if(mi->base_addr != base_addr)\n            {\n                mi->base_addr = base_addr;\n                xh_core_hook(mi);\n            }\n        }\n        else\n        {\n            //not exist, create a new map info\n            if(NULL == (mi = (xh_core_map_info_t *)malloc(sizeof(xh_core_map_info_t)))) continue;\n            if(NULL == (mi->pathname = strdup(pathname)))\n            {\n                free(mi);\n                continue;\n            }\n            mi->base_addr = base_addr;\n\n            //repeated?\n            //We only keep the first one, that is the real base address\n            if(NULL != RB_INSERT(xh_core_map_info_tree, &map_info_refreshed, mi))\n            {\n#if XH_CORE_DEBUG\n                XH_LOG_DEBUG(\"repeated map info when create: %s\", line);\n#endif\n                free(mi->pathname);\n                free(mi);\n                continue;\n            }\n\n            //hook\n            xh_core_hook(mi); //hook\n        }\n    }\n    fclose(fp);\n\n    //free all missing map item, maybe dlclosed?\n    RB_FOREACH_SAFE(mi, xh_core_map_info_tree, &xh_core_map_info, mi_tmp)\n    {\n#if XH_CORE_DEBUG\n        XH_LOG_DEBUG(\"remove missing map info: %s\", mi->pathname);\n#endif\n        RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi);\n        if(mi->pathname) free(mi->pathname);\n        free(mi);\n    }\n\n    //save the new refreshed map info tree\n    xh_core_map_info = map_info_refreshed;\n\n    XH_LOG_INFO(\"map refreshed\");\n    \n#if XH_CORE_DEBUG\n    RB_FOREACH(mi, xh_core_map_info_tree, &xh_core_map_info)\n        XH_LOG_DEBUG(\"  %\"PRIxPTR\" %s\\n\", mi->base_addr, mi->pathname);\n#endif\n}\n\nstatic void *xh_core_refresh_thread_func(void *arg)\n{\n    (void)arg;\n    \n    pthread_setname_np(pthread_self(), \"xh_refresh_loop\");\n\n    while(xh_core_refresh_thread_running)\n    {\n        //waiting for a refresh task or exit\n        pthread_mutex_lock(&xh_core_mutex);\n        while(!xh_core_refresh_thread_do && xh_core_refresh_thread_running)\n        {\n            pthread_cond_wait(&xh_core_cond, &xh_core_mutex);\n        }\n        if(!xh_core_refresh_thread_running)\n        {\n            pthread_mutex_unlock(&xh_core_mutex);\n            break;\n        }\n        xh_core_refresh_thread_do = 0;\n        pthread_mutex_unlock(&xh_core_mutex);\n\n        //refresh\n        pthread_mutex_lock(&xh_core_refresh_mutex);\n        xh_core_refresh_impl();\n        pthread_mutex_unlock(&xh_core_refresh_mutex);\n    }\n\n    return NULL;\n}\n\nstatic void xh_core_init_once()\n{\n    if(xh_core_inited) return;\n\n    pthread_mutex_lock(&xh_core_mutex);\n\n    if(xh_core_inited) goto end;\n\n    xh_core_inited = 1;\n    \n    //dump debug info\n    XH_LOG_INFO(\"%s\\n\", xh_version_str_full());\n#if XH_CORE_DEBUG\n    xh_core_hook_info_t *hi;\n    TAILQ_FOREACH(hi, &xh_core_hook_info, link)\n        XH_LOG_INFO(\"  hook: %s @ %s, (%p, %p)\\n\", hi->symbol, hi->pathname_regex_str,\n                    hi->new_func, hi->old_func);\n    xh_core_ignore_info_t *ii;\n    TAILQ_FOREACH(ii, &xh_core_ignore_info, link)\n        XH_LOG_INFO(\"  ignore: %s @ %s\\n\", ii->symbol ? ii->symbol : \"ALL \",\n                    ii->pathname_regex_str);\n#endif\n    \n    //register signal handler\n    if(0 != xh_core_add_sigsegv_handler()) goto end;\n\n    //OK\n    xh_core_init_ok = 1;\n\n end:\n    pthread_mutex_unlock(&xh_core_mutex);\n}\n\nstatic void xh_core_init_async_once()\n{\n    if(xh_core_async_inited) return;\n    \n    pthread_mutex_lock(&xh_core_mutex);\n    \n    if(xh_core_async_inited) goto end;\n\n    xh_core_async_inited = 1;\n    \n    //create async refresh thread\n    xh_core_refresh_thread_running = 1;\n    if(0 != pthread_create(&xh_core_refresh_thread_tid, NULL, &xh_core_refresh_thread_func, NULL))\n    {\n        xh_core_refresh_thread_running = 0;\n        goto end;\n    }\n\n    //OK\n    xh_core_async_init_ok = 1;\n    \n end:\n    pthread_mutex_unlock(&xh_core_mutex);\n}\n\nint xh_core_refresh(int async)\n{\n    //init\n    xh_core_init_once();\n    if(!xh_core_init_ok) return XH_ERRNO_UNKNOWN;\n\n    if(async)\n    {\n        //init for async\n        xh_core_init_async_once();\n        if(!xh_core_async_init_ok) return XH_ERRNO_UNKNOWN;\n    \n        //refresh async\n        pthread_mutex_lock(&xh_core_mutex);\n        xh_core_refresh_thread_do = 1;\n        pthread_cond_signal(&xh_core_cond);\n        pthread_mutex_unlock(&xh_core_mutex);\n    }\n    else\n    {\n        //refresh sync\n        pthread_mutex_lock(&xh_core_refresh_mutex);\n        xh_core_refresh_impl();\n        pthread_mutex_unlock(&xh_core_refresh_mutex);\n    }\n    \n    return 0;\n}\n\nvoid xh_core_clear()\n{\n    //stop the async refresh thread\n    if(xh_core_async_init_ok)\n    {\n        pthread_mutex_lock(&xh_core_mutex);\n        xh_core_refresh_thread_running = 0;\n        pthread_cond_signal(&xh_core_cond);\n        pthread_mutex_unlock(&xh_core_mutex);\n        \n        pthread_join(xh_core_refresh_thread_tid, NULL);\n        xh_core_async_init_ok = 0;\n    }\n    xh_core_async_inited = 0;\n\n    //unregister the sig handler\n    if(xh_core_init_ok)\n    {\n        xh_core_del_sigsegv_handler();\n        xh_core_init_ok = 0;\n    }\n    xh_core_inited = 0;\n\n    pthread_mutex_lock(&xh_core_mutex);\n    pthread_mutex_lock(&xh_core_refresh_mutex);\n        \n    //free all map info\n    xh_core_map_info_t *mi, *mi_tmp;\n    RB_FOREACH_SAFE(mi, xh_core_map_info_tree, &xh_core_map_info, mi_tmp)\n    {\n        RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi);\n        if(mi->pathname) free(mi->pathname);\n        free(mi);\n    }\n\n    //free all hook info\n    xh_core_hook_info_t *hi, *hi_tmp;\n    TAILQ_FOREACH_SAFE(hi, &xh_core_hook_info, link, hi_tmp)\n    {\n        TAILQ_REMOVE(&xh_core_hook_info, hi, link);\n#if XH_CORE_DEBUG\n        free(hi->pathname_regex_str);\n#endif\n        regfree(&(hi->pathname_regex));\n        free(hi->symbol);\n        free(hi);\n    }\n\n    //free all ignore info\n    xh_core_ignore_info_t *ii, *ii_tmp;\n    TAILQ_FOREACH_SAFE(ii, &xh_core_ignore_info, link, ii_tmp)\n    {\n        TAILQ_REMOVE(&xh_core_ignore_info, ii, link);\n#if XH_CORE_DEBUG\n        free(ii->pathname_regex_str);\n#endif\n        regfree(&(ii->pathname_regex));\n        free(ii->symbol);\n        free(ii);\n    }\n\n    pthread_mutex_unlock(&xh_core_refresh_mutex);\n    pthread_mutex_unlock(&xh_core_mutex);\n}\n\nvoid xh_core_enable_debug(int flag)\n{\n    xh_log_priority = (flag ? ANDROID_LOG_DEBUG : ANDROID_LOG_WARN);\n}\n\nvoid xh_core_enable_sigsegv_protection(int flag)\n{\n    xh_core_sigsegv_enable = (flag ? 1 : 0);\n}\n"
  },
  {
    "path": "libxhook/jni/xh_core.h",
    "content": "// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n//\n\n// Created by caikelun on 2018-04-11.\n\n#ifndef XH_CORE_H\n#define XH_CORE_H 1\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint xh_core_register(const char *pathname_regex_str, const char *symbol,\n                     void *new_func, void **old_func);\n\nint xh_core_ignore(const char *pathname_regex_str, const char *symbol);\n\nint xh_core_refresh(int async);\n\nvoid xh_core_clear();\n\nvoid xh_core_enable_debug(int flag);\n\nvoid xh_core_enable_sigsegv_protection(int flag);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "libxhook/jni/xh_elf.c",
    "content": "// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n//\n\n// Created by caikelun on 2018-04-11.\n\n#include <unistd.h>\n#include <stdint.h>\n#include <inttypes.h>\n#include <elf.h>\n#include <link.h>\n#include <string.h>\n#include <errno.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <sys/syscall.h>\n#include \"xh_errno.h\"\n#include \"xh_log.h\"\n#include \"xh_util.h\"\n#include \"xh_elf.h\"\n\n#define XH_ELF_DEBUG 0\n\n#ifndef EI_ABIVERSION\n#define EI_ABIVERSION 8\n#endif\n\n#if defined(__arm__)\n#define XH_ELF_R_GENERIC_JUMP_SLOT R_ARM_JUMP_SLOT      //.rel.plt\n#define XH_ELF_R_GENERIC_GLOB_DAT  R_ARM_GLOB_DAT       //.rel.dyn\n#define XH_ELF_R_GENERIC_ABS       R_ARM_ABS32          //.rel.dyn\n#elif defined(__aarch64__)\n#define XH_ELF_R_GENERIC_JUMP_SLOT R_AARCH64_JUMP_SLOT\n#define XH_ELF_R_GENERIC_GLOB_DAT  R_AARCH64_GLOB_DAT\n#define XH_ELF_R_GENERIC_ABS       R_AARCH64_ABS64\n#elif defined(__i386__)\n#define XH_ELF_R_GENERIC_JUMP_SLOT R_386_JMP_SLOT\n#define XH_ELF_R_GENERIC_GLOB_DAT  R_386_GLOB_DAT\n#define XH_ELF_R_GENERIC_ABS       R_386_32\n#elif defined(__x86_64__)\n#define XH_ELF_R_GENERIC_JUMP_SLOT R_X86_64_JUMP_SLOT\n#define XH_ELF_R_GENERIC_GLOB_DAT  R_X86_64_GLOB_DAT\n#define XH_ELF_R_GENERIC_ABS       R_X86_64_64\n#endif\n\n#if defined(__LP64__)\n#define XH_ELF_R_SYM(info)  ELF64_R_SYM(info)\n#define XH_ELF_R_TYPE(info) ELF64_R_TYPE(info)\n#else\n#define XH_ELF_R_SYM(info)  ELF32_R_SYM(info)\n#define XH_ELF_R_TYPE(info) ELF32_R_TYPE(info)\n#endif\n\n//iterator for plain PLT\ntypedef struct\n{\n    uint8_t  *cur;\n    uint8_t  *end;\n    int       is_use_rela;\n} xh_elf_plain_reloc_iterator_t;\n\nstatic void xh_elf_plain_reloc_iterator_init(xh_elf_plain_reloc_iterator_t *self,\n                                             ElfW(Addr) rel, ElfW(Word) rel_sz, int is_use_rela)\n{\n    self->cur = (uint8_t *)rel;\n    self->end = self->cur + rel_sz;\n    self->is_use_rela = is_use_rela;\n}\n\nstatic void *xh_elf_plain_reloc_iterator_next(xh_elf_plain_reloc_iterator_t *self)\n{\n    if(self->cur >= self->end) return NULL;\n\n    void *ret = (void *)(self->cur);\n    self->cur += (self->is_use_rela ? sizeof(ElfW(Rela)) : sizeof(ElfW(Rel)));\n    return ret;\n}\n\n//sleb128 decoder\ntypedef struct\n{\n    uint8_t  *cur;\n    uint8_t  *end;\n} xh_elf_sleb128_decoder_t;\n\nstatic void xh_elf_sleb128_decoder_init(xh_elf_sleb128_decoder_t *self,\n                                        ElfW(Addr) rel, ElfW(Word) rel_sz)\n{\n    self->cur = (uint8_t *)rel;\n    self->end = self->cur + rel_sz;\n}\n\nstatic int xh_elf_sleb128_decoder_next(xh_elf_sleb128_decoder_t *self, size_t *ret)\n{\n    size_t value = 0;\n    static const size_t size = 8 * sizeof(value);\n    size_t shift = 0;\n    uint8_t byte;\n\n    do\n    {\n        if(self->cur >= self->end)\n            return XH_ERRNO_FORMAT;\n        \n        byte = *(self->cur)++;\n        value |= ((size_t)(byte & 127) << shift);\n        shift += 7;\n    } while(byte & 128);\n\n    if(shift < size && (byte & 64))\n    {\n        value |= -((size_t)(1) << shift);\n    }\n    \n    *ret = value;\n    return 0;\n}\n\n//iterator for sleb128 decoded packed PLT\ntypedef struct\n{\n    xh_elf_sleb128_decoder_t decoder;\n    size_t                   relocation_count;\n    size_t                   group_size;\n    size_t                   group_flags;\n    size_t                   group_r_offset_delta;\n    size_t                   relocation_index;\n    size_t                   relocation_group_index;\n    ElfW(Rela)               rela;\n    ElfW(Rel)                rel;\n    ElfW(Addr)               r_offset;\n    size_t                   r_info;\n    ssize_t                  r_addend;\n    int                      is_use_rela;\n} xh_elf_packed_reloc_iterator_t;\n\nconst size_t RELOCATION_GROUPED_BY_INFO_FLAG         = 1;\nconst size_t RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2;\nconst size_t RELOCATION_GROUPED_BY_ADDEND_FLAG       = 4;\nconst size_t RELOCATION_GROUP_HAS_ADDEND_FLAG        = 8;\n\nstatic int xh_elf_packed_reloc_iterator_init(xh_elf_packed_reloc_iterator_t *self,\n                                             ElfW(Addr) rel, ElfW(Word) rel_sz, int is_use_rela)\n{\n    int r;\n    \n    memset(self, 0, sizeof(xh_elf_packed_reloc_iterator_t));\n    xh_elf_sleb128_decoder_init(&(self->decoder), rel, rel_sz);\n    self->is_use_rela = is_use_rela;\n    \n    if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->relocation_count)))) return r;\n    if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), (size_t *)&(self->r_offset)))) return r;\n    return 0;\n}\n\nstatic int xh_elf_packed_reloc_iterator_read_group_fields(xh_elf_packed_reloc_iterator_t *self)\n{\n    int    r;\n    size_t val;\n\n    if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->group_size)))) return r;\n    if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->group_flags)))) return r;\n    \n    if(self->group_flags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG)\n        if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->group_r_offset_delta)))) return r;\n\n    if(self->group_flags & RELOCATION_GROUPED_BY_INFO_FLAG)\n        if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), (size_t *)&(self->r_info)))) return r;\n\n    if((self->group_flags & RELOCATION_GROUP_HAS_ADDEND_FLAG) &&\n       (self->group_flags & RELOCATION_GROUPED_BY_ADDEND_FLAG))\n    {\n        if(0 == self->is_use_rela)\n        {\n            XH_LOG_ERROR(\"unexpected r_addend in android.rel section\");\n            return XH_ERRNO_FORMAT;\n        }\n        if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &val))) return r;\n        self->r_addend += (ssize_t)val;\n    }\n    else if(0 == (self->group_flags & RELOCATION_GROUP_HAS_ADDEND_FLAG))\n    {\n        self->r_addend = 0;\n    }\n    \n    self->relocation_group_index = 0;\n    return 0;\n}\n\nstatic void *xh_elf_packed_reloc_iterator_next(xh_elf_packed_reloc_iterator_t *self)\n{\n    size_t val;\n\n    if(self->relocation_index >= self->relocation_count) return NULL;\n\n    if(self->relocation_group_index == self->group_size)\n    {\n        if(0 != xh_elf_packed_reloc_iterator_read_group_fields(self)) return NULL;\n    }\n\n    if(self->group_flags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG)\n    {\n        self->r_offset += self->group_r_offset_delta;\n    }\n    else\n    {\n        if(0 != xh_elf_sleb128_decoder_next(&(self->decoder), &val)) return NULL;\n        self->r_offset += val;\n    }\n    \n    if(0 == (self->group_flags & RELOCATION_GROUPED_BY_INFO_FLAG))\n        if(0 != xh_elf_sleb128_decoder_next(&(self->decoder), &(self->r_info))) return NULL;\n\n    if(self->is_use_rela &&\n       (self->group_flags & RELOCATION_GROUP_HAS_ADDEND_FLAG) &&\n       (0 == (self->group_flags & RELOCATION_GROUPED_BY_ADDEND_FLAG)))\n    {\n        if(0 != xh_elf_sleb128_decoder_next(&(self->decoder), &val)) return NULL;\n        self->r_addend += (ssize_t)val;\n    }\n    \n    self->relocation_index++;\n    self->relocation_group_index++;\n\n    if(self->is_use_rela)\n    {\n        self->rela.r_offset = self->r_offset;\n        self->rela.r_info = self->r_info;\n        self->rela.r_addend = self->r_addend;\n        return (void *)(&(self->rela));\n    }\n    else\n    {\n        self->rel.r_offset = self->r_offset;\n        self->rel.r_info = self->r_info;\n        return (void *)(&(self->rel));\n    }\n}\n\n//ELF header checker\nint xh_elf_check_elfheader(uintptr_t base_addr)\n{\n    ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base_addr;\n\n    //check magic\n    if(0 != memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) return XH_ERRNO_FORMAT;\n\n    //check class (64/32)\n#if defined(__LP64__)\n    if(ELFCLASS64 != ehdr->e_ident[EI_CLASS]) return XH_ERRNO_FORMAT;\n#else\n    if(ELFCLASS32 != ehdr->e_ident[EI_CLASS]) return XH_ERRNO_FORMAT;\n#endif\n\n    //check endian (little/big)\n    if(ELFDATA2LSB != ehdr->e_ident[EI_DATA]) return XH_ERRNO_FORMAT;\n\n    //check version\n    if(EV_CURRENT != ehdr->e_ident[EI_VERSION]) return XH_ERRNO_FORMAT;\n\n    //check type\n    if(ET_EXEC != ehdr->e_type && ET_DYN != ehdr->e_type) return XH_ERRNO_FORMAT;\n\n    //check machine\n#if defined(__arm__)\n    if(EM_ARM != ehdr->e_machine) return XH_ERRNO_FORMAT;\n#elif defined(__aarch64__)\n    if(EM_AARCH64 != ehdr->e_machine) return XH_ERRNO_FORMAT;\n#elif defined(__i386__)\n    if(EM_386 != ehdr->e_machine) return XH_ERRNO_FORMAT;\n#elif defined(__x86_64__)\n    if(EM_X86_64 != ehdr->e_machine) return XH_ERRNO_FORMAT;\n#else\n    return XH_ERRNO_FORMAT;\n#endif\n\n    //check version\n    if(EV_CURRENT != ehdr->e_version) return XH_ERRNO_FORMAT;\n\n    return 0;\n}\n\n//ELF hash func\nstatic uint32_t xh_elf_hash(const uint8_t *name)\n{\n    uint32_t h = 0, g;\n\n    while (*name) {\n        h = (h << 4) + *name++;\n        g = h & 0xf0000000;\n        h ^= g;\n        h ^= g >> 24;\n    }\n\n    return h;\n}\n\n//GNU hash func\nstatic uint32_t xh_elf_gnu_hash(const uint8_t *name)\n{\n    uint32_t h = 5381;\n\n    while(*name != 0)\n    {\n        h += (h << 5) + *name++;\n    }\n    return h;\n}\n\nstatic ElfW(Phdr) *xh_elf_get_first_segment_by_type(xh_elf_t *self, ElfW(Word) type)\n{\n    ElfW(Phdr) *phdr;\n    \n    for(phdr = self->phdr; phdr < self->phdr + self->ehdr->e_phnum; phdr++)\n    {\n        if(phdr->p_type == type)\n        {\n            return phdr;\n        }\n    }\n    return NULL;\n}\n\nstatic ElfW(Phdr) *xh_elf_get_first_segment_by_type_offset(xh_elf_t *self, ElfW(Word) type, ElfW(Off) offset)\n{\n    ElfW(Phdr) *phdr;\n    \n    for(phdr = self->phdr; phdr < self->phdr + self->ehdr->e_phnum; phdr++)\n    {\n        if(phdr->p_type == type && phdr->p_offset == offset)\n        {\n            return phdr;\n        }\n    }\n    return NULL;\n}\n\nstatic int xh_elf_hash_lookup(xh_elf_t *self, const char *symbol, uint32_t *symidx)\n{\n    uint32_t    hash = xh_elf_hash((uint8_t *)symbol);\n    const char *symbol_cur;\n    uint32_t    i;\n    \n    for(i = self->bucket[hash % self->bucket_cnt]; 0 != i; i = self->chain[i])\n    {\n        symbol_cur = self->strtab + self->symtab[i].st_name;\n        \n        if(0 == strcmp(symbol, symbol_cur))\n        {\n            *symidx = i;\n            XH_LOG_INFO(\"found %s at symidx: %u (ELF_HASH)\\n\", symbol, *symidx);\n            return 0;\n        }\n    }\n    \n    return XH_ERRNO_NOTFND;\n}\n\nstatic int xh_elf_gnu_hash_lookup_def(xh_elf_t *self, const char *symbol, uint32_t *symidx)\n{\n    uint32_t hash = xh_elf_gnu_hash((uint8_t *)symbol);\n    \n    static uint32_t elfclass_bits = sizeof(ElfW(Addr)) * 8;\n    size_t word = self->bloom[(hash / elfclass_bits) % self->bloom_sz];\n    size_t mask = 0\n        | (size_t)1 << (hash % elfclass_bits)\n        | (size_t)1 << ((hash >> self->bloom_shift) % elfclass_bits);\n    \n    //if at least one bit is not set, this symbol is surely missing\n    if((word & mask) != mask) return XH_ERRNO_NOTFND;\n\n    //ignore STN_UNDEF\n    uint32_t i = self->bucket[hash % self->bucket_cnt];\n    if(i < self->symoffset) return XH_ERRNO_NOTFND;\n    \n    //loop through the chain\n    while(1)\n    {\n        const char     *symname = self->strtab + self->symtab[i].st_name;\n        const uint32_t  symhash = self->chain[i - self->symoffset];\n        \n        if((hash | (uint32_t)1) == (symhash | (uint32_t)1) && 0 == strcmp(symbol, symname))\n        {\n            *symidx = i;\n            XH_LOG_INFO(\"found %s at symidx: %u (GNU_HASH DEF)\\n\", symbol, *symidx);\n            return 0;\n        }\n        \n        //chain ends with an element with the lowest bit set to 1\n        if(symhash & (uint32_t)1) break;\n        \n        i++;\n    }\n    \n    return XH_ERRNO_NOTFND;\n}\n\nstatic int xh_elf_gnu_hash_lookup_undef(xh_elf_t *self, const char *symbol, uint32_t *symidx)\n{\n    uint32_t i;\n    \n    for(i = 0; i < self->symoffset; i++)\n    {\n        const char *symname = self->strtab + self->symtab[i].st_name;\n        if(0 == strcmp(symname, symbol))\n        {\n            *symidx = i;\n            XH_LOG_INFO(\"found %s at symidx: %u (GNU_HASH UNDEF)\\n\", symbol, *symidx);\n            return 0;\n        }\n    }\n    return XH_ERRNO_NOTFND;\n}\n\nstatic int xh_elf_gnu_hash_lookup(xh_elf_t *self, const char *symbol, uint32_t *symidx)\n{\n    if(0 == xh_elf_gnu_hash_lookup_def(self, symbol, symidx)) return 0;\n    if(0 == xh_elf_gnu_hash_lookup_undef(self, symbol, symidx)) return 0;\n    return XH_ERRNO_NOTFND;\n}\n\nstatic int xh_elf_find_symidx_by_name(xh_elf_t *self, const char *symbol, uint32_t *symidx)\n{\n    if(self->is_use_gnu_hash)\n        return xh_elf_gnu_hash_lookup(self, symbol, symidx);\n    else\n        return xh_elf_hash_lookup(self, symbol, symidx);\n}\n\nstatic int xh_elf_replace_function(xh_elf_t *self, const char *symbol, ElfW(Addr) addr, void *new_func, void **old_func)\n{\n    void         *old_addr;\n    unsigned int  old_prot = 0;\n    unsigned int  need_prot = PROT_READ | PROT_WRITE;\n    int           r;\n\n    //already replaced?\n    //here we assume that we always have read permission, is this a problem?\n    if(*(void **)addr == new_func) return 0;\n\n    //get old prot\n    if(0 != (r = xh_util_get_addr_protect(addr, self->pathname, &old_prot)))\n    {\n        XH_LOG_ERROR(\"get addr prot failed. ret: %d\", r);\n        return r;\n    }\n    \n    if(old_prot != need_prot)\n    {\n        //set new prot\n        if(0 != (r = xh_util_set_addr_protect(addr, need_prot)))\n        {\n            XH_LOG_ERROR(\"set addr prot failed. ret: %d\", r);\n            return r;\n        }\n    }\n    \n    //save old func\n    old_addr = *(void **)addr;\n    if(NULL != old_func) *old_func = old_addr;\n\n    //replace func\n    *(void **)addr = new_func; //segmentation fault sometimes\n\n    if(old_prot != need_prot)\n    {\n        //restore the old prot\n        if(0 != (r = xh_util_set_addr_protect(addr, old_prot)))\n        {\n            XH_LOG_WARN(\"restore addr prot failed. ret: %d\", r);\n        }\n    }\n    \n    //clear cache\n    xh_util_flush_instruction_cache(addr);\n\n    XH_LOG_INFO(\"XH_HK_OK %p: %p -> %p %s %s\\n\", (void *)addr, old_addr, new_func, symbol, self->pathname);\n    return 0;\n}\n\nstatic int xh_elf_check(xh_elf_t *self)\n{\n    if(0 == self->base_addr)\n    {\n        XH_LOG_ERROR(\"base_addr == 0\\n\");\n        return 1;\n    }\n    if(0 == self->bias_addr)\n    {\n        XH_LOG_ERROR(\"bias_addr == 0\\n\");\n        return 1;\n    }\n    if(NULL == self->ehdr)\n    {\n        XH_LOG_ERROR(\"ehdr == NULL\\n\");\n        return 1;\n    }\n    if(NULL == self->phdr)\n    {\n        XH_LOG_ERROR(\"phdr == NULL\\n\");\n        return 1;\n    }\n    if(NULL == self->strtab)\n    {\n        XH_LOG_ERROR(\"strtab == NULL\\n\");\n        return 1;\n    }\n    if(NULL == self->symtab)\n    {\n        XH_LOG_ERROR(\"symtab == NULL\\n\");\n        return 1;\n    }\n    if(NULL == self->bucket)\n    {\n        XH_LOG_ERROR(\"bucket == NULL\\n\");\n        return 1;\n    }\n    if(NULL == self->chain)\n    {\n        XH_LOG_ERROR(\"chain == NULL\\n\");\n        return 1;\n    }\n    if(1 == self->is_use_gnu_hash && NULL == self->bloom)\n    {\n        XH_LOG_ERROR(\"bloom == NULL\\n\");\n        return 1;\n    }\n\n    return 0;\n}\n\n#if XH_ELF_DEBUG\n\nstatic void xh_elf_dump_elfheader(xh_elf_t *self)\n{\n    static char alpha_tab[17] = \"0123456789ABCDEF\";\n    int         i;\n    uint8_t     ch;\n    char        buff[EI_NIDENT * 3 + 1];\n\n    for(i = 0; i < EI_NIDENT; i++)\n    {\n        ch = self->ehdr->e_ident[i];\n        buff[i * 3 + 0] = alpha_tab[(int)((ch >> 4) & 0x0F)];\n        buff[i * 3 + 1] = alpha_tab[(int)(ch & 0x0F)];\n        buff[i * 3 + 2] = ' ';\n    }\n    buff[EI_NIDENT * 3] = '\\0';\n\n    XH_LOG_DEBUG(\"Elf Header:\\n\");\n    XH_LOG_DEBUG(\"  Magic:                             %s\\n\",                                 buff);\n    XH_LOG_DEBUG(\"  Class:                             %#x\\n\",                                self->ehdr->e_ident[EI_CLASS]);\n    XH_LOG_DEBUG(\"  Data:                              %#x\\n\",                                self->ehdr->e_ident[EI_DATA]);\n    XH_LOG_DEBUG(\"  Version:                           %#x\\n\",                                self->ehdr->e_ident[EI_VERSION]);\n    XH_LOG_DEBUG(\"  OS/ABI:                            %#x\\n\",                                self->ehdr->e_ident[EI_OSABI]);\n    XH_LOG_DEBUG(\"  ABI Version:                       %#x\\n\",                                self->ehdr->e_ident[EI_ABIVERSION]);\n    XH_LOG_DEBUG(\"  Type:                              %#x\\n\",                                self->ehdr->e_type);\n    XH_LOG_DEBUG(\"  Machine:                           %#x\\n\",                                self->ehdr->e_machine);\n    XH_LOG_DEBUG(\"  Version:                           %#x\\n\",                                self->ehdr->e_version);\n    XH_LOG_DEBUG(\"  Entry point address:               %\"XH_UTIL_FMT_X\"\\n\",                   self->ehdr->e_entry);\n    XH_LOG_DEBUG(\"  Start of program headers:          %\"XH_UTIL_FMT_X\" (bytes into file)\\n\", self->ehdr->e_phoff);\n    XH_LOG_DEBUG(\"  Start of section headers:          %\"XH_UTIL_FMT_X\" (bytes into file)\\n\", self->ehdr->e_shoff);\n    XH_LOG_DEBUG(\"  Flags:                             %#x\\n\",                                self->ehdr->e_flags);\n    XH_LOG_DEBUG(\"  Size of this header:               %u (bytes)\\n\",                         self->ehdr->e_ehsize);\n    XH_LOG_DEBUG(\"  Size of program headers:           %u (bytes)\\n\",                         self->ehdr->e_phentsize);\n    XH_LOG_DEBUG(\"  Number of program headers:         %u\\n\",                                 self->ehdr->e_phnum);\n    XH_LOG_DEBUG(\"  Size of section headers:           %u (bytes)\\n\",                         self->ehdr->e_shentsize);\n    XH_LOG_DEBUG(\"  Number of section headers:         %u\\n\",                                 self->ehdr->e_shnum);\n    XH_LOG_DEBUG(\"  Section header string table index: %u\\n\",                                 self->ehdr->e_shstrndx);\n}\n\nstatic void xh_elf_dump_programheader(xh_elf_t *self)\n{\n    ElfW(Phdr) *phdr = self->phdr;\n    size_t i;\n    \n    XH_LOG_DEBUG(\"Program Headers:\\n\");\n    XH_LOG_DEBUG(\"  %-8s \" \\\n                 \"%-\"XH_UTIL_FMT_FIXED_S\" \" \\\n                 \"%-\"XH_UTIL_FMT_FIXED_S\" \" \\\n                 \"%-\"XH_UTIL_FMT_FIXED_S\" \" \\\n                 \"%-\"XH_UTIL_FMT_FIXED_S\" \" \\\n                 \"%-\"XH_UTIL_FMT_FIXED_S\" \" \\\n                 \"%-8s \" \\\n                 \"%-s\\n\",\n                 \"Type\",\n                 \"Offset\",\n                 \"VirtAddr\",\n                 \"PhysAddr\",\n                 \"FileSiz\",\n                 \"MemSiz\",\n                 \"Flg\",\n                 \"Align\");\n    for(i = 0; i < self->ehdr->e_phnum; i++, phdr++)\n    {\n        XH_LOG_DEBUG(\"  %-8x \" \\\n                     \"%.\"XH_UTIL_FMT_FIXED_X\" \" \\\n                     \"%.\"XH_UTIL_FMT_FIXED_X\" \" \\\n                     \"%.\"XH_UTIL_FMT_FIXED_X\" \" \\\n                     \"%.\"XH_UTIL_FMT_FIXED_X\" \" \\\n                     \"%.\"XH_UTIL_FMT_FIXED_X\" \" \\\n                     \"%-8x \" \\\n                     \"%\"XH_UTIL_FMT_X\"\\n\",\n                     phdr->p_type,\n                     phdr->p_offset,\n                     phdr->p_vaddr,\n                     phdr->p_paddr,\n                     phdr->p_filesz,\n                     phdr->p_memsz,\n                     phdr->p_flags,\n                     phdr->p_align);\n    }\n}\n\nstatic void xh_elf_dump_dynamic(xh_elf_t *self)\n{\n    ElfW(Dyn) *dyn = self->dyn;\n    size_t     dyn_cnt = (self->dyn_sz / sizeof(ElfW(Dyn)));\n    size_t     i;\n\n    XH_LOG_DEBUG(\"Dynamic section contains %zu entries:\\n\", dyn_cnt);\n    XH_LOG_DEBUG(\"  %-\"XH_UTIL_FMT_FIXED_S\" \" \\\n                 \"%s\\n\",\n                 \"Tag\",\n                 \"Val\");\n    for(i = 0; i < dyn_cnt; i++, dyn++)\n    {\n        XH_LOG_DEBUG(\"  %-\"XH_UTIL_FMT_FIXED_X\" \" \\\n                     \"%-\"XH_UTIL_FMT_X\"\\n\",\n                     dyn->d_tag,\n                     dyn->d_un.d_val);\n    }\n}\n\nstatic void xh_elf_dump_rel(xh_elf_t *self, const char *type, ElfW(Addr) rel_addr, ElfW(Word) rel_sz)\n{\n    ElfW(Rela) *rela;\n    ElfW(Rel)  *rel;\n    ElfW(Word)  cnt;\n    ElfW(Word)  i;\n    ElfW(Sym)  *sym;\n\n    if(self->is_use_rela)\n    {\n        rela = (ElfW(Rela) *)(rel_addr);\n        cnt  = rel_sz / sizeof(ElfW(Rela));\n    }\n    else\n    {\n        rel = (ElfW(Rel) *)(rel_addr);\n        cnt = rel_sz / sizeof(ElfW(Rel));\n    }\n\n    XH_LOG_DEBUG(\"Relocation section '.rel%s%s' contains %u entries:\\n\",\n                 (self->is_use_rela ? \"a\" : \"\"), type, cnt);\n    XH_LOG_DEBUG(\"  %-\"XH_UTIL_FMT_FIXED_S\" \" \\\n                 \"%-\"XH_UTIL_FMT_FIXED_S\" \" \\\n                 \"%-8s \" \\\n                 \"%-8s \" \\\n                 \"%-8s \" \\\n                 \"%s\\n\",\n                 \"Offset\",\n                 \"Info\",\n                 \"Type\",\n                 \"Sym.Idx\",\n                 \"Sym.Val\",\n                 \"Sym.Name\");\n    const char *fmt = \"  %.\"XH_UTIL_FMT_FIXED_X\" \" \\\n                      \"%.\"XH_UTIL_FMT_FIXED_X\" \" \\\n                      \"%.8x \" \\\n                      \"%.8u \" \\\n                      \"%.8x \" \\\n                      \"%s\\n\";\n    for(i = 0; i < cnt; i++)\n    {\n        if(self->is_use_rela)\n        {\n            sym = &(self->symtab[XH_ELF_R_SYM(rela[i].r_info)]);\n            XH_LOG_DEBUG(fmt,\n                         rela[i].r_offset,\n                         rela[i].r_info,\n                         XH_ELF_R_TYPE(rela[i].r_info),\n                         XH_ELF_R_SYM(rela[i].r_info),\n                         sym->st_value,\n                         self->strtab + sym->st_name);\n        }\n        else\n        {\n            sym = &(self->symtab[XH_ELF_R_SYM(rel[i].r_info)]);\n            XH_LOG_DEBUG(fmt,\n                         rel[i].r_offset,\n                         rel[i].r_info,\n                         XH_ELF_R_TYPE(rel[i].r_info),\n                         XH_ELF_R_SYM(rel[i].r_info),\n                         sym->st_value,\n                         self->strtab + sym->st_name);\n        }\n    }\n}\n\nstatic void xh_elf_dump_symtab(xh_elf_t *self)\n{\n    if(self->is_use_gnu_hash) return;\n    \n    ElfW(Word)  symtab_cnt = self->chain_cnt;\n    ElfW(Word)  i;\n    \n    XH_LOG_DEBUG(\"Symbol table '.dynsym' contains %u entries:\\n\", symtab_cnt);\n    XH_LOG_DEBUG(\"  %-8s \" \\\n                 \"%-\"XH_UTIL_FMT_FIXED_S\" \" \\\n                 \"%s\\n\",\n                 \"Idx\",\n                 \"Value\",\n                 \"Name\");\n    for(i = 0; i < symtab_cnt; i++)\n    {\n        XH_LOG_DEBUG(\"  %-8u \" \\\n                     \"%.\"XH_UTIL_FMT_FIXED_X\" \" \\\n                     \"%s\\n\",\n                     i,\n                     self->symtab[i].st_value,\n                     self->strtab + self->symtab[i].st_name);\n    }\n}\n\nstatic void xh_elf_dump(xh_elf_t *self)\n{\n    if(xh_log_priority < ANDROID_LOG_DEBUG) return;\n\n    XH_LOG_DEBUG(\"Elf Pathname: %s\\n\", self->pathname);\n    XH_LOG_DEBUG(\"Elf bias addr: %p\\n\", (void *)self->bias_addr);\n    xh_elf_dump_elfheader(self);\n    xh_elf_dump_programheader(self);\n    xh_elf_dump_dynamic(self);\n    xh_elf_dump_rel(self, \".plt\", self->relplt, self->relplt_sz);\n    xh_elf_dump_rel(self, \".dyn\", self->reldyn, self->reldyn_sz);\n    xh_elf_dump_symtab(self);\n}\n\n#endif\n\nint xh_elf_init(xh_elf_t *self, uintptr_t base_addr, const char *pathname)\n{\n    if(0 == base_addr || NULL == pathname) return XH_ERRNO_INVAL;\n\n    //always reset\n    memset(self, 0, sizeof(xh_elf_t));\n    \n    self->pathname = pathname;\n    self->base_addr = (ElfW(Addr))base_addr;\n    self->ehdr = (ElfW(Ehdr) *)base_addr;\n    self->phdr = (ElfW(Phdr) *)(base_addr + self->ehdr->e_phoff); //segmentation fault sometimes\n\n    //find the first load-segment with offset 0\n    ElfW(Phdr) *phdr0 = xh_elf_get_first_segment_by_type_offset(self, PT_LOAD, 0);\n    if(NULL == phdr0)\n    {\n        XH_LOG_ERROR(\"Can NOT found the first load segment. %s\", pathname);\n        return XH_ERRNO_FORMAT;\n    }\n\n#if XH_ELF_DEBUG\n    if(0 != phdr0->p_vaddr)\n        XH_LOG_DEBUG(\"first load-segment vaddr NOT 0 (vaddr: %p). %s\",\n                     (void *)(phdr0->p_vaddr), pathname);\n#endif\n\n    //save load bias addr\n    if(self->base_addr < phdr0->p_vaddr) return XH_ERRNO_FORMAT;\n    self->bias_addr = self->base_addr - phdr0->p_vaddr;\n    \n    //find dynamic-segment\n    ElfW(Phdr) *dhdr = xh_elf_get_first_segment_by_type(self, PT_DYNAMIC);\n    if(NULL == dhdr)\n    {\n        XH_LOG_ERROR(\"Can NOT found dynamic segment. %s\", pathname);\n        return XH_ERRNO_FORMAT;\n    }\n\n    //parse dynamic-segment\n    self->dyn          = (ElfW(Dyn) *)(self->bias_addr + dhdr->p_vaddr);\n    self->dyn_sz       = dhdr->p_memsz;\n    ElfW(Dyn) *dyn     = self->dyn;\n    ElfW(Dyn) *dyn_end = self->dyn + (self->dyn_sz / sizeof(ElfW(Dyn)));\n    uint32_t  *raw;\n    for(; dyn < dyn_end; dyn++)\n    {\n        switch(dyn->d_tag) //segmentation fault sometimes\n        {\n        case DT_NULL:\n            //the end of the dynamic-section\n            dyn = dyn_end;\n            break;\n        case DT_STRTAB:\n            {\n                self->strtab = (const char *)(self->bias_addr + dyn->d_un.d_ptr);\n                if((ElfW(Addr))(self->strtab) < self->base_addr) return XH_ERRNO_FORMAT;\n                break;\n            }\n        case DT_SYMTAB:\n            {\n                self->symtab = (ElfW(Sym) *)(self->bias_addr + dyn->d_un.d_ptr);\n                if((ElfW(Addr))(self->symtab) < self->base_addr) return XH_ERRNO_FORMAT;\n                break;\n            }\n        case DT_PLTREL:\n            //use rel or rela?\n            self->is_use_rela = (dyn->d_un.d_val == DT_RELA ? 1 : 0);\n            break;\n        case DT_JMPREL:\n            {\n                self->relplt = (ElfW(Addr))(self->bias_addr + dyn->d_un.d_ptr);\n                if((ElfW(Addr))(self->relplt) < self->base_addr) return XH_ERRNO_FORMAT;\n                break;\n            }\n        case DT_PLTRELSZ:\n            self->relplt_sz = dyn->d_un.d_val;\n            break;\n        case DT_REL:\n        case DT_RELA:\n            {\n                self->reldyn = (ElfW(Addr))(self->bias_addr + dyn->d_un.d_ptr);\n                if((ElfW(Addr))(self->reldyn) < self->base_addr) return XH_ERRNO_FORMAT;\n                break;\n            }\n        case DT_RELSZ:\n        case DT_RELASZ:\n            self->reldyn_sz = dyn->d_un.d_val;\n            break;\n        case DT_ANDROID_REL:\n        case DT_ANDROID_RELA:\n            {\n                self->relandroid = (ElfW(Addr))(self->bias_addr + dyn->d_un.d_ptr);\n                if((ElfW(Addr))(self->relandroid) < self->base_addr) return XH_ERRNO_FORMAT;\n                break;\n            }\n        case DT_ANDROID_RELSZ:\n        case DT_ANDROID_RELASZ:\n            self->relandroid_sz = dyn->d_un.d_val;\n            break;\n        case DT_HASH:\n            {\n                //ignore DT_HASH when ELF contains DT_GNU_HASH hash table\n                if(1 == self->is_use_gnu_hash) continue;\n\n                raw = (uint32_t *)(self->bias_addr + dyn->d_un.d_ptr);\n                if((ElfW(Addr))raw < self->base_addr) return XH_ERRNO_FORMAT;\n                self->bucket_cnt  = raw[0];\n                self->chain_cnt   = raw[1];\n                self->bucket      = &raw[2];\n                self->chain       = &(self->bucket[self->bucket_cnt]);\n                break;\n            }\n        case DT_GNU_HASH:\n            {\n                raw = (uint32_t *)(self->bias_addr + dyn->d_un.d_ptr);\n                if((ElfW(Addr))raw < self->base_addr) return XH_ERRNO_FORMAT;\n                self->bucket_cnt  = raw[0];\n                self->symoffset   = raw[1];\n                self->bloom_sz    = raw[2];\n                self->bloom_shift = raw[3];\n                self->bloom       = (ElfW(Addr) *)(&raw[4]);\n                self->bucket      = (uint32_t *)(&(self->bloom[self->bloom_sz]));\n                self->chain       = (uint32_t *)(&(self->bucket[self->bucket_cnt]));\n                self->is_use_gnu_hash = 1;\n                break;\n            }\n        default:\n            break;\n        }\n    }\n\n    //check android rel/rela\n    if(0 != self->relandroid)\n    {\n        const char *rel = (const char *)self->relandroid;\n        if(self->relandroid_sz < 4 ||\n           rel[0] != 'A' ||\n           rel[1] != 'P' ||\n           rel[2] != 'S' ||\n           rel[3] != '2')\n        {\n            XH_LOG_ERROR(\"android rel/rela format error\\n\");\n            return XH_ERRNO_FORMAT;\n        }\n        \n        self->relandroid += 4;\n        self->relandroid_sz -= 4;\n    }\n\n    //check elf info\n    if(0 != xh_elf_check(self))\n    {\n        XH_LOG_ERROR(\"elf init check failed. %s\", pathname);\n        return XH_ERRNO_FORMAT;\n    }\n    \n#if XH_ELF_DEBUG\n    xh_elf_dump(self);\n#endif\n\n    XH_LOG_INFO(\"init OK: %s (%s %s PLT:%u DYN:%u ANDROID:%u)\\n\", self->pathname,\n                self->is_use_rela ? \"RELA\" : \"REL\",\n                self->is_use_gnu_hash ? \"GNU_HASH\" : \"ELF_HASH\",\n                self->relplt_sz, self->reldyn_sz, self->relandroid_sz);\n\n    return 0;\n}\n\nstatic int xh_elf_find_and_replace_func(xh_elf_t *self, const char *section,\n                                        int is_plt, const char *symbol,\n                                        void *new_func, void **old_func,\n                                        uint32_t symidx, void *rel_common,\n                                        int *found)\n{\n    ElfW(Rela)    *rela;\n    ElfW(Rel)     *rel;\n    ElfW(Addr)     r_offset;\n    size_t         r_info;\n    size_t         r_sym;\n    size_t         r_type;\n    ElfW(Addr)     addr;\n    int            r;\n\n    if(NULL != found) *found = 0;\n    \n    if(self->is_use_rela)\n    {\n        rela = (ElfW(Rela) *)rel_common;\n        r_info = rela->r_info;\n        r_offset = rela->r_offset;\n    }\n    else\n    {\n        rel = (ElfW(Rel) *)rel_common;\n        r_info = rel->r_info;\n        r_offset = rel->r_offset;\n    }\n\n    //check sym\n    r_sym = XH_ELF_R_SYM(r_info);\n    if(r_sym != symidx) return 0;\n\n    //check type\n    r_type = XH_ELF_R_TYPE(r_info);\n    if(is_plt && r_type != XH_ELF_R_GENERIC_JUMP_SLOT) return 0;\n    if(!is_plt && (r_type != XH_ELF_R_GENERIC_GLOB_DAT && r_type != XH_ELF_R_GENERIC_ABS)) return 0;\n\n    //we found it\n    XH_LOG_INFO(\"found %s at %s offset: %p\\n\", symbol, section, (void *)r_offset);\n    if(NULL != found) *found = 1;\n\n    //do replace\n    addr = self->bias_addr + r_offset;\n    if(addr < self->base_addr) return XH_ERRNO_FORMAT;\n    if(0 != (r = xh_elf_replace_function(self, symbol, addr, new_func, old_func)))\n    {\n        XH_LOG_ERROR(\"replace function failed: %s at %s\\n\", symbol, section);\n        return r;\n    }\n\n    return 0;\n}\n\nint xh_elf_hook(xh_elf_t *self, const char *symbol, void *new_func, void **old_func)\n{\n    uint32_t                        symidx;\n    void                           *rel_common;\n    xh_elf_plain_reloc_iterator_t   plain_iter;\n    xh_elf_packed_reloc_iterator_t  packed_iter;\n    int                             found;\n    int                             r;\n\n    if(NULL == self->pathname)\n    {\n        XH_LOG_ERROR(\"not inited\\n\");\n        return XH_ERRNO_ELFINIT; //not inited?\n    }\n\n    if(NULL == symbol || NULL == new_func) return XH_ERRNO_INVAL;\n\n    XH_LOG_INFO(\"hooking %s in %s\\n\", symbol, self->pathname);\n    \n    //find symbol index by symbol name\n    if(0 != (r = xh_elf_find_symidx_by_name(self, symbol, &symidx))) return 0;\n    \n    //replace for .rel(a).plt\n    if(0 != self->relplt)\n    {\n        xh_elf_plain_reloc_iterator_init(&plain_iter, self->relplt, self->relplt_sz, self->is_use_rela);\n        while(NULL != (rel_common = xh_elf_plain_reloc_iterator_next(&plain_iter)))\n        {\n            if(0 != (r = xh_elf_find_and_replace_func(self,\n                                                      (self->is_use_rela ? \".rela.plt\" : \".rel.plt\"), 1,\n                                                      symbol, new_func, old_func,\n                                                      symidx, rel_common, &found))) return r;\n            if(found) break;\n        }\n    }\n\n    //replace for .rel(a).dyn\n    if(0 != self->reldyn)\n    {\n        xh_elf_plain_reloc_iterator_init(&plain_iter, self->reldyn, self->reldyn_sz, self->is_use_rela);\n        while(NULL != (rel_common = xh_elf_plain_reloc_iterator_next(&plain_iter)))\n        {\n            if(0 != (r = xh_elf_find_and_replace_func(self,\n                                                      (self->is_use_rela ? \".rela.dyn\" : \".rel.dyn\"), 0,\n                                                      symbol, new_func, old_func,\n                                                      symidx, rel_common, NULL))) return r;\n        }\n    }\n\n    //replace for .rel(a).android\n    if(0 != self->relandroid)\n    {\n        xh_elf_packed_reloc_iterator_init(&packed_iter, self->relandroid, self->relandroid_sz, self->is_use_rela);\n        while(NULL != (rel_common = xh_elf_packed_reloc_iterator_next(&packed_iter)))\n        {\n            if(0 != (r = xh_elf_find_and_replace_func(self,\n                                                      (self->is_use_rela ? \".rela.android\" : \".rel.android\"), 0,\n                                                      symbol, new_func, old_func,\n                                                      symidx, rel_common, NULL))) return r;\n        }\n    }\n    \n    return 0;\n}\n"
  },
  {
    "path": "libxhook/jni/xh_elf.h",
    "content": "// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n//\n\n// Created by caikelun on 2018-04-11.\n\n#ifndef XH_ELF_H\n#define XH_ELF_H 1\n\n#include <stdint.h>\n#include <elf.h>\n#include <link.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct\n{\n    const char *pathname;\n    \n    ElfW(Addr)  base_addr;\n    ElfW(Addr)  bias_addr;\n    \n    ElfW(Ehdr) *ehdr;\n    ElfW(Phdr) *phdr;\n\n    ElfW(Dyn)  *dyn; //.dynamic\n    ElfW(Word)  dyn_sz;\n\n    const char *strtab; //.dynstr (string-table)\n    ElfW(Sym)  *symtab; //.dynsym (symbol-index to string-table's offset)\n\n    ElfW(Addr)  relplt; //.rel.plt or .rela.plt\n    ElfW(Word)  relplt_sz;\n    \n    ElfW(Addr)  reldyn; //.rel.dyn or .rela.dyn\n    ElfW(Word)  reldyn_sz;\n    \n    ElfW(Addr)  relandroid; //android compressed rel or rela\n    ElfW(Word)  relandroid_sz;\n\n    //for ELF hash\n    uint32_t   *bucket;\n    uint32_t    bucket_cnt;\n    uint32_t   *chain;\n    uint32_t    chain_cnt; //invalid for GNU hash\n\n    //append for GNU hash\n    uint32_t    symoffset;\n    ElfW(Addr) *bloom;\n    uint32_t    bloom_sz;\n    uint32_t    bloom_shift;\n    \n    int         is_use_rela;\n    int         is_use_gnu_hash;\n} xh_elf_t;\n\nint xh_elf_init(xh_elf_t *self, uintptr_t base_addr, const char *pathname);\nint xh_elf_hook(xh_elf_t *self, const char *symbol, void *new_func, void **old_func);\n\nint xh_elf_check_elfheader(uintptr_t base_addr);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "libxhook/jni/xh_errno.h",
    "content": "// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n//\n\n// Created by caikelun on 2018-04-11.\n\n#ifndef XH_ERRNO_H\n#define XH_ERRNO_H 1\n\n#define XH_ERRNO_UNKNOWN 1001\n#define XH_ERRNO_INVAL   1002\n#define XH_ERRNO_NOMEM   1003\n#define XH_ERRNO_REPEAT  1004\n#define XH_ERRNO_NOTFND  1005\n#define XH_ERRNO_BADMAPS 1006\n#define XH_ERRNO_FORMAT  1007\n#define XH_ERRNO_ELFINIT 1008\n#define XH_ERRNO_SEGVERR 1009\n\n#endif\n"
  },
  {
    "path": "libxhook/jni/xh_jni.c",
    "content": "// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n//\n\n// Created by caikelun on 2018-04-11.\n\n#include <jni.h>\n#include \"xhook.h\"\n\n#define JNI_API_DEF(f) Java_com_qiyi_xhook_NativeHandler_##f\n\nJNIEXPORT jint JNI_API_DEF(refresh)(JNIEnv *env, jobject obj, jboolean async)\n{\n    (void)env;\n    (void)obj;\n\n    return xhook_refresh(async ? 1 : 0);\n}\n\nJNIEXPORT void JNI_API_DEF(clear)(JNIEnv *env, jobject obj)\n{\n    (void)env;\n    (void)obj;\n\n    xhook_clear();\n}\n\nJNIEXPORT void JNI_API_DEF(enableDebug)(JNIEnv *env, jobject obj, jboolean flag)\n{\n    (void)env;\n    (void)obj;\n\n    xhook_enable_debug(flag ? 1 : 0);\n}\n\nJNIEXPORT void JNI_API_DEF(enableSigSegvProtection)(JNIEnv *env, jobject obj, jboolean flag)\n{\n    (void)env;\n    (void)obj;\n\n    xhook_enable_sigsegv_protection(flag ? 1 : 0);\n}\n"
  },
  {
    "path": "libxhook/jni/xh_log.c",
    "content": "// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n//\n\n// Created by caikelun on 2018-04-11.\n\n#include <android/log.h>\n#include \"xh_log.h\"\n\nandroid_LogPriority xh_log_priority = ANDROID_LOG_WARN;\n"
  },
  {
    "path": "libxhook/jni/xh_log.h",
    "content": "// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n//\n\n// Created by caikelun on 2018-04-11.\n\n#ifndef XH_LOG_H\n#define XH_LOG_H 1\n\n#include <android/log.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern android_LogPriority xh_log_priority;\n\n#define XH_LOG_TAG \"xhook\"\n#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)\n#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)\n#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)\n#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)\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "libxhook/jni/xh_util.c",
    "content": "// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n//\n\n// Created by caikelun on 2018-04-11.\n\n#include <unistd.h>\n#include <stdint.h>\n#include <inttypes.h>\n#include <elf.h>\n#include <link.h>\n#include <string.h>\n#include <stdio.h>\n#include <errno.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <sys/syscall.h>\n#include \"xh_util.h\"\n#include \"xh_errno.h\"\n#include \"xh_log.h\"\n\n#define PAGE_START(addr) ((addr) & PAGE_MASK)\n#define PAGE_END(addr)   (PAGE_START(addr + sizeof(uintptr_t) - 1) + PAGE_SIZE)\n#define PAGE_COVER(addr) (PAGE_END(addr) - PAGE_START(addr))\n\nint xh_util_get_mem_protect(uintptr_t addr, size_t len, const char *pathname, unsigned int *prot)\n{\n    uintptr_t  start_addr = addr;\n    uintptr_t  end_addr = addr + len;\n    FILE      *fp;\n    char       line[512];\n    uintptr_t  start, end;\n    char       perm[5];\n    int        load0 = 1;\n    int        found_all = 0;\n\n    *prot = 0;\n    \n    if(NULL == (fp = fopen(\"/proc/self/maps\", \"r\"))) return XH_ERRNO_BADMAPS;\n    \n    while(fgets(line, sizeof(line), fp))\n    {\n        if(NULL != pathname)\n            if(NULL == strstr(line, pathname)) continue;\n        \n        if(sscanf(line, \"%\"PRIxPTR\"-%\"PRIxPTR\" %4s \", &start, &end, perm) != 3) continue;\n        \n        if(perm[3] != 'p') continue;\n        \n        if(start_addr >= start && start_addr < end)\n        {\n            if(load0)\n            {\n                //first load segment\n                if(perm[0] == 'r') *prot |= PROT_READ;\n                if(perm[1] == 'w') *prot |= PROT_WRITE;\n                if(perm[2] == 'x') *prot |= PROT_EXEC;\n                load0 = 0;\n            }\n            else\n            {\n                //others\n                if(perm[0] != 'r') *prot &= ~PROT_READ;\n                if(perm[1] != 'w') *prot &= ~PROT_WRITE;\n                if(perm[2] != 'x') *prot &= ~PROT_EXEC;\n            }\n\n            if(end_addr <= end)\n            {\n                found_all = 1;\n                break; //finished\n            }\n            else\n            {\n                start_addr = end; //try to find the next load segment\n            }\n        }\n    }\n    \n    fclose(fp);\n\n    if(!found_all) return XH_ERRNO_SEGVERR;\n    \n    return 0;\n}\n\nint xh_util_get_addr_protect(uintptr_t addr, const char *pathname, unsigned int *prot)\n{\n    return xh_util_get_mem_protect(addr, sizeof(addr), pathname, prot);\n}\n\nint xh_util_set_addr_protect(uintptr_t addr, unsigned int prot)\n{\n    if(0 != mprotect((void *)PAGE_START(addr), PAGE_COVER(addr), (int)prot))\n        return 0 == errno ? XH_ERRNO_UNKNOWN : errno;\n    \n    return 0;\n}\n\nvoid xh_util_flush_instruction_cache(uintptr_t addr)\n{\n    __builtin___clear_cache((void *)PAGE_START(addr), (void *)PAGE_END(addr));\n}\n"
  },
  {
    "path": "libxhook/jni/xh_util.h",
    "content": "// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n//\n\n// Created by caikelun on 2018-04-11.\n\n#ifndef XH_UTILS_H\n#define XH_UTILS_H 1\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#if defined(__LP64__)\n#define XH_UTIL_FMT_LEN     \"16\"\n#define XH_UTIL_FMT_X       \"llx\"\n#else\n#define XH_UTIL_FMT_LEN     \"8\"\n#define XH_UTIL_FMT_X       \"x\"\n#endif\n\n#define XH_UTIL_FMT_FIXED_X XH_UTIL_FMT_LEN XH_UTIL_FMT_X\n#define XH_UTIL_FMT_FIXED_S XH_UTIL_FMT_LEN \"s\"\n\nint xh_util_get_mem_protect(uintptr_t addr, size_t len, const char *pathname, unsigned int *prot);\nint xh_util_get_addr_protect(uintptr_t addr, const char *pathname, unsigned int *prot);\nint xh_util_set_addr_protect(uintptr_t addr, unsigned int prot);\nvoid xh_util_flush_instruction_cache(uintptr_t addr);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "libxhook/jni/xh_version.c",
    "content": "// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n//\n\n// Created by caikelun on 2018-04-11.\n\n#include \"xh_version.h\"\n\n#define XH_VERSION_MAJOR 1\n#define XH_VERSION_MINOR 2\n#define XH_VERSION_EXTRA 0\n\n#define XH_VERSION ((XH_VERSION_MAJOR << 16) | (XH_VERSION_MINOR <<  8) | (XH_VERSION_EXTRA))\n\n#define XH_VERSION_TO_STR_HELPER(x) #x\n#define XH_VERSION_TO_STR(x) XH_VERSION_TO_STR_HELPER(x)\n\n#define XH_VERSION_STR XH_VERSION_TO_STR(XH_VERSION_MAJOR) \".\" \\\n                       XH_VERSION_TO_STR(XH_VERSION_MINOR) \".\" \\\n                       XH_VERSION_TO_STR(XH_VERSION_EXTRA)\n\n#if defined(__arm__)\n#define XH_VERSION_ARCH \"arm\"\n#elif defined(__aarch64__)\n#define XH_VERSION_ARCH \"aarch64\"\n#elif defined(__i386__)\n#define XH_VERSION_ARCH \"x86\"\n#elif defined(__x86_64__)\n#define XH_VERSION_ARCH \"x86_64\"\n#else\n#define XH_VERSION_ARCH \"unknown\"\n#endif\n\n#define XH_VERSION_STR_FULL \"libxhook \"XH_VERSION_STR\" (\"XH_VERSION_ARCH\")\"\n\nunsigned int xh_version()\n{\n    return XH_VERSION;\n}\n\nconst char *xh_version_str()\n{\n    return XH_VERSION_STR;\n}\n\nconst char *xh_version_str_full()\n{\n    return XH_VERSION_STR_FULL;\n}\n"
  },
  {
    "path": "libxhook/jni/xh_version.h",
    "content": "// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n//\n\n// Created by caikelun on 2018-04-11.\n\n#ifndef XH_VERSION_H\n#define XH_VERSION_H 1\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nunsigned int xh_version();\n\nconst char *xh_version_str();\n\nconst char *xh_version_str_full();\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "libxhook/jni/xhook.c",
    "content": "// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n//\n\n// Created by caikelun on 2018-04-11.\n\n#include \"xh_core.h\"\n#include \"xhook.h\"\n\nint xhook_register(const char *pathname_regex_str, const char *symbol,\n                   void *new_func, void **old_func)\n{\n    return xh_core_register(pathname_regex_str, symbol, new_func, old_func);\n}\n\nint xhook_ignore(const char *pathname_regex_str, const char *symbol)\n{\n    return xh_core_ignore(pathname_regex_str, symbol);\n}\n\nint xhook_refresh(int async)\n{\n    return xh_core_refresh(async);\n}\n\nvoid xhook_clear()\n{\n    return xh_core_clear();\n}\n\nvoid xhook_enable_debug(int flag)\n{\n    return xh_core_enable_debug(flag);\n}\n\nvoid xhook_enable_sigsegv_protection(int flag)\n{\n    return xh_core_enable_sigsegv_protection(flag);\n}\n"
  },
  {
    "path": "libxhook/jni/xhook.h",
    "content": "// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n//\n\n// Created by caikelun on 2018-04-11.\n\n#ifndef XHOOK_H\n#define XHOOK_H 1\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define XHOOK_EXPORT __attribute__((visibility(\"default\")))\n\nint xhook_register(const char *pathname_regex_str, const char *symbol,\n                   void *new_func, void **old_func) XHOOK_EXPORT;\n\nint xhook_ignore(const char *pathname_regex_str, const char *symbol) XHOOK_EXPORT;\n\nint xhook_refresh(int async) XHOOK_EXPORT;\n\nvoid xhook_clear() XHOOK_EXPORT;\n\nvoid xhook_enable_debug(int flag) XHOOK_EXPORT;\n\nvoid xhook_enable_sigsegv_protection(int flag) XHOOK_EXPORT;\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "xhookwrapper/.gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n"
  },
  {
    "path": "xhookwrapper/app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "xhookwrapper/app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 29\n    defaultConfig {\n        applicationId \"com.qiyi.xhookwrapper\"\n        minSdkVersion 14\n        targetSdkVersion 29\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    sourceSets {\n        main {\n            jniLibs.srcDirs = ['libs']\n        }\n    }\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    implementation project(':xhook')\n    implementation project(':biz')\n    implementation 'androidx.appcompat:appcompat:1.1.0'\n    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'\n}\n"
  },
  {
    "path": "xhookwrapper/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "xhookwrapper/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.qiyi.xhookwrapper\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n        <activity android:name=\".MainActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "xhookwrapper/app/src/main/java/com/qiyi/test/NativeHandler.java",
    "content": "package com.qiyi.test;\n\n/**\n * Created by caikelun on 18/01/2018.\n */\n\npublic class NativeHandler {\n    private static final NativeHandler ourInstance = new NativeHandler();\n\n    public static NativeHandler getInstance() {\n        return ourInstance;\n    }\n\n    private NativeHandler() {\n    }\n\n    public native void start();\n}\n"
  },
  {
    "path": "xhookwrapper/app/src/main/java/com/qiyi/test/Test.java",
    "content": "package com.qiyi.test;\n\n/**\n * Created by caikelun on 18/01/2018.\n */\n\npublic class Test {\n    private static final Test ourInstance = new Test();\n\n    public static Test getInstance() {\n        return ourInstance;\n    }\n\n    private Test() {\n    }\n\n    public synchronized void init() {\n        System.loadLibrary(\"test\");\n    }\n\n    public synchronized void start() {\n        com.qiyi.test.NativeHandler.getInstance().start();\n    }\n}\n"
  },
  {
    "path": "xhookwrapper/app/src/main/java/com/qiyi/xhookwrapper/MainActivity.java",
    "content": "package com.qiyi.xhookwrapper;\n\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.os.Bundle;\nimport android.util.Log;\n\npublic class MainActivity extends AppCompatActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n\n        //load xhook\n        com.qiyi.xhook.XHook.getInstance().init(this.getApplicationContext());\n        if(!com.qiyi.xhook.XHook.getInstance().isInited()) {\n            return;\n        }\n        //com.qiyi.xhook.XHook.getInstance().enableDebug(true); //default is false\n        //com.qiyi.xhook.XHook.getInstance().enableSigSegvProtection(false); //default is true\n\n        //load and run your biz lib (for register hook points)\n        com.qiyi.biz.Biz.getInstance().init();\n        com.qiyi.biz.Biz.getInstance().start();\n\n        //xhook do refresh\n        com.qiyi.xhook.XHook.getInstance().refresh(false);\n\n        //load and run the target lib\n        com.qiyi.test.Test.getInstance().init();\n        com.qiyi.test.Test.getInstance().start();\n        try {\n            Thread.sleep(200);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n\n        //xhook do refresh again\n        com.qiyi.xhook.XHook.getInstance().refresh(false);\n\n        //xhook do refresh again for some reason,\n        //maybe called after some System.loadLibrary() and System.load()\n        //*\n        new Thread(new Runnable() {\n            @Override\n            public void run() {\n                while(true)\n                {\n                    com.qiyi.xhook.XHook.getInstance().refresh(true);\n\n                    try {\n                        Thread.sleep(5000);\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                }\n            }\n        }).start();\n        //*/\n    }\n}\n"
  },
  {
    "path": "xhookwrapper/app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillColor=\"#26A69A\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n</vector>\n"
  },
  {
    "path": "xhookwrapper/app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillType=\"evenOdd\"\n        android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n        android:strokeColor=\"#00000000\"\n        android:strokeWidth=\"1\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"78.5885\"\n                android:endY=\"90.9159\"\n                android:startX=\"48.7653\"\n                android:startY=\"61.0927\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n        android:strokeColor=\"#00000000\"\n        android:strokeWidth=\"1\" />\n</vector>\n"
  },
  {
    "path": "xhookwrapper/app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    tools:context=\".MainActivity\">\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"Hello World!\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "xhookwrapper/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "xhookwrapper/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "xhookwrapper/app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n</resources>\n"
  },
  {
    "path": "xhookwrapper/app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">xhookwrapper</string>\n</resources>\n"
  },
  {
    "path": "xhookwrapper/app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "xhookwrapper/biz/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "xhookwrapper/biz/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 29\n    defaultConfig {\n        minSdkVersion 14\n        targetSdkVersion 29\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    sourceSets {\n        main {\n            jniLibs.srcDirs = ['libs']\n        }\n    }\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n\n    implementation 'androidx.appcompat:appcompat:1.1.0'\n    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'\n}\n"
  },
  {
    "path": "xhookwrapper/biz/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "xhookwrapper/biz/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.qiyi.biz\" />\n"
  },
  {
    "path": "xhookwrapper/biz/src/main/java/com/qiyi/biz/Biz.java",
    "content": "package com.qiyi.biz;\n\n/**\n * Created by caikelun on 18/01/2018.\n */\n\npublic class Biz {\n    private static final Biz ourInstance = new Biz();\n\n    public static Biz getInstance() {\n        return ourInstance;\n    }\n\n    private Biz() {\n    }\n\n    public synchronized void init() {\n        System.loadLibrary(\"biz\");\n    }\n\n    public synchronized void start() {\n        com.qiyi.biz.NativeHandler.getInstance().start();\n    }\n}\n"
  },
  {
    "path": "xhookwrapper/biz/src/main/java/com/qiyi/biz/NativeHandler.java",
    "content": "package com.qiyi.biz;\n\n/**\n * Created by caikelun on 18/01/2018.\n */\n\npublic class NativeHandler {\n    private static final NativeHandler ourInstance = new NativeHandler();\n\n    public static NativeHandler getInstance() {\n        return ourInstance;\n    }\n\n    private NativeHandler() {\n    }\n\n    public native void start();\n}\n"
  },
  {
    "path": "xhookwrapper/biz/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">biz</string>\n</resources>\n"
  },
  {
    "path": "xhookwrapper/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    \n    repositories {\n        google()\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:3.3.2'\n        \n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        jcenter()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "xhookwrapper/gradle/wrapper/gradle-wrapper.properties",
    "content": "#Tue Mar 26 19:19:54 CST 2019\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.10.1-all.zip\n"
  },
  {
    "path": "xhookwrapper/gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n"
  },
  {
    "path": "xhookwrapper/gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "xhookwrapper/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "xhookwrapper/settings.gradle",
    "content": "include ':app', ':xhook', ':biz'\n"
  },
  {
    "path": "xhookwrapper/xhook/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "xhookwrapper/xhook/build.gradle",
    "content": "apply plugin: 'com.android.library'\napply plugin: 'maven'\n\ndef version = \"1.2.0\"\n\nandroid {\n    compileSdkVersion 29\n    buildToolsVersion '29.0.2'\n    defaultConfig {\n        minSdkVersion 14\n        targetSdkVersion 29\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_6\n        targetCompatibility JavaVersion.VERSION_1_6\n    }\n    buildTypes {\n        debug {\n            minifyEnabled false\n            useProguard false\n        }\n        release {\n            minifyEnabled false\n            useProguard false\n        }\n    }\n    sourceSets {\n        main {\n            jniLibs.srcDirs = ['libs']\n        }\n    }\n    uploadArchives {\n        repositories {\n            mavenDeployer {\n                repository(url: \"\") {\n                    authentication(userName: \"\", password: \"\")\n                }\n                pom.version = \"$version\"\n                pom.artifactId = \"xhook\"\n                pom.groupId = \"com.qiyi.xhook\"\n            }\n        }\n    }\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n}\n"
  },
  {
    "path": "xhookwrapper/xhook/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "xhookwrapper/xhook/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.qiyi.xhook\"\n    android:versionCode=\"1\"\n    android:versionName=\"1.0\" />\n"
  },
  {
    "path": "xhookwrapper/xhook/src/main/java/com/qiyi/xhook/NativeHandler.java",
    "content": "package com.qiyi.xhook;\n\n/**\n * Created by caikelun on 18/01/2018.\n */\n\npublic class NativeHandler {\n    private static final NativeHandler ourInstance = new NativeHandler();\n\n    public static NativeHandler getInstance() {\n        return ourInstance;\n    }\n\n    private NativeHandler() {\n    }\n\n    public native int refresh(boolean async);\n\n    public native void clear();\n\n    public native void enableDebug(boolean flag);\n\n    public native void enableSigSegvProtection(boolean flag);\n\n}\n"
  },
  {
    "path": "xhookwrapper/xhook/src/main/java/com/qiyi/xhook/XHook.java",
    "content": "package com.qiyi.xhook;\n\nimport android.content.Context;\nimport android.util.Log;\n\n/**\n * Created by caikelun on 18/01/2018.\n */\n\npublic class XHook {\n    private static final XHook ourInstance = new XHook();\n    private static boolean inited = false;\n\n    public static XHook getInstance() {\n        return ourInstance;\n    }\n\n    private XHook() {\n    }\n\n    /**\n     * Check if xhook has inited.\n     * @return true if xhook has inited, false otherwise.\n     */\n    public synchronized boolean isInited() {\n        return inited;\n    }\n\n    /**\n     * Init xhook.\n     * @param ctx The application context.\n     * @return true if successful, false otherwise.\n     */\n    public synchronized boolean init(Context ctx) {\n        if(inited) {\n            return true;\n        }\n\n        try {\n            System.loadLibrary(\"xhook\");\n            inited = true;\n        } catch (Throwable e) {\n            try {\n                System.load(ctx.getFilesDir().getParent() + \"/lib/libxhook.so\");\n                inited = true;\n            } catch (Throwable ex) {\n                ex.printStackTrace();\n                Log.e(\"xhook\", \"load libxhook.so failed\");\n            }\n        }\n        return inited;\n    }\n\n    /**\n     * Re-hook after System.loadLibrary() and System.load().\n     * @param async true if to refresh in async mode; otherwise, refresh in sync mode.\n     * @return 0 if successful, false otherwise.\n     */\n    public synchronized void refresh(boolean async) {\n        if(!inited) {\n            return;\n        }\n\n        try {\n            com.qiyi.xhook.NativeHandler.getInstance().refresh(async);\n        } catch (Throwable ex) {\n            ex.printStackTrace();\n            Log.e(\"xhook\", \"xhook native refresh failed\");\n        }\n    }\n\n    /**\n     * Clear all cache.\n     */\n    public synchronized void clear() {\n        if(!inited) {\n            return;\n        }\n\n        try {\n            com.qiyi.xhook.NativeHandler.getInstance().clear();\n        } catch (Throwable ex) {\n            ex.printStackTrace();\n            Log.e(\"xhook\", \"xhook native clear failed\");\n        }\n    }\n\n    /**\n     * Enable/disable the debug log to logcat. (disabled by default)\n     * @param flag the bool flag.\n     */\n    public synchronized void enableDebug(boolean flag) {\n        if(!inited) {\n            return;\n        }\n\n        try {\n            com.qiyi.xhook.NativeHandler.getInstance().enableDebug(flag);\n        } catch (Throwable ex) {\n            ex.printStackTrace();\n            Log.e(\"xhook\", \"xhook native enableDebug failed\");\n        }\n    }\n\n    /**\n     * Enable/disable the segmentation fault protection. (enabled by default)\n     * @param flag the bool flag.\n     */\n    public synchronized void enableSigSegvProtection(boolean flag) {\n        if (!inited) {\n            return;\n        }\n\n        try {\n            com.qiyi.xhook.NativeHandler.getInstance().enableSigSegvProtection(flag);\n        } catch (Throwable ex) {\n            ex.printStackTrace();\n            Log.e(\"xhook\", \"xhook native enableSigSegvProtection failed\");\n        }\n    }\n}\n"
  },
  {
    "path": "xhookwrapper/xhook/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">xhook</string>\n</resources>\n"
  }
]