diff --git a/CHINESE.md b/CHINESE.md
index 12e0613cf..02a7f3c65 100644
--- a/CHINESE.md
+++ b/CHINESE.md
@@ -14,11 +14,31 @@ VA目前被广泛应用于双开/多开、应用市场、模拟定位、一键
 
 申明
 ---
-当您需要将VirtualApp用于**商业用途**时，请务必联系QQ：10890 购买商业授权。您如果未经授权将VirtualApp的App模块作为您自己的App用于牟利或上传软件市场，我们取证后将直接报警（侵犯著作权罪）。购买商业授权是对我们最大的支持和认可，我们将投入更多精力和时间来不断完善优化VirtualApp，作为购买商业授权的回报，您可以获得未开放的商业版本和1vs1的支持（技术、运营、预警）！同时我们也支持基于VirtualApp的APP订制开发，请联系：QQ：10890 洽谈。
-
-请注意
------
-VirtualApp代码的更新频率非常快（`以小时为单位`），每一次代码的更新都有可能修复重大BUG，所以请 `watch` 本项目，并注意随时更新代码，以免给您带来损失！
+VirtualApp在2017年8月份正式公司化运作，当您需要将VirtualApp用于商业用途时，请务必联系QQ：10890 购买商业授权。您如果未经授权将VirtualApp的代码作为您自己的代码用于商业牟利、内部使用或上传软件市场，我们将直接报警（侵犯著作权罪），这将对您所属的公司造成法律诉讼和刑事责任，影响到您公司的商誉和投资。
+ 
+购买商业授权为您节省大量开发完善时间，保障产品高效上线运营，让您有更多时间用于创新及盈利。VirtualApp商业授权包含：商业版代码、说明接入文档、微信和QQ群全年技术支持。VirtualApp对外开放的源代码将于2017年12月31日停止更新，VirtualApp商业版代码将持续更新！
+ 
+ 
+商业版更新日志：
+
+**2018年01月09日**
+1. 修复华为emui8.0的一个bug
+
+**2018年01月08日**
+1. 完善定位
+2. 设置手机信息，imei伪装算法
+3. 适配8.0某个功能（主要app：whatsapp）
+4. 修复内部微信等应用，无法更新图片，视频
+5. demo增加安装监听，自动升级克隆模式的应用
+6. 7.0的file provider适配
+
+**2017年12月28日**
+1. 增加了定位代码
+2. 代码进行了架构优化
+3. 与开源版不同的特征
+4. 解决了微信被封的一些问题
+5. 修复了部分机型兼容性
+6. 修复了12个小BUG
 
 
 已支持的加固(不断更新)
@@ -90,10 +110,6 @@ MicroG的必要模块：
     VirtualCore.getCore().findApp({PackageName});
 ```
 
-License
--------
-GPL 3.0
-
-技术支持
+BUG反馈
 ------------
-QQ群：598536
+zl@aluohe.com
diff --git a/LICENSE.txt b/LICENSE.txt
deleted file mode 100644
index 94a9ed024..000000000
--- a/LICENSE.txt
+++ /dev/null
@@ -1,674 +0,0 @@
-                    GNU GENERAL PUBLIC LICENSE
-                       Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-                            Preamble
-
-  The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
-  The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works.  By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users.  We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors.  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
-  To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights.  Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received.  You must make sure that they, too, receive
-or can get the source code.  And you must show them these terms so they
-know their rights.
-
-  Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
-  For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software.  For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
-  Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so.  This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software.  The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable.  Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products.  If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
-  Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary.  To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-                       TERMS AND CONDITIONS
-
-  0. Definitions.
-
-  "This License" refers to version 3 of the GNU General Public License.
-
-  "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
-  "The Program" refers to any copyrightable work licensed under this
-License.  Each licensee is addressed as "you".  "Licensees" and
-"recipients" may be individuals or organizations.
-
-  To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy.  The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
-  A "covered work" means either the unmodified Program or a work based
-on the Program.
-
-  To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy.  Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
-  To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies.  Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
-  An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License.  If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
-  1. Source Code.
-
-  The "source code" for a work means the preferred form of the work
-for making modifications to it.  "Object code" means any non-source
-form of a work.
-
-  A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
-  The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form.  A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
-  The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities.  However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work.  For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
-  The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
-  The Corresponding Source for a work in source code form is that
-same work.
-
-  2. Basic Permissions.
-
-  All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met.  This License explicitly affirms your unlimited
-permission to run the unmodified Program.  The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work.  This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
-  You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force.  You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright.  Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
-  Conveying under any other circumstances is permitted solely under
-the conditions stated below.  Sublicensing is not allowed; section 10
-makes it unnecessary.
-
-  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
-  No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
-  When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
-  4. Conveying Verbatim Copies.
-
-  You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
-  You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
-  5. Conveying Modified Source Versions.
-
-  You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
-    a) The work must carry prominent notices stating that you modified
-    it, and giving a relevant date.
-
-    b) The work must carry prominent notices stating that it is
-    released under this License and any conditions added under section
-    7.  This requirement modifies the requirement in section 4 to
-    "keep intact all notices".
-
-    c) You must license the entire work, as a whole, under this
-    License to anyone who comes into possession of a copy.  This
-    License will therefore apply, along with any applicable section 7
-    additional terms, to the whole of the work, and all its parts,
-    regardless of how they are packaged.  This License gives no
-    permission to license the work in any other way, but it does not
-    invalidate such permission if you have separately received it.
-
-    d) If the work has interactive user interfaces, each must display
-    Appropriate Legal Notices; however, if the Program has interactive
-    interfaces that do not display Appropriate Legal Notices, your
-    work need not make them do so.
-
-  A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit.  Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
-  6. Conveying Non-Source Forms.
-
-  You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
-    a) Convey the object code in, or embodied in, a physical product
-    (including a physical distribution medium), accompanied by the
-    Corresponding Source fixed on a durable physical medium
-    customarily used for software interchange.
-
-    b) Convey the object code in, or embodied in, a physical product
-    (including a physical distribution medium), accompanied by a
-    written offer, valid for at least three years and valid for as
-    long as you offer spare parts or customer support for that product
-    model, to give anyone who possesses the object code either (1) a
-    copy of the Corresponding Source for all the software in the
-    product that is covered by this License, on a durable physical
-    medium customarily used for software interchange, for a price no
-    more than your reasonable cost of physically performing this
-    conveying of source, or (2) access to copy the
-    Corresponding Source from a network server at no charge.
-
-    c) Convey individual copies of the object code with a copy of the
-    written offer to provide the Corresponding Source.  This
-    alternative is allowed only occasionally and noncommercially, and
-    only if you received the object code with such an offer, in accord
-    with subsection 6b.
-
-    d) Convey the object code by offering access from a designated
-    place (gratis or for a charge), and offer equivalent access to the
-    Corresponding Source in the same way through the same place at no
-    further charge.  You need not require recipients to copy the
-    Corresponding Source along with the object code.  If the place to
-    copy the object code is a network server, the Corresponding Source
-    may be on a different server (operated by you or a third party)
-    that supports equivalent copying facilities, provided you maintain
-    clear directions next to the object code saying where to find the
-    Corresponding Source.  Regardless of what server hosts the
-    Corresponding Source, you remain obligated to ensure that it is
-    available for as long as needed to satisfy these requirements.
-
-    e) Convey the object code using peer-to-peer transmission, provided
-    you inform other peers where the object code and Corresponding
-    Source of the work are being offered to the general public at no
-    charge under subsection 6d.
-
-  A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
-  A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling.  In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage.  For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product.  A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
-  "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source.  The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
-  If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information.  But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
-  The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed.  Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
-  Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
-  7. Additional Terms.
-
-  "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law.  If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
-  When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it.  (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.)  You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
-  Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
-    a) Disclaiming warranty or limiting liability differently from the
-    terms of sections 15 and 16 of this License; or
-
-    b) Requiring preservation of specified reasonable legal notices or
-    author attributions in that material or in the Appropriate Legal
-    Notices displayed by works containing it; or
-
-    c) Prohibiting misrepresentation of the origin of that material, or
-    requiring that modified versions of such material be marked in
-    reasonable ways as different from the original version; or
-
-    d) Limiting the use for publicity purposes of names of licensors or
-    authors of the material; or
-
-    e) Declining to grant rights under trademark law for use of some
-    trade names, trademarks, or service marks; or
-
-    f) Requiring indemnification of licensors and authors of that
-    material by anyone who conveys the material (or modified versions of
-    it) with contractual assumptions of liability to the recipient, for
-    any liability that these contractual assumptions directly impose on
-    those licensors and authors.
-
-  All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10.  If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term.  If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
-  If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
-  Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
-  8. Termination.
-
-  You may not propagate or modify a covered work except as expressly
-provided under this License.  Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
-  However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
-  Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
-  Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License.  If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
-  9. Acceptance Not Required for Having Copies.
-
-  You are not required to accept this License in order to receive or
-run a copy of the Program.  Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance.  However,
-nothing other than this License grants you permission to propagate or
-modify any covered work.  These actions infringe copyright if you do
-not accept this License.  Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
-  10. Automatic Licensing of Downstream Recipients.
-
-  Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License.  You are not responsible
-for enforcing compliance by third parties with this License.
-
-  An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations.  If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
-  You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License.  For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
-  11. Patents.
-
-  A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based.  The
-work thus licensed is called the contributor's "contributor version".
-
-  A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version.  For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
-  Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
-  In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement).  To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
-  If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients.  "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
-  If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
-  A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License.  You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
-  Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
-  12. No Surrender of Others' Freedom.
-
-  If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all.  For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
-  13. Use with the GNU Affero General Public License.
-
-  Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work.  The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
-  14. Revised Versions of this License.
-
-  The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-  Each version is given a distinguishing version number.  If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation.  If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
-  If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
-  Later license versions may give you additional or different
-permissions.  However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
-  15. Disclaimer of Warranty.
-
-  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  16. Limitation of Liability.
-
-  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
-  17. Interpretation of Sections 15 and 16.
-
-  If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
-                     END OF TERMS AND CONDITIONS
-
-            How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
-  If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
-    <program>  Copyright (C) <year>  <name of author>
-    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
-  You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-<http://www.gnu.org/licenses/>.
-
-  The GNU General Public License does not permit incorporating your program
-into proprietary programs.  If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library.  If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.  But first, please read
-<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/README.md b/README.md
index 8ee3bf35d..24a891a42 100644
--- a/README.md
+++ b/README.md
@@ -70,11 +70,7 @@ Documentation
 
 VirtualApp currently has **no documentation**, If you are interested in VirtualApp, please send email to me.
 
-License
--------
-GPL 3.0
-
-About Author
+Contact us
 ------------
 
-    Lody (imlody@foxmail.com)
+    zl@aluohe.com
diff --git a/VirtualApp/.gitignore b/VirtualApp/.gitignore
index c07e41de4..6f0efc106 100644
--- a/VirtualApp/.gitignore
+++ b/VirtualApp/.gitignore
@@ -7,3 +7,4 @@
 .DS_Store
 /build
 /captures
+!*.apk
diff --git a/VirtualApp/app/build.gradle b/VirtualApp/app/build.gradle
index acee148a4..d81390460 100644
--- a/VirtualApp/app/build.gradle
+++ b/VirtualApp/app/build.gradle
@@ -1,10 +1,9 @@
 apply plugin: 'com.android.application'
-apply plugin: 'me.tatarka.retrolambda'
 
 
 android {
     compileSdkVersion 25
-    buildToolsVersion "25.0.2"
+    buildToolsVersion '26.0.2'
     defaultConfig {
         applicationId "io.virtualapp"
         minSdkVersion 15
@@ -53,13 +52,13 @@ dependencies {
     compile fileTree(include: ['*.jar'], dir: 'libs')
     compile project(':lib')
     //Android Lib
-    compile 'com.android.support:multidex:1.0.1'
-    compile 'com.android.support:appcompat-v7:25.0.1'
+    compile 'com.android.support:multidex:1.0.2'
+    compile 'com.android.support:appcompat-v7:25.4.0'
     compile 'com.melnykov:floatingactionbutton:1.3.0'
-    compile 'com.android.support:recyclerview-v7:25.0.1'
-    compile 'com.android.support:percent:25.0.1'
-    compile 'com.android.support:design:25.0.1'
-    compile 'com.android.support:cardview-v7:25.0.1'
+    compile 'com.android.support:recyclerview-v7:25.4.0'
+    compile 'com.android.support:percent:25.4.0'
+    compile 'com.android.support:design:25.4.0'
+    compile 'com.android.support:cardview-v7:25.4.0'
     //Promise Support
     compile 'org.jdeferred:jdeferred-android-aar:1.2.4'
     // ThirdParty
diff --git a/VirtualApp/app/src/main/res/values-zh-rTW/strings.xml b/VirtualApp/app/src/main/res/values-zh-rTW/strings.xml
new file mode 100644
index 000000000..84b69e6f8
--- /dev/null
+++ b/VirtualApp/app/src/main/res/values-zh-rTW/strings.xml
@@ -0,0 +1,24 @@
+<resources>
+    <string name="app_name">VirtualApp</string>
+    <string name="desktop">桌面</string>
+    <string name="add_app">新增App</string>
+    <string name="preparing">正在打開App，請稍候…</string>
+    <string name="delete">刪除</string>
+    <string name="create_shortcut">建立捷徑</string>
+    <string name="new_user">新的用戶</string>
+    <string name="enable">開啟</string>
+    <string name="save">儲存</string>
+    <string name="save_success">儲存成功!</string>
+    <string name="manufacturer">製造商</string>
+    <string name="wait">請稍候…</string>
+    <string name="brand">品牌</string>
+    <string name="device">機型</string>
+    <string name="fake_device_info">偽造裝置資訊</string>
+    <string name="wifi_status">Wifi狀態</string>
+    <string name="config_device_info">配置裝置資訊</string>
+    <string name="about">關於</string>
+    <string name="clone_apps">克隆App</string>
+    <string name="external_storage">外部空間</string>
+    <string name="install_d">安裝 (%d)</string>
+    <string name="install_too_much_once_time">不能一次性安裝超過 9 個程式！</string>
+</resources>
diff --git a/VirtualApp/build.gradle b/VirtualApp/build.gradle
index 7aa701d03..cc3fc8ce5 100644
--- a/VirtualApp/build.gradle
+++ b/VirtualApp/build.gradle
@@ -3,11 +3,14 @@
 buildscript {
     repositories {
         jcenter()
+        maven {
+            url 'https://maven.google.com/'
+            name 'Google'
+        }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:2.3.1'
-        classpath 'me.tatarka:gradle-retrolambda:3.6.0'
-        classpath 'com.android.tools.build:gradle-experimental:0.8.0'
+        classpath 'com.android.tools.build:gradle:3.0.1'
+        classpath 'com.android.tools.build:gradle-experimental:0.11.0'
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
     }
@@ -19,6 +22,10 @@ allprojects {
         maven {
             url "https://jitpack.io"
         }
+        maven {
+            url 'https://maven.google.com/'
+            name 'Google'
+        }
     }
 }
 
diff --git a/VirtualApp/gradle/wrapper/gradle-wrapper.properties b/VirtualApp/gradle/wrapper/gradle-wrapper.properties
index 00a91bd00..074ca5298 100644
--- a/VirtualApp/gradle/wrapper/gradle-wrapper.properties
+++ b/VirtualApp/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Sun Jan 15 17:29:32 CST 2017
+#Sun Nov 19 13:36:42 CST 2017
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
diff --git a/VirtualApp/lib/build.gradle b/VirtualApp/lib/build.gradle
index d0dd7fa60..b32248efc 100644
--- a/VirtualApp/lib/build.gradle
+++ b/VirtualApp/lib/build.gradle
@@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
 
 android {
     compileSdkVersion 24
-    buildToolsVersion "25.0.2"
+    buildToolsVersion '26.0.2'
 
     defaultConfig {
         minSdkVersion 14
@@ -11,7 +11,7 @@ android {
         versionName "1.0"
         externalNativeBuild {
             ndkBuild {
-                abiFilters "armeabi", "armeabi-v7a"
+                abiFilters "armeabi-v7a", "x86"
             }
         }
     }
@@ -35,4 +35,5 @@ android {
 
 dependencies {
     compile fileTree(include: ['*.jar'], dir: 'libs')
+    compile 'me.weishu.exposed:exposed-core:0.1.4'
 }
diff --git a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IAccountManager.aidl b/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IAccountManager.aidl
deleted file mode 100644
index 0847ff426..000000000
--- a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IAccountManager.aidl
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.lody.virtual.server;
-
-import android.accounts.IAccountManagerResponse;
-import android.accounts.Account;
-import android.accounts.AuthenticatorDescription;
-import android.os.Bundle;
-
-
-/**
- * Central application service that provides account management.
- * @hide
- */
-interface IAccountManager {
-    AuthenticatorDescription[] getAuthenticatorTypes(int userId);
-    void getAccountsByFeatures(int userId, in IAccountManagerResponse response, in String type, in String[] features);
-    String getPreviousName(int userId, in Account account);
-    Account[] getAccounts(int userId, in String type);
-    void getAuthToken(int userId, in IAccountManagerResponse response, in Account account, in String authTokenType, in boolean notifyOnAuthFailure, in boolean expectActivityLaunch, in Bundle loginOptions);
-    void setPassword(int userId, in Account account, in String password);
-    void setAuthToken(int userId, in Account account, in String authTokenType, in String authToken);
-    void setUserData(int userId, in Account account, in String key, in String value);
-    void hasFeatures(int userId, in IAccountManagerResponse response,
-    							in Account account, in String[] features);
-    void updateCredentials(int userId, in IAccountManagerResponse response, in Account account,
-    								  in String authTokenType, in boolean expectActivityLaunch,
-    								  in Bundle loginOptions);
-    void editProperties(int userId, in IAccountManagerResponse response, in String accountType,
-    							   in boolean expectActivityLaunch);
-    void getAuthTokenLabel(int userId, in IAccountManagerResponse response, in String accountType,
-    								  in String authTokenType);
-    String getUserData(int userId, in Account account, in String key);
-    String getPassword(int userId, in Account account);
-    void confirmCredentials(int userId, in IAccountManagerResponse response, in Account account, in Bundle options, in boolean expectActivityLaunch);
-    void addAccount(int userId, in IAccountManagerResponse response, in String accountType,
-    						   in String authTokenType, in String[] requiredFeatures,
-    						   in boolean expectActivityLaunch, in Bundle optionsIn);
-    boolean addAccountExplicitly(int userId, in Account account, in String password, in Bundle extras);
-    boolean removeAccountExplicitly(int userId, in Account account);
-    void renameAccount(int userId, in IAccountManagerResponse response, in Account accountToRename, in String newName);
-    void removeAccount(in int userId, in IAccountManagerResponse response, in Account account,
-    							  in boolean expectActivityLaunch);
-    void clearPassword(int userId, in Account account);
-    boolean accountAuthenticated(int userId, in Account account);
-    void invalidateAuthToken(int userId, in String accountType, in String authToken);
-    String peekAuthToken(int userId, in Account account, in String authTokenType);
-
-}
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IActivityManager.aidl b/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IActivityManager.aidl
deleted file mode 100644
index 94d8625b5..000000000
--- a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IActivityManager.aidl
+++ /dev/null
@@ -1,116 +0,0 @@
-// IActivityManager.aidl
-package com.lody.virtual.server;
-
-import com.lody.virtual.remote.VParceledListSlice;
-import com.lody.virtual.remote.AppTaskInfo;
-import com.lody.virtual.remote.PendingIntentData;
-import com.lody.virtual.remote.PendingResultData;
-import com.lody.virtual.remote.BadgerInfo;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.app.Notification;
-import android.app.IServiceConnection;
-import android.app.IActivityManager.ContentProviderHolder;
-import com.lody.virtual.server.interfaces.IProcessObserver;
-
-
-interface IActivityManager {
-
-    int initProcess(in String packageName, in String processName, int userId);
-
-    int getFreeStubCount();
-
-    int getSystemPid();
-
-    int getUidByPid(int pid);
-
-    boolean isAppProcess(String processName);
-
-    boolean isAppRunning(String packageName, int userId);
-
-    boolean isAppPid(int pid);
-
-    String getAppProcessName(int pid);
-
-    List<String> getProcessPkgList(int pid);
-
-    void killAllApps();
-
-    void killAppByPkg(String pkg, int userId);
-
-    void killApplicationProcess(String procName, int vuid);
-
-    void dump();
-
-    void registerProcessObserver(in IProcessObserver observer);
-
-    void unregisterProcessObserver(in IProcessObserver observer);
-
-    String getInitialPackage(int pid);
-
-    void handleApplicationCrash();
-
-    void appDoneExecuting();
-
-    int startActivities(in Intent[] intents, in String[] resolvedTypes, in IBinder token, in Bundle options, in int userId);
-
-    int startActivity(in Intent intent, in ActivityInfo info, in IBinder resultTo, in Bundle options, String resultWho, int requestCode, int userId);
-
-    void onActivityCreated(in ComponentName component, in ComponentName caller, in IBinder token, in Intent intent, in String affinity, int taskId, int launchMode, int flags);
-
-    void onActivityResumed(int userId, in IBinder token);
-
-    boolean onActivityDestroyed(int userId, in IBinder token);
-
-    ComponentName getActivityClassForToken(int userId, in IBinder token);
-
-    String getCallingPackage(int userId, in IBinder token);
-
-    ComponentName getCallingActivity(int userId, in IBinder token);
-
-    AppTaskInfo getTaskInfo(int taskId);
-
-    String getPackageForToken(int userId, in IBinder token);
-
-    boolean isVAServiceToken(in IBinder token);
-
-    ComponentName startService(in IBinder caller,in Intent service, String resolvedType, int userId);
-
-    int stopService(in IBinder caller, in Intent service, String resolvedType, int userId);
-
-    boolean stopServiceToken(in ComponentName className, in IBinder token, int startId, int userId);
-
-    void setServiceForeground(in ComponentName className, in IBinder token, int id,
-                            in Notification notification, boolean removeNotification, int userId);
-
-    int bindService(in IBinder caller, in IBinder token, in Intent service,
-                    String resolvedType, in IServiceConnection connection, int flags, int userId);
-
-    boolean unbindService(in IServiceConnection connection, int userId);
-
-    void unbindFinished(in IBinder token, in Intent service, in boolean doRebind, int userId);
-
-    void serviceDoneExecuting(in IBinder token, in int type, in int startId, in int res, int userId);
-
-    IBinder peekService(in Intent service, String resolvedType, int userId);
-
-    void publishService(in IBinder token, in Intent intent, in IBinder service, int userId);
-
-    VParceledListSlice getServices(int maxNum, int flags, int userId);
-
-    IBinder acquireProviderClient(int userId, in ProviderInfo info);
-
-    PendingIntentData getPendingIntent(IBinder binder);
-
-    void addPendingIntent(IBinder binder, String packageName);
-
-    void removePendingIntent(IBinder binder);
-
-    String getPackageForIntentSender(IBinder binder);
-
-    void processRestarted(in String packageName, in String processName, int userId);
-
-    void broadcastFinish(in PendingResultData res);
-
-    void notifyBadgerChange(in BadgerInfo info);
-}
diff --git a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IAppManager.aidl b/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IAppManager.aidl
deleted file mode 100644
index ca665ed60..000000000
--- a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IAppManager.aidl
+++ /dev/null
@@ -1,35 +0,0 @@
-// IAppManager.aidl
-package com.lody.virtual.server;
-
-import com.lody.virtual.server.interfaces.IPackageObserver;
-import com.lody.virtual.server.interfaces.IAppRequestListener;
-import com.lody.virtual.remote.InstalledAppInfo;
-import com.lody.virtual.remote.InstallResult;
-
-interface IAppManager {
-    int[] getPackageInstalledUsers(String packageName);
-    void scanApps();
-    void addVisibleOutsidePackage(String pkg);
-    void removeVisibleOutsidePackage(String pkg);
-    boolean isOutsidePackageVisible(String pkg);
-    InstalledAppInfo getInstalledAppInfo(String pkg, int flags);
-    InstallResult installPackage(String path, int flags);
-    boolean isPackageLaunched(int userId, String packageName);
-    void setPackageHidden(int userId, String packageName, boolean hidden);
-    boolean installPackageAsUser(int userId, String packageName);
-    boolean uninstallPackageAsUser(String packageName, int userId);
-    boolean uninstallPackage(String packageName);
-    List<InstalledAppInfo> getInstalledApps(int flags);
-    List<InstalledAppInfo> getInstalledAppsAsUser(int userId, int flags);
-    int getInstalledAppCount();
-    boolean isAppInstalled(String packageName);
-    boolean isAppInstalledAsUser(int userId, String packageName);
-
-    void registerObserver(IPackageObserver observer);
-    void unregisterObserver(IPackageObserver observer);
-
-    void setAppRequestListener(IAppRequestListener listener);
-    void clearAppRequestListener();
-    IAppRequestListener getAppRequestListener();
-
-}
diff --git a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IDeviceInfoManager.aidl b/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IDeviceInfoManager.aidl
deleted file mode 100644
index 3305d799d..000000000
--- a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IDeviceInfoManager.aidl
+++ /dev/null
@@ -1,12 +0,0 @@
-// IDeviceInfoManager.aidl
-package com.lody.virtual.server;
-
-import com.lody.virtual.remote.VDeviceInfo;
-
-interface IDeviceInfoManager {
-
-    VDeviceInfo getDeviceInfo(int userId);
-
-    void updateDeviceInfo(int userId, in VDeviceInfo info);
-
-}
diff --git a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IJobScheduler.aidl b/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IJobScheduler.aidl
deleted file mode 100644
index 5e230364c..000000000
--- a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IJobScheduler.aidl
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.lody.virtual.server;
-
-import android.app.job.JobInfo;
-
- /**
-  * IPC interface that supports the app-facing {@link #JobScheduler} api.
-  */
-interface IJobScheduler {
-    int schedule(in JobInfo job);
-    void cancel(int jobId);
-    void cancelAll();
-    List<JobInfo> getAllPendingJobs();
-}
diff --git a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/INotificationManager.aidl b/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/INotificationManager.aidl
deleted file mode 100644
index c48b2320d..000000000
--- a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/INotificationManager.aidl
+++ /dev/null
@@ -1,14 +0,0 @@
-// INotificationManager.aidl
-package com.lody.virtual.server;
-
-// Declare any non-default types here with import statements
-import android.app.Notification;
-
-interface INotificationManager {
-    int dealNotificationId(int id, String packageName, String tag, int userId);
-    String dealNotificationTag(int id, String packageName, String tag, int userId);
-    boolean areNotificationsEnabledForPackage(String packageName, int userId);
-    void setNotificationsEnabledForPackage(String packageName, boolean enable, int userId);
-    void addNotification(int id, String tag, String packageName, int userId);
-    void cancelAllNotification(String packageName, int userId);
-}
diff --git a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IPackageManager.aidl b/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IPackageManager.aidl
deleted file mode 100644
index 973cbf7ef..000000000
--- a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IPackageManager.aidl
+++ /dev/null
@@ -1,78 +0,0 @@
-// IPackageManager.aidl
-package com.lody.virtual.server;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ServiceInfo;
-import android.content.pm.ProviderInfo;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.IntentFilter;
-import android.content.pm.PermissionInfo;
-import android.content.pm.PermissionGroupInfo;
-
-import com.lody.virtual.remote.ReceiverInfo;
-import com.lody.virtual.remote.VParceledListSlice;
-
-import com.lody.virtual.server.IPackageInstaller;
-
-interface IPackageManager {
-
-        int getPackageUid(String packageName, int userId);
-
-        String[] getPackagesForUid(int vuid);
-
-        List<String> getSharedLibraries(String pkgName);
-
-        int checkPermission(String permName, String pkgName, int userId);
-
-        PackageInfo getPackageInfo(String packageName, int flags, int userId);
-
-        ActivityInfo getActivityInfo(in ComponentName componentName, int flags, int userId);
-
-        boolean activitySupportsIntent(in ComponentName component, in Intent intent,
-                                           										  in String resolvedType);
-         ActivityInfo getReceiverInfo(in ComponentName componentName, int flags, int userId);
-
-         ServiceInfo getServiceInfo(in ComponentName componentName, int flags, int userId);
-
-         ProviderInfo getProviderInfo(in ComponentName componentName, int flags, int userId);
-
-         ResolveInfo resolveIntent(in Intent intent, in String resolvedType, int flags, int userId);
-
-         List<ResolveInfo> queryIntentActivities(in Intent intent,in  String resolvedType, int flags, int userId);
-
-         List<ResolveInfo> queryIntentReceivers(in Intent intent, String resolvedType, int flags, int userId);
-
-         ResolveInfo resolveService(in Intent intent, String resolvedType, int flags, int userId);
-
-         List<ResolveInfo> queryIntentServices(in Intent intent, String resolvedType, int flags, int userId);
-
-         List<ResolveInfo> queryIntentContentProviders(in Intent intent, String resolvedType, int flags, int userId);
-
-         VParceledListSlice getInstalledPackages(int flags, int userId);
-
-         VParceledListSlice getInstalledApplications(int flags, int userId);
-
-         PermissionInfo getPermissionInfo(in String name, int flags);
-
-         List<PermissionInfo> queryPermissionsByGroup(in String group, int flags);
-
-         PermissionGroupInfo getPermissionGroupInfo(in String name, int flags);
-
-         List<PermissionGroupInfo> getAllPermissionGroups(int flags);
-
-         ProviderInfo resolveContentProvider(in String name, int flags, int userId);
-
-         ApplicationInfo getApplicationInfo(in String packageName, int flags, int userId);
-
-         VParceledListSlice queryContentProviders(in String processName, int vuid, int flags);
-
-         List<String> querySharedPackages(in String packageName);
-
-         String getNameForUid(int uid);
-
-         IPackageInstaller getPackageInstaller();
-}
diff --git a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IUserManager.aidl b/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IUserManager.aidl
deleted file mode 100644
index 1f0faded3..000000000
--- a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IUserManager.aidl
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.lody.virtual.server;
-
-import android.os.ParcelFileDescriptor;
-import com.lody.virtual.os.VUserInfo;
-import android.graphics.Bitmap;
-
-/**
-*
- */
-interface IUserManager {
-    VUserInfo createUser(in String name, int flags);
-    boolean removeUser(int userHandle);
-    void setUserName(int userHandle, String name);
-    void setUserIcon(int userHandle, in Bitmap icon);
-    Bitmap getUserIcon(int userHandle);
-    List<VUserInfo> getUsers(boolean excludeDying);
-    VUserInfo getUserInfo(int userHandle);
-    void setGuestEnabled(boolean enable);
-    boolean isGuestEnabled();
-    void wipeUser(int userHandle);
-    int getUserSerialNumber(int userHandle);
-    int getUserHandle(int userSerialNumber);
-}
diff --git a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IVirtualLocationManager.aidl b/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IVirtualLocationManager.aidl
deleted file mode 100644
index 750319aa8..000000000
--- a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IVirtualLocationManager.aidl
+++ /dev/null
@@ -1,30 +0,0 @@
-// IVirtualLocationManager.aidl
-package com.lody.virtual.server;
-
-import com.lody.virtual.remote.vloc.VCell;
-import com.lody.virtual.remote.vloc.VWifi;
-import com.lody.virtual.remote.vloc.VLocation;
-
-interface IVirtualLocationManager {
-
-    int getMode(int userId, in String pkg);
-    void setMode(int userId, in String pkg, int mode);
-
-    void setCell(in int userId, in String pkg, in VCell cell);
-    void setAllCell(in int userId, in String pkg, in List<VCell> cell);
-    void setNeighboringCell(in int userId, in String pkg, in List<VCell> cell);
-
-    void setGlobalCell(in VCell cell);
-    void setGlobalAllCell(in List<VCell> cell);
-    void setGlobalNeighboringCell(in List<VCell> cell);
-
-    VCell getCell(in int userId, in String pkg);
-    List<VCell> getAllCell(in int userId, in String pkg);
-    List<VCell> getNeighboringCell(in int userId, in String pkg);
-
-    void setLocation(in int userId, in String pkg, in VLocation loc);
-    VLocation getLocation(in int userId, in String pkg);
-
-    void setGlobalLocation(in VLocation loc);
-    VLocation getGlobalLocation();
-}
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IVirtualStorageService.aidl b/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IVirtualStorageService.aidl
deleted file mode 100644
index 042b8254b..000000000
--- a/VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IVirtualStorageService.aidl
+++ /dev/null
@@ -1,15 +0,0 @@
-// IVirtualStorageService.aidl
-package com.lody.virtual.server;
-
-
-interface IVirtualStorageService {
-
-   void setVirtualStorage(in String packageName, in int userId, in String vsPath);
-
-   String getVirtualStorage(in String packageName, in int userId);
-
-   void setVirtualStorageState(in String packageName, in int userId, in boolean enable);
-
-   boolean isVirtualStorageEnable(in String packageName, in int userId);
-
-}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/VClientImpl.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/VClientImpl.java
index ff8b8349b..e06c316b7 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/VClientImpl.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/VClientImpl.java
@@ -12,6 +12,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
+import android.content.res.Configuration;
 import android.os.Binder;
 import android.os.Build;
 import android.os.ConditionVariable;
@@ -20,6 +21,7 @@
 import android.os.IInterface;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Parcelable;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.StrictMode;
@@ -60,11 +62,17 @@
 import mirror.android.app.ActivityThread;
 import mirror.android.app.ActivityThreadNMR1;
 import mirror.android.app.ContextImpl;
+import mirror.android.app.ContextImplKitkat;
 import mirror.android.app.IActivityManager;
 import mirror.android.app.LoadedApk;
+import mirror.android.app.LoadedApkICS;
+import mirror.android.app.LoadedApkKitkat;
 import mirror.android.content.ContentProviderHolderOreo;
+import mirror.android.content.res.CompatibilityInfo;
 import mirror.android.providers.Settings;
 import mirror.android.renderscript.RenderScriptCacheDir;
+import mirror.android.view.CompatibilityInfoHolder;
+import mirror.android.view.DisplayAdjustments;
 import mirror.android.view.HardwareRenderer;
 import mirror.android.view.RenderScript;
 import mirror.android.view.ThreadedRenderer;
@@ -266,6 +274,7 @@ private void bindApplicationNoCheck(String packageName, String processName, Cond
         } else {
             codeCacheDir = context.getCacheDir();
         }
+
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
             if (HardwareRenderer.setupDiskCache != null) {
                 HardwareRenderer.setupDiskCache.call(codeCacheDir);
@@ -289,6 +298,17 @@ private void bindApplicationNoCheck(String packageName, String processName, Cond
         mirror.android.app.ActivityThread.AppBindData.info.set(boundApp, data.info);
         VMRuntime.setTargetSdkVersion.call(VMRuntime.getRuntime.call(), data.appInfo.targetSdkVersion);
 
+        Configuration configuration = context.getResources().getConfiguration();
+        Object compatInfo = CompatibilityInfo.ctor.newInstance(data.appInfo, configuration.screenLayout, configuration.smallestScreenWidthDp, false);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+                DisplayAdjustments.setCompatibilityInfo.call(ContextImplKitkat.mDisplayAdjustments.get(context), compatInfo);
+            }
+            DisplayAdjustments.setCompatibilityInfo.call(LoadedApkKitkat.mDisplayAdjustments.get(mBoundApplication.info), compatInfo);
+        } else {
+            CompatibilityInfoHolder.set.call(LoadedApkICS.mCompatibilityInfo.get(mBoundApplication.info), compatInfo);
+        }
+
         boolean conflict = SpecialComponentList.isConflictingInstrumentation(packageName);
         if (!conflict) {
             InvocationStubManager.getInstance().checkEnv(AppInstrumentation.class);
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/core/InvocationStubManager.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/core/InvocationStubManager.java
index 55cc89fab..a7712f023 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/core/InvocationStubManager.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/core/InvocationStubManager.java
@@ -47,6 +47,7 @@
 import com.lody.virtual.client.hook.proxies.usage.UsageStatsManagerStub;
 import com.lody.virtual.client.hook.proxies.user.UserManagerStub;
 import com.lody.virtual.client.hook.proxies.vibrator.VibratorStub;
+import com.lody.virtual.client.hook.proxies.view.AutoFillManagerStub;
 import com.lody.virtual.client.hook.proxies.wifi.WifiManagerStub;
 import com.lody.virtual.client.hook.proxies.wifi_scanner.WifiScannerStub;
 import com.lody.virtual.client.hook.proxies.window.WindowManagerStub;
@@ -179,6 +180,9 @@ private void injectInternal() throws Throwable {
                 addInjector(new ShortcutServiceStub());
                 addInjector(new DevicePolicyManagerStub());
             }
+            if (Build.VERSION.SDK_INT >= 26) {
+				addInjector(new AutoFillManagerStub());
+			}
 		}
 	}
 
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/core/VirtualCore.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/core/VirtualCore.java
index 2a9d7a4fc..bf77a3fe3 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/core/VirtualCore.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/core/VirtualCore.java
@@ -16,6 +16,7 @@
 import android.graphics.Bitmap;
 import android.os.Bundle;
 import android.os.ConditionVariable;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteException;
@@ -28,17 +29,20 @@
 import com.lody.virtual.client.hook.delegate.ComponentDelegate;
 import com.lody.virtual.client.hook.delegate.PhoneInfoDelegate;
 import com.lody.virtual.client.hook.delegate.TaskDescriptionDelegate;
-import com.lody.virtual.client.ipc.LocalProxyUtils;
 import com.lody.virtual.client.ipc.ServiceManagerNative;
 import com.lody.virtual.client.ipc.VActivityManager;
 import com.lody.virtual.client.ipc.VPackageManager;
 import com.lody.virtual.client.stub.VASettings;
 import com.lody.virtual.helper.compat.BundleCompat;
+import com.lody.virtual.helper.ipcbus.IPCBus;
+import com.lody.virtual.helper.ipcbus.IPCSingleton;
+import com.lody.virtual.helper.ipcbus.IServerCache;
 import com.lody.virtual.helper.utils.BitmapUtils;
 import com.lody.virtual.os.VUserHandle;
 import com.lody.virtual.remote.InstallResult;
 import com.lody.virtual.remote.InstalledAppInfo;
-import com.lody.virtual.server.IAppManager;
+import com.lody.virtual.server.interfaces.IAppManager;
+import com.lody.virtual.server.ServiceCache;
 import com.lody.virtual.server.interfaces.IAppRequestListener;
 import com.lody.virtual.server.interfaces.IPackageObserver;
 import com.lody.virtual.server.interfaces.IUiCallback;
@@ -82,7 +86,7 @@ public final class VirtualCore {
      */
     private String processName;
     private ProcessType processType;
-    private IAppManager mService;
+    private IPCSingleton<IAppManager> singleton = new IPCSingleton<>(IAppManager.class);
     private boolean isStartUp;
     private PackageInfo hostPkgInfo;
     private int systemPid;
@@ -178,6 +182,17 @@ public void startup(Context context) throws Throwable {
             mainThread = ActivityThread.currentActivityThread.call();
             unHookPackageManager = context.getPackageManager();
             hostPkgInfo = unHookPackageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_PROVIDERS);
+            IPCBus.initialize(new IServerCache() {
+                @Override
+                public void join(String serverName, IBinder binder) {
+                    ServiceCache.addService(serverName, binder);
+                }
+
+                @Override
+                public IBinder query(String serverName) {
+                    return ServiceManagerNative.getService(serverName);
+                }
+            });
             detectProcessType();
             InvocationStubManager invocationStubManager = InvocationStubManager.getInstance();
             invocationStubManager.init();
@@ -252,19 +267,7 @@ private void detectProcessType() {
     }
 
     private IAppManager getService() {
-        if (mService == null
-                || (!VirtualCore.get().isVAppProcess() && !mService.asBinder().isBinderAlive())) {
-            synchronized (this) {
-                Object remote = getStubInterface();
-                mService = LocalProxyUtils.genProxy(IAppManager.class, remote);
-            }
-        }
-        return mService;
-    }
-
-    private Object getStubInterface() {
-        return IAppManager.Stub
-                .asInterface(ServiceManagerNative.getService(ServiceManagerNative.APP));
+        return singleton.get();
     }
 
     /**
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/env/SpecialComponentList.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/env/SpecialComponentList.java
index 9d1872b22..91c95e88f 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/env/SpecialComponentList.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/env/SpecialComponentList.java
@@ -43,6 +43,7 @@ public final class SpecialComponentList {
         SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_BATTERY_OKAY);
         SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_POWER_CONNECTED);
         SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_POWER_DISCONNECTED);
+        SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_USER_PRESENT);
         SYSTEM_BROADCAST_ACTION.add("android.provider.Telephony.SMS_RECEIVED");
         SYSTEM_BROADCAST_ACTION.add("android.provider.Telephony.SMS_DELIVER");
         SYSTEM_BROADCAST_ACTION.add("android.net.wifi.STATE_CHANGE");
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/ActivityManagerStub.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/ActivityManagerStub.java
index 077514925..329f54207 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/ActivityManagerStub.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/ActivityManagerStub.java
@@ -115,27 +115,6 @@ public Object call(Object who, Method method, Object... args) throws Throwable {
                     return _infos;
                 }
             });
-            addMethodProxy(new StaticMethodProxy("getRunningTasks") {
-                @Override
-                public Object call(Object who, Method method, Object... args) throws Throwable {
-                    Object _infos = method.invoke(who, args);
-                    //noinspection unchecked
-                    List<ActivityManager.RunningTaskInfo> infos =
-                            ParceledListSliceCompat.isReturnParceledListSlice(method)
-                                    ? ParceledListSlice.getList.call(_infos)
-                                    : (List) _infos;
-                    for (ActivityManager.RunningTaskInfo info : infos) {
-                        AppTaskInfo taskInfo = VActivityManager.get().getTaskInfo(info.id);
-                        if (taskInfo == null) {
-                            continue;
-                        }
-                        info.description = "Virtual";
-                        info.topActivity = taskInfo.topActivity;
-                        info.baseActivity = taskInfo.baseActivity;
-                    }
-                    return _infos;
-                }
-            });
         }
     }
 
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/HCallbackStub.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/HCallbackStub.java
index 050eb5f05..170660916 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/HCallbackStub.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/HCallbackStub.java
@@ -3,7 +3,6 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.ServiceInfo;
 import android.os.Handler;
 import android.os.IBinder;
@@ -19,6 +18,9 @@
 import com.lody.virtual.remote.InstalledAppInfo;
 import com.lody.virtual.remote.StubActivityRecord;
 
+import java.util.List;
+
+import me.weishu.exposed.ExposedBridge;
 import mirror.android.app.ActivityManagerNative;
 import mirror.android.app.ActivityThread;
 import mirror.android.app.IActivityManager;
@@ -107,6 +109,8 @@ private boolean handleLaunchActivity(Message msg) {
             ComponentName caller = saveInstance.caller;
             IBinder token = ActivityThread.ActivityClientRecord.token.get(r);
             ActivityInfo info = saveInstance.info;
+            ClassLoader appClassLoader = VClientImpl.get().getClassLoader(info.applicationInfo);
+
             if (VClientImpl.get().getToken() == null) {
                 InstalledAppInfo installedAppInfo = VirtualCore.get().getInstalledAppInfo(info.packageName, 0);
                 if(installedAppInfo == null){
@@ -117,6 +121,12 @@ private boolean handleLaunchActivity(Message msg) {
                 return false;
             }
             if (!VClientImpl.get().isBound()) {
+                ExposedBridge.initOnce(VirtualCore.get().getContext(), info.applicationInfo, appClassLoader);
+                List<InstalledAppInfo> installedApps = VirtualCore.get().getInstalledApps(0);
+                for (InstalledAppInfo installedApp : installedApps) {
+                    ExposedBridge.loadModule(installedApp.apkPath, installedApp.getOdexFile().getParent(),
+                            installedApp.libPath, info.applicationInfo, appClassLoader);
+                }
                 VClientImpl.get().bindApplication(info.packageName, info.processName);
                 getH().sendMessageAtFrontOfQueue(Message.obtain(msg));
                 return false;
@@ -127,8 +137,8 @@ private boolean handleLaunchActivity(Message msg) {
                     false
             );
             VActivityManager.get().onActivityCreate(ComponentUtils.toComponentName(info), caller, token, info, intent, ComponentUtils.getTaskAffinity(info), taskId, info.launchMode, info.flags);
-            ClassLoader appClassLoader = VClientImpl.get().getClassLoader(info.applicationInfo);
-            intent.setExtrasClassLoader(appClassLoader);
+
+            intent.setExtrasClassLoader(ExposedBridge.getAppClassLoaderWithXposed(appClassLoader));
             ActivityThread.ActivityClientRecord.intent.set(r, intent);
             ActivityThread.ActivityClientRecord.activityInfo.set(r, info);
             return true;
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/MethodProxies.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/MethodProxies.java
index d49d1052f..a6a415560 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/MethodProxies.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/MethodProxies.java
@@ -55,6 +55,7 @@
 import com.lody.virtual.helper.utils.BitmapUtils;
 import com.lody.virtual.helper.utils.ComponentUtils;
 import com.lody.virtual.helper.utils.DrawableUtils;
+import com.lody.virtual.helper.utils.FileUtils;
 import com.lody.virtual.helper.utils.Reflect;
 import com.lody.virtual.helper.utils.VLog;
 import com.lody.virtual.os.VUserHandle;
@@ -63,6 +64,10 @@
 import com.lody.virtual.server.interfaces.IAppRequestListener;
 
 import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.lang.ref.WeakReference;
 import java.lang.reflect.Method;
 import java.util.List;
@@ -360,6 +365,7 @@ static class StartActivity extends MethodProxy {
 
         private static final String SCHEME_FILE = "file";
         private static final String SCHEME_PACKAGE = "package";
+        private static final String SCHEME_CONTENT = "content";
 
         @Override
         public String getMethodName() {
@@ -475,6 +481,32 @@ private boolean handleInstallRequest(Intent intent) {
                     } catch (RemoteException e) {
                         e.printStackTrace();
                     }
+                } else if (SCHEME_CONTENT.equals(packageUri.getScheme())){
+                    InputStream inputStream = null;
+                    OutputStream outputStream = null;
+                    File sharedFileCopy = new File(getHostContext().getCacheDir(), packageUri.getLastPathSegment());
+                    try {
+                        inputStream = getHostContext().getContentResolver().openInputStream(packageUri);
+                        outputStream = new FileOutputStream(sharedFileCopy);
+                        byte[] buffer = new byte[1024];
+                        int count;
+                        while ((count = inputStream.read(buffer)) > 0) {
+                            outputStream.write(buffer, 0, count);
+                        }
+                        outputStream.flush();
+
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    } finally {
+                        FileUtils.closeQuietly(inputStream);
+                        FileUtils.closeQuietly(outputStream);
+                    }
+                    try {
+                        listener.onRequestInstall(sharedFileCopy.getPath());
+                        return true;
+                    } catch (RemoteException e) {
+                        e.printStackTrace();
+                    }
                 }
 
             }
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/location/MethodProxies.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/location/MethodProxies.java
index 7aa156b8e..9edefbb3e 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/location/MethodProxies.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/location/MethodProxies.java
@@ -282,7 +282,7 @@ public String getMethodName() {
 
         @Override
         public Object afterCall(Object who, Method method, Object[] args, Object result) throws Throwable {
-            if (isFakeLocationEnable()) {
+            if (!isFakeLocationEnable()) {
                 return super.afterCall(who, method, args, result);
             }
             try {
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/view/AutoFillManagerStub.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/view/AutoFillManagerStub.java
new file mode 100644
index 000000000..27c898c2c
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/view/AutoFillManagerStub.java
@@ -0,0 +1,83 @@
+package com.lody.virtual.client.hook.proxies.view;
+
+import android.annotation.SuppressLint;
+import android.util.Log;
+
+import com.lody.virtual.client.hook.base.BinderInvocationProxy;
+import com.lody.virtual.client.hook.base.MethodProxy;
+import com.lody.virtual.client.hook.utils.MethodParameterUtils;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import mirror.android.view.IAutoFillManager;
+
+/**
+ * @author 陈磊.
+ */
+
+public class AutoFillManagerStub extends BinderInvocationProxy {
+
+    private static final String TAG = "AutoFillManagerStub";
+
+    private static final String AUTO_FILL_NAME = "autofill";
+    public AutoFillManagerStub() {
+        super(IAutoFillManager.Stub.asInterface, AUTO_FILL_NAME);
+    }
+
+    @SuppressLint("WrongConstant")
+    @Override
+    public void inject() throws Throwable {
+        super.inject();
+        try {
+            Object AutoFillManagerInstance = getContext().getSystemService(AUTO_FILL_NAME);
+            if (AutoFillManagerInstance == null) {
+                throw new NullPointerException("AutoFillManagerInstance is null.");
+            }
+            Object AutoFillManagerProxy = getInvocationStub().getProxyInterface();
+            if (AutoFillManagerProxy == null) {
+                throw new NullPointerException("AutoFillManagerProxy is null.");
+            }
+            Field AutoFillManagerServiceField = AutoFillManagerInstance.getClass().getDeclaredField("mService");
+            AutoFillManagerServiceField.setAccessible(true);
+            AutoFillManagerServiceField.set(AutoFillManagerInstance, AutoFillManagerProxy);
+        } catch (Throwable tr) {
+            Log.e(TAG, "AutoFillManagerStub inject error.", tr);
+            return;
+        }
+        addMethodProxy(new MethodProxy() {
+            @Override
+            public String getMethodName() {
+                return "startSession";
+            }
+            @Override
+            public boolean beforeCall(Object who, Method method, Object... args) {
+                MethodParameterUtils.replaceLastAppPkg(args);
+                return super.beforeCall(who, method, args);
+            }
+        });
+        addMethodProxy(new MethodProxy() {
+            @Override
+            public String getMethodName() {
+                return "updateOrRestartSession";
+            }
+            @Override
+            public boolean beforeCall(Object who, Method method, Object... args) {
+                MethodParameterUtils.replaceLastAppPkg(args);
+                return super.beforeCall(who, method, args);
+            }
+        });
+        addMethodProxy(new MethodProxy() {
+            @Override
+            public String getMethodName() {
+                return "isServiceEnabled";
+            }
+            @Override
+            public boolean beforeCall(Object who, Method method, Object... args) {
+                MethodParameterUtils.replaceLastAppPkg(args);
+                return super.beforeCall(who, method, args);
+            }
+        });
+    }
+
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VAccountManager.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VAccountManager.java
index 084adaf37..075b779be 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VAccountManager.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VAccountManager.java
@@ -10,11 +10,11 @@
 import android.os.Handler;
 import android.os.RemoteException;
 
-import com.lody.virtual.client.core.VirtualCore;
 import com.lody.virtual.client.env.VirtualRuntime;
 import com.lody.virtual.client.stub.AmsTask;
+import com.lody.virtual.helper.ipcbus.IPCSingleton;
 import com.lody.virtual.os.VUserHandle;
-import com.lody.virtual.server.IAccountManager;
+import com.lody.virtual.server.interfaces.IAccountManager;
 
 import static com.lody.virtual.helper.compat.AccountManagerCompat.KEY_ANDROID_PACKAGE_NAME;
 
@@ -26,31 +26,19 @@ public class VAccountManager {
 
     private static VAccountManager sMgr = new VAccountManager();
 
-    private IAccountManager mRemote;
+    private IPCSingleton<IAccountManager> singleton = new IPCSingleton<>(IAccountManager.class);
 
     public static VAccountManager get() {
         return sMgr;
     }
 
-    public IAccountManager getRemote() {
-        if (mRemote == null ||
-                (!mRemote.asBinder().isBinderAlive() && !VirtualCore.get().isVAppProcess())) {
-            synchronized (VAccountManager.class) {
-                Object remote = getStubInterface();
-                mRemote = LocalProxyUtils.genProxy(IAccountManager.class, remote);
-            }
-        }
-        return mRemote;
-    }
-
-    private Object getStubInterface() {
-        return IAccountManager.Stub
-                .asInterface(ServiceManagerNative.getService(ServiceManagerNative.ACCOUNT));
+    public IAccountManager getService() {
+        return singleton.get();
     }
 
     public AuthenticatorDescription[] getAuthenticatorTypes() {
         try {
-            return getRemote().getAuthenticatorTypes(VUserHandle.myUserId());
+            return getService().getAuthenticatorTypes(VUserHandle.myUserId());
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -58,7 +46,7 @@ public AuthenticatorDescription[] getAuthenticatorTypes() {
 
     public void removeAccount(IAccountManagerResponse response, Account account, boolean expectActivityLaunch) {
         try {
-            getRemote().removeAccount(VUserHandle.myUserId(), response, account, expectActivityLaunch);
+            getService().removeAccount(VUserHandle.myUserId(), response, account, expectActivityLaunch);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -66,7 +54,7 @@ public void removeAccount(IAccountManagerResponse response, Account account, boo
 
     public void getAuthToken(IAccountManagerResponse response, Account account, String authTokenType, boolean notifyOnAuthFailure, boolean expectActivityLaunch, Bundle loginOptions) {
         try {
-            getRemote().getAuthToken(VUserHandle.myUserId(), response, account, authTokenType, notifyOnAuthFailure, expectActivityLaunch, loginOptions);
+            getService().getAuthToken(VUserHandle.myUserId(), response, account, authTokenType, notifyOnAuthFailure, expectActivityLaunch, loginOptions);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -74,7 +62,7 @@ public void getAuthToken(IAccountManagerResponse response, Account account, Stri
 
     public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
         try {
-            return getRemote().addAccountExplicitly(VUserHandle.myUserId(), account, password, extras);
+            return getService().addAccountExplicitly(VUserHandle.myUserId(), account, password, extras);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -82,7 +70,7 @@ public boolean addAccountExplicitly(Account account, String password, Bundle ext
 
     public Account[] getAccounts(int userId, String type) {
         try {
-            return getRemote().getAccounts(userId, type);
+            return getService().getAccounts(userId, type);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -90,7 +78,7 @@ public Account[] getAccounts(int userId, String type) {
 
     public Account[] getAccounts(String type) {
         try {
-            return getRemote().getAccounts(VUserHandle.myUserId(), type);
+            return getService().getAccounts(VUserHandle.myUserId(), type);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -98,7 +86,7 @@ public Account[] getAccounts(String type) {
 
     public String peekAuthToken(Account account, String authTokenType) {
         try {
-            return getRemote().peekAuthToken(VUserHandle.myUserId(), account, authTokenType);
+            return getService().peekAuthToken(VUserHandle.myUserId(), account, authTokenType);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -106,7 +94,7 @@ public String peekAuthToken(Account account, String authTokenType) {
 
     public String getPreviousName(Account account) {
         try {
-            return getRemote().getPreviousName(VUserHandle.myUserId(), account);
+            return getService().getPreviousName(VUserHandle.myUserId(), account);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -114,7 +102,7 @@ public String getPreviousName(Account account) {
 
     public void hasFeatures(IAccountManagerResponse response, Account account, String[] features) {
         try {
-            getRemote().hasFeatures(VUserHandle.myUserId(), response, account, features);
+            getService().hasFeatures(VUserHandle.myUserId(), response, account, features);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -122,7 +110,7 @@ public void hasFeatures(IAccountManagerResponse response, Account account, Strin
 
     public boolean accountAuthenticated(Account account) {
         try {
-            return getRemote().accountAuthenticated(VUserHandle.myUserId(), account);
+            return getService().accountAuthenticated(VUserHandle.myUserId(), account);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -130,7 +118,7 @@ public boolean accountAuthenticated(Account account) {
 
     public void clearPassword(Account account) {
         try {
-            getRemote().clearPassword(VUserHandle.myUserId(), account);
+            getService().clearPassword(VUserHandle.myUserId(), account);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -138,7 +126,7 @@ public void clearPassword(Account account) {
 
     public void renameAccount(IAccountManagerResponse response, Account accountToRename, String newName) {
         try {
-            getRemote().renameAccount(VUserHandle.myUserId(), response, accountToRename, newName);
+            getService().renameAccount(VUserHandle.myUserId(), response, accountToRename, newName);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -146,7 +134,7 @@ public void renameAccount(IAccountManagerResponse response, Account accountToRen
 
     public void setPassword(Account account, String password) {
         try {
-            getRemote().setPassword(VUserHandle.myUserId(), account, password);
+            getService().setPassword(VUserHandle.myUserId(), account, password);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -154,7 +142,7 @@ public void setPassword(Account account, String password) {
 
     public void addAccount(int userId, IAccountManagerResponse response, String accountType, String authTokenType, String[] requiredFeatures, boolean expectActivityLaunch, Bundle optionsIn) {
         try {
-            getRemote().addAccount(userId, response, accountType, authTokenType, requiredFeatures, expectActivityLaunch, optionsIn);
+            getService().addAccount(userId, response, accountType, authTokenType, requiredFeatures, expectActivityLaunch, optionsIn);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -162,7 +150,7 @@ public void addAccount(int userId, IAccountManagerResponse response, String acco
 
     public void addAccount(IAccountManagerResponse response, String accountType, String authTokenType, String[] requiredFeatures, boolean expectActivityLaunch, Bundle optionsIn) {
         try {
-            getRemote().addAccount(VUserHandle.myUserId(), response, accountType, authTokenType, requiredFeatures, expectActivityLaunch, optionsIn);
+            getService().addAccount(VUserHandle.myUserId(), response, accountType, authTokenType, requiredFeatures, expectActivityLaunch, optionsIn);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -170,7 +158,7 @@ public void addAccount(IAccountManagerResponse response, String accountType, Str
 
     public void updateCredentials(IAccountManagerResponse response, Account account, String authTokenType, boolean expectActivityLaunch, Bundle loginOptions) {
         try {
-            getRemote().updateCredentials(VUserHandle.myUserId(), response, account, authTokenType, expectActivityLaunch, loginOptions);
+            getService().updateCredentials(VUserHandle.myUserId(), response, account, authTokenType, expectActivityLaunch, loginOptions);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -178,7 +166,7 @@ public void updateCredentials(IAccountManagerResponse response, Account account,
 
     public boolean removeAccountExplicitly(Account account) {
         try {
-            return getRemote().removeAccountExplicitly(VUserHandle.myUserId(), account);
+            return getService().removeAccountExplicitly(VUserHandle.myUserId(), account);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -186,7 +174,7 @@ public boolean removeAccountExplicitly(Account account) {
 
     public void setUserData(Account account, String key, String value) {
         try {
-            getRemote().setUserData(VUserHandle.myUserId(), account, key, value);
+            getService().setUserData(VUserHandle.myUserId(), account, key, value);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -194,7 +182,7 @@ public void setUserData(Account account, String key, String value) {
 
     public void editProperties(IAccountManagerResponse response, String accountType, boolean expectActivityLaunch) {
         try {
-            getRemote().editProperties(VUserHandle.myUserId(), response, accountType, expectActivityLaunch);
+            getService().editProperties(VUserHandle.myUserId(), response, accountType, expectActivityLaunch);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -202,7 +190,7 @@ public void editProperties(IAccountManagerResponse response, String accountType,
 
     public void getAuthTokenLabel(IAccountManagerResponse response, String accountType, String authTokenType) {
         try {
-            getRemote().getAuthTokenLabel(VUserHandle.myUserId(), response, accountType, authTokenType);
+            getService().getAuthTokenLabel(VUserHandle.myUserId(), response, accountType, authTokenType);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -210,7 +198,7 @@ public void getAuthTokenLabel(IAccountManagerResponse response, String accountTy
 
     public void confirmCredentials(IAccountManagerResponse response, Account account, Bundle options, boolean expectActivityLaunch) {
         try {
-            getRemote().confirmCredentials(VUserHandle.myUserId(), response, account, options, expectActivityLaunch);
+            getService().confirmCredentials(VUserHandle.myUserId(), response, account, options, expectActivityLaunch);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -218,7 +206,7 @@ public void confirmCredentials(IAccountManagerResponse response, Account account
 
     public void invalidateAuthToken(String accountType, String authToken) {
         try {
-            getRemote().invalidateAuthToken(VUserHandle.myUserId(), accountType, authToken);
+            getService().invalidateAuthToken(VUserHandle.myUserId(), accountType, authToken);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -226,7 +214,7 @@ public void invalidateAuthToken(String accountType, String authToken) {
 
     public void getAccountsByFeatures(IAccountManagerResponse response, String type, String[] features) {
         try {
-            getRemote().getAccountsByFeatures(VUserHandle.myUserId(), response, type, features);
+            getService().getAccountsByFeatures(VUserHandle.myUserId(), response, type, features);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -234,7 +222,7 @@ public void getAccountsByFeatures(IAccountManagerResponse response, String type,
 
     public void setAuthToken(Account account, String authTokenType, String authToken) {
         try {
-            getRemote().setAuthToken(VUserHandle.myUserId(), account, authTokenType, authToken);
+            getService().setAuthToken(VUserHandle.myUserId(), account, authTokenType, authToken);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -242,7 +230,7 @@ public void setAuthToken(Account account, String authTokenType, String authToken
 
     public Object getPassword(Account account) {
         try {
-            return getRemote().getPassword(VUserHandle.myUserId(), account);
+            return getService().getPassword(VUserHandle.myUserId(), account);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -250,7 +238,7 @@ public Object getPassword(Account account) {
 
     public String getUserData(Account account, String key) {
         try {
-            return getRemote().getUserData(VUserHandle.myUserId(), account, key);
+            return getService().getUserData(VUserHandle.myUserId(), account, key);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -265,7 +253,6 @@ public String getUserData(Account account, String key) {
      * <p>This method may be called from any thread, but the returned
      * {@link AccountManagerFuture} must not be used on the main thread.
      * <p>
-     *
      */
     public AccountManagerFuture<Bundle> addAccount(final int userId, final String accountType,
                                                    final String authTokenType, final String[] requiredFeatures,
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VActivityManager.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VActivityManager.java
index c9ab8e09f..b4a7a537b 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VActivityManager.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VActivityManager.java
@@ -18,6 +18,7 @@
 import com.lody.virtual.client.env.VirtualRuntime;
 import com.lody.virtual.client.hook.secondary.ServiceConnectionDelegate;
 import com.lody.virtual.helper.compat.ActivityManagerCompat;
+import com.lody.virtual.helper.ipcbus.IPCSingleton;
 import com.lody.virtual.helper.utils.ComponentUtils;
 import com.lody.virtual.os.VUserHandle;
 import com.lody.virtual.remote.AppTaskInfo;
@@ -25,8 +26,7 @@
 import com.lody.virtual.remote.PendingIntentData;
 import com.lody.virtual.remote.PendingResultData;
 import com.lody.virtual.remote.VParceledListSlice;
-import com.lody.virtual.server.IActivityManager;
-import com.lody.virtual.server.interfaces.IProcessObserver;
+import com.lody.virtual.server.interfaces.IActivityManager;
 
 import java.util.HashMap;
 import java.util.List;
@@ -42,27 +42,14 @@ public class VActivityManager {
 
     private static final VActivityManager sAM = new VActivityManager();
     private final Map<IBinder, ActivityClientRecord> mActivities = new HashMap<IBinder, ActivityClientRecord>(6);
-    private IActivityManager mRemote;
+    private IPCSingleton<IActivityManager> singleton = new IPCSingleton<>(IActivityManager.class);
 
     public static VActivityManager get() {
         return sAM;
     }
 
     public IActivityManager getService() {
-        if (mRemote == null ||
-                (!mRemote.asBinder().isBinderAlive() && !VirtualCore.get().isVAppProcess())) {
-            synchronized (VActivityManager.class) {
-                final Object remote = getRemoteInterface();
-                mRemote = LocalProxyUtils.genProxy(IActivityManager.class, remote);
-            }
-        }
-        return mRemote;
-    }
-
-
-    private Object getRemoteInterface() {
-        return IActivityManager.Stub
-                .asInterface(ServiceManagerNative.getService(ServiceManagerNative.ACTIVITY));
+        return singleton.get();
     }
 
 
@@ -331,14 +318,6 @@ public void killApplicationProcess(String procName, int uid) {
         }
     }
 
-    public void registerProcessObserver(IProcessObserver observer) {
-        try {
-            getService().registerProcessObserver(observer);
-        } catch (RemoteException e) {
-            e.printStackTrace();
-        }
-    }
-
     public void killAppByPkg(String pkg, int userId) {
         try {
             getService().killAppByPkg(pkg, userId);
@@ -347,14 +326,6 @@ public void killAppByPkg(String pkg, int userId) {
         }
     }
 
-    public void unregisterProcessObserver(IProcessObserver observer) {
-        try {
-            getService().unregisterProcessObserver(observer);
-        } catch (RemoteException e) {
-            e.printStackTrace();
-        }
-    }
-
     public void appDoneExecuting() {
         try {
             getService().appDoneExecuting();
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VDeviceManager.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VDeviceManager.java
index 680037efe..4bd0cb06e 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VDeviceManager.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VDeviceManager.java
@@ -1,12 +1,11 @@
 package com.lody.virtual.client.ipc;
 
-import android.os.IBinder;
 import android.os.RemoteException;
 
-import com.lody.virtual.client.core.VirtualCore;
 import com.lody.virtual.client.env.VirtualRuntime;
+import com.lody.virtual.helper.ipcbus.IPCSingleton;
 import com.lody.virtual.remote.VDeviceInfo;
-import com.lody.virtual.server.IDeviceInfoManager;
+import com.lody.virtual.server.interfaces.IDeviceInfoManager;
 
 /**
  * @author Lody
@@ -15,7 +14,7 @@
 public class VDeviceManager {
 
     private static final VDeviceManager sInstance = new VDeviceManager();
-    private IDeviceInfoManager mRemote;
+    private IPCSingleton<IDeviceInfoManager> singleton = new IPCSingleton<>(IDeviceInfoManager.class);
 
 
     public static VDeviceManager get() {
@@ -23,27 +22,15 @@ public static VDeviceManager get() {
     }
 
 
-    public IDeviceInfoManager getRemote() {
-        if (mRemote == null ||
-                (!mRemote.asBinder().isBinderAlive() && !VirtualCore.get().isVAppProcess())) {
-            synchronized (this) {
-                Object remote = getRemoteInterface();
-                mRemote = LocalProxyUtils.genProxy(IDeviceInfoManager.class, remote);
-            }
-        }
-        return mRemote;
-    }
-
-    private Object getRemoteInterface() {
-        final IBinder binder = ServiceManagerNative.getService(ServiceManagerNative.DEVICE);
-        return IDeviceInfoManager.Stub.asInterface(binder);
+    public IDeviceInfoManager getService() {
+        return singleton.get();
     }
 
     public VDeviceInfo getDeviceInfo(int userId) {
         try {
-            return getRemote().getDeviceInfo(userId);
+            return getService().getDeviceInfo(userId);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VJobScheduler.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VJobScheduler.java
index d51de1732..a0b0de9e0 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VJobScheduler.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VJobScheduler.java
@@ -1,12 +1,11 @@
 package com.lody.virtual.client.ipc;
 
 import android.app.job.JobInfo;
-import android.os.IBinder;
 import android.os.RemoteException;
 
-import com.lody.virtual.client.core.VirtualCore;
 import com.lody.virtual.client.env.VirtualRuntime;
-import com.lody.virtual.server.IJobScheduler;
+import com.lody.virtual.helper.ipcbus.IPCSingleton;
+import com.lody.virtual.server.interfaces.IJobService;
 
 import java.util.List;
 
@@ -18,31 +17,19 @@ public class VJobScheduler {
 
     private static final VJobScheduler sInstance = new VJobScheduler();
 
-    private IJobScheduler mRemote;
+    private IPCSingleton<IJobService> singleton = new IPCSingleton<>(IJobService.class);
 
     public static VJobScheduler get() {
         return sInstance;
     }
 
-    public IJobScheduler getRemote() {
-        if (mRemote == null ||
-                (!mRemote.asBinder().isBinderAlive() && !VirtualCore.get().isVAppProcess())) {
-            synchronized (this) {
-                Object remote = getRemoteInterface();
-                mRemote = LocalProxyUtils.genProxy(IJobScheduler.class, remote);
-            }
-        }
-        return mRemote;
-    }
-
-    private Object getRemoteInterface() {
-        final IBinder binder = ServiceManagerNative.getService(ServiceManagerNative.JOB);
-        return IJobScheduler.Stub.asInterface(binder);
+    public IJobService getService() {
+        return singleton.get();
     }
 
     public int schedule(JobInfo job) {
         try {
-            return getRemote().schedule(job);
+            return getService().schedule(job);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -50,7 +37,7 @@ public int schedule(JobInfo job) {
 
     public List<JobInfo> getAllPendingJobs() {
         try {
-            return getRemote().getAllPendingJobs();
+            return getService().getAllPendingJobs();
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -58,7 +45,7 @@ public List<JobInfo> getAllPendingJobs() {
 
     public void cancelAll() {
         try {
-            getRemote().cancelAll();
+            getService().cancelAll();
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -66,7 +53,7 @@ public void cancelAll() {
 
     public void cancel(int jobId) {
         try {
-            getRemote().cancel(jobId);
+            getService().cancel(jobId);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VNotificationManager.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VNotificationManager.java
index a1402e0c8..19cfb61b1 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VNotificationManager.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VNotificationManager.java
@@ -1,11 +1,11 @@
 package com.lody.virtual.client.ipc;
 
 import android.app.Notification;
-import android.os.IBinder;
 import android.os.RemoteException;
 
 import com.lody.virtual.client.core.VirtualCore;
-import com.lody.virtual.server.INotificationManager;
+import com.lody.virtual.helper.ipcbus.IPCSingleton;
+import com.lody.virtual.server.interfaces.INotificationManager;
 import com.lody.virtual.server.notification.NotificationCompat;
 
 /**
@@ -14,7 +14,7 @@
 public class VNotificationManager {
     private static final VNotificationManager sInstance = new VNotificationManager();
     private final NotificationCompat mNotificationCompat;
-    private INotificationManager mRemote;
+    private IPCSingleton<INotificationManager> singleton = new IPCSingleton<>(INotificationManager.class);
 
     private VNotificationManager() {
         mNotificationCompat = NotificationCompat.create();
@@ -25,18 +25,11 @@ public static VNotificationManager get() {
     }
 
     public INotificationManager getService() {
-        if (mRemote == null ||
-                (!mRemote.asBinder().isBinderAlive() && !VirtualCore.get().isVAppProcess())) {
-            synchronized (VNotificationManager.class) {
-                final IBinder pmBinder = ServiceManagerNative.getService(ServiceManagerNative.NOTIFICATION);
-                mRemote = INotificationManager.Stub.asInterface(pmBinder);
-            }
-        }
-        return mRemote;
+        return singleton.get();
     }
 
     public boolean dealNotification(int id, Notification notification, String packageName) {
-        if(notification == null)return false;
+        if (notification == null) return false;
         return VirtualCore.get().getHostPkg().equals(packageName)
                 || mNotificationCompat.dealNotification(id, notification, packageName);
     }
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VPackageManager.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VPackageManager.java
index 317b0180e..2bff2d514 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VPackageManager.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VPackageManager.java
@@ -10,13 +10,12 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.os.IBinder;
 import android.os.RemoteException;
 
-import com.lody.virtual.client.core.VirtualCore;
 import com.lody.virtual.client.env.VirtualRuntime;
+import com.lody.virtual.helper.ipcbus.IPCSingleton;
 import com.lody.virtual.server.IPackageInstaller;
-import com.lody.virtual.server.IPackageManager;
+import com.lody.virtual.server.interfaces.IPackageManager;
 
 import java.util.List;
 
@@ -26,31 +25,19 @@
 public class VPackageManager {
 
     private static final VPackageManager sMgr = new VPackageManager();
-    private IPackageManager mRemote;
+    private IPCSingleton<IPackageManager> singleton = new IPCSingleton<>(IPackageManager.class);
 
     public static VPackageManager get() {
         return sMgr;
     }
 
-    public IPackageManager getInterface() {
-        if (mRemote == null ||
-                (!mRemote.asBinder().isBinderAlive() && !VirtualCore.get().isVAppProcess())) {
-            synchronized (VPackageManager.class) {
-                Object remote = getRemoteInterface();
-                mRemote = LocalProxyUtils.genProxy(IPackageManager.class, remote);
-            }
-        }
-        return mRemote;
-    }
-
-    private Object getRemoteInterface() {
-        final IBinder pmBinder = ServiceManagerNative.getService(ServiceManagerNative.PACKAGE);
-        return IPackageManager.Stub.asInterface(pmBinder);
+    public IPackageManager getService() {
+        return singleton.get();
     }
 
     public int checkPermission(String permName, String pkgName, int userId) {
         try {
-            return getInterface().checkPermission(permName, pkgName, userId);
+            return getService().checkPermission(permName, pkgName, userId);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -58,7 +45,7 @@ public int checkPermission(String permName, String pkgName, int userId) {
 
     public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) {
         try {
-            return getInterface().resolveService(intent, resolvedType, flags, userId);
+            return getService().resolveService(intent, resolvedType, flags, userId);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -66,7 +53,7 @@ public ResolveInfo resolveService(Intent intent, String resolvedType, int flags,
 
     public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) {
         try {
-            return getInterface().getPermissionGroupInfo(name, flags);
+            return getService().getPermissionGroupInfo(name, flags);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -75,7 +62,7 @@ public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) {
     public List<ApplicationInfo> getInstalledApplications(int flags, int userId) {
         try {
             // noinspection unchecked
-            return getInterface().getInstalledApplications(flags, userId).getList();
+            return getService().getInstalledApplications(flags, userId).getList();
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -83,7 +70,7 @@ public List<ApplicationInfo> getInstalledApplications(int flags, int userId) {
 
     public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
         try {
-            return getInterface().getPackageInfo(packageName, flags, userId);
+            return getService().getPackageInfo(packageName, flags, userId);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -91,7 +78,7 @@ public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
 
     public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) {
         try {
-            return getInterface().resolveIntent(intent, resolvedType, flags, userId);
+            return getService().resolveIntent(intent, resolvedType, flags, userId);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -99,7 +86,7 @@ public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags,
 
     public List<ResolveInfo> queryIntentContentProviders(Intent intent, String resolvedType, int flags, int userId) {
         try {
-            return getInterface().queryIntentContentProviders(intent, resolvedType, flags, userId);
+            return getService().queryIntentContentProviders(intent, resolvedType, flags, userId);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -107,7 +94,7 @@ public List<ResolveInfo> queryIntentContentProviders(Intent intent, String resol
 
     public ActivityInfo getReceiverInfo(ComponentName componentName, int flags, int userId) {
         try {
-            return getInterface().getReceiverInfo(componentName, flags, userId);
+            return getService().getReceiverInfo(componentName, flags, userId);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -115,7 +102,7 @@ public ActivityInfo getReceiverInfo(ComponentName componentName, int flags, int
 
     public List<PackageInfo> getInstalledPackages(int flags, int userId) {
         try {
-            return getInterface().getInstalledPackages(flags, userId).getList();
+            return getService().getInstalledPackages(flags, userId).getList();
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -123,7 +110,7 @@ public List<PackageInfo> getInstalledPackages(int flags, int userId) {
 
     public List<PermissionInfo> queryPermissionsByGroup(String group, int flags) {
         try {
-            return getInterface().queryPermissionsByGroup(group, flags);
+            return getService().queryPermissionsByGroup(group, flags);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -131,7 +118,7 @@ public List<PermissionInfo> queryPermissionsByGroup(String group, int flags) {
 
     public PermissionInfo getPermissionInfo(String name, int flags) {
         try {
-            return getInterface().getPermissionInfo(name, flags);
+            return getService().getPermissionInfo(name, flags);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -139,7 +126,7 @@ public PermissionInfo getPermissionInfo(String name, int flags) {
 
     public ActivityInfo getActivityInfo(ComponentName componentName, int flags, int userId) {
         try {
-            return getInterface().getActivityInfo(componentName, flags, userId);
+            return getService().getActivityInfo(componentName, flags, userId);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -147,7 +134,7 @@ public ActivityInfo getActivityInfo(ComponentName componentName, int flags, int
 
     public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) {
         try {
-            return getInterface().queryIntentReceivers(intent, resolvedType, flags, userId);
+            return getService().queryIntentReceivers(intent, resolvedType, flags, userId);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -155,7 +142,7 @@ public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType
 
     public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
         try {
-            return getInterface().getAllPermissionGroups(flags);
+            return getService().getAllPermissionGroups(flags);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -163,7 +150,7 @@ public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
 
     public List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) {
         try {
-            return getInterface().queryIntentActivities(intent, resolvedType, flags, userId);
+            return getService().queryIntentActivities(intent, resolvedType, flags, userId);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -171,7 +158,7 @@ public List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedTyp
 
     public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags, int userId) {
         try {
-            return getInterface().queryIntentServices(intent, resolvedType, flags, userId);
+            return getService().queryIntentServices(intent, resolvedType, flags, userId);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -179,7 +166,7 @@ public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType,
 
     public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
         try {
-            return getInterface().getApplicationInfo(packageName, flags, userId);
+            return getService().getApplicationInfo(packageName, flags, userId);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -187,7 +174,7 @@ public ApplicationInfo getApplicationInfo(String packageName, int flags, int use
 
     public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
         try {
-            return getInterface().resolveContentProvider(name, flags, userId);
+            return getService().resolveContentProvider(name, flags, userId);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -195,7 +182,7 @@ public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
 
     public ServiceInfo getServiceInfo(ComponentName componentName, int flags, int userId) {
         try {
-            return getInterface().getServiceInfo(componentName, flags, userId);
+            return getService().getServiceInfo(componentName, flags, userId);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -203,7 +190,7 @@ public ServiceInfo getServiceInfo(ComponentName componentName, int flags, int us
 
     public ProviderInfo getProviderInfo(ComponentName componentName, int flags, int userId) {
         try {
-            return getInterface().getProviderInfo(componentName, flags, userId);
+            return getService().getProviderInfo(componentName, flags, userId);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -211,7 +198,7 @@ public ProviderInfo getProviderInfo(ComponentName componentName, int flags, int
 
     public boolean activitySupportsIntent(ComponentName component, Intent intent, String resolvedType) {
         try {
-            return getInterface().activitySupportsIntent(component, intent, resolvedType);
+            return getService().activitySupportsIntent(component, intent, resolvedType);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -220,7 +207,7 @@ public boolean activitySupportsIntent(ComponentName component, Intent intent, St
     public List<ProviderInfo> queryContentProviders(String processName, int uid, int flags) {
         try {
             // noinspection unchecked
-            return getInterface().queryContentProviders(processName, uid, flags).getList();
+            return getService().queryContentProviders(processName, uid, flags).getList();
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -228,7 +215,7 @@ public List<ProviderInfo> queryContentProviders(String processName, int uid, int
 
     public List<String> querySharedPackages(String packageName) {
         try {
-            return getInterface().querySharedPackages(packageName);
+            return getService().querySharedPackages(packageName);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -236,7 +223,7 @@ public List<String> querySharedPackages(String packageName) {
 
     public String[] getPackagesForUid(int uid) {
         try {
-            return getInterface().getPackagesForUid(uid);
+            return getService().getPackagesForUid(uid);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -244,7 +231,7 @@ public String[] getPackagesForUid(int uid) {
 
     public int getPackageUid(String packageName, int userId) {
         try {
-            return getInterface().getPackageUid(packageName, userId);
+            return getService().getPackageUid(packageName, userId);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -252,7 +239,7 @@ public int getPackageUid(String packageName, int userId) {
 
     public String getNameForUid(int uid) {
         try {
-            return getInterface().getNameForUid(uid);
+            return getService().getNameForUid(uid);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -261,7 +248,7 @@ public String getNameForUid(int uid) {
 
     public IPackageInstaller getPackageInstaller() {
         try {
-            return getInterface().getPackageInstaller();
+            return IPackageInstaller.Stub.asInterface(getService().getPackageInstaller());
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VirtualLocationManager.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VirtualLocationManager.java
index e9e5393f9..98160ffce 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VirtualLocationManager.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VirtualLocationManager.java
@@ -1,14 +1,13 @@
 package com.lody.virtual.client.ipc;
 
-import android.os.IBinder;
 import android.os.RemoteException;
 
-import com.lody.virtual.client.core.VirtualCore;
 import com.lody.virtual.client.env.VirtualRuntime;
 import com.lody.virtual.client.hook.base.MethodProxy;
+import com.lody.virtual.helper.ipcbus.IPCSingleton;
 import com.lody.virtual.remote.vloc.VCell;
 import com.lody.virtual.remote.vloc.VLocation;
-import com.lody.virtual.server.IVirtualLocationManager;
+import com.lody.virtual.server.interfaces.IVirtualLocationManager;
 
 import java.util.List;
 
@@ -19,7 +18,7 @@
 public class VirtualLocationManager {
 
     private static final VirtualLocationManager sInstance = new VirtualLocationManager();
-    private IVirtualLocationManager mRemote;
+    private IPCSingleton<IVirtualLocationManager> singleton = new IPCSingleton<>(IVirtualLocationManager.class);
 
     public static final int MODE_CLOSE = 0;
     public static final int MODE_USE_GLOBAL = 1;
@@ -31,25 +30,14 @@ public static VirtualLocationManager get() {
     }
 
 
-    public IVirtualLocationManager getRemote() {
-        if (mRemote == null ||
-                (!mRemote.asBinder().isBinderAlive() && !VirtualCore.get().isVAppProcess())) {
-            synchronized (this) {
-                Object remote = getRemoteInterface();
-                mRemote = LocalProxyUtils.genProxy(IVirtualLocationManager.class, remote);
-            }
-        }
-        return mRemote;
+    public IVirtualLocationManager getService() {
+        return singleton.get();
     }
 
-    private Object getRemoteInterface() {
-        final IBinder binder = ServiceManagerNative.getService(ServiceManagerNative.VIRTUAL_LOC);
-        return IVirtualLocationManager.Stub.asInterface(binder);
-    }
 
     public int getMode(int userId, String pkg) {
         try {
-            return getRemote().getMode(userId, pkg);
+            return getService().getMode(userId, pkg);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -61,7 +49,7 @@ public int getMode() {
 
     public void setMode(int userId, String pkg, int mode) {
         try {
-            getRemote().setMode(userId, pkg, mode);
+            getService().setMode(userId, pkg, mode);
         } catch (RemoteException e) {
             VirtualRuntime.crash(e);
         }
@@ -69,7 +57,7 @@ public void setMode(int userId, String pkg, int mode) {
 
     public void setCell(int userId, String pkg, VCell cell) {
         try {
-            getRemote().setCell(userId, pkg, cell);
+            getService().setCell(userId, pkg, cell);
         } catch (RemoteException e) {
             VirtualRuntime.crash(e);
         }
@@ -77,7 +65,7 @@ public void setCell(int userId, String pkg, VCell cell) {
 
     public void setAllCell(int userId, String pkg, List<VCell> cell) {
         try {
-            getRemote().setAllCell(userId, pkg, cell);
+            getService().setAllCell(userId, pkg, cell);
         } catch (RemoteException e) {
             VirtualRuntime.crash(e);
         }
@@ -85,7 +73,7 @@ public void setAllCell(int userId, String pkg, List<VCell> cell) {
 
     public void setNeighboringCell(int userId, String pkg, List<VCell> cell) {
         try {
-            getRemote().setNeighboringCell(userId, pkg, cell);
+            getService().setNeighboringCell(userId, pkg, cell);
         } catch (RemoteException e) {
             VirtualRuntime.crash(e);
         }
@@ -93,7 +81,7 @@ public void setNeighboringCell(int userId, String pkg, List<VCell> cell) {
 
     public VCell getCell(int userId, String pkg) {
         try {
-            return getRemote().getCell(userId, pkg);
+            return getService().getCell(userId, pkg);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -101,7 +89,7 @@ public VCell getCell(int userId, String pkg) {
 
     public List<VCell> getAllCell(int userId, String pkg) {
         try {
-            return getRemote().getAllCell(userId, pkg);
+            return getService().getAllCell(userId, pkg);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -109,7 +97,7 @@ public List<VCell> getAllCell(int userId, String pkg) {
 
     public List<VCell> getNeighboringCell(int userId, String pkg) {
         try {
-            return getRemote().getNeighboringCell(userId, pkg);
+            return getService().getNeighboringCell(userId, pkg);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -118,7 +106,7 @@ public List<VCell> getNeighboringCell(int userId, String pkg) {
 
     public void setGlobalCell(VCell cell) {
         try {
-            getRemote().setGlobalCell(cell);
+            getService().setGlobalCell(cell);
         } catch (RemoteException e) {
             VirtualRuntime.crash(e);
         }
@@ -126,7 +114,7 @@ public void setGlobalCell(VCell cell) {
 
     public void setGlobalAllCell(List<VCell> cell) {
         try {
-            getRemote().setGlobalAllCell(cell);
+            getService().setGlobalAllCell(cell);
         } catch (RemoteException e) {
             VirtualRuntime.crash(e);
         }
@@ -134,7 +122,7 @@ public void setGlobalAllCell(List<VCell> cell) {
 
     public void setGlobalNeighboringCell(List<VCell> cell) {
         try {
-            getRemote().setGlobalNeighboringCell(cell);
+            getService().setGlobalNeighboringCell(cell);
         } catch (RemoteException e) {
             VirtualRuntime.crash(e);
         }
@@ -142,7 +130,7 @@ public void setGlobalNeighboringCell(List<VCell> cell) {
 
     public void setLocation(int userId, String pkg, VLocation loc) {
         try {
-            getRemote().setLocation(userId, pkg, loc);
+            getService().setLocation(userId, pkg, loc);
         } catch (RemoteException e) {
             VirtualRuntime.crash(e);
         }
@@ -150,7 +138,7 @@ public void setLocation(int userId, String pkg, VLocation loc) {
 
     public VLocation getLocation(int userId, String pkg) {
         try {
-            return getRemote().getLocation(userId, pkg);
+            return getService().getLocation(userId, pkg);
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
@@ -162,7 +150,7 @@ public VLocation getLocation() {
 
     public void setGlobalLocation(VLocation loc) {
         try {
-            getRemote().setGlobalLocation(loc);
+            getService().setGlobalLocation(loc);
         } catch (RemoteException e) {
             VirtualRuntime.crash(e);
         }
@@ -170,7 +158,7 @@ public void setGlobalLocation(VLocation loc) {
 
     public VLocation getGlobalLocation() {
         try {
-            return getRemote().getGlobalLocation();
+            return getService().getGlobalLocation();
         } catch (RemoteException e) {
             return VirtualRuntime.crash(e);
         }
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VirtualStorageManager.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VirtualStorageManager.java
index 58f8844ec..81d7b6b1e 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VirtualStorageManager.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VirtualStorageManager.java
@@ -1,12 +1,11 @@
 package com.lody.virtual.client.ipc;
 
 
-import android.os.IBinder;
 import android.os.RemoteException;
 
-import com.lody.virtual.client.core.VirtualCore;
 import com.lody.virtual.client.env.VirtualRuntime;
-import com.lody.virtual.server.IVirtualStorageService;
+import com.lody.virtual.helper.ipcbus.IPCSingleton;
+import com.lody.virtual.server.interfaces.IVirtualStorageService;
 
 /**
  * @author Lody
@@ -15,7 +14,7 @@
 public class VirtualStorageManager {
 
     private static final VirtualStorageManager sInstance = new VirtualStorageManager();
-    private IVirtualStorageService mRemote;
+    private IPCSingleton<IVirtualStorageService> singleton = new IPCSingleton<>(IVirtualStorageService.class);
 
 
     public static VirtualStorageManager get() {
@@ -24,19 +23,7 @@ public static VirtualStorageManager get() {
 
 
     public IVirtualStorageService getRemote() {
-        if (mRemote == null ||
-                (!mRemote.asBinder().isBinderAlive() && !VirtualCore.get().isVAppProcess())) {
-            synchronized (this) {
-                Object remote = getRemoteInterface();
-                mRemote = LocalProxyUtils.genProxy(IVirtualStorageService.class, remote);
-            }
-        }
-        return mRemote;
-    }
-
-    private Object getRemoteInterface() {
-        final IBinder binder = ServiceManagerNative.getService(ServiceManagerNative.VS);
-        return IVirtualStorageService.Stub.asInterface(binder);
+        return singleton.get();
     }
 
     public void setVirtualStorage(String packageName, int userId, String vsPath) {
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IPCBus.java b/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IPCBus.java
new file mode 100644
index 000000000..aa066b410
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IPCBus.java
@@ -0,0 +1,42 @@
+package com.lody.virtual.helper.ipcbus;
+
+
+import android.os.IBinder;
+
+import java.lang.reflect.Proxy;
+
+/**
+ * @author Lody
+ */
+public class IPCBus {
+
+    private static IServerCache sCache;
+
+    public static void initialize(IServerCache cache) {
+        sCache = cache;
+    }
+
+    private static void checkInitialized() {
+        if (sCache == null) {
+            throw new IllegalStateException("please call initialize() at first.");
+        }
+    }
+
+    public static void register(Class<?> interfaceClass, Object server) {
+        checkInitialized();
+        ServerInterface serverInterface = new ServerInterface(interfaceClass);
+        TransformBinder binder = new TransformBinder(serverInterface, server);
+        sCache.join(serverInterface.getInterfaceName(), binder);
+    }
+
+    public static <T> T get(Class<?> interfaceClass) {
+        checkInitialized();
+        ServerInterface serverInterface = new ServerInterface(interfaceClass);
+        IBinder binder = sCache.query(serverInterface.getInterfaceName());
+        if (binder == null) {
+            return null;
+        }
+        //noinspection unchecked
+        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, new IPCInvocationBridge(serverInterface, binder));
+    }
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IPCInvocationBridge.java b/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IPCInvocationBridge.java
new file mode 100644
index 000000000..cff24a10f
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IPCInvocationBridge.java
@@ -0,0 +1,29 @@
+package com.lody.virtual.helper.ipcbus;
+
+import android.os.IBinder;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+/**
+ * @author Lody
+ */
+public class IPCInvocationBridge implements InvocationHandler {
+
+    private ServerInterface serverInterface;
+    private IBinder binder;
+
+    public IPCInvocationBridge(ServerInterface serverInterface, IBinder binder) {
+        this.serverInterface = serverInterface;
+        this.binder = binder;
+    }
+
+    @Override
+    public Object invoke(Object o, Method method, Object[] args) throws Throwable {
+        IPCMethod ipcMethod = serverInterface.getIPCMethod(method);
+        if (ipcMethod == null) {
+            throw new IllegalStateException("Can not found the ipc method : " + method.getDeclaringClass().getName() + "@" +  method.getName());
+        }
+        return ipcMethod.callRemote(binder, args);
+    }
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IPCMethod.java b/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IPCMethod.java
new file mode 100644
index 000000000..12ebcde04
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IPCMethod.java
@@ -0,0 +1,174 @@
+package com.lody.virtual.helper.ipcbus;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * @author Lody
+ */
+public class IPCMethod {
+
+    private int code;
+    private Method method;
+    private String interfaceName;
+    private MethodParamConverter[] converters;
+    private MethodParamConverter resultConverter;
+
+
+    public IPCMethod(int code, Method method, String interfaceName) {
+        this.code = code;
+        this.method = method;
+        this.interfaceName = interfaceName;
+        Class<?>[] parameterTypes = method.getParameterTypes();
+        converters = new MethodParamConverter[parameterTypes.length];
+        for (int i = 0; i < parameterTypes.length; i++) {
+            if (isAidlParam(parameterTypes[i])) {
+                converters[i] = new AidlParamConverter(parameterTypes[i]);
+            }
+        }
+        Class<?> returnType = method.getReturnType();
+        if (isAidlParam(returnType)) {
+            resultConverter = new AidlParamConverter(returnType);
+        }
+    }
+
+    private boolean isAidlParam(Class<?> type) {
+        return type.isInterface() && IInterface.class.isAssignableFrom(type);
+    }
+
+
+    public String getInterfaceName() {
+        return interfaceName;
+    }
+
+    public Method getMethod() {
+        return method;
+    }
+
+    public void handleTransact(Object server, Parcel data, Parcel reply) {
+        data.enforceInterface(interfaceName);
+        Object[] parameters = data.readArray(getClass().getClassLoader());
+        if (parameters != null && parameters.length > 0) {
+            for (int i = 0; i < parameters.length; i++) {
+                if (converters[i] != null) {
+                    parameters[i] = converters[i].convert(parameters[i]);
+                }
+            }
+        }
+        try {
+            Object res = method.invoke(server, parameters);
+            reply.writeNoException();
+            reply.writeValue(res);
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+            reply.writeException(e);
+        } catch (InvocationTargetException e) {
+            e.printStackTrace();
+            reply.writeException(e);
+        }
+    }
+
+    private static Method findAsInterfaceMethod(Class<?> type) {
+        for (Class<?> innerClass : type.getDeclaredClasses()) {
+            // public static class Stub extends Binder implements IType
+            if (Modifier.isStatic(innerClass.getModifiers())
+                    && Binder.class.isAssignableFrom(innerClass)
+                    && type.isAssignableFrom(innerClass)) {
+                // public static IType asInterface(android.os.IBinder obj)
+                for (Method method : innerClass.getDeclaredMethods()) {
+                    if (Modifier.isStatic(method.getModifiers())) {
+                        Class<?>[] types = method.getParameterTypes();
+                        if (types.length == 1 && types[0] == IBinder.class) {
+                            return method;
+                        }
+                    }
+                }
+            }
+        }
+        throw new IllegalStateException("Can not found the " + type.getName() + "$Stub.asInterface method.");
+    }
+
+    public Object callRemote(IBinder server, Object[] args) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        Object result;
+        try {
+            data.writeInterfaceToken(interfaceName);
+            data.writeArray(args);
+            server.transact(code, data, reply, 0);
+            reply.readException();
+            result = readValue(reply);
+            if (resultConverter != null) {
+                result = resultConverter.convert(result);
+            }
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+        return result;
+    }
+
+    private Object readValue(Parcel replay) {
+        Object result = replay.readValue(getClass().getClassLoader());
+        if (result instanceof Parcelable[]) {
+            Parcelable[] parcelables = (Parcelable[]) result;
+            Object[] results = (Object[]) Array.newInstance(method.getReturnType().getComponentType(), parcelables.length);
+            System.arraycopy(parcelables, 0, results, 0, results.length);
+            return results;
+        }
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        IPCMethod ipcMethod = (IPCMethod) o;
+
+        return method != null ? method.equals(ipcMethod.method) : ipcMethod.method == null;
+    }
+
+    public interface MethodParamConverter {
+        Object convert(Object param);
+    }
+
+    private class AidlParamConverter implements MethodParamConverter {
+
+        private Method asInterfaceMethod;
+        private Class<?> type;
+
+        AidlParamConverter(Class<?> type) {
+            this.type = type;
+        }
+
+        @Override
+        public Object convert(Object param) {
+            if (param != null) {
+                if (asInterfaceMethod == null) {
+                    synchronized (this) {
+                        if (asInterfaceMethod == null) {
+                            asInterfaceMethod = findAsInterfaceMethod(type);
+                        }
+                    }
+                }
+                try {
+                    return asInterfaceMethod.invoke(null, param);
+                } catch (Throwable e) {
+                    throw new IllegalStateException(e);
+                }
+            }
+            return null;
+        }
+    }
+
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IPCSingleton.java b/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IPCSingleton.java
new file mode 100644
index 000000000..56c3f00c1
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IPCSingleton.java
@@ -0,0 +1,26 @@
+package com.lody.virtual.helper.ipcbus;
+
+/**
+ * @author Lody
+ */
+public class IPCSingleton<T> {
+
+    private Class<?> ipcClass;
+    private T instance;
+
+    public IPCSingleton(Class<?> ipcClass) {
+        this.ipcClass = ipcClass;
+    }
+
+    public T get() {
+        if (instance == null) {
+            synchronized (this) {
+                if (instance == null) {
+                    instance = IPCBus.get(ipcClass);
+                }
+            }
+        }
+        return instance;
+    }
+
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IServerCache.java b/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IServerCache.java
new file mode 100644
index 000000000..63d3c8cd6
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IServerCache.java
@@ -0,0 +1,11 @@
+package com.lody.virtual.helper.ipcbus;
+
+import android.os.IBinder;
+
+/**
+ * @author Lody
+ */
+public interface IServerCache {
+    void join(String serverName, IBinder binder);
+    IBinder query(String serverName);
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/ServerInterface.java b/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/ServerInterface.java
new file mode 100644
index 000000000..103f039cf
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/ServerInterface.java
@@ -0,0 +1,48 @@
+package com.lody.virtual.helper.ipcbus;
+
+import android.os.Binder;
+
+import com.lody.virtual.helper.collection.SparseArray;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Lody
+ */
+public class ServerInterface {
+
+    private Class<?> interfaceClass;
+    private final SparseArray<IPCMethod> codeToInterfaceMethod;
+    private final Map<Method, IPCMethod> methodToIPCMethodMap;
+
+    public ServerInterface(Class<?> interfaceClass) {
+        this.interfaceClass = interfaceClass;
+        Method[] methods = interfaceClass.getMethods();
+        codeToInterfaceMethod = new SparseArray<>(methods.length);
+        methodToIPCMethodMap = new HashMap<>(methods.length);
+        for (int i = 0; i < methods.length; i++) {
+            int code = Binder.FIRST_CALL_TRANSACTION + i;
+            IPCMethod ipcMethod = new IPCMethod(code, methods[i], interfaceClass.getName());
+            codeToInterfaceMethod.put(code, ipcMethod);
+            methodToIPCMethodMap.put(methods[i], ipcMethod);
+        }
+    }
+
+    public Class<?> getInterfaceClass() {
+        return interfaceClass;
+    }
+
+    public String getInterfaceName() {
+        return interfaceClass.getName();
+    }
+
+    public IPCMethod getIPCMethod(int code) {
+        return codeToInterfaceMethod.get(code);
+    }
+
+    public IPCMethod getIPCMethod(Method method) {
+        return methodToIPCMethodMap.get(method);
+    }
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/TransformBinder.java b/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/TransformBinder.java
new file mode 100644
index 000000000..459b786a6
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/TransformBinder.java
@@ -0,0 +1,38 @@
+package com.lody.virtual.helper.ipcbus;
+
+import android.os.Binder;
+import android.os.Parcel;
+import android.os.RemoteException;
+
+
+/**
+ * @author Lody
+ */
+public class TransformBinder extends Binder {
+
+    private ServerInterface serverInterface;
+    private Object server;
+
+    public TransformBinder(ServerInterface serverInterface, Object server) {
+        this.serverInterface = serverInterface;
+        this.server = server;
+    }
+
+    @Override
+    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
+        if (code == INTERFACE_TRANSACTION) {
+            reply.writeString(serverInterface.getInterfaceName());
+            return true;
+        }
+        IPCMethod method = serverInterface.getIPCMethod(code);
+        if (method != null) {
+            try {
+                method.handleTransact(server, data, reply);
+            } catch (Throwable e) {
+                e.printStackTrace();
+            }
+            return true;
+        }
+        return super.onTransact(code, data, reply, flags);
+    }
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/os/VEnvironment.java b/VirtualApp/lib/src/main/java/com/lody/virtual/os/VEnvironment.java
index be1d1ae51..72a5a4ee9 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/os/VEnvironment.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/os/VEnvironment.java
@@ -4,6 +4,7 @@
 import android.os.Build;
 
 import com.lody.virtual.client.core.VirtualCore;
+import com.lody.virtual.client.env.VirtualRuntime;
 import com.lody.virtual.helper.utils.FileUtils;
 import com.lody.virtual.helper.utils.VLog;
 
@@ -116,7 +117,7 @@ public static File getDalvikCacheDirectory() {
     }
 
     public static File getOdexFile(String packageName) {
-        return new File(DALVIK_CACHE_DIRECTORY, "base.odex");
+        return new File(DALVIK_CACHE_DIRECTORY, "data@app@" + packageName + "-1@base.apk@classes.dex");
     }
 
     public static File getDataAppPackageDirectory(String packageName) {
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/os/VUserManager.java b/VirtualApp/lib/src/main/java/com/lody/virtual/os/VUserManager.java
index aeef66ccb..20b6e4537 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/os/VUserManager.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/os/VUserManager.java
@@ -4,13 +4,11 @@
 import android.os.RemoteException;
 import android.util.Log;
 
-import com.lody.virtual.client.ipc.ServiceManagerNative;
-import com.lody.virtual.server.IUserManager;
+import com.lody.virtual.helper.ipcbus.IPCBus;
+import com.lody.virtual.server.interfaces.IUserManager;
 
 import java.util.List;
 
-import static com.lody.virtual.client.ipc.ServiceManagerNative.USER;
-
 /**
  * Manages users and user details on a multi-user system.
  */
@@ -108,7 +106,7 @@ public class VUserManager {
     /** @hide */
     public synchronized static VUserManager get() {
         if (sInstance == null) {
-            IUserManager remote = IUserManager.Stub.asInterface(ServiceManagerNative.getService(USER));
+            IUserManager remote = IPCBus.get(IUserManager.class);
             sInstance = new VUserManager(remote);
         }
         return sInstance;
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/BinderProvider.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/BinderProvider.java
index 327ce44a4..a3bd51306 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/BinderProvider.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/BinderProvider.java
@@ -11,14 +11,23 @@
 import android.os.RemoteException;
 
 import com.lody.virtual.client.core.VirtualCore;
-import com.lody.virtual.client.ipc.ServiceManagerNative;
 import com.lody.virtual.client.stub.DaemonService;
 import com.lody.virtual.helper.compat.BundleCompat;
+import com.lody.virtual.helper.ipcbus.IPCBus;
 import com.lody.virtual.server.accounts.VAccountManagerService;
 import com.lody.virtual.server.am.BroadcastSystem;
 import com.lody.virtual.server.am.VActivityManagerService;
 import com.lody.virtual.server.device.VDeviceManagerService;
+import com.lody.virtual.server.interfaces.IAccountManager;
+import com.lody.virtual.server.interfaces.IActivityManager;
+import com.lody.virtual.server.interfaces.IAppManager;
+import com.lody.virtual.server.interfaces.IDeviceInfoManager;
+import com.lody.virtual.server.interfaces.INotificationManager;
+import com.lody.virtual.server.interfaces.IPackageManager;
 import com.lody.virtual.server.interfaces.IServiceFetcher;
+import com.lody.virtual.server.interfaces.IUserManager;
+import com.lody.virtual.server.interfaces.IVirtualLocationManager;
+import com.lody.virtual.server.interfaces.IVirtualStorageService;
 import com.lody.virtual.server.job.VJobSchedulerService;
 import com.lody.virtual.server.location.VirtualLocationService;
 import com.lody.virtual.server.notification.VNotificationManagerService;
@@ -27,6 +36,8 @@
 import com.lody.virtual.server.pm.VUserManagerService;
 import com.lody.virtual.server.vs.VirtualStorageService;
 
+import mirror.android.app.job.IJobScheduler;
+
 /**
  * @author Lody
  */
@@ -42,38 +53,36 @@ public boolean onCreate() {
             return true;
         }
         VPackageManagerService.systemReady();
-        addService(ServiceManagerNative.PACKAGE, VPackageManagerService.get());
+        IPCBus.register(IPackageManager.class, VPackageManagerService.get());
         VActivityManagerService.systemReady(context);
-        addService(ServiceManagerNative.ACTIVITY, VActivityManagerService.get());
-        addService(ServiceManagerNative.USER, VUserManagerService.get());
+        IPCBus.register(IActivityManager.class, VActivityManagerService.get());
+        IPCBus.register(IUserManager.class, VUserManagerService.get());
         VAppManagerService.systemReady();
-        addService(ServiceManagerNative.APP, VAppManagerService.get());
+        IPCBus.register(IAppManager.class, VAppManagerService.get());
         BroadcastSystem.attach(VActivityManagerService.get(), VAppManagerService.get());
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-            addService(ServiceManagerNative.JOB, VJobSchedulerService.get());
+            IPCBus.register(IJobScheduler.class, VJobSchedulerService.get());
         }
         VNotificationManagerService.systemReady(context);
-        addService(ServiceManagerNative.NOTIFICATION, VNotificationManagerService.get());
+        IPCBus.register(INotificationManager.class, VNotificationManagerService.get());
         VAppManagerService.get().scanApps();
         VAccountManagerService.systemReady();
-        addService(ServiceManagerNative.ACCOUNT, VAccountManagerService.get());
-        addService(ServiceManagerNative.VS, VirtualStorageService.get());
-        addService(ServiceManagerNative.DEVICE, VDeviceManagerService.get());
-        addService(ServiceManagerNative.VIRTUAL_LOC, VirtualLocationService.get());
+        IPCBus.register(IAccountManager.class, VAccountManagerService.get());
+        IPCBus.register(IVirtualStorageService.class, VirtualStorageService.get());
+        IPCBus.register(IDeviceInfoManager.class, VDeviceManagerService.get());
+        IPCBus.register(IVirtualLocationManager.class, VirtualLocationService.get());
         return true;
     }
 
-
-    private void addService(String name, IBinder service) {
-        ServiceCache.addService(name, service);
-    }
-
     @Override
     public Bundle call(String method, String arg, Bundle extras) {
         if ("@".equals(method)) {
             Bundle bundle = new Bundle();
             BundleCompat.putBinder(bundle, "_VA_|_binder_", mServiceFetcher);
             return bundle;
+        }
+        if ("register".equals(method)) {
+
         }
         return null;
     }
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/accounts/VAccountManagerService.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/accounts/VAccountManagerService.java
index fb37458fb..db74c4ff9 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/accounts/VAccountManagerService.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/accounts/VAccountManagerService.java
@@ -34,8 +34,8 @@
 import com.lody.virtual.os.VBinder;
 import com.lody.virtual.os.VEnvironment;
 import com.lody.virtual.os.VUserHandle;
-import com.lody.virtual.server.IAccountManager;
 import com.lody.virtual.server.am.VActivityManagerService;
+import com.lody.virtual.server.interfaces.IAccountManager;
 import com.lody.virtual.server.pm.VPackageManagerService;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -62,7 +62,7 @@
 /**
  * @author Lody
  */
-public class VAccountManagerService extends IAccountManager.Stub {
+public class VAccountManagerService implements IAccountManager {
 
     private static final AtomicReference<VAccountManagerService> sInstance = new AtomicReference<>();
     private static final long CHECK_IN_TIME = 30 * 24 * 60 * 1000L;
@@ -185,16 +185,6 @@ private List<Account> getAccountList(int userId, String type) {
         }
     }
 
-    @Override
-    public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
-        try {
-            return super.onTransact(code, data, reply, flags);
-        } catch (Throwable e) {
-            e.printStackTrace();
-            throw e;
-        }
-    }
-
     @Override
     public final void getAuthToken(final int userId, final IAccountManagerResponse response, final Account account, final String authTokenType, final boolean notifyOnAuthFailure, boolean expectActivityLaunch, final Bundle loginOptions) {
         if (response == null) {
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/am/BroadcastSystem.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/am/BroadcastSystem.java
index d6f701aaf..8d7a98251 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/am/BroadcastSystem.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/am/BroadcastSystem.java
@@ -8,18 +8,22 @@
 import android.content.pm.ActivityInfo;
 import android.os.Build;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Message;
 
 import com.lody.virtual.client.core.VirtualCore;
 import com.lody.virtual.client.env.SpecialComponentList;
 import com.lody.virtual.helper.collection.ArrayMap;
+import com.lody.virtual.helper.utils.Reflect;
 import com.lody.virtual.helper.utils.VLog;
 import com.lody.virtual.remote.PendingResultData;
 import com.lody.virtual.server.pm.PackageSetting;
 import com.lody.virtual.server.pm.VAppManagerService;
 import com.lody.virtual.server.pm.parser.VPackage;
 
+import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -61,8 +65,12 @@ private BroadcastSystem(Context context, VActivityManagerService ams, VAppManage
         this.mContext = context;
         this.mApp = app;
         this.mAMS = ams;
-        mScheduler = new StaticScheduler();
-        mTimeoutHandler = new TimeoutHandler();
+        HandlerThread broadcastThread = new HandlerThread("BroadcastThread");
+        HandlerThread anrThread = new HandlerThread("BroadcastAnrThread");
+        broadcastThread.start();
+        anrThread.start();
+        mScheduler = new StaticScheduler(broadcastThread.getLooper());
+        mTimeoutHandler = new TimeoutHandler(anrThread.getLooper());
         fuckHuaWeiVerifier();
     }
 
@@ -97,7 +105,15 @@ private void fuckHuaWeiVerifier() {
             if (packageInfo != null) {
                 Object receiverResource = LoadedApkHuaWei.mReceiverResource.get(packageInfo);
                 if (receiverResource != null) {
-                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+                    if (Build.VERSION.SDK_INT >= 26) {
+                        Map<Integer, List<String>> whiteListMap = Reflect.on(receiverResource).get("mWhiteListMap");
+                        List<String> whiteList = whiteListMap.get(0);
+                        if (whiteList == null) {
+                            whiteList = new ArrayList<>();
+                            whiteListMap.put(0, whiteList);
+                        }
+                        whiteList.add(mContext.getPackageName());
+                    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                         if (ReceiverResourceN.mWhiteList != null) {
                             List<String> whiteList = ReceiverResourceN.mWhiteList.get(receiverResource);
                             List<String> newWhiteList = new ArrayList<>();
@@ -108,7 +124,6 @@ private void fuckHuaWeiVerifier() {
                             }
                             ReceiverResourceN.mWhiteList.set(receiverResource, newWhiteList);
                         }
-
                     } else {
                         if (ReceiverResourceM.mWhiteList != null) {
                             String[] whiteList = ReceiverResourceM.mWhiteList.get(receiverResource);
@@ -198,6 +213,9 @@ void broadcastSent(int vuid, ActivityInfo receiverInfo, PendingResultData res) {
 
     private static final class StaticScheduler extends Handler {
 
+        StaticScheduler(Looper looper) {
+            super(looper);
+        }
     }
 
     private static final class BroadcastRecord {
@@ -213,6 +231,11 @@ private static final class BroadcastRecord {
     }
 
     private final class TimeoutHandler extends Handler {
+
+        TimeoutHandler(Looper looper) {
+            super(looper);
+        }
+
         @Override
         public void handleMessage(Message msg) {
             IBinder token = (IBinder) msg.obj;
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/am/VActivityManagerService.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/am/VActivityManagerService.java
index a034a5649..e6f720835 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/am/VActivityManagerService.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/am/VActivityManagerService.java
@@ -49,7 +49,7 @@
 import com.lody.virtual.remote.PendingIntentData;
 import com.lody.virtual.remote.PendingResultData;
 import com.lody.virtual.remote.VParceledListSlice;
-import com.lody.virtual.server.IActivityManager;
+import com.lody.virtual.server.interfaces.IActivityManager;
 import com.lody.virtual.server.interfaces.IProcessObserver;
 import com.lody.virtual.server.pm.PackageCacheManager;
 import com.lody.virtual.server.pm.PackageSetting;
@@ -68,12 +68,13 @@
 import mirror.android.app.IServiceConnectionO;
 
 import static android.os.Process.killProcess;
+import static com.lody.virtual.os.VBinder.getCallingPid;
 import static com.lody.virtual.os.VUserHandle.getUserId;
 
 /**
  * @author Lody
  */
-public class VActivityManagerService extends IActivityManager.Stub {
+public class VActivityManagerService implements IActivityManager {
 
     private static final boolean BROADCAST_NOT_STARTED_PKG = false;
 
@@ -233,7 +234,7 @@ private void processDead(ProcessRecord record) {
     public IBinder acquireProviderClient(int userId, ProviderInfo info) {
         ProcessRecord callerApp;
         synchronized (mPidsSelfLocked) {
-            callerApp = findProcessLocked(VBinder.getCallingPid());
+            callerApp = findProcessLocked(getCallingPid());
         }
         if (callerApp == null) {
             throw new SecurityException("Who are you?");
@@ -263,17 +264,6 @@ public String getCallingPackage(int userId, IBinder token) {
         return mMainStack.getCallingPackage(userId, token);
     }
 
-
-    @Override
-    public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
-        try {
-            return super.onTransact(code, data, reply, flags);
-        } catch (Throwable e) {
-            e.printStackTrace();
-            throw e;
-        }
-    }
-
     private void addRecord(ServiceRecord r) {
         mHistory.add(r);
     }
@@ -389,7 +379,7 @@ private void stopServiceCommon(ServiceRecord r, ComponentName className) {
                 // Report to all of the connections that the service is no longer
                 // available.
                 try {
-                    if(Build.VERSION.SDK_INT >= 26) {
+                    if (Build.VERSION.SDK_INT >= 26) {
                         IServiceConnectionO.connected.call(connection, className, null, true);
                     } else {
                         connection.connected(className, null);
@@ -560,7 +550,7 @@ public void publishService(IBinder token, Intent intent, IBinder service, int us
         }
     }
 
-    private void connectService(IServiceConnection conn, ComponentName component, ServiceRecord.IntentBindRecord r,boolean dead) {
+    private void connectService(IServiceConnection conn, ComponentName component, ServiceRecord.IntentBindRecord r, boolean dead) {
         try {
             BinderDelegateService delegateService = new BinderDelegateService(component, r.binder);
             if (Build.VERSION.SDK_INT >= 26) {
@@ -718,7 +708,7 @@ private void attachClient(int pid, final IBinder clientBinder) {
         }
         try {
             final ProcessRecord record = app;
-            clientBinder.linkToDeath(new DeathRecipient() {
+            clientBinder.linkToDeath(new IBinder.DeathRecipient() {
                 @Override
                 public void binderDied() {
                     clientBinder.unlinkToDeath(this, 0);
@@ -941,16 +931,6 @@ public void dump() {
 
     }
 
-    @Override
-    public void registerProcessObserver(IProcessObserver observer) {
-
-    }
-
-    @Override
-    public void unregisterProcessObserver(IProcessObserver observer) {
-
-    }
-
     @Override
     public String getInitialPackage(int pid) {
         synchronized (mPidsSelfLocked) {
@@ -970,7 +950,7 @@ public void handleApplicationCrash() {
     @Override
     public void appDoneExecuting() {
         synchronized (mPidsSelfLocked) {
-            ProcessRecord r = mPidsSelfLocked.get(VBinder.getCallingPid());
+            ProcessRecord r = mPidsSelfLocked.get(getCallingPid());
             if (r != null) {
                 r.doneExecuting = true;
                 r.lock.open();
@@ -1118,7 +1098,7 @@ public void broadcastFinish(PendingResultData res) {
     }
 
     @Override
-    public void notifyBadgerChange(BadgerInfo info) throws RemoteException {
+    public void notifyBadgerChange(BadgerInfo info) {
         Intent intent = new Intent(VASettings.ACTION_BADGER_CHANGE);
         intent.putExtra("userId", info.userId);
         intent.putExtra("packageName", info.packageName);
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/device/VDeviceManagerService.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/device/VDeviceManagerService.java
index 326fa8bee..8064737fb 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/device/VDeviceManagerService.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/device/VDeviceManagerService.java
@@ -6,7 +6,7 @@
 
 import com.lody.virtual.helper.collection.SparseArray;
 import com.lody.virtual.remote.VDeviceInfo;
-import com.lody.virtual.server.IDeviceInfoManager;
+import com.lody.virtual.server.interfaces.IDeviceInfoManager;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -17,7 +17,7 @@
  * @author Lody
  */
 
-public class VDeviceManagerService extends IDeviceInfoManager.Stub {
+public class VDeviceManagerService implements IDeviceInfoManager {
 
     private static VDeviceManagerService sInstance = new VDeviceManagerService();
     private final SparseArray<VDeviceInfo> mDeviceInfos = new SparseArray<>();
@@ -53,7 +53,7 @@ private void addDeviceInfoToPool(VDeviceInfo info) {
     }
 
     @Override
-    public VDeviceInfo getDeviceInfo(int userId) throws RemoteException {
+    public VDeviceInfo getDeviceInfo(int userId) {
         VDeviceInfo info;
         synchronized (mDeviceInfos) {
             info = mDeviceInfos.get(userId);
@@ -67,7 +67,7 @@ public VDeviceInfo getDeviceInfo(int userId) throws RemoteException {
     }
 
     @Override
-    public void updateDeviceInfo(int userId, VDeviceInfo info) throws RemoteException {
+    public void updateDeviceInfo(int userId, VDeviceInfo info) {
         synchronized (mDeviceInfos) {
             if (info != null) {
                 mDeviceInfos.put(userId, info);
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IAccountManager.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IAccountManager.java
new file mode 100644
index 000000000..6a2d85871
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IAccountManager.java
@@ -0,0 +1,61 @@
+package com.lody.virtual.server.interfaces;
+
+import android.accounts.Account;
+import android.accounts.AuthenticatorDescription;
+import android.accounts.IAccountManagerResponse;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+/**
+ * @author Lody
+ */
+public interface IAccountManager {
+
+    AuthenticatorDescription[] getAuthenticatorTypes(int userId) throws RemoteException;
+
+    void getAccountsByFeatures(int userId, IAccountManagerResponse response, String type, String[] features) throws RemoteException;
+
+    String getPreviousName(int userId, Account account) throws RemoteException;
+
+    Account[] getAccounts(int userId, String type) throws RemoteException;
+
+    void getAuthToken(int userId, IAccountManagerResponse response, Account account, String authTokenType, boolean notifyOnAuthFailure, boolean expectActivityLaunch, Bundle loginOptions) throws RemoteException;
+
+    void setPassword(int userId, Account account, String password) throws RemoteException;
+
+    void setAuthToken(int userId, Account account, String authTokenType, String authToken) throws RemoteException;
+
+    void setUserData(int userId, Account account, String key, String value) throws RemoteException;
+
+    void hasFeatures(int userId, IAccountManagerResponse response, Account account, String[] features) throws RemoteException;
+
+    void updateCredentials(int userId, IAccountManagerResponse response, Account account, String authTokenType, boolean expectActivityLaunch, Bundle loginOptions) throws RemoteException;
+
+    void editProperties(int userId, IAccountManagerResponse response, String accountType, boolean expectActivityLaunch) throws RemoteException;
+
+    void getAuthTokenLabel(int userId, IAccountManagerResponse response, String accountType, String authTokenType) throws RemoteException;
+
+    String getUserData(int userId, Account account, String key) throws RemoteException;
+
+    String getPassword(int userId, Account account) throws RemoteException;
+
+    void confirmCredentials(int userId, IAccountManagerResponse response, Account account, Bundle options, boolean expectActivityLaunch) throws RemoteException;
+
+    void addAccount(int userId, IAccountManagerResponse response, String accountType, String authTokenType, String[] requiredFeatures, boolean expectActivityLaunch, Bundle optionsIn) throws RemoteException;
+
+    boolean addAccountExplicitly(int userId, Account account, String password, Bundle extras) throws RemoteException;
+
+    boolean removeAccountExplicitly(int userId, Account account) throws RemoteException;
+
+    void renameAccount(int userId, IAccountManagerResponse response, Account accountToRename, String newName) throws RemoteException;
+
+    void removeAccount(int userId, IAccountManagerResponse response, Account account, boolean expectActivityLaunch) throws RemoteException;
+
+    void clearPassword(int userId, Account account) throws RemoteException;
+
+    boolean accountAuthenticated(int userId, Account account) throws RemoteException;
+
+    void invalidateAuthToken(int userId, String accountType, String authToken) throws RemoteException;
+
+    String peekAuthToken(int userId, Account account, String authTokenType) throws RemoteException;
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IActivityManager.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IActivityManager.java
new file mode 100644
index 000000000..408b06add
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IActivityManager.java
@@ -0,0 +1,106 @@
+package com.lody.virtual.server.interfaces;
+
+import android.app.*;
+import android.content.*;
+import android.content.pm.*;
+import android.os.*;
+
+import com.lody.virtual.remote.*;
+
+/**
+ * @author Lody
+ */
+public interface IActivityManager {
+
+    int initProcess(String packageName, String processName, int userId) throws RemoteException;
+
+    int getFreeStubCount() throws RemoteException;
+
+    int getSystemPid() throws RemoteException;
+
+    int getUidByPid(int pid) throws RemoteException;
+
+    boolean isAppProcess(String processName) throws RemoteException;
+
+    boolean isAppRunning(String packageName, int userId) throws RemoteException;
+
+    boolean isAppPid(int pid) throws RemoteException;
+
+    String getAppProcessName(int pid) throws RemoteException;
+
+    java.util.List<String> getProcessPkgList(int pid) throws RemoteException;
+
+    void killAllApps() throws RemoteException;
+
+    void killAppByPkg(String pkg, int userId) throws RemoteException;
+
+    void killApplicationProcess(String processName, int vuid) throws RemoteException;
+
+    void dump() throws RemoteException;
+
+    String getInitialPackage(int pid) throws RemoteException;
+
+    void handleApplicationCrash() throws RemoteException;
+
+    void appDoneExecuting() throws RemoteException;
+
+    int startActivities(Intent[] intents, String[] resolvedTypes, IBinder token, Bundle options, int userId) throws RemoteException;
+
+    int startActivity(Intent intent, ActivityInfo info, IBinder resultTo, Bundle options, String resultWho, int requestCode, int userId) throws RemoteException;
+
+    void onActivityCreated(ComponentName component, ComponentName caller, IBinder token, Intent intent, String affinity, int taskId, int launchMode, int flags) throws RemoteException;
+
+    void onActivityResumed(int userId, IBinder token) throws RemoteException;
+
+    boolean onActivityDestroyed(int userId, IBinder token) throws RemoteException;
+
+    ComponentName getActivityClassForToken(int userId, IBinder token) throws RemoteException;
+
+    String getCallingPackage(int userId, IBinder token) throws RemoteException;
+
+    ComponentName getCallingActivity(int userId, IBinder token) throws RemoteException;
+
+    AppTaskInfo getTaskInfo(int taskId) throws RemoteException;
+
+    String getPackageForToken(int userId, IBinder token) throws RemoteException;
+
+    boolean isVAServiceToken(IBinder token) throws RemoteException;
+
+    ComponentName startService(IBinder caller, Intent service, String resolvedType, int userId) throws RemoteException;
+
+    int stopService(IBinder caller, Intent service, String resolvedType, int userId) throws RemoteException;
+
+    boolean stopServiceToken(ComponentName className, IBinder token, int startId, int userId) throws RemoteException;
+
+    void setServiceForeground(ComponentName className, IBinder token, int id, Notification notification, boolean removeNotification, int userId) throws RemoteException;
+
+    int bindService(IBinder caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, int userId) throws RemoteException;
+
+    boolean unbindService(IServiceConnection connection, int userId) throws RemoteException;
+
+    void unbindFinished(IBinder token, Intent service, boolean doRebind, int userId) throws RemoteException;
+
+    void serviceDoneExecuting(IBinder token, int type, int startId, int res, int userId) throws RemoteException;
+
+    IBinder peekService(Intent service, String resolvedType, int userId) throws RemoteException;
+
+    void publishService(IBinder token, Intent intent, IBinder service, int userId) throws RemoteException;
+
+    VParceledListSlice getServices(int maxNum, int flags, int userId) throws RemoteException;
+
+    IBinder acquireProviderClient(int userId, ProviderInfo info) throws RemoteException;
+
+    PendingIntentData getPendingIntent(IBinder binder) throws RemoteException;
+
+    void addPendingIntent(IBinder binder, String packageName) throws RemoteException;
+
+    void removePendingIntent(IBinder binder) throws RemoteException;
+
+    String getPackageForIntentSender(IBinder binder) throws RemoteException;
+
+    void processRestarted(String packageName, String processName, int userId) throws RemoteException;
+
+    void broadcastFinish(PendingResultData res) throws RemoteException;
+
+    void notifyBadgerChange(BadgerInfo info) throws RemoteException;
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IAppManager.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IAppManager.java
new file mode 100644
index 000000000..cbf2e085f
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IAppManager.java
@@ -0,0 +1,58 @@
+package com.lody.virtual.server.interfaces;
+
+import android.os.RemoteException;
+
+import com.lody.virtual.remote.InstallResult;
+import com.lody.virtual.remote.InstalledAppInfo;
+
+import java.util.List;
+
+/**
+ * @author Lody
+ */
+public interface IAppManager {
+
+    int[] getPackageInstalledUsers(String packageName) throws RemoteException;
+
+    void scanApps() throws RemoteException;
+
+    void addVisibleOutsidePackage(String pkg) throws RemoteException;
+
+    void removeVisibleOutsidePackage(String pkg) throws RemoteException;
+
+    boolean isOutsidePackageVisible(String pkg) throws RemoteException;
+
+    InstalledAppInfo getInstalledAppInfo(String pkg, int flags) throws RemoteException;
+
+    InstallResult installPackage(String path, int flags) throws RemoteException;
+
+    boolean isPackageLaunched(int userId, String packageName) throws RemoteException;
+
+    void setPackageHidden(int userId, String packageName, boolean hidden) throws RemoteException;
+
+    boolean installPackageAsUser(int userId, String packageName) throws RemoteException;
+
+    boolean uninstallPackageAsUser(String packageName, int userId) throws RemoteException;
+
+    boolean uninstallPackage(String packageName) throws RemoteException;
+
+    List<InstalledAppInfo> getInstalledApps(int flags) throws RemoteException;
+
+    List<InstalledAppInfo> getInstalledAppsAsUser(int userId, int flags) throws RemoteException;
+
+    int getInstalledAppCount() throws RemoteException;
+
+    boolean isAppInstalled(String packageName) throws RemoteException;
+
+    boolean isAppInstalledAsUser(int userId, String packageName) throws RemoteException;
+
+    void registerObserver(IPackageObserver observer) throws RemoteException;
+
+    void unregisterObserver(IPackageObserver observer) throws RemoteException;
+
+    void setAppRequestListener(IAppRequestListener listener) throws RemoteException;
+
+    void clearAppRequestListener() throws RemoteException;
+
+    IAppRequestListener getAppRequestListener() throws RemoteException;
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IDeviceInfoManager.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IDeviceInfoManager.java
new file mode 100644
index 000000000..c7470892d
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IDeviceInfoManager.java
@@ -0,0 +1,16 @@
+package com.lody.virtual.server.interfaces;
+
+import android.os.RemoteException;
+
+import com.lody.virtual.remote.VDeviceInfo;
+
+/**
+ * @author Lody
+ */
+public interface IDeviceInfoManager {
+
+    VDeviceInfo getDeviceInfo(int userId) throws RemoteException;
+
+    void updateDeviceInfo(int userId, VDeviceInfo info) throws RemoteException;
+
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IJobService.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IJobService.java
new file mode 100644
index 000000000..36c08a5bc
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IJobService.java
@@ -0,0 +1,20 @@
+package com.lody.virtual.server.interfaces;
+
+import android.app.job.JobInfo;
+import android.os.RemoteException;
+
+import java.util.List;
+
+/**
+ * @author Lody
+ */
+public interface IJobService {
+
+    int schedule(JobInfo job) throws RemoteException;
+
+    void cancel(int jobId) throws RemoteException;
+
+    void cancelAll() throws RemoteException;
+
+    List<JobInfo> getAllPendingJobs() throws RemoteException;
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/INotificationManager.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/INotificationManager.java
new file mode 100644
index 000000000..9bc7d41c4
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/INotificationManager.java
@@ -0,0 +1,21 @@
+package com.lody.virtual.server.interfaces;
+
+import android.os.RemoteException;
+
+/**
+ * @author Lody
+ */
+public interface INotificationManager {
+
+    int dealNotificationId(int id, String packageName, String tag, int userId) throws RemoteException;
+
+    String dealNotificationTag(int id, String packageName, String tag, int userId) throws RemoteException;
+
+    boolean areNotificationsEnabledForPackage(String packageName, int userId) throws RemoteException;
+
+    void setNotificationsEnabledForPackage(String packageName, boolean enable, int userId) throws RemoteException;
+
+    void addNotification(int id, String tag, String packageName, int userId) throws RemoteException;
+
+    void cancelAllNotification(String packageName, int userId) throws RemoteException;
+}
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IPackageManager.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IPackageManager.java
new file mode 100644
index 000000000..c2e98bd4c
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IPackageManager.java
@@ -0,0 +1,72 @@
+package com.lody.virtual.server.interfaces;
+
+import android.content.*;
+import android.content.pm.*;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.lody.virtual.remote.VParceledListSlice;
+
+import java.util.List;
+
+/**
+ * @author Lody
+ */
+public interface IPackageManager {
+
+    int getPackageUid(String packageName, int userId) throws RemoteException;
+
+    String[] getPackagesForUid(int vuid) throws RemoteException;
+
+    List<String> getSharedLibraries(String pkgName) throws RemoteException;
+
+    int checkPermission(String permName, String pkgName, int userId) throws RemoteException;
+
+    PackageInfo getPackageInfo(String packageName, int flags, int userId) throws RemoteException;
+
+    ActivityInfo getActivityInfo(ComponentName componentName, int flags, int userId) throws RemoteException;
+
+    boolean activitySupportsIntent(ComponentName component, Intent intent, String resolvedType) throws RemoteException;
+
+    ActivityInfo getReceiverInfo(ComponentName componentName, int flags, int userId) throws RemoteException;
+
+    ServiceInfo getServiceInfo(ComponentName componentName, int flags, int userId) throws RemoteException;
+
+    ProviderInfo getProviderInfo(ComponentName componentName, int flags, int userId) throws RemoteException;
+
+    ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) throws RemoteException;
+
+    List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) throws RemoteException;
+
+    List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) throws RemoteException;
+
+    ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) throws RemoteException;
+
+    List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags, int userId) throws RemoteException;
+
+    List<ResolveInfo> queryIntentContentProviders(Intent intent, String resolvedType, int flags, int userId) throws RemoteException;
+
+    VParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) throws RemoteException;
+
+    VParceledListSlice<ApplicationInfo> getInstalledApplications(int flags, int userId) throws RemoteException;
+
+    PermissionInfo getPermissionInfo(String name, int flags) throws RemoteException;
+
+    List<PermissionInfo> queryPermissionsByGroup(String group, int flags) throws RemoteException;
+
+    PermissionGroupInfo getPermissionGroupInfo(String name, int flags) throws RemoteException;
+
+    List<PermissionGroupInfo> getAllPermissionGroups(int flags) throws RemoteException;
+
+    ProviderInfo resolveContentProvider(String name, int flags, int userId) throws RemoteException;
+
+    ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) throws RemoteException;
+
+    VParceledListSlice queryContentProviders(String processName, int vuid, int flags) throws RemoteException;
+
+    List<String> querySharedPackages(String packageName) throws RemoteException;
+
+    String getNameForUid(int uid) throws RemoteException;
+
+    IBinder getPackageInstaller() throws RemoteException;
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IUserManager.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IUserManager.java
new file mode 100644
index 000000000..c3497596a
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IUserManager.java
@@ -0,0 +1,37 @@
+package com.lody.virtual.server.interfaces;
+
+import android.graphics.Bitmap;
+import android.os.RemoteException;
+
+import com.lody.virtual.os.VUserInfo;
+
+import java.util.List;
+
+/**
+ * @author Lody
+ */
+public interface IUserManager {
+    VUserInfo createUser(String name, int flags) throws RemoteException;
+
+    boolean removeUser(int userHandle) throws RemoteException;
+
+    void setUserName(int userHandle, String name) throws RemoteException;
+
+    void setUserIcon(int userHandle, Bitmap icon) throws RemoteException;
+
+    Bitmap getUserIcon(int userHandle) throws RemoteException;
+
+    List<VUserInfo> getUsers(boolean excludeDying) throws RemoteException;
+
+    VUserInfo getUserInfo(int userHandle) throws RemoteException;
+
+    void setGuestEnabled(boolean enable) throws RemoteException;
+
+    boolean isGuestEnabled() throws RemoteException;
+
+    void wipeUser(int userHandle) throws RemoteException;
+
+    int getUserSerialNumber(int userHandle) throws RemoteException;
+
+    int getUserHandle(int userSerialNumber) throws RemoteException;
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IVirtualLocationManager.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IVirtualLocationManager.java
new file mode 100644
index 000000000..f7097b5d8
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IVirtualLocationManager.java
@@ -0,0 +1,44 @@
+package com.lody.virtual.server.interfaces;
+
+import android.os.RemoteException;
+
+import com.lody.virtual.remote.vloc.VCell;
+import com.lody.virtual.remote.vloc.VLocation;
+
+import java.util.List;
+
+/**
+ * @author Lody
+ */
+public interface IVirtualLocationManager {
+
+    int getMode(int userId, String pkg) throws RemoteException;
+
+    void setMode(int userId, String pkg, int mode) throws RemoteException;
+
+    void setCell(int userId, String pkg, VCell cell) throws RemoteException;
+
+    void setAllCell(int userId, String pkg, List<VCell> cell) throws RemoteException;
+
+    void setNeighboringCell(int userId, String pkg, List<VCell> cell) throws RemoteException;
+
+    void setGlobalCell(VCell cell) throws RemoteException;
+
+    void setGlobalAllCell(List<VCell> cell) throws RemoteException;
+
+    void setGlobalNeighboringCell(List<VCell> cell) throws RemoteException;
+
+    VCell getCell(int userId, String pkg) throws RemoteException;
+
+    List<VCell> getAllCell(int userId, String pkg) throws RemoteException;
+
+    List<VCell> getNeighboringCell(int userId, String pkg) throws RemoteException;
+
+    void setLocation(int userId, String pkg, VLocation loc) throws RemoteException;
+
+    VLocation getLocation(int userId, String pkg) throws RemoteException;
+
+    void setGlobalLocation(VLocation loc) throws RemoteException;
+
+    VLocation getGlobalLocation() throws RemoteException;
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IVirtualStorageService.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IVirtualStorageService.java
new file mode 100644
index 000000000..a998ad288
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IVirtualStorageService.java
@@ -0,0 +1,17 @@
+package com.lody.virtual.server.interfaces;
+
+import android.os.RemoteException;
+
+/**
+ * @author Lody
+ */
+public interface IVirtualStorageService {
+
+    void setVirtualStorage(String packageName, int userId, String vsPath) throws RemoteException;
+
+    String getVirtualStorage(String packageName, int userId) throws RemoteException;
+
+    void setVirtualStorageState(String packageName, int userId, boolean enable) throws RemoteException;
+
+    boolean isVirtualStorageEnable(String packageName, int userId) throws RemoteException;
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/job/VJobSchedulerService.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/job/VJobSchedulerService.java
index b4f0a8f87..7abb6faa2 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/job/VJobSchedulerService.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/job/VJobSchedulerService.java
@@ -9,7 +9,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
-import android.os.RemoteException;
 import android.text.TextUtils;
 
 import com.lody.virtual.client.core.VirtualCore;
@@ -18,7 +17,7 @@
 import com.lody.virtual.helper.utils.Singleton;
 import com.lody.virtual.os.VBinder;
 import com.lody.virtual.os.VEnvironment;
-import com.lody.virtual.server.IJobScheduler;
+import com.lody.virtual.server.interfaces.IJobService;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -34,7 +33,7 @@
  * @author Lody
  */
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-public class VJobSchedulerService extends IJobScheduler.Stub {
+public class VJobSchedulerService implements IJobService {
 
     private static final String TAG = VJobScheduler.class.getSimpleName();
 
@@ -179,7 +178,7 @@ public JobConfig[] newArray(int size) {
 
 
     @Override
-    public int schedule(JobInfo job) throws RemoteException {
+    public int schedule(JobInfo job) {
         int vuid = VBinder.getCallingUid();
         int id = job.getId();
         ComponentName service = job.getService();
@@ -257,7 +256,7 @@ private void readJobs() {
     }
 
     @Override
-    public void cancel(int jobId) throws RemoteException {
+    public void cancel(int jobId) {
         int vuid = VBinder.getCallingUid();
         synchronized (mJobStore) {
             boolean changed = false;
@@ -280,7 +279,7 @@ public void cancel(int jobId) throws RemoteException {
     }
 
     @Override
-    public void cancelAll() throws RemoteException {
+    public void cancelAll() {
         int vuid = VBinder.getCallingUid();
         synchronized (mJobStore) {
             boolean changed = false;
@@ -303,7 +302,7 @@ public void cancelAll() throws RemoteException {
     }
 
     @Override
-    public List<JobInfo> getAllPendingJobs() throws RemoteException {
+    public List<JobInfo> getAllPendingJobs() {
         int vuid = VBinder.getCallingUid();
         List<JobInfo> jobs = mScheduler.getAllPendingJobs();
         synchronized (mJobStore) {
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/location/VirtualLocationService.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/location/VirtualLocationService.java
index dcd387906..e7dedff10 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/location/VirtualLocationService.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/location/VirtualLocationService.java
@@ -9,7 +9,7 @@
 import com.lody.virtual.os.VEnvironment;
 import com.lody.virtual.remote.vloc.VCell;
 import com.lody.virtual.remote.vloc.VLocation;
-import com.lody.virtual.server.IVirtualLocationManager;
+import com.lody.virtual.server.interfaces.IVirtualLocationManager;
 
 import java.util.HashMap;
 import java.util.List;
@@ -19,7 +19,7 @@
  * @author Lody
  */
 
-public class VirtualLocationService extends IVirtualLocationManager.Stub {
+public class VirtualLocationService implements IVirtualLocationManager {
 
     private static final VirtualLocationService sInstance = new VirtualLocationService();
     private final SparseArray<Map<String, VLocConfig>> mLocConfigs = new SparseArray<>();
@@ -123,7 +123,7 @@ private VirtualLocationService() {
     }
 
     @Override
-    public int getMode(int userId, String pkg) throws RemoteException {
+    public int getMode(int userId, String pkg) {
         synchronized (mLocConfigs) {
             VLocConfig config = getOrCreateConfig(userId, pkg);
             mPersistenceLayer.save();
@@ -132,7 +132,7 @@ public int getMode(int userId, String pkg) throws RemoteException {
     }
 
     @Override
-    public void setMode(int userId, String pkg, int mode) throws RemoteException {
+    public void setMode(int userId, String pkg, int mode) {
         synchronized (mLocConfigs) {
             getOrCreateConfig(userId, pkg).mode = mode;
             mPersistenceLayer.save();
@@ -155,43 +155,43 @@ private VLocConfig getOrCreateConfig(int userId, String pkg) {
     }
 
     @Override
-    public void setCell(int userId, String pkg, VCell cell) throws RemoteException {
+    public void setCell(int userId, String pkg, VCell cell) {
         getOrCreateConfig(userId, pkg).cell = cell;
         mPersistenceLayer.save();
     }
 
     @Override
-    public void setAllCell(int userId, String pkg, List<VCell> cell) throws RemoteException {
+    public void setAllCell(int userId, String pkg, List<VCell> cell) {
         getOrCreateConfig(userId, pkg).allCell = cell;
         mPersistenceLayer.save();
     }
 
     @Override
-    public void setNeighboringCell(int userId, String pkg, List<VCell> cell) throws RemoteException {
+    public void setNeighboringCell(int userId, String pkg, List<VCell> cell) {
         getOrCreateConfig(userId, pkg).neighboringCell = cell;
         mPersistenceLayer.save();
     }
 
     @Override
-    public void setGlobalCell(VCell cell) throws RemoteException {
+    public void setGlobalCell(VCell cell) {
         mGlobalConfig.cell = cell;
         mPersistenceLayer.save();
     }
 
     @Override
-    public void setGlobalAllCell(List<VCell> cell) throws RemoteException {
+    public void setGlobalAllCell(List<VCell> cell) {
         mGlobalConfig.allCell = cell;
         mPersistenceLayer.save();
     }
 
     @Override
-    public void setGlobalNeighboringCell(List<VCell> cell) throws RemoteException {
+    public void setGlobalNeighboringCell(List<VCell> cell) {
         mGlobalConfig.neighboringCell = cell;
         mPersistenceLayer.save();
     }
 
     @Override
-    public VCell getCell(int userId, String pkg) throws RemoteException {
+    public VCell getCell(int userId, String pkg) {
         VLocConfig config = getOrCreateConfig(userId, pkg);
         mPersistenceLayer.save();
         switch (config.mode) {
@@ -206,7 +206,7 @@ public VCell getCell(int userId, String pkg) throws RemoteException {
     }
 
     @Override
-    public List<VCell> getAllCell(int userId, String pkg) throws RemoteException {
+    public List<VCell> getAllCell(int userId, String pkg) {
         VLocConfig config = getOrCreateConfig(userId, pkg);
         mPersistenceLayer.save();
         switch (config.mode) {
@@ -221,7 +221,7 @@ public List<VCell> getAllCell(int userId, String pkg) throws RemoteException {
     }
 
     @Override
-    public List<VCell> getNeighboringCell(int userId, String pkg) throws RemoteException {
+    public List<VCell> getNeighboringCell(int userId, String pkg) {
         VLocConfig config = getOrCreateConfig(userId, pkg);
         mPersistenceLayer.save();
         switch (config.mode) {
@@ -236,13 +236,13 @@ public List<VCell> getNeighboringCell(int userId, String pkg) throws RemoteExcep
     }
 
     @Override
-    public void setLocation(int userId, String pkg, VLocation loc) throws RemoteException {
+    public void setLocation(int userId, String pkg, VLocation loc) {
         getOrCreateConfig(userId, pkg).location = loc;
         mPersistenceLayer.save();
     }
 
     @Override
-    public VLocation getLocation(int userId, String pkg) throws RemoteException {
+    public VLocation getLocation(int userId, String pkg) {
         VLocConfig config = getOrCreateConfig(userId, pkg);
         mPersistenceLayer.save();
         switch (config.mode) {
@@ -257,12 +257,12 @@ public VLocation getLocation(int userId, String pkg) throws RemoteException {
     }
 
     @Override
-    public void setGlobalLocation(VLocation loc) throws RemoteException {
+    public void setGlobalLocation(VLocation loc) {
         mGlobalConfig.location = loc;
     }
 
     @Override
-    public VLocation getGlobalLocation() throws RemoteException {
+    public VLocation getGlobalLocation() {
         return mGlobalConfig.location;
     }
 
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/MappedMemoryRegion.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/MappedMemoryRegion.java
new file mode 100644
index 000000000..832707fdc
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/MappedMemoryRegion.java
@@ -0,0 +1,47 @@
+package com.lody.virtual.server.memory;
+
+/**
+ * @author Lody
+ */
+public class MappedMemoryRegion {
+
+    public static class FileMapping {
+        public final long offset;
+        public final long majorDeviceNumber;
+        public final long minorDeviceNumber;
+        public final long inode;
+
+        public FileMapping(long off, long major, long minor, long inode) {
+            this.offset = off;
+            this.majorDeviceNumber = major;
+            this.minorDeviceNumber = minor;
+            this.inode = inode;
+        }
+    }
+
+    public final long startAddress;
+    public final long endAddress;
+    public final boolean isReadable;
+    public final boolean isWritable;
+    public final boolean isExecutable;
+    public final boolean isShared;
+
+    public final FileMapping fileMapInfo;
+
+    public final String description;
+
+    public MappedMemoryRegion(long start, long end, boolean read, boolean write, boolean exec, boolean shared, long off, long majorDevNum, long minorDevNum, long inode, String desc) {
+        this.startAddress = start;
+        this.endAddress = end;
+        this.isReadable = read;
+        this.isWritable = write;
+        this.isExecutable = exec;
+        this.isShared = shared;
+        this.fileMapInfo = (inode == 0) ? null : new FileMapping(off, majorDevNum, minorDevNum, inode);
+        this.description = desc;
+    }
+
+    public boolean isMappedFromFile() {
+        return this.fileMapInfo != null;
+    }
+}
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/MemoryRegionParser.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/MemoryRegionParser.java
new file mode 100644
index 000000000..5491db5bd
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/MemoryRegionParser.java
@@ -0,0 +1,70 @@
+package com.lody.virtual.server.memory;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author Lody
+ */
+public class MemoryRegionParser {
+    /**
+     * Regular Expression for /proc/self/maps line is
+     * <p>
+     * ([0-9a-f]+)  -   ([0-9a-f]+)  \s  ([r-]) ([w-]) ([x-]) ([sp])   \s    ([0-9a-f]+)   \s  ([0-9a-f]+) :   ([0-9a-f]+)     \s  (\d+)   \s?  (.*)
+     * StartAddress        EndAddress    Read   Write  Execute Shared        Filemap Offset   Major devnum    Minor devnum         Inode       Description
+     */
+    public static final String PATTERN = "([0-9a-f]+)-([0-9a-f]+)\\s([r-])([w-])([x-])([sp])\\s([0-9a-f]+)\\s([0-9a-f]+):([0-9a-f]+)\\s(\\d+)\\s?(.*)";
+    public final static Pattern MAPS_LINE_PATTERN = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE);
+
+    private static long parseHex(String s) {
+        return Long.parseLong(s, 16);
+    }
+
+    private static MappedMemoryRegion parseMapLine(String line) {
+        line = line.trim();
+        Matcher m = MAPS_LINE_PATTERN.matcher(line);
+        if (!m.matches()) {
+            throw new IllegalArgumentException(String.format("The provided line does not match the pattern for /proc/$pid/maps lines. Given: %s", line));
+        }
+
+        if (m.groupCount() != 11) // group(0) not included in this.
+        {
+            throw new InternalError(String.format(Locale.ENGLISH, "Invalid group count: Found %d, but expected %d", m.groupCount(), 12));
+        }
+
+        long start = parseHex(m.group(1));
+        long end = parseHex(m.group(2));
+        boolean read = m.group(3).equals("r");
+        boolean write = m.group(4).equals("w");
+        boolean exec = m.group(5).equals("x");
+        boolean shared = m.group(6).equals("s");
+        long fileOffset = parseHex(m.group(7));
+        long majorDevNum = parseHex(m.group(8));
+        long minorDevNum = parseHex(m.group(9));
+        long inode = parseHex(m.group(10));
+        String desc = m.group(11);
+
+        return new MappedMemoryRegion(start, end, read, write, exec, shared, fileOffset, majorDevNum, minorDevNum, inode, desc);
+    }
+
+
+    public static List<MappedMemoryRegion> getMemoryRegions(int pid) throws IOException {
+        List<MappedMemoryRegion> list = new LinkedList<>();
+        BufferedReader reader = new BufferedReader(new FileReader(String.format(Locale.ENGLISH, "/proc/%d/maps", pid)));
+        String line;
+        while ((line = reader.readLine()) != null) {
+            MappedMemoryRegion region = parseMapLine(line);
+            if (region.isReadable && region.isWritable && !region.description.endsWith("(deleted)")) {
+                list.add(region);
+            }
+        }
+        return list;
+    }
+}
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/MemoryScanEngine.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/MemoryScanEngine.java
new file mode 100644
index 000000000..c9b21c385
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/MemoryScanEngine.java
@@ -0,0 +1,111 @@
+package com.lody.virtual.server.memory;
+
+import com.lody.virtual.helper.utils.VLog;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author Lody
+ */
+public class MemoryScanEngine {
+
+    private List<MappedMemoryRegion> regions;
+
+    private int pid;
+    private ProcessMemory memory;
+    private static final int PAGE = 4096;
+    private List<Match> matches;
+
+    public MemoryScanEngine(int pid) throws IOException {
+        this.pid = pid;
+        this.memory = new ProcessMemory(pid);
+        updateMemoryLayout();
+    }
+
+    public void updateMemoryLayout() {
+        try {
+            regions = MemoryRegionParser.getMemoryRegions(pid);
+        } catch (IOException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    public List<Match> getMatches() {
+        return matches;
+    }
+
+    public void search(MemoryValue value) throws IOException {
+        matches = new LinkedList<>();
+        byte[] bytes = new byte[PAGE];
+        byte[] valueBytes = value.toBytes();
+        for (MappedMemoryRegion region : regions) {
+            long start = region.startAddress;
+            long end = region.endAddress;
+            try {
+                while (start < end) {
+                    int read = Math.min(bytes.length, (int) (end - start));
+                    read = memory.read(start, bytes, read);
+                    matches.addAll(matchBytes(region, start, bytes, read, valueBytes));
+                    start += PAGE;
+                }
+            } catch (IOException e) {
+                VLog.e(getClass().getSimpleName(), "Unable to read region : " + region.description);
+            }
+        }
+    }
+
+    public void modify(Match match, MemoryValue value) throws IOException {
+        memory.write(match.address, value.toBytes());
+    }
+
+    public void modifyAll(MemoryValue value) throws IOException {
+        for (Match match : matches) {
+            modify(match, value);
+        }
+    }
+
+    public class Match {
+        MappedMemoryRegion region;
+        long address;
+        int len;
+
+        public Match(MappedMemoryRegion region, long address, int len) {
+            this.region = region;
+            this.address = address;
+            this.len = len;
+        }
+    }
+
+
+    private List<Match> matchBytes(MappedMemoryRegion region, long startAddress, byte[] page, int read, byte[] value) {
+        List<Match> matches = new LinkedList<>();
+        int start = 0;
+        int len = value.length;
+        int step = 2;
+        while (start < read) {
+            boolean match = true;
+            for (int i = 0; i < len && i + start < read; i++) {
+                if (page[start + i] != value[i]) {
+                    match = false;
+                    break;
+                }
+            }
+            if (match) {
+                matches.add(new Match(region, startAddress + start, len));
+            }
+            start += step;
+        }
+        return matches;
+    }
+
+
+    public void close() {
+        try {
+            memory.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/MemoryValue.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/MemoryValue.java
new file mode 100644
index 000000000..59f7cd3c8
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/MemoryValue.java
@@ -0,0 +1,67 @@
+package com.lody.virtual.server.memory;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * @author Lody
+ */
+public abstract class MemoryValue {
+    
+    private static final ByteOrder BYTE_ORDER = ByteOrder.BIG_ENDIAN;
+
+    public enum ValueType {
+        INT2, // short
+        INT4, // int
+        INT8, // long
+    }
+
+    public abstract byte[] toBytes();
+
+
+    public static class INT2 extends MemoryValue {
+
+        private short val;
+
+        public INT2(short val) {
+            this.val = val;
+        }
+
+        @Override
+        public byte[] toBytes() {
+            ByteBuffer buffer = ByteBuffer.allocate(2);
+            return buffer.putShort(val).order(BYTE_ORDER).array();
+        }
+    }
+
+    public static class INT4 extends MemoryValue {
+
+        private int val;
+
+        public INT4(int val) {
+            this.val = val;
+        }
+
+        @Override
+        public byte[] toBytes() {
+            ByteBuffer buffer = ByteBuffer.allocate(4);
+            return buffer.order(BYTE_ORDER).putInt(val).array();
+        }
+    }
+
+    public static class INT8 extends MemoryValue {
+
+        private long val;
+
+        public INT8(long val) {
+            this.val = val;
+        }
+
+        @Override
+        public byte[] toBytes() {
+            ByteBuffer buffer = ByteBuffer.allocate(8);
+            return buffer.order(BYTE_ORDER).putLong(val).array();
+        }
+    }
+
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/ProcessMemory.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/ProcessMemory.java
new file mode 100644
index 000000000..0c86baae5
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/ProcessMemory.java
@@ -0,0 +1,33 @@
+package com.lody.virtual.server.memory;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.Locale;
+
+/**
+ * @author Lody
+ */
+public class ProcessMemory {
+
+    private int pid;
+    private RandomAccessFile memFile;
+
+    public ProcessMemory(int pid) throws IOException {
+        this.pid = pid;
+        this.memFile = new RandomAccessFile(String.format(Locale.ENGLISH, "/proc/%d/mem", pid), "rw");
+    }
+
+    public void write(long offset, byte[] bytes) throws IOException {
+        memFile.seek(offset);
+        memFile.write(bytes);
+    }
+
+    public int read(long offset, byte[] bytes, int len) throws IOException {
+        memFile.seek(offset);
+        return memFile.read(bytes, 0, len);
+    }
+
+    public void close() throws IOException {
+        memFile.close();
+    }
+}
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/notification/NotificationFixer.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/notification/NotificationFixer.java
index 499c3444f..84e3c616d 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/notification/NotificationFixer.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/notification/NotificationFixer.java
@@ -142,8 +142,7 @@ boolean fixRemoteViewActions(Context appContext, boolean installed, final Remote
                         mActions.remove(action);
                         continue;
                     }
-                    if (ReflectionActionCompat.isInstance(action)
-                            || (action.getClass().getSimpleName().endsWith("ReflectionAction"))) {
+                    if (ReflectionActionCompat.isInstance(action)) {
                         int viewId = Reflect.on(action).get("viewId");
 
                         String methodName = Reflect.on(action).get("methodName");
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/notification/VNotificationManagerService.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/notification/VNotificationManagerService.java
index 8ba98af09..a3093b5e2 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/notification/VNotificationManagerService.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/notification/VNotificationManagerService.java
@@ -5,14 +5,14 @@
 import android.text.TextUtils;
 
 import com.lody.virtual.helper.utils.VLog;
-import com.lody.virtual.server.INotificationManager;
+import com.lody.virtual.server.interfaces.INotificationManager;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 
-public class VNotificationManagerService extends INotificationManager.Stub {
+public class VNotificationManagerService implements INotificationManager {
     private static final AtomicReference<VNotificationManagerService> gService = new AtomicReference<>();
     private NotificationManager mNotificationManager;
     static final String TAG = NotificationCompat.class.getSimpleName();
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/PrivilegeAppOptimizer.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/PrivilegeAppOptimizer.java
index 20b84cf5a..02ce3fbab 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/PrivilegeAppOptimizer.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/PrivilegeAppOptimizer.java
@@ -1,6 +1,7 @@
 package com.lody.virtual.server.pm;
 
 import android.content.Intent;
+import android.util.Log;
 
 import com.lody.virtual.client.stub.VASettings;
 import com.lody.virtual.os.VUserHandle;
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VAppManagerService.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VAppManagerService.java
index 41c569e37..01541163b 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VAppManagerService.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VAppManagerService.java
@@ -3,6 +3,7 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Build;
+import android.os.IBinder;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 
@@ -19,11 +20,11 @@
 import com.lody.virtual.os.VUserHandle;
 import com.lody.virtual.remote.InstallResult;
 import com.lody.virtual.remote.InstalledAppInfo;
-import com.lody.virtual.server.IAppManager;
 import com.lody.virtual.server.accounts.VAccountManagerService;
 import com.lody.virtual.server.am.BroadcastSystem;
 import com.lody.virtual.server.am.UidSystem;
 import com.lody.virtual.server.am.VActivityManagerService;
+import com.lody.virtual.server.interfaces.IAppManager;
 import com.lody.virtual.server.interfaces.IAppRequestListener;
 import com.lody.virtual.server.interfaces.IPackageObserver;
 import com.lody.virtual.server.pm.parser.PackageParserEx;
@@ -42,7 +43,7 @@
 /**
  * @author Lody
  */
-public class VAppManagerService extends IAppManager.Stub {
+public class VAppManagerService implements IAppManager {
 
     private static final String TAG = VAppManagerService.class.getSimpleName();
     private static final AtomicReference<VAppManagerService> sService = new AtomicReference<>();
@@ -513,7 +514,7 @@ public void setAppRequestListener(final IAppRequestListener listener) {
         this.mAppRequestListener = listener;
         if (listener != null) {
             try {
-                listener.asBinder().linkToDeath(new DeathRecipient() {
+                listener.asBinder().linkToDeath(new IBinder.DeathRecipient() {
                     @Override
                     public void binderDied() {
                         listener.asBinder().unlinkToDeath(this, 0);
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VPackageManagerService.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VPackageManagerService.java
index 4c5a1f7af..22970a456 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VPackageManagerService.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VPackageManagerService.java
@@ -13,8 +13,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.os.Build;
-import android.os.Parcel;
-import android.os.RemoteException;
+import android.os.IBinder;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -25,7 +24,7 @@
 import com.lody.virtual.os.VUserHandle;
 import com.lody.virtual.remote.VParceledListSlice;
 import com.lody.virtual.server.IPackageInstaller;
-import com.lody.virtual.server.IPackageManager;
+import com.lody.virtual.server.interfaces.IPackageManager;
 import com.lody.virtual.server.pm.installer.VPackageInstallerService;
 import com.lody.virtual.server.pm.parser.PackageParserEx;
 import com.lody.virtual.server.pm.parser.VPackage;
@@ -40,12 +39,11 @@
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
 
-import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 
 /**
  * @author Lody
  */
-public class VPackageManagerService extends IPackageManager.Stub {
+public class VPackageManagerService implements IPackageManager {
 
     static final String TAG = "PackageManager";
     static final Comparator<ResolveInfo> sResolvePrioritySorter = new Comparator<ResolveInfo>() {
@@ -269,7 +267,7 @@ private int updateFlagsNought(int flags) {
             // give them what they want
         } else {
             // Caller expressed no opinion, so match based on user state
-            flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
+            flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
         }
         return flags;
     }
@@ -774,17 +772,7 @@ public List<String> querySharedPackages(String packageName) {
     }
 
     @Override
-    public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
-        try {
-            return super.onTransact(code, data, reply, flags);
-        } catch (Throwable e) {
-            e.printStackTrace();
-            throw e;
-        }
-    }
-
-    @Override
-    public IPackageInstaller getPackageInstaller() {
+    public IBinder getPackageInstaller() {
         return VPackageInstallerService.get();
     }
 
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VUserManagerService.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VUserManagerService.java
index b45ca62ad..320dc3be1 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VUserManagerService.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VUserManagerService.java
@@ -25,7 +25,7 @@
 import com.lody.virtual.os.VUserInfo;
 import com.lody.virtual.os.VUserManager;
 import com.lody.virtual.server.am.VActivityManagerService;
-import com.lody.virtual.server.IUserManager;
+import com.lody.virtual.server.interfaces.IUserManager;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -47,7 +47,7 @@
 /**
  * @author Lody
  */
-public class VUserManagerService extends IUserManager.Stub {
+public class VUserManagerService implements IUserManager {
 
     private static final String LOG_TAG = "VUserManagerService";
 
@@ -317,7 +317,7 @@ public void makeInitialized(int userId) {
             if (info == null || info.partial) {
                 VLog.w(LOG_TAG, "makeInitialized: unknown user #" + userId);
             }
-            if ((info.flags& VUserInfo.FLAG_INITIALIZED) == 0) {
+            if ((info.flags & VUserInfo.FLAG_INITIALIZED) == 0) {
                 info.flags |= VUserInfo.FLAG_INITIALIZED;
                 writeUserLocked(info);
             }
@@ -360,6 +360,7 @@ private void writeBitmapLocked(VUserInfo info, Bitmap bitmap) {
     /**
      * Returns an array of user ids. This array is cached here for quick access, so do not modify or
      * cache it elsewhere.
+     *
      * @return the array of user ids.
      */
     public int[] getUserIds() {
@@ -513,7 +514,7 @@ private void writeUserLocked(VUserInfo userInfo) {
             serializer.attribute(null, ATTR_LAST_LOGGED_IN_TIME,
                     Long.toString(userInfo.lastLoggedInTime));
             if (userInfo.iconPath != null) {
-                serializer.attribute(null,  ATTR_ICON_PATH, userInfo.iconPath);
+                serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath);
             }
             if (userInfo.partial) {
                 serializer.attribute(null, ATTR_PARTIAL, "true");
@@ -699,7 +700,7 @@ public VUserInfo createUser(String name, int flags) {
             Intent addedIntent = new Intent(Constants.ACTION_USER_ADDED);
             addedIntent.putExtra(Constants.EXTRA_USER_HANDLE, userInfo.id);
             VActivityManagerService.get().sendBroadcastAsUser(addedIntent, VUserHandle.ALL,
-                        null);
+                    null);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -709,6 +710,7 @@ public VUserInfo createUser(String name, int flags) {
     /**
      * Removes a user and all data directories created for that user. This method should be called
      * after the user's processes have been terminated.
+     *
      * @param userHandle the user's id
      */
     public boolean removeUser(int userHandle) {
@@ -728,15 +730,16 @@ public boolean removeUser(int userHandle) {
         }
         if (DBG) VLog.i(LOG_TAG, "Stopping user " + userHandle);
         int res = VActivityManagerService.get().stopUser(userHandle,
-                    new IStopUserCallback.Stub() {
-                        @Override
-                        public void userStopped(int userId) {
-                            finishRemoveUser(userId);
-                        }
-                        @Override
-                        public void userStopAborted(int userId) {
-                        }
-            });
+                new IStopUserCallback.Stub() {
+                    @Override
+                    public void userStopped(int userId) {
+                        finishRemoveUser(userId);
+                    }
+
+                    @Override
+                    public void userStopAborted(int userId) {
+                    }
+                });
         return res == ActivityManagerCompat.USER_OP_SUCCESS;
     }
 
@@ -749,14 +752,14 @@ void finishRemoveUser(final int userHandle) {
             Intent addedIntent = new Intent(Constants.ACTION_USER_REMOVED);
             addedIntent.putExtra(Constants.EXTRA_USER_HANDLE, userHandle);
             VActivityManagerService.get().sendOrderedBroadcastAsUser(addedIntent, VUserHandle.ALL,
-                   null,
+                    null,
                     new BroadcastReceiver() {
                         @Override
                         public void onReceive(Context context, Intent intent) {
                             if (DBG) {
                                 VLog.i(LOG_TAG,
                                         "USER_REMOVED broadcast sent, cleaning up user data "
-                                        + userHandle);
+                                                + userHandle);
                             }
                             new Thread() {
                                 public void run() {
@@ -843,6 +846,7 @@ private void updateUserIdsLocked() {
 
     /**
      * Make a note of the last started time of a user.
+     *
      * @param userId the user that was just foregrounded
      */
     public void userForeground(int userId) {
@@ -864,6 +868,7 @@ public void userForeground(int userId) {
      * Returns the next available user id, filling in any holes in the ids.
      * TODO: May not be a good idea to recycle ids, in case it results in confusion
      * for data and battery stats collection, or unexpected cross-talk.
+     *
      * @return
      */
     private int getNextAvailableIdLocked() {
@@ -880,8 +885,4 @@ private int getNextAvailableIdLocked() {
         }
     }
 
-    @Override
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-
-    }
 }
diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/vs/VirtualStorageService.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/vs/VirtualStorageService.java
index 8eedbc776..2e8e04b96 100644
--- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/vs/VirtualStorageService.java
+++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/vs/VirtualStorageService.java
@@ -3,7 +3,7 @@
 import android.os.RemoteException;
 import android.util.SparseArray;
 
-import com.lody.virtual.server.IVirtualStorageService;
+import com.lody.virtual.server.interfaces.IVirtualStorageService;
 import com.lody.virtual.server.pm.VUserManagerService;
 
 import java.util.HashMap;
@@ -12,7 +12,7 @@
  * @author Lody
  */
 
-public class VirtualStorageService extends IVirtualStorageService.Stub {
+public class VirtualStorageService implements IVirtualStorageService {
 
     private static final VirtualStorageService sService = new VirtualStorageService();
     private final VSPersistenceLayer mLayer = new VSPersistenceLayer(this);
@@ -31,7 +31,7 @@ SparseArray<HashMap<String, VSConfig>> getConfigs() {
     }
 
     @Override
-    public void setVirtualStorage(String packageName, int userId, String vsPath) throws RemoteException {
+    public void setVirtualStorage(String packageName, int userId, String vsPath) {
         checkUserId(userId);
         synchronized (mConfigs) {
             VSConfig config = getOrCreateVSConfigLocked(packageName, userId);
@@ -57,7 +57,7 @@ private VSConfig getOrCreateVSConfigLocked(String packageName, int userId) {
 
 
     @Override
-    public String getVirtualStorage(String packageName, int userId) throws RemoteException {
+    public String getVirtualStorage(String packageName, int userId) {
         checkUserId(userId);
         synchronized (mConfigs) {
             VSConfig config = getOrCreateVSConfigLocked(packageName, userId);
@@ -66,7 +66,7 @@ public String getVirtualStorage(String packageName, int userId) throws RemoteExc
     }
 
     @Override
-    public void setVirtualStorageState(String packageName, int userId, boolean enable) throws RemoteException {
+    public void setVirtualStorageState(String packageName, int userId, boolean enable) {
         checkUserId(userId);
         synchronized (mConfigs) {
             VSConfig config = getOrCreateVSConfigLocked(packageName, userId);
@@ -77,7 +77,7 @@ public void setVirtualStorageState(String packageName, int userId, boolean enabl
     }
 
     @Override
-    public boolean isVirtualStorageEnable(String packageName, int userId) throws RemoteException {
+    public boolean isVirtualStorageEnable(String packageName, int userId) {
         checkUserId(userId);
         synchronized (mConfigs) {
             VSConfig config = getOrCreateVSConfigLocked(packageName, userId);
diff --git a/VirtualApp/lib/src/main/java/mirror/android/app/ContextImplKitkat.java b/VirtualApp/lib/src/main/java/mirror/android/app/ContextImplKitkat.java
index 8618f7160..6f92c656d 100644
--- a/VirtualApp/lib/src/main/java/mirror/android/app/ContextImplKitkat.java
+++ b/VirtualApp/lib/src/main/java/mirror/android/app/ContextImplKitkat.java
@@ -10,4 +10,5 @@ public class ContextImplKitkat {
     public static RefObject<File[]> mExternalCacheDirs;
     public static RefObject<File[]> mExternalFilesDirs;
     public static RefObject<String> mOpPackageName;
+    public static RefObject<Object> mDisplayAdjustments;
 }
diff --git a/VirtualApp/lib/src/main/java/mirror/android/app/LoadedApkICS.java b/VirtualApp/lib/src/main/java/mirror/android/app/LoadedApkICS.java
new file mode 100644
index 000000000..0cc032450
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/mirror/android/app/LoadedApkICS.java
@@ -0,0 +1,10 @@
+package mirror.android.app;
+
+
+import mirror.RefClass;
+import mirror.RefObject;
+
+public class LoadedApkICS {
+    public static Class<?> Class = RefClass.load(LoadedApkICS.class, "android.app.LoadedApk");
+    public static RefObject<Object> mCompatibilityInfo;
+}
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/java/mirror/android/app/LoadedApkKitkat.java b/VirtualApp/lib/src/main/java/mirror/android/app/LoadedApkKitkat.java
new file mode 100644
index 000000000..344abcd58
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/mirror/android/app/LoadedApkKitkat.java
@@ -0,0 +1,10 @@
+package mirror.android.app;
+
+
+import mirror.RefClass;
+import mirror.RefObject;
+
+public class LoadedApkKitkat {
+    public static Class<?> Class = RefClass.load(LoadedApkKitkat.class, "android.app.LoadedApk");
+    public static RefObject<Object> mDisplayAdjustments;
+}
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/java/mirror/android/view/CompatibilityInfoHolder.java b/VirtualApp/lib/src/main/java/mirror/android/view/CompatibilityInfoHolder.java
new file mode 100644
index 000000000..c149afc29
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/mirror/android/view/CompatibilityInfoHolder.java
@@ -0,0 +1,12 @@
+package mirror.android.view;
+
+
+import mirror.MethodReflectParams;
+import mirror.RefClass;
+import mirror.RefMethod;
+
+public class CompatibilityInfoHolder {
+    public static Class<?> Class = RefClass.load(CompatibilityInfoHolder.class, "android.view.CompatibilityInfoHolder");
+    @MethodReflectParams({"android.content.res.CompatibilityInfo"})
+    public static RefMethod<Void> set;
+}
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/java/mirror/android/view/DisplayAdjustments.java b/VirtualApp/lib/src/main/java/mirror/android/view/DisplayAdjustments.java
new file mode 100644
index 000000000..190010cac
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/mirror/android/view/DisplayAdjustments.java
@@ -0,0 +1,12 @@
+package mirror.android.view;
+
+
+import mirror.MethodReflectParams;
+import mirror.RefClass;
+import mirror.RefMethod;
+
+public class DisplayAdjustments {
+    public static Class<?> Class = RefClass.load(DisplayAdjustments.class, "android.view.DisplayAdjustments");
+    @MethodReflectParams({"android.content.res.CompatibilityInfo"})
+    public static RefMethod<Void> setCompatibilityInfo;
+}
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/java/mirror/android/view/IAutoFillManager.java b/VirtualApp/lib/src/main/java/mirror/android/view/IAutoFillManager.java
new file mode 100644
index 000000000..2669e4497
--- /dev/null
+++ b/VirtualApp/lib/src/main/java/mirror/android/view/IAutoFillManager.java
@@ -0,0 +1,24 @@
+package mirror.android.view;
+
+import android.os.IBinder;
+import android.os.IInterface;
+
+import mirror.MethodParams;
+import mirror.RefClass;
+import mirror.RefStaticMethod;
+
+/**
+ * @author 陈磊.
+ */
+
+public class IAutoFillManager {
+
+    public static Class<?> TYPE = RefClass.load(IAutoFillManager.class, "android.view.autofill.IAutoFillManager");
+
+    public static class Stub {
+        public static Class<?> TYPE = RefClass.load(Stub.class, "android.view.autofill.IAutoFillManager$Stub");
+        @MethodParams(IBinder.class)
+        public static RefStaticMethod<IInterface> asInterface;
+    }
+
+}
diff --git a/VirtualApp/lib/src/main/jni/Android.mk b/VirtualApp/lib/src/main/jni/Android.mk
index 7b720825b..ee971bd28 100644
--- a/VirtualApp/lib/src/main/jni/Android.mk
+++ b/VirtualApp/lib/src/main/jni/Android.mk
@@ -16,10 +16,13 @@ LOCAL_SRC_FILES := Jni/VAJni.cpp \
 				   Foundation/SymbolFinder.cpp \
 				   Foundation/Path.cpp \
 				   Foundation/SandboxFs.cpp \
+				   Substrate/hde64.c \
+                   Substrate/SubstrateDebug.cpp \
+                   Substrate/SubstrateHook.cpp \
+                   Substrate/SubstratePosixMemory.cpp \
 
 LOCAL_LDLIBS := -llog -latomic
-LOCAL_STATIC_LIBRARIES := hookzz fb
+LOCAL_STATIC_LIBRARIES := fb
 
 include $(BUILD_SHARED_LIBRARY)
-include $(MAIN_LOCAL_PATH)/HookZz/Android.mk
 include $(MAIN_LOCAL_PATH)/fb/Android.mk
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/Application.mk b/VirtualApp/lib/src/main/jni/Application.mk
index 5e56b2e91..7ec75bdf1 100644
--- a/VirtualApp/lib/src/main/jni/Application.mk
+++ b/VirtualApp/lib/src/main/jni/Application.mk
@@ -1,4 +1,4 @@
-APP_ABI := armeabi armeabi-v7a
+APP_ABI := armeabi-v7a x86
 APP_PLATFORM := android-14
 APP_STL := gnustl_static
 APP_OPTIM := release
diff --git a/VirtualApp/lib/src/main/jni/Foundation/IOUniformer.cpp b/VirtualApp/lib/src/main/jni/Foundation/IOUniformer.cpp
index 081091ffc..47f216a37 100644
--- a/VirtualApp/lib/src/main/jni/Foundation/IOUniformer.cpp
+++ b/VirtualApp/lib/src/main/jni/Foundation/IOUniformer.cpp
@@ -4,11 +4,7 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <fb/include/fb/ALog.h>
-
-extern "C" {
-#include <HookZz/include/hookzz.h>
-}
-
+#include <Substrate/CydiaSubstrate.h>
 
 #include "IOUniformer.h"
 #include "SandboxFs.h"
@@ -75,7 +71,7 @@ hook_function(void *handle, const char *symbol, void *new_func, void **old_func)
     if (addr == NULL) {
         return;
     }
-    ZzHookReplace(addr, new_func, old_func);
+    MSHookFunction(addr, new_func, old_func);
 }
 
 
@@ -86,7 +82,8 @@ void IOUniformer::redirect(const char *orig_path, const char *new_path) {
 }
 
 const char *IOUniformer::query(const char *orig_path) {
-    return reverse_relocate_path(orig_path);
+    int res;
+    return relocate_path(orig_path, &res);
 }
 
 void IOUniformer::whitelist(const char *_path) {
@@ -643,19 +640,19 @@ void hook_dlopen(int api_level) {
     if (api_level > 23) {
         if (findSymbol("__dl__Z9do_dlopenPKciPK17android_dlextinfoPv", "linker",
                        (unsigned long *) &symbol) == 0) {
-            ZzHookReplace(symbol, (void *) new_do_dlopen_V24,
+            MSHookFunction(symbol, (void *) new_do_dlopen_V24,
                           (void **) &orig_do_dlopen_V24);
         }
     } else if (api_level >= 19) {
         if (findSymbol("__dl__Z9do_dlopenPKciPK17android_dlextinfo", "linker",
                        (unsigned long *) &symbol) == 0) {
-            ZzHookReplace(symbol, (void *) new_do_dlopen_V19,
+            MSHookFunction(symbol, (void *) new_do_dlopen_V19,
                           (void **) &orig_do_dlopen_V19);
         }
     } else {
         if (findSymbol("__dl_dlopen", "linker",
                        (unsigned long *) &symbol) == 0) {
-            ZzHookReplace(symbol, (void *) new_dlopen, (void **) &orig_dlopen);
+            MSHookFunction(symbol, (void *) new_dlopen, (void **) &orig_dlopen);
         }
     }
 }
diff --git a/VirtualApp/lib/src/main/jni/Foundation/SandboxFs.cpp b/VirtualApp/lib/src/main/jni/Foundation/SandboxFs.cpp
index f9c3f5a5b..9f352d20c 100644
--- a/VirtualApp/lib/src/main/jni/Foundation/SandboxFs.cpp
+++ b/VirtualApp/lib/src/main/jni/Foundation/SandboxFs.cpp
@@ -80,7 +80,7 @@ int get_replace_item_count() {
     return replace_item_count;
 }
 
-inline bool match_path(bool is_folder, size_t size, char *item_path, char *path) {
+inline bool match_path(bool is_folder, size_t size, const char *item_path, const char *path) {
     if (is_folder) {
         if (strlen(path) < size) {
             // ignore the last '/'
@@ -91,18 +91,16 @@ inline bool match_path(bool is_folder, size_t size, char *item_path, char *path)
 }
 
 
-const char *relocate_path(const char *_path, int *result) {
-    if (_path == NULL) {
+const char *relocate_path(const char *path, int *result) {
+    if (path == NULL) {
         *result = NOT_MATCH;
         return NULL;
     }
-    char *path = canonicalize_filename(_path);
     for (int i = 0; i < keep_item_count; ++i) {
         PathItem &item = keep_items[i];
         if (strcmp(item.path, path) == 0) {
             *result = KEEP;
-            free(path);
-            return _path;
+            return path;
         }
     }
     for (int i = 0; i < forbidden_item_count; ++i) {
@@ -111,22 +109,27 @@ const char *relocate_path(const char *_path, int *result) {
             *result = FORBID;
             // Permission denied
             errno = 13;
-            free(path);
             return NULL;
         }
     }
     for (int i = 0; i < replace_item_count; ++i) {
         ReplaceItem &item = replace_items[i];
         if (match_path(item.is_folder, item.orig_size, item.orig_path, path)) {
-            std::string redirect_path(item.new_path);
-            redirect_path += path + item.orig_size;
             *result = MATCH;
-            free(path);
-            return strdup(redirect_path.c_str());
+            int len = strlen(path);
+            if (len < item.orig_size) {
+                //remove last /
+                std::string redirect_path(item.new_path, 0, item.new_size - 1);
+                return strdup(redirect_path.c_str());
+            } else {
+                std::string redirect_path(item.new_path);
+                redirect_path += path + item.orig_size;
+                return strdup(redirect_path.c_str());
+            }
         }
     }
     *result = NOT_MATCH;
-    return _path;
+    return path;
 }
 
 
@@ -159,10 +162,18 @@ const char *reverse_relocate_path(const char *_path) {
     for (int i = 0; i < replace_item_count; ++i) {
         ReplaceItem &item = replace_items[i];
         if (match_path(item.is_folder, item.new_size, item.new_path, path)) {
-            std::string reverse_path(item.orig_path);
-            reverse_path += path + item.new_size;
-            free(path);
-            return strdup(reverse_path.c_str());
+            int len = strlen(path);
+            if (len < item.new_size) {
+                //remove last /
+                std::string reverse_path(item.orig_path, 0, item.orig_size - 1);
+                free(path);
+                return strdup(reverse_path.c_str());
+            } else {
+                std::string reverse_path(item.orig_path);
+                reverse_path += path + item.new_size;
+                free(path);
+                return strdup(reverse_path.c_str());
+            }
         }
     }
     return _path;
diff --git a/VirtualApp/lib/src/main/jni/HookZz/.gitignore b/VirtualApp/lib/src/main/jni/HookZz/.gitignore
index 5713f6d07..93f0552df 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/.gitignore
+++ b/VirtualApp/lib/src/main/jni/HookZz/.gitignore
@@ -5,7 +5,6 @@ darwin.ios.release.mk
 .vscode/
 cmake-build-debug/
 MachoParser.xcworkspace/
-CMakeLists.txt
 
 # Prerequisites
 *.d
diff --git a/VirtualApp/lib/src/main/jni/HookZz/Android.mk b/VirtualApp/lib/src/main/jni/HookZz/Android.mk
index 7c0ff5671..e56863372 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/Android.mk
+++ b/VirtualApp/lib/src/main/jni/HookZz/Android.mk
@@ -22,6 +22,9 @@ ifeq ($(TARGET_ARCH), arm)
 else ifeq ($(TARGET_ARCH), arm64)
 	ZZ_SRC += $(wildcard $(LOCAL_PATH)/src/platforms/arch-arm64/*.c) \
 			$(wildcard $(LOCAL_PATH)/src/platforms/backend-arm64/*.c)
+else ifeq ($(TARGET_ARCH), x86)
+	ZZ_SRC += $(wildcard $(LOCAL_PATH)/src/platforms/arch-x86/*.c) \
+			$(wildcard $(LOCAL_PATH)/src/platforms/backend-x86/*.c)
 endif
 
 LOCAL_MODULE := hookzz
diff --git a/VirtualApp/lib/src/main/jni/HookZz/Makefile b/VirtualApp/lib/src/main/jni/HookZz/Makefile
index 5ad7f240e..a8cd29d72 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/Makefile
+++ b/VirtualApp/lib/src/main/jni/HookZz/Makefile
@@ -8,7 +8,7 @@ HOOKZZ_DIR := $(abspath .)
 LOCAL_PATH := $(abspath .)
 OUTPUT_DIR := $(abspath build)
 
-CFLAGS ?= -O0 -g -std=c99
+CFLAGS ?= -O0 -g
 CXXFLAGS = $(CFLAGS) -stdlib=libc++ -std=c++11 -gmodules
 LDFLAGS ?=
 LIBS_CFLAGS ?= -fPIC
@@ -57,6 +57,32 @@ ifeq ($(BACKEND), ios)
 
 	CFLAGS += -arch $(ZZ_ARCH)
 	
+	ZZ_GCC_SOURCE := $(ZZ_GCC_BIN) -isysroot $(ZZ_SDK_ROOT)
+	ZZ_GXX_SOURCE := $(ZZ_GXX_BIN) -isysroot $(ZZ_SDK_ROOT)
+	ZZ_GCC_TEST := $(ZZ_GCC_BIN) -isysroot $(ZZ_SDK_ROOT)
+	ZZ_GXX_TEST := $(ZZ_GXX_BIN) -isysroot $(ZZ_SDK_ROOT)
+else ifeq ($(BACKEND), macos)
+	ifeq ($(ARCH), x86)
+		ZZ_ARCH := i386
+	else ifeq ($(ARCH), x86_64)
+		ZZ_ARCH := x86_64
+	endif
+
+	ZZ_BACKEND := macos
+	ZZ_GXX_BIN := $(shell xcrun --sdk macosx --find clang++)
+	ZZ_GCC_BIN := $(shell xcrun --sdk macosx --find clang)
+	ZZ_SDK_ROOT := $(shell xcrun --sdk macosx --show-sdk-path)
+	ZZ_AR_BIN := $(shell which ar)
+	ZZ_RANLIB_BIN := $(shell which ranlib)
+
+	ZZ_DEPS_SRCS += $(wildcard $(ZZ_DEPS_PATH)/darwin/*.c)
+	ZZ_SRCS += $(wildcard $(ZZ_SRCS_PATH)/platforms/backend-darwin/*.c)
+	
+	ZZ_CFLAGS := -g -fPIC -shared -dynamiclib
+	ZZ_DLL := lib$(HOOKZZ_NAME).dylib
+
+	CFLAGS += -arch $(ZZ_ARCH)
+	
 	ZZ_GCC_SOURCE := $(ZZ_GCC_BIN) -isysroot $(ZZ_SDK_ROOT)
 	ZZ_GXX_SOURCE := $(ZZ_GXX_BIN) -isysroot $(ZZ_SDK_ROOT)
 	ZZ_GCC_TEST := $(ZZ_GCC_BIN) -isysroot $(ZZ_SDK_ROOT)
@@ -68,19 +94,27 @@ else ifeq ($(BACKEND), android)
 		ZZ_ARCH := armv7
 		ZZ_API_LEVEL := android-19
 		ZZ_CROSS_PREFIX := arm-linux-androideabi-
+		ZZ_BIN_CROSS_PREFIX := arm-linux-androideabi-
 	else ifeq ($(ARCH), arm64)
 		ZZ_ARCH := arm64
 		ZZ_API_LEVEL := android-21
 		ZZ_CROSS_PREFIX := aarch64-linux-android-
+		ZZ_BIN_CROSS_PREFIX := aarch64-linux-android-
+	else ifeq ($(ARCH), x86)
+		ZZ_ARCH := x86
+		ZZ_API_LEVEL := android-21
+		ZZ_CROSS_PREFIX := x86-
+		ZZ_BIN_CROSS_PREFIX := i686-linux-android-
+
 	endif
 
 	HOST_DIR := $(shell echo $(HOST) | tr A-Z a-z)-$(HOST_ARCH)
 	ZZ_NDK_HOME := $(shell dirname `which ndk-build`)
 	ZZ_SDK_ROOT := $(ZZ_NDK_HOME)/platforms/$(ZZ_API_LEVEL)/arch-$(ARCH)
-	ZZ_GCC_BIN := $(ZZ_NDK_HOME)/toolchains/$(ZZ_CROSS_PREFIX)4.9/prebuilt/$(HOST_DIR)/bin/$(ZZ_CROSS_PREFIX)gcc
-	ZZ_GXX_BIN := $(ZZ_NDK_HOME)/toolchains/$(ZZ_CROSS_PREFIX)4.9/prebuilt/$(HOST_DIR)/bin/$(ZZ_CROSS_PREFIX)g++
-	ZZ_AR_BIN := $(ZZ_NDK_HOME)/toolchains/$(ZZ_CROSS_PREFIX)4.9/prebuilt/$(HOST_DIR)/bin/$(ZZ_CROSS_PREFIX)ar
-	ZZ_RANLIB_BIN := $(ZZ_NDK_HOME)/toolchains/$(ZZ_CROSS_PREFIX)4.9/prebuilt/$(HOST_DIR)/bin/$(ZZ_CROSS_PREFIX)ranlib
+	ZZ_GCC_BIN := $(ZZ_NDK_HOME)/toolchains/$(ZZ_CROSS_PREFIX)4.9/prebuilt/$(HOST_DIR)/bin/$(ZZ_BIN_CROSS_PREFIX)gcc
+	ZZ_GXX_BIN := $(ZZ_NDK_HOME)/toolchains/$(ZZ_CROSS_PREFIX)4.9/prebuilt/$(HOST_DIR)/bin/$(ZZ_BIN_CROSS_PREFIX)g++
+	ZZ_AR_BIN := $(ZZ_NDK_HOME)/toolchains/$(ZZ_CROSS_PREFIX)4.9/prebuilt/$(HOST_DIR)/bin/$(ZZ_BIN_CROSS_PREFIX)ar
+	ZZ_RANLIB_BIN := $(ZZ_NDK_HOME)/toolchains/$(ZZ_CROSS_PREFIX)4.9/prebuilt/$(HOST_DIR)/bin/$(ZZ_BIN_CROSS_PREFIX)ranlib
 
 	ZZ_DEPS_SRCS += $(wildcard $(ZZ_DEPS_PATH)/linux/*.c)
 	ZZ_SRCS += $(wildcard $(ZZ_SRCS_PATH)/platforms/backend-linux/*.c)
diff --git a/VirtualApp/lib/src/main/jni/HookZz/README.md b/VirtualApp/lib/src/main/jni/HookZz/README.md
index d051bc50e..c14c9889f 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/README.md
+++ b/VirtualApp/lib/src/main/jni/HookZz/README.md
@@ -6,9 +6,11 @@ ref to: [frida-gum](https://github.com/frida/frida-gum) and [minhook](https://gi
 
 **special thanks to [frida-gum](https://github.com/frida/frida-gum) perfect code and modular architecture, frida is aircraft carrier, HookZz is boat, but still with some tricks**
 
+**thanks for @lauos with contributing android code**
+
 # Features
 
-- **inlinehook without Jailbreak [new-90%]**
+- **solidify inlinehook without Jailbreak [new-90%]**
 
 - **GOT hook with HookZz(i.e. change fishhook to inlinehook), better for APM [new-0%]**
 
@@ -22,7 +24,7 @@ ref to: [frida-gum](https://github.com/frida/frida-gum) and [minhook](https://gi
 
 - hook **address(a piece of code)** with `pre_call` and `half_call`
 
-- (almost)only **one instruction** to hook(i.e. hook **short funciton, even only one instruction**) [arm64]
+- (almost)only **one instruction** to hook(i.e. hook **short funciton, even only one instruction**) [arm/thumb/arm64]
 
 - runtime code patch, without codesign limit
 
@@ -163,7 +165,7 @@ and check `build/android-armv7/*`
 
 #### `test_hook_printf.c` output for arm64-ios
 
-test hook `printf` with `try_near_jump` option and `replace_call`, `pre_call`, `post_call`
+test hook `printf` with `try_near_jump` option , and `ZzEnableDebugMode()` with `replace_call`, `pre_call`, `post_call`.
 
 ```
 ZzThunkerBuildThunk:
diff --git a/VirtualApp/lib/src/main/jni/HookZz/include/hookzz.h b/VirtualApp/lib/src/main/jni/HookZz/include/hookzz.h
index 9d118699b..34eb953d5 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/include/hookzz.h
+++ b/VirtualApp/lib/src/main/jni/HookZz/include/hookzz.h
@@ -100,7 +100,12 @@ typedef struct _RegState {
 
     zuint32 lr;
 } RegState;
+#elif defined(__i386__)
+typedef struct _RegState {
+} RegState;
 #elif defined(__x86_64__)
+typedef struct _RegState {
+} RegState;
 #endif
 #endif
 
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/allocator.c b/VirtualApp/lib/src/main/jni/HookZz/src/allocator.c
index deee6fcf2..2dbbe6ed4 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/allocator.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/allocator.c
@@ -144,8 +144,9 @@ ZZSTATUS ZzAddMemoryPage(ZzAllocator *allocator, ZzMemoryPage *page) {
 ZzCodeSlice *ZzNewCodeSlice(ZzAllocator *allocator, zsize code_slice_size) {
     ZzCodeSlice *code_slice = NULL;
     ZzMemoryPage *page = NULL;
+    int i;
 
-    for (int i = 0; i < allocator->size; i++) {
+    for (i = 0; i < allocator->size; i++) {
         page = allocator->memory_pages[i];
         // 1. page is initialized
         // 2. can't be codecave
@@ -195,8 +196,8 @@ ZzCodeSlice *ZzNewNearCodeSlice(ZzAllocator *allocator, zaddr address, zsize red
                                 zsize code_slice_size) {
     ZzCodeSlice *code_slice = NULL;
     ZzMemoryPage *page = NULL;
-
-    for (int i = 0; i < allocator->size; i++) {
+    int i;
+    for (i = 0; i < allocator->size; i++) {
         page = allocator->memory_pages[i];
         // 1. page is initialized
         // 2. can't be codecave
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/interceptor.c b/VirtualApp/lib/src/main/jni/HookZz/src/interceptor.c
index 1e1ade135..4e513f0c8 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/interceptor.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/interceptor.c
@@ -15,25 +15,29 @@
  */
 
 #include <stdlib.h>
+#include <string.h>
 
 #include "interceptor.h"
 #include "trampoline.h"
+#include "zzinfo.h"
 
 #define ZZHOOKENTRIES_DEFAULT 100
 ZzInterceptor *g_interceptor = NULL;
 
-zbool g_DebugModeFlag = FALSE;
-
 ZZSTATUS ZzInitializeInterceptor(void) {
     ZzInterceptor *interceptor = g_interceptor;
     ZzHookFunctionEntrySet *hook_function_entry_set;
 
     if (NULL == interceptor) {
         interceptor = (ZzInterceptor *)malloc(sizeof(ZzInterceptor));
+        memset(interceptor, 0, sizeof(ZzInterceptor));
+
         hook_function_entry_set = &(interceptor->hook_function_entry_set);
         hook_function_entry_set->capacity = ZZHOOKENTRIES_DEFAULT;
         hook_function_entry_set->entries =
             (ZzHookFunctionEntry **)malloc(sizeof(ZzHookFunctionEntry *) * hook_function_entry_set->capacity);
+        memset(hook_function_entry_set->entries, 0, sizeof(ZzHookFunctionEntry *) * hook_function_entry_set->capacity);
+
         if (!hook_function_entry_set->entries) {
             return ZZ_FAILED;
         }
@@ -52,10 +56,6 @@ ZZSTATUS ZzInitializeInterceptor(void) {
     return ZZ_ALREADY_INIT;
 }
 
-void ZzEnableDebugMode(void) { g_DebugModeFlag = TRUE; }
-
-zbool ZzIsEnableDebugMode(void) { return g_DebugModeFlag; }
-
 ZzHookFunctionEntry *ZzFindHookFunctionEntry(zpointer target_ptr) {
     ZzInterceptor *interceptor = g_interceptor;
     if (!interceptor)
@@ -63,7 +63,8 @@ ZzHookFunctionEntry *ZzFindHookFunctionEntry(zpointer target_ptr) {
 
     ZzHookFunctionEntrySet *hook_function_entry_set = &(interceptor->hook_function_entry_set);
 
-    for (int i = 0; i < hook_function_entry_set->size; ++i) {
+    int i;
+    for (i = 0; i < hook_function_entry_set->size; ++i) {
         if ((hook_function_entry_set->entries)[i] && target_ptr == (hook_function_entry_set->entries)[i]->target_ptr) {
             return (hook_function_entry_set->entries)[i];
         }
@@ -97,27 +98,24 @@ void ZzInitializeHookFunctionEntry(ZzHookFunctionEntry *entry, int hook_type, zp
     ZzInterceptor *interceptor = g_interceptor;
     ZzHookFunctionEntrySet *hook_function_entry_set = &(interceptor->hook_function_entry_set);
 
+    memset(entry, 0, sizeof(ZzHookFunctionEntry));
+
     entry->hook_type = hook_type;
     entry->id = hook_function_entry_set->size;
     entry->isEnabled = 0;
     entry->try_near_jump = try_near_jump;
     entry->interceptor = interceptor;
-
     entry->target_ptr = target_ptr;
     entry->target_end_ptr = target_end_ptr;
-
     entry->replace_call = replace_call;
     entry->pre_call = (zpointer)pre_call;
     entry->half_call = (zpointer)half_call;
     entry->post_call = (zpointer)post_call;
-
     entry->on_enter_trampoline = NULL;
     entry->on_invoke_trampoline = NULL;
     entry->on_half_trampoline = NULL;
     entry->on_leave_trampoline = NULL;
-
     entry->origin_prologue.address = target_ptr;
-
     entry->thread_local_key = ZzThreadNewThreadLocalKeyPtr();
 
     /* key function */
@@ -127,6 +125,10 @@ void ZzInitializeHookFunctionEntry(ZzHookFunctionEntry *entry, int hook_type, zp
 
 ZZSTATUS ZzBuildHook(zpointer target_ptr, zpointer replace_call_ptr, zpointer *origin_ptr, PRECALL pre_call_ptr,
                      POSTCALL post_call_ptr, zbool try_near_jump) {
+#if defined(__i386__) || defined(__x86_64__)
+    ZzInfoLog("%s", "x86 & x86_64 arch not support");
+    return ZZ_FAILED;
+#endif
 
     ZZSTATUS status = ZZ_DONE_HOOK;
     ZzInterceptor *interceptor = g_interceptor;
@@ -153,6 +155,8 @@ ZZSTATUS ZzBuildHook(zpointer target_ptr, zpointer replace_call_ptr, zpointer *o
         }
 
         entry = (ZzHookFunctionEntry *)malloc(sizeof(ZzHookFunctionEntry));
+        memset(entry, 0, sizeof(ZzHookFunctionEntry));
+
         ZzInitializeHookFunctionEntry(entry, HOOK_FUNCTION_TYPE, target_ptr, 0, replace_call_ptr, pre_call_ptr, NULL,
                                       post_call_ptr, try_near_jump);
 
@@ -165,7 +169,10 @@ ZZSTATUS ZzBuildHook(zpointer target_ptr, zpointer replace_call_ptr, zpointer *o
 
 ZZSTATUS ZzBuildHookAddress(zpointer target_start_ptr, zpointer target_end_ptr, PRECALL pre_call_ptr,
                             HALFCALL half_call_ptr, zbool try_near_jump) {
-
+#if defined(__i386__) || defined(__x86_64__)
+    ZzInfoLog("%s", "x86 & x86_64 arch not support");
+    return ZZ_FAILED;
+#endif
     ZZSTATUS status = ZZ_DONE_HOOK;
     ZzInterceptor *interceptor = g_interceptor;
     ZzHookFunctionEntrySet *hook_function_entry_set = NULL;
@@ -191,6 +198,8 @@ ZZSTATUS ZzBuildHookAddress(zpointer target_start_ptr, zpointer target_end_ptr,
         }
 
         entry = (ZzHookFunctionEntry *)malloc(sizeof(ZzHookFunctionEntry));
+        memset(entry, 0, sizeof(ZzHookFunctionEntry));
+
         ZzInitializeHookFunctionEntry(entry, HOOK_ADDRESS_TYPE, target_start_ptr, target_end_ptr, NULL, pre_call_ptr,
                                       half_call_ptr, NULL, try_near_jump);
 
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/interceptor.h b/VirtualApp/lib/src/main/jni/HookZz/src/interceptor.h
index 1b01931c5..92c26fb57 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/interceptor.h
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/interceptor.h
@@ -50,7 +50,6 @@ struct _ZzInterceptor;
 struct _ZzHookFunctionEntryBackend;
 typedef struct _ZzHookFunctionEntry {
     int hook_type;
-    zbool isNearJump;
     unsigned long id;
     zbool isEnabled;
     zbool try_near_jump;
@@ -93,6 +92,4 @@ typedef struct _ZzInterceptor {
     ZzAllocator *allocator;
 } ZzInterceptor;
 
-zbool ZzIsEnableDebugMode(void);
-
 #endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/instructions.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/instructions.c
index a3c1895ec..00ec11d1c 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/instructions.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/instructions.c
@@ -1,13 +1,13 @@
 #include "instructions.h"
-
 #include <string.h>
 
-zuint32 get_insn_sub(zuint32 insn, int start, int length) { return (insn >> start) & (1 << (length - 1)); }
+zuint32 get_insn_sub(zuint32 insn, int start, int length) { return (insn >> start) & ((1 << length) - 1); }
 
 zbool insn_equal(zuint32 insn, char *opstr) {
     zuint32 mask = 0, value = 0;
     zsize length = strlen(opstr);
-    for (int i = length - 1, j = 0; i >= 0 && j < length; i--, j++) {
+    int i, j;
+    for (i = length - 1, j = 0; i >= 0 && j < length; i--, j++) {
         if (opstr[i] == 'x') {
             mask = mask | (0 << j);
         } else if (opstr[i] == '0') {
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/instructions.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/instructions.h
index 835aaa9a5..f8b41e269 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/instructions.h
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/instructions.h
@@ -39,6 +39,12 @@ typedef struct _Instruction {
     zuint16 insn2;
 } ZzInstruction;
 
+typedef struct _ZzRelocateInstruction {
+    const ZzInstruction *insn_ctx;
+    zaddr relocated_offset;
+    zsize relocated_length;
+} ZzRelocateInstruction;
+
 zuint32 get_insn_sub(zuint32 insn, int start, int length);
 zbool insn_equal(zuint32 insn, char *opstr);
 
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-arm.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-arm.c
index 2fedae632..1274db44f 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-arm.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-arm.c
@@ -16,41 +16,10 @@
 
 #include "reader-arm.h"
 
-// static csh handle;
-
-// void zz_arm_reader_capstone_init(void) {
-//     cs_err err = 0;
-
-//     err = cs_open(CS_ARCH_ARM, CS_MODE_ARM, &handle);
-//     if (err) {
-//         Xerror("Failed on cs_open() with error returned: %u\n", err);
-//         exit(-1);
-//     }
-
-//     cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON);
-// }
-
-// cs_insn *zz_arm_reader_disassemble_at(zpointer address) {
-//     if (!handle)
-//         zz_arm_reader_capstone_init();
-//     cs_insn *insn;
-//     size_t count;
-//     count = cs_disasm(handle, address, 16, (unsigned long)address, 0, &insn);
-//     if (!insn) {
-// #if defined(DEBUG_MODE)
-//         debug_break();
-// #endif
-//         Xerror("zz_arm_reader_disassemble_at error at %p", (zpointer)address);
-//     }
-//     return insn;
-// }
-
 zpointer zz_arm_reader_read_one_instruction(ZzInstruction *insn_ctx, zpointer address) {
-    // ZzInstruction *insn = (ZzInstruction *)malloc(sizeof(ZzInstruction));
-
     insn_ctx->type = ARM_INSN;
     insn_ctx->address = (zaddr)address;
-    insn_ctx->pc = (zaddr)address;
+    insn_ctx->pc = (zaddr)address + 8;
     insn_ctx->insn = *(zuint32 *)address;
     insn_ctx->size = 4;
     return (zpointer)insn_ctx->pc;
@@ -60,30 +29,30 @@ zpointer zz_arm_reader_read_one_instruction(ZzInstruction *insn_ctx, zpointer ad
 // A5 ARM Instruction Set Encoding
 // A5.3 Load/store word and unsigned byte
 ARMInsnType GetARMInsnType(zuint32 insn) {
-    zuint32 op, op1;
-    op1 = get_insn_sub(insn, 20, 5);
 
-    if (insn_equal(insn, "xxxx0101x0011111xxxxxxxxxxxxxxxx")) {
-        return ARM_INS_LDR_A1;
+    if (insn_equal(insn, "xxxx0000100xxxxxxxxxxxxxxxx0xxxx") && (get_insn_sub(insn, 28, 4) != 0xF)) {
+        return ARM_INS_ADD_register_A1;
     }
 
-    if (insn_equal(insn, "xxxx001010001111xxxxxxxxxxxxxxxx")) {
-        return ARM_INS_ADR_A1;
+    if (insn_equal(insn, "xxxx0101x0011111xxxxxxxxxxxxxxxx") && (get_insn_sub(insn, 28, 4) != 0xF)) {
+        return ARM_INS_LDR_literal_A1;
     }
 
-    if (insn_equal(insn, "xxxx001001001111xxxxxxxxxxxxxxxx")) {
+    if (insn_equal(insn, "xxxx001010001111xxxxxxxxxxxxxxxx") && (get_insn_sub(insn, 28, 4) != 0xF)) {
+        return ARM_INS_ADR_A1;
+    }
+    if (insn_equal(insn, "xxxx001001001111xxxxxxxxxxxxxxxx") && (get_insn_sub(insn, 28, 4) != 0xF)) {
         return ARM_INS_ADR_A2;
     }
-
-    if (insn_equal(insn, "xxxx1010xxxxxxxxxxxxxxxxxxxxxxxx")) {
+    if (insn_equal(insn, "xxxx1010xxxxxxxxxxxxxxxxxxxxxxxx") && (get_insn_sub(insn, 28, 4) != 0xF)) {
         return ARM_INS_B_A1;
     }
-
-    if (insn_equal(insn, "xxxx1011xxxxxxxxxxxxxxxxxxxxxxxx")) {
-        return ARM_INS_BLBLX_A1;
+    if (insn_equal(insn, "xxxx1011xxxxxxxxxxxxxxxxxxxxxxxx") && (get_insn_sub(insn, 28, 4) != 0xF)) {
+        return ARM_INS_BLBLX_immediate_A1;
     }
     if (insn_equal(insn, "1111101xxxxxxxxxxxxxxxxxxxxxxxxx")) {
-        return ARM_INS_BLBLX_A2;
+        return ARM_INS_BLBLX_immediate_A2;
     }
+
     return ARM_UNDEF;
 }
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-arm.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-arm.h
index f3915f077..7936dc901 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-arm.h
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-arm.h
@@ -27,12 +27,13 @@
 #include "zzdeps/zz.h"
 
 typedef enum _ARMInsnType {
-    ARM_INS_LDR_A1,
+    ARM_INS_ADD_register_A1,
+    ARM_INS_LDR_literal_A1,
     ARM_INS_ADR_A1,
     ARM_INS_ADR_A2,
     ARM_INS_B_A1,
-    ARM_INS_BLBLX_A1,
-    ARM_INS_BLBLX_A2,
+    ARM_INS_BLBLX_immediate_A1,
+    ARM_INS_BLBLX_immediate_A2,
     ARM_UNDEF
 } ARMInsnType;
 
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-thumb.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-thumb.c
index c46b65de7..652433b79 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-thumb.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-thumb.c
@@ -16,44 +16,11 @@
 
 #include "reader-thumb.h"
 
-// static csh handle;
-
-// void zz_thumb_reader_capstone_init(void) {
-//     cs_err err = 0;
-
-//     err = cs_open(CS_ARCH_ARM, CS_MODE_THUMB, &handle);
-//     if (err) {
-//         Xerror("Failed on cs_open() with error returned: %u\n", err);
-//         exit(-1);
-//     }
-
-//     cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON);
-// }
-
-// cs_insn *zz_thumb_reader_disassemble_at(zpointer address) {
-//     if (!handle)
-//         zz_thumb_reader_capstone_init();
-//     cs_insn *insn;
-//     size_t count;
-//     count = cs_disasm(handle, address, 16, (unsigned long)address, 0, &insn);
-//     if (!insn) {
-// #if defined(DEBUG_MODE)
-//         debug_break();
-// #endif
-//         Xerror("zz_thumb_reader_disassemble_at error at %p", (zpointer)address);
-//     }
-//     return insn;
-// }
-
 zbool insn_is_thumb2(zuint32 insn) {
     // PAGE: A6-221
     // PAGE: A6-230
-    if (get_insn_sub(insn & 0x0000FFFF, 11, 2) == 0) {
-        return FALSE;
-    }
-    return TRUE;
 
-    if (insn_equal(insn & 0x0000FFFF, "11101xxxxxxxxxxx") || insn_equal(insn & 0x0000FFFF, "11110xxxxxxxxxxx") ||
+    if (insn_equal(insn & 0x0000FFFF, "11101xxxxxxxxxxx") || insn_equal(insn & 0x0000FFFF, "11111xxxxxxxxxxx") ||
         insn_equal(insn & 0x0000FFFF, "11110xxxxxxxxxxx")) {
         return TRUE;
     } else {
@@ -63,7 +30,8 @@ zbool insn_is_thumb2(zuint32 insn) {
 
 zpointer zz_thumb_reader_read_one_instruction(ZzInstruction *insn_ctx, zpointer address) {
     // ZzInstruction *insn_ctx = (ZzInstruction *)malloc(sizeof(ZzInstruction));
-    insn_ctx->pc = (zaddr)address;
+    insn_ctx->pc = (zaddr)address + 4;
+    insn_ctx->address = (zaddr)address;
     insn_ctx->insn = *(zuint32 *)address;
 
     // PAGE: A6-221
@@ -84,52 +52,58 @@ zpointer zz_thumb_reader_read_one_instruction(ZzInstruction *insn_ctx, zpointer
 // ARM Manual
 // A5 ARM Instruction Set Encoding
 // A5.3 Load/store word and unsigned byte
-THUMBInsnType GetTHUMBInsnType(zuint32 insn) {
-    // zuint32 insn = insn_ctx->insn;
-    zuint32 op, op1;
+THUMBInsnType GetTHUMBInsnType(zuint16 insn1, zuint16 insn2) {
+
+    if (!insn_is_thumb2(insn1) && insn_equal(insn1, "1011x0x1xxxxxxxx")) {
+        return THUMB_INS_CBNZ_CBZ;
+    }
+
+    if (!insn_is_thumb2(insn1) && insn_equal(insn1, "01000100xxxxxxxx")) {
+        return THUMB_INS_ADD_register_T2;
+    }
 
-    if (!insn_is_thumb2(insn) && insn_equal(insn, "01001xxxxxxxxxxx")) {
-        return THUMB_INS_LDR_T1;
+    if (!insn_is_thumb2(insn1) && insn_equal(insn1, "01001xxxxxxxxxxx")) {
+        return THUMB_INS_LDR_literal_T1;
     }
 
-    if (insn_is_thumb2(insn) && insn_equal(insn, "11111000x1011111xxxxxxxxxxxxxxxx")) {
-        return THUMB_INS_LDR_T2;
+    if (insn_is_thumb2(insn1) && insn_equal(insn1, "11111000x1011111") && insn_equal(insn2, "xxxxxxxxxxxxxxxx")) {
+        return THUMB_INS_LDR_literal_T2;
     }
 
-    if (!insn_is_thumb2(insn) && insn_equal(insn, "10100xxxxxxxxxxx")) {
+    if (!insn_is_thumb2(insn1) && insn_equal(insn1, "10100xxxxxxxxxxx")) {
         return THUMB_INS_ADR_T1;
     }
 
-    if (insn_is_thumb2(insn) && insn_equal(insn, "11110x10101011110xxxxxxxxxxxxxxx")) {
+    if (insn_is_thumb2(insn1) && insn_equal(insn1, "11110x1010101111") && insn_equal(insn2, "0xxxxxxxxxxxxxxx")) {
         return THUMB_INS_ADR_T2;
     }
 
-    if (insn_is_thumb2(insn) && insn_equal(insn, "11110x10000011110xxxxxxxxxxxxxxx")) {
+    if (insn_is_thumb2(insn1) && insn_equal(insn1, "11110x1000001111") && insn_equal(insn2, "0xxxxxxxxxxxxxxx")) {
         return THUMB_INS_ADR_T3;
     }
 
-    if (!insn_is_thumb2(insn) && insn_equal(insn, "1101xxxxxxxxxxxx")) {
+    if (!insn_is_thumb2(insn1) && insn_equal(insn1, "1101xxxxxxxxxxxx")) {
         return THUMB_INS_B_T1;
     }
 
-    if (!insn_is_thumb2(insn) && insn_equal(insn, "11100xxxxxxxxxxx")) {
+    if (!insn_is_thumb2(insn1) && insn_equal(insn1, "11100xxxxxxxxxxx")) {
         return THUMB_INS_B_T2;
     }
 
-    if (insn_is_thumb2(insn) && insn_equal(insn, "11110xxxxxxxxxxx10x0xxxxxxxxxxxx")) {
+    if (insn_is_thumb2(insn1) && insn_equal(insn1, "11110xxxxxxxxxxx") && insn_equal(insn2, "10x0xxxxxxxxxxxx")) {
         return THUMB_INS_B_T3;
     }
 
-    if (insn_is_thumb2(insn) && insn_equal(insn, "11110xxxxxxxxxxx10x0xxxxxxxxxxxx")) {
+    if (insn_is_thumb2(insn1) && insn_equal(insn1, "11110xxxxxxxxxxx") && insn_equal(insn2, "10x1xxxxxxxxxxxx")) {
         return THUMB_INS_B_T4;
     }
 
-    if (insn_is_thumb2(insn) && insn_equal(insn, "11110xxxxxxxxxxx11x1xxxxxxxxxxxx")) {
-        return THUMB_INS_BLBLX_T1;
+    if (insn_is_thumb2(insn1) && insn_equal(insn1, "11110xxxxxxxxxxx") && insn_equal(insn2, "11x1xxxxxxxxxxxx")) {
+        return THUMB_INS_BLBLX_immediate_T1;
     }
 
-    if (insn_is_thumb2(insn) && insn_equal(insn, "11110xxxxxxxxxxx11x0xxxxxxxxxxxx")) {
-        return THUMB_INS_BLBLX_T2;
+    if (insn_is_thumb2(insn1) && insn_equal(insn1, "11110xxxxxxxxxxx") && insn_equal(insn2, "11x0xxxxxxxxxxxx")) {
+        return THUMB_INS_BLBLX_immediate_T2;
     }
 
     return THUMB_UNDEF;
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-thumb.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-thumb.h
index 28090719f..3d1dee803 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-thumb.h
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-thumb.h
@@ -27,8 +27,10 @@
 #include "zzdeps/zz.h"
 
 typedef enum _THUMBInsnType {
-    THUMB_INS_LDR_T1,
-    THUMB_INS_LDR_T2,
+    THUMB_INS_CBNZ_CBZ,
+    THUMB_INS_ADD_register_T2,
+    THUMB_INS_LDR_literal_T1,
+    THUMB_INS_LDR_literal_T2,
     THUMB_INS_ADR_T1,
     THUMB_INS_ADR_T2,
     THUMB_INS_ADR_T3,
@@ -36,12 +38,12 @@ typedef enum _THUMBInsnType {
     THUMB_INS_B_T2,
     THUMB_INS_B_T3,
     THUMB_INS_B_T4,
-    THUMB_INS_BLBLX_T1,
-    THUMB_INS_BLBLX_T2,
+    THUMB_INS_BLBLX_immediate_T1,
+    THUMB_INS_BLBLX_immediate_T2,
     THUMB_UNDEF
 } THUMBInsnType;
 
-THUMBInsnType GetTHUMBInsnType(zuint32 insn);
+THUMBInsnType GetTHUMBInsnType(zuint16 insn1, zuint16 insn2);
 zpointer zz_thumb_reader_read_one_instruction(ZzInstruction *insn_ctx, zpointer address);
 
 #endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-arm.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-arm.c
index c832492fa..ca7c55838 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-arm.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-arm.c
@@ -24,31 +24,43 @@
 void zz_arm_relocator_init(ZzArmRelocator *relocator, zpointer input_code, ZzArmWriter *output) {
     relocator->inpos = 0;
     relocator->outpos = 0;
-
     relocator->input_start = input_code;
     relocator->input_cur = input_code;
     relocator->input_pc = (zaddr)input_code;
+    relocator->output = output;
+    relocator->relocate_literal_insns_size = 0;
+    relocator->try_relocated_length = 0;
 
     relocator->input_insns = (ZzInstruction *)malloc(MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzInstruction));
     memset(relocator->input_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzInstruction));
-
-    relocator->output = output;
+    relocator->output_insns =
+        (ZzRelocateInstruction *)malloc(MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzRelocateInstruction));
+    memset(relocator->output_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzRelocateInstruction));
+    relocator->relocate_literal_insns =
+        (ZzLiteralInstruction **)malloc(MAX_LITERAL_INSN_SIZE * sizeof(ZzLiteralInstruction *));
+    memset(relocator->relocate_literal_insns, 0, MAX_LITERAL_INSN_SIZE * sizeof(ZzLiteralInstruction *));
 }
 
 void zz_arm_relocator_reset(ZzArmRelocator *self, zpointer input_code, ZzArmWriter *output) {
     self->input_cur = input_code;
     self->input_start = input_code;
     self->input_pc = (zaddr)input_code;
-
     self->inpos = 0;
     self->outpos = 0;
-
     self->output = output;
+    self->relocate_literal_insns_size = 0;
+    self->try_relocated_length = 0;
+
+    memset(self->input_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzInstruction));
+    memset(self->output_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzRelocateInstruction));
+    memset(self->relocate_literal_insns, 0, MAX_LITERAL_INSN_SIZE * sizeof(ZzLiteralInstruction *));
 }
 
 zsize zz_arm_relocator_read_one(ZzArmRelocator *self, ZzInstruction *instruction) {
     ZzInstruction *insn_ctx = &self->input_insns[self->inpos];
+    ZzRelocateInstruction *re_insn_ctx = &self->output_insns[self->inpos];
 
+    re_insn_ctx->insn_ctx = insn_ctx;
     zz_arm_reader_read_one_instruction(insn_ctx, self->input_cur);
 
     // switch (1) {}
@@ -64,31 +76,101 @@ zsize zz_arm_relocator_read_one(ZzArmRelocator *self, ZzInstruction *instruction
     return self->input_cur - self->input_start;
 }
 void zz_arm_relocator_try_relocate(zpointer address, zuint min_bytes, zuint *max_bytes) {
-    *max_bytes = 16;
+    int tmp_size = 0;
+    zpointer target_addr;
+    ZzInstruction insn_ctx;
+    zbool early_end = FALSE;
+    target_addr = (zpointer)address;
+
+    do {
+        zz_arm_reader_read_one_instruction(&insn_ctx, target_addr);
+        switch (GetARMInsnType(insn_ctx.insn)) {
+        case ARM_INS_B_A1: {
+            zuint32 cond = get_insn_sub(insn_ctx.insn, 28, 4);
+            if (cond == 0xE)
+                early_end = TRUE;
+        }; break;
+        default:;
+        }
+        tmp_size += insn_ctx.size;
+        target_addr = target_addr + insn_ctx.size;
+    } while (tmp_size < min_bytes);
+
+    if (early_end) {
+        *max_bytes = tmp_size;
+    }
     return;
 }
 
+zaddr zz_arm_relocator_get_insn_relocated_offset(ZzArmRelocator *self, zaddr address) {
+    const ZzInstruction *insn_ctx;
+    const ZzRelocateInstruction *re_insn_ctx;
+    int i;
+    for (i = 0; i < self->inpos; i++) {
+        re_insn_ctx = &self->output_insns[i];
+        insn_ctx = re_insn_ctx->insn_ctx;
+        if (insn_ctx->address == address && re_insn_ctx->relocated_offset) {
+            return re_insn_ctx->relocated_offset;
+        }
+    }
+    return 0;
+}
+
+void zz_arm_relocator_relocate_writer(ZzArmRelocator *relocator, zaddr code_address) {
+    ZzArmWriter *arm_writer;
+    arm_writer = relocator->output;
+    if (relocator->relocate_literal_insns_size) {
+        int i;
+        zaddr literal_address, relocated_offset, relocated_address, *literal_address_ptr;
+        for (i = 0; i < relocator->relocate_literal_insns_size; i++) {
+            literal_address_ptr = (zaddr *)relocator->relocate_literal_insns[i]->literal_address_ptr;
+            literal_address = *literal_address_ptr;
+            relocated_offset = zz_arm_relocator_get_insn_relocated_offset(relocator, literal_address);
+            if (relocated_offset) {
+                relocated_address = code_address + relocated_offset;
+                *literal_address_ptr = relocated_address;
+            }
+        }
+    }
+}
+
 void zz_arm_relocator_write_all(ZzArmRelocator *self) {
     zuint count = 0;
+    zuint outpos = self->outpos;
+    ZzArmWriter arm_writer = *self->output;
+
     while (zz_arm_relocator_write_one(self))
         count++;
 }
 
-// static zbool zz_arm_branch_is_unconditional(const cs_insn *insn_ctx) {
-//     switch (insn_ctx->detail->arm.cc) {
-//     case ARM_CC_INVALID:
-//     case ARM_CC_AL:
-//         return TRUE;
-//     default:
-//         return FALSE;
-//     }
-// }
+// PAGE: A8-312
+static zbool zz_arm_relocator_rewrite_ADD_register_A1(ZzArmRelocator *self, const ZzInstruction *insn_ctx,
+                                                      ZzRelocateInstruction *re_insn_ctx) {
+    zuint32 insn = insn_ctx->insn;
+
+    zuint32 Rn_ndx, Rd_ndx, Rm_ndx;
+    Rn_ndx = get_insn_sub(insn, 16, 4);
+    Rd_ndx = get_insn_sub(insn, 12, 4);
+    Rm_ndx = get_insn_sub(insn, 0, 4);
+
+    if (Rn_ndx != ZZ_ARM_REG_PC) {
+        return FALSE;
+    }
+    // push R7
+    zz_arm_writer_put_push_reg(self->output, ZZ_ARM_REG_R7);
+    zz_arm_writer_put_ldr_b_reg_address(self->output, ZZ_ARM_REG_R7, insn_ctx->pc);
+    zz_arm_writer_put_instruction(self->output, (insn & 0xFFF0FFFF) | ZZ_ARM_REG_R7 << 16);
+    // pop R7
+    zz_arm_writer_put_pop_reg(self->output, ZZ_ARM_REG_R7);
+    return TRUE;
+}
 
 // PAGE: A8-410
-static zbool zz_arm_relocator_rewrite_LDR_A1(ZzArmRelocator *self, ZzInstruction *insn_ctx) {
+static zbool zz_arm_relocator_rewrite_LDR_literal_A1(ZzArmRelocator *self, const ZzInstruction *insn_ctx,
+                                                     ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn = insn_ctx->insn;
     zuint32 imm12 = get_insn_sub(insn, 0, 12);
-    zuint32 imm32 = imm12 << 2;
+    zuint32 imm32 = imm12;
     zbool add = get_insn_sub(insn, 7 + 16, 1) == 1;
     zaddr target_address;
     if (add)
@@ -99,99 +181,155 @@ static zbool zz_arm_relocator_rewrite_LDR_A1(ZzArmRelocator *self, ZzInstruction
 
     zz_arm_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);
     zz_arm_writer_put_ldr_reg_reg_imm(self->output, Rt_ndx, Rt_ndx, 0);
+
     return TRUE;
 }
 
 // PAGE: A8-322
-static zbool zz_arm_relocator_rewrite_ADR_A1(ZzArmRelocator *self, ZzInstruction *insn_ctx) {
+static zbool zz_arm_relocator_rewrite_ADR_A1(ZzArmRelocator *self, const ZzInstruction *insn_ctx,
+                                             ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn = insn_ctx->insn;
     zuint32 imm12 = get_insn_sub(insn, 0, 12);
-    zuint32 imm32 = imm12 << 2;
+    zuint32 imm32 = imm12;
     zaddr target_address;
     target_address = insn_ctx->pc + imm32;
     int Rt_ndx = get_insn_sub(insn, 12, 4);
+
     zz_arm_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);
+
     return TRUE;
 }
 
 // PAGE: A8-322
-static zbool zz_arm_relocator_rewrite_ADR_A2(ZzArmRelocator *self, ZzInstruction *insn_ctx) {
+static zbool zz_arm_relocator_rewrite_ADR_A2(ZzArmRelocator *self, const ZzInstruction *insn_ctx,
+                                             ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn = insn_ctx->insn;
     zuint32 imm12 = get_insn_sub(insn, 0, 12);
-    zuint32 imm32 = imm12 << 2;
+    zuint32 imm32 = imm12;
     zaddr target_address;
     target_address = insn_ctx->pc - imm32;
     int Rt_ndx = get_insn_sub(insn, 12, 4);
+
     zz_arm_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);
+
     return TRUE;
 }
 
+// 0x000 : b.cond 0x0;
+// 0x004 : b 0x4
+// 0x008 : ldr pc, [pc, #0]
+// 0x00c : .long 0x0
+// 0x010 : remain code
+
 // PAGE: A8-334
-static zbool zz_arm_relocator_rewrite_B_A1(ZzArmRelocator *self, ZzInstruction *insn_ctx) {
+static zbool zz_arm_relocator_rewrite_B_A1(ZzArmRelocator *self, const ZzInstruction *insn_ctx,
+                                           ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn = insn_ctx->insn;
     zuint32 imm24 = get_insn_sub(insn, 0, 24);
     zuint32 imm32 = imm24 << 2;
     zaddr target_address;
     target_address = insn_ctx->pc + imm32;
+
+    zz_arm_writer_put_instruction(self->output, (insn & 0xFF000000) | 0);
+    zz_arm_writer_put_b_imm(self->output, 0x4);
     zz_arm_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address);
+
     return TRUE;
 }
 
+// 0x000 : bl.cond 0x0;
+
+// 0x004 : b 0x10
+
+// 0x008 : ldr lr, [pc, #0]
+// 0x00c : b 0x0
+// 0x010 : .long 0x0
+
+// 0x014 : ldr pc, [pc, #0]
+// 0x018 : .long 0x0
+
+// 0x01c : remain code
+
 // PAGE: A8-348
-static zbool zz_arm_relocator_rewrite_BLBLX_A1(ZzArmRelocator *self, ZzInstruction *insn_ctx) {
+static zbool zz_arm_relocator_rewrite_BLBLX_immediate_A1(ZzArmRelocator *self, const ZzInstruction *insn_ctx,
+                                                         ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn = insn_ctx->insn;
     zuint32 imm24 = get_insn_sub(insn, 0, 24);
     zuint32 imm32 = imm24 << 2;
     zaddr target_address;
-    target_address = insn_ctx->pc + imm32;
-    zz_arm_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_LR, insn_ctx->pc + 2 * 4);
+    target_address = ALIGN_4(insn_ctx->pc) + imm32;
+
+    // CurrentInstrSet = thumb
+    // targetInstrSet = arm
+
+    // convert 'bl' to 'b', but save 'cond'
+    zz_arm_writer_put_instruction(self->output, (insn & 0xF0000000) | 0b1010 << 24 | 0);
+
+    ZzArmWriter ouput_bak = *self->output;
+
+    zz_arm_writer_put_b_imm(self->output, 0);
+    ZzLiteralInstruction **literal_insn_ptr = &(self->relocate_literal_insns[self->relocate_literal_insns_size++]);
+    zz_arm_writer_put_ldr_b_reg_relocate_address(self->output, ZZ_ARM_REG_LR, insn_ctx->pc - 4, literal_insn_ptr);
     zz_arm_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address);
+
+    // overwrite `zz_arm_writer_put_b_imm`
+    zz_arm_writer_put_b_imm(&ouput_bak, self->output->pc - ouput_bak.pc - 8);
     return TRUE;
 }
 
 // PAGE: A8-348
-static zbool zz_arm_relocator_rewrite_BLBLX_A2(ZzArmRelocator *self, ZzInstruction *insn_ctx) {
+static zbool zz_arm_relocator_rewrite_BLBLX_immediate_A2(ZzArmRelocator *self, const ZzInstruction *insn_ctx,
+                                                         ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn = insn_ctx->insn;
     zuint32 H = get_insn_sub(insn, 24, 1);
     zuint32 imm24 = get_insn_sub(insn, 0, 24);
-    zuint32 imm32 = imm24 << 2 | H << 1;
+    zuint32 imm32 = (imm24 << 2) | (H << 1);
     zaddr target_address;
     target_address = insn_ctx->pc + imm32;
-    zz_arm_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_LR, insn_ctx->pc + 2 * 4);
+
+    ZzLiteralInstruction **literal_insn_ptr = &(self->relocate_literal_insns[self->relocate_literal_insns_size++]);
+    zz_arm_writer_put_ldr_b_reg_relocate_address(self->output, ZZ_ARM_REG_LR, insn_ctx->pc - 4, literal_insn_ptr);
     zz_arm_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address);
+
     return TRUE;
 }
 
 zbool zz_arm_relocator_write_one(ZzArmRelocator *self) {
-    ZzInstruction *insn_ctx;
+    const ZzInstruction *insn_ctx;
+    ZzRelocateInstruction *re_insn_ctx;
     zbool rewritten = FALSE;
 
     if (self->inpos != self->outpos) {
         insn_ctx = &self->input_insns[self->outpos];
+        re_insn_ctx = &self->output_insns[self->outpos];
+
         self->outpos++;
     } else
         return FALSE;
 
-    insn_ctx->pc = self->input_pc + 8;
+    re_insn_ctx->relocated_offset = (zaddr)self->output->pc - (zaddr)self->output->base;
 
     switch (GetARMInsnType(insn_ctx->insn)) {
-    case ARM_INS_LDR_A1:
-        rewritten = zz_arm_relocator_rewrite_LDR_A1(self, insn_ctx);
+    case ARM_INS_ADD_register_A1:
+        rewritten = zz_arm_relocator_rewrite_ADD_register_A1(self, insn_ctx, re_insn_ctx);
+        break;
+    case ARM_INS_LDR_literal_A1:
+        rewritten = zz_arm_relocator_rewrite_LDR_literal_A1(self, insn_ctx, re_insn_ctx);
         break;
     case ARM_INS_ADR_A1:
-        rewritten = zz_arm_relocator_rewrite_ADR_A1(self, insn_ctx);
+        rewritten = zz_arm_relocator_rewrite_ADR_A1(self, insn_ctx, re_insn_ctx);
         break;
     case ARM_INS_ADR_A2:
-        rewritten = zz_arm_relocator_rewrite_ADR_A2(self, insn_ctx);
+        rewritten = zz_arm_relocator_rewrite_ADR_A2(self, insn_ctx, re_insn_ctx);
         break;
     case ARM_INS_B_A1:
-        rewritten = zz_arm_relocator_rewrite_B_A1(self, insn_ctx);
+        rewritten = zz_arm_relocator_rewrite_B_A1(self, insn_ctx, re_insn_ctx);
         break;
-    case ARM_INS_BLBLX_A1:
-        rewritten = zz_arm_relocator_rewrite_BLBLX_A1(self, insn_ctx);
+    case ARM_INS_BLBLX_immediate_A1:
+        rewritten = zz_arm_relocator_rewrite_BLBLX_immediate_A1(self, insn_ctx, re_insn_ctx);
         break;
-    case ARM_INS_BLBLX_A2:
-        rewritten = zz_arm_relocator_rewrite_BLBLX_A2(self, insn_ctx);
+    case ARM_INS_BLBLX_immediate_A2:
+        rewritten = zz_arm_relocator_rewrite_BLBLX_immediate_A2(self, insn_ctx, re_insn_ctx);
         break;
     case ARM_UNDEF:
         rewritten = FALSE;
@@ -199,71 +337,8 @@ zbool zz_arm_relocator_write_one(ZzArmRelocator *self) {
     }
     if (!rewritten)
         zz_arm_writer_put_bytes(self->output, (zbyte *)&insn_ctx->insn, insn_ctx->size);
+
+    re_insn_ctx->relocated_length =
+        (zaddr)self->output->pc - (zaddr)self->output->base - (zaddr)re_insn_ctx->relocated_offset;
     return TRUE;
 }
-
-// static zbool zz_arm_relocator_rewrite_ldr(ZzArmRelocator *self, ZzInstruction *insn_ctx) {
-
-//     const cs_arm_op *dst = &insn_ctx->detail->operands[0];
-//     const cs_arm_op *src = &insn_ctx->detail->operands[1];
-//     zint disp;
-
-//     if (src->type != ARM_OP_MEM || src->mem.base != ARM_REG_PC)
-//         return TRUE;
-
-//     disp = src->mem.disp;
-
-//     zz_arm_writer_put_ldr_b_reg_address(self->output, dst->reg, insn_ctx->pc);
-//     if (disp > 0xff) {
-//         zz_arm_writer_put_add_reg_reg_imm(self->output, dst->reg, dst->reg, 0xc00 | ((disp >> 8) & 0xff));
-//     }
-//     zz_arm_writer_put_add_reg_reg_imm(self->output, dst->reg, dst->reg, disp & 0xff);
-//     zz_arm_writer_put_ldr_reg_reg_imm(self->output, dst->reg, dst->reg, 0);
-
-//     return TRUE;
-// }
-
-// static zbool zz_arm_relocator_rewrite_add(ZzArmRelocator *self, ZzInstruction *insn_ctx) {
-//     const cs_arm_op *dst = &insn_ctx->detail->operands[0];
-//     const cs_arm_op *left = &insn_ctx->detail->operands[1];
-//     const cs_arm_op *right = &insn_ctx->detail->operands[2];
-
-//     if (left->reg != ARM_REG_PC || right->type != ARM_OP_REG)
-//         return FALSE;
-
-//     if (right->reg == dst->reg) {
-//         zz_arm_writer_put_add_reg_reg_imm(self->output, dst->reg, dst->reg, insn_ctx->pc & 0xff);
-//         zz_arm_writer_put_add_reg_reg_imm(self->output, dst->reg, dst->reg, 0xc00 | ((insn_ctx->pc >> 8) & 0xff));
-//         zz_arm_writer_put_add_reg_reg_imm(self->output, dst->reg, dst->reg, 0x800 | ((insn_ctx->pc >> 16) & 0xff));
-//         zz_arm_writer_put_add_reg_reg_imm(self->output, dst->reg, dst->reg, 0x400 | ((insn_ctx->pc >> 24) & 0xff));
-//     } else {
-//         zz_arm_writer_put_ldr_reg_address(self->output, dst->reg, insn_ctx->pc);
-//         zz_arm_writer_put_add_reg_reg_imm(self->output, dst->reg, right->reg, 0);
-//     }
-
-//     return TRUE;
-// }
-
-// static zbool zz_arm_relocator_rewrite_b(ZzArmRelocator *self, cs_mode target_mode, ZzInstruction *insn_ctx) {
-//     cs_insn *insn_cs = insn_ctx->insn_cs;
-//     const cs_arm_op *target = &insn_ctx->detail->operands[0];
-
-//     if (target->type != ARM_OP_IMM)
-//         return FALSE;
-
-//     zz_arm_writer_put_ldr_reg_address(self->output, ARM_REG_PC,
-//                                       (target_mode == CS_MODE_THUMB) ? target->imm | 1 : target->imm);
-//     return TRUE;
-// }
-
-// static zbool zz_arm_relocator_rewrite_bl(ZzArmRelocator *self, cs_mode target_mode, ZzInstruction *insn_ctx) {
-//     const cs_arm_op *target = &insn_ctx->detail->operands[0];
-
-//     if (target->type != ARM_OP_IMM)
-//         return FALSE;
-
-//     zz_arm_writer_put_ldr_reg_address(self->output, ARM_REG_LR, (zaddr)self->output->pc + (2 * 4));
-//     zz_arm_writer_put_ldr_reg_address(self->output, ARM_REG_PC,
-//                                       (target_mode == CS_MODE_THUMB) ? target->imm | 1 : target->imm);
-//     return TRUE;
-// }
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-arm.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-arm.h
index f8df420e9..a1ee866ed 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-arm.h
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-arm.h
@@ -33,14 +33,18 @@
 #include "zzdeps/zz.h"
 
 typedef struct _ZzArmRelocator {
+    zbool try_relocated_again;
+    zsize try_relocated_length;
     zpointer input_start;
     zpointer input_cur;
     zaddr input_pc;
-    ZzInstruction *input_insns;
-    ZzArmWriter *output;
-
     zuint inpos;
     zuint outpos;
+    ZzInstruction *input_insns;
+    ZzRelocateInstruction *output_insns;
+    ZzLiteralInstruction **relocate_literal_insns;
+    zsize relocate_literal_insns_size;
+    ZzArmWriter *output;
 } ZzArmRelocator;
 
 void zz_arm_relocator_init(ZzArmRelocator *relocator, zpointer input_code, ZzArmWriter *output);
@@ -49,4 +53,5 @@ void zz_arm_relocator_write_all(ZzArmRelocator *self);
 zsize zz_arm_relocator_read_one(ZzArmRelocator *self, ZzInstruction *instruction);
 void zz_arm_relocator_try_relocate(zpointer address, zuint min_bytes, zuint *max_bytes);
 zbool zz_arm_relocator_write_one(ZzArmRelocator *self);
+void zz_arm_relocator_relocate_writer(ZzArmRelocator *relocator, zaddr code_address);
 #endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-thumb.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-thumb.c
index e259b7174..f56f71f5e 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-thumb.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-thumb.c
@@ -21,31 +21,49 @@
 
 #define MAX_RELOCATOR_INSTRUCIONS_SIZE 64
 
-void zz_thumb_relocator_init(ZzThumbRelocator *relocator, zpointer input_code, ZzThumbWriter *writer) {
+void zz_thumb_relocator_init(ZzThumbRelocator *relocator, zpointer input_code, ZzThumbWriter *output) {
+
+    memset(relocator, 0, sizeof(ZzThumbRelocator));
+
     relocator->inpos = 0;
     relocator->outpos = 0;
-
     relocator->input_start = input_code;
     relocator->input_cur = input_code;
     relocator->input_pc = (zaddr)input_code;
+    relocator->output = output;
+    relocator->relocate_literal_insns_size = 0;
+    relocator->try_relocated_length = 0;
+
     relocator->input_insns = (ZzInstruction *)malloc(MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzInstruction));
     memset(relocator->input_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzInstruction));
+    relocator->output_insns =
+        (ZzRelocateInstruction *)malloc(MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzRelocateInstruction));
+    memset(relocator->output_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzRelocateInstruction));
+    relocator->relocate_literal_insns =
+        (ZzLiteralInstruction **)malloc(MAX_LITERAL_INSN_SIZE * sizeof(ZzLiteralInstruction *));
+    memset(relocator->relocate_literal_insns, 0, MAX_LITERAL_INSN_SIZE * sizeof(ZzLiteralInstruction *));
 }
 
 void zz_thumb_relocator_reset(ZzThumbRelocator *self, zpointer input_code, ZzThumbWriter *output) {
     self->input_cur = input_code;
     self->input_start = input_code;
     self->input_pc = (zaddr)input_code;
-
     self->inpos = 0;
     self->outpos = 0;
-
     self->output = output;
+    self->relocate_literal_insns_size = 0;
+    self->try_relocated_length = 0;
+
+    memset(self->input_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzInstruction));
+    memset(self->output_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzRelocateInstruction));
+    memset(self->relocate_literal_insns, 0, MAX_LITERAL_INSN_SIZE * sizeof(ZzLiteralInstruction *));
 }
 
 zsize zz_thumb_relocator_read_one(ZzThumbRelocator *self, ZzInstruction *instruction) {
     ZzInstruction *insn_ctx = &self->input_insns[self->inpos];
+    ZzRelocateInstruction *re_insn_ctx = &self->output_insns[self->inpos];
 
+    re_insn_ctx->insn_ctx = insn_ctx;
     zz_thumb_reader_read_one_instruction(insn_ctx, self->input_cur);
 
     // switch (1) {}
@@ -53,7 +71,7 @@ zsize zz_thumb_relocator_read_one(ZzThumbRelocator *self, ZzInstruction *instruc
     self->inpos++;
 
     if (instruction != NULL)
-        instruction = insn_ctx;
+        *instruction = *insn_ctx;
 
     self->input_cur += insn_ctx->size;
     self->input_pc += insn_ctx->size;
@@ -62,30 +80,161 @@ zsize zz_thumb_relocator_read_one(ZzThumbRelocator *self, ZzInstruction *instruc
 }
 
 void zz_thumb_relocator_try_relocate(zpointer address, zuint min_bytes, zuint *max_bytes) {
-    *max_bytes = 16;
+    int tmp_size = 0;
+    zbool is_thumb;
+    zpointer target_addr;
+    ZzInstruction insn_ctx;
+    zbool early_end = FALSE;
+    is_thumb = INSTRUCTION_IS_THUMB((zaddr)address);
+    target_addr = (zpointer)address;
+
+    do {
+        zz_thumb_reader_read_one_instruction(&insn_ctx, target_addr);
+        switch (GetTHUMBInsnType(insn_ctx.insn1, insn_ctx.insn2)) {
+        case THUMB_INS_B_T2:
+            early_end = TRUE;
+            break;
+        case THUMB_INS_B_T4:
+            early_end = TRUE;
+            break;
+        default:;
+        }
+        tmp_size += insn_ctx.size;
+        target_addr = target_addr + insn_ctx.size;
+    } while (tmp_size < min_bytes);
+
+    if (early_end) {
+        *max_bytes = tmp_size;
+    }
     return;
 }
 
+zaddr zz_thumb_relocator_get_insn_relocated_offset(ZzThumbRelocator *self, zaddr address) {
+    const ZzInstruction *insn_ctx;
+    const ZzRelocateInstruction *re_insn_ctx;
+    int i;
+
+    for (i = 0; i < self->inpos; i++) {
+        re_insn_ctx = &self->output_insns[i];
+        insn_ctx = re_insn_ctx->insn_ctx;
+        if (insn_ctx->address == address && re_insn_ctx->relocated_offset) {
+            return re_insn_ctx->relocated_offset;
+        }
+    }
+    return 0;
+}
+
+void zz_thumb_relocator_relocate_writer(ZzThumbRelocator *relocator, zaddr code_address) {
+    ZzThumbWriter *thumb_writer;
+    thumb_writer = relocator->output;
+    if (relocator->relocate_literal_insns_size) {
+        int i;
+        zaddr literal_address, relocated_offset, relocated_address, *literal_address_ptr;
+        for (i = 0; i < relocator->relocate_literal_insns_size; i++) {
+            literal_address_ptr = (zaddr *)relocator->relocate_literal_insns[i]->literal_address_ptr;
+            literal_address = *literal_address_ptr;
+            relocated_offset = zz_thumb_relocator_get_insn_relocated_offset(relocator, literal_address & ~(zaddr)1);
+            if (relocated_offset) {
+                relocated_address = code_address + relocated_offset + 1;
+                *literal_address_ptr = relocated_address;
+            }
+        }
+    }
+}
+
 void zz_thumb_relocator_write_all(ZzThumbRelocator *self) {
     zuint count = 0;
+    zuint outpos = self->outpos;
+    ZzThumbWriter thumb_writer = *self->output;
     while (zz_thumb_relocator_write_one(self))
         count++;
 }
 
+// A8-357
+// 0: cbz #0
+// 2: b #6
+// 4: ldr pc, #0
+// 8: .long ?
+// c: next insn
+static zbool zz_thumb_relocator_rewrite_CBNZ_CBZ(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,
+                                                 ZzRelocateInstruction *re_insn_ctx) {
+
+    zuint32 insn1 = insn_ctx->insn1;
+    zuint16 op, i, imm5, Rn_ndx;
+    zuint32 imm32, nonzero;
+
+    op = get_insn_sub(insn1, 11, 1);
+    i = get_insn_sub(insn1, 9, 1);
+    imm5 = get_insn_sub(insn1, 3, 5);
+    Rn_ndx = get_insn_sub(insn1, 0, 3);
+
+    imm32 = imm5 << 1 | i << (5 + 1);
+    nonzero = (op == 1);
+
+    zaddr target_address = insn_ctx->pc + imm32;
+
+    /* for align , simple solution, maybe the correct solution is get `ldr_reg_address` length and adjust the immediate
+     * of `b_imm`. */
+    if ((zaddr)self->output->pc % 4) {
+        zz_thumb_writer_put_nop(self->output);
+    }
+    zz_thumb_writer_put_instruction(self->output, (insn1 & 0b1111110100000111) | 0);
+
+    zz_thumb_writer_put_b_imm(self->output, 0x6);
+    ZzLiteralInstruction **literal_insn_ptr = &(self->relocate_literal_insns[self->relocate_literal_insns_size++]);
+    zz_thumb_writer_put_ldr_reg_relocate_address(self->output, ZZ_ARM_REG_PC, target_address + 1, literal_insn_ptr);
+
+    // zz_thumb_writer_put_b_imm(self->output, 0x10);
+    // zz_thumb_writer_put_push_reg(self->output, ZZ_ARM_REG_R0);
+    // zz_thumb_writer_put_push_reg(self->output, ZZ_ARM_REG_R0);
+    // zz_thumb_writer_put_ldr_b_reg_address(self->output, ZZ_ARM_REG_R0, target_address + 1);
+    // zz_thumb_writer_put_str_reg_reg_offset(self->output, ZZ_ARM_REG_R0, ZZ_ARM_REG_SP, 4);
+    // zz_thumb_writer_put_pop_reg(self->output, ZZ_ARM_REG_R0);
+    // zz_thumb_writer_put_pop_reg(self->output, ZZ_ARM_REG_PC);
+    return TRUE;
+}
+
+// PAGE: A8-310
+static zbool zz_thumb_relocator_rewrite_ADD_register_T2(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,
+                                                        ZzRelocateInstruction *re_insn_ctx) {
+    zuint32 insn1 = insn_ctx->insn1;
+
+    zuint16 Rm_ndx, Rdn_ndx, DN, Rd_ndx;
+    Rm_ndx = get_insn_sub(insn1, 3, 4);
+    Rdn_ndx = get_insn_sub(insn1, 0, 3);
+    DN = get_insn_sub(insn1, 7, 1);
+    Rd_ndx = (DN << 3) | Rdn_ndx;
+
+    if (Rm_ndx != ZZ_ARM_REG_PC) {
+        return FALSE;
+    }
+
+    zz_thumb_writer_put_push_reg(self->output, ZZ_ARM_REG_R7);
+    zz_thumb_writer_put_ldr_b_reg_address(self->output, ZZ_ARM_REG_R7, insn_ctx->pc);
+    zz_thumb_writer_put_instruction(self->output, (insn1 & 0b1111111110000111) | ZZ_ARM_REG_R7 << 3);
+    zz_thumb_writer_put_pop_reg(self->output, ZZ_ARM_REG_R7);
+
+    return TRUE;
+}
+
 // PAGE: A8-410
-zbool zz_thumb_relocator_rewrite_LDR_T1(ZzThumbRelocator *self, ZzInstruction *insn_ctx) {
+zbool zz_thumb_relocator_rewrite_LDR_literal_T1(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,
+                                                ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn1 = insn_ctx->insn1;
     zuint32 imm8 = get_insn_sub(insn1, 0, 8);
     zuint32 imm32 = imm8 << 2;
-    zaddr target_address = insn_ctx->pc + imm32;
+    zaddr target_address = ALIGN_4(insn_ctx->pc) + imm32;
     int Rt_ndx = get_insn_sub(insn1, 8, 3);
-    zz_thumb_writer_put_ldr_reg_address(self->output, Rt_ndx, target_address);
+
+    zz_thumb_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);
     zz_thumb_writer_put_ldr_reg_reg_offset(self->output, Rt_ndx, Rt_ndx, 0);
+
     return TRUE;
 }
 
 // PAGE: A8-410
-zbool zz_thumb_relocator_rewrite_LDR_T2(ZzThumbRelocator *self, ZzInstruction *insn_ctx) {
+zbool zz_thumb_relocator_rewrite_LDR_literal_T2(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,
+                                                ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn1 = insn_ctx->insn1;
     zuint32 insn2 = insn_ctx->insn2;
 
@@ -95,18 +244,20 @@ zbool zz_thumb_relocator_rewrite_LDR_T2(ZzThumbRelocator *self, ZzInstruction *i
     zbool add = get_insn_sub(insn_ctx->insn1, 7, 1) == 1;
     zaddr target_address;
     if (add)
-        target_address = insn_ctx->pc + imm32;
+        target_address = ALIGN_4(insn_ctx->pc) + imm32;
     else
-        target_address = insn_ctx->pc - imm32;
+        target_address = ALIGN_4(insn_ctx->pc) - imm32;
     int Rt_ndx = get_insn_sub(insn_ctx->insn2, 12, 4);
 
-    zz_thumb_writer_put_ldr_reg_address(self->output, Rt_ndx, target_address);
+    zz_thumb_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);
     zz_thumb_writer_put_ldr_reg_reg_offset(self->output, Rt_ndx, Rt_ndx, 0);
+
     return TRUE;
 }
 
 // PAGE: A8-322
-zbool zz_thumb_relocator_rewrite_ADR_T1(ZzThumbRelocator *self, ZzInstruction *insn_ctx) {
+zbool zz_thumb_relocator_rewrite_ADR_T1(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,
+                                        ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn1 = insn_ctx->insn1;
 
     zuint32 imm8 = get_insn_sub(insn1, 0, 8);
@@ -114,12 +265,13 @@ zbool zz_thumb_relocator_rewrite_ADR_T1(ZzThumbRelocator *self, ZzInstruction *i
     zaddr target_address = insn_ctx->pc + imm32;
     int Rt_ndx = get_insn_sub(insn1, 8, 3);
 
-    zz_thumb_writer_put_ldr_reg_address(self->output, Rt_ndx, target_address);
+    zz_thumb_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);
     return TRUE;
 }
 
 // PAGE: A8-322
-zbool zz_thumb_relocator_rewrite_ADR_T2(ZzThumbRelocator *self, ZzInstruction *insn_ctx) {
+zbool zz_thumb_relocator_rewrite_ADR_T2(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,
+                                        ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn1 = insn_ctx->insn1;
     zuint32 insn2 = insn_ctx->insn2;
 
@@ -129,12 +281,13 @@ zbool zz_thumb_relocator_rewrite_ADR_T2(ZzThumbRelocator *self, ZzInstruction *i
     zaddr target_address;
     target_address = insn_ctx->pc - imm32;
     int Rt_ndx = get_insn_sub(insn_ctx->insn2, 8, 4);
-    zz_thumb_writer_put_ldr_reg_address(self->output, Rt_ndx, target_address);
+    zz_thumb_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);
     return TRUE;
 }
 
 // PAGE: A8-322
-zbool zz_thumb_relocator_rewrite_ADR_T3(ZzThumbRelocator *self, ZzInstruction *insn_ctx) {
+zbool zz_thumb_relocator_rewrite_ADR_T3(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,
+                                        ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn1 = insn_ctx->insn1;
     zuint32 insn2 = insn_ctx->insn2;
 
@@ -144,12 +297,20 @@ zbool zz_thumb_relocator_rewrite_ADR_T3(ZzThumbRelocator *self, ZzInstruction *i
     zaddr target_address;
     target_address = insn_ctx->pc + imm32;
     int Rt_ndx = get_insn_sub(insn_ctx->insn2, 8, 4);
-    zz_thumb_writer_put_ldr_reg_address(self->output, Rt_ndx, target_address);
+
+    zz_thumb_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);
     return TRUE;
 }
 
+// 0x000 : b.cond 0x0;
+// 0x002 : b 0x6
+// 0x004 : ldr pc, [pc, #0]
+// 0x008 : .long 0x0
+// 0x00c : remain code
+
 // PAGE: A8-334
-zbool zz_thumb_relocator_rewrite_B_T1(ZzThumbRelocator *self, ZzInstruction *insn_ctx) {
+zbool zz_thumb_relocator_rewrite_B_T1(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,
+                                      ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn1 = insn_ctx->insn1;
     // zuint32 insn2 = insn_ctx->insn2;
 
@@ -157,27 +318,39 @@ zbool zz_thumb_relocator_rewrite_B_T1(ZzThumbRelocator *self, ZzInstruction *ins
     zuint32 imm32 = imm8 << 1;
     zaddr target_address = insn_ctx->pc + imm32;
 
-    zz_thumb_writer_put_instruction(self->output, (insn1 & 0xFF00));
+    /* for align , simple solution, maybe the correct solution is get `ldr_reg_address` length and adjust the immediate
+     * of `b_imm`. */
+    if ((zaddr)self->output->pc % 4) {
+        zz_thumb_writer_put_nop(self->output);
+    }
+    zz_thumb_writer_put_instruction(self->output, (insn1 & 0xFF00) | 0);
     zz_thumb_writer_put_b_imm(self->output, 0x6);
-    zz_thumb_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address);
+    zz_thumb_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address + 1);
     return TRUE;
 }
 
 // PAGE: A8-334
-zbool zz_thumb_relocator_rewrite_B_T2(ZzThumbRelocator *self, ZzInstruction *insn_ctx) {
+zbool zz_thumb_relocator_rewrite_B_T2(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,
+                                      ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn1 = insn_ctx->insn1;
-    // zuint32 insn2 = insn_ctx->insn2;
 
     zuint32 imm11 = get_insn_sub(insn1, 0, 11);
     zuint32 imm32 = imm11 << 1;
     zaddr target_address = insn_ctx->pc + imm32;
 
-    zz_thumb_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address);
+    zz_thumb_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address + 1);
     return TRUE;
 }
 
+// 0x002 : b.cond.W 0x2;
+// 0x006 : b 0x6
+// 0x008 : ldr pc, [pc, #0]
+// 0x00c : .long 0x0
+// 0x010 : remain code
+
 // PAGE: A8-334
-zbool zz_thumb_relocator_rewrite_B_T3(ZzThumbRelocator *self, ZzInstruction *insn_ctx) {
+zbool zz_thumb_relocator_rewrite_B_T3(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,
+                                      ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn1 = insn_ctx->insn1;
     zuint32 insn2 = insn_ctx->insn2;
 
@@ -191,14 +364,21 @@ zbool zz_thumb_relocator_rewrite_B_T3(ZzThumbRelocator *self, ZzInstruction *ins
     zaddr target_address;
     target_address = insn_ctx->pc + imm32;
 
-    zz_thumb_writer_put_instruction(self->output, (insn_ctx->insn & 0b11010000000000001111101111000000) | 0b1);
+    /* for align , simple solution, maybe the correct solution is get `ldr_reg_address` length and adjust the immediate
+     * of `b_imm`. */
+    if ((zaddr)self->output->pc % 4 == 0) {
+        zz_thumb_writer_put_nop(self->output);
+    }
+    zz_thumb_writer_put_instruction(self->output, insn_ctx->insn1 & 0b1111101111000000);
+    zz_thumb_writer_put_instruction(self->output, (insn_ctx->insn2 & 0b1101000000000000) | 0b1);
     zz_thumb_writer_put_b_imm(self->output, 0x6);
-    zz_thumb_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address);
+    zz_thumb_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address + 1);
     return TRUE;
 }
 
 // PAGE: A8-334
-zbool zz_thumb_relocator_rewrite_B_T4(ZzThumbRelocator *self, ZzInstruction *insn_ctx) {
+zbool zz_thumb_relocator_rewrite_B_T4(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,
+                                      ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn1 = insn_ctx->insn1;
     zuint32 insn2 = insn_ctx->insn2;
 
@@ -207,19 +387,20 @@ zbool zz_thumb_relocator_rewrite_B_T4(ZzThumbRelocator *self, ZzInstruction *ins
     zuint32 J1 = get_insn_sub(insn_ctx->insn2, 13, 1);
     zuint32 imm10 = get_insn_sub(insn_ctx->insn1, 0, 10);
     zuint32 imm11 = get_insn_sub(insn_ctx->insn2, 0, 11);
-    zuint32 I1 = (~(J1 ^ S));
-    zuint32 I2 = (~(J2 ^ S));
+    zuint32 I1 = (~(J1 ^ S)) & 0x1;
+    zuint32 I2 = (~(J2 ^ S)) & 0x1;
     zuint32 imm32 =
         imm11 << 1 | imm10 << (1 + 11) | I1 << (1 + 11 + 6) | I2 << (1 + 11 + 6 + 1) | S << (1 + 11 + 6 + 1 + 1);
     zaddr target_address;
     target_address = insn_ctx->pc + imm32;
 
-    zz_thumb_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address);
+    zz_thumb_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address + 1);
     return TRUE;
 }
 
 // PAGE: A8-348
-zbool zz_thumb_relocator_rewrite_BLBLX_T1(ZzThumbRelocator *self, ZzInstruction *insn_ctx) {
+zbool zz_thumb_relocator_rewrite_BLBLX_immediate_T1(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,
+                                                    ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn1 = insn_ctx->insn1;
     zuint32 insn2 = insn_ctx->insn2;
 
@@ -228,20 +409,25 @@ zbool zz_thumb_relocator_rewrite_BLBLX_T1(ZzThumbRelocator *self, ZzInstruction
     zuint32 J1 = get_insn_sub(insn_ctx->insn2, 13, 1);
     zuint32 imm10 = get_insn_sub(insn_ctx->insn1, 0, 10);
     zuint32 imm11 = get_insn_sub(insn_ctx->insn2, 0, 11);
-    zuint32 I1 = (~(J1 ^ S));
-    zuint32 I2 = (~(J2 ^ S));
+    zuint32 I1 = (~(J1 ^ S)) & 0x1;
+    zuint32 I2 = (~(J2 ^ S)) & 0x1;
     zuint32 imm32 =
         imm11 << 1 | imm10 << (1 + 11) | I1 << (1 + 11 + 6) | I2 << (1 + 11 + 6 + 1) | S << (1 + 11 + 6 + 1 + 1);
     zaddr target_address;
+
+    // CurrentInstrSet = thumb
+    // targetInstrSet = arm
     target_address = insn_ctx->pc + imm32;
 
-    zz_thumb_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_LR, insn_ctx->pc + 2 * 4);
+    ZzLiteralInstruction **literal_insn_ptr = &(self->relocate_literal_insns[self->relocate_literal_insns_size++]);
+    zz_thumb_writer_put_ldr_b_reg_relocate_address(self->output, ZZ_ARM_REG_LR, insn_ctx->pc + 1, literal_insn_ptr);
     zz_thumb_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address + 1);
     return TRUE;
 }
 
 // PAGE: A8-348
-zbool zz_thumb_relocator_rewrite_BLBLX_T2(ZzThumbRelocator *self, ZzInstruction *insn_ctx) {
+zbool zz_thumb_relocator_rewrite_BLBLX_T2(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,
+                                          ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn1 = insn_ctx->insn1;
     zuint32 insn2 = insn_ctx->insn2;
 
@@ -249,64 +435,78 @@ zbool zz_thumb_relocator_rewrite_BLBLX_T2(ZzThumbRelocator *self, ZzInstruction
     zuint32 J2 = get_insn_sub(insn_ctx->insn2, 11, 1);
     zuint32 J1 = get_insn_sub(insn_ctx->insn2, 13, 1);
     zuint32 imm10_1 = get_insn_sub(insn_ctx->insn1, 0, 10);
-    zuint32 imm10_16 = get_insn_sub(insn_ctx->insn2, 1, 10);
-    zuint32 I1 = (~(J1 ^ S));
-    zuint32 I2 = (~(J2 ^ S));
+    zuint32 imm10_2 = get_insn_sub(insn_ctx->insn2, 1, 10);
+    zuint32 I1 = (~(J1 ^ S)) & 0x1;
+    zuint32 I2 = (~(J2 ^ S)) & 0x1;
+    ;
+    zuint32 H = get_insn_sub(insn_ctx->insn2, 0, 1);
     zuint32 imm32 =
-        imm10_1 << 2 | imm10_16 << (2 + 10) | I1 << (2 + 10 + 6) | I2 << (2 + 10 + 6 + 1) | S << (2 + 10 + 6 + 1 + 1);
+        imm10_2 << 2 | imm10_1 << (2 + 10) | I1 << (2 + 10 + 6) | I2 << (2 + 10 + 6 + 1) | S << (2 + 10 + 6 + 1 + 1);
     zaddr target_address;
-    target_address = insn_ctx->pc + imm32;
 
-    zz_thumb_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_LR, insn_ctx->pc + 2 * 4);
-    zz_thumb_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address + 1);
+    // CurrentInstrSet = thumb
+    // targetInstrSet = arm
+    target_address = ALIGN_4(insn_ctx->pc) + imm32;
+
+    ZzLiteralInstruction **literal_insn_ptr = &(self->relocate_literal_insns[self->relocate_literal_insns_size++]);
+    zz_thumb_writer_put_ldr_b_reg_relocate_address(self->output, ZZ_ARM_REG_LR, insn_ctx->pc + 1, literal_insn_ptr);
+    zz_thumb_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address);
     return TRUE;
 }
 
 zbool zz_thumb_relocator_write_one(ZzThumbRelocator *self) {
-    ZzInstruction *insn_ctx;
+    const ZzInstruction *insn_ctx;
+    ZzRelocateInstruction *re_insn_ctx;
     zbool rewritten = FALSE;
 
     if (self->inpos != self->outpos) {
         insn_ctx = &self->input_insns[self->outpos];
+        re_insn_ctx = &self->output_insns[self->outpos];
         self->outpos++;
     } else
         return FALSE;
 
-    insn_ctx->pc = self->input_pc + 4;
+    re_insn_ctx->relocated_offset = (zaddr)self->output->pc - (zaddr)self->output->base;
 
-    switch (GetTHUMBInsnType(insn_ctx->insn)) {
-    case THUMB_INS_LDR_T1:
-        rewritten = zz_thumb_relocator_rewrite_LDR_T1(self, insn_ctx);
+    switch (GetTHUMBInsnType(insn_ctx->insn1, insn_ctx->insn2)) {
+    case THUMB_INS_CBNZ_CBZ:
+        rewritten = zz_thumb_relocator_rewrite_CBNZ_CBZ(self, insn_ctx, re_insn_ctx);
+        break;
+    case THUMB_INS_ADD_register_T2:
+        rewritten = zz_thumb_relocator_rewrite_ADD_register_T2(self, insn_ctx, re_insn_ctx);
         break;
-    case THUMB_INS_LDR_T2:
-        rewritten = zz_thumb_relocator_rewrite_LDR_T2(self, insn_ctx);
+    case THUMB_INS_LDR_literal_T1:
+        rewritten = zz_thumb_relocator_rewrite_LDR_literal_T1(self, insn_ctx, re_insn_ctx);
+        break;
+    case THUMB_INS_LDR_literal_T2:
+        rewritten = zz_thumb_relocator_rewrite_LDR_literal_T2(self, insn_ctx, re_insn_ctx);
         break;
     case THUMB_INS_ADR_T1:
-        rewritten = zz_thumb_relocator_rewrite_ADR_T1(self, insn_ctx);
+        rewritten = zz_thumb_relocator_rewrite_ADR_T1(self, insn_ctx, re_insn_ctx);
         break;
     case THUMB_INS_ADR_T2:
-        rewritten = zz_thumb_relocator_rewrite_ADR_T2(self, insn_ctx);
+        rewritten = zz_thumb_relocator_rewrite_ADR_T2(self, insn_ctx, re_insn_ctx);
         break;
     case THUMB_INS_ADR_T3:
-        rewritten = zz_thumb_relocator_rewrite_ADR_T3(self, insn_ctx);
+        rewritten = zz_thumb_relocator_rewrite_ADR_T3(self, insn_ctx, re_insn_ctx);
         break;
     case THUMB_INS_B_T1:
-        rewritten = zz_thumb_relocator_rewrite_B_T1(self, insn_ctx);
+        rewritten = zz_thumb_relocator_rewrite_B_T1(self, insn_ctx, re_insn_ctx);
         break;
     case THUMB_INS_B_T2:
-        rewritten = zz_thumb_relocator_rewrite_B_T2(self, insn_ctx);
+        rewritten = zz_thumb_relocator_rewrite_B_T2(self, insn_ctx, re_insn_ctx);
         break;
     case THUMB_INS_B_T3:
-        rewritten = zz_thumb_relocator_rewrite_B_T3(self, insn_ctx);
+        rewritten = zz_thumb_relocator_rewrite_B_T3(self, insn_ctx, re_insn_ctx);
         break;
     case THUMB_INS_B_T4:
-        rewritten = zz_thumb_relocator_rewrite_B_T4(self, insn_ctx);
+        rewritten = zz_thumb_relocator_rewrite_B_T4(self, insn_ctx, re_insn_ctx);
         break;
-    case THUMB_INS_BLBLX_T1:
-        rewritten = zz_thumb_relocator_rewrite_BLBLX_T1(self, insn_ctx);
+    case THUMB_INS_BLBLX_immediate_T1:
+        rewritten = zz_thumb_relocator_rewrite_BLBLX_immediate_T1(self, insn_ctx, re_insn_ctx);
         break;
-    case THUMB_INS_BLBLX_T2:
-        rewritten = zz_thumb_relocator_rewrite_BLBLX_T2(self, insn_ctx);
+    case THUMB_INS_BLBLX_immediate_T2:
+        rewritten = zz_thumb_relocator_rewrite_BLBLX_T2(self, insn_ctx, re_insn_ctx);
         break;
     case THUMB_UNDEF:
         rewritten = FALSE;
@@ -314,76 +514,9 @@ zbool zz_thumb_relocator_write_one(ZzThumbRelocator *self) {
     }
     if (!rewritten)
         zz_thumb_writer_put_bytes(self->output, (zbyte *)&insn_ctx->insn, insn_ctx->size);
+
+    re_insn_ctx->relocated_length =
+        (zaddr)self->output->pc - (zaddr)self->output->base - (zaddr)re_insn_ctx->relocated_offset;
+
     return TRUE;
 }
-
-// zbool zz_thumb_relocator_rewrite_ldr(ZzThumbRelocator *self, ZzInstruction *insn_ctx) {
-//     cs_arm_op *dst = &insn_ctx->detail->operands[0];
-//     cs_arm_op *src = &insn_ctx->detail->operands[1];
-//     zaddr absolute_pc;
-
-//     if (src->type != ARM_OP_MEM || src->mem.base != ARM_REG_PC)
-//         return FALSE;
-
-//     absolute_pc = insn_ctx->pc & ~((zaddr)(4 - 1));
-//     absolute_pc += src->mem.disp;
-
-//     zz_thumb_writer_put_ldr_b_reg_address(self->output, dst->reg, absolute_pc);
-//     zz_thumb_writer_put_ldr_reg_reg(self->output, dst->reg, dst->reg);
-//     return TRUE;
-// }
-
-// zbool zz_thumb_relocator_rewrite_add(ZzThumbRelocator *self, ZzInstruction *insn_ctx) {
-//     const cs_arm_op *dst = &insn_ctx->detail->operands[0];
-//     const cs_arm_op *src = &insn_ctx->detail->operands[1];
-//     arm_reg temp_reg;
-
-//     if (insn_ctx->detail->op_count != 2)
-//         return FALSE;
-//     else if (src->type != ARM_OP_REG || src->reg != ARM_REG_PC)
-//         return FALSE;
-
-//     if (dst->reg != ARM_REG_R0)
-//         temp_reg = ARM_REG_R0;
-//     else
-//         temp_reg = ARM_REG_R1;
-
-// #if defined(DEBUG_MODE)
-//     debug_break();
-// #endif
-//     return TRUE;
-// }
-
-// zbool zz_thumb_relocator_rewrite_b(ZzThumbRelocator *self, cs_mode target_mode, ZzInstruction *insn_ctx) {
-//     const cs_arm_op *target = &insn_ctx->detail->operands[0];
-
-//     if (target->type != ARM_OP_IMM)
-//         return FALSE;
-// #if defined(DEBUG_MODE)
-//     debug_break();
-// #endif
-//     return TRUE;
-// }
-
-// zbool zz_thumb_relocator_rewrite_b_cond(ZzThumbRelocator *self, ZzInstruction *insn_ctx) {
-//     const cs_arm_op *target = &insn_ctx->detail->operands[0];
-
-//     if (target->type != ARM_OP_IMM)
-//         return FALSE;
-
-// #if defined(DEBUG_MODE)
-//     debug_break();
-// #endif
-//     return TRUE;
-// }
-
-// zbool zz_thumb_relocator_rewrite_bl(ZzThumbRelocator *self, cs_mode target_mode, ZzInstruction *insn_ctx) {
-//     const cs_arm_op *target = &insn_ctx->detail->operands[0];
-
-//     if (target->type != ARM_OP_IMM)
-//         return FALSE;
-// #if defined(DEBUG_MODE)
-//     debug_break();
-// #endif
-//     return TRUE;
-// }
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-thumb.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-thumb.h
index 679bbd2f4..1baeac0eb 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-thumb.h
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-thumb.h
@@ -32,12 +32,16 @@
 #include "zzdeps/zz.h"
 
 typedef struct _ZzThumbRelocator {
+    zbool try_relocated_again;
+    zsize try_relocated_length;
     zpointer input_start;
     zpointer input_cur;
     zaddr input_pc;
     ZzInstruction *input_insns;
+    ZzRelocateInstruction *output_insns;
+    ZzLiteralInstruction **relocate_literal_insns;
+    zsize relocate_literal_insns_size;
     ZzThumbWriter *output;
-
     zuint inpos;
     zuint outpos;
 } ZzThumbRelocator;
@@ -46,12 +50,8 @@ void zz_thumb_relocator_init(ZzThumbRelocator *relocator, zpointer input_code, Z
 void zz_thumb_relocator_reset(ZzThumbRelocator *self, zpointer input_code, ZzThumbWriter *output);
 zsize zz_thumb_relocator_read_one(ZzThumbRelocator *self, ZzInstruction *instruction);
 zbool zz_thumb_relocator_write_one(ZzThumbRelocator *self);
+void zz_thumb_relocator_relocate_writer(ZzThumbRelocator *relocator, zaddr code_address);
 void zz_thumb_relocator_write_all(ZzThumbRelocator *self);
 void zz_thumb_relocator_try_relocate(zpointer address, zuint min_bytes, zuint *max_bytes);
 
-zbool zz_thumb_relocator_rewrite_ldr(ZzThumbRelocator *self, ZzInstruction *insn_ctx);
-zbool zz_thumb_relocator_rewrite_add(ZzThumbRelocator *self, ZzInstruction *insn_ctx);
-zbool zz_thumb_relocator_rewrite_b(ZzThumbRelocator *self, ZzInstruction *insn_ctx);
-zbool zz_thumb_relocator_rewrite_b_cond(ZzThumbRelocator *self, ZzInstruction *insn_ctx);
-zbool zz_thumb_relocator_rewrite_bl(ZzThumbRelocator *self, ZzInstruction *insn_ctx);
 #endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-arm.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-arm.c
index 9f661da72..d9883eddf 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-arm.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-arm.c
@@ -20,37 +20,69 @@
 
 // ATTENTION !!!:
 // 写 writer 部分, 需要参考, `Instrcution Set Encoding` 部分
-// `witer` REF: `ZzInstruction Set Encoding`
+// `writer` REF: `ZzInstruction Set Encoding`
 
 ZzArmWriter *zz_arm_writer_new(zpointer data_ptr) {
     ZzArmWriter *writer = (ZzArmWriter *)malloc(sizeof(ZzArmWriter));
-    int t = 4 - (zaddr)data_ptr % 4;
+    memset(writer, 0, sizeof(ZzArmWriter));
 
-    writer->codedata = data_ptr + t;
-    writer->base = data_ptr + t;
-    writer->pc = data_ptr + t;
+    zaddr align_address = (zaddr)data_ptr & ~(zaddr)3;
+    writer->codedata = (zpointer)align_address;
+    writer->base = (zpointer)align_address;
+    writer->pc = align_address;
     writer->size = 0;
+
+    writer->literal_insn_size = 0;
+    memset(writer->literal_insns, 0, sizeof(ZzLiteralInstruction) * MAX_LITERAL_INSN_SIZE);
+
     return writer;
 }
 
 void zz_arm_writer_init(ZzArmWriter *self, zpointer data_ptr) { zz_arm_writer_reset(self, data_ptr); }
 
 void zz_arm_writer_reset(ZzArmWriter *self, zpointer data_ptr) {
-    int t = (zaddr)data_ptr % 4;
 
-    self->codedata = data_ptr + t;
-    self->base = data_ptr + t;
-    self->pc = data_ptr + t + 8;
+    zaddr align_address = (zaddr)data_ptr & ~(zaddr)3;
+    self->codedata = (zpointer)align_address;
+    self->base = (zpointer)align_address;
+    self->pc = align_address;
+
+    self->literal_insn_size = 0;
+    memset(self->literal_insns, 0, sizeof(ZzLiteralInstruction) * MAX_LITERAL_INSN_SIZE);
+
     self->size = 0;
 }
 
+zsize zz_arm_writer_near_jump_range_size() { return ((1 << 23) << 2); }
+
+// ------- relocator -------
+
+ZzLiteralInstruction *zz_arm_writer_put_ldr_b_reg_relocate_address(ZzArmWriter *self, ZzARMReg reg, zaddr address,
+                                                                   ZzLiteralInstruction **literal_insn_ptr) {
+    zz_arm_writer_put_ldr_b_reg_address(self, reg, address);
+    ZzLiteralInstruction *literal_insn = &(self->literal_insns[self->literal_insn_size - 1]);
+    *literal_insn_ptr = literal_insn;
+    return literal_insn;
+}
+
+ZzLiteralInstruction *zz_arm_writer_put_ldr_reg_relocate_address(ZzArmWriter *self, ZzARMReg reg, zaddr address,
+                                                                 ZzLiteralInstruction **literal_insn_ptr) {
+    zz_arm_writer_put_ldr_reg_address(self, reg, address);
+    ZzLiteralInstruction *literal_insn = &(self->literal_insns[self->literal_insn_size - 1]);
+    *literal_insn_ptr = literal_insn;
+    return literal_insn;
+}
+
 // ------- user custom -------
 
 void zz_arm_writer_put_ldr_b_reg_address(ZzArmWriter *self, ZzARMReg reg, zaddr address) {
+    self->literal_insns[self->literal_insn_size].literal_insn_ptr = self->codedata;
     zz_arm_writer_put_ldr_reg_reg_imm(self, reg, ZZ_ARM_REG_PC, 0);
     zz_arm_writer_put_b_imm(self, 0x0);
+    self->literal_insns[self->literal_insn_size++].literal_address_ptr = self->codedata;
     zz_arm_writer_put_bytes(self, (zpointer)&address, sizeof(zpointer));
 }
+
 void zz_arm_writer_put_bx_to_thumb(ZzArmWriter *self) {
     zz_arm_writer_put_sub_reg_reg_imm(self, ZZ_ARM_REG_SP, ZZ_ARM_REG_SP, 0x8);
     zz_arm_writer_put_str_reg_reg_imm(self, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0);
@@ -148,8 +180,11 @@ void zz_arm_writer_put_str_reg_reg_imm(ZzArmWriter *self, ZzARMReg dst_reg, ZzAR
     zz_arm_writer_put_instruction(self, 0xe4000000 | rd.index << 12 | rs.index << 16 | P << 24 | U << 23 | W << 21 |
                                             (imm & ZZ_INT12_MASK));
 }
+
 void zz_arm_writer_put_ldr_reg_address(ZzArmWriter *self, ZzARMReg reg, zaddr address) {
+    self->literal_insns[self->literal_insn_size].literal_insn_ptr = self->codedata;
     zz_arm_writer_put_ldr_reg_reg_imm(self, reg, ZZ_ARM_REG_PC, -4);
+    self->literal_insns[self->literal_insn_size++].literal_address_ptr = self->codedata;
     zz_arm_writer_put_bytes(self, (zpointer)&address, sizeof(zpointer));
 }
 
@@ -179,4 +214,17 @@ void zz_arm_writer_put_bx_reg(ZzArmWriter *self, ZzARMReg reg) {
 
 void zz_arm_writer_put_nop(ZzArmWriter *self) { zz_arm_writer_put_instruction(self, 0xe320f000); }
 
-zsize zz_arm_writer_near_jump_range_size() { return ((1 << 23) << 2); }
\ No newline at end of file
+void zz_arm_writer_put_push_reg(ZzArmWriter *self, ZzARMReg reg) {
+    ZzArmRegInfo ri;
+    zz_arm_register_describe(reg, &ri);
+    zz_arm_writer_put_instruction(self, 0b11100101001011010000000000000100 | ri.index << 12);
+    return;
+}
+
+void zz_arm_writer_put_pop_reg(ZzArmWriter *self, ZzARMReg reg) {
+    ZzArmRegInfo ri;
+    zz_arm_register_describe(reg, &ri);
+
+    zz_arm_writer_put_instruction(self, 0b11100100100111010000000000000100 | ri.index << 12);
+    return;
+}
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-arm.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-arm.h
index 8b0b27f34..95826608c 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-arm.h
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-arm.h
@@ -38,6 +38,7 @@ typedef ZzWriter ZzArmWriter;
 ZzArmWriter *zz_arm_writer_new(zpointer data_ptr);
 void zz_arm_writer_init(ZzArmWriter *self, zpointer data_ptr);
 void zz_arm_writer_reset(ZzArmWriter *self, zpointer data_ptr);
+zsize zz_arm_writer_near_jump_range_size();
 
 // ------- user custom -------
 
@@ -61,6 +62,10 @@ void zz_arm_writer_put_ldr_reg_reg_imm_A1(ZzArmWriter *self, ZzARMReg dst_reg, Z
 void zz_arm_writer_put_ldr_reg_address(ZzArmWriter *self, ZzARMReg reg, zaddr address);
 void zz_arm_writer_put_add_reg_reg_imm(ZzArmWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg, zuint32 imm);
 void zz_arm_writer_put_sub_reg_reg_imm(ZzArmWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg, zuint32 imm);
-zsize zz_arm_writer_near_jump_range_size();
-
+void zz_arm_writer_put_push_reg(ZzArmWriter *self, ZzARMReg reg);
+void zz_arm_writer_put_pop_reg(ZzArmWriter *self, ZzARMReg reg);
+ZzLiteralInstruction *zz_arm_writer_put_ldr_b_reg_relocate_address(ZzArmWriter *self, ZzARMReg reg, zaddr address,
+                                                                   ZzLiteralInstruction **literal_insn_ptr);
+ZzLiteralInstruction *zz_arm_writer_put_ldr_reg_relocate_address(ZzArmWriter *self, ZzARMReg reg, zaddr address,
+                                                                 ZzLiteralInstruction **literal_insn_ptr);
 #endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-thumb.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-thumb.c
index 803be02a3..c55db42fe 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-thumb.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-thumb.c
@@ -24,44 +24,60 @@
 
 ZzThumbWriter *zz_thumb_writer_new(zpointer data_ptr) {
     ZzThumbWriter *writer = (ZzThumbWriter *)malloc(sizeof(ZzThumbWriter));
-    int t = 4 - (zaddr)data_ptr % 4;
+    memset(writer, 0, sizeof(ZzThumbWriter));
 
-    writer->codedata = data_ptr + t;
-    writer->base = data_ptr + t;
-    writer->pc = data_ptr + t;
+    zaddr align_address = (zaddr)data_ptr & ~(zaddr)3;
+    writer->codedata = (zpointer)align_address;
+    writer->base = (zpointer)align_address;
+    writer->pc = align_address;
     writer->size = 0;
+
+    writer->literal_insn_size = 0;
+    memset(writer->literal_insns, 0, sizeof(ZzLiteralInstruction) * MAX_LITERAL_INSN_SIZE);
+
     return writer;
 }
 
 void zz_thumb_writer_init(ZzThumbWriter *self, zpointer data_ptr) { zz_thumb_writer_reset(self, data_ptr); }
 
 void zz_thumb_writer_reset(ZzThumbWriter *self, zpointer data_ptr) {
-    int t = (zaddr)data_ptr % 4;
+    zaddr align_address = (zaddr)data_ptr & ~(zaddr)3;
 
-    self->codedata = data_ptr + t;
-    self->base = data_ptr + t;
-    self->pc = data_ptr + t + 4;
+    self->codedata = (zpointer)align_address;
+    self->base = (zpointer)align_address;
+    self->pc = align_address;
     self->size = 0;
+
+    self->literal_insn_size = 0;
+    memset(self->literal_insns, 0, sizeof(ZzLiteralInstruction) * MAX_LITERAL_INSN_SIZE);
 }
 
-zpointer zz_thumb_writer_put_ldr_b_reg_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address) {
-    ZzArmRegInfo ri;
+zsize zz_thumb_writer_near_jump_range_size() { return ((1 << 23) << 1); }
 
-    zz_arm_register_describe(reg, &ri);
+// ------- relocator -------
 
-    // if (((zaddr)self->pc) % 4) {
-    //     zz_thumb_writer_put_ldr_reg_imm(self, reg, 0x4);
-    //     if (ri.meta <= ZZ_ARM_REG_R7) {
-    //         zz_thumb_writer_put_nop(self);
-    //     }
-    // } else {
-    //     if (ri.meta <= ZZ_ARM_REG_R7) {
-    //         zz_thumb_writer_put_ldr_reg_imm(self, reg, 0x0);
-    //     } else {
-    //         zz_thumb_writer_put_ldr_reg_imm(self, reg, 0x4);
-    //         zz_thumb_writer_put_nop(self);
-    //     }
-    // }
+ZzLiteralInstruction *zz_thumb_writer_put_ldr_b_reg_relocate_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address,
+                                                                     ZzLiteralInstruction **literal_insn_ptr) {
+    zz_thumb_writer_put_ldr_b_reg_address(self, reg, address);
+    ZzLiteralInstruction *literal_insn = &(self->literal_insns[self->literal_insn_size - 1]);
+    *literal_insn_ptr = literal_insn;
+    return literal_insn;
+}
+
+ZzLiteralInstruction *zz_thumb_writer_put_ldr_reg_relocate_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address,
+                                                                   ZzLiteralInstruction **literal_insn_ptr) {
+    zz_thumb_writer_put_ldr_reg_address(self, reg, address);
+    ZzLiteralInstruction *literal_insn = &(self->literal_insns[self->literal_insn_size - 1]);
+    *literal_insn_ptr = literal_insn;
+    return literal_insn;
+}
+
+// ------- custom -------
+
+void zz_thumb_writer_put_ldr_b_reg_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address) {
+    ZzArmRegInfo ri;
+    zz_arm_register_describe(reg, &ri);
+    self->literal_insns[self->literal_insn_size].literal_insn_ptr = self->codedata;
 
     if ((((zaddr)self->pc) % 4)) {
         if (ri.meta <= ZZ_ARM_REG_R7) {
@@ -80,14 +96,17 @@ zpointer zz_thumb_writer_put_ldr_b_reg_address(ZzThumbWriter *self, ZzARMReg reg
     }
 
     zz_thumb_writer_put_b_imm(self, 0x2);
+    self->literal_insns[self->literal_insn_size++].literal_address_ptr = self->codedata;
     zz_thumb_writer_put_bytes(self, (zpointer)&address, sizeof(zpointer));
-    return self->pc;
+    return;
 }
 
-zpointer zz_thumb_writer_put_ldr_reg_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address) {
+void zz_thumb_writer_put_ldr_reg_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address) {
     ZzArmRegInfo ri;
-
     zz_arm_register_describe(reg, &ri);
+
+    self->literal_insns[self->literal_insn_size].literal_insn_ptr = self->codedata;
+
     if ((((zaddr)self->pc) % 4)) {
         if (ri.meta <= ZZ_ARM_REG_R7) {
             zz_thumb_writer_put_ldr_reg_imm(self, reg, 0x0);
@@ -101,43 +120,40 @@ zpointer zz_thumb_writer_put_ldr_reg_address(ZzThumbWriter *self, ZzARMReg reg,
             zz_thumb_writer_put_nop(self);
     }
 
-    // if ((((zaddr)self->pc) % 4)) {
-    //     zz_thumb_writer_put_nop(self);
-    // }
+    self->literal_insns[self->literal_insn_size++].literal_address_ptr = self->codedata;
     zz_thumb_writer_put_bytes(self, (zpointer)&address, sizeof(zpointer));
-    return self->pc;
+    return;
 }
-// ------- user custom -------
 
 // ------- architecture default -------
-zpointer zz_thumb_writer_put_nop(ZzThumbWriter *self) {
+void zz_thumb_writer_put_nop(ZzThumbWriter *self) {
     zz_thumb_writer_put_instruction(self, 0x46c0);
-    return self->pc;
+    return;
 }
 
-zpointer zz_thumb_writer_put_bytes(ZzThumbWriter *self, zbyte *data, zuint data_size) {
+void zz_thumb_writer_put_bytes(ZzThumbWriter *self, zbyte *data, zuint data_size) {
     memcpy(self->codedata, data, data_size);
     self->codedata = (zpointer)self->codedata + data_size;
     self->pc += data_size;
     self->size += data_size;
-    return self->pc;
+    return;
 }
 
-zpointer zz_thumb_writer_put_instruction(ZzThumbWriter *self, uint16_t insn) {
+void zz_thumb_writer_put_instruction(ZzThumbWriter *self, uint16_t insn) {
     *(uint16_t *)(self->codedata) = insn;
     self->codedata = (zpointer)self->codedata + sizeof(uint16_t);
     self->pc += 2;
     self->size += 2;
-    return self->pc;
+    return;
 }
 
-zpointer zz_thumb_writer_put_b_imm(ZzThumbWriter *self, zuint32 imm) {
+void zz_thumb_writer_put_b_imm(ZzThumbWriter *self, zuint32 imm) {
 
     zz_thumb_writer_put_instruction(self, 0xe000 | ((imm / 2) & ZZ_INT11_MASK));
-    return self->pc;
+    return;
 }
 
-zpointer zz_thumb_writer_put_bx_reg(ZzThumbWriter *self, ZzARMReg reg) {
+void zz_thumb_writer_put_bx_reg(ZzThumbWriter *self, ZzARMReg reg) {
     ZzArmRegInfo ri;
 
     zz_arm_register_describe(reg, &ri);
@@ -148,20 +164,20 @@ zpointer zz_thumb_writer_put_bx_reg(ZzThumbWriter *self, ZzARMReg reg) {
 
     zz_thumb_writer_put_instruction(self, 0x4700 | (ri.index << 3));
     zz_thumb_writer_put_nop(self);
-    return self->pc;
+    return;
 }
 
-zpointer zz_thumb_writer_put_blx_reg(ZzThumbWriter *self, ZzARMReg reg) {
+void zz_thumb_writer_put_blx_reg(ZzThumbWriter *self, ZzARMReg reg) {
     ZzArmRegInfo ri;
 
     zz_arm_register_describe(reg, &ri);
 
     zz_thumb_writer_put_instruction(self, 0x4780 | (ri.index << 3));
-    return self->pc;
+    return;
 }
 
 // A8.8.18
-zpointer zz_thumb_writer_put_branch_imm(ZzThumbWriter *self, zuint32 imm, zbool link, zbool thumb) {
+void zz_thumb_writer_put_branch_imm(ZzThumbWriter *self, zuint32 imm, zbool link, zbool thumb) {
     union {
         zint32 i;
         zuint32 u;
@@ -179,27 +195,27 @@ zpointer zz_thumb_writer_put_branch_imm(ZzThumbWriter *self, zuint32 imm, zbool
 
     zz_thumb_writer_put_instruction(self, 0xf000 | (s << 10) | imm10);
     zz_thumb_writer_put_instruction(self, 0x8000 | (link << 14) | (j1 << 13) | (thumb << 12) | (j2 << 11) | imm11);
-    return self->pc;
+    return;
 }
 
-zpointer zz_thumb_writer_put_bl_imm(ZzThumbWriter *self, zuint32 imm) {
+void zz_thumb_writer_put_bl_imm(ZzThumbWriter *self, zuint32 imm) {
     zz_thumb_writer_put_branch_imm(self, imm, TRUE, TRUE);
-    return self->pc;
+    return;
 }
 
-zpointer zz_thumb_writer_put_blx_imm(ZzThumbWriter *self, zuint32 imm) {
+void zz_thumb_writer_put_blx_imm(ZzThumbWriter *self, zuint32 imm) {
     zz_thumb_writer_put_branch_imm(self, imm, TRUE, FALSE);
-    return self->pc;
+    return;
 }
 
-zpointer zz_thumb_writer_put_b_imm32(ZzThumbWriter *self, zuint32 imm) {
+void zz_thumb_writer_put_b_imm32(ZzThumbWriter *self, zuint32 imm) {
     zz_thumb_writer_put_branch_imm(self, imm, FALSE, TRUE);
-    return self->pc;
+    return;
 }
 
 // PAGE: A8-410
 // A8.8.64 LDR (literal)
-zpointer zz_thumb_writer_put_ldr_reg_imm(ZzThumbWriter *self, ZzARMReg reg, zint32 imm) {
+void zz_thumb_writer_put_ldr_reg_imm(ZzThumbWriter *self, ZzARMReg reg, zint32 imm) {
     ZzArmRegInfo ri;
 
     zz_arm_register_describe(reg, &ri);
@@ -214,65 +230,11 @@ zpointer zz_thumb_writer_put_ldr_reg_imm(ZzThumbWriter *self, ZzARMReg reg, zint
         zz_thumb_writer_put_instruction(self, 0xf85f | (add << 7));
         zz_thumb_writer_put_instruction(self, (ri.index << 12) | ABS(imm));
     }
-    return self->pc;
-}
-
-// static zpointer zz_thumb_writer_put_transfer_reg_reg_offset(ZzThumbWriter
-// *self,
-//                                                         ZzThumbMemoryOperation
-//                                                         operation,
-//                                                         ZzARMReg left_reg,
-//                                                         ZzARMReg right_reg,
-//                                                         zint32 right_offset)
-//                                                         {
-//     zz_arm_register_describe(left_reg, &lr);
-//     zz_arm_register_describe(right_reg, &rr);
-
-//     if (right_offset >= 0) {
-//         if (lr.meta <= ZZ_ARM_REG_R7 && (rr.meta <= ZZ_ARM_REG_R7 || rr.meta ==
-//         ZZ_ARM_REG_SP) &&
-//             ((rr.meta == ZZ_ARM_REG_SP && right_offset <= 1020) ||
-//              (rr.meta != ZZ_ARM_REG_SP && right_offset <= 124)) &&
-//             (right_offset % 4) == 0) {
-//             zuint16 insn;
-
-//             if (rr.meta == ZZ_ARM_REG_SP)
-//                 insn = 0x9000 | (lr.index << 8) | (right_offset / 4);
-//             else
-//                 insn = 0x6000 | (right_offset / 4) << 6 | (rr.index << 3) |
-//                 lr.index;
-
-//             if (operation == ZZ_THUMB_MEMORY_LOAD)
-//                 insn |= 0x0800;
-
-//             zz_thumb_writer_put_instruction(self, insn);
-//         } else {
-//             if (right_offset > 4095)
-//                 return;
-//             zz_thumb_writer_put_instruction(
-//                 self, 0xf8c0 | ((operation == ZZ_THUMB_MEMORY_LOAD) ? 0x0010
-//                 : 0x0000) |
-//                 rr.index);
-//             zz_thumb_writer_put_instruction(self, (lr.index << 12) |
-//             right_offset);
-//         }
-//     } else {
-//         if ((rr.index & 0xF) == 0xF) {
-//             zz_thumb_writer_put_ldr_reg_imm(self, left_reg, right_offset);
-//         } else {
-//             zz_thumb_writer_put_instruction(
-//                 self, 0xf840 | ((operation == ZZ_THUMB_MEMORY_LOAD) ? 0x0010
-//                 : 0x0000) |
-//                 rr.index);
-//             zz_thumb_writer_put_instruction(self, 0x0c00 | (lr.index << 12) |
-//                                                       (ABS(right_offset) &
-//                                                       ZZ_INT8_MASK));
-//         }
-//     }
-// }
-
-zpointer zz_thumb_writer_put_transfer_reg_reg_offset_T1(ZzThumbWriter *self, ZzThumbMemoryOperation operation,
-                                                        ZzARMReg left_reg, ZzARMReg right_reg, zint32 right_offset) {
+    return;
+}
+
+zbool zz_thumb_writer_put_transfer_reg_reg_offset_T1(ZzThumbWriter *self, ZzThumbMemoryOperation operation,
+                                                     ZzARMReg left_reg, ZzARMReg right_reg, zint32 right_offset) {
     ZzArmRegInfo lr, rr;
 
     zz_arm_register_describe(left_reg, &lr);
@@ -281,20 +243,20 @@ zpointer zz_thumb_writer_put_transfer_reg_reg_offset_T1(ZzThumbWriter *self, ZzT
     zuint16 insn;
 
     if (right_offset < 0)
-        return 0;
+        return FALSE;
 
     if (lr.meta <= ZZ_ARM_REG_R7 && rr.meta <= ZZ_ARM_REG_R7 && right_offset < ((1 << 5) << 2)) {
         insn = 0x6000 | (right_offset / 4) << 6 | (rr.index << 3) | lr.index;
         if (operation == ZZ_THUMB_MEMORY_LOAD)
             insn |= 0x0800;
         zz_thumb_writer_put_instruction(self, insn);
-        return self->pc;
+        return TRUE;
     }
-    return 0;
+    return FALSE;
 }
 
-zpointer zz_thumb_writer_put_transfer_reg_reg_offset_T2(ZzThumbWriter *self, ZzThumbMemoryOperation operation,
-                                                        ZzARMReg left_reg, ZzARMReg right_reg, zint32 right_offset) {
+zbool zz_thumb_writer_put_transfer_reg_reg_offset_T2(ZzThumbWriter *self, ZzThumbMemoryOperation operation,
+                                                     ZzARMReg left_reg, ZzARMReg right_reg, zint32 right_offset) {
     ZzArmRegInfo lr, rr;
 
     zz_arm_register_describe(left_reg, &lr);
@@ -303,20 +265,20 @@ zpointer zz_thumb_writer_put_transfer_reg_reg_offset_T2(ZzThumbWriter *self, ZzT
     zuint16 insn;
 
     if (right_offset < 0)
-        return 0;
+        return FALSE;
 
     if (rr.meta == ZZ_ARM_REG_SP && lr.meta <= ZZ_ARM_REG_R7 && right_offset < ((1 << 8) << 2)) {
         insn = 0x9000 | (lr.index << 8) | (right_offset / 4);
         if (operation == ZZ_THUMB_MEMORY_LOAD)
             insn |= 0x0800;
         zz_thumb_writer_put_instruction(self, insn);
-        return self->pc;
+        return TRUE;
     }
-    return 0;
+    return FALSE;
 }
 
-zpointer zz_thumb_writer_put_transfer_reg_reg_offset_T3(ZzThumbWriter *self, ZzThumbMemoryOperation operation,
-                                                        ZzARMReg left_reg, ZzARMReg right_reg, zint32 right_offset) {
+zbool zz_thumb_writer_put_transfer_reg_reg_offset_T3(ZzThumbWriter *self, ZzThumbMemoryOperation operation,
+                                                     ZzARMReg left_reg, ZzARMReg right_reg, zint32 right_offset) {
     ZzArmRegInfo lr, rr;
 
     zz_arm_register_describe(left_reg, &lr);
@@ -325,13 +287,9 @@ zpointer zz_thumb_writer_put_transfer_reg_reg_offset_T3(ZzThumbWriter *self, ZzT
     zuint16 insn;
 
     if (right_offset < 0)
-        return 0;
+        return FALSE;
 
     if (right_offset < (1 << 12)) {
-        // if (operation == ZZ_THUMB_MEMORY_LOAD && rr.meta == ZZ_ARM_REG_PC &&
-        // (self->pc % 4)) {
-        //     zz_thumb_writer_put_nop(self);
-        // }
         if (rr.meta == ZZ_ARM_REG_PC) {
             zz_thumb_writer_put_ldr_reg_imm(self, left_reg, right_offset);
         }
@@ -339,14 +297,14 @@ zpointer zz_thumb_writer_put_transfer_reg_reg_offset_T3(ZzThumbWriter *self, ZzT
                                         0xf8c0 | ((operation == ZZ_THUMB_MEMORY_LOAD) ? 0x0010 : 0x0000) | rr.index);
         zz_thumb_writer_put_instruction(self, (lr.index << 12) | right_offset);
 
-        return self->pc;
+        return TRUE;
     }
-    return 0;
+    return FALSE;
 }
 
-zpointer zz_thumb_writer_put_transfer_reg_reg_offset_T4(ZzThumbWriter *self, ZzThumbMemoryOperation operation,
-                                                        ZzARMReg left_reg, ZzARMReg right_reg, zint32 right_offset,
-                                                        zbool index, zbool wback) {
+zbool zz_thumb_writer_put_transfer_reg_reg_offset_T4(ZzThumbWriter *self, ZzThumbMemoryOperation operation,
+                                                     ZzARMReg left_reg, ZzARMReg right_reg, zint32 right_offset,
+                                                     zbool index, zbool wback) {
     ZzArmRegInfo lr, rr;
 
     zz_arm_register_describe(left_reg, &lr);
@@ -365,65 +323,64 @@ zpointer zz_thumb_writer_put_transfer_reg_reg_offset_T4(ZzThumbWriter *self, ZzT
                                                       rr.index);
             zz_thumb_writer_put_instruction(self, 0x0800 | (lr.index << 12) | (index << 10) | (add << 9) |
                                                       (wback << 8) | (ABS(right_offset)));
-            return self->pc;
+            return TRUE;
         }
     }
-    return 0;
+    return FALSE;
 }
 
 // PAGE: A8-406
 // PAGE: A8.8.203 STR (immediate, Thumb)
-static zpointer zz_thumb_writer_put_transfer_reg_reg_offset(ZzThumbWriter *self, ZzThumbMemoryOperation operation,
-                                                            ZzARMReg left_reg, ZzARMReg right_reg,
-                                                            zint32 right_offset) {
+static void zz_thumb_writer_put_transfer_reg_reg_offset(ZzThumbWriter *self, ZzThumbMemoryOperation operation,
+                                                        ZzARMReg left_reg, ZzARMReg right_reg, zint32 right_offset) {
     if (zz_thumb_writer_put_transfer_reg_reg_offset_T1(self, operation, left_reg, right_reg, right_offset))
-        return self->pc;
+        return;
 
     if (zz_thumb_writer_put_transfer_reg_reg_offset_T2(self, operation, left_reg, right_reg, right_offset))
-        return self->pc;
+        return;
 
     if (zz_thumb_writer_put_transfer_reg_reg_offset_T3(self, operation, left_reg, right_reg, right_offset))
-        return self->pc;
+        return;
     if (zz_thumb_writer_put_transfer_reg_reg_offset_T4(self, operation, left_reg, right_reg, right_offset, 1, 0))
-        return self->pc;
-    return 0;
+        return;
+    return;
 }
 
-zpointer zz_thumb_writer_put_ldr_reg_reg_offset(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg,
-                                                zint32 src_offset) {
+void zz_thumb_writer_put_ldr_reg_reg_offset(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg,
+                                            zint32 src_offset) {
     zz_thumb_writer_put_transfer_reg_reg_offset(self, ZZ_THUMB_MEMORY_LOAD, dst_reg, src_reg, src_offset);
-    return self->pc;
+    return;
 }
 
-zpointer zz_thumb_writer_put_str_reg_reg_offset(ZzThumbWriter *self, ZzARMReg src_reg, ZzARMReg dst_reg,
-                                                zint32 dst_offset) {
+void zz_thumb_writer_put_str_reg_reg_offset(ZzThumbWriter *self, ZzARMReg src_reg, ZzARMReg dst_reg,
+                                            zint32 dst_offset) {
     zz_thumb_writer_put_transfer_reg_reg_offset(self, ZZ_THUMB_MEMORY_STORE, src_reg, dst_reg, dst_offset);
-    return self->pc;
+    return;
 }
 
-zpointer zz_thumb_writer_put_ldr_index_reg_reg_offset(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg,
-                                                      zint32 src_offset, zbool index) {
+void zz_thumb_writer_put_ldr_index_reg_reg_offset(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg,
+                                                  zint32 src_offset, zbool index) {
     zz_thumb_writer_put_transfer_reg_reg_offset_T4(self, ZZ_THUMB_MEMORY_LOAD, dst_reg, src_reg, src_offset, index, 1);
-    return self->pc;
+    return;
 }
 
-zpointer zz_thumb_writer_put_str_index_reg_reg_offset(ZzThumbWriter *self, ZzARMReg src_reg, ZzARMReg dst_reg,
-                                                      zint32 dst_offset, zbool index) {
+void zz_thumb_writer_put_str_index_reg_reg_offset(ZzThumbWriter *self, ZzARMReg src_reg, ZzARMReg dst_reg,
+                                                  zint32 dst_offset, zbool index) {
     zz_thumb_writer_put_transfer_reg_reg_offset_T4(self, ZZ_THUMB_MEMORY_STORE, src_reg, dst_reg, dst_offset, index, 1);
-    return self->pc;
+    return;
 }
 
-zpointer zz_thumb_writer_put_str_reg_reg(ZzThumbWriter *self, ZzARMReg src_reg, ZzARMReg dst_reg) {
+void zz_thumb_writer_put_str_reg_reg(ZzThumbWriter *self, ZzARMReg src_reg, ZzARMReg dst_reg) {
     zz_thumb_writer_put_str_reg_reg_offset(self, src_reg, dst_reg, 0);
-    return self->pc;
+    return;
 }
 
-zpointer zz_thumb_writer_put_ldr_reg_reg(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg) {
+void zz_thumb_writer_put_ldr_reg_reg(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg) {
     zz_thumb_writer_put_ldr_reg_reg_offset(self, dst_reg, src_reg, 0);
-    return self->pc;
+    return;
 }
 
-zpointer zz_thumb_writer_put_add_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, zint32 imm) {
+void zz_thumb_writer_put_add_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, zint32 imm) {
     ZzArmRegInfo dst;
     zuint16 sign_mask, insn;
 
@@ -444,16 +401,15 @@ zpointer zz_thumb_writer_put_add_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg,
     }
 
     zz_thumb_writer_put_instruction(self, insn);
-    return self->pc;
+    return;
 }
 
-zpointer zz_thumb_writer_put_sub_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, zint32 imm) {
+void zz_thumb_writer_put_sub_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, zint32 imm) {
     zz_thumb_writer_put_add_reg_imm(self, dst_reg, -imm);
-    return self->pc;
+    return;
 }
 
-zpointer zz_thumb_writer_put_add_reg_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg left_reg,
-                                             zint32 right_value) {
+void zz_thumb_writer_put_add_reg_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg left_reg, zint32 right_value) {
     ZzArmRegInfo dst, left;
     zuint16 insn;
 
@@ -464,11 +420,17 @@ zpointer zz_thumb_writer_put_add_reg_reg_imm(ZzThumbWriter *self, ZzARMReg dst_r
         return zz_thumb_writer_put_add_reg_imm(self, dst_reg, right_value);
     }
 
-    if (left.meta == ZZ_ARM_REG_SP || left.meta == ZZ_ARM_REG_PC) {
-        zuint16 base_mask;
+    if (dst.meta <= ZZ_ARM_REG_R7 && left.meta <= ZZ_ARM_REG_R7 && ABS(right_value) < (1 << 3)) {
+        zuint32 sign_mask = 0;
 
-        if (right_value < 0 || right_value % 4 != 0)
-            return 0;
+        if (right_value < 0)
+            sign_mask = 1 << 9;
+
+        insn = 0x1c00 | sign_mask | (ABS(right_value) << 6) | (left.index << 3) | dst.index;
+        zz_thumb_writer_put_instruction(self, insn);
+    } else if ((left.meta == ZZ_ARM_REG_SP || left.meta == ZZ_ARM_REG_PC) && dst.meta <= ZZ_ARM_REG_R7 &&
+               right_value > 0 && (right_value % 4 == 0) && right_value < (1 << 8)) {
+        zuint16 base_mask;
 
         if (left.meta == ZZ_ARM_REG_SP)
             base_mask = 0x0800;
@@ -476,27 +438,64 @@ zpointer zz_thumb_writer_put_add_reg_reg_imm(ZzThumbWriter *self, ZzARMReg dst_r
             base_mask = 0x0000;
 
         insn = 0xa000 | base_mask | (dst.index << 8) | (right_value / 4);
+        zz_thumb_writer_put_instruction(self, insn);
     } else {
-        zuint16 sign_mask = 0x0000;
+        zuint16 insn1, insn2;
+        zuint i, imm3, imm8;
+        i = (ABS(right_value) >> (3 + 8)) & 0x1;
+        imm3 = (ABS(right_value) >> 8) & 0b111;
+        imm8 = ABS(right_value) & 0b11111111;
+
+        // A8-708, sub
+        // A8-306 add
+        if (right_value < 0)
+            zz_thumb_writer_put_instruction(self, 0b1111001010100000 | i << 10 | left.index);
+        else
+            zz_thumb_writer_put_instruction(self, 0b1111001000000000 | i << 10 | left.index);
+        zz_thumb_writer_put_instruction(self, 0b0 | imm3 << 12 | dst.index << 8 | imm8);
+    }
 
-        if (ABS(right_value) > 7)
-            return 0;
+    return;
+}
 
-        if (right_value < 0)
-            sign_mask = 0x0200;
+void zz_thumb_writer_put_sub_reg_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg left_reg, zint32 right_value) {
+    zz_thumb_writer_put_add_reg_reg_imm(self, dst_reg, left_reg, -right_value);
+    return;
+}
 
-        insn = 0x1c00 | sign_mask | (ABS(right_value) << 6) | (left.index << 3) | dst.index;
-    }
+void zz_thumb_writer_put_push_reg(ZzThumbWriter *self, ZzARMReg reg) {
+    ZzArmRegInfo ri;
+    zz_arm_register_describe(reg, &ri);
 
-    zz_thumb_writer_put_instruction(self, insn);
+    zuint16 M, register_list;
+    M = 0;
 
-    return self->pc;
+    zz_thumb_writer_put_instruction(self, 0b1011010000000000 | M << 8 | 1 << ri.index);
+    return;
 }
 
-zpointer zz_thumb_writer_put_sub_reg_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg left_reg,
-                                             zint32 right_value) {
-    zz_thumb_writer_put_add_reg_reg_imm(self, dst_reg, left_reg, -right_value);
-    return self->pc;
+void zz_thumb_writer_put_pop_reg(ZzThumbWriter *self, ZzARMReg reg) {
+    ZzArmRegInfo ri;
+    zz_arm_register_describe(reg, &ri);
+
+    zuint16 P, register_list;
+    P = 0;
+
+    zz_thumb_writer_put_instruction(self, 0b1011110000000000 | P << 8 | 1 << ri.index);
+    return;
 }
 
-zsize zz_thumb_writer_near_jump_range_size() { return ((1 << 23) << 1); }
+void zz_thumb_writer_put_add_reg_reg_reg(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg left_reg, ZzARMReg right_reg) {
+    ZzArmRegInfo dst, left, right;
+    zz_arm_register_describe(dst_reg, &dst);
+    zz_arm_register_describe(left_reg, &left);
+    zz_arm_register_describe(right_reg, &right);
+
+    zuint16 Rm_ndx, Rn_ndx, Rd_ndx;
+    Rd_ndx = dst.index;
+    Rm_ndx = right.index;
+    Rn_ndx = left.index;
+
+    zz_thumb_writer_put_instruction(self, 0b0001100000000000 | Rm_ndx << 6 | Rn_ndx << 3 | Rd_ndx);
+    return;
+}
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-thumb.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-thumb.h
index ebe5ffb95..a05f03dec 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-thumb.h
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-thumb.h
@@ -40,44 +40,47 @@ typedef enum _ZzThumbMemoryOperation { ZZ_THUMB_MEMORY_LOAD, ZZ_THUMB_MEMORY_STO
 
 // ------- user custom -------
 
-zpointer zz_thumb_writer_put_ldr_b_reg_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address);
+void zz_thumb_writer_put_ldr_b_reg_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address);
 
 // ------- architecture default -------
 
 ZzThumbWriter *zz_thumb_writer_new(zpointer data_ptr);
 void zz_thumb_writer_init(ZzThumbWriter *self, zpointer data_ptr);
 void zz_thumb_writer_reset(ZzThumbWriter *self, zpointer data_ptr);
-zpointer zz_thumb_writer_put_nop(ZzThumbWriter *self);
-zpointer zz_thumb_writer_put_bytes(ZzThumbWriter *self, zbyte *data, zuint data_size);
-zpointer zz_thumb_writer_put_instruction(ZzThumbWriter *self, uint16_t insn);
-zpointer zz_thumb_writer_put_b_imm(ZzThumbWriter *self, zuint32 imm);
-zpointer zz_thumb_writer_put_bx_reg(ZzThumbWriter *self, ZzARMReg reg);
-zpointer zz_thumb_writer_put_blx_reg(ZzThumbWriter *self, ZzARMReg reg);
-zpointer zz_thumb_writer_put_branch_imm(ZzThumbWriter *self, zuint32 imm, zbool link, zbool thumb);
-zpointer zz_thumb_writer_put_bl_imm(ZzThumbWriter *self, zuint32 imm);
-zpointer zz_thumb_writer_put_blx_imm(ZzThumbWriter *self, zuint32 imm);
-zpointer zz_thumb_writer_put_b_imm32(ZzThumbWriter *self, zuint32 imm);
+zsize zz_thumb_writer_near_jump_range_size();
+void zz_thumb_writer_put_nop(ZzThumbWriter *self);
+void zz_thumb_writer_put_bytes(ZzThumbWriter *self, zbyte *data, zuint data_size);
+void zz_thumb_writer_put_instruction(ZzThumbWriter *self, uint16_t insn);
+void zz_thumb_writer_put_b_imm(ZzThumbWriter *self, zuint32 imm);
+void zz_thumb_writer_put_bx_reg(ZzThumbWriter *self, ZzARMReg reg);
+void zz_thumb_writer_put_blx_reg(ZzThumbWriter *self, ZzARMReg reg);
+void zz_thumb_writer_put_branch_imm(ZzThumbWriter *self, zuint32 imm, zbool link, zbool thumb);
+void zz_thumb_writer_put_bl_imm(ZzThumbWriter *self, zuint32 imm);
+void zz_thumb_writer_put_blx_imm(ZzThumbWriter *self, zuint32 imm);
+void zz_thumb_writer_put_b_imm32(ZzThumbWriter *self, zuint32 imm);
 
-zpointer zz_thumb_writer_put_ldr_reg_imm(ZzThumbWriter *self, ZzARMReg reg, zint32 imm);
-zpointer zz_thumb_writer_put_ldr_reg_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address);
+void zz_thumb_writer_put_ldr_reg_imm(ZzThumbWriter *self, ZzARMReg reg, zint32 imm);
+void zz_thumb_writer_put_ldr_reg_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address);
 
-static zpointer zz_thumb_writer_put_transfer_reg_reg_offset(ZzThumbWriter *self, ZzThumbMemoryOperation operation,
-                                                            ZzARMReg left_reg, ZzARMReg right_reg, zint32 right_offset);
-zpointer zz_thumb_writer_put_ldr_reg_reg_offset(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg,
-                                                zint32 src_offset);
-zpointer zz_thumb_writer_put_str_reg_reg_offset(ZzThumbWriter *self, ZzARMReg src_reg, ZzARMReg dst_reg,
-                                                zint32 dst_offset);
-zpointer zz_thumb_writer_put_str_index_reg_reg_offset(ZzThumbWriter *self, ZzARMReg src_reg, ZzARMReg dst_reg,
-                                                      zint32 dst_offset, zbool index);
-zpointer zz_thumb_writer_put_ldr_index_reg_reg_offset(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg,
-                                                      zint32 src_offset, zbool index);
-zpointer zz_thumb_writer_put_str_reg_reg(ZzThumbWriter *self, ZzARMReg src_reg, ZzARMReg dst_reg);
-zpointer zz_thumb_writer_put_ldr_reg_reg(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg);
-zpointer zz_thumb_writer_put_add_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, zint32 imm);
-zpointer zz_thumb_writer_put_sub_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, zint32 imm);
-zpointer zz_thumb_writer_put_add_reg_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg left_reg,
-                                             zint32 right_value);
-zpointer zz_thumb_writer_put_sub_reg_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg left_reg,
-                                             zint32 right_value);
-zsize zz_thumb_writer_near_jump_range_size();
+static void zz_thumb_writer_put_transfer_reg_reg_offset(ZzThumbWriter *self, ZzThumbMemoryOperation operation,
+                                                        ZzARMReg left_reg, ZzARMReg right_reg, zint32 right_offset);
+void zz_thumb_writer_put_ldr_reg_reg_offset(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg, zint32 src_offset);
+void zz_thumb_writer_put_str_reg_reg_offset(ZzThumbWriter *self, ZzARMReg src_reg, ZzARMReg dst_reg, zint32 dst_offset);
+void zz_thumb_writer_put_str_index_reg_reg_offset(ZzThumbWriter *self, ZzARMReg src_reg, ZzARMReg dst_reg,
+                                                  zint32 dst_offset, zbool index);
+void zz_thumb_writer_put_ldr_index_reg_reg_offset(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg,
+                                                  zint32 src_offset, zbool index);
+void zz_thumb_writer_put_str_reg_reg(ZzThumbWriter *self, ZzARMReg src_reg, ZzARMReg dst_reg);
+void zz_thumb_writer_put_ldr_reg_reg(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg);
+void zz_thumb_writer_put_add_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, zint32 imm);
+void zz_thumb_writer_put_sub_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, zint32 imm);
+void zz_thumb_writer_put_add_reg_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg left_reg, zint32 right_value);
+void zz_thumb_writer_put_sub_reg_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg left_reg, zint32 right_value);
+void zz_thumb_writer_put_push_reg(ZzThumbWriter *self, ZzARMReg reg);
+void zz_thumb_writer_put_pop_reg(ZzThumbWriter *self, ZzARMReg reg);
+void zz_thumb_writer_put_add_reg_reg_reg(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg left_reg, ZzARMReg right_reg);
+ZzLiteralInstruction *zz_thumb_writer_put_ldr_reg_relocate_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address,
+                                                                   ZzLiteralInstruction **literal_insn_ptr);
+ZzLiteralInstruction *zz_thumb_writer_put_ldr_b_reg_relocate_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address,
+                                                                     ZzLiteralInstruction **literal_insn_ptr);
 #endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/instructions.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/instructions.c
index 390d6ee6f..00ec11d1c 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/instructions.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/instructions.c
@@ -1,12 +1,13 @@
 #include "instructions.h"
 #include <string.h>
 
-zuint32 get_insn_sub(zuint32 insn, int start, int length) { return (insn >> start) & (1 << (length - 1)); }
+zuint32 get_insn_sub(zuint32 insn, int start, int length) { return (insn >> start) & ((1 << length) - 1); }
 
 zbool insn_equal(zuint32 insn, char *opstr) {
     zuint32 mask = 0, value = 0;
     zsize length = strlen(opstr);
-    for (int i = length - 1, j = 0; i >= 0 && j < length; i--, j++) {
+    int i, j;
+    for (i = length - 1, j = 0; i >= 0 && j < length; i--, j++) {
         if (opstr[i] == 'x') {
             mask = mask | (0 << j);
         } else if (opstr[i] == '0') {
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/instructions.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/instructions.h
index 78948cdcf..d16d377c5 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/instructions.h
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/instructions.h
@@ -26,6 +26,12 @@ typedef struct _ZzInstruction {
     zuint32 insn;
 } ZzInstruction;
 
+typedef struct _ZzRelocateInstruction {
+    const ZzInstruction *insn_ctx;
+    zaddr relocated_offset;
+    zsize relocated_length;
+} ZzRelocateInstruction;
+
 zuint32 get_insn_sub(zuint32 insn, int start, int length);
 zbool insn_equal(zuint32 insn, char *opstr);
 
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/reader-arm64.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/reader-arm64.c
index 28a548a83..37c3b4f9a 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/reader-arm64.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/reader-arm64.c
@@ -18,38 +18,7 @@
 #include "zzdeps/common/debugbreak.h"
 #include "zzdeps/zz.h"
 
-// static csh handle;
-
-// void zz_arm64_reader_capstone_init(void) {
-//     cs_err err = 0;
-
-//     err = cs_open(CS_ARCH_ARM6464, CS_MODE_ARM64, &handle);
-//     if (err) {
-//         Xerror("Failed on cs_open() with error returned: %u\n", err);
-//         exit(-1);
-//     }
-
-//     cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON);
-// }
-
-// cs_insn *zz_arm64_reader_disassemble_at(zpointer address) {
-//     if (!handle)
-//         zz_arm64_reader_capstone_init();
-//     cs_insn *insn;
-//     size_t count;
-//     count = cs_disasm(handle, address, 16, (unsigned long)address, 0, &insn);
-//     if (!insn) {
-// #if defined(DEBUG_MODE)
-//         debug_break();
-// #endif
-//         Xerror("zz_arm64_reader_disassemble_at error at %p", (zpointer)address);
-//     }
-//     return insn;
-// }
-
 zpointer zz_arm64_reader_read_one_instruction(ZzInstruction *insn_ctx, zpointer address) {
-    // ZzInstruction *insn = (ZzInstruction *)malloc(sizeof(ZzInstruction));
-
     insn_ctx->address = (zaddr)address;
     insn_ctx->size = 4;
     insn_ctx->pc = (zaddr)address;
@@ -58,9 +27,6 @@ zpointer zz_arm64_reader_read_one_instruction(ZzInstruction *insn_ctx, zpointer
 }
 
 ARM64InsnType GetARM64InsnType(zuint32 insn) {
-    zuint32 op, op1;
-    op1 = get_insn_sub(insn, 20, 5);
-
     // PAGE: C6-673
     if (insn_equal(insn, "01011000xxxxxxxxxxxxxxxxxxxxxxxx")) {
         return ARM64_INS_LDR_literal;
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/relocator-arm64.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/relocator-arm64.c
index ab613cd62..b64dbd8db 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/relocator-arm64.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/relocator-arm64.c
@@ -23,75 +23,43 @@
 void zz_arm64_relocator_init(ZzArm64Relocator *relocator, zpointer input_code, ZzArm64Writer *output) {
     relocator->inpos = 0;
     relocator->outpos = 0;
-
+    relocator->output = output;
     relocator->input_start = input_code;
     relocator->input_cur = input_code;
     relocator->input_pc = (zaddr)input_code;
-    relocator->input_insns = (ZzInstruction *)malloc(MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzInstruction));
+    relocator->relocate_literal_insns_size = 0;
+    relocator->try_relocated_length = 0;
 
+    relocator->input_insns = (ZzInstruction *)malloc(MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzInstruction));
     memset(relocator->input_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzInstruction));
-
-    relocator->output = output;
+    relocator->output_insns =
+        (ZzRelocateInstruction *)malloc(MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzRelocateInstruction));
+    memset(relocator->output_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzRelocateInstruction));
+    relocator->relocate_literal_insns =
+        (ZzLiteralInstruction **)malloc(MAX_LITERAL_INSN_SIZE * sizeof(ZzLiteralInstruction *));
+    memset(relocator->relocate_literal_insns, 0, MAX_LITERAL_INSN_SIZE * sizeof(ZzLiteralInstruction *));
 }
 
 void zz_arm64_relocator_reset(ZzArm64Relocator *self, zpointer input_code, ZzArm64Writer *output) {
     self->input_cur = input_code;
     self->input_start = input_code;
     self->input_pc = (zaddr)input_code;
-
     self->inpos = 0;
     self->outpos = 0;
-
     self->output = output;
-}
+    self->relocate_literal_insns_size = 0;
+    self->try_relocated_length = 0;
 
-// zsize zz_arm64_relocator_read_one(ZzArm64Relocator *self, ZzInstruction *instruction) {
-//     insn_cs **insn_ctx_ptr, *insn_cs;
-//     ZzInstruction insn_ctx = self->input_insns[self->inpos];
-//     insn_ctx_ptr = &insn_ctx.insn_cs;
-
-//     if (cs_disasm(self->capstone, self->input_cur, 4, self->input_pc, 1, insn_ctx_ptr) != 1) {
-//         return 0;
-//     }
-
-//     insn_cs = *insn_ctx_ptr;
-
-//     // zbool flag = TRUE;
-//     // switch (insn_cs->id) {
-//     // case ARM64_INS_B:
-//     //     if (branch_is_unconditional(ins))
-//     //         flag = relocator_rewrite_b(ins, relocate_writer);
-//     //     else
-//     //         flag = relocator_rewrite_b_cond(ins, relocate_writer);
-//     //     break;
-//     // case ARM64_INS_LDR:
-//     //     flag = relocator_rewrite_ldr(ins, relocate_writer);
-//     //     break;
-//     // case ARM64_INS_ADR:
-//     // case ARM64_INS_ADRP:
-//     //     flag = relocator_rewrite_adr(ins, relocate_writer);
-//     //     break;
-//     // case ARM64_INS_BL:
-//     //     flag = relocator_rewrite_bl(ins, relocate_writer);
-//     //     break;
-//     // default:
-//     //     zz_arm64_writer_put_bytes(relocate_writer, address, insn_cs->size);
-//     // }
-//     // if (!flag)
-//     //     zz_arm64_writer_put_bytes(relocate_writer, address, insn_cs->size);
-
-//     if (instruction != NULL)
-//         *instruction = insn_ctx;
-
-//     self->input_cur += insn_cs->size;
-//     self->input_pc += insn_cs->size;
-
-//     return self->input_cur - self->input_start;
-// }
+    memset(self->input_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzInstruction));
+    memset(self->output_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzRelocateInstruction));
+    memset(self->relocate_literal_insns, 0, MAX_LITERAL_INSN_SIZE * sizeof(ZzLiteralInstruction *));
+}
 
 zsize zz_arm64_relocator_read_one(ZzArm64Relocator *self, ZzInstruction *instruction) {
     ZzInstruction *insn_ctx = &self->input_insns[self->inpos];
+    ZzRelocateInstruction *re_insn_ctx = &self->output_insns[self->inpos];
 
+    re_insn_ctx->insn_ctx = insn_ctx;
     zz_arm64_reader_read_one_instruction(insn_ctx, self->input_cur);
 
     // switch (0) {}
@@ -99,7 +67,7 @@ zsize zz_arm64_relocator_read_one(ZzArm64Relocator *self, ZzInstruction *instruc
     self->inpos++;
 
     if (instruction != NULL)
-        instruction = insn_ctx;
+        *instruction = *insn_ctx;
 
     self->input_cur += insn_ctx->size;
     self->input_pc += insn_ctx->size;
@@ -107,174 +75,76 @@ zsize zz_arm64_relocator_read_one(ZzArm64Relocator *self, ZzInstruction *instruc
     return self->input_cur - self->input_start;
 }
 
+zaddr zz_arm64_relocator_get_insn_relocated_offset(ZzArm64Relocator *self, zaddr address) {
+    const ZzInstruction *insn_ctx;
+    const ZzRelocateInstruction *re_insn_ctx;
+    int i;
+    for (i = 0; i < self->inpos; i++) {
+        re_insn_ctx = &self->output_insns[i];
+        insn_ctx = re_insn_ctx->insn_ctx;
+        if (insn_ctx->address == address && re_insn_ctx->relocated_offset) {
+            return re_insn_ctx->relocated_offset;
+        }
+    }
+    return 0;
+}
+
+void zz_arm64_relocator_relocate_writer(ZzArm64Relocator *relocator, zaddr code_address) {
+    ZzArm64Writer *arm64_writer;
+    arm64_writer = relocator->output;
+    if (relocator->relocate_literal_insns_size) {
+        int i;
+        zaddr *rebase_ptr;
+        zaddr literal_address, relocated_offset, relocated_address, *literal_address_ptr;
+        for (i = 0; i < relocator->relocate_literal_insns_size; i++) {
+            literal_address_ptr = relocator->relocate_literal_insns[i]->literal_address_ptr;
+            literal_address = *literal_address_ptr;
+            relocated_offset = zz_arm64_relocator_get_insn_relocated_offset(relocator, literal_address);
+            if (relocated_offset) {
+                relocated_address = code_address + relocated_offset;
+                *literal_address_ptr = relocated_address;
+            }
+        }
+    }
+}
+
 void zz_arm64_relocator_write_all(ZzArm64Relocator *self) {
     zuint count = 0;
+    zuint outpos = self->outpos;
+    ZzArm64Writer arm64_writer = *self->output;
+
     while (zz_arm64_relocator_write_one(self))
         count++;
 }
 
 void zz_arm64_relocator_try_relocate(zpointer address, zuint min_bytes, zuint *max_bytes) {
-    *max_bytes = 16;
+    int tmp_size = 0;
+    zpointer target_addr;
+    ZzInstruction insn_ctx;
+    zbool early_end = FALSE;
+    target_addr = (zpointer)address;
+
+    do {
+        zz_arm64_reader_read_one_instruction(&insn_ctx, target_addr);
+        switch (GetARM64InsnType(insn_ctx.insn)) {
+        case ARM64_INS_B:
+            early_end = TRUE;
+            break;
+        default:;
+        }
+        tmp_size += insn_ctx.size;
+        target_addr = target_addr + insn_ctx.size;
+    } while (tmp_size < min_bytes);
+
+    if (early_end) {
+        *max_bytes = tmp_size;
+    }
     return;
 }
 
-// static zbool zz_arm64_branch_is_unconditional(const cs_insn *insn) {
-//     switch (insn->detail->arm64.cc) {
-//     case ARM64_CC_INVALID:
-//     case ARM64_CC_AL:
-//     case ARM64_CC_NV:
-//         return TRUE;
-//     default:
-//         return FALSE;
-//     }
-// }
-
-// static zbool zz_arm64_relocator_rewrite_ldr(ZzArm64Relocator *self, ZzInstruction *insn_ctx) {
-//     const cs_arm64_op *dst = &insn_ctx->detail->operands[0];
-//     const cs_arm64_op *src = &insn_ctx->detail->operands[1];
-//     zbool dst_reg_is_fp_or_simd;
-//     ZzARM64Reg tmp_reg;
-
-//     (void)self;
-
-//     if (src->type != ARM64_OP_IMM)
-//         return FALSE;
-
-//     dst_reg_is_fp_or_simd = (dst->reg >= ARM64_REG_S0 && dst->reg <= ARM64_REG_S31) ||
-//                             (dst->reg >= ARM64_REG_D0 && dst->reg <= ARM64_REG_D31) ||
-//                             (dst->reg >= ARM64_REG_Q0 && dst->reg <= ARM64_REG_Q31);
-//     if (dst_reg_is_fp_or_simd) {
-// #if defined(DEBUG_MODE)
-//         debug_break();
-// #endif
-//     } else {
-//         if (dst->reg >= ARM64_REG_W0 && dst->reg <= ARM64_REG_W28)
-//             tmp_reg = ZZ_ARM64_REG_X0 + (dst->reg - ARM64_REG_W0);
-//         else if (dst->reg >= ARM64_REG_W29 && dst->reg <= ARM64_REG_W30)
-//             tmp_reg = ZZ_ARM64_REG_X29 + (dst->reg - ARM64_REG_W29);
-//         else
-//             tmp_reg = dst->reg;
-
-//         zz_arm64_writer_put_ldr_b_reg_address(self->output, tmp_reg, src->imm);
-//         zz_arm64_writer_put_ldr_reg_reg_offset(self->output, dst->reg, tmp_reg, 0);
-//     }
-
-//     return TRUE;
-// }
-
-// static zbool zz_arm64_relocator_rewrite_adr(ZzArm64Relocator *self, ZzInstruction *insn_ctx) {
-//     const cs_arm64_op *dst = &insn_ctx->detail->operands[0];
-//     const cs_arm64_op *label = &insn_ctx->detail->operands[1];
-
-//     zz_arm64_writer_put_ldr_b_reg_address(self->output, dst->reg, label->imm);
-//     return TRUE;
-// }
-
-// static zbool zz_arm64_relocator_rewrite_b(ZzArm64Relocator *self, ZzInstruction *insn_ctx) {
-//     const cs_arm64_op *target = &insn_ctx->detail->operands[0];
-
-//     zz_arm64_writer_put_ldr_b_reg_address(self->output, ZZ_ARM64_REG_X17, target->imm);
-
-//     return TRUE;
-// }
-
-// static zbool zz_arm64_relocator_rewrite_b_cond(ZzArm64Relocator *self, ZzInstruction *insn_ctx) {
-//     const cs_arm64_op *target = &insn_ctx->detail->operands[0];
-
-//     zz_arm64_writer_put_b_cond_imm(self->output, insn_ctx->detail->cc, 0x8);
-//     zz_arm64_writer_put_b_imm(self->output, 0x4 + 0x14);
-
-//     zz_arm64_writer_put_ldr_br_reg_address(self->output, ZZ_ARM64_REG_X17, target->imm);
-
-//     return TRUE;
-// }
-
-// static zbool zz_arm64_relocator_rewrite_bl(ZzArm64Relocator *self, ZzInstruction *insn_ctx) {
-//     const cs_arm64_op *target = &insn_ctx->detail->operands[0];
-
-//     zz_arm64_writer_put_ldr_br_reg_address(self->output, ZZ_ARM64_REG_LR, target->imm);
-
-//     return TRUE;
-// }
-
-// zbool relocator_rewrite_ldr(ZzInstruction *ins, ZzArm64Writer *relocate_writer)
-// {
-//     cs_arm64 ins_csd = ins->insn_cs->detail->arm64;
-//     const cs_arm64_op *dst = &ins_csd.operands[0];
-//     const cs_arm64_op *src = &ins_csd.operands[1];
-//     if (src->type != ARM64_OP_IMM)
-//         return FALSE;
-//     return TRUE;
-// }
-
-// zbool relocator_rewrite_b(ZzInstruction *ins, ZzArm64Writer *relocate_writer) {
-//     cs_arm64 ins_csd = ins->insn_cs->detail->arm64;
-//     zaddr target_addr = ins_csd.operands[0].imm;
-
-//     // zz_arm64_writer_put_ldr_br_b_reg_address(relocate_writer,
-//     ZZ_ARM64_REG_X17,
-//     // target_addr);
-//     zz_arm64_writer_put_ldr_reg_address(relocate_writer, ZZ_ARM64_REG_X17,
-//                                         target_addr);
-//     zz_arm64_writer_put_br_reg(relocate_writer, ZZ_ARM64_REG_X17);
-//     return TRUE;
-// }
-
-// zbool relocator_rewrite_bl(ZzInstruction *ins, ZzArm64Writer *relocate_writer)
-// {
-//     cs_arm64 ins_csd = ins->insn_cs->detail->arm64;
-//     zaddr target_addr = ins_csd.operands[0].imm;
-
-//     zz_arm64_writer_put_ldr_reg_address(relocate_writer, ZZ_ARM64_REG_X17,
-//                                         target_addr);
-//     zz_arm64_writer_put_blr_reg(relocate_writer, ZZ_ARM64_REG_X17);
-//     return TRUE;
-// }
-
-// /*
-//     origin:
-//         1. j.eq [3]
-
-//         2. [...]
-//         3. [...]
-
-//     rwrite:
-//         1. j.eq [1.2]
-//         1.1 b [2]
-//         1.2 abs_jmp [3]
-
-//         2. [...]
-//         3. [...]
-//  */
-// zbool relocator_rewrite_b_cond(ZzInstruction *ins,
-//                                ZzArm64Writer *relocate_writer) {
-//     cs_arm64 ins_csd = ins->insn_cs->detail->arm64;
-//     zaddr target_addr = ins_csd.operands[0].imm;
-
-//     zz_arm64_writer_put_b_cond_imm(relocate_writer, ins_csd.cc, 0x8);
-//     zz_arm64_writer_put_b_imm(relocate_writer, 0x4 + 0x14);
-
-//     // zz_arm64_writer_put_ldr_br_b_reg_address(relocate_writer,
-//     ZZ_ARM64_REG_X17,
-//     // target_addr);
-//     zz_arm64_writer_put_ldr_reg_address(relocate_writer, ZZ_ARM64_REG_X17,
-//                                         target_addr);
-//     zz_arm64_writer_put_br_reg(relocate_writer, ZZ_ARM64_REG_X17);
-//     return TRUE;
-// }
-
-// zbool relocator_rewrite_adr(ZzInstruction *ins, ZzArm64Writer *relocate_writer)
-// {
-//     cs_arm64 ins_csd = ins->insn_cs->detail->arm64;
-
-//     const cs_arm64_op dst = ins_csd.operands[0];
-//     const cs_arm64_op label = ins_csd.operands[1];
-//     zz_arm64_writer_put_ldr_reg_address(relocate_writer, dst.reg, label.imm);
-//     return TRUE;
-// }
-
 // PAGE: C6-673
-static zbool zz_arm64_relocator_rewrite_LDR_literal(ZzArm64Relocator *self, ZzInstruction *insn_ctx) {
+static zbool zz_arm64_relocator_rewrite_LDR_literal(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,
+                                                    ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn = insn_ctx->insn;
     // TODO: check opc == 10, with signed
     zuint32 imm19 = get_insn_sub(insn, 5, 19);
@@ -286,11 +156,13 @@ static zbool zz_arm64_relocator_rewrite_LDR_literal(ZzArm64Relocator *self, ZzIn
 
     zz_arm64_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);
     zz_arm64_writer_put_ldr_reg_reg_offset(self->output, Rt_ndx, Rt_ndx, 0);
+
     return TRUE;
 }
 
 // PAGE: C6-535
-static zbool zz_arm64_relocator_rewrite_ADR(ZzArm64Relocator *self, ZzInstruction *insn_ctx) {
+static zbool zz_arm64_relocator_rewrite_ADR(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,
+                                            ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn = insn_ctx->insn;
     zuint32 immhi = get_insn_sub(insn, 5, 19);
     zuint32 immlo = get_insn_sub(insn, 29, 2);
@@ -301,11 +173,13 @@ static zbool zz_arm64_relocator_rewrite_ADR(ZzArm64Relocator *self, ZzInstructio
     int Rt_ndx = get_insn_sub(insn, 0, 4);
 
     zz_arm64_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);
+
     return TRUE;
 }
 
 // PAGE: C6-536
-static zbool zz_arm64_relocator_rewrite_ADRP(ZzArm64Relocator *self, ZzInstruction *insn_ctx) {
+static zbool zz_arm64_relocator_rewrite_ADRP(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,
+                                             ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn = insn_ctx->insn;
     zuint32 immhi = get_insn_sub(insn, 5, 19);
     zuint32 immlo = get_insn_sub(insn, 29, 2);
@@ -313,15 +187,17 @@ static zbool zz_arm64_relocator_rewrite_ADRP(ZzArm64Relocator *self, ZzInstructi
     zuint64 imm = immhi << 2 << 12 | immlo << 12;
 
     zaddr target_address;
-    target_address = (insn_ctx->pc & (1 << 12)) + imm;
+    target_address = (insn_ctx->pc & 0xFFFFFFFFFFFFF000) + imm;
     int Rt_ndx = get_insn_sub(insn, 0, 4);
 
     zz_arm64_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);
+
     return TRUE;
 }
 
 // PAGE: C6-550
-static zbool zz_arm64_relocator_rewrite_B(ZzArm64Relocator *self, ZzInstruction *insn_ctx) {
+static zbool zz_arm64_relocator_rewrite_B(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,
+                                          ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn = insn_ctx->insn;
     zuint32 imm26 = get_insn_sub(insn, 0, 26);
 
@@ -329,14 +205,15 @@ static zbool zz_arm64_relocator_rewrite_B(ZzArm64Relocator *self, ZzInstruction
 
     zaddr target_address;
     target_address = insn_ctx->pc + offset;
-    int Rt_ndx = get_insn_sub(insn, 0, 4);
 
     zz_arm64_writer_put_ldr_br_reg_address(self->output, ZZ_ARM64_REG_X17, target_address);
+
     return TRUE;
 }
 
 // PAGE: C6-560
-static zbool zz_arm64_relocator_rewrite_BL(ZzArm64Relocator *self, ZzInstruction *insn_ctx) {
+static zbool zz_arm64_relocator_rewrite_BL(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,
+                                           ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn = insn_ctx->insn;
     zuint32 imm26 = get_insn_sub(insn, 0, 26);
 
@@ -344,30 +221,28 @@ static zbool zz_arm64_relocator_rewrite_BL(ZzArm64Relocator *self, ZzInstruction
 
     zaddr target_address;
     target_address = insn_ctx->pc + offset;
-    int Rt_ndx = get_insn_sub(insn, 0, 4);
 
     zz_arm64_writer_put_ldr_blr_b_reg_address(self->output, ZZ_ARM64_REG_X17, target_address);
+    ZzLiteralInstruction **literal_insn_ptr = &(self->relocate_literal_insns[self->relocate_literal_insns_size++]);
+    zz_arm64_writer_put_ldr_br_reg_relocate_address(self->output, ZZ_ARM64_REG_X17, insn_ctx->pc + 4, literal_insn_ptr);
+
     return TRUE;
 }
 
-/*
-    origin:
-        1. j.eq [3]
+// 0x000 : b.cond 0x8;
 
-        2. [...]
-        3. [...]
+// 0x004 : b 0x14
 
-    rwrite:
-        1. j.eq [1.2]
-        1.1 b [2]
-        1.2 abs_jmp [3]
+// 0x008 : ldr x17, [pc, #4]
+// 0x00c : br x17
+// 0x010 : .long 0x0
+// 0x014 : .long 0x0
 
-        2. [...]
-        3. [...]
- */
+// 0x018 : remain code
 
 // PAGE: C6-549
-static zbool zz_arm64_relocator_rewrite_B_cond(ZzArm64Relocator *self, ZzInstruction *insn_ctx) {
+static zbool zz_arm64_relocator_rewrite_B_cond(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,
+                                               ZzRelocateInstruction *re_insn_ctx) {
     zuint32 insn = insn_ctx->insn;
     zuint32 imm19 = get_insn_sub(insn, 5, 19);
 
@@ -379,42 +254,45 @@ static zbool zz_arm64_relocator_rewrite_B_cond(ZzArm64Relocator *self, ZzInstruc
     zuint32 cond = get_insn_sub(insn, 0, 4);
 
     zz_arm64_writer_put_b_cond_imm(self->output, cond, 0x8);
-    zz_arm64_writer_put_b_imm(self->output, 0xc);
+    zz_arm64_writer_put_b_imm(self->output, 0x14);
     zz_arm64_writer_put_ldr_br_reg_address(self->output, ZZ_ARM64_REG_X17, target_address);
 
     return TRUE;
 }
 
 zbool zz_arm64_relocator_write_one(ZzArm64Relocator *self) {
-    ZzInstruction *insn_ctx;
+    const ZzInstruction *insn_ctx;
+    ZzRelocateInstruction *re_insn_ctx;
+
     zbool rewritten = FALSE;
 
     if (self->inpos != self->outpos) {
         insn_ctx = &self->input_insns[self->outpos];
+        re_insn_ctx = &self->output_insns[self->outpos];
         self->outpos++;
     } else
         return FALSE;
 
-    insn_ctx->pc = insn_ctx->pc;
+    re_insn_ctx->relocated_offset = (zaddr)self->output->pc - (zaddr)self->output->base;
 
     switch (GetARM64InsnType(insn_ctx->insn)) {
     case ARM64_INS_LDR_literal:
-        rewritten = zz_arm64_relocator_rewrite_LDR_literal(self, insn_ctx);
+        rewritten = zz_arm64_relocator_rewrite_LDR_literal(self, insn_ctx, re_insn_ctx);
         break;
     case ARM64_INS_ADR:
-        rewritten = zz_arm64_relocator_rewrite_ADR(self, insn_ctx);
+        rewritten = zz_arm64_relocator_rewrite_ADR(self, insn_ctx, re_insn_ctx);
         break;
     case ARM64_INS_ADRP:
-        rewritten = zz_arm64_relocator_rewrite_ADRP(self, insn_ctx);
+        rewritten = zz_arm64_relocator_rewrite_ADRP(self, insn_ctx, re_insn_ctx);
         break;
     case ARM64_INS_B:
-        rewritten = zz_arm64_relocator_rewrite_B(self, insn_ctx);
+        rewritten = zz_arm64_relocator_rewrite_B(self, insn_ctx, re_insn_ctx);
         break;
     case ARM64_INS_BL:
-        rewritten = zz_arm64_relocator_rewrite_BL(self, insn_ctx);
+        rewritten = zz_arm64_relocator_rewrite_BL(self, insn_ctx, re_insn_ctx);
         break;
     case ARM64_INS_B_cond:
-        rewritten = zz_arm64_relocator_rewrite_B_cond(self, insn_ctx);
+        rewritten = zz_arm64_relocator_rewrite_B_cond(self, insn_ctx, re_insn_ctx);
         break;
     default:
         rewritten = FALSE;
@@ -422,5 +300,7 @@ zbool zz_arm64_relocator_write_one(ZzArm64Relocator *self) {
     }
     if (!rewritten)
         zz_arm64_writer_put_bytes(self->output, (zbyte *)&insn_ctx->insn, insn_ctx->size);
+    re_insn_ctx->relocated_length =
+        (zaddr)self->output->pc - (zaddr)self->output->base - (zaddr)re_insn_ctx->relocated_offset;
     return TRUE;
 }
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/relocator-arm64.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/relocator-arm64.h
index 155bdf53e..e0d8824e1 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/relocator-arm64.h
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/relocator-arm64.h
@@ -33,14 +33,18 @@
 #include "zzdeps/zz.h"
 
 typedef struct _ZzArm64Relocator {
+    zbool try_relocated_again;
+    zsize try_relocated_length;
     zpointer input_start;
     zpointer input_cur;
     zaddr input_pc;
-    ZzInstruction *input_insns;
-    ZzArm64Writer *output;
-
     zuint inpos;
     zuint outpos;
+    ZzInstruction *input_insns;
+    ZzRelocateInstruction *output_insns;
+    ZzArm64Writer *output;
+    ZzLiteralInstruction **relocate_literal_insns;
+    zsize relocate_literal_insns_size;
 } ZzArm64Relocator;
 
 void zz_arm64_relocator_init(ZzArm64Relocator *relocator, zpointer input_code, ZzArm64Writer *writer);
@@ -52,9 +56,14 @@ void zz_arm64_relocator_write_all(ZzArm64Relocator *self);
 void zz_arm64_relocator_try_relocate(zpointer address, zuint min_bytes, zuint *max_bytes);
 
 /* rewrite */
-static zbool zz_arm64_relocator_rewrite_ldr(ZzArm64Relocator *self, ZzInstruction *insn_ctx);
-static zbool zz_arm64_relocator_rewrite_adr(ZzArm64Relocator *self, ZzInstruction *insn_ctx);
-static zbool zz_arm64_relocator_rewrite_b(ZzArm64Relocator *self, ZzInstruction *insn_ctx);
-static zbool zz_arm64_relocator_rewrite_b_cond(ZzArm64Relocator *self, ZzInstruction *insn_ctx);
-static zbool zz_arm64_relocator_rewrite_bl(ZzArm64Relocator *self, ZzInstruction *insn_ctx);
+static zbool zz_arm64_relocator_rewrite_ldr(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,
+                                            ZzRelocateInstruction *re_insn_ctx);
+static zbool zz_arm64_relocator_rewrite_adr(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,
+                                            ZzRelocateInstruction *re_insn_ctx);
+static zbool zz_arm64_relocator_rewrite_b(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,
+                                          ZzRelocateInstruction *re_insn_ctx);
+static zbool zz_arm64_relocator_rewrite_b_cond(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,
+                                               ZzRelocateInstruction *re_insn_ctx);
+static zbool zz_arm64_relocator_rewrite_bl(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,
+                                           ZzRelocateInstruction *re_insn_ctx);
 #endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/writer-arm64.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/writer-arm64.c
index 35c4a898d..ae1ec1353 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/writer-arm64.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/writer-arm64.c
@@ -30,59 +30,86 @@
 
 ZzArm64Writer *zz_arm64_writer_new(zpointer data_ptr) {
     ZzArm64Writer *writer = (ZzArm64Writer *)malloc(sizeof(ZzArm64Writer));
-    int t = 4 - (zaddr)data_ptr % 4;
+    memset(writer, 0, sizeof(ZzArm64Writer));
 
-    writer->codedata = data_ptr + t;
-    writer->base = data_ptr + t;
-    writer->pc = data_ptr + t;
+    zaddr align_address = (zaddr)data_ptr & ~(zaddr)3;
+    writer->codedata = (zpointer)align_address;
+    writer->base = (zpointer)align_address;
+    writer->pc = align_address;
     writer->size = 0;
+
+    writer->literal_insn_size = 0;
+    memset(writer->literal_insns, 0, sizeof(ZzLiteralInstruction) * MAX_LITERAL_INSN_SIZE);
+
     return writer;
 }
 
 void zz_arm64_writer_init(ZzArm64Writer *self, zpointer target_addr) { zz_arm64_writer_reset(self, target_addr); }
 
 void zz_arm64_writer_reset(ZzArm64Writer *self, zpointer data_ptr) {
-    int t = (zaddr)data_ptr % 4;
+    zaddr align_address = (zaddr)data_ptr & ~(zaddr)3;
 
-    self->codedata = data_ptr + t;
-    self->base = data_ptr + t;
-    self->pc = data_ptr + t;
+    self->codedata = (zpointer)align_address;
+    self->base = (zpointer)align_address;
+    self->pc = align_address;
     self->size = 0;
+
+    self->literal_insn_size = 0;
+    memset(self->literal_insns, 0, sizeof(ZzLiteralInstruction) * MAX_LITERAL_INSN_SIZE);
+}
+
+// ======= relocator =======
+
+ZzLiteralInstruction *zz_arm64_writer_put_ldr_br_reg_relocate_address(ZzWriter *self, ZzARM64Reg reg, zaddr address,
+                                                                      ZzLiteralInstruction **literal_insn_ptr) {
+
+    zz_arm64_writer_put_ldr_br_reg_address(self, reg, address);
+    ZzLiteralInstruction *literal_insn = &(self->literal_insns[self->literal_insn_size - 1]);
+    *literal_insn_ptr = literal_insn;
+    return literal_insn;
 }
 
 // ======= user custom =======
 
 void zz_arm64_writer_put_ldr_br_reg_address(ZzWriter *self, ZzARM64Reg reg, zaddr address) {
+    self->literal_insns[self->literal_insn_size].literal_insn_ptr = self->codedata;
     zz_arm64_writer_put_ldr_reg_imm(self, reg, (zuint)0x8);
     zz_arm64_writer_put_br_reg(self, reg);
+    self->literal_insns[self->literal_insn_size++].literal_address_ptr = self->codedata;
     zz_arm64_writer_put_bytes(self, (zpointer)&address, sizeof(zpointer));
 }
 
 void zz_arm64_writer_put_ldr_blr_b_reg_address(ZzWriter *self, ZzARM64Reg reg, zaddr address) {
+    self->literal_insns[self->literal_insn_size].literal_insn_ptr = self->codedata;
     zz_arm64_writer_put_ldr_reg_imm(self, reg, (zuint)0xc);
     zz_arm64_writer_put_blr_reg(self, reg);
     zz_arm64_writer_put_b_imm(self, 0xc);
+    self->literal_insns[self->literal_insn_size++].literal_address_ptr = self->codedata;
     zz_arm64_writer_put_bytes(self, (zpointer)&address, sizeof(zpointer));
 }
 
 void zz_arm64_writer_put_ldr_b_reg_address(ZzWriter *self, ZzARM64Reg reg, zaddr address) {
+    self->literal_insns[self->literal_insn_size].literal_insn_ptr = self->codedata;
     zz_arm64_writer_put_ldr_reg_imm(self, reg, (zuint)0x8);
     zz_arm64_writer_put_b_imm(self, 0xc);
+    self->literal_insns[self->literal_insn_size++].literal_address_ptr = self->codedata;
     zz_arm64_writer_put_bytes(self, (zpointer)&address, sizeof(address));
 }
 
 zsize zz_arm64_writer_near_jump_range_size() { return ((1 << 25) << 2); }
 
 void zz_arm64_writer_put_ldr_br_b_reg_address(ZzWriter *self, ZzARM64Reg reg, zaddr address) {
+    self->literal_insns[self->literal_insn_size].literal_insn_ptr = self->codedata;
     zz_arm64_writer_put_ldr_reg_imm(self, reg, (zuint)0xc);
     zz_arm64_writer_put_br_reg(self, reg);
     zz_arm64_writer_put_b_imm(self, 0xc);
+    self->literal_insns[self->literal_insn_size++].literal_address_ptr = self->codedata;
     zz_arm64_writer_put_bytes(self, (zpointer)&address, sizeof(address));
 }
 
 // ======= default =======
 
-zpointer zz_arm64_writer_put_ldr_reg_imm(ZzWriter *self, ZzARM64Reg reg, zuint32 offset) {
+void zz_arm64_writer_put_ldr_reg_imm(ZzWriter *self, ZzARM64Reg reg, zuint32 offset) {
     ZzArm64RegInfo ri;
     zz_arm64_register_describe(reg, &ri);
 
@@ -92,12 +119,11 @@ zpointer zz_arm64_writer_put_ldr_reg_imm(ZzWriter *self, ZzARM64Reg reg, zuint32
     Rt_ndx = ri.index;
 
     zz_arm64_writer_put_instruction(self, 0x58000000 | imm19 << 5 | Rt_ndx);
-    return self->pc;
+    return;
 }
 
 // PAGE: C6-871
-zpointer zz_arm64_writer_put_str_reg_reg_offset(ZzWriter *self, ZzARM64Reg src_reg, ZzARM64Reg dst_reg,
-                                                zuint64 offset) {
+void zz_arm64_writer_put_str_reg_reg_offset(ZzWriter *self, ZzARM64Reg src_reg, ZzARM64Reg dst_reg, zuint64 offset) {
     ZzArm64RegInfo rs, rd;
 
     zz_arm64_register_describe(src_reg, &rs);
@@ -114,11 +140,10 @@ zpointer zz_arm64_writer_put_str_reg_reg_offset(ZzWriter *self, ZzARM64Reg src_r
     zuint32 imm12 = offset >> size;
 
     zz_arm64_writer_put_instruction(self, 0x39000000 | size << 30 | opc << 22 | imm12 << 10 | Rn_ndx << 5 | Rt_ndx);
-    return self->pc;
+    return;
 }
 
-zpointer zz_arm64_writer_put_ldr_reg_reg_offset(ZzWriter *self, ZzARM64Reg dst_reg, ZzARM64Reg src_reg,
-                                                zuint64 offset) {
+void zz_arm64_writer_put_ldr_reg_reg_offset(ZzWriter *self, ZzARM64Reg dst_reg, ZzARM64Reg src_reg, zuint64 offset) {
     ZzArm64RegInfo rs, rd;
 
     zz_arm64_register_describe(src_reg, &rs);
@@ -135,22 +160,22 @@ zpointer zz_arm64_writer_put_ldr_reg_reg_offset(ZzWriter *self, ZzARM64Reg dst_r
     zuint32 imm12 = offset >> size;
 
     zz_arm64_writer_put_instruction(self, 0x39000000 | size << 30 | opc << 22 | imm12 << 10 | Rn_ndx << 5 | Rt_ndx);
-    return self->pc;
+    return;
 }
 
 // C6-562
-zpointer zz_arm64_writer_put_br_reg(ZzWriter *self, ZzARM64Reg reg) {
+void zz_arm64_writer_put_br_reg(ZzWriter *self, ZzARM64Reg reg) {
     ZzArm64RegInfo ri;
     zz_arm64_register_describe(reg, &ri);
 
     zuint32 op = 0, Rn_ndx;
     Rn_ndx = ri.index;
     zz_arm64_writer_put_instruction(self, 0xd61f0000 | op << 21 | Rn_ndx << 5);
-    return self->pc;
+    return;
 }
 
 // C6-561
-zpointer zz_arm64_writer_put_blr_reg(ZzWriter *self, ZzARM64Reg reg) {
+void zz_arm64_writer_put_blr_reg(ZzWriter *self, ZzARM64Reg reg) {
     ZzArm64RegInfo ri;
     zz_arm64_register_describe(reg, &ri);
 
@@ -159,29 +184,29 @@ zpointer zz_arm64_writer_put_blr_reg(ZzWriter *self, ZzARM64Reg reg) {
     Rn_ndx = ri.index;
 
     zz_arm64_writer_put_instruction(self, 0xd63f0000 | op << 21 | Rn_ndx << 5);
-    return self->pc;
+    return;
 }
 
 // C6-550
-zpointer zz_arm64_writer_put_b_imm(ZzWriter *self, zuint64 offset) {
+void zz_arm64_writer_put_b_imm(ZzWriter *self, zuint64 offset) {
     zuint32 op = 0b0, imm26;
     imm26 = (offset >> 2) & 0x03ffffff;
     zz_arm64_writer_put_instruction(self, 0x14000000 | op << 31 | imm26);
-    return self->pc;
+    return;
 }
 
 // TODO: standard form, need fix others
 // PAGE: C6-549
-zpointer zz_arm64_writer_put_b_cond_imm(ZzWriter *self, zuint32 condition, zuint64 imm) {
+void zz_arm64_writer_put_b_cond_imm(ZzWriter *self, zuint32 condition, zuint64 imm) {
     zuint32 imm19, cond;
     cond = condition;
     imm19 = (imm >> 2) & 0x7ffff;
     zz_arm64_writer_put_instruction(self, 0x54000000 | imm19 << 5 | cond);
-    return self->pc;
+    return;
 }
 
 // C6-525
-zpointer zz_arm64_writer_put_add_reg_reg_imm(ZzWriter *self, ZzARM64Reg dst_reg, ZzARM64Reg left_reg, zuint64 imm) {
+void zz_arm64_writer_put_add_reg_reg_imm(ZzWriter *self, ZzARM64Reg dst_reg, ZzARM64Reg left_reg, zuint64 imm) {
     ZzArm64RegInfo rd, rl;
 
     zz_arm64_register_describe(dst_reg, &rd);
@@ -195,11 +220,11 @@ zpointer zz_arm64_writer_put_add_reg_reg_imm(ZzWriter *self, ZzARM64Reg dst_reg,
 
     zz_arm64_writer_put_instruction(self, 0x11000000 | sf << 31 | op << 30 | S << 29 | shift << 22 | imm12 << 10 |
                                               Rn_ndx << 5 | Rd_ndx);
-    return self->pc;
+    return;
 }
 
 // C6-930
-zpointer zz_arm64_writer_put_sub_reg_reg_imm(ZzWriter *self, ZzARM64Reg dst_reg, ZzARM64Reg left_reg, zuint64 imm) {
+void zz_arm64_writer_put_sub_reg_reg_imm(ZzWriter *self, ZzARM64Reg dst_reg, ZzARM64Reg left_reg, zuint64 imm) {
     ZzArm64RegInfo rd, rl;
 
     zz_arm64_register_describe(dst_reg, &rd);
@@ -213,21 +238,21 @@ zpointer zz_arm64_writer_put_sub_reg_reg_imm(ZzWriter *self, ZzARM64Reg dst_reg,
 
     zz_arm64_writer_put_instruction(self, 0x11000000 | sf << 31 | op << 30 | S << 29 | shift << 22 | imm12 << 10 |
                                               Rn_ndx << 5 | Rd_ndx);
-    return self->pc;
+    return;
 }
 
-zpointer zz_arm64_writer_put_bytes(ZzWriter *self, zbyte *data, zsize size) {
+void zz_arm64_writer_put_bytes(ZzWriter *self, zbyte *data, zsize size) {
     memcpy(self->codedata, data, size);
     self->codedata = (zpointer)self->codedata + size;
     self->pc += size;
     self->size += size;
-    return self->pc;
+    return;
 }
 
-zpointer zz_arm64_writer_put_instruction(ZzWriter *self, zuint32 insn) {
+void zz_arm64_writer_put_instruction(ZzWriter *self, zuint32 insn) {
     *(zuint32 *)(self->codedata) = insn;
     self->codedata = (zpointer)self->codedata + sizeof(zuint32);
     self->pc += 4;
     self->size += 4;
-    return self->pc;
+    return;
 }
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/writer-arm64.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/writer-arm64.h
index 009639f39..2b7370fbd 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/writer-arm64.h
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/writer-arm64.h
@@ -50,16 +50,21 @@ void zz_arm64_writer_put_ldr_br_b_reg_address(ZzArm64Writer *self, ZzARM64Reg re
 
 // ======= default =======
 
-zpointer zz_arm64_writer_put_ldr_reg_imm(ZzWriter *self, ZzARM64Reg reg, zuint32 offset);
-zpointer zz_arm64_writer_put_str_reg_reg_offset(ZzWriter *self, ZzARM64Reg src_reg, ZzARM64Reg dst_reg, zuint64 offset);
-zpointer zz_arm64_writer_put_ldr_reg_reg_offset(ZzWriter *self, ZzARM64Reg dst_reg, ZzARM64Reg src_reg, zuint64 offset);
-zpointer zz_arm64_writer_put_br_reg(ZzWriter *self, ZzARM64Reg reg);
-zpointer zz_arm64_writer_put_blr_reg(ZzWriter *self, ZzARM64Reg reg);
-zpointer zz_arm64_writer_put_b_imm(ZzWriter *self, zuint64 offset);
-zpointer zz_arm64_writer_put_b_cond_imm(ZzWriter *self, zuint32 condition, zuint64 imm);
-zpointer zz_arm64_writer_put_add_reg_reg_imm(ZzWriter *self, ZzARM64Reg dst_reg, ZzARM64Reg left_reg, zuint64 imm);
-zpointer zz_arm64_writer_put_sub_reg_reg_imm(ZzWriter *self, ZzARM64Reg dst_reg, ZzARM64Reg left_reg, zuint64 imm);
-zpointer zz_arm64_writer_put_bytes(ZzWriter *self, zbyte *data, zsize size);
-zpointer zz_arm64_writer_put_instruction(ZzWriter *self, zuint32 insn);
+void zz_arm64_writer_put_ldr_reg_imm(ZzWriter *self, ZzARM64Reg reg, zuint32 offset);
+void zz_arm64_writer_put_str_reg_reg_offset(ZzWriter *self, ZzARM64Reg src_reg, ZzARM64Reg dst_reg, zuint64 offset);
+void zz_arm64_writer_put_ldr_reg_reg_offset(ZzWriter *self, ZzARM64Reg dst_reg, ZzARM64Reg src_reg, zuint64 offset);
+void zz_arm64_writer_put_br_reg(ZzWriter *self, ZzARM64Reg reg);
+void zz_arm64_writer_put_blr_reg(ZzWriter *self, ZzARM64Reg reg);
+void zz_arm64_writer_put_b_imm(ZzWriter *self, zuint64 offset);
+void zz_arm64_writer_put_b_cond_imm(ZzWriter *self, zuint32 condition, zuint64 imm);
+void zz_arm64_writer_put_add_reg_reg_imm(ZzWriter *self, ZzARM64Reg dst_reg, ZzARM64Reg left_reg, zuint64 imm);
+void zz_arm64_writer_put_sub_reg_reg_imm(ZzWriter *self, ZzARM64Reg dst_reg, ZzARM64Reg left_reg, zuint64 imm);
+void zz_arm64_writer_put_bytes(ZzWriter *self, zbyte *data, zsize size);
+void zz_arm64_writer_put_instruction(ZzWriter *self, zuint32 insn);
+
+// ======= relocator =======
+
+ZzLiteralInstruction *zz_arm64_writer_put_ldr_br_reg_relocate_address(ZzWriter *self, ZzARM64Reg reg, zaddr address,
+                                                                      ZzLiteralInstruction **literal_insn_ptr);
 
 #endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/instructions.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/instructions.c
new file mode 100644
index 000000000..dc09a0d61
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/instructions.c
@@ -0,0 +1,2 @@
+#include "instructions.h"
+#include <string.h>
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/instructions.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/instructions.h
new file mode 100644
index 000000000..adc4675f8
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/instructions.h
@@ -0,0 +1,31 @@
+/**
+ *    Copyright 2017 jmpews
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef platforms_arch_x86_instructions_h
+#define platforms_arch_x86_instructions_h
+
+#include "hookzz.h"
+
+typedef struct _ZzInstruction {
+} ZzInstruction;
+
+typedef struct _ZzRelocateInstruction {
+    const ZzInstruction *insn_ctx;
+    zaddr relocated_offset;
+    zsize relocated_length;
+} ZzRelocateInstruction;
+
+#endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/reader-x86.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/reader-x86.c
new file mode 100644
index 000000000..8185d3ed0
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/reader-x86.c
@@ -0,0 +1,23 @@
+/**
+ *    Copyright 2017 jmpews
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include "reader-x86.h"
+#include "zzdeps/common/debugbreak.h"
+#include "zzdeps/zz.h"
+
+zpointer zz_x86_reader_read_one_instruction(ZzInstruction *insn_ctx, zpointer address) { return NULL; }
+
+X86InsnType GetX86InsnType(zuint32 insn) { return X86_UNDEF; }
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/reader-x86.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/reader-x86.h
new file mode 100644
index 000000000..864610be2
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/reader-x86.h
@@ -0,0 +1,37 @@
+/**
+ *    Copyright 2017 jmpews
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef platforms_arch_x86_reader_h
+#define platforms_arch_x86_reader_h
+
+// platforms
+#include "instructions.h"
+
+// hookzz
+
+// zzdeps
+#include "hookzz.h"
+#include "zzdefs.h"
+#include "zzdeps/common/debugbreak.h"
+#include "zzdeps/zz.h"
+
+typedef enum _X86InsnType { X86_UNDEF } X86InsnType;
+
+X86InsnType GetX86InsnType(zuint32 insn);
+
+zpointer zz_x86_reader_read_one_instruction(ZzInstruction *insn_ctx, zpointer address);
+
+#endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/trampoline-arm.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/regs-x86.c
similarity index 86%
rename from VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/trampoline-arm.h
rename to VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/regs-x86.c
index 6548be5b4..91af33c98 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/trampoline-arm.h
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/regs-x86.c
@@ -12,4 +12,10 @@
  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  *    See the License for the specific language governing permissions and
  *    limitations under the License.
- */
\ No newline at end of file
+ */
+
+#include "regs-x86.h"
+
+void zz_x86_register_describe(ZzX86Reg reg, ZzX86RegInfo *ri) {
+
+}
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/regs-x86.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/regs-x86.h
new file mode 100644
index 000000000..377ee37a7
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/regs-x86.h
@@ -0,0 +1,39 @@
+/**
+ *    Copyright 2017 jmpews
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef platforms_arch_x86_regs_h
+#define platforms_arch_x86_regs_h
+
+// platforms
+#include "instructions.h"
+
+// hookzz
+
+// zzdeps
+#include "hookzz.h"
+#include "zzdefs.h"
+#include "zzdeps/common/debugbreak.h"
+#include "zzdeps/zz.h"
+
+typedef enum _ZzX86Reg { X86_REG_UNDEF } ZzX86Reg;
+
+typedef struct _ZzX86RegInfo {
+
+} ZzX86RegInfo;
+
+void zz_x86_register_describe(ZzX86Reg reg, ZzX86RegInfo *ri);
+
+#endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/relocator-x86.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/relocator-x86.c
new file mode 100644
index 000000000..3380a97ae
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/relocator-x86.c
@@ -0,0 +1,48 @@
+/**
+ *    Copyright 2017 jmpews
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include "relocator-x86.h"
+#include <stdlib.h>
+#include <string.h>
+
+#define MAX_RELOCATOR_INSTRUCIONS_SIZE 64
+
+void zz_x86_relocator_init(ZzX86Relocator *relocator, zpointer input_code, ZzX86Writer *output) {
+}
+
+void zz_x86_relocator_reset(ZzX86Relocator *self, zpointer input_code, ZzX86Writer *output) {
+}
+
+zsize zz_x86_relocator_read_one(ZzX86Relocator *self, ZzInstruction *instruction) {
+    return 0;
+}
+
+zaddr zz_x86_relocator_get_insn_relocated_offset(ZzX86Relocator *self, zaddr address) {
+    return 0;
+}
+
+void zz_x86_relocator_relocate_writer(ZzX86Relocator *relocator, zaddr code_address) {
+}
+
+void zz_x86_relocator_write_all(ZzX86Relocator *self) {
+}
+
+void zz_x86_relocator_try_relocate(zpointer address, zuint min_bytes, zuint *max_bytes) {
+}
+
+zbool zz_x86_relocator_write_one(ZzX86Relocator *self) {
+    return TRUE;
+}
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/relocator-x86.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/relocator-x86.h
new file mode 100644
index 000000000..ed6a1efda
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/relocator-x86.h
@@ -0,0 +1,58 @@
+/**
+ *    Copyright 2017 jmpews
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef platforms_arch_x86_relocator_h
+#define platforms_arch_x86_relocator_h
+
+// platforms
+#include "instructions.h"
+#include "reader-x86.h"
+#include "regs-x86.h"
+#include "writer-x86.h"
+
+// hookzz
+#include "writer.h"
+
+// zzdeps
+#include "hookzz.h"
+#include "zzdefs.h"
+#include "zzdeps/common/debugbreak.h"
+#include "zzdeps/zz.h"
+
+typedef struct _ZzX86Relocator {
+    zbool try_relocated_again;
+    zsize try_relocated_length;
+    zpointer input_start;
+    zpointer input_cur;
+    zaddr input_pc;
+    zuint inpos;
+    zuint outpos;
+    ZzInstruction *input_insns;
+    ZzRelocateInstruction *output_insns;
+    ZzX86Writer *output;
+    ZzLiteralInstruction **relocate_literal_insns;
+    zsize relocate_literal_insns_size;
+} ZzX86Relocator;
+
+void zz_x86_relocator_init(ZzX86Relocator *relocator, zpointer input_code, ZzX86Writer *writer);
+void zz_x86_relocator_reset(ZzX86Relocator *self, zpointer input_code, ZzX86Writer *output);
+
+zsize zz_x86_relocator_read_one(ZzX86Relocator *self, ZzInstruction *instruction);
+zbool zz_x86_relocator_write_one(ZzX86Relocator *self);
+void zz_x86_relocator_write_all(ZzX86Relocator *self);
+void zz_x86_relocator_try_relocate(zpointer address, zuint min_bytes, zuint *max_bytes);
+
+#endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/writer-x86.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/writer-x86.c
new file mode 100644
index 000000000..eb6afe6c7
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/writer-x86.c
@@ -0,0 +1,47 @@
+/**
+ *    Copyright 2017 jmpews
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "writer-x86.h"
+
+ZzX86Writer *zz_x86_writer_new(zpointer data_ptr) {
+    return NULL;
+}
+
+void zz_x86_writer_init(ZzX86Writer *self, zpointer target_addr) { zz_x86_writer_reset(self, target_addr); }
+
+void zz_x86_writer_reset(ZzX86Writer *self, zpointer data_ptr) {
+}
+
+zsize zz_x86_writer_near_jump_range_size() { return 0; }
+
+
+void zz_x86_writer_put_bytes(ZzWriter *self, zbyte *data, zsize size) {
+
+}
+
+void zz_x86_writer_put_instruction(ZzWriter *self, zuint32 insn) {
+
+}
+
+
+// ======= relocator =======
+
+// ======= user custom =======
+
+// ======= default =======
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/writer-x86.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/writer-x86.h
new file mode 100644
index 000000000..602921036
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/writer-x86.h
@@ -0,0 +1,52 @@
+/**
+ *    Copyright 2017 jmpews
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef platforms_arch_x86_writer_h
+#define platforms_arch_x86_writer_h
+
+// platforms
+#include "instructions.h"
+#include "regs-x86.h"
+#include "writer-x86.h"
+
+// hookzz
+#include "writer.h"
+
+// zzdeps
+#include "hookzz.h"
+#include "zzdefs.h"
+#include "zzdeps/common/debugbreak.h"
+#include "zzdeps/zz.h"
+
+typedef ZzWriter ZzX86Writer;
+
+ZzX86Writer *zz_x86_writer_new(zpointer data_ptr);
+
+void zz_x86_writer_reset(ZzX86Writer *self, zpointer data_ptr);
+
+void zz_x86_writer_init(ZzX86Writer *self, zpointer target_addr);
+
+zsize zz_x86_writer_near_jump_range_size();
+void zz_x86_writer_put_bytes(ZzWriter *self, zbyte *data, zsize size);
+void zz_x86_writer_put_instruction(ZzWriter *self, zuint32 insn);
+
+// ======= user custom =======
+
+// ======= default =======
+
+// ======= relocator =======
+
+#endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/interceptor-arm.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/interceptor-arm.c
new file mode 100644
index 000000000..c37bb132f
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/interceptor-arm.c
@@ -0,0 +1,563 @@
+/**
+ *    Copyright 2017 jmpews
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include "interceptor-arm.h"
+#include "zzinfo.h"
+
+#include <stdlib.h>
+
+#define ZZ_THUMB_TINY_REDIRECT_SIZE 4
+#define ZZ_THUMB_FULL_REDIRECT_SIZE 8
+#define ZZ_ARM_TINY_REDIRECT_SIZE 4
+#define ZZ_ARM_FULL_REDIRECT_SIZE 8
+
+ZzInterceptorBackend *ZzBuildInteceptorBackend(ZzAllocator *allocator) {
+    if (!ZzMemoryIsSupportAllocateRXPage()) {
+        return NULL;
+    }
+    ZZSTATUS status;
+    ZzInterceptorBackend *backend = (ZzInterceptorBackend *)malloc(sizeof(ZzInterceptorBackend));
+    memset(backend, 0, sizeof(ZzInterceptorBackend));
+
+    zz_arm_writer_init(&backend->arm_writer, NULL);
+    zz_arm_relocator_init(&backend->arm_relocator, NULL, &backend->arm_writer);
+    zz_thumb_writer_init(&backend->thumb_writer, NULL);
+    zz_thumb_relocator_init(&backend->thumb_relocator, NULL, &backend->thumb_writer);
+
+    backend->allocator = allocator;
+    backend->enter_thunk = NULL;
+    backend->half_thunk = NULL;
+    backend->leave_thunk = NULL;
+
+    status = ZzThunkerBuildThunk(backend);
+
+    if (status == ZZ_FAILED) {
+        ZzInfoLog("%s", "ZzThunkerBuildThunk return ZZ_FAILED\n");
+        return NULL;
+    }
+
+    return backend;
+}
+
+ZzCodeSlice *zz_code_patch_thumb_writer(ZzThumbWriter *thumb_writer, ZzAllocator *allocator, zaddr target_addr,
+                                        zsize range_size) {
+    ZzCodeSlice *code_slice = NULL;
+    if (range_size > 0) {
+        code_slice = ZzNewNearCodeSlice(allocator, target_addr, range_size, thumb_writer->size);
+    } else {
+        code_slice = ZzNewCodeSlice(allocator, thumb_writer->size + 4);
+    }
+    if (!code_slice)
+        return NULL;
+
+    if (!ZzMemoryPatchCode((zaddr)code_slice->data, thumb_writer->base, thumb_writer->size)) {
+
+        free(code_slice);
+        return NULL;
+    }
+    return code_slice;
+}
+
+ZzCodeSlice *zz_code_patch_thumb_relocate_writer(ZzThumbRelocator *thumb_relocator, ZzThumbWriter *thumb_writer,
+                                                 ZzAllocator *allocator, zaddr target_addr, zsize range_size) {
+    ZzCodeSlice *code_slice = NULL;
+    if (range_size > 0) {
+        code_slice = ZzNewNearCodeSlice(allocator, target_addr, range_size, thumb_writer->size);
+    } else {
+        code_slice = ZzNewCodeSlice(allocator, thumb_writer->size + 4);
+    }
+    if (!code_slice)
+        return NULL;
+
+    zz_thumb_relocator_relocate_writer(thumb_relocator, (zaddr)code_slice->data);
+
+    if (!ZzMemoryPatchCode((zaddr)code_slice->data, thumb_writer->base, thumb_writer->size)) {
+
+        free(code_slice);
+        return NULL;
+    }
+    return code_slice;
+}
+
+ZzCodeSlice *zz_code_patch_arm_writer(ZzArmWriter *arm_writer, ZzAllocator *allocator, zaddr target_addr,
+                                      zsize range_size) {
+    ZzCodeSlice *code_slice = NULL;
+    if (range_size > 0) {
+        code_slice = ZzNewNearCodeSlice(allocator, target_addr, range_size, arm_writer->size);
+    } else {
+        code_slice = ZzNewCodeSlice(allocator, arm_writer->size + 4);
+    }
+    if (!code_slice)
+        return NULL;
+
+    if (!ZzMemoryPatchCode((zaddr)code_slice->data, arm_writer->base, arm_writer->size)) {
+        free(code_slice);
+        return NULL;
+    }
+    return code_slice;
+}
+
+ZzCodeSlice *zz_code_patch_arm_relocate_writer(ZzArmRelocator *arm_relocator, ZzArmWriter *arm_writer,
+                                               ZzAllocator *allocator, zaddr target_addr, zsize range_size) {
+    ZzCodeSlice *code_slice = NULL;
+    if (range_size > 0) {
+        code_slice = ZzNewNearCodeSlice(allocator, target_addr, range_size, arm_writer->size);
+    } else {
+        code_slice = ZzNewCodeSlice(allocator, arm_writer->size + 4);
+    }
+    if (!code_slice)
+        return NULL;
+
+    zz_arm_relocator_relocate_writer(arm_relocator, (zaddr)code_slice->data);
+
+    if (!ZzMemoryPatchCode((zaddr)code_slice->data, arm_writer->base, arm_writer->size)) {
+        free(code_slice);
+        return NULL;
+    }
+    return code_slice;
+}
+
+ZZSTATUS ZzPrepareTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {
+    zbool is_thumb = FALSE;
+    zaddr target_addr = (zaddr)entry->target_ptr;
+    zuint redirect_limit = 0;
+
+    ZzArmHookFunctionEntryBackend *entry_backend;
+    entry_backend = (ZzArmHookFunctionEntryBackend *)malloc(sizeof(ZzArmHookFunctionEntryBackend));
+    memset(entry_backend, 0, sizeof(ZzArmHookFunctionEntryBackend));
+
+    entry->backend = (struct _ZzHookFunctionEntryBackend *)entry_backend;
+
+    is_thumb = INSTRUCTION_IS_THUMB((zaddr)entry->target_ptr);
+    if (is_thumb)
+        target_addr = (zaddr)entry->target_ptr & ~(zaddr)1;
+
+    if (is_thumb) {
+        if (entry->try_near_jump) {
+            entry_backend->redirect_code_size = ZZ_THUMB_TINY_REDIRECT_SIZE;
+        } else {
+            zz_thumb_relocator_try_relocate((zpointer)target_addr, ZZ_THUMB_FULL_REDIRECT_SIZE, &redirect_limit);
+            if (redirect_limit != 0 && redirect_limit > ZZ_THUMB_TINY_REDIRECT_SIZE &&
+                redirect_limit < ZZ_THUMB_FULL_REDIRECT_SIZE) {
+                entry->try_near_jump = TRUE;
+                entry_backend->redirect_code_size = ZZ_THUMB_TINY_REDIRECT_SIZE;
+            } else if (redirect_limit != 0 && redirect_limit < ZZ_THUMB_TINY_REDIRECT_SIZE) {
+                return ZZ_FAILED;
+            } else {
+                entry_backend->redirect_code_size = ZZ_THUMB_FULL_REDIRECT_SIZE;
+                if (target_addr % 4) {
+                    entry_backend->redirect_code_size += 2;
+                }
+            }
+        }
+        self->thumb_relocator.try_relocated_length = entry_backend->redirect_code_size;
+    } else {
+        if (entry->try_near_jump) {
+            entry_backend->redirect_code_size = ZZ_ARM_TINY_REDIRECT_SIZE;
+        } else {
+            zz_arm_relocator_try_relocate((zpointer)target_addr, ZZ_ARM_FULL_REDIRECT_SIZE, &redirect_limit);
+            if (redirect_limit != 0 && redirect_limit > ZZ_ARM_TINY_REDIRECT_SIZE &&
+                redirect_limit < ZZ_ARM_FULL_REDIRECT_SIZE) {
+                entry->try_near_jump = TRUE;
+                entry_backend->redirect_code_size = ZZ_ARM_TINY_REDIRECT_SIZE;
+            } else if (redirect_limit != 0 && redirect_limit < ZZ_ARM_TINY_REDIRECT_SIZE) {
+                return ZZ_FAILED;
+            } else {
+                entry_backend->redirect_code_size = ZZ_ARM_FULL_REDIRECT_SIZE;
+            }
+        }
+        self->arm_relocator.try_relocated_length = entry_backend->redirect_code_size;
+    }
+
+    zz_arm_relocator_init(&self->arm_relocator, (zpointer)target_addr, &self->arm_writer);
+    zz_thumb_relocator_init(&self->thumb_relocator, (zpointer)target_addr, &self->thumb_writer);
+    return ZZ_SUCCESS;
+}
+
+ZZSTATUS ZzBuildEnterTransferTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {
+    zbyte temp_code_slice_data[256] = {0};
+    ZzArmWriter *arm_writer = NULL;
+    ZzArmWriter *thumb_writer = NULL;
+    ZzCodeSlice *code_slice = NULL;
+    ZzArmHookFunctionEntryBackend *entry_backend = (ZzArmHookFunctionEntryBackend *)entry->backend;
+    ZZSTATUS status = ZZ_SUCCESS;
+    zbool is_thumb = TRUE;
+    zaddr target_addr = (zaddr)entry->target_ptr;
+
+    is_thumb = INSTRUCTION_IS_THUMB((zaddr)entry->target_ptr);
+    if (is_thumb)
+        target_addr = (zaddr)entry->target_ptr & ~(zaddr)1;
+
+    if (is_thumb) {
+        thumb_writer = &self->thumb_writer;
+        zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
+
+        /* jump to on_enter_trampoline */
+        zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)entry->on_enter_trampoline);
+
+        /* code patch */
+        code_slice = zz_code_patch_thumb_writer(thumb_writer, self->allocator, target_addr,
+                                                zz_thumb_writer_near_jump_range_size());
+        if (code_slice)
+            entry->on_enter_transfer_trampoline = code_slice->data;
+        else
+            return ZZ_FAILED;
+    } else {
+        arm_writer = &self->arm_writer;
+        zz_arm_writer_reset(arm_writer, temp_code_slice_data);
+
+        /* jump to on_enter_trampoline */
+        zz_arm_writer_put_ldr_reg_address(arm_writer, ZZ_ARM_REG_PC, (zaddr)entry->on_enter_trampoline);
+
+        /* code patch */
+        code_slice =
+            zz_code_patch_arm_writer(arm_writer, self->allocator, target_addr, zz_arm_writer_near_jump_range_size());
+        if (code_slice)
+            entry->on_enter_transfer_trampoline = code_slice->data;
+        else
+            return ZZ_FAILED;
+    }
+
+    if (ZzIsEnableDebugMode()) {
+        char buffer[1024] = {};
+        sprintf(buffer + strlen(buffer), "%s\n", "ZzBuildEnterTransferTrampoline:");
+        sprintf(buffer + strlen(buffer),
+                "LogInfo: on_enter_transfer_trampoline at %p, length: %ld. and will jump to "
+                "on_enter_trampoline(%p).\n",
+                code_slice->data, code_slice->size, entry->on_enter_trampoline);
+        ZzInfoLog("%s", buffer);
+    }
+
+    free(code_slice);
+    return status;
+}
+
+ZZSTATUS ZzBuildEnterTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {
+    zbyte temp_code_slice_data[256] = {0};
+    ZzArmWriter *arm_writer = NULL;
+    ZzArmWriter *thumb_writer = NULL;
+    ZzCodeSlice *code_slice = NULL;
+    ZzArmHookFunctionEntryBackend *entry_backend = (ZzArmHookFunctionEntryBackend *)entry->backend;
+    ZZSTATUS status = ZZ_SUCCESS;
+    zbool is_thumb;
+
+    is_thumb = INSTRUCTION_IS_THUMB((zaddr)entry->target_ptr);
+
+    thumb_writer = &self->thumb_writer;
+    zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
+
+    /* prepare 2 stack space: 1. next_hop 2. entry arg */
+    zz_thumb_writer_put_sub_reg_imm(thumb_writer, ZZ_ARM_REG_SP, 0xc);
+    zz_thumb_writer_put_str_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0); // push r7
+    zz_thumb_writer_put_ldr_b_reg_address(thumb_writer, ZZ_ARM_REG_R1, (zaddr)entry);
+    zz_thumb_writer_put_str_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x4);
+    zz_thumb_writer_put_ldr_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0); // pop r7
+    zz_thumb_writer_put_add_reg_imm(thumb_writer, ZZ_ARM_REG_SP, 0x4);
+
+    /* jump to enter thunk */
+    zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)self->enter_thunk);
+
+    /* code patch */
+    code_slice = zz_code_patch_thumb_writer(thumb_writer, self->allocator, 0, 0);
+    if (code_slice)
+        entry->on_enter_trampoline = code_slice->data + 1;
+    else
+        return ZZ_FAILED;
+
+    /* debug log */
+    if (ZzIsEnableDebugMode()) {
+        char buffer[1024] = {};
+        sprintf(buffer + strlen(buffer), "%s\n", "ZzBuildEnterTrampoline:");
+        sprintf(buffer + strlen(buffer),
+                "LogInfo: on_enter_trampoline at %p, length: %ld. hook-entry: %p. and will jump to "
+                "enter_thunk(%p)\n",
+                code_slice->data, code_slice->size, (void *)entry, (void *)self->enter_thunk);
+        ZzInfoLog("%s", buffer);
+    }
+
+    if ((is_thumb && entry_backend->redirect_code_size == ZZ_THUMB_TINY_REDIRECT_SIZE) ||
+        (!is_thumb && entry_backend->redirect_code_size == ZZ_ARM_TINY_REDIRECT_SIZE)) {
+        ZzBuildEnterTransferTrampoline(self, entry);
+    }
+
+    free(code_slice);
+    return status;
+}
+
+ZZSTATUS ZzBuildInvokeTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {
+    zbyte temp_code_slice_data[256] = {0};
+    ZzCodeSlice *code_slice = NULL;
+    ZzArmHookFunctionEntryBackend *entry_backend = (ZzArmHookFunctionEntryBackend *)entry->backend;
+    ZZSTATUS status = ZZ_SUCCESS;
+    zbool is_thumb = TRUE;
+    zaddr target_addr = (zaddr)entry->target_ptr;
+    zpointer target_end_addr = 0;
+    zpointer restore_target_addr;
+
+    is_thumb = INSTRUCTION_IS_THUMB((zaddr)entry->target_ptr);
+    if (is_thumb)
+        target_addr = (zaddr)entry->target_ptr & ~(zaddr)1;
+
+    if (is_thumb) {
+        target_end_addr = (zpointer)((zaddr)entry->target_end_ptr & ~(zaddr)1);
+    }
+
+    if (is_thumb) {
+        ZzThumbRelocator *thumb_relocator;
+        ZzThumbWriter *thumb_writer;
+        thumb_relocator = &self->thumb_relocator;
+        thumb_writer = &self->thumb_writer;
+
+        zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
+        zz_thumb_relocator_reset(thumb_relocator, (zpointer)target_addr, thumb_writer);
+        zsize tmp_relocator_insn_size = 0;
+        entry->target_half_ret_addr = 0;
+
+        if (entry->hook_type == HOOK_FUNCTION_TYPE) {
+            do {
+                zz_thumb_relocator_read_one(thumb_relocator, NULL);
+                tmp_relocator_insn_size = thumb_relocator->input_cur - thumb_relocator->input_start;
+            } while (tmp_relocator_insn_size < entry_backend->redirect_code_size);
+            zz_thumb_relocator_write_all(thumb_relocator);
+        } else if (entry->hook_type == HOOK_ADDRESS_TYPE) {
+            do {
+                zz_thumb_relocator_read_one(thumb_relocator, NULL);
+                zz_thumb_relocator_write_one(thumb_relocator);
+                tmp_relocator_insn_size = thumb_relocator->input_cur - thumb_relocator->input_start;
+                if (thumb_relocator->input_cur >= target_end_addr && !entry->target_half_ret_addr) {
+                    /* jump to rest target address */
+                    zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)entry->on_half_trampoline);
+                    entry->target_half_ret_addr = (zpointer)(thumb_writer->size + 1);
+                }
+            } while (tmp_relocator_insn_size < entry_backend->redirect_code_size ||
+                     thumb_relocator->input_cur < target_end_addr);
+        }
+
+        /* jump to rest target address */
+        restore_target_addr = (zpointer)((zaddr)target_addr + tmp_relocator_insn_size);
+        zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)(restore_target_addr + 1));
+
+        /* code patch */
+        code_slice = zz_code_patch_thumb_relocate_writer(thumb_relocator, thumb_writer, self->allocator, 0, 0);
+        if (code_slice)
+            entry->on_invoke_trampoline = code_slice->data + 1;
+        else
+            return ZZ_FAILED;
+    } else {
+        ZzArmRelocator *arm_relocator;
+        ZzArmWriter *arm_writer;
+        arm_relocator = &self->arm_relocator;
+        arm_writer = &self->arm_writer;
+
+        zz_arm_writer_reset(arm_writer, temp_code_slice_data);
+        zz_arm_relocator_reset(arm_relocator, (zpointer)target_addr, arm_writer);
+        entry->target_half_ret_addr = 0;
+        zsize tmp_relocator_insn_size = 0;
+
+        if (entry->hook_type == HOOK_FUNCTION_TYPE) {
+            do {
+                zz_arm_relocator_read_one(arm_relocator, NULL);
+                tmp_relocator_insn_size = arm_relocator->input_cur - arm_relocator->input_start;
+            } while (tmp_relocator_insn_size < entry_backend->redirect_code_size);
+            zz_arm_relocator_write_all(arm_relocator);
+        } else if (entry->hook_type == HOOK_ADDRESS_TYPE) {
+            do {
+                zz_arm_relocator_read_one(arm_relocator, NULL);
+                zz_arm_relocator_write_one(arm_relocator);
+                tmp_relocator_insn_size = arm_relocator->input_cur - arm_relocator->input_start;
+                if (arm_relocator->input_cur >= target_end_addr && !entry->target_half_ret_addr) {
+                    /* jump to rest target address */
+                    zz_arm_writer_put_ldr_reg_address(arm_writer, ZZ_ARM_REG_PC, (zaddr)entry->on_half_trampoline);
+                    entry->target_half_ret_addr = (zpointer)arm_writer->size;
+                }
+            } while (tmp_relocator_insn_size < entry_backend->redirect_code_size ||
+                     arm_relocator->input_cur < target_end_addr);
+        }
+
+        /* jump to rest target address */
+        restore_target_addr = (zpointer)((zaddr)target_addr + tmp_relocator_insn_size);
+        zz_arm_writer_put_ldr_reg_address(arm_writer, ZZ_ARM_REG_PC, (zaddr)restore_target_addr);
+
+        /* code patch */
+        code_slice = zz_code_patch_arm_relocate_writer(arm_relocator, arm_writer, self->allocator, 0, 0);
+        if (code_slice)
+            entry->on_invoke_trampoline = code_slice->data;
+        else
+            return ZZ_FAILED;
+    }
+
+    /* update target_half_ret_addr */
+    if (entry->hook_type == HOOK_ADDRESS_TYPE) {
+        entry->target_half_ret_addr += (zaddr)code_slice->data;
+    }
+
+    /* debug log */
+    if (ZzIsEnableDebugMode()) {
+        char buffer[1024] = {};
+        sprintf(buffer + strlen(buffer), "%s\n", "ZzBuildInvokeTrampoline:");
+        sprintf(buffer + strlen(buffer),
+                "LogInfo: on_invoke_trampoline at %p, length: %ld. and will jump to rest code(%p).\n", code_slice->data,
+                code_slice->size, restore_target_addr);
+        if (is_thumb) {
+            sprintf(buffer + strlen(buffer),
+                    "ThumbInstructionFix: origin instruction at %p, end at %p, relocator instruction nums %ld\n",
+                    (&self->thumb_relocator)->input_start, (&self->thumb_relocator)->input_cur,
+                    (&self->thumb_relocator)->inpos);
+        } else {
+            sprintf(buffer + strlen(buffer),
+                    "ArmInstructionFix: origin instruction at %p, end at %p, relocator instruction nums %ld\n",
+                    (&self->arm_relocator)->input_start, (&self->arm_relocator)->input_cur,
+                    (&self->arm_relocator)->inpos);
+        }
+
+        char origin_prologue[256] = {0};
+        int t = 0;
+        zpointer p;
+        if (is_thumb) {
+            for (p = (&self->thumb_relocator)->input_start; p < (&self->thumb_relocator)->input_cur; p++, t = t + 5) {
+                sprintf(origin_prologue + t, "0x%.2x ", *(unsigned char *)p);
+            }
+        } else {
+            for (p = (&self->arm_relocator)->input_start; p < (&self->arm_relocator)->input_cur; p++, t = t + 5) {
+                sprintf(origin_prologue + t, "0x%.2x ", *(unsigned char *)p);
+            }
+        }
+        sprintf(buffer + strlen(buffer), "origin_prologue: %s\n", origin_prologue);
+
+        ZzInfoLog("%s", buffer);
+    }
+
+    free(code_slice);
+    return status;
+}
+
+ZZSTATUS ZzBuildHalfTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {
+    zbyte temp_code_slice_data[256] = {0};
+    ZzArmWriter *arm_writer = NULL;
+    ZzArmWriter *thumb_writer = NULL;
+    ZzCodeSlice *code_slice = NULL;
+    ZZSTATUS status = ZZ_SUCCESS;
+
+    thumb_writer = &self->thumb_writer;
+    zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
+
+    /* prepare 2 stack space: 1. next_hop 2. entry arg */
+    zz_thumb_writer_put_sub_reg_imm(thumb_writer, ZZ_ARM_REG_SP, 0xc);
+    zz_thumb_writer_put_str_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0); // push r7
+    zz_thumb_writer_put_ldr_b_reg_address(thumb_writer, ZZ_ARM_REG_R1, (zaddr)entry);
+    zz_thumb_writer_put_str_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x4);
+    zz_thumb_writer_put_ldr_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0); // pop r7
+    zz_thumb_writer_put_add_reg_imm(thumb_writer, ZZ_ARM_REG_SP, 0x4);
+
+    /* jump to half_thunk */
+    zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)self->half_thunk);
+
+    /* code patch */
+    code_slice = zz_code_patch_thumb_writer(thumb_writer, self->allocator, 0, 0);
+    if (code_slice)
+        entry->on_half_trampoline = code_slice->data + 1;
+    else
+        return ZZ_FAILED;
+
+    free(code_slice);
+    return status;
+}
+
+ZZSTATUS ZzBuildLeaveTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {
+    zbyte temp_code_slice_data[256] = {0};
+    ZzCodeSlice *code_slice = NULL;
+    ZZSTATUS status = ZZ_SUCCESS;
+    zbool is_thumb = TRUE;
+    ZzArmWriter *thumb_writer;
+
+    thumb_writer = &self->thumb_writer;
+    zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
+
+    /* prepare 2 stack space: 1. next_hop 2. entry arg */
+    zz_thumb_writer_put_sub_reg_imm(thumb_writer, ZZ_ARM_REG_SP, 0xc);
+    zz_thumb_writer_put_str_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0); // push r7
+    zz_thumb_writer_put_ldr_b_reg_address(thumb_writer, ZZ_ARM_REG_R1, (zaddr)entry);
+    zz_thumb_writer_put_str_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x4);
+    zz_thumb_writer_put_ldr_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0); // pop r7
+    zz_thumb_writer_put_add_reg_imm(thumb_writer, ZZ_ARM_REG_SP, 0x4);
+
+    /* jump to leave_thunk */
+    zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)self->leave_thunk);
+
+    /* code patch */
+    code_slice = zz_code_patch_thumb_writer(thumb_writer, self->allocator, 0, 0);
+    if (code_slice)
+        entry->on_leave_trampoline = code_slice->data + 1;
+    else
+        return ZZ_FAILED;
+
+    /* debug log */
+    if (ZzIsEnableDebugMode()) {
+        char buffer[1024] = {};
+        sprintf(buffer + strlen(buffer), "%s\n", "ZzBuildLeaveTrampoline:");
+        sprintf(buffer + strlen(buffer),
+                "LogInfo: on_leave_trampoline at %p, length: %ld. and will jump to leave_thunk(%p).\n",
+                code_slice->data, code_slice->size, self->leave_thunk);
+        ZzInfoLog("%s", buffer);
+    }
+
+    free(code_slice);
+    return ZZ_DONE;
+}
+
+ZZSTATUS ZzActivateTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {
+    zbyte temp_code_slice_data[256] = {0};
+    ZzCodeSlice *code_slice = NULL;
+    ZzArmHookFunctionEntryBackend *entry_backend = (ZzArmHookFunctionEntryBackend *)entry->backend;
+    ZZSTATUS status = ZZ_SUCCESS;
+    zbool is_thumb = TRUE;
+    zaddr target_addr = (zaddr)entry->target_ptr;
+
+    is_thumb = INSTRUCTION_IS_THUMB((zaddr)entry->target_ptr);
+    if (is_thumb)
+        target_addr = (zaddr)entry->target_ptr & ~(zaddr)1;
+
+    if (is_thumb) {
+        ZzThumbWriter *thumb_writer;
+        thumb_writer = &self->thumb_writer;
+        zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
+        thumb_writer->pc = target_addr + 4;
+
+        if (entry_backend->redirect_code_size == ZZ_THUMB_TINY_REDIRECT_SIZE) {
+            zz_thumb_writer_put_b_imm32(thumb_writer,
+                                        (zaddr)entry->on_enter_transfer_trampoline - (zaddr)thumb_writer->pc);
+        } else {
+            zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)entry->on_enter_trampoline);
+        }
+        if (!ZzMemoryPatchCode((zaddr)target_addr, thumb_writer->base, thumb_writer->size))
+            return ZZ_FAILED;
+    } else {
+        ZzArmWriter *arm_writer;
+        arm_writer = &self->arm_writer;
+        zz_arm_writer_reset(arm_writer, temp_code_slice_data);
+        arm_writer->pc = target_addr + 8;
+
+        if (entry_backend->redirect_code_size == ZZ_ARM_TINY_REDIRECT_SIZE) {
+            zz_arm_writer_put_b_imm(arm_writer, (zaddr)entry->on_enter_transfer_trampoline - (zaddr)arm_writer->pc);
+        } else {
+            zz_arm_writer_put_ldr_reg_address(arm_writer, ZZ_ARM_REG_PC, (zaddr)entry->on_enter_trampoline);
+        }
+        if (!ZzMemoryPatchCode((zaddr)target_addr, arm_writer->base, arm_writer->size))
+            return ZZ_FAILED;
+    }
+
+    return ZZ_DONE_HOOK;
+}
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/interceptor-arm.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/interceptor-arm.h
index 663b7fa40..5563dfb8d 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/interceptor-arm.h
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/interceptor-arm.h
@@ -55,4 +55,9 @@ typedef struct _ZzArmHookFuntionEntryBackend {
     zuint redirect_code_size;
 } ZzArmHookFunctionEntryBackend;
 
+ZzCodeSlice *zz_code_patch_thumb_writer(ZzThumbWriter *thumb_writer, ZzAllocator *allocator, zaddr target_addr,
+                                        zsize range_size);
+ZzCodeSlice *zz_code_patch_arm_writer(ZzArmWriter *arm_writer, ZzAllocator *allocator, zaddr target_addr,
+                                      zsize range_size);
+
 #endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/thunker-arm.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/thunker-arm.c
index 9505347d8..51529186d 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/thunker-arm.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/thunker-arm.c
@@ -15,6 +15,7 @@
  */
 
 #include "thunker-arm.h"
+#include "zzinfo.h"
 
 // 前提: arm 可以直接访问 pc 寄存器, 也就是说无需中间寄存器就可以实现 `abs
 // jump`.
@@ -88,20 +89,22 @@ void function_context_begin_invocation(ZzHookFunctionEntry *entry, zpointer next
                                        zpointer caller_ret_addr) {
 
     Xdebug("target %p call begin-invocation", entry->target_ptr);
+
     ZzThreadStack *threadstack = ZzGetCurrentThreadStack(entry->thread_local_key);
     if (!threadstack) {
         threadstack = ZzNewThreadStack(entry->thread_local_key);
     }
-
     ZzCallStack *callstack = ZzNewCallStack();
     ZzPushCallStack(threadstack, callstack);
 
+    /* call pre_call */
     if (entry->pre_call) {
         PRECALL pre_call;
         pre_call = entry->pre_call;
         (*pre_call)(rs, (ThreadStack *)threadstack, (CallStack *)callstack);
     }
 
+    /* set next hop */
     if (entry->replace_call) {
         *(zpointer *)next_hop = entry->replace_call;
     } else {
@@ -117,6 +120,7 @@ void function_context_begin_invocation(ZzHookFunctionEntry *entry, zpointer next
 void function_context_half_invocation(ZzHookFunctionEntry *entry, zpointer next_hop, RegState *rs,
                                       zpointer caller_ret_addr) {
     Xdebug("target %p call half-invocation", entry->target_ptr);
+
     ZzThreadStack *threadstack = ZzGetCurrentThreadStack(entry->thread_local_key);
     if (!threadstack) {
 #if defined(DEBUG_MODE)
@@ -125,11 +129,14 @@ void function_context_half_invocation(ZzHookFunctionEntry *entry, zpointer next_
     }
     ZzCallStack *callstack = ZzPopCallStack(threadstack);
 
+    /* call half_call */
     if (entry->half_call) {
         HALFCALL half_call;
         half_call = entry->half_call;
         (*half_call)(rs, (ThreadStack *)threadstack, (CallStack *)callstack);
     }
+
+    /* set next hop */
     *(zpointer *)next_hop = (zpointer)entry->target_half_ret_addr;
 
     ZzFreeCallStack(callstack);
@@ -138,6 +145,7 @@ void function_context_half_invocation(ZzHookFunctionEntry *entry, zpointer next_
 // just like post_call, wow!
 void function_context_end_invocation(ZzHookFunctionEntry *entry, zpointer next_hop, RegState *rs) {
     Xdebug("%p call end-invocation", entry->target_ptr);
+
     ZzThreadStack *threadstack = ZzGetCurrentThreadStack(entry->thread_local_key);
     if (!threadstack) {
 #if defined(DEBUG_MODE)
@@ -146,21 +154,22 @@ void function_context_end_invocation(ZzHookFunctionEntry *entry, zpointer next_h
     }
     ZzCallStack *callstack = ZzPopCallStack(threadstack);
 
+    /* call post_call */
     if (entry->post_call) {
         POSTCALL post_call;
         post_call = entry->post_call;
         (*post_call)(rs, (ThreadStack *)threadstack, (CallStack *)callstack);
     }
+
+    /* set next hop */
     *(zpointer *)next_hop = callstack->caller_ret_addr;
 
     ZzFreeCallStack(callstack);
 }
 
-// A4.1.10 BX
 void zz_thumb_thunker_build_enter_thunk(ZzWriter *writer) {
 
     /* save general registers and sp */
-
     zz_thumb_writer_put_bx_reg(writer, ZZ_ARM_REG_PC);
     zz_arm_writer_put_bytes(writer, THUMB_FUNCTION_ADDRESS((void *)ctx_save), 15 * 4);
     zz_arm_writer_put_add_reg_reg_imm(writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_PC, 1);
@@ -186,7 +195,6 @@ void zz_thumb_thunker_build_enter_thunk(ZzWriter *writer) {
 
     /* restore general registers and sp */
     zz_thumb_writer_put_add_reg_imm(writer, ZZ_ARM_REG_SP, 0x8);
-
     zz_thumb_writer_put_bx_reg(writer, ZZ_ARM_REG_PC);
     zz_arm_writer_put_bytes(writer, THUMB_FUNCTION_ADDRESS((void *)ctx_restore), 14 * 4);
     zz_arm_writer_put_bx_to_thumb(writer);
@@ -228,7 +236,6 @@ void zz_thumb_thunker_build_half_thunk(ZzWriter *writer) {
 
     /* restore general registers and sp */
     zz_thumb_writer_put_add_reg_imm(writer, ZZ_ARM_REG_SP, 0x8);
-
     zz_thumb_writer_put_bx_reg(writer, ZZ_ARM_REG_PC);
     zz_arm_writer_put_bytes(writer, THUMB_FUNCTION_ADDRESS((void *)ctx_restore), 14 * 4);
     zz_arm_writer_put_bx_to_thumb(writer);
@@ -254,8 +261,11 @@ void zz_thumb_thunker_build_leave_thunk(ZzWriter *writer) {
     zz_thumb_writer_put_str_reg_reg_offset(writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x4);
 
     /* pass enter func args */
+    /* entry */
     zz_thumb_writer_put_ldr_reg_reg_offset(writer, ZZ_ARM_REG_R0, ZZ_ARM_REG_SP, CTX_SAVE_STACK_OFFSET + 0x8);
+    /* next hop*/
     zz_thumb_writer_put_add_reg_reg_imm(writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, CTX_SAVE_STACK_OFFSET + 0x8 + 0x4);
+    /* RegState */
     zz_thumb_writer_put_add_reg_reg_imm(writer, ZZ_ARM_REG_R2, ZZ_ARM_REG_SP, 0x4);
 
     /* call function_context_begin_invocation */
@@ -264,7 +274,6 @@ void zz_thumb_thunker_build_leave_thunk(ZzWriter *writer) {
 
     /* restore general registers and sp */
     zz_thumb_writer_put_add_reg_imm(writer, ZZ_ARM_REG_SP, 0x8);
-
     zz_thumb_writer_put_bx_reg(writer, ZZ_ARM_REG_PC);
     zz_arm_writer_put_bytes(writer, THUMB_FUNCTION_ADDRESS((void *)ctx_restore), 14 * 4);
     zz_arm_writer_put_bx_to_thumb(writer);
@@ -286,89 +295,99 @@ ZZSTATUS ZzThunkerBuildThunk(ZzInterceptorBackend *self) {
     thumb_writer = &self->thumb_writer;
     zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
 
-    code_slice = NULL;
-    do {
-        zz_thumb_thunker_build_enter_thunk(thumb_writer);
-        if (code_slice) {
-            if (!ZzMemoryPatchCode((zaddr)code_slice->data, thumb_writer->base, thumb_writer->size))
-                return ZZ_FAILED;
-            break;
-        }
-        code_slice = ZzNewCodeSlice(self->allocator, thumb_writer->size + 4);
-        if (!code_slice) {
-#if defined(DEBUG_MODE)
-            debug_break();
-#endif
-            return ZZ_FAILED;
-        } else {
-            zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
-            thumb_writer->pc = code_slice->data + 4;
-        }
-    } while (code_slice);
+    /* buid enter_thunk */
+    zz_thumb_thunker_build_enter_thunk(thumb_writer);
+
+    /* code patch */
+    code_slice = zz_code_patch_thumb_writer(thumb_writer, self->allocator, 0, 0);
+    if (code_slice)
+        self->enter_thunk = code_slice->data + 1;
+    else
+        return ZZ_FAILED;
+
+    /* debug log */
     if (ZzIsEnableDebugMode()) {
-        char buffer[1024] = {};
+        char buffer[2048] = {};
+        char thunk_buffer[2048] = {};
+        int t = 0;
+        zpointer p;
         sprintf(buffer + strlen(buffer), "%s\n", "ZzThunkerBuildThunk:");
+
+        for (p = thumb_writer->base; p < thumb_writer->base + thumb_writer->size; p++, t = t + 5) {
+            sprintf(thunk_buffer + t, "0x%.2x ", *(unsigned char *)p);
+        }
+
+        ZzInfoLog("%s", thunk_buffer);
+        // sprintf(buffer + strlen(buffer), "enter_thunk: %s\n", thunk_buffer);
+
         sprintf(buffer + strlen(buffer), "LogInfo: enter_thunk at %p, length: %ld.\n", code_slice->data,
                 code_slice->size);
-        Xinfo("%s", buffer);
+        ZzInfoLog("%s", buffer);
     }
 
-    /* set thumb enter_thunk */
-    self->enter_thunk = code_slice->data + 1;
-
     zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
-    code_slice = NULL;
-    do {
-        zz_thumb_thunker_build_leave_thunk(thumb_writer);
-        if (code_slice) {
-            if (!ZzMemoryPatchCode((zaddr)code_slice->data, thumb_writer->base, thumb_writer->size))
-                return ZZ_FAILED;
-            break;
-        }
-        code_slice = ZzNewCodeSlice(self->allocator, thumb_writer->size + 4);
-        if (!code_slice) {
-#if defined(DEBUG_MODE)
-            debug_break();
-#endif
-            return ZZ_FAILED;
-        } else {
-            zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
-            thumb_writer->pc = code_slice->data + 4;
-        }
-    } while (code_slice);
+
+    /* build leave_thunk */
+    zz_thumb_thunker_build_leave_thunk(thumb_writer);
+
+    /* code patch */
+    code_slice = zz_code_patch_thumb_writer(thumb_writer, self->allocator, 0, 0);
+    if (code_slice)
+        self->leave_thunk = code_slice->data + 1;
+    else
+        return ZZ_FAILED;
+
+    /* debug log */
     if (ZzIsEnableDebugMode()) {
-        char buffer[1024] = {};
+        char buffer[2048] = {};
+        char thunk_buffer[2048] = {};
+        int t = 0;
+        zpointer p;
         sprintf(buffer + strlen(buffer), "%s\n", "ZzThunkerBuildThunk:");
+
+        for (p = thumb_writer->base; p < thumb_writer->base + thumb_writer->size; p++, t = t + 5) {
+            sprintf(thunk_buffer + t, "0x%.2x ", *(unsigned char *)p);
+        }
+
+        ZzInfoLog("%s", thunk_buffer);
+        // sprintf(buffer + strlen(buffer), "enter_thunk: %s\n", thunk_buffer);
+
         sprintf(buffer + strlen(buffer), "LogInfo: leave_thunk at %p, length: %ld.\n", code_slice->data,
                 code_slice->size);
-        Xinfo("%s", buffer);
+        ZzInfoLog("%s", buffer);
     }
 
-    /* set thumb leave_thunk */
-    self->leave_thunk = code_slice->data + 1;
-
     zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
-    code_slice = NULL;
-    do {
-        zz_thumb_thunker_build_half_thunk(thumb_writer);
-        if (code_slice) {
-            if (!ZzMemoryPatchCode((zaddr)code_slice->data, thumb_writer->base, thumb_writer->size))
-                return ZZ_FAILED;
-            break;
-        }
-        code_slice = ZzNewCodeSlice(self->allocator, thumb_writer->size + 4);
-        if (!code_slice) {
-#if defined(DEBUG_MODE)
-            debug_break();
-#endif
-            return ZZ_FAILED;
-        } else {
-            zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
-            thumb_writer->pc = code_slice->data + 4;
+
+    /* build half_thunk */
+    zz_thumb_thunker_build_half_thunk(thumb_writer);
+
+    /* code patch */
+    code_slice = zz_code_patch_thumb_writer(thumb_writer, self->allocator, 0, 0);
+    if (code_slice)
+        self->half_thunk = code_slice->data + 1;
+    else
+        return ZZ_FAILED;
+
+    /* debug log */
+    if (ZzIsEnableDebugMode()) {
+        char buffer[2048] = {};
+        char thunk_buffer[2048] = {};
+        int t = 0;
+        zpointer p;
+        sprintf(buffer + strlen(buffer), "%s\n", "ZzThunkerBuildThunk:");
+
+        for (p = thumb_writer->base; p < thumb_writer->base + thumb_writer->size; p++, t = t + 5) {
+            sprintf(thunk_buffer + t, "0x%.2x ", *(unsigned char *)p);
         }
-    } while (code_slice);
 
-    /* set thumb half_thunk */
-    self->half_thunk = code_slice->data + 1;
+        ZzInfoLog("%s", thunk_buffer);
+        // sprintf(buffer + strlen(buffer), "half_thunk: %s\n", thunk_buffer);
+
+        sprintf(buffer + strlen(buffer), "LogInfo: half_thunk at %p, length: %ld.\n", code_slice->data,
+                code_slice->size);
+        ZzInfoLog("%s", buffer);
+    }
+
     return status;
 }
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/trampoline-arm.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/trampoline-arm.c
deleted file mode 100644
index d45a0afba..000000000
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/trampoline-arm.c
+++ /dev/null
@@ -1,560 +0,0 @@
-/**
- *    Copyright 2017 jmpews
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- */
-
-#include "interceptor-arm.h"
-
-#include <stdlib.h>
-
-#define INSTRUCTION_IS_THUMB(insn_addr) ((insn_addr & 0x1) == 0x1)
-
-#define ZZ_THUMB_TINY_REDIRECT_SIZE 4
-#define ZZ_THUMB_FULL_REDIRECT_SIZE 8
-#define ZZ_ARM_TINY_REDIRECT_SIZE 4
-#define ZZ_ARM_FULL_REDIRECT_SIZE 8
-
-ZzInterceptorBackend *ZzBuildInteceptorBackend(ZzAllocator *allocator) {
-    ZzInterceptorBackend *backend = (ZzInterceptorBackend *)malloc(sizeof(ZzInterceptorBackend));
-    backend->allocator = allocator;
-
-    zz_arm_writer_init(&backend->arm_writer, NULL);
-    zz_arm_relocator_init(&backend->arm_relocator, NULL, &backend->arm_writer);
-    zz_thumb_writer_init(&backend->thumb_writer, NULL);
-    zz_thumb_relocator_init(&backend->thumb_relocator, NULL, &backend->thumb_writer);
-
-    backend->enter_thunk = NULL;
-    backend->half_thunk = NULL;
-    backend->leave_thunk = NULL;
-
-    ZzThunkerBuildThunk(backend);
-    return backend;
-}
-
-ZZSTATUS ZzPrepareTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {
-    zbool is_thumb = FALSE;
-    zpointer target_addr = entry->target_ptr;
-    zuint redirect_limit;
-
-    ZzArmHookFunctionEntryBackend *entry_backend;
-    entry_backend = (ZzArmHookFunctionEntryBackend *)malloc(sizeof(ZzArmHookFunctionEntryBackend));
-    entry->backend = (struct _ZzHookFunctionEntryBackend *)entry_backend;
-
-    is_thumb = INSTRUCTION_IS_THUMB((zaddr)target_addr);
-
-    if (is_thumb) {
-        if (entry->try_near_jump) {
-            entry_backend->redirect_code_size = ZZ_THUMB_TINY_REDIRECT_SIZE;
-        } else {
-            zz_thumb_relocator_try_relocate(target_addr, ZZ_THUMB_FULL_REDIRECT_SIZE, &redirect_limit);
-            entry_backend->redirect_code_size = ZZ_THUMB_FULL_REDIRECT_SIZE;
-        }
-    } else {
-        if (entry->try_near_jump) {
-            entry_backend->redirect_code_size = ZZ_ARM_TINY_REDIRECT_SIZE;
-        } else {
-            zz_arm_relocator_try_relocate(target_addr, ZZ_ARM_FULL_REDIRECT_SIZE, &redirect_limit);
-            entry_backend->redirect_code_size = ZZ_ARM_FULL_REDIRECT_SIZE;
-        }
-    }
-
-    zz_arm_relocator_init(&self->arm_relocator, target_addr, &self->arm_writer);
-    zz_thumb_relocator_init(&self->thumb_relocator, target_addr, &self->thumb_writer);
-    return ZZ_SUCCESS;
-}
-
-ZZSTATUS ZzBuildEnterTransferTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {
-    zbyte temp_code_slice_data[256] = {0};
-    ZzArmWriter *arm_writer = NULL;
-    ZzArmWriter *thumb_writer = NULL;
-    ZzCodeSlice *code_slice = NULL;
-    ZzArmHookFunctionEntryBackend *entry_backend = (ZzArmHookFunctionEntryBackend *)entry->backend;
-    ZZSTATUS status = ZZ_SUCCESS;
-    zbool is_thumb = TRUE;
-    zpointer target_addr = entry->target_ptr;
-
-    is_thumb = INSTRUCTION_IS_THUMB((zaddr)target_addr);
-    if (is_thumb) {
-        thumb_writer = &self->thumb_writer;
-        zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
-
-        code_slice = NULL;
-        do {
-            zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)entry->on_enter_trampoline);
-            if (code_slice) {
-                if (!ZzMemoryPatchCode((zaddr)code_slice->data, thumb_writer->base, thumb_writer->size))
-                    return ZZ_FAILED;
-                break;
-            }
-            code_slice = ZzNewNearCodeSlice(self->allocator, (zaddr)entry->target_ptr,
-                                            zz_thumb_writer_near_jump_range_size(), thumb_writer->size);
-            if (!code_slice) {
-                return ZZ_FAILED;
-            } else {
-                zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
-                thumb_writer->pc = code_slice->data + 4;
-            }
-        } while (code_slice);
-        entry->on_enter_transfer_trampoline = code_slice->data;
-    } else {
-        arm_writer = &self->arm_writer;
-        zz_arm_writer_reset(arm_writer, temp_code_slice_data);
-
-        code_slice = NULL;
-        do {
-            zz_arm_writer_put_ldr_reg_address(arm_writer, ZZ_ARM_REG_PC, (zaddr)entry->on_enter_trampoline);
-
-            if (code_slice) {
-                if (!ZzMemoryPatchCode((zaddr)code_slice->data, arm_writer->base, arm_writer->size))
-                    return ZZ_FAILED;
-                break;
-            }
-            code_slice = ZzNewNearCodeSlice(self->allocator, (zaddr)entry->target_ptr,
-                                            zz_arm_writer_near_jump_range_size(), arm_writer->size);
-            if (!code_slice) {
-                return ZZ_FAILED;
-            } else {
-                zz_arm_writer_reset(arm_writer, temp_code_slice_data);
-                arm_writer->pc = code_slice->data + 8;
-            }
-
-        } while (code_slice);
-        entry->on_enter_transfer_trampoline = code_slice->data;
-    }
-
-    if (ZzIsEnableDebugMode()) {
-        char buffer[1024] = {};
-        sprintf(buffer + strlen(buffer), "%s\n", "ZzBuildEnterTransferTrampoline:");
-        sprintf(buffer + strlen(buffer),
-                "LogInfo: on_enter_transfer_trampoline at %p, length: %ld. and will jump to on_enter_trampoline(%p).\n",
-                code_slice->data, code_slice->size, entry->on_enter_trampoline);
-        Xinfo("%s", buffer);
-    }
-    return status;
-}
-
-ZZSTATUS ZzBuildEnterTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {
-    zbyte temp_code_slice_data[256] = {0};
-    ZzArmWriter *arm_writer = NULL;
-    ZzArmWriter *thumb_writer = NULL;
-    ZzCodeSlice *code_slice = NULL;
-    ZzArmHookFunctionEntryBackend *entry_backend = (ZzArmHookFunctionEntryBackend *)entry->backend;
-    ZZSTATUS status = ZZ_SUCCESS;
-    zbool is_thumb = TRUE;
-    zpointer target_addr = entry->target_ptr;
-
-    thumb_writer = &self->thumb_writer;
-    zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
-
-    is_thumb = INSTRUCTION_IS_THUMB((zaddr)target_addr);
-
-    code_slice = NULL;
-    do {
-        /* 2 stack space: 1. next_hop 2. entry arg */
-        zz_thumb_writer_put_sub_reg_imm(thumb_writer, ZZ_ARM_REG_SP, 0xc);
-        /* push r7 */
-        zz_thumb_writer_put_str_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0);
-
-        zz_thumb_writer_put_ldr_b_reg_address(thumb_writer, ZZ_ARM_REG_R1, (zaddr)entry);
-        zz_thumb_writer_put_str_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x4);
-
-        /* pop r7 */
-        zz_thumb_writer_put_ldr_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0);
-        zz_thumb_writer_put_add_reg_imm(thumb_writer, ZZ_ARM_REG_SP, 0x4);
-
-        /* jump to enter thunk */
-        zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)self->enter_thunk);
-
-        if (code_slice) {
-            if (!ZzMemoryPatchCode((zaddr)code_slice->data, thumb_writer->base, thumb_writer->size))
-                return ZZ_FAILED;
-            break;
-        }
-
-        code_slice = ZzNewCodeSlice(self->allocator, thumb_writer->size + 4);
-
-        if (!code_slice) {
-#if defined(DEBUG_MODE)
-            debug_break();
-#endif
-            return ZZ_FAILED;
-        } else {
-            zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
-            thumb_writer->pc = code_slice->data + 4;
-        }
-    } while (code_slice);
-
-    if (ZzIsEnableDebugMode()) {
-        char buffer[1024] = {};
-        sprintf(buffer + strlen(buffer), "%s\n", "ZzBuildEnterTrampoline:");
-        sprintf(buffer + strlen(buffer),
-                "LogInfo: on_enter_trampoline at %p, length: %ld. hook-entry: %p. and will jump to enter_thunk(%p)\n",
-                code_slice->data, code_slice->size, (void *)entry, (void *)self->enter_thunk);
-        Xinfo("%s", buffer);
-    }
-
-    entry->on_enter_trampoline = code_slice->data + 1;
-    if ((is_thumb && entry_backend->redirect_code_size == ZZ_THUMB_TINY_REDIRECT_SIZE) ||
-        (!is_thumb && entry_backend->redirect_code_size == ZZ_ARM_TINY_REDIRECT_SIZE)) {
-        ZzBuildEnterTransferTrampoline(self, entry);
-    }
-
-    return status;
-}
-
-ZZSTATUS ZzBuildInvokeTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {
-    zbyte temp_code_slice_data[256] = {0};
-    ZzCodeSlice *code_slice = NULL;
-    ZzArmHookFunctionEntryBackend *entry_backend = (ZzArmHookFunctionEntryBackend *)entry->backend;
-    ZZSTATUS status = ZZ_SUCCESS;
-    zbool is_thumb = TRUE;
-    zpointer target_addr = entry->target_ptr;
-    zpointer target_end_addr = 0;
-    zpointer restore_target_addr;
-
-    is_thumb = INSTRUCTION_IS_THUMB((zaddr)target_addr);
-
-    if (is_thumb) {
-        target_addr = (zpointer)((zaddr)target_addr & ~(zaddr)1);
-        target_end_addr = (zpointer)((zaddr)entry->target_end_ptr & ~(zaddr)1);
-    }
-
-    if (is_thumb) {
-        ZzThumbRelocator *thumb_relocator;
-        ZzThumbWriter *thumb_writer;
-        thumb_relocator = &self->thumb_relocator;
-        thumb_writer = &self->thumb_writer;
-
-        zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
-
-        code_slice = NULL;
-        do {
-            zz_thumb_relocator_reset(thumb_relocator, target_addr, thumb_writer);
-            zsize tmp_relocator_insn_size = 0;
-            entry->target_half_ret_addr = 0;
-
-            if (entry->hook_type == HOOK_FUNCTION_TYPE) {
-                do {
-                    zz_thumb_relocator_read_one(thumb_relocator, NULL);
-                    tmp_relocator_insn_size = thumb_relocator->input_cur - thumb_relocator->input_start;
-                } while (tmp_relocator_insn_size < entry_backend->redirect_code_size);
-                zz_thumb_relocator_write_all(thumb_relocator);
-            } else if (entry->hook_type == HOOK_ADDRESS_TYPE) {
-                do {
-                    zz_thumb_relocator_read_one(thumb_relocator, NULL);
-                    zz_thumb_relocator_write_one(thumb_relocator);
-                    tmp_relocator_insn_size = thumb_relocator->input_cur - thumb_relocator->input_start;
-                    if (thumb_relocator->input_cur >= target_end_addr && !entry->target_half_ret_addr) {
-                        /* jump to rest target address */
-                        zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC,
-                                                            (zaddr)entry->on_half_trampoline);
-
-                        entry->target_half_ret_addr = (zpointer)(thumb_writer->size + 1);
-                    }
-                } while (tmp_relocator_insn_size < entry_backend->redirect_code_size ||
-                         thumb_relocator->input_cur < target_end_addr);
-            }
-
-            restore_target_addr = (zpointer)((zaddr)target_addr + tmp_relocator_insn_size);
-
-            /* jump to rest target address */
-            zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)(restore_target_addr + 1));
-
-            if (code_slice) {
-                if (!ZzMemoryPatchCode((zaddr)code_slice->data, thumb_writer->base, thumb_writer->size))
-                    return ZZ_FAILED;
-                break;
-            }
-            code_slice = ZzNewCodeSlice(self->allocator, thumb_writer->size + 4);
-            if (!code_slice) {
-#if defined(DEBUG_MODE)
-                debug_break();
-#endif
-                return ZZ_FAILED;
-            } else {
-                zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
-                thumb_writer->pc = code_slice->data + 4;
-            }
-        } while (code_slice);
-
-        /* set thumb on_invoke_trampoline */
-        entry->on_invoke_trampoline = code_slice->data + 1;
-    } else {
-        ZzArmRelocator *arm_relocator;
-        ZzArmWriter *arm_writer;
-        arm_relocator = &self->arm_relocator;
-        arm_writer = &self->arm_writer;
-
-        zz_arm_writer_reset(arm_writer, temp_code_slice_data);
-        code_slice = NULL;
-
-        do {
-            zz_arm_relocator_reset(arm_relocator, target_addr, arm_writer);
-            entry->target_half_ret_addr = 0;
-            zsize tmp_relocator_insn_size = 0;
-
-            if (entry->hook_type == HOOK_FUNCTION_TYPE) {
-                do {
-                    zz_arm_relocator_read_one(arm_relocator, NULL);
-                    tmp_relocator_insn_size = arm_relocator->input_cur - arm_relocator->input_start;
-                } while (tmp_relocator_insn_size < entry_backend->redirect_code_size);
-                zz_arm_relocator_write_all(arm_relocator);
-            } else if (entry->hook_type == HOOK_ADDRESS_TYPE) {
-                do {
-                    zz_arm_relocator_read_one(arm_relocator, NULL);
-                    zz_arm_relocator_write_one(arm_relocator);
-                    tmp_relocator_insn_size = arm_relocator->input_cur - arm_relocator->input_start;
-                    if (arm_relocator->input_cur >= target_end_addr && !entry->target_half_ret_addr) {
-                        /* jump to rest target address */
-                        zz_arm_writer_put_ldr_reg_address(arm_writer, ZZ_ARM_REG_PC, (zaddr)entry->on_half_trampoline);
-
-                        entry->target_half_ret_addr = (zpointer)arm_writer->size;
-                    }
-                } while (tmp_relocator_insn_size < entry_backend->redirect_code_size ||
-                         arm_relocator->input_cur < target_end_addr);
-            }
-
-            restore_target_addr = (zpointer)((zaddr)target_addr + tmp_relocator_insn_size);
-
-            /* jump to rest target address */
-            zz_arm_writer_put_ldr_reg_address(arm_writer, ZZ_ARM_REG_PC, (zaddr)restore_target_addr);
-
-            if (code_slice) {
-                if (!ZzMemoryPatchCode((zaddr)code_slice->data, arm_writer->base, arm_writer->size))
-                    return ZZ_FAILED;
-                break;
-            }
-            code_slice = ZzNewCodeSlice(self->allocator, arm_writer->size + 4);
-            if (!code_slice) {
-#if defined(DEBUG_MODE)
-                debug_break();
-#endif
-                return ZZ_FAILED;
-            } else {
-                zz_arm_writer_reset(arm_writer, temp_code_slice_data);
-                arm_writer->pc = code_slice->data + 8;
-            }
-
-        } while (code_slice);
-
-        /* set arm on_invoke_trampoline */
-        entry->on_invoke_trampoline = code_slice->data;
-    }
-
-    if (entry->hook_type == HOOK_ADDRESS_TYPE) {
-        // update target_half_ret_addr
-        entry->target_half_ret_addr += (zaddr)code_slice->data;
-    }
-
-    if (ZzIsEnableDebugMode()) {
-        char buffer[1024] = {};
-        sprintf(buffer + strlen(buffer), "%s\n", "ZzBuildInvokeTrampoline:");
-        sprintf(buffer + strlen(buffer),
-                "LogInfo: on_invoke_trampoline at %p, length: %ld. and will jump to rest code(%p).\n", code_slice->data,
-                code_slice->size, restore_target_addr);
-        if (is_thumb) {
-            sprintf(buffer + strlen(buffer),
-                    "ThumbInstructionFix: origin instruction at %p, end at %p, relocator instruction nums %ld\n",
-                    (&self->thumb_relocator)->input_start, (&self->thumb_relocator)->input_cur,
-                    (&self->thumb_relocator)->inpos);
-        } else {
-            sprintf(buffer + strlen(buffer),
-                    "ArmInstructionFix: origin instruction at %p, end at %p, relocator instruction nums %ld\n",
-                    (&self->arm_relocator)->input_start, (&self->arm_relocator)->input_cur,
-                    (&self->arm_relocator)->inpos);
-        }
-
-        char origin_prologue[256] = {0};
-        int t = 0;
-        if (is_thumb) {
-            for (zpointer p = (&self->thumb_relocator)->input_start; p < (&self->thumb_relocator)->input_cur;
-                 p++, t = t + 5) {
-                sprintf(origin_prologue + t, "0x%.2x ", *(unsigned char *)p);
-            }
-        } else {
-            for (zpointer p = (&self->arm_relocator)->input_start; p < (&self->arm_relocator)->input_cur;
-                 p++, t = t + 5) {
-                sprintf(origin_prologue + t, "0x%.2x ", *(unsigned char *)p);
-            }
-        }
-        sprintf(buffer + strlen(buffer), "origin_prologue: %s\n", origin_prologue);
-
-        Xinfo("%s", buffer);
-    }
-
-    return status;
-}
-
-ZZSTATUS ZzBuildHalfTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {
-    zbyte temp_code_slice_data[256] = {0};
-    ZzArmWriter *arm_writer = NULL;
-    ZzArmWriter *thumb_writer = NULL;
-    ZzCodeSlice *code_slice = NULL;
-    ZzArmHookFunctionEntryBackend *entry_backend = (ZzArmHookFunctionEntryBackend *)entry->backend;
-    ZZSTATUS status = ZZ_SUCCESS;
-    zbool is_thumb = TRUE;
-    zpointer target_addr = entry->target_ptr;
-
-    thumb_writer = &self->thumb_writer;
-    zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
-
-    is_thumb = INSTRUCTION_IS_THUMB((zaddr)target_addr);
-
-    code_slice = NULL;
-    do {
-        /* 2 stack space: 1. next_hop 2. entry arg */
-        zz_thumb_writer_put_sub_reg_imm(thumb_writer, ZZ_ARM_REG_SP, 0xc);
-        /* push r7 */
-        zz_thumb_writer_put_str_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0);
-
-        zz_thumb_writer_put_ldr_b_reg_address(thumb_writer, ZZ_ARM_REG_R1, (zaddr)entry);
-        zz_thumb_writer_put_str_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x4);
-
-        /* pop r7 */
-        zz_thumb_writer_put_ldr_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0);
-        zz_thumb_writer_put_add_reg_imm(thumb_writer, ZZ_ARM_REG_SP, 0x4);
-
-        zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)self->half_thunk);
-
-        if (code_slice) {
-            if (!ZzMemoryPatchCode((zaddr)code_slice->data, thumb_writer->base, thumb_writer->size))
-                return ZZ_FAILED;
-            break;
-        }
-        code_slice = ZzNewCodeSlice(self->allocator, thumb_writer->size + 4);
-
-        if (!code_slice) {
-#if defined(DEBUG_MODE)
-            debug_break();
-#endif
-            return ZZ_FAILED;
-        } else {
-            zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
-            thumb_writer->pc = code_slice->data + 4;
-        }
-    } while (code_slice);
-
-    /* set thumb on_half_trampoline */
-    entry->on_half_trampoline = code_slice->data + 1;
-
-    return status;
-}
-
-ZZSTATUS ZzBuildLeaveTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {
-    zbyte temp_code_slice_data[256] = {0};
-    ZzCodeSlice *code_slice = NULL;
-    ZZSTATUS status = ZZ_SUCCESS;
-    zbool is_thumb = TRUE;
-    ZzArmWriter *thumb_writer;
-
-    ZzArmHookFunctionEntryBackend *entry_backend = (ZzArmHookFunctionEntryBackend *)entry->backend;
-
-    zpointer target_addr = entry->target_ptr;
-    thumb_writer = &self->thumb_writer;
-    zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
-
-    is_thumb = INSTRUCTION_IS_THUMB((zaddr)target_addr);
-
-    code_slice = NULL;
-    do {
-        /* 2 stack space: 1. next_hop 2. entry arg */
-        zz_thumb_writer_put_sub_reg_imm(thumb_writer, ZZ_ARM_REG_SP, 0xc);
-        /* push r7 */
-        zz_thumb_writer_put_str_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0);
-
-        zz_thumb_writer_put_ldr_b_reg_address(thumb_writer, ZZ_ARM_REG_R1, (zaddr)entry);
-        zz_thumb_writer_put_str_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x4);
-
-        /* pop r7 */
-        zz_thumb_writer_put_ldr_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0);
-        zz_thumb_writer_put_add_reg_imm(thumb_writer, ZZ_ARM_REG_SP, 0x4);
-
-        zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)self->leave_thunk);
-
-        if (code_slice) {
-
-            if (!ZzMemoryPatchCode((zaddr)code_slice->data, thumb_writer->base, thumb_writer->size))
-                return ZZ_FAILED;
-            break;
-        }
-        code_slice = ZzNewCodeSlice(self->allocator, thumb_writer->size + 4);
-        if (!code_slice) {
-#if defined(DEBUG_MODE)
-            debug_break();
-#endif
-            return ZZ_FAILED;
-        } else {
-            zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
-            thumb_writer->pc = code_slice->data + 4;
-        }
-    } while (code_slice);
-
-    if (ZzIsEnableDebugMode()) {
-        char buffer[1024] = {};
-        sprintf(buffer + strlen(buffer), "%s\n", "ZzBuildLeaveTrampoline:");
-        sprintf(buffer + strlen(buffer),
-                "LogInfo: on_leave_trampoline at %p, length: %ld. and will jump to leave_thunk(%p).\n",
-                code_slice->data, code_slice->size, self->leave_thunk);
-        Xinfo("%s", buffer);
-    }
-
-    /* set thumb on_leave_trampoline */
-    entry->on_leave_trampoline = code_slice->data + 1;
-
-    return ZZ_DONE;
-}
-
-ZZSTATUS ZzActivateTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {
-    zbyte temp_code_slice_data[256] = {0};
-    ZzCodeSlice *code_slice = NULL;
-    ZzArmHookFunctionEntryBackend *entry_backend = (ZzArmHookFunctionEntryBackend *)entry->backend;
-    ZZSTATUS status = ZZ_SUCCESS;
-    zbool is_thumb = TRUE;
-    zpointer target_addr = entry->target_ptr;
-
-    is_thumb = INSTRUCTION_IS_THUMB((zaddr)target_addr);
-
-    if (is_thumb) {
-        target_addr = (zpointer)((zaddr)target_addr & ~(zaddr)1);
-
-        ZzThumbWriter *thumb_writer;
-        thumb_writer = &self->thumb_writer;
-        zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
-        thumb_writer->pc = target_addr + 4;
-
-        if (entry_backend->redirect_code_size == ZZ_THUMB_TINY_REDIRECT_SIZE) {
-            zz_thumb_writer_put_b_imm32(thumb_writer,
-                                        (zaddr)entry->on_enter_transfer_trampoline - (zaddr)thumb_writer->pc);
-        } else {
-            zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)entry->on_enter_trampoline);
-        }
-        if (!ZzMemoryPatchCode((zaddr)target_addr, thumb_writer->base, thumb_writer->size))
-            return ZZ_FAILED;
-    } else {
-        ZzArmWriter *arm_writer;
-        arm_writer = &self->arm_writer;
-        zz_arm_writer_reset(arm_writer, temp_code_slice_data);
-        arm_writer->pc = target_addr + 8;
-
-        if (entry_backend->redirect_code_size == ZZ_ARM_TINY_REDIRECT_SIZE) {
-            zz_arm_writer_put_b_imm(arm_writer, (zaddr)entry->on_enter_transfer_trampoline - (zaddr)arm_writer->pc);
-        } else {
-            zz_arm_writer_put_ldr_reg_address(arm_writer, ZZ_ARM_REG_PC, (zaddr)entry->on_enter_trampoline);
-        }
-        if (!ZzMemoryPatchCode((zaddr)target_addr, arm_writer->base, arm_writer->size))
-            return ZZ_FAILED;
-    }
-
-    return ZZ_DONE_HOOK;
-}
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm64/trampoline-arm64.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm64/interceptor-arm64.c
similarity index 50%
rename from VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm64/trampoline-arm64.c
rename to VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm64/interceptor-arm64.c
index e7dd82489..3df307a8d 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm64/trampoline-arm64.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm64/interceptor-arm64.c
@@ -15,6 +15,7 @@
  */
 
 #include "interceptor-arm64.h"
+#include "zzinfo.h"
 #include <stdlib.h>
 #include <string.h>
 
@@ -24,121 +25,112 @@
 ZzInterceptorBackend *ZzBuildInteceptorBackend(ZzAllocator *allocator) {
     if (!ZzMemoryIsSupportAllocateRXPage()) {
         return NULL;
-        // return backend;
     }
+    ZZSTATUS status;
+
     ZzInterceptorBackend *backend = (ZzInterceptorBackend *)malloc(sizeof(ZzInterceptorBackend));
-    backend->allocator = allocator;
+    memset(backend, 0, sizeof(ZzInterceptorBackend));
 
     zz_arm64_writer_init(&backend->arm64_writer, NULL);
     zz_arm64_relocator_init(&backend->arm64_relocator, NULL, &backend->arm64_writer);
 
+    backend->allocator = allocator;
     backend->enter_thunk = NULL;
     backend->half_thunk = NULL;
     backend->leave_thunk = NULL;
 
-    ZzThunkerBuildThunk(backend);
+    status = ZzThunkerBuildThunk(backend);
+    if (status == ZZ_FAILED) {
+        ZzInfoLog("%s", "ZzThunkerBuildThunk return ZZ_FAILED\n");
+        return NULL;
+    }
+
     return backend;
 }
 
+ZzCodeSlice *zz_code_patch_arm64_writer(ZzArm64Writer *arm64_writer, ZzAllocator *allocator, zaddr target_addr,
+                                        zsize range_size) {
+    ZzCodeSlice *code_slice = NULL;
+    if (range_size > 0) {
+        code_slice = ZzNewNearCodeSlice(allocator, target_addr, range_size, arm64_writer->size);
+    } else {
+        code_slice = ZzNewCodeSlice(allocator, arm64_writer->size + 4);
+    }
+    if (!code_slice)
+        return NULL;
+
+    if (!ZzMemoryPatchCode((zaddr)code_slice->data, arm64_writer->base, arm64_writer->size)) {
+        free(code_slice);
+        return NULL;
+    }
+    return code_slice;
+}
+
+ZzCodeSlice *zz_code_patch_arm64_relocate_writer(ZzArm64Relocator *relocator, ZzArm64Writer *arm64_writer,
+                                                 ZzAllocator *allocator, zaddr target_addr, zsize range_size) {
+    ZzCodeSlice *code_slice = NULL;
+    if (range_size > 0) {
+        code_slice = ZzNewNearCodeSlice(allocator, target_addr, range_size, arm64_writer->size);
+    } else {
+        code_slice = ZzNewCodeSlice(allocator, arm64_writer->size + 4);
+    }
+    if (!code_slice)
+        return NULL;
+
+    if (!ZzMemoryPatchCode((zaddr)code_slice->data, arm64_writer->base, arm64_writer->size)) {
+        free(code_slice);
+        return NULL;
+    }
+    return code_slice;
+}
+
 ZZSTATUS ZzPrepareTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {
-    zpointer target_addr = entry->target_ptr;
-    zuint redirect_limit;
+    zaddr target_addr = (zaddr)entry->target_ptr;
+    zuint redirect_limit = 0;
 
     ZzArm64HookFunctionEntryBackend *entry_backend;
     entry_backend = (ZzArm64HookFunctionEntryBackend *)malloc(sizeof(ZzArm64HookFunctionEntryBackend));
+    memset(entry_backend, 0, sizeof(ZzArm64HookFunctionEntryBackend));
+
     entry->backend = (struct _ZzHookFunctionEntryBackend *)entry_backend;
 
     if (entry->try_near_jump) {
         entry_backend->redirect_code_size = ZZ_ARM64_TINY_REDIRECT_SIZE;
     } else {
-        zz_arm64_relocator_try_relocate(target_addr, ZZ_ARM64_FULL_REDIRECT_SIZE, &redirect_limit);
-        entry_backend->redirect_code_size = ZZ_ARM64_FULL_REDIRECT_SIZE;
+        zz_arm64_relocator_try_relocate((zpointer)target_addr, ZZ_ARM64_FULL_REDIRECT_SIZE, &redirect_limit);
+        if (redirect_limit != 0 && redirect_limit > ZZ_ARM64_TINY_REDIRECT_SIZE &&
+            redirect_limit < ZZ_ARM64_FULL_REDIRECT_SIZE) {
+            entry->try_near_jump = TRUE;
+            entry_backend->redirect_code_size = ZZ_ARM64_TINY_REDIRECT_SIZE;
+        } else if (redirect_limit != 0 && redirect_limit < ZZ_ARM64_TINY_REDIRECT_SIZE) {
+            return ZZ_FAILED;
+        } else {
+            entry_backend->redirect_code_size = ZZ_ARM64_FULL_REDIRECT_SIZE;
+        }
     }
 
-    zz_arm64_relocator_init(&self->arm64_relocator, target_addr, &self->arm64_writer);
+    self->arm64_relocator.try_relocated_length = entry_backend->redirect_code_size;
+    zz_arm64_relocator_init(&self->arm64_relocator, (zpointer)target_addr, &self->arm64_writer);
     return ZZ_SUCCESS;
 }
 
-// __attribute__((__naked__)) void on_enter_trampoline_template() {
-//     __asm__ volatile(
-//         /* store entry address and reserve space for next hop */
-//         "sub sp, sp, 0x10\n"
-//         "ldr x17, #0x8\n"
-//         "b #0xc\n"
-//         /* entry address */
-//         ".long 0x0\n"
-//         ".long 0x0\n"
-//         "str x17, [sp]\n"
-//         "ldr x17, #0x8\n"
-//         "br x17\n"
-//         /* enter_thunk address */
-//         ".long 0x0\n"
-//         ".long 0x0");
-// }
-
-// __attribute__((__naked__)) void on_inovke_trampoline_template() {
-//     __asm__ volatile(
-//         /* fixed instruction */
-//         "nop\n"
-//         "nop\n"
-//         "nop\n"
-//         "nop\n"
-//         "nop\n"
-//         "nop\n"
-//         "nop\n"
-//         "nop\n"
-//         "ldr x17, #8\n"
-//         "br x17\n"
-//         /* rest of orgin function address */
-//         ".long 0x0\n"
-//         ".long 0x0");
-// }
-
-// __attribute__((__naked__)) void on_leave_trampoline_template() {
-//     __asm__ volatile(
-//         /* store entry address and reserve space for next hop */
-//         "sub sp, sp, 0x10\n"
-//         "ldr x17, #0x8\n"
-//         "b #0xc\n"
-//         /* entry address */
-//         ".long 0x0\n"
-//         ".long 0x0\n"
-//         "str x17, [sp]\n"
-//         "ldr x17, #0x8\n"
-//         "br x17\n"
-//         /* leave_thunk address */
-//         ".long 0x0\n"
-//         ".long 0x0");
-// }
 ZZSTATUS ZzBuildEnterTransferTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {
     zbyte temp_code_slice_data[256] = {0};
     ZzArm64Writer *arm64_writer = NULL;
     ZzCodeSlice *code_slice = NULL;
     ZzArm64HookFunctionEntryBackend *entry_backend = (ZzArm64HookFunctionEntryBackend *)entry->backend;
     ZZSTATUS status = ZZ_SUCCESS;
-    zpointer target_addr = entry->target_ptr;
+    zaddr target_addr = (zaddr)entry->target_ptr;
 
     arm64_writer = &self->arm64_writer;
     zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);
-
-    code_slice = NULL;
-    do {
-        zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)entry->on_enter_trampoline);
-        if (code_slice) {
-            if (!ZzMemoryPatchCode((zaddr)code_slice->data, arm64_writer->base, arm64_writer->size))
-                return ZZ_FAILED;
-            break;
-        }
-        code_slice = ZzNewNearCodeSlice(self->allocator, (zaddr)entry->target_ptr,
-                                        zz_arm64_writer_near_jump_range_size(), arm64_writer->size + 4);
-        if (!code_slice) {
-            return ZZ_FAILED;
-        } else {
-            zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);
-            arm64_writer->pc = code_slice->data;
-        }
-    } while (code_slice);
-    entry->on_enter_transfer_trampoline = code_slice->data;
+    zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)entry->on_enter_trampoline);
+    code_slice =
+        zz_code_patch_arm64_writer(arm64_writer, self->allocator, target_addr, zz_arm64_writer_near_jump_range_size());
+    if (code_slice)
+        entry->on_enter_transfer_trampoline = code_slice->data;
+    else
+        return ZZ_FAILED;
 
     if (ZzIsEnableDebugMode()) {
         char buffer[1024] = {};
@@ -146,8 +138,10 @@ ZZSTATUS ZzBuildEnterTransferTrampoline(ZzInterceptorBackend *self, ZzHookFuncti
         sprintf(buffer + strlen(buffer),
                 "LogInfo: on_enter_transfer_trampoline at %p, length: %ld. and will jump to on_enter_trampoline(%p).\n",
                 code_slice->data, code_slice->size, entry->on_enter_trampoline);
-        Xinfo("%s", buffer);
+        ZzInfoLog("%s", buffer);
     }
+
+    free(code_slice);
     return status;
 }
 ZZSTATUS ZzBuildEnterTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {
@@ -156,56 +150,41 @@ ZZSTATUS ZzBuildEnterTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry
     ZzCodeSlice *code_slice = NULL;
     ZzArm64HookFunctionEntryBackend *entry_backend = (ZzArm64HookFunctionEntryBackend *)entry->backend;
     ZZSTATUS status = ZZ_SUCCESS;
-    zpointer target_addr = entry->target_ptr;
+    zaddr target_addr = (zaddr)entry->target_ptr;
 
     arm64_writer = &self->arm64_writer;
     zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);
 
-    code_slice = NULL;
-    do {
-        /* 2 stack space: 1. next_hop 2. entry arg */
-        zz_arm64_writer_put_sub_reg_reg_imm(arm64_writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 0x8);
+    /* prepare 2 stack space: 1. next_hop 2. entry arg */
+    zz_arm64_writer_put_sub_reg_reg_imm(arm64_writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 0x8);
+    zz_arm64_writer_put_ldr_b_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)entry);
+    zz_arm64_writer_put_str_reg_reg_offset(arm64_writer, ZZ_ARM64_REG_X17, ZZ_ARM64_REG_SP, 0x0);
 
-        zz_arm64_writer_put_ldr_b_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)entry);
-        zz_arm64_writer_put_str_reg_reg_offset(arm64_writer, ZZ_ARM64_REG_X17, ZZ_ARM64_REG_SP, 0x0);
+    /* jump to enter thunk */
+    zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)self->enter_thunk);
 
-        /* jump to enter thunk */
-        zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)self->enter_thunk);
-
-        if (code_slice) {
-            if (!ZzMemoryPatchCode((zaddr)code_slice->data, arm64_writer->base, arm64_writer->size))
-                return ZZ_FAILED;
-            break;
-        }
+    /* code patch */
+    code_slice = zz_code_patch_arm64_writer(arm64_writer, self->allocator, 0, 0);
+    if (code_slice)
+        entry->on_enter_trampoline = code_slice->data;
+    else
+        return ZZ_FAILED;
 
-        if (!code_slice)
-            code_slice = ZzNewCodeSlice(self->allocator, arm64_writer->size + 4);
-
-        if (!code_slice) {
-#if defined(DEBUG_MODE)
-            debug_break();
-#endif
-            return ZZ_FAILED;
-        } else {
-            zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);
-            arm64_writer->pc = code_slice->data;
-        }
-    } while (code_slice);
+    /* debug log */
     if (ZzIsEnableDebugMode()) {
         char buffer[1024] = {};
         sprintf(buffer + strlen(buffer), "%s\n", "ZzBuildEnterTrampoline:");
         sprintf(buffer + strlen(buffer),
                 "LogInfo: on_enter_trampoline at %p, length: %ld. hook-entry: %p. and will jump to enter_thunk(%p).\n",
                 code_slice->data, code_slice->size, (void *)entry, (void *)self->enter_thunk);
-        Xinfo("%s", buffer);
+        ZzInfoLog("%s", buffer);
     }
 
-    entry->on_enter_trampoline = code_slice->data;
-
     if (entry_backend->redirect_code_size == ZZ_ARM64_TINY_REDIRECT_SIZE) {
         ZzBuildEnterTransferTrampoline(self, entry);
     }
 
+    free(code_slice);
     return status;
 }
 
@@ -214,7 +193,7 @@ ZZSTATUS ZzBuildInvokeTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry
     ZzCodeSlice *code_slice = NULL;
     ZzArm64HookFunctionEntryBackend *entry_backend = (ZzArm64HookFunctionEntryBackend *)entry->backend;
     ZZSTATUS status = ZZ_SUCCESS;
-    zpointer target_addr = entry->target_ptr;
+    zaddr target_addr = (zaddr)entry->target_ptr;
     zpointer restore_target_addr;
 
     ZzArm64Relocator *arm64_relocator;
@@ -223,56 +202,48 @@ ZZSTATUS ZzBuildInvokeTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry
     arm64_writer = &self->arm64_writer;
 
     zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);
+    zz_arm64_relocator_reset(arm64_relocator, (zpointer)target_addr, arm64_writer);
+    zsize tmp_relocator_insn_size = 0;
+    entry->target_half_ret_addr = 0;
+
+    if (entry->hook_type == HOOK_FUNCTION_TYPE) {
+        do {
+            zz_arm64_relocator_read_one(arm64_relocator, NULL);
+            tmp_relocator_insn_size = arm64_relocator->input_cur - arm64_relocator->input_start;
+        } while (tmp_relocator_insn_size < entry_backend->redirect_code_size);
+        zz_arm64_relocator_write_all(arm64_relocator);
+    } else if (entry->hook_type == HOOK_ADDRESS_TYPE) {
+        do {
+            zz_arm64_relocator_read_one(arm64_relocator, NULL);
+            zz_arm64_relocator_write_one(arm64_relocator);
+            tmp_relocator_insn_size = arm64_relocator->input_cur - arm64_relocator->input_start;
+            if (arm64_relocator->input_cur >= entry->target_end_ptr && !entry->target_half_ret_addr) {
+                zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17,
+                                                       (zaddr)entry->on_half_trampoline);
+
+                entry->target_half_ret_addr = (zpointer)arm64_writer->size;
+            }
+        } while (tmp_relocator_insn_size < entry_backend->redirect_code_size ||
+                 arm64_relocator->input_cur < entry->target_end_ptr);
+    }
 
-    code_slice = NULL;
-    do {
-        zz_arm64_relocator_reset(arm64_relocator, target_addr, arm64_writer);
-        zsize tmp_relocator_insn_size = 0;
-        entry->target_half_ret_addr = 0;
-
-        if (entry->hook_type == HOOK_FUNCTION_TYPE) {
-            do {
-                zz_arm64_relocator_read_one(arm64_relocator, NULL);
-                tmp_relocator_insn_size = arm64_relocator->input_cur - arm64_relocator->input_start;
-            } while (tmp_relocator_insn_size < entry_backend->redirect_code_size);
-            zz_arm64_relocator_write_all(arm64_relocator);
-        } else if (entry->hook_type == HOOK_ADDRESS_TYPE) {
-            do {
-                zz_arm64_relocator_read_one(arm64_relocator, NULL);
-                zz_arm64_relocator_write_one(arm64_relocator);
-                tmp_relocator_insn_size = arm64_relocator->input_cur - arm64_relocator->input_start;
-                if (arm64_relocator->input_cur >= entry->target_end_ptr && !entry->target_half_ret_addr) {
-                    /* jump to rest target address */
-                    zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17,
-                                                           (zaddr)entry->on_half_trampoline);
-
-                    entry->target_half_ret_addr = (zpointer)arm64_writer->size;
-                }
-            } while (tmp_relocator_insn_size < entry_backend->redirect_code_size ||
-                     arm64_relocator->input_cur < entry->target_end_ptr);
-        }
+    /* jump to rest target address */
+    restore_target_addr = (zpointer)((zaddr)target_addr + tmp_relocator_insn_size);
+    zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)restore_target_addr);
 
-        restore_target_addr = (zpointer)((zaddr)target_addr + tmp_relocator_insn_size);
+    /* code patch */
+    code_slice = zz_code_patch_arm64_relocate_writer(arm64_relocator, arm64_writer, self->allocator, 0, 0);
+    if (code_slice)
+        entry->on_invoke_trampoline = code_slice->data;
+    else
+        return ZZ_FAILED;
 
-        /* jump to rest target address */
-        zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)restore_target_addr);
+    /* update target_half_ret_addr */
+    if (entry->hook_type == HOOK_ADDRESS_TYPE) {
+        entry->target_half_ret_addr += (zaddr)code_slice->data;
+    }
 
-        if (code_slice) {
-            if (!ZzMemoryPatchCode((zaddr)code_slice->data, arm64_writer->base, arm64_writer->size))
-                return ZZ_FAILED;
-            break;
-        }
-        code_slice = ZzNewCodeSlice(self->allocator, arm64_writer->size + 4);
-        if (!code_slice) {
-#if defined(DEBUG_MODE)
-            debug_break();
-#endif
-            return ZZ_FAILED;
-        } else {
-            zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);
-            arm64_writer->pc = code_slice->data;
-        }
-    } while (code_slice);
+    /* debug log */
     if (ZzIsEnableDebugMode()) {
         char buffer[1024] = {0};
         sprintf(buffer + strlen(buffer), "%s\n", "ZzBuildInvokeTrampoline:");
@@ -286,20 +257,16 @@ ZZSTATUS ZzBuildInvokeTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry
 
         char origin_prologue[256] = {0};
         int t = 0;
-        for (zpointer p = (&self->arm64_relocator)->input_start; p < (&self->arm64_relocator)->input_cur;
-             p++, t = t + 5) {
+        zpointer p;
+        for (p = (&self->arm64_relocator)->input_start; p < (&self->arm64_relocator)->input_cur; p++, t = t + 5) {
             sprintf(origin_prologue + t, "0x%.2x ", *(unsigned char *)p);
         }
         sprintf(buffer + strlen(buffer), "origin_prologue: %s\n", origin_prologue);
 
-        Xinfo("%s", buffer);
+        ZzInfoLog("%s", buffer);
     }
 
-    if (entry->hook_type == HOOK_ADDRESS_TYPE) {
-        // update target_half_ret_addr
-        entry->target_half_ret_addr += (zaddr)code_slice->data;
-    }
-    entry->on_invoke_trampoline = code_slice->data;
+    free(code_slice);
     return status;
 }
 
@@ -309,42 +276,25 @@ ZZSTATUS ZzBuildHalfTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *
     ZzCodeSlice *code_slice = NULL;
     ZzArm64HookFunctionEntryBackend *entry_backend = (ZzArm64HookFunctionEntryBackend *)entry->backend;
     ZZSTATUS status = ZZ_SUCCESS;
-    zpointer target_addr = entry->target_ptr;
+    zaddr target_addr = (zaddr)entry->target_ptr;
 
     arm64_writer = &self->arm64_writer;
     zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);
 
-    code_slice = NULL;
-    do {
-        /* 2 stack space: 1. next_hop 2. entry arg */
-        zz_arm64_writer_put_sub_reg_reg_imm(arm64_writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 0x8);
-
-        zz_arm64_writer_put_ldr_b_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)entry);
-        zz_arm64_writer_put_str_reg_reg_offset(arm64_writer, ZZ_ARM64_REG_X17, ZZ_ARM64_REG_SP, 0x0);
-
-        zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)self->half_thunk);
-
-        if (code_slice) {
-            if (!ZzMemoryPatchCode((zaddr)code_slice->data, arm64_writer->base, arm64_writer->size))
-                return ZZ_FAILED;
-            break;
-        }
+    /* prepare 2 stack space: 1. next_hop 2. entry arg */
+    zz_arm64_writer_put_sub_reg_reg_imm(arm64_writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 0x8);
+    zz_arm64_writer_put_ldr_b_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)entry);
+    zz_arm64_writer_put_str_reg_reg_offset(arm64_writer, ZZ_ARM64_REG_X17, ZZ_ARM64_REG_SP, 0x0);
 
-        if (!code_slice)
-            code_slice = ZzNewCodeSlice(self->allocator, arm64_writer->size + 4);
-        if (!code_slice) {
-#if defined(DEBUG_MODE)
-            debug_break();
-#endif
-            return ZZ_FAILED;
-        } else {
-            zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);
-            arm64_writer->pc = code_slice->data;
-        }
-    } while (code_slice);
+    /* jump to half thunk */
+    zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)self->half_thunk);
 
-    /* set arm64 on_half_trampoline */
-    entry->on_half_trampoline = code_slice->data;
+    /* code patch */
+    code_slice = zz_code_patch_arm64_writer(arm64_writer, self->allocator, 0, 0);
+    if (code_slice)
+        entry->on_half_trampoline = code_slice->data;
+    else
+        return ZZ_FAILED;
 
     return status;
 }
@@ -353,50 +303,38 @@ ZZSTATUS ZzBuildLeaveTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry
     zbyte temp_code_slice_data[256] = {0};
     ZzCodeSlice *code_slice = NULL;
     ZzArm64HookFunctionEntryBackend *entry_backend = (ZzArm64HookFunctionEntryBackend *)entry->backend;
-    zpointer target_addr = entry->target_ptr;
-    ZzArm64Writer *arm64_writer;
+    zaddr target_addr = (zaddr)entry->target_ptr;
+    ZzArm64Writer *arm64_writer = NULL;
 
     arm64_writer = &self->arm64_writer;
     zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);
 
-    do {
-        /* 2 stack space: 1. next_hop 2. entry arg */
-        zz_arm64_writer_put_sub_reg_reg_imm(arm64_writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 0x8);
+    /* prepare 2 stack space: 1. next_hop 2. entry arg */
+    zz_arm64_writer_put_sub_reg_reg_imm(arm64_writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 0x8);
+    zz_arm64_writer_put_ldr_b_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)entry);
+    zz_arm64_writer_put_str_reg_reg_offset(arm64_writer, ZZ_ARM64_REG_X17, ZZ_ARM64_REG_SP, 0x0);
 
-        zz_arm64_writer_put_ldr_b_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)entry);
-        zz_arm64_writer_put_str_reg_reg_offset(arm64_writer, ZZ_ARM64_REG_X17, ZZ_ARM64_REG_SP, 0x0);
+    /* jump to leave thunk */
+    zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)self->leave_thunk);
 
-        /* jump to leave thunk */
-        zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)self->leave_thunk);
-        if (code_slice) {
-            if (!ZzMemoryPatchCode((zaddr)code_slice->data, arm64_writer->base, arm64_writer->size))
-                return ZZ_FAILED;
-            break;
-        }
-        code_slice = ZzNewCodeSlice(self->allocator, arm64_writer->size + 4);
-        if (!code_slice) {
-#if defined(DEBUG_MODE)
-            debug_break();
-#endif
-            return ZZ_FAILED;
-        } else {
-            zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);
-            arm64_writer->pc = code_slice->data;
-        }
-    } while (code_slice);
+    /* code patch */
+    code_slice = zz_code_patch_arm64_writer(arm64_writer, self->allocator, 0, 0);
+    if (code_slice)
+        entry->on_leave_trampoline = code_slice->data;
+    else
+        return ZZ_FAILED;
 
+    /* debug log */
     if (ZzIsEnableDebugMode()) {
         char buffer[1024] = {};
         sprintf(buffer + strlen(buffer), "%s\n", "ZzBuildLeaveTrampoline:");
         sprintf(buffer + strlen(buffer),
                 "LogInfo: on_leave_trampoline at %p, length: %ld. and will jump to leave_thunk(%p).\n",
                 code_slice->data, code_slice->size, self->leave_thunk);
-        Xinfo("%s", buffer);
+        ZzInfoLog("%s", buffer);
     }
 
-    /* set arm64 on_leave_trampoline */
-    entry->on_leave_trampoline = code_slice->data;
-
+    free(code_slice);
     return ZZ_DONE;
 }
 
@@ -405,7 +343,7 @@ ZZSTATUS ZzActivateTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *e
     ZzCodeSlice *code_slice = NULL;
     ZzArm64HookFunctionEntryBackend *entry_backend = (ZzArm64HookFunctionEntryBackend *)entry->backend;
     ZZSTATUS status = ZZ_SUCCESS;
-    zpointer target_addr = entry->target_ptr;
+    zaddr target_addr = (zaddr)entry->target_ptr;
     ZzArm64Writer *arm64_writer;
 
     arm64_writer = &self->arm64_writer;
@@ -413,7 +351,7 @@ ZZSTATUS ZzActivateTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *e
     arm64_writer->pc = target_addr;
 
     if (entry_backend->redirect_code_size == ZZ_ARM64_TINY_REDIRECT_SIZE) {
-        zz_arm64_writer_put_b_imm(arm64_writer, (zaddr)entry->on_enter_transfer_trampoline - (zaddr)target_addr);
+        zz_arm64_writer_put_b_imm(arm64_writer, (zaddr)entry->on_enter_transfer_trampoline - (zaddr)arm64_writer->pc);
     } else {
         zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)entry->on_enter_trampoline);
     }
@@ -457,8 +395,8 @@ ZZSTATUS ZzActivateSolidifyTrampoline(ZzHookFunctionEntry *entry, zaddr target_f
 
     ZzHookFunctionEntryNoJB *nojb_entry =
         (ZzHookFunctionEntryNoJB *)(data_seg_cmd->vmaddr + sizeof(ZzHookFunctionEntryNoJB) + aslr_slide);
-
-    for (unsigned long i = 0; i < nojb_backend->num_of_entry; i++) {
+    unsigned long i;
+    for (i = 0; i < nojb_backend->num_of_entry; i++) {
         nojb_entry = &nojb_entry[i];
         if ((zaddr)nojb_entry->target_fileoff == target_fileoff) {
             nojb_entry->entry_address = entry;
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm64/interceptor-arm64.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm64/interceptor-arm64.h
index 77eef0514..8001aca0b 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm64/interceptor-arm64.h
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm64/interceptor-arm64.h
@@ -57,4 +57,7 @@ void leave_thunk_template();
 void on_enter_trampoline_template();
 void on_invoke_trampoline_template();
 void on_leave_trampoline_template();
+
+ZzCodeSlice *zz_code_patch_arm64_writer(ZzArm64Writer *arm64_writer, ZzAllocator *allocator, zaddr target_addr,
+                                        zsize range_size);
 #endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm64/thunker-arm64.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm64/thunker-arm64.c
index 0956b1bfe..a5b3dc648 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm64/thunker-arm64.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm64/thunker-arm64.c
@@ -15,6 +15,7 @@
  */
 
 #include "thunker-arm64.h"
+#include "zzinfo.h"
 #include <string.h>
 
 /*
@@ -103,22 +104,23 @@
 // just like pre_call, wow!
 void function_context_begin_invocation(ZzHookFunctionEntry *entry, zpointer next_hop, RegState *rs,
                                        zpointer caller_ret_addr) {
+    Xinfo("target %p call begin-invocation", entry->target_ptr);
 
-    Xdebug("target %p call begin-invocation", entry->target_ptr);
     ZzThreadStack *stack = ZzGetCurrentThreadStack(entry->thread_local_key);
     if (!stack) {
         stack = ZzNewThreadStack(entry->thread_local_key);
     }
-
     ZzCallStack *callstack = ZzNewCallStack();
     ZzPushCallStack(stack, callstack);
 
+    /* call pre_call */
     if (entry->pre_call) {
         PRECALL pre_call;
         pre_call = entry->pre_call;
         (*pre_call)(rs, (ThreadStack *)stack, (CallStack *)callstack);
     }
 
+    /* set next hop */
     if (entry->replace_call) {
         *(zpointer *)next_hop = entry->replace_call;
     } else {
@@ -135,6 +137,7 @@ void function_context_begin_invocation(ZzHookFunctionEntry *entry, zpointer next
 void function_context_half_invocation(ZzHookFunctionEntry *entry, zpointer next_hop, RegState *rs,
                                       zpointer caller_ret_addr) {
     Xdebug("target %p call half-invocation", entry->target_ptr);
+
     ZzThreadStack *stack = ZzGetCurrentThreadStack(entry->thread_local_key);
     if (!stack) {
 #if defined(DEBUG_MODE)
@@ -143,11 +146,14 @@ void function_context_half_invocation(ZzHookFunctionEntry *entry, zpointer next_
     }
     ZzCallStack *callstack = ZzPopCallStack(stack);
 
+    /* call half_call */
     if (entry->half_call) {
         HALFCALL half_call;
         half_call = entry->half_call;
         (*half_call)(rs, (ThreadStack *)stack, (CallStack *)callstack);
     }
+
+    /*  set next hop */
     *(zpointer *)next_hop = (zpointer)entry->target_half_ret_addr;
 
     ZzFreeCallStack(callstack);
@@ -156,6 +162,7 @@ void function_context_half_invocation(ZzHookFunctionEntry *entry, zpointer next_
 // just like post_call, wow!
 void function_context_end_invocation(ZzHookFunctionEntry *entry, zpointer next_hop, RegState *rs) {
     Xdebug("%p call end-invocation", entry->target_ptr);
+
     ZzThreadStack *stack = ZzGetCurrentThreadStack(entry->thread_local_key);
     if (!stack) {
 #if defined(DEBUG_MODE)
@@ -164,13 +171,15 @@ void function_context_end_invocation(ZzHookFunctionEntry *entry, zpointer next_h
     }
     ZzCallStack *callstack = ZzPopCallStack(stack);
 
+    /* call post_call */
     if (entry->post_call) {
         POSTCALL post_call;
         post_call = entry->post_call;
         (*post_call)(rs, (ThreadStack *)stack, (CallStack *)callstack);
     }
-    *(zpointer *)next_hop = callstack->caller_ret_addr;
 
+    /* set next hop */
+    *(zpointer *)next_hop = callstack->caller_ret_addr;
     ZzFreeCallStack(callstack);
 }
 
@@ -277,6 +286,7 @@ void zz_arm64_thunker_build_enter_thunk(ZzWriter *writer) {
     /* save general registers and sp */
     zz_arm64_writer_put_bytes(writer, (void *)ctx_save, 23 * 4);
     zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_X1, ZZ_ARM64_REG_SP, 8 + CTX_SAVE_STACK_OFFSET + 2 * 8);
+
     /* trick: use the `ctx_save` left [sp]*/
     zz_arm64_writer_put_str_reg_reg_offset(writer, ZZ_ARM64_REG_X1, ZZ_ARM64_REG_SP, 0 * 8);
 
@@ -289,7 +299,6 @@ void zz_arm64_thunker_build_enter_thunk(ZzWriter *writer) {
     /* next hop*/
     zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_X1, ZZ_ARM64_REG_SP,
                                         2 * 8 + 8 + CTX_SAVE_STACK_OFFSET + 0x8);
-
     /* RegState */
     zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_X2, ZZ_ARM64_REG_SP, 2 * 8);
     /* caller ret address */
@@ -297,6 +306,7 @@ void zz_arm64_thunker_build_enter_thunk(ZzWriter *writer) {
 
     /* call function_context_begin_invocation */
     zz_arm64_writer_put_ldr_blr_b_reg_address(writer, ZZ_ARM64_REG_X17, (zaddr)function_context_begin_invocation);
+
     /* alignment padding + dummy PC */
     zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 8);
 
@@ -317,6 +327,7 @@ void zz_arm64_thunker_build_half_thunk(ZzWriter *writer) {
     /* save general registers and sp */
     zz_arm64_writer_put_bytes(writer, (void *)ctx_save, 23 * 4);
     zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_X1, ZZ_ARM64_REG_SP, 8 + CTX_SAVE_STACK_OFFSET + 2 * 8);
+
     /* trick: use the `ctx_save` left [sp]*/
     zz_arm64_writer_put_str_reg_reg_offset(writer, ZZ_ARM64_REG_X1, ZZ_ARM64_REG_SP, 0 * 8);
 
@@ -337,6 +348,7 @@ void zz_arm64_thunker_build_half_thunk(ZzWriter *writer) {
 
     /* call function_context_half_invocation */
     zz_arm64_writer_put_ldr_blr_b_reg_address(writer, ZZ_ARM64_REG_X17, (zaddr)function_context_half_invocation);
+
     /* alignment padding + dummy PC */
     zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 8);
 
@@ -453,6 +465,7 @@ void zz_arm64_thunker_build_leave_thunk(ZzWriter *writer) {
     /* save general registers and sp */
     zz_arm64_writer_put_bytes(writer, (void *)ctx_save, 23 * 4);
     zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_X1, ZZ_ARM64_REG_SP, 8 + CTX_SAVE_STACK_OFFSET + 2 * 8);
+
     /* trick: use the `ctx_save` left [sp]*/
     zz_arm64_writer_put_str_reg_reg_offset(writer, ZZ_ARM64_REG_X1, ZZ_ARM64_REG_SP, 0 * 8);
 
@@ -471,6 +484,7 @@ void zz_arm64_thunker_build_leave_thunk(ZzWriter *writer) {
 
     /* call function_context_end_invocation */
     zz_arm64_writer_put_ldr_blr_b_reg_address(writer, ZZ_ARM64_REG_X17, (zaddr)function_context_end_invocation);
+
     /* alignment padding + dummy PC */
     zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 8);
 
@@ -488,7 +502,7 @@ void zz_arm64_thunker_build_leave_thunk(ZzWriter *writer) {
 }
 
 ZZSTATUS ZzThunkerBuildThunk(ZzInterceptorBackend *self) {
-    zbyte temp_code_slice_data[256] = {0};
+    zbyte temp_code_slice_data[512] = {0};
     ZzArm64Writer *arm64_writer = NULL;
     ZzCodeSlice *code_slice = NULL;
     ZZSTATUS status = ZZ_SUCCESS;
@@ -496,91 +510,66 @@ ZZSTATUS ZzThunkerBuildThunk(ZzInterceptorBackend *self) {
     arm64_writer = &self->arm64_writer;
     zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);
 
-    code_slice = NULL;
-    do {
-        zz_arm64_thunker_build_enter_thunk(arm64_writer);
-        if (code_slice) {
-            if (!ZzMemoryPatchCode((zaddr)code_slice->data, arm64_writer->base, arm64_writer->size))
-                return ZZ_FAILED;
-            break;
-        }
-        code_slice = ZzNewCodeSlice(self->allocator, arm64_writer->size + 4);
-        if (!code_slice) {
-#if defined(DEBUG_MODE)
-            debug_break();
-#endif
-            return ZZ_FAILED;
-        } else {
-            zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);
-            arm64_writer->pc = code_slice->data;
-        }
-    } while (code_slice);
-
-    /* set arm64 enter_thunk */
-    // self->enter_thunk = code_slice->data;
-    self->enter_thunk = (void *)enter_thunk_template;
+    /* build enter_thunk */
+    zz_arm64_thunker_build_enter_thunk(arm64_writer);
+
+    /* code patch */
+    code_slice = zz_code_patch_arm64_writer(arm64_writer, self->allocator, 0, 0);
+    if (code_slice)
+        self->enter_thunk = (void *)enter_thunk_template;
+    else
+        return ZZ_FAILED;
+
+    /* debug log */
     if (ZzIsEnableDebugMode()) {
         char buffer[1024] = {};
         sprintf(buffer + strlen(buffer), "%s\n", "ZzThunkerBuildThunk:");
         sprintf(buffer + strlen(buffer), "LogInfo: enter_thunk at %p, use enter_thunk_template.\n",
                 (void *)enter_thunk_template);
-        Xinfo("%s", buffer);
+        ZzInfoLog("%s", buffer);
     }
 
     zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);
-    code_slice = NULL;
-    do {
-        zz_arm64_thunker_build_leave_thunk(arm64_writer);
-        if (code_slice) {
-            if (!ZzMemoryPatchCode((zaddr)code_slice->data, arm64_writer->base, arm64_writer->size))
-                return ZZ_FAILED;
-            break;
-        }
-        code_slice = ZzNewCodeSlice(self->allocator, arm64_writer->size + 4);
-        if (!code_slice) {
-#if defined(DEBUG_MODE)
-            debug_break();
-#endif
-            return ZZ_FAILED;
-        } else {
-            zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);
-            arm64_writer->pc = code_slice->data;
-        }
-    } while (code_slice);
+
+    /* build  leave_thunk */
+    zz_arm64_thunker_build_leave_thunk(arm64_writer);
+
+    /* code patch */
+    code_slice = zz_code_patch_arm64_writer(arm64_writer, self->allocator, 0, 0);
+    if (code_slice)
+        self->leave_thunk = code_slice->data;
+    else
+        return ZZ_FAILED;
+
+    /* debug log */
     if (ZzIsEnableDebugMode()) {
         char buffer[1024] = {};
         sprintf(buffer + strlen(buffer), "%s\n", "ZzThunkerBuildThunk:");
         sprintf(buffer + strlen(buffer), "LogInfo: leave_thunk at %p, length: %ld.\n", code_slice->data,
                 code_slice->size);
-        Xinfo("%s", buffer);
+        ZzInfoLog("%s", buffer);
     }
 
-    /* set arm64 leave_thunk */
-    self->leave_thunk = code_slice->data;
-
     zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);
-    code_slice = NULL;
-    do {
-        zz_arm64_thunker_build_half_thunk(arm64_writer);
-        if (code_slice) {
-            if (!ZzMemoryPatchCode((zaddr)code_slice->data, arm64_writer->base, arm64_writer->size))
-                return ZZ_FAILED;
-            break;
-        }
-        code_slice = ZzNewCodeSlice(self->allocator, arm64_writer->size + 4);
-        if (!code_slice) {
-#if defined(DEBUG_MODE)
-            debug_break();
-#endif
-            return ZZ_FAILED;
-        } else {
-            zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);
-            arm64_writer->pc = code_slice->data;
-        }
-    } while (code_slice);
-
-    /* set arm64 half_thunk */
-    self->half_thunk = code_slice->data;
+
+    /* build half_thunk */
+    zz_arm64_thunker_build_half_thunk(arm64_writer);
+
+    /* code patch */
+    code_slice = zz_code_patch_arm64_writer(arm64_writer, self->allocator, 0, 0);
+    if (code_slice)
+        self->half_thunk = code_slice->data;
+    else
+        return ZZ_FAILED;
+
+    /* debug log */
+    if (ZzIsEnableDebugMode()) {
+        char buffer[1024] = {};
+        sprintf(buffer + strlen(buffer), "%s\n", "ZzThunkerBuildThunk:");
+        sprintf(buffer + strlen(buffer), "LogInfo: half_thunk at %p, length: %ld.\n", code_slice->data,
+                code_slice->size);
+        ZzInfoLog("%s", buffer);
+    }
 
     return status;
 }
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/interceptor-template-x86.s b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/interceptor-template-x86.s
new file mode 100644
index 000000000..b690233d8
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/interceptor-template-x86.s
@@ -0,0 +1,10 @@
+// .section	__TEXT,__text,regular,pure_instructions
+// .ios_version_min 11, 0
+.align 4
+.globl _ctx_save
+.globl _ctx_restore
+.globl _enter_thunk_template
+.globl _leave_thunk_template
+.globl _on_enter_trampoline_template
+.globl _on_invoke_trampoline_template
+.globl _on_leave_trampoline_template
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/interceptor-x86.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/interceptor-x86.c
new file mode 100644
index 000000000..0f1fdc1ed
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/interceptor-x86.c
@@ -0,0 +1,48 @@
+/**
+ *    Copyright 2017 jmpews
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include "interceptor-x86.h"
+#include "zzinfo.h"
+#include <stdlib.h>
+#include <string.h>
+
+#define ZZ_X86_TINY_REDIRECT_SIZE 4
+#define ZZ_X86_FULL_REDIRECT_SIZE 16
+
+ZzInterceptorBackend *ZzBuildInteceptorBackend(ZzAllocator *allocator) { return NULL; }
+
+ZzCodeSlice *zz_code_patch_x86_writer(ZzX86Writer *x86_writer, ZzAllocator *allocator, zaddr target_addr,
+                                      zsize range_size) {
+    return NULL;
+}
+ZzCodeSlice *zz_code_patch_x86_relocate_writer(ZzX86Relocator *relocator, ZzX86Writer *x86_writer,
+                                               ZzAllocator *allocator, zaddr target_addr, zsize range_size) {
+    return NULL;
+}
+
+ZZSTATUS ZzPrepareTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) { return ZZ_FAILED; }
+
+ZZSTATUS ZzBuildEnterTransferTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) { return ZZ_FAILED; }
+
+ZZSTATUS ZzBuildEnterTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) { return ZZ_FAILED; }
+
+ZZSTATUS ZzBuildInvokeTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) { return ZZ_FAILED; }
+
+ZZSTATUS ZzBuildHalfTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) { return ZZ_FAILED; }
+
+ZZSTATUS ZzBuildLeaveTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) { return ZZ_FAILED; }
+
+ZZSTATUS ZzActivateTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) { return ZZ_FAILED; }
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/interceptor-x86.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/interceptor-x86.h
new file mode 100644
index 000000000..39848f888
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/interceptor-x86.h
@@ -0,0 +1,54 @@
+/**
+ *    Copyright 2017 jmpews
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef platforms_backend_x86_intercetor_x86
+#define platforms_backend_x86_intercetor_x86
+
+// platforms
+#include "platforms/arch-x86/relocator-x86.h"
+#include "platforms/arch-x86/writer-x86.h"
+
+// hookzz
+#include "allocator.h"
+#include "interceptor.h"
+#include "thunker.h"
+
+// zzdeps
+#include "hookzz.h"
+#include "zzdefs.h"
+#include "zzdeps/common/debugbreak.h"
+#include "zzdeps/zz.h"
+
+#define CTX_SAVE_STACK_OFFSET (8 + 30 * 8 + 8 * 16)
+
+typedef struct _ZzInterceptorBackend {
+
+} ZzInterceptorBackend;
+
+typedef struct _ZzX86HookFuntionEntryBackend {
+} ZzX86HookFunctionEntryBackend;
+
+void ctx_save();
+void ctx_restore();
+void enter_thunk_template();
+void leave_thunk_template();
+void on_enter_trampoline_template();
+void on_invoke_trampoline_template();
+void on_leave_trampoline_template();
+
+ZzCodeSlice *zz_code_patch_x86_writer(ZzX86Writer *x86_writer, ZzAllocator *allocator, zaddr target_addr,
+                                      zsize range_size);
+#endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/thunker-x86.c b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/thunker-x86.c
new file mode 100644
index 000000000..59278e206
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/thunker-x86.c
@@ -0,0 +1,115 @@
+/**
+ *    Copyright 2017 jmpews
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include "thunker-x86.h"
+#include "zzinfo.h"
+#include <string.h>
+
+
+// just like pre_call, wow!
+void function_context_begin_invocation(ZzHookFunctionEntry *entry, zpointer next_hop, RegState *rs,
+                                       zpointer caller_ret_addr) {
+    Xinfo("target %p call begin-invocation", entry->target_ptr);
+
+    ZzThreadStack *stack = ZzGetCurrentThreadStack(entry->thread_local_key);
+    if (!stack) {
+        stack = ZzNewThreadStack(entry->thread_local_key);
+    }
+    ZzCallStack *callstack = ZzNewCallStack();
+    ZzPushCallStack(stack, callstack);
+
+    /* call pre_call */
+    if (entry->pre_call) {
+        PRECALL pre_call;
+        pre_call = entry->pre_call;
+        (*pre_call)(rs, (ThreadStack *)stack, (CallStack *)callstack);
+    }
+
+    /* set next hop */
+    if (entry->replace_call) {
+        *(zpointer *)next_hop = entry->replace_call;
+    } else {
+        *(zpointer *)next_hop = entry->on_invoke_trampoline;
+    }
+
+    if (entry->hook_type == HOOK_FUNCTION_TYPE) {
+        callstack->caller_ret_addr = *(zpointer *)caller_ret_addr;
+        *(zpointer *)caller_ret_addr = entry->on_leave_trampoline;
+    }
+}
+
+// just like post_call, wow!
+void function_context_half_invocation(ZzHookFunctionEntry *entry, zpointer next_hop, RegState *rs,
+                                      zpointer caller_ret_addr) {
+    Xdebug("target %p call half-invocation", entry->target_ptr);
+
+    ZzThreadStack *stack = ZzGetCurrentThreadStack(entry->thread_local_key);
+    if (!stack) {
+#if defined(DEBUG_MODE)
+        debug_break();
+#endif
+    }
+    ZzCallStack *callstack = ZzPopCallStack(stack);
+
+    /* call half_call */
+    if (entry->half_call) {
+        HALFCALL half_call;
+        half_call = entry->half_call;
+        (*half_call)(rs, (ThreadStack *)stack, (CallStack *)callstack);
+    }
+
+    /*  set next hop */
+    *(zpointer *)next_hop = (zpointer)entry->target_half_ret_addr;
+
+    ZzFreeCallStack(callstack);
+}
+
+// just like post_call, wow!
+void function_context_end_invocation(ZzHookFunctionEntry *entry, zpointer next_hop, RegState *rs) {
+    Xdebug("%p call end-invocation", entry->target_ptr);
+
+    ZzThreadStack *stack = ZzGetCurrentThreadStack(entry->thread_local_key);
+    if (!stack) {
+#if defined(DEBUG_MODE)
+        debug_break();
+#endif
+    }
+    ZzCallStack *callstack = ZzPopCallStack(stack);
+
+    /* call post_call */
+    if (entry->post_call) {
+        POSTCALL post_call;
+        post_call = entry->post_call;
+        (*post_call)(rs, (ThreadStack *)stack, (CallStack *)callstack);
+    }
+
+    /* set next hop */
+    *(zpointer *)next_hop = callstack->caller_ret_addr;
+    ZzFreeCallStack(callstack);
+}
+
+void zz_x86_thunker_build_enter_thunk(ZzWriter *writer) {
+}
+
+void zz_x86_thunker_build_half_thunk(ZzWriter *writer) {
+}
+
+void zz_x86_thunker_build_leave_thunk(ZzWriter *writer) {
+}
+
+ZZSTATUS ZzThunkerBuildThunk(ZzInterceptorBackend *self) {
+    return ZZ_FAILED;
+}
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/thunker-x86.h b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/thunker-x86.h
new file mode 100644
index 000000000..554626c4a
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/thunker-x86.h
@@ -0,0 +1,35 @@
+/**
+ *    Copyright 2017 jmpews
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef platforms_backend_x86_thunker_x86
+#define platforms_backend_x86_thunker_x86
+
+// platforms
+#include "platforms/arch-x86/relocator-x86.h"
+#include "platforms/arch-x86/writer-x86.h"
+
+#include "interceptor-x86.h"
+
+// hookzz
+#include "stack.h"
+#include "thunker.h"
+
+// zzdeps
+#include "hookzz.h"
+#include "zzdeps/common/debugbreak.h"
+#include "zzdeps/zz.h"
+
+#endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/stack.c b/VirtualApp/lib/src/main/jni/HookZz/src/stack.c
index 8637f4f27..6329b5e28 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/stack.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/stack.c
@@ -44,6 +44,7 @@ ZzThreadStack *ZzNewThreadStack(zpointer key_ptr) {
 ZzCallStack *ZzNewCallStack() {
     ZzCallStack *callstack;
     callstack = (ZzCallStack *)malloc(sizeof(ZzCallStack));
+
     callstack->capacity = 4;
 
     callstack->items = (ZzCallStackItem *)malloc(sizeof(ZzCallStackItem) * callstack->capacity);
@@ -74,8 +75,8 @@ zbool ZzPushCallStack(ZzThreadStack *stack, ZzCallStack *callstack) {
         return FALSE;
 
     if (stack->size >= stack->capacity) {
-        ZzCallStack **callstacks = (ZzCallStack **)realloc(
-            stack->callstacks, sizeof(ZzCallStack *) * (stack->capacity) * 2);
+        ZzCallStack **callstacks =
+            (ZzCallStack **)realloc(stack->callstacks, sizeof(ZzCallStack *) * (stack->capacity) * 2);
         if (!callstacks)
             return FALSE;
         stack->callstacks = callstacks;
@@ -93,7 +94,8 @@ zpointer ZzGetCallStackData(CallStack *callstack_ptr, char *key) {
     ZzCallStack *callstack = (ZzCallStack *)callstack_ptr;
     if (!callstack)
         return NULL;
-    for (int i = 0; i < callstack->size; ++i) {
+    int i;
+    for (i = 0; i < callstack->size; ++i) {
         if (!strcmp(callstack->items[i].key, key)) {
             return callstack->items[i].value;
         }
@@ -105,8 +107,8 @@ ZzCallStackItem *ZzNewCallStackData(ZzCallStack *callstack) {
     if (!callstack)
         return NULL;
     if (callstack->size >= callstack->capacity) {
-        ZzCallStackItem *callstackitems = (ZzCallStackItem *)realloc(
-            callstack->items, sizeof(ZzCallStackItem) * callstack->capacity * 2);
+        ZzCallStackItem *callstackitems =
+            (ZzCallStackItem *)realloc(callstack->items, sizeof(ZzCallStackItem) * callstack->capacity * 2);
         if (!callstackitems)
             return NULL;
         callstack->items = callstackitems;
@@ -115,8 +117,7 @@ ZzCallStackItem *ZzNewCallStackData(ZzCallStack *callstack) {
     return &(callstack->items[callstack->size++]);
 }
 
-zbool ZzSetCallStackData(CallStack *callstack_ptr, char *key, zpointer value_ptr,
-                         zsize value_size) {
+zbool ZzSetCallStackData(CallStack *callstack_ptr, char *key, zpointer value_ptr, zsize value_size) {
     ZzCallStack *callstack = (ZzCallStack *)callstack_ptr;
     if (!callstack)
         return FALSE;
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/stack.h b/VirtualApp/lib/src/main/jni/HookZz/src/stack.h
index 511de7d44..0c0f6ed82 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/stack.h
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/stack.h
@@ -52,15 +52,10 @@ typedef struct _ZzThreadStack {
 } ZzThreadStack;
 
 ZzThreadStack *ZzNewThreadStack(zpointer key_ptr);
-
-ZzThreadStack *ZzGetCurrentThreadStack(zpointer key_ptr);
-
 ZzCallStack *ZzNewCallStack();
-
-void ZzFreeCallStack(ZzCallStack *callstack);
-
-ZzCallStack *ZzPopCallStack(ZzThreadStack *stack);
-
+ZzThreadStack *ZzGetCurrentThreadStack(zpointer key_ptr);
 zbool ZzPushCallStack(ZzThreadStack *stack, ZzCallStack *callstack);
+ZzCallStack *ZzPopCallStack(ZzThreadStack *stack);
+void ZzFreeCallStack(ZzCallStack *callstack);
 
 #endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/writer.h b/VirtualApp/lib/src/main/jni/HookZz/src/writer.h
index 62cd18ae1..a1036f62c 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/writer.h
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/writer.h
@@ -19,11 +19,22 @@
 
 #include "hookzz.h"
 
+#define MAX_LITERAL_INSN_SIZE 128
+
+typedef struct _ZzLiteralInstruction {
+    zpointer literal_insn_ptr;
+    zaddr *literal_address_ptr;
+} ZzLiteralInstruction;
+
 typedef struct _ZzWriter {
-    zpointer *codedata;
+    zpointer codedata;
     zpointer base;
-    zpointer pc;
-    zuint size;
+    zaddr pc;
+    zsize size;
+
+    ZzLiteralInstruction literal_insns[MAX_LITERAL_INSN_SIZE];
+    zsize literal_insn_size;
+
 } ZzWriter;
 
 #endif
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/zzdefs.h b/VirtualApp/lib/src/main/jni/HookZz/src/zzdefs.h
index 7135936fe..9630b0f89 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/zzdefs.h
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/zzdefs.h
@@ -38,4 +38,6 @@
 #define ZZ_INT26_MASK 0x03ffffff
 #define ZZ_INT28_MASK 0x0fffffff
 
-#define THUMB_FUNCTION_ADDRESS(target_addr) (void *)((unsigned long)target_addr & ~(unsigned long)1)
\ No newline at end of file
+#define THUMB_FUNCTION_ADDRESS(target_addr) (void *)((unsigned long)target_addr & ~(unsigned long)1)
+#define INSTRUCTION_IS_THUMB(insn_addr) ((insn_addr & 0x1) == 0x1)
+#define ALIGN_4(target_addr) ((unsigned long)target_addr & ~(unsigned long)3)
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/darwin/macho-utils-darwin.c b/VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/darwin/macho-utils-darwin.c
index e1c25d7aa..1ab97a980 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/darwin/macho-utils-darwin.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/darwin/macho-utils-darwin.c
@@ -55,8 +55,7 @@ zpointer zz_macho_get_dyld_load_address_via_task(task_t task) {
         KR_ERROR(kr);
         return 0;
     }
-    struct dyld_all_image_infos *allImageInfos =
-        (struct dyld_all_image_infos *)infoData.all_image_info_addr;
+    struct dyld_all_image_infos *allImageInfos = (struct dyld_all_image_infos *)infoData.all_image_info_addr;
     allImageInfos = (struct dyld_all_image_infos *)malloc(sizeof(struct dyld_all_image_infos));
     if (zz_vm_read_data_via_task(task, infoData.all_image_info_addr, allImageInfos,
                                  sizeof(struct dyld_all_image_infos))) {
@@ -76,14 +75,14 @@ task_t zz_darwin_get_task_via_pid(int pid) {
     return t;
 }
 
-struct segment_command_64 *zz_macho_get_segment_64_via_name(struct mach_header_64 *header,
-                                                            char *segment_name) {
+struct segment_command_64 *zz_macho_get_segment_64_via_name(struct mach_header_64 *header, char *segment_name) {
     struct load_command *load_cmd;
     struct segment_command_64 *seg_cmd_64;
     struct section_64 *sect_64;
 
     load_cmd = (zpointer)header + sizeof(struct mach_header_64);
-    for (zsize i = 0; i < header->ncmds; i++, load_cmd = (zpointer)load_cmd + load_cmd->cmdsize) {
+    zsize i;
+    for (i = 0; i < header->ncmds; i++, load_cmd = (zpointer)load_cmd + load_cmd->cmdsize) {
         if (load_cmd->cmd == LC_SEGMENT_64) {
             seg_cmd_64 = (struct segment_command_64 *)load_cmd;
             if (!strcmp(seg_cmd_64->segname, segment_name)) {
@@ -94,20 +93,19 @@ struct segment_command_64 *zz_macho_get_segment_64_via_name(struct mach_header_6
     return NULL;
 }
 
-struct section_64 *zz_macho_get_section_64_via_name(struct mach_header_64 *header,
-                                                    char *sect_name) {
+struct section_64 *zz_macho_get_section_64_via_name(struct mach_header_64 *header, char *sect_name) {
     struct load_command *load_cmd;
     struct segment_command_64 *seg_cmd_64;
     struct section_64 *sect_64;
 
     load_cmd = (zpointer)header + sizeof(struct mach_header_64);
-    for (zsize i = 0; i < header->ncmds; i++, load_cmd = (zpointer)load_cmd + load_cmd->cmdsize) {
+    zsize i;
+    zsize j;
+    for (i = 0; i < header->ncmds; i++, load_cmd = (zpointer)load_cmd + load_cmd->cmdsize) {
         if (load_cmd->cmd == LC_SEGMENT_64) {
             seg_cmd_64 = (struct segment_command_64 *)load_cmd;
-            sect_64 =
-                (struct section_64 *)((zpointer)seg_cmd_64 + sizeof(struct segment_command_64));
-            for (zsize j = 0; j < seg_cmd_64->nsects;
-                 j++, sect_64 = (zpointer)sect_64 + sizeof(struct section_64)) {
+            sect_64 = (struct section_64 *)((zpointer)seg_cmd_64 + sizeof(struct segment_command_64));
+            for (j = 0; j < seg_cmd_64->nsects; j++, sect_64 = (zpointer)sect_64 + sizeof(struct section_64)) {
                 if (!strcmp(sect_64->sectname, sect_name)) {
                     return sect_64;
                 }
@@ -121,9 +119,10 @@ struct load_command *zz_macho_get_load_command_via_cmd(struct mach_header_64 *he
     struct load_command *load_cmd;
     struct segment_command_64 *seg_cmd_64;
     struct section_64 *sect_64;
+    zsize i;
 
     load_cmd = (zpointer)header + sizeof(struct mach_header_64);
-    for (zsize i = 0; i < header->ncmds; i++, load_cmd = (zpointer)load_cmd + load_cmd->cmdsize) {
+    for (i = 0; i < header->ncmds; i++, load_cmd = (zpointer)load_cmd + load_cmd->cmdsize) {
         if (load_cmd->cmd == cmd) {
             return load_cmd;
         }
@@ -139,13 +138,13 @@ zpointer zz_macho_get_symbol_via_name(struct mach_header_64 *header, const char
         zz_macho_get_segment_64_via_name((struct mach_header_64 *)header, (char *)"__LINKEDIT");
     zsize slide = (zaddr)header - (zaddr)seg_cmd_64->vmaddr;
     zsize linkEditBase = seg_cmd_64_linkedit->vmaddr - seg_cmd_64_linkedit->fileoff + slide;
-    struct symtab_command *symtab =
-        (struct symtab_command *)zz_macho_get_load_command_via_cmd(header, LC_SYMTAB);
+    struct symtab_command *symtab = (struct symtab_command *)zz_macho_get_load_command_via_cmd(header, LC_SYMTAB);
 
     char *sym_str_table = (char *)linkEditBase + symtab->stroff;
     struct nlist_64 *sym_table = (struct nlist_64 *)(linkEditBase + symtab->symoff);
 
-    for (int i = 0; i < symtab->nsyms; i++) {
+    int i;
+    for (i = 0; i < symtab->nsyms; i++) {
         if (sym_table[i].n_value && !strcmp(name, &sym_str_table[sym_table[i].n_un.n_strx])) {
             return (void *)(uint64_t)(sym_table[i].n_value + slide);
         }
@@ -158,9 +157,10 @@ zpointer zz_macho_get_section_64_address_via_name(struct mach_header_64 *header,
     struct segment_command_64 *seg_cmd_64;
     struct section_64 *sect_64;
     zsize slide, linkEditBase;
+    zsize i, j;
 
     load_cmd = (zpointer)header + sizeof(struct mach_header_64);
-    for (zsize i = 0; i < header->ncmds; i++, load_cmd = (zpointer)load_cmd + load_cmd->cmdsize) {
+    for (i = 0; i < header->ncmds; i++, load_cmd = (zpointer)load_cmd + load_cmd->cmdsize) {
         if (load_cmd->cmd == LC_SEGMENT_64) {
             seg_cmd_64 = (struct segment_command_64 *)load_cmd;
             if ((seg_cmd_64->fileoff == 0) && (seg_cmd_64->filesize != 0)) {
@@ -169,10 +169,8 @@ zpointer zz_macho_get_section_64_address_via_name(struct mach_header_64 *header,
             if (strcmp(seg_cmd_64->segname, "__LINKEDIT") == 0) {
                 linkEditBase = seg_cmd_64->vmaddr - seg_cmd_64->fileoff + slide;
             }
-            sect_64 =
-                (struct section_64 *)((zpointer)seg_cmd_64 + sizeof(struct segment_command_64));
-            for (zsize j = 0; j < seg_cmd_64->nsects;
-                 j++, sect_64 = (zpointer)sect_64 + sizeof(struct section_64)) {
+            sect_64 = (struct section_64 *)((zpointer)seg_cmd_64 + sizeof(struct segment_command_64));
+            for (j = 0; j < seg_cmd_64->nsects; j++, sect_64 = (zpointer)sect_64 + sizeof(struct section_64)) {
                 if (!strcmp(sect_64->sectname, sect_name)) {
                     return (zpointer)(sect_64->addr + slide);
                 }
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/darwin/memory-utils-darwin.c b/VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/darwin/memory-utils-darwin.c
index 75806fa4d..4ac4cbbba 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/darwin/memory-utils-darwin.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/darwin/memory-utils-darwin.c
@@ -332,8 +332,8 @@ zpointer zz_vm_search_code_cave(zaddr address, zsize range_size, zsize size) {
 
     MemoryLayout *mlayout = zz_vm_get_memory_layout_via_task(mach_task_self());
 
-    for (int i = 0; i < mlayout->size; i++) {
-
+    int i;
+    for (i = 0; i < mlayout->size; i++) {
         if (mlayout->mem[i].flags == (1 << 0 | 1 << 2)) {
             search_start = (zaddr)mlayout->mem[i].start;
             search_end = (zaddr)mlayout->mem[i].end;
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/linux/memory-utils-linux.c b/VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/linux/memory-utils-linux.c
index 9c5c60e2b..6ad056a23 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/linux/memory-utils-linux.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/linux/memory-utils-linux.c
@@ -80,8 +80,8 @@ zpointer zz_linux_vm_search_code_cave(zaddr address, zsize range_size, zsize siz
 
     MemoryLayout *mlayout = zz_linux_vm_get_memory_layout_via_pid(-1);
 
-    for (int i = 0; i < mlayout->size; i++) {
-
+    int i;
+    for (i = 0; i < mlayout->size; i++) {
         if (mlayout->mem[i].flags == (1 << 0 | 1 << 2)) {
             search_start = (zaddr)mlayout->mem[i].start;
             search_end = (zaddr)mlayout->mem[i].end;
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/posix/thread-utils-posix.c b/VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/posix/thread-utils-posix.c
index 04c048eaa..151b22d62 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/posix/thread-utils-posix.c
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/posix/thread-utils-posix.c
@@ -35,8 +35,8 @@ zbool zz_posix_thread_add_thread_local_key(ThreadLocalKeyList *keylist, ThreadLo
         return FALSE;
 
     if (keylist->size >= keylist->capacity) {
-        ThreadLocalKey **keys_tmp = (ThreadLocalKey **)realloc(
-            keylist->keys, sizeof(ThreadLocalKey *) * keylist->capacity * 2);
+        ThreadLocalKey **keys_tmp =
+            (ThreadLocalKey **)realloc(keylist->keys, sizeof(ThreadLocalKey *) * keylist->capacity * 2);
         if (!keys_tmp)
             return FALSE;
         keylist->keys = keys_tmp;
@@ -65,9 +65,11 @@ zpointer zz_posix_thread_new_thread_local_key_ptr() {
 
 zpointer zz_posix_thread_get_current_thread_data(zpointer key_ptr) {
     ThreadLocalKeyList *g_keys = g_thread_local_key_list;
+    zsize i;
+
     if (!key_ptr)
         return NULL;
-    for (zsize i = 0; i < g_keys->size; i++) {
+    for (i = 0; i < g_keys->size; i++) {
         if (g_keys->keys[i] == key_ptr)
             return (zpointer)pthread_getspecific(g_keys->keys[i]->key);
     }
@@ -76,8 +78,9 @@ zpointer zz_posix_thread_get_current_thread_data(zpointer key_ptr) {
 
 zbool zz_posix_thread_set_current_thread_data(zpointer key_ptr, zpointer data) {
     ThreadLocalKeyList *g_keys = g_thread_local_key_list;
+    zsize i;
 
-    for (zsize i = 0; i < g_keys->size; i++) {
+    for (i = 0; i < g_keys->size; i++) {
         if (g_keys->keys[i] == key_ptr)
             return pthread_setspecific(g_keys->keys[i]->key, data);
     }
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/zzinfo.c b/VirtualApp/lib/src/main/jni/HookZz/src/zzinfo.c
new file mode 100644
index 000000000..338a8dd8f
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/zzinfo.c
@@ -0,0 +1,25 @@
+/**
+ *    Copyright 2017 jmpews
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include "zzinfo.h"
+
+ZzInfo g_zz;
+
+void ZzEnableDebugMode() { g_zz.g_enable_debug_flag = TRUE; }
+
+zbool ZzIsEnableDebugMode() { return g_zz.g_enable_debug_flag; }
+
+ZzInfo *ZzInfoObtain(void) { return &g_zz; }
diff --git a/VirtualApp/lib/src/main/jni/HookZz/src/zzinfo.h b/VirtualApp/lib/src/main/jni/HookZz/src/zzinfo.h
new file mode 100644
index 000000000..db9d37f17
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/src/zzinfo.h
@@ -0,0 +1,44 @@
+//    Copyright 2017 jmpews
+//
+//    Licensed under the Apache License, Version 2.0 (the "License");
+//    you may not use this file except in compliance with the License.
+//    You may obtain a copy of the License at
+//
+//        http://www.apache.org/licenses/LICENSE-2.0
+//
+//    Unless required by applicable law or agreed to in writing, software
+//    distributed under the License is distributed on an "AS IS" BASIS,
+//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//    See the License for the specific language governing permissions and
+//    limitations under the License.
+
+#ifndef zzinfo_h
+#define zzinfo_h
+
+// platforms
+
+// hookzz
+#include "hookzz.h"
+
+// zzdeps
+#include "zzdefs.h"
+#include "zzdeps/common/debugbreak.h"
+#include "zzdeps/zz.h"
+
+typedef struct _ZzInfo {
+    zbool g_enable_debug_flag;
+} ZzInfo;
+
+ZzInfo *ZzInfoObtain(void);
+zbool ZzIsEnableDebugMode();
+
+#if defined(__ANDROID__)
+#include <android/log.h>
+#define ZzInfoLog(fmt, ...)                                                                                            \
+    { __android_log_print(ANDROID_LOG_INFO, "zzinfo", fmt, __VA_ARGS__); }
+#else
+#define ZzInfoLog(fmt, ...)                                                                                            \
+    { Xinfo(fmt, __VA_ARGS__); }
+#endif
+
+#endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/tests/arm-insn-fix/makefile b/VirtualApp/lib/src/main/jni/HookZz/tests/arm-insn-fix/makefile
new file mode 100644
index 000000000..a4a8203f0
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/tests/arm-insn-fix/makefile
@@ -0,0 +1,19 @@
+NO_COLOR=\x1b[0m
+OK_COLOR=\x1b[32;01m
+ERROR_COLOR=\x1b[31;01m
+WARN_COLOR=\x1b[33;01m
+
+
+HOOKZZ_INCLUDE_DIR := -I$(abspath ../../include) -I$(abspath ../../src)
+HOOKZZ_LIB_DIR := -L$(abspath ../../build/ios-armv7)
+
+ZZ_GCC_TEST := $(shell xcrun --sdk iphoneos --find clang) -isysroot $(shell xcrun --sdk iphoneos --show-sdk-path) -arch armv7 -O0 -g
+
+# -undefined dynamic_lookup
+test: 
+	@$(ZZ_GCC_TEST) $(HOOKZZ_INCLUDE_DIR) -c test_insn_fix.c -o test_insn_fix.o
+	@$(ZZ_GCC_TEST) -dynamiclib $(HOOKZZ_LIB_DIR) -lhookzz.static test_insn_fix.o -o test_insn_fix.dylib
+	@echo "$(OK_COLOR)build [test_insn_fix.dylib] success for armv7! $(NO_COLOR)"
+clean:
+	rm -rf test_insn_fix.o
+	rm -rf test_insn_fix.dylib
diff --git a/VirtualApp/lib/src/main/jni/HookZz/tests/arm-insn-fix/test_insn_fix.c b/VirtualApp/lib/src/main/jni/HookZz/tests/arm-insn-fix/test_insn_fix.c
new file mode 100644
index 000000000..26069eb60
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/tests/arm-insn-fix/test_insn_fix.c
@@ -0,0 +1,106 @@
+#include "hookzz.h"
+#include <stdio.h>
+#include <unistd.h>
+
+static void thumb_insn_need_fix() {
+    __asm__ volatile(".code 16\n"
+
+                     "add r0, pc\n"
+
+                     "ldr r0, [pc, #8]\n"
+                     "ldr.W r0, [pc, #8]\n"
+
+                     "adr r0, #8\n"
+                     "adr.W r0, #8\n"
+                     "adr.W r0, #-8\n"
+
+                     "beq #8\n"
+                     "b #8\n"
+                     "beq.W #8\n"
+                     "b.W #8\n"
+
+                     "bl #8\n"
+                     "blx #8\n"
+                     "nop");
+}
+
+#include "platforms/backend-arm64/interceptor-arm64.h"
+#include <stdlib.h>
+
+#if 1
+__attribute__((constructor)) void test_insn_fix_thumb() {
+
+    ZzInterceptorBackend *backend = (ZzInterceptorBackend *)malloc(sizeof(ZzInterceptorBackend));
+    zbyte temp_code_slice_data[256] = {0};
+
+    zz_arm_writer_init(&backend->arm_writer, NULL);
+    zz_arm_relocator_init(&backend->arm_relocator, NULL, &backend->arm_writer);
+    zz_thumb_writer_init(&backend->thumb_writer, NULL);
+    zz_thumb_relocator_init(&backend->thumb_relocator, NULL, &backend->thumb_writer);
+
+    ZzThumbRelocator *thumb_relocator;
+    ZzThumbWriter *thumb_writer;
+    thumb_relocator = &backend->thumb_relocator;
+    thumb_writer = &backend->thumb_writer;
+
+    zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);
+
+    zz_thumb_relocator_reset(thumb_relocator, (zpointer)((zaddr)thumb_insn_need_fix & ~(zaddr)1), thumb_writer);
+    zsize tmp_relocator_insn_size = 0;
+
+    do {
+        zz_thumb_relocator_read_one(thumb_relocator, NULL);
+        zz_thumb_relocator_write_one(thumb_relocator);
+        tmp_relocator_insn_size = thumb_relocator->input_cur - thumb_relocator->input_start;
+    } while (tmp_relocator_insn_size < 36);
+}
+#endif
+
+#if 0
+__attribute__((__naked__)) void arm_insn_need_fix() {
+    __asm__ volatile(".arm\n"
+                     "add r0, pc, r0\n"
+
+                     "ldr r0, [pc, #8]\n"
+
+                     "adr r0, #8\n"
+                     "adr r0, #-8\n"
+
+                     "beq #8\n"
+                     "b #8\n"
+
+                     "bl #8\n"
+                     "blx #8\n"
+                     "nop");
+}
+
+#include "platforms/backend-arm/interceptor-arm.h"
+#include <stdlib.h>
+
+__attribute__((constructor)) void test_insn_fix_arm() {
+
+    ZzInterceptorBackend *backend = (ZzInterceptorBackend *)malloc(sizeof(ZzInterceptorBackend));
+    zbyte temp_code_slice_data[256] = {0};
+
+    zz_arm_writer_init(&backend->arm_writer, NULL);
+    zz_arm_relocator_init(&backend->arm_relocator, NULL, &backend->arm_writer);
+    zz_thumb_writer_init(&backend->thumb_writer, NULL);
+    zz_thumb_relocator_init(&backend->thumb_relocator, NULL, &backend->thumb_writer);
+
+    ZzArmRelocator *arm_relocator;
+    ZzArmWriter *arm_writer;
+    arm_relocator = &backend->arm_relocator;
+    arm_writer = &backend->arm_writer;
+
+    zz_arm_writer_reset(arm_writer, temp_code_slice_data);
+
+    zz_arm_relocator_reset(arm_relocator, (zpointer)((zaddr)arm_insn_need_fix & ~(zaddr)1), arm_writer);
+    zsize tmp_relocator_insn_size = 0;
+
+    do {
+        zz_arm_relocator_read_one(arm_relocator, NULL);
+        zz_arm_relocator_write_one(arm_relocator);
+        tmp_relocator_insn_size = arm_relocator->input_cur - arm_relocator->input_start;
+    } while (tmp_relocator_insn_size < 36);
+}
+#endif
\ No newline at end of file
diff --git a/VirtualApp/lib/src/main/jni/HookZz/tests/arm-ios/makefile b/VirtualApp/lib/src/main/jni/HookZz/tests/arm-ios/makefile
index 381c389b5..ed1ac5305 100644
--- a/VirtualApp/lib/src/main/jni/HookZz/tests/arm-ios/makefile
+++ b/VirtualApp/lib/src/main/jni/HookZz/tests/arm-ios/makefile
@@ -27,6 +27,12 @@ test:
 	@$(ZZ_GCC_TEST) -dynamiclib -Wl,-U,_func -framework Foundation -L$(HOOKZZ_LIB_DIR) -lhookzz.static test_hook_printf.o -o $(HOOKZZ_LIB_DIR)/test_hook_printf.dylib
 	@echo "$(OK_COLOR)build [test_hook_printf.dylib] success for armv7-ios! $(NO_COLOR)"
 
+
+	@$(ZZ_GCC_TEST) -I$(HOOKZZ_INCLUDE_DIR) -c test_hook_freeaddr.c -o test_hook_freeaddr.o
+	@$(ZZ_GCC_TEST) -dynamiclib -Wl,-U,_func -framework Foundation -L$(HOOKZZ_LIB_DIR) -lhookzz.static test_hook_freeaddr.o -o $(HOOKZZ_LIB_DIR)/test_hook_freeaddr.dylib
+	@echo "$(OK_COLOR)build [test_hook_freeaddr.dylib] success for armv7-ios! $(NO_COLOR)"
+
+
 	@echo "$(OK_COLOR)build [test] success for armv7-ios-hookzz! $(NO_COLOR)"
 
 clean:
diff --git a/VirtualApp/lib/src/main/jni/HookZz/tests/arm-ios/test_hook_freeaddr.c b/VirtualApp/lib/src/main/jni/HookZz/tests/arm-ios/test_hook_freeaddr.c
new file mode 100644
index 000000000..e9a16ea1a
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/tests/arm-ios/test_hook_freeaddr.c
@@ -0,0 +1,70 @@
+/**
+ *    Copyright 2017 jmpews
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include "hookzz.h"
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+void (*orig_freeaddrinfo)(struct addrinfo *ai);
+void fake_freeaddrinfo(struct addrinfo *ai) { orig_freeaddrinfo(ai); }
+
+__attribute__((constructor)) void test_hook_freeaddrinfo() {
+    ZzEnableDebugMode();
+    ZzHook((void *)freeaddrinfo, (void *)fake_freeaddrinfo, &orig_freeaddrinfo, NULL, NULL, FALSE);
+
+    int sockfd;
+    struct addrinfo hints, *servinfo, *p;
+    int rv;
+
+    memset(&hints, 0, sizeof hints);
+    hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_flags = AI_PASSIVE; // use my IP address
+
+    if ((rv = getaddrinfo(NULL, "3490", &hints, &servinfo)) != 0) {
+        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
+        exit(1);
+    }
+
+    // loop through all the results and bind to the first we can
+    for (p = servinfo; p != NULL; p = p->ai_next) {
+        if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
+            perror("socket");
+            continue;
+        }
+
+        if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
+            close(sockfd);
+            perror("bind");
+            continue;
+        }
+
+        break; // if we get here, we must have connected successfully
+    }
+
+    if (p == NULL) {
+        // looped off the end of the list with no successful bind
+        fprintf(stderr, "failed to bind socket\n");
+        exit(2);
+    }
+
+    freeaddrinfo(servinfo); // all done with this structure
+}
diff --git a/VirtualApp/lib/src/main/jni/HookZz/tests/arm64-insn-fix/makefile b/VirtualApp/lib/src/main/jni/HookZz/tests/arm64-insn-fix/makefile
new file mode 100644
index 000000000..5974da54e
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/tests/arm64-insn-fix/makefile
@@ -0,0 +1,19 @@
+NO_COLOR=\x1b[0m
+OK_COLOR=\x1b[32;01m
+ERROR_COLOR=\x1b[31;01m
+WARN_COLOR=\x1b[33;01m
+
+
+HOOKZZ_INCLUDE_DIR := -I$(abspath ../../include) -I$(abspath ../../src)
+HOOKZZ_LIB_DIR := -L$(abspath ../../build/ios-arm64)
+
+ZZ_GCC_TEST := $(shell xcrun --sdk iphoneos --find clang) -isysroot $(shell xcrun --sdk iphoneos --show-sdk-path) -arch arm64 -O0 -g
+
+# -undefined dynamic_lookup
+test: 
+	@$(ZZ_GCC_TEST) $(HOOKZZ_INCLUDE_DIR) -c test_insn_fix.c -o test_insn_fix.o
+	@$(ZZ_GCC_TEST) -dynamiclib $(HOOKZZ_LIB_DIR) -lhookzz.static test_insn_fix.o -o test_insn_fix.dylib
+	@echo "$(OK_COLOR)build [test_insn_fix.dylib] success for arm64! $(NO_COLOR)"
+clean:
+	rm -rf test_insn_fix.o
+	rm -rf test_insn_fix.dylib
diff --git a/VirtualApp/lib/src/main/jni/HookZz/tests/arm64-insn-fix/test_insn_fix.c b/VirtualApp/lib/src/main/jni/HookZz/tests/arm64-insn-fix/test_insn_fix.c
new file mode 100644
index 000000000..e2e793d9e
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/HookZz/tests/arm64-insn-fix/test_insn_fix.c
@@ -0,0 +1,40 @@
+#include "hookzz.h"
+#include <stdio.h>
+#include <unistd.h>
+
+static void arm64_insn_need_fix() {
+    __asm__ volatile("bl #40\n"
+                     "nop");
+}
+
+#include "platforms/backend-arm64/interceptor-arm64.h"
+#include <stdlib.h>
+
+#if 1
+__attribute__((constructor)) void test_insn_fix_arm64() {
+
+    ZzInterceptorBackend *backend = (ZzInterceptorBackend *)malloc(sizeof(ZzInterceptorBackend));
+    zbyte temp_code_slice_data[256] = {0};
+
+    zz_arm64_writer_init(&backend->arm64_writer, NULL);
+    zz_arm64_relocator_init(&backend->arm64_relocator, NULL, &backend->arm64_writer);
+    zz_arm64_writer_init(&backend->arm64_writer, NULL);
+    zz_arm64_relocator_init(&backend->arm64_relocator, NULL, &backend->arm64_writer);
+
+    ZzArm64Relocator *arm64_relocator;
+    ZzArm64Writer *arm64_writer;
+    arm64_relocator = &backend->arm64_relocator;
+    arm64_writer = &backend->arm64_writer;
+
+    zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);
+
+    zz_arm64_relocator_reset(arm64_relocator, (zpointer)((zaddr)arm64_insn_need_fix & ~(zaddr)1), arm64_writer);
+    zsize tmp_relocator_insn_size = 0;
+
+    do {
+        zz_arm64_relocator_read_one(arm64_relocator, NULL);
+        zz_arm64_relocator_write_one(arm64_relocator);
+        tmp_relocator_insn_size = arm64_relocator->input_cur - arm64_relocator->input_start;
+    } while (tmp_relocator_insn_size < 36);
+}
+#endif
diff --git a/VirtualApp/lib/src/main/jni/Jni/Helper.h b/VirtualApp/lib/src/main/jni/Jni/Helper.h
index 727e10a71..398fd7b91 100644
--- a/VirtualApp/lib/src/main/jni/Jni/Helper.h
+++ b/VirtualApp/lib/src/main/jni/Jni/Helper.h
@@ -5,9 +5,27 @@
 #ifndef NDK_LOG_H
 #define NDK_LOG_H
 
-
-#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+#include <fb/include/fb/fbjni.h>
 
 #define NATIVE_METHOD(func_ptr, func_name, signature) { func_name, signature, reinterpret_cast<void*>(func_ptr) }
 
+class ScopeUtfString {
+public:
+    ScopeUtfString(jstring j_str) : _j_str(j_str),
+                                    _c_str(facebook::jni::Environment::current()->GetStringUTFChars(j_str, NULL)) {
+    }
+
+    const char *c_str() {
+        return _c_str;
+    }
+
+    ~ScopeUtfString() {
+        facebook::jni::Environment::current()->ReleaseStringUTFChars(_j_str, _c_str);
+    }
+
+private:
+    jstring _j_str;
+    const char *_c_str;
+};
+
 #endif //NDK_LOG_H
diff --git a/VirtualApp/lib/src/main/jni/Jni/VAJni.cpp b/VirtualApp/lib/src/main/jni/Jni/VAJni.cpp
index 4bde8bd76..aa0db1170 100644
--- a/VirtualApp/lib/src/main/jni/Jni/VAJni.cpp
+++ b/VirtualApp/lib/src/main/jni/Jni/VAJni.cpp
@@ -10,61 +10,48 @@
 using namespace facebook::jni;
 
 static void jni_nativeLaunchEngine(alias_ref<jclass> clazz, JArrayClass<jobject> javaMethods,
-                            jstring packageName,
-                            jboolean isArt, jint apiLevel, jint cameraMethodType) {
+                                   jstring packageName,
+                                   jboolean isArt, jint apiLevel, jint cameraMethodType) {
     hookAndroidVM(javaMethods, packageName, isArt, apiLevel, cameraMethodType);
 }
 
 
 static void jni_nativeEnableIORedirect(alias_ref<jclass>, jstring selfSoPath, jint apiLevel,
-                                jint preview_api_level) {
-    JNIEnv *env = Environment::current();
-    const char *so_path = env->GetStringUTFChars(selfSoPath, NULL);
-    IOUniformer::startUniformer(so_path, apiLevel, preview_api_level);
-    env->ReleaseStringUTFChars(selfSoPath, so_path);
+                                       jint preview_api_level) {
+    ScopeUtfString so_path(selfSoPath);
+    IOUniformer::startUniformer(so_path.c_str(), apiLevel, preview_api_level);
 }
 
 static void jni_nativeIOWhitelist(alias_ref<jclass> jclazz, jstring _path) {
-    JNIEnv *env = Environment::current();
-    const char *path = Environment::current()->GetStringUTFChars(_path, NULL);
-    IOUniformer::whitelist(path);
-    env->ReleaseStringUTFChars(_path, path);
+    ScopeUtfString path(_path);
+    IOUniformer::whitelist(path.c_str());
 }
 
 static void jni_nativeIOForbid(alias_ref<jclass> jclazz, jstring _path) {
-    JNIEnv *env = Environment::current();
-    const char *path = env->GetStringUTFChars(_path, NULL);
-    IOUniformer::forbid(path);
-    env->ReleaseStringUTFChars(_path, path);
+    ScopeUtfString path(_path);
+    IOUniformer::forbid(path.c_str());
 }
 
 
 static void jni_nativeIORedirect(alias_ref<jclass> jclazz, jstring origPath, jstring newPath) {
-    JNIEnv *env = Environment::current();
-    const char *orig_path = env->GetStringUTFChars(origPath, NULL);
-    const char *new_path = env->GetStringUTFChars(newPath, NULL);
-    IOUniformer::redirect(orig_path, new_path);
-    env->ReleaseStringUTFChars(origPath, orig_path);
-    env->ReleaseStringUTFChars(newPath, new_path);
+    ScopeUtfString orig_path(origPath);
+    ScopeUtfString new_path(newPath);
+    IOUniformer::redirect(orig_path.c_str(), new_path.c_str());
 
 }
 
 static jstring jni_nativeGetRedirectedPath(alias_ref<jclass> jclazz, jstring origPath) {
-    JNIEnv *env = Environment::current();
-    const char *orig_path = env->GetStringUTFChars(origPath, NULL);
-    const char *redirected_path = IOUniformer::query(orig_path);
-    env->ReleaseStringUTFChars(origPath, orig_path);
+    ScopeUtfString orig_path(origPath);
+    const char *redirected_path = IOUniformer::query(orig_path.c_str());
     if (redirected_path != NULL) {
-        return env->NewStringUTF(redirected_path);
+        return Environment::current()->NewStringUTF(redirected_path);
     }
     return NULL;
 }
 
 static jstring jni_nativeReverseRedirectedPath(alias_ref<jclass> jclazz, jstring redirectedPath) {
-    JNIEnv *env = Environment::current();
-    const char *redirected_path = env->GetStringUTFChars(redirectedPath, NULL);
-    const char *orig_path = IOUniformer::reverse(redirected_path);
-    env->ReleaseStringUTFChars(redirectedPath, redirected_path);
+    ScopeUtfString redirected_path(redirectedPath);
+    const char *orig_path = IOUniformer::reverse(redirected_path.c_str());
     return Environment::current()->NewStringUTF(orig_path);
 }
 
@@ -76,14 +63,22 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
     return initialize(vm, [] {
         nativeEngineClass = findClassStatic("com/lody/virtual/client/NativeEngine");
         nativeEngineClass->registerNatives({
-                makeNativeMethod("nativeEnableIORedirect", jni_nativeEnableIORedirect),
-                makeNativeMethod("nativeIOWhitelist", jni_nativeIOWhitelist),
-                makeNativeMethod("nativeIOForbid", jni_nativeIOForbid),
-                makeNativeMethod("nativeIORedirect", jni_nativeIORedirect),
-                makeNativeMethod("nativeGetRedirectedPath", jni_nativeGetRedirectedPath),
-                makeNativeMethod("nativeReverseRedirectedPath", jni_nativeReverseRedirectedPath),
-                makeNativeMethod("nativeLaunchEngine", jni_nativeLaunchEngine),
-        });
+                        makeNativeMethod("nativeEnableIORedirect",
+                                         jni_nativeEnableIORedirect),
+                        makeNativeMethod("nativeIOWhitelist",
+                                         jni_nativeIOWhitelist),
+                        makeNativeMethod("nativeIOForbid",
+                                         jni_nativeIOForbid),
+                        makeNativeMethod("nativeIORedirect",
+                                         jni_nativeIORedirect),
+                        makeNativeMethod("nativeGetRedirectedPath",
+                                         jni_nativeGetRedirectedPath),
+                        makeNativeMethod("nativeReverseRedirectedPath",
+                                         jni_nativeReverseRedirectedPath),
+                        makeNativeMethod("nativeLaunchEngine",
+                                         jni_nativeLaunchEngine),
+                }
+        );
     });
 }
 
diff --git a/VirtualApp/lib/src/main/jni/Substrate/Buffer.hpp b/VirtualApp/lib/src/main/jni/Substrate/Buffer.hpp
new file mode 100755
index 000000000..34d9df32e
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/Substrate/Buffer.hpp
@@ -0,0 +1,38 @@
+/* Cydia Substrate - Powerful Code Insertion Platform
+ * Copyright (C) 2008-2011  Jay Freeman (saurik)
+*/
+
+/* GNU Lesser General Public License, Version 3 {{{ */
+/*
+ * Substrate is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Substrate is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Substrate.  If not, see <http://www.gnu.org/licenses/>.
+**/
+/* }}} */
+
+#ifndef SUBSTRATE_BUFFER_HPP
+#define SUBSTRATE_BUFFER_HPP
+
+#include <string.h>
+
+template <typename Type_>
+_disused static _finline void MSWrite(uint8_t *&buffer, Type_ value) {
+    *reinterpret_cast<Type_ *>(buffer) = value;
+    buffer += sizeof(Type_);
+}
+
+_disused static _finline void MSWrite(uint8_t *&buffer, uint8_t *data, size_t size) {
+    memcpy(buffer, data, size);
+    buffer += size;
+}
+
+#endif//SUBSTRATE_BUFFER_HPP
diff --git a/VirtualApp/lib/src/main/jni/Substrate/CydiaSubstrate.h b/VirtualApp/lib/src/main/jni/Substrate/CydiaSubstrate.h
new file mode 100755
index 000000000..bb806aa9f
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/Substrate/CydiaSubstrate.h
@@ -0,0 +1,152 @@
+/* Cydia Substrate - Powerful Code Insertion Platform
+ * Copyright (C) 2008-2011  Jay Freeman (saurik)
+*/
+
+/* GNU Lesser General Public License, Version 3 {{{ */
+/*
+ * Substrate is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Substrate is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Substrate.  If not, see <http://www.gnu.org/licenses/>.
+**/
+/* }}} */
+
+#ifndef SUBSTRATE_H_
+#define SUBSTRATE_H_
+
+#ifdef __APPLE__
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <mach-o/nlist.h>
+#ifdef __cplusplus
+}
+#endif
+
+#include <objc/runtime.h>
+#include <objc/message.h>
+#endif
+
+#include <dlfcn.h>
+#include <stdlib.h>
+
+#define _finline \
+    inline __attribute__((__always_inline__))
+#define _disused \
+    __attribute__((__unused__))
+
+#define _extern \
+    extern "C" __attribute__((__visibility__("default")))
+
+#ifdef __cplusplus
+#define _default(value) = value
+#else
+#define _default(value)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+bool MSHookProcess(pid_t pid, const char *library);
+
+typedef const void *MSImageRef;
+
+MSImageRef MSGetImageByName(const char *file);
+void *MSFindSymbol(MSImageRef image, const char *name);
+
+void MSHookFunction(void *symbol, void *replace, void **result);
+
+#ifdef __APPLE__
+#ifdef __arm__
+__attribute__((__deprecated__))
+IMP MSHookMessage(Class _class, SEL sel, IMP imp, const char *prefix _default(NULL));
+#endif
+void MSHookMessageEx(Class _class, SEL sel, IMP imp, IMP *result);
+#endif
+
+#ifdef SubstrateInternal
+typedef void *SubstrateAllocatorRef;
+typedef struct __SubstrateProcess *SubstrateProcessRef;
+typedef struct __SubstrateMemory *SubstrateMemoryRef;
+
+SubstrateProcessRef SubstrateProcessCreate(SubstrateAllocatorRef allocator, pid_t pid);
+void SubstrateProcessRelease(SubstrateProcessRef process);
+
+SubstrateMemoryRef SubstrateMemoryCreate(SubstrateAllocatorRef allocator, SubstrateProcessRef process, void *data, size_t size);
+void SubstrateMemoryRelease(SubstrateMemoryRef memory);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef __cplusplus
+
+#ifdef SubstrateInternal
+struct SubstrateHookMemory {
+    SubstrateMemoryRef handle_;
+
+    SubstrateHookMemory(SubstrateProcessRef process, void *data, size_t size) :
+        handle_(SubstrateMemoryCreate(NULL, NULL, data, size))
+    {
+    }
+
+    ~SubstrateHookMemory() {
+        if (handle_ != NULL)
+            SubstrateMemoryRelease(handle_);
+    }
+};
+#endif
+
+
+template<typename Type_>
+static inline void MSHookFunction(Type_ *symbol, Type_ *replace, Type_ **result) {
+    MSHookFunction(
+            reinterpret_cast<void *>(symbol),
+            reinterpret_cast<void *>(replace),
+            reinterpret_cast<void **>(result)
+    );
+}
+
+template<typename Type_>
+static inline void MSHookFunction(Type_ *symbol, Type_ *replace) {
+    return MSHookFunction(symbol, replace, reinterpret_cast<Type_ **>(NULL));
+}
+
+template<typename Type_>
+static inline void MSHookSymbol(Type_ *&value, const char *name, MSImageRef image = NULL) {
+    value = reinterpret_cast<Type_ *>(MSFindSymbol(image, name));
+}
+
+template<typename Type_>
+static inline void MSHookFunction(const char *name, Type_ *replace, Type_ **result = NULL) {
+    Type_ *symbol;
+    MSHookSymbol(symbol, name);
+    return MSHookFunction(symbol, replace, result);
+}
+
+#endif
+
+#define MSHook(type, name, args...) \
+    _disused static type (*_ ## name)(args); \
+    static type $ ## name(args)
+
+#ifdef __cplusplus
+#define MSHake(name) \
+    &$ ## name, &_ ## name
+#else
+#define MSHake(name) \
+    &$ ## name, (void **) &_ ## name
+#endif
+
+
+#endif//SUBSTRATE_H_
diff --git a/VirtualApp/lib/src/main/jni/Substrate/SubstrateARM.hpp b/VirtualApp/lib/src/main/jni/Substrate/SubstrateARM.hpp
new file mode 100755
index 000000000..7dd313900
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/Substrate/SubstrateARM.hpp
@@ -0,0 +1,65 @@
+/* Cydia Substrate - Powerful Code Insertion Platform
+ * Copyright (C) 2008-2011  Jay Freeman (saurik)
+*/
+
+/* GNU Lesser General Public License, Version 3 {{{ */
+/*
+ * Substrate is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Substrate is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Substrate.  If not, see <http://www.gnu.org/licenses/>.
+**/
+/* }}} */
+
+#ifndef SUBSTRATE_ARM_HPP
+#define SUBSTRATE_ARM_HPP
+
+enum A$r {
+    A$r0, A$r1, A$r2, A$r3,
+    A$r4, A$r5, A$r6, A$r7,
+    A$r8, A$r9, A$r10, A$r11,
+    A$r12, A$r13, A$r14, A$r15,
+    A$sp = A$r13,
+    A$lr = A$r14,
+    A$pc = A$r15
+};
+
+enum A$c {
+    A$eq, A$ne, A$cs, A$cc,
+    A$mi, A$pl, A$vs, A$vc,
+    A$hi, A$ls, A$ge, A$lt,
+    A$gt, A$le, A$al,
+    A$hs = A$cs,
+    A$lo = A$cc
+};
+
+#define A$mrs_rm_cpsr(rd) /* mrs rd, cpsr */ \
+    (0xe10f0000 | ((rd) << 12))
+#define A$msr_cpsr_f_rm(rm) /* msr cpsr_f, rm */ \
+    (0xe128f000 | (rm))
+#define A$ldr_rd_$rn_im$(rd, rn, im) /* ldr rd, [rn, #im] */ \
+    (0xe5100000 | ((im) < 0 ? 0 : 1 << 23) | ((rn) << 16) | ((rd) << 12) | abs(im))
+#define A$str_rd_$rn_im$(rd, rn, im) /* sr rd, [rn, #im] */ \
+    (0xe5000000 | ((im) < 0 ? 0 : 1 << 23) | ((rn) << 16) | ((rd) << 12) | abs(im))
+#define A$sub_rd_rn_$im(rd, rn, im) /* sub, rd, rn, #im */ \
+    (0xe2400000 | ((rn) << 16) | ((rd) << 12) | (im & 0xff))
+#define A$blx_rm(rm) /* blx rm */ \
+    (0xe12fff30 | (rm))
+#define A$mov_rd_rm(rd, rm) /* mov rd, rm */ \
+    (0xe1a00000 | ((rd) << 12) | (rm))
+#define A$ldmia_sp$_$rs$(rs) /* ldmia sp!, {rs} */ \
+    (0xe8b00000 | (A$sp << 16) | (rs))
+#define A$stmdb_sp$_$rs$(rs) /* stmdb sp!, {rs} */ \
+    (0xe9200000 | (A$sp << 16) | (rs))
+#define A$stmia_sp$_$r0$  0xe8ad0001 /* stmia sp!, {r0}   */
+#define A$bx_r0           0xe12fff10 /* bx r0             */
+
+#endif//SUBSTRATE_ARM_HPP
diff --git a/VirtualApp/lib/src/main/jni/Substrate/SubstrateDebug.cpp b/VirtualApp/lib/src/main/jni/Substrate/SubstrateDebug.cpp
new file mode 100755
index 000000000..2df6ef487
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/Substrate/SubstrateDebug.cpp
@@ -0,0 +1,96 @@
+/* Cydia Substrate - Powerful Code Insertion Platform
+ * Copyright (C) 2008-2011  Jay Freeman (saurik)
+*/
+
+/* GNU Lesser General Public License, Version 3 {{{ */
+/*
+ * Substrate is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Substrate is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Substrate.  If not, see <http://www.gnu.org/licenses/>.
+**/
+/* }}} */
+
+#include "SubstrateHook.h"
+#include "SubstrateDebug.hpp"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+_extern bool MSDebug;
+bool MSDebug = false;
+
+static char _MSHexChar(uint8_t value) {
+    return value < 0x20 || value >= 0x80 ? '.' : value;
+}
+
+#define HexWidth_ 16
+#define HexDepth_ 4
+
+void MSLogHexEx(const void *vdata, size_t size, size_t stride, const char *mark) {
+    const uint8_t *data((const uint8_t *) vdata);
+
+    size_t i(0), j;
+
+    char d[256];
+    size_t b(0);
+    d[0] = '\0';
+
+    while (i != size) {
+        if (i % HexWidth_ == 0) {
+            if (mark != NULL)
+                b += sprintf(d + b, "\n[%s] ", mark);
+            b += sprintf(d + b, "0x%.3zx:", i);
+        }
+
+        b += sprintf(d + b, " ");
+
+        for (size_t q(0); q != stride; ++q)
+            b += sprintf(d + b, "%.2x", data[i + stride - q - 1]);
+
+        i += stride;
+
+        for (size_t q(1); q != stride; ++q)
+            b += sprintf(d + b, " ");
+
+        if (i % HexDepth_ == 0)
+            b += sprintf(d + b, " ");
+
+        if (i % HexWidth_ == 0) {
+            b += sprintf(d + b, " ");
+            for (j = i - HexWidth_; j != i; ++j)
+                b += sprintf(d + b, "%c", _MSHexChar(data[j]));
+
+            lprintf("%s", d);
+            b = 0;
+            d[0] = '\0';
+        }
+    }
+
+    if (i % HexWidth_ != 0) {
+        for (j = i % HexWidth_; j != HexWidth_; ++j)
+            b += sprintf(d + b, "   ");
+        for (j = 0; j != (HexWidth_ - i % HexWidth_ + HexDepth_ - 1) / HexDepth_; ++j)
+            b += sprintf(d + b, " ");
+        b += sprintf(d + b, " ");
+        for (j = i / HexWidth_ * HexWidth_; j != i; ++j)
+            b += sprintf(d + b, "%c", _MSHexChar(data[j]));
+
+        lprintf("%s", d);
+        b = 0;
+        d[0] = '\0';
+    }
+}
+
+void MSLogHex(const void *vdata, size_t size, const char *mark) {
+    return MSLogHexEx(vdata, size, 1, mark);
+}
diff --git a/VirtualApp/lib/src/main/jni/Substrate/SubstrateDebug.hpp b/VirtualApp/lib/src/main/jni/Substrate/SubstrateDebug.hpp
new file mode 100755
index 000000000..9c554c851
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/Substrate/SubstrateDebug.hpp
@@ -0,0 +1,33 @@
+/* Cydia Substrate - Powerful Code Insertion Platform
+ * Copyright (C) 2008-2011  Jay Freeman (saurik)
+*/
+
+/* GNU Lesser General Public License, Version 3 {{{ */
+/*
+ * Substrate is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Substrate is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Substrate.  If not, see <http://www.gnu.org/licenses/>.
+**/
+/* }}} */
+
+#ifndef SUBSTRATE_DEBUG_HPP
+#define SUBSTRATE_DEBUG_HPP
+
+#include "SubstrateLog.hpp"
+#define lprintf(format, ...) \
+    MSLog(MSLogLevelNotice, format, ## __VA_ARGS__)
+
+extern "C" bool MSDebug;
+void MSLogHexEx(const void *vdata, size_t size, size_t stride, const char *mark = 0);
+void MSLogHex(const void *vdata, size_t size, const char *mark = 0);
+
+#endif//SUBSTRATE_DEBUG_HPP
diff --git a/VirtualApp/lib/src/main/jni/Substrate/SubstrateHook.cpp b/VirtualApp/lib/src/main/jni/Substrate/SubstrateHook.cpp
new file mode 100755
index 000000000..34f2a26dd
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/Substrate/SubstrateHook.cpp
@@ -0,0 +1,934 @@
+/* Cydia Substrate - Powerful Code Insertion Platform
+ * Copyright (C) 2008-2011  Jay Freeman (saurik)
+*/
+
+/* GNU Lesser General Public License, Version 3 {{{ */
+/*
+ * Substrate is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Substrate is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Substrate.  If not, see <http://www.gnu.org/licenses/>.
+**/
+/* }}} */
+
+#define SubstrateInternal
+#include "CydiaSubstrate.h"
+
+#include <sys/mman.h>
+
+#define _trace() do { \
+    MSLog(MSLogLevelNotice, "_trace(%u)", __LINE__); \
+} while (false)
+
+#if defined(__i386__) || defined(__x86_64__)
+#include "hde64.h"
+#endif
+
+#include "SubstrateDebug.hpp"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef __arm__
+/* WebCore (ARM) PC-Relative:
+X    1  ldr r*,[pc,r*] !=
+     2 fldd d*,[pc,#*]
+X    5  str r*,[pc,r*] !=
+     8 flds s*,[pc,#*]
+   400  ldr r*,[pc,r*] ==
+   515  add r*, pc,r*  ==
+X 4790  ldr r*,[pc,#*]    */
+
+// x=0; while IFS= read -r line; do if [[ ${#line} -ne 0 && $line == +([^\;]): ]]; then x=2; elif [[ $line == ' +'* && $x -ne 0 ]]; then ((--x)); echo "$x${line}"; fi; done <WebCore.asm >WebCore.pc
+// grep pc WebCore.pc | cut -c 40- | sed -Ee 's/^ldr *(ip|r[0-9]*),\[pc,\#0x[0-9a-f]*\].*/ ldr r*,[pc,#*]/;s/^add *r[0-9]*,pc,r[0-9]*.*/ add r*, pc,r*/;s/^(st|ld)r *r([0-9]*),\[pc,r([0-9]*)\].*/ \1r r\2,[pc,r\3]/;s/^fld(s|d) *(s|d)[0-9]*,\[pc,#0x[0-9a-f]*].*/fld\1 \2*,[pc,#*]/' | sort | uniq -c | sort -n
+
+#include "SubstrateARM.hpp"
+
+#define T$Label(l, r) \
+    (((r) - (l)) * 2 - 4 + ((l) % 2 == 0 ? 0 : 2))
+
+#define T$pop_$r0$ 0xbc01 // pop {r0}
+#define T$b(im) /* b im */ \
+    (0xde00 | (im & 0xff))
+#define T$blx(rm) /* blx rm */ \
+    (0x4780 | (rm << 3))
+#define T$bx(rm) /* bx rm */ \
+    (0x4700 | (rm << 3))
+#define T$nop /* nop */ \
+    (0x46c0)
+
+#define T$add_rd_rm(rd, rm) /* add rd, rm */ \
+    (0x4400 | (((rd) & 0x8) >> 3 << 7) | (((rm) & 0x8) >> 3 << 6) | (((rm) & 0x7) << 3) | ((rd) & 0x7))
+#define T$push_r(r) /* push r... */ \
+    (0xb400 | (((r) & (1 << A$lr)) >> A$lr << 8) | ((r) & 0xff))
+#define T$pop_r(r) /* pop r... */ \
+    (0xbc00 | (((r) & (1 << A$pc)) >> A$pc << 8) | ((r) & 0xff))
+#define T$mov_rd_rm(rd, rm) /* mov rd, rm */ \
+    (0x4600 | (((rd) & 0x8) >> 3 << 7) | (((rm) & 0x8) >> 3 << 6) | (((rm) & 0x7) << 3) | ((rd) & 0x7))
+#define T$ldr_rd_$rn_im_4$(rd, rn, im) /* ldr rd, [rn, #im * 4] */ \
+    (0x6800 | (((im) & 0x1f) << 6) | ((rn) << 3) | (rd))
+#define T$ldr_rd_$pc_im_4$(rd, im) /* ldr rd, [PC, #im * 4] */ \
+    (0x4800 | ((rd) << 8) | ((im) & 0xff))
+#define T$cmp_rn_$im(rn, im) /* cmp rn, #im */ \
+    (0x2000 | ((rn) << 8) | ((im) & 0xff))
+#define T$it$_cd(cd, ms) /* it<ms>, cd */ \
+    (0xbf00 | ((cd) << 4) | (ms))
+#define T$cbz$_rn_$im(op,rn,im) /* cb<op>z rn, #im */ \
+    (0xb100 | ((op) << 11) | (((im) & 0x40) >> 6 << 9) | (((im) & 0x3e) >> 1 << 3) | (rn))
+#define T$b$_$im(cond,im) /* b<cond> #im */ \
+    (cond == A$al ? 0xe000 | (((im) >> 1) & 0x7ff) : 0xd000 | ((cond) << 8) | (((im) >> 1) & 0xff))
+
+#define T1$ldr_rt_$rn_im$(rt, rn, im) /* ldr rt, [rn, #im] */ \
+    (0xf850 | ((im < 0 ? 0 : 1) << 7) | (rn))
+#define T2$ldr_rt_$rn_im$(rt, rn, im) /* ldr rt, [rn, #im] */ \
+    (((rt) << 12) | abs(im))
+
+#define T1$mrs_rd_apsr(rd) /* mrs rd, apsr */ \
+    (0xf3ef)
+#define T2$mrs_rd_apsr(rd) /* mrs rd, apsr */ \
+    (0x8000 | ((rd) << 8))
+
+#define T1$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \
+    (0xf380 | (rn))
+#define T2$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \
+    (0x8c00)
+#define T$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \
+    (T2$msr_apsr_nzcvqg_rn(rn) << 16 | T1$msr_apsr_nzcvqg_rn(rn))
+
+static inline bool A$pcrel$r(uint32_t ic) {
+    return (ic & 0x0c000000) == 0x04000000 && (ic & 0xf0000000) != 0xf0000000 && (ic & 0x000f0000) == 0x000f0000;
+}
+
+static inline bool T$32bit$i(uint16_t ic) {
+    return ((ic & 0xe000) == 0xe000 && (ic & 0x1800) != 0x0000);
+}
+
+static inline bool T$pcrel$cbz(uint16_t ic) {
+    return (ic & 0xf500) == 0xb100;
+}
+
+static inline bool T$pcrel$b(uint16_t ic) {
+    return (ic & 0xf000) == 0xd000 && (ic & 0x0e00) != 0x0e00;
+}
+
+static inline bool T2$pcrel$b(uint16_t *ic) {
+    return (ic[0] & 0xf800) == 0xf000 && ((ic[1] & 0xd000) == 0x9000 || (ic[1] & 0xd000) == 0x8000 && (ic[0] & 0x0380) != 0x0380);
+}
+
+static inline bool T$pcrel$bl(uint16_t *ic) {
+    return (ic[0] & 0xf800) == 0xf000 && ((ic[1] & 0xd000) == 0xd000 || (ic[1] & 0xd001) == 0xc000);
+}
+
+static inline bool T$pcrel$ldr(uint16_t ic) {
+    return (ic & 0xf800) == 0x4800;
+}
+
+static inline bool T$pcrel$add(uint16_t ic) {
+    return (ic & 0xff78) == 0x4478;
+}
+
+static inline bool T$pcrel$ldrw(uint16_t ic) {
+    return (ic & 0xff7f) == 0xf85f;
+}
+
+static size_t MSGetInstructionWidthThumb(void *start) {
+    uint16_t *thumb(reinterpret_cast<uint16_t *>(start));
+    return T$32bit$i(thumb[0]) ? 4 : 2;
+}
+
+static size_t MSGetInstructionWidthARM(void *start) {
+    return 4;
+}
+
+extern "C" size_t MSGetInstructionWidth(void *start) {
+    if ((reinterpret_cast<uintptr_t>(start) & 0x1) == 0)
+        return MSGetInstructionWidthARM(start);
+    else
+        return MSGetInstructionWidthThumb(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(start) & ~0x1));
+}
+
+static size_t SubstrateHookFunctionThumb(SubstrateProcessRef process, void *symbol, void *replace, void **result) {
+    if (symbol == NULL)
+        return 0;
+printf("SubstrateHookFunctionThumb\n");
+    uint16_t *area(reinterpret_cast<uint16_t *>(symbol));
+
+    unsigned align((reinterpret_cast<uintptr_t>(area) & 0x2) == 0 ? 0 : 1);
+    uint16_t *thumb(area + align);
+
+    uint32_t *arm(reinterpret_cast<uint32_t *>(thumb + 2));
+    uint16_t *trail(reinterpret_cast<uint16_t *>(arm + 2));
+
+    if (
+        (align == 0 || area[0] == T$nop) &&
+        thumb[0] == T$bx(A$pc) &&
+        thumb[1] == T$nop &&
+        arm[0] == A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8)
+    ) {
+        if (result != NULL)
+            *result = reinterpret_cast<void *>(arm[1]);
+
+        SubstrateHookMemory code(process, arm + 1, sizeof(uint32_t) * 1);
+
+        arm[1] = reinterpret_cast<uint32_t>(replace);
+
+        return sizeof(arm[0]);
+    }
+
+    size_t required((trail - area) * sizeof(uint16_t));
+
+    size_t used(0);
+    while (used < required)
+        used += MSGetInstructionWidthThumb(reinterpret_cast<uint8_t *>(area) + used);
+    used = (used + sizeof(uint16_t) - 1) / sizeof(uint16_t) * sizeof(uint16_t);
+
+    size_t blank((used - required) / sizeof(uint16_t));
+
+    uint16_t backup[used / sizeof(uint16_t)];
+    memcpy(backup, area, used);
+
+    if (MSDebug) {
+        char name[16];
+        sprintf(name, "%p", area);
+        MSLogHexEx(area, used + sizeof(uint16_t), 2, name);
+    }
+
+    if (result != NULL) {
+
+    size_t length(used);
+    for (unsigned offset(0); offset != used / sizeof(uint16_t); ++offset)
+        if (T$pcrel$ldr(backup[offset]))
+            length += 3 * sizeof(uint16_t);
+        else if (T$pcrel$b(backup[offset]))
+            length += 6 * sizeof(uint16_t);
+        else if (T2$pcrel$b(backup + offset)) {
+            length += 5 * sizeof(uint16_t);
+            ++offset;
+        } else if (T$pcrel$bl(backup + offset)) {
+            length += 5 * sizeof(uint16_t);
+            ++offset;
+        } else if (T$pcrel$cbz(backup[offset])) {
+            length += 16 * sizeof(uint16_t);
+        } else if (T$pcrel$ldrw(backup[offset])) {
+            length += 4 * sizeof(uint16_t);
+            ++offset;
+        } else if (T$pcrel$add(backup[offset]))
+            length += 6 * sizeof(uint16_t);
+        else if (T$32bit$i(backup[offset]))
+            ++offset;
+
+    unsigned pad((length & 0x2) == 0 ? 0 : 1);
+    length += (pad + 2) * sizeof(uint16_t) + 2 * sizeof(uint32_t);
+
+    uint16_t *buffer(reinterpret_cast<uint16_t *>(mmap(
+        NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0
+    )));
+
+    if (buffer == MAP_FAILED) {
+        MSLog(MSLogLevelError, "MS:Error:mmap() = %d", errno);
+        *result = NULL;
+        return 0;
+    }
+
+    if (false) fail: {
+        munmap(buffer, length);
+        *result = NULL;
+        return 0;
+    }
+
+    size_t start(pad), end(length / sizeof(uint16_t));
+    uint32_t *trailer(reinterpret_cast<uint32_t *>(buffer + end));
+    for (unsigned offset(0); offset != used / sizeof(uint16_t); ++offset) {
+        if (T$pcrel$ldr(backup[offset])) {
+            union {
+                uint16_t value;
+
+                struct {
+                    uint16_t immediate : 8;
+                    uint16_t rd : 3;
+                    uint16_t : 5;
+                };
+            } bits = {backup[offset+0]};
+
+            buffer[start+0] = T$ldr_rd_$pc_im_4$(bits.rd, T$Label(start+0, end-2) / 4);
+            buffer[start+1] = T$ldr_rd_$rn_im_4$(bits.rd, bits.rd, 0);
+
+            // XXX: this code "works", but is "wrong": the mechanism is more complex than this
+            *--trailer = ((reinterpret_cast<uint32_t>(area + offset) + 4) & ~0x2) + bits.immediate * 4;
+
+            start += 2;
+            end -= 2;
+        } else if (T$pcrel$b(backup[offset])) {
+            union {
+                uint16_t value;
+
+                struct {
+                    uint16_t imm8 : 8;
+                    uint16_t cond : 4;
+                    uint16_t /*1101*/ : 4;
+                };
+            } bits = {backup[offset+0]};
+
+            intptr_t jump(bits.imm8 << 1);
+            jump |= 1;
+            jump <<= 23;
+            jump >>= 23;
+
+            buffer[start+0] = T$b$_$im(bits.cond, (end-6 - (start+0)) * 2 - 4);
+
+            *--trailer = reinterpret_cast<uint32_t>(area + offset) + 4 + jump;
+            *--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
+            *--trailer = T$nop << 16 | T$bx(A$pc);
+
+            start += 1;
+            end -= 6;
+        } else if (T2$pcrel$b(backup + offset)) {
+            union {
+                uint16_t value;
+
+                struct {
+                    uint16_t imm6 : 6;
+                    uint16_t cond : 4;
+                    uint16_t s : 1;
+                    uint16_t : 5;
+                };
+            } bits = {backup[offset+0]};
+
+            union {
+                uint16_t value;
+
+                struct {
+                    uint16_t imm11 : 11;
+                    uint16_t j2 : 1;
+                    uint16_t a : 1;
+                    uint16_t j1 : 1;
+                    uint16_t : 2;
+                };
+            } exts = {backup[offset+1]};
+
+            intptr_t jump(1);
+            jump |= exts.imm11 << 1;
+            jump |= bits.imm6 << 12;
+
+            if (exts.a) {
+                jump |= bits.s << 24;
+                jump |= (~(bits.s ^ exts.j1) & 0x1) << 23;
+                jump |= (~(bits.s ^ exts.j2) & 0x1) << 22;
+                jump |= bits.cond << 18;
+                jump <<= 7;
+                jump >>= 7;
+            } else {
+                jump |= bits.s << 20;
+                jump |= exts.j2 << 19;
+                jump |= exts.j1 << 18;
+                jump <<= 11;
+                jump >>= 11;
+            }
+
+            buffer[start+0] = T$b$_$im(exts.a ? A$al : bits.cond, (end-6 - (start+0)) * 2 - 4);
+
+            *--trailer = reinterpret_cast<uint32_t>(area + offset) + 4 + jump;
+            *--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
+            *--trailer = T$nop << 16 | T$bx(A$pc);
+
+            ++offset;
+            start += 1;
+            end -= 6;
+        } else if (T$pcrel$bl(backup + offset)) {
+            union {
+                uint16_t value;
+
+                struct {
+                    uint16_t immediate : 10;
+                    uint16_t s : 1;
+                    uint16_t : 5;
+                };
+            } bits = {backup[offset+0]};
+
+            union {
+                uint16_t value;
+
+                struct {
+                    uint16_t immediate : 11;
+                    uint16_t j2 : 1;
+                    uint16_t x : 1;
+                    uint16_t j1 : 1;
+                    uint16_t : 2;
+                };
+            } exts = {backup[offset+1]};
+
+            int32_t jump(0);
+            jump |= bits.s << 24;
+            jump |= (~(bits.s ^ exts.j1) & 0x1) << 23;
+            jump |= (~(bits.s ^ exts.j2) & 0x1) << 22;
+            jump |= bits.immediate << 12;
+            jump |= exts.immediate << 1;
+            jump |= exts.x;
+            jump <<= 7;
+            jump >>= 7;
+
+            buffer[start+0] = T$push_r(1 << A$r7);
+            buffer[start+1] = T$ldr_rd_$pc_im_4$(A$r7, ((end-2 - (start+1)) * 2 - 4 + 2) / 4);
+            buffer[start+2] = T$mov_rd_rm(A$lr, A$r7);
+            buffer[start+3] = T$pop_r(1 << A$r7);
+            buffer[start+4] = T$blx(A$lr);
+
+            *--trailer = reinterpret_cast<uint32_t>(area + offset) + 4 + jump;
+
+            ++offset;
+            start += 5;
+            end -= 2;
+        } else if (T$pcrel$cbz(backup[offset])) {
+            union {
+                uint16_t value;
+
+                struct {
+                    uint16_t rn : 3;
+                    uint16_t immediate : 5;
+                    uint16_t : 1;
+                    uint16_t i : 1;
+                    uint16_t : 1;
+                    uint16_t op : 1;
+                    uint16_t : 4;
+                };
+            } bits = {backup[offset+0]};
+
+            intptr_t jump(1);
+            jump |= bits.i << 6;
+            jump |= bits.immediate << 1;
+
+            //jump <<= 24;
+            //jump >>= 24;
+
+            unsigned rn(bits.rn);
+            unsigned rt(rn == A$r7 ? A$r6 : A$r7);
+
+            buffer[start+0] = T$push_r(1 << rt);
+            buffer[start+1] = T1$mrs_rd_apsr(rt);
+            buffer[start+2] = T2$mrs_rd_apsr(rt);
+            buffer[start+3] = T$cbz$_rn_$im(bits.op, rn, (end-10 - (start+3)) * 2 - 4);
+            buffer[start+4] = T1$msr_apsr_nzcvqg_rn(rt);
+            buffer[start+5] = T2$msr_apsr_nzcvqg_rn(rt);
+            buffer[start+6] = T$pop_r(1 << rt);
+
+            *--trailer = reinterpret_cast<uint32_t>(area + offset) + 4 + jump;
+            *--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
+            *--trailer = T$nop << 16 | T$bx(A$pc);
+            *--trailer = T$nop << 16 | T$pop_r(1 << rt);
+            *--trailer = T$msr_apsr_nzcvqg_rn(rt);
+
+#if 0
+            if ((start & 0x1) == 0)
+                buffer[start++] = T$nop;
+            buffer[start++] = T$bx(A$pc);
+            buffer[start++] = T$nop;
+
+            uint32_t *arm(reinterpret_cast<uint32_t *>(buffer + start));
+            arm[0] = A$add(A$lr, A$pc, 1);
+            arm[1] = A$ldr_rd_$rn_im$(A$pc, A$pc, (trailer - arm) * sizeof(uint32_t) - 8);
+#endif
+
+            start += 7;
+            end -= 10;
+        } else if (T$pcrel$ldrw(backup[offset])) {
+            union {
+                uint16_t value;
+
+                struct {
+                    uint16_t : 7;
+                    uint16_t u : 1;
+                    uint16_t : 8;
+                };
+            } bits = {backup[offset+0]};
+
+            union {
+                uint16_t value;
+
+                struct {
+                    uint16_t immediate : 12;
+                    uint16_t rt : 4;
+                };
+            } exts = {backup[offset+1]};
+
+            buffer[start+0] = T1$ldr_rt_$rn_im$(exts.rt, A$pc, T$Label(start+0, end-2));
+            buffer[start+1] = T2$ldr_rt_$rn_im$(exts.rt, A$pc, T$Label(start+0, end-2));
+
+            buffer[start+2] = T1$ldr_rt_$rn_im$(exts.rt, exts.rt, 0);
+            buffer[start+3] = T2$ldr_rt_$rn_im$(exts.rt, exts.rt, 0);
+
+            // XXX: this code "works", but is "wrong": the mechanism is more complex than this
+            *--trailer = ((reinterpret_cast<uint32_t>(area + offset) + 4) & ~0x2) + (bits.u == 0 ? -exts.immediate : exts.immediate);
+
+            ++offset;
+            start += 4;
+            end -= 2;
+        } else if (T$pcrel$add(backup[offset])) {
+            union {
+                uint16_t value;
+
+                struct {
+                    uint16_t rd : 3;
+                    uint16_t rm : 3;
+                    uint16_t h2 : 1;
+                    uint16_t h1 : 1;
+                    uint16_t : 8;
+                };
+            } bits = {backup[offset+0]};
+
+            if (bits.h1) {
+                MSLog(MSLogLevelError, "MS:Error:pcrel(%u):add (rd > r7)", offset);
+                goto fail;
+            }
+
+            unsigned rt(bits.rd == A$r7 ? A$r6 : A$r7);
+
+            buffer[start+0] = T$push_r(1 << rt);
+            buffer[start+1] = T$mov_rd_rm(rt, (bits.h1 << 3) | bits.rd);
+            buffer[start+2] = T$ldr_rd_$pc_im_4$(bits.rd, T$Label(start+2, end-2) / 4);
+            buffer[start+3] = T$add_rd_rm((bits.h1 << 3) | bits.rd, rt);
+            buffer[start+4] = T$pop_r(1 << rt);
+            *--trailer = reinterpret_cast<uint32_t>(area + offset) + 4;
+
+            start += 5;
+            end -= 2;
+        } else if (T$32bit$i(backup[offset])) {
+            buffer[start++] = backup[offset];
+            buffer[start++] = backup[++offset];
+        } else {
+            buffer[start++] = backup[offset];
+        }
+    }
+
+    buffer[start++] = T$bx(A$pc);
+    buffer[start++] = T$nop;
+
+    uint32_t *transfer = reinterpret_cast<uint32_t *>(buffer + start);
+    transfer[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
+    transfer[1] = reinterpret_cast<uint32_t>(area + used / sizeof(uint16_t)) + 1;
+
+    if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) {
+        MSLog(MSLogLevelError, "MS:Error:mprotect():%d", errno);
+        return 0;
+    }
+
+    *result = reinterpret_cast<uint8_t *>(buffer + pad) + 1;
+
+    if (MSDebug) {
+        char name[16];
+        sprintf(name, "%p", *result);
+        MSLogHexEx(buffer, length, 2, name);
+    }
+
+    }
+
+    {
+        SubstrateHookMemory code(process, area, used);
+
+        if (align != 0)
+            area[0] = T$nop;
+
+        thumb[0] = T$bx(A$pc);
+        thumb[1] = T$nop;
+
+        arm[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
+        arm[1] = reinterpret_cast<uint32_t>(replace);
+
+        for (unsigned offset(0); offset != blank; ++offset)
+            trail[offset] = T$nop;
+    }
+
+    if (MSDebug) {
+        char name[16];
+        sprintf(name, "%p", area);
+        MSLogHexEx(area, used + sizeof(uint16_t), 2, name);
+    }
+	
+	return used;
+}
+
+static size_t SubstrateHookFunctionARM(SubstrateProcessRef process, void *symbol, void *replace, void **result) {
+    if (symbol == NULL)
+        return 0;
+printf("SubstrateHookFunctionARM\n");
+    uint32_t *area(reinterpret_cast<uint32_t *>(symbol));
+    uint32_t *arm(area);
+
+    const size_t used(8);
+
+    uint32_t backup[used / sizeof(uint32_t)] = {arm[0], arm[1]};
+
+    if (MSDebug) {
+        char name[16];
+        sprintf(name, "%p", area);
+        MSLogHexEx(area, used + sizeof(uint32_t), 4, name);
+    }
+
+    if (result != NULL) {
+
+    if (backup[0] == A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8)) {
+        *result = reinterpret_cast<void *>(backup[1]);
+        
+		return sizeof(backup[0]);
+    }
+
+    size_t length(used);
+    for (unsigned offset(0); offset != used / sizeof(uint32_t); ++offset)
+        if (A$pcrel$r(backup[offset])) {
+            if ((backup[offset] & 0x02000000) == 0 || (backup[offset] & 0x0000f000 >> 12) != (backup[offset] & 0x0000000f))
+                length += 2 * sizeof(uint32_t);
+            else
+                length += 4 * sizeof(uint32_t);
+        }
+
+    length += 2 * sizeof(uint32_t);
+
+    uint32_t *buffer(reinterpret_cast<uint32_t *>(mmap(
+        NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0
+    )));
+
+    if (buffer == MAP_FAILED) {
+        MSLog(MSLogLevelError, "MS:Error:mmap() = %d", errno);
+        *result = NULL;
+        return 0;
+    }
+
+    if (false) fail: {
+        munmap(buffer, length);
+        *result = NULL;
+        return 0;
+    }
+
+    size_t start(0), end(length / sizeof(uint32_t));
+    uint32_t *trailer(reinterpret_cast<uint32_t *>(buffer + end));
+    for (unsigned offset(0); offset != used / sizeof(uint32_t); ++offset)
+        if (A$pcrel$r(backup[offset])) {
+            union {
+                uint32_t value;
+
+                struct {
+                    uint32_t rm : 4;
+                    uint32_t : 1;
+                    uint32_t shift : 2;
+                    uint32_t shiftamount : 5;
+                    uint32_t rd : 4;
+                    uint32_t rn : 4;
+                    uint32_t l : 1;
+                    uint32_t w : 1;
+                    uint32_t b : 1;
+                    uint32_t u : 1;
+                    uint32_t p : 1;
+                    uint32_t mode : 1;
+                    uint32_t type : 2;
+                    uint32_t cond : 4;
+                };
+            } bits = {backup[offset+0]}, copy(bits);
+
+            bool guard;
+            if (bits.mode == 0 || bits.rd != bits.rm) {
+                copy.rn = bits.rd;
+                guard = false;
+            } else {
+                copy.rn = bits.rm != A$r0 ? A$r0 : A$r1;
+                guard = true;
+            }
+
+            if (guard)
+                buffer[start++] = A$stmdb_sp$_$rs$((1 << copy.rn));
+
+            buffer[start+0] = A$ldr_rd_$rn_im$(copy.rn, A$pc, (end-1 - (start+0)) * 4 - 8);
+            buffer[start+1] = copy.value;
+
+            start += 2;
+
+            if (guard)
+                buffer[start++] = A$ldmia_sp$_$rs$((1 << copy.rn));
+
+            *--trailer = reinterpret_cast<uint32_t>(area + offset) + 8;
+            end -= 1;
+        } else
+            buffer[start++] = backup[offset];
+
+    buffer[start+0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
+    buffer[start+1] = reinterpret_cast<uint32_t>(area + used / sizeof(uint32_t));
+
+    if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) {
+        MSLog(MSLogLevelError, "MS:Error:mprotect():%d", errno);
+        goto fail;
+    }
+
+    *result = buffer;
+
+    if (MSDebug) {
+        char name[16];
+        sprintf(name, "%p", *result);
+        MSLogHexEx(buffer, length, 4, name);
+    }
+
+    }
+
+    {
+        SubstrateHookMemory code(process, symbol, used);
+
+        arm[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
+        arm[1] = reinterpret_cast<uint32_t>(replace);
+    }
+
+    if (MSDebug) {
+        char name[16];
+        sprintf(name, "%p", area);
+        MSLogHexEx(area, used + sizeof(uint32_t), 4, name);
+    }
+	
+	return used;
+}
+
+static size_t SubstrateHookFunction(SubstrateProcessRef process, void *symbol, void *replace, void **result) {
+    if (MSDebug)
+        MSLog(MSLogLevelNotice, "SubstrateHookFunction(%p, %p, %p, %p)\n", process, symbol, replace, result);
+    if ((reinterpret_cast<uintptr_t>(symbol) & 0x1) == 0)
+        return SubstrateHookFunctionARM(process, symbol, replace, result);
+    else
+        return SubstrateHookFunctionThumb(process, reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(symbol) & ~0x1), replace, result);
+}
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include "SubstrateX86.hpp"
+
+static size_t MSGetInstructionWidthIntel(void *start) {
+    hde64s decode;
+    return hde64_disasm(start, &decode);
+}
+
+static void SubstrateHookFunction(SubstrateProcessRef process, void *symbol, void *replace, void **result) {
+    if (MSDebug)
+        MSLog(MSLogLevelNotice, "MSHookFunction(%p, %p, %p)\n", symbol, replace, result);
+    if (symbol == NULL)
+        return;
+
+    uintptr_t source(reinterpret_cast<uintptr_t>(symbol));
+    uintptr_t target(reinterpret_cast<uintptr_t>(replace));
+
+    uint8_t *area(reinterpret_cast<uint8_t *>(symbol));
+
+    size_t required(MSSizeOfJump(target, source));
+
+    if (MSDebug) {
+        char name[16];
+        sprintf(name, "%p", area);
+        MSLogHex(area, 32, name);
+    }
+
+    size_t used(0);
+    while (used < required) {
+        size_t width(MSGetInstructionWidthIntel(area + used));
+        if (width == 0) {
+            MSLog(MSLogLevelError, "MS:Error:MSGetInstructionWidthIntel(%p) == 0", area + used);
+            return;
+        }
+
+        used += width;
+    }
+
+    size_t blank(used - required);
+
+    if (MSDebug) {
+        char name[16];
+        sprintf(name, "%p", area);
+        MSLogHex(area, used + sizeof(uint16_t), name);
+    }
+
+    uint8_t backup[used];
+    memcpy(backup, area, used);
+
+    if (result != NULL) {
+
+    if (backup[0] == 0xe9) {
+        *result = reinterpret_cast<void *>(source + 5 + *reinterpret_cast<uint32_t *>(backup + 1));
+        return;
+    }
+
+    if (!ia32 && backup[0] == 0xff && backup[1] == 0x25) {
+        *result = *reinterpret_cast<void **>(source + 6 + *reinterpret_cast<uint32_t *>(backup + 2));
+        return;
+    }
+
+    size_t length(used + MSSizeOfJump(source + used));
+
+    for (size_t offset(0), width; offset != used; offset += width) {
+        hde64s decode;
+        hde64_disasm(backup + offset, &decode);
+        width = decode.len;
+        //_assert(width != 0 && offset + width <= used);
+
+#ifdef __LP64__
+        if ((decode.modrm & 0xc7) == 0x05) {
+            if (decode.opcode == 0x8b) {
+                void *destiny(area + offset + width + int32_t(decode.disp.disp32));
+                uint8_t reg(decode.rex_r << 3 | decode.modrm_reg);
+                length -= decode.len;
+                length += MSSizeOfPushPointer(destiny);
+                length += MSSizeOfPop(reg);
+                length += MSSizeOfMove64();
+            } else {
+                MSLog(MSLogLevelError, "MS:Error: Unknown RIP-Relative (%.2x %.2x)", decode.opcode, decode.opcode2);
+                continue;
+            }
+        } else
+#endif
+
+        if (backup[offset] == 0xe8) {
+            int32_t relative(*reinterpret_cast<int32_t *>(backup + offset + 1));
+            void *destiny(area + offset + decode.len + relative);
+
+            if (relative == 0) {
+                length -= decode.len;
+                length += MSSizeOfPushPointer(destiny);
+            } else {
+                length += MSSizeOfSkip();
+                length += MSSizeOfJump(destiny);
+            }
+        } else if (backup[offset] == 0xeb) {
+            length -= decode.len;
+            length += MSSizeOfJump(area + offset + decode.len + *reinterpret_cast<int8_t *>(backup + offset + 1));
+        } else if (backup[offset] == 0xe9) {
+            length -= decode.len;
+            length += MSSizeOfJump(area + offset + decode.len + *reinterpret_cast<int32_t *>(backup + offset + 1));
+        } else if (
+            backup[offset] == 0xe3 ||
+            (backup[offset] & 0xf0) == 0x70
+            // XXX: opcode2 & 0xf0 is 0x80?
+        ) {
+            length += decode.len;
+            length += MSSizeOfJump(area + offset + decode.len + *reinterpret_cast<int8_t *>(backup + offset + 1));
+        }
+    }
+
+    uint8_t *buffer(reinterpret_cast<uint8_t *>(mmap(
+        NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0
+    )));
+
+    if (buffer == MAP_FAILED) {
+        MSLog(MSLogLevelError, "MS:Error:mmap() = %d", errno);
+        *result = NULL;
+        return;
+    }
+
+    if (false) fail: {
+        munmap(buffer, length);
+        *result = NULL;
+        return;
+    }
+
+    {
+        uint8_t *current(buffer);
+
+        for (size_t offset(0), width; offset != used; offset += width) {
+            hde64s decode;
+            hde64_disasm(backup + offset, &decode);
+            width = decode.len;
+            //_assert(width != 0 && offset + width <= used);
+
+#ifdef __LP64__
+            if ((decode.modrm & 0xc7) == 0x05) {
+                if (decode.opcode == 0x8b) {
+                    void *destiny(area + offset + width + int32_t(decode.disp.disp32));
+                    uint8_t reg(decode.rex_r << 3 | decode.modrm_reg);
+                    MSPushPointer(current, destiny);
+                    MSWritePop(current, reg);
+                    MSWriteMove64(current, reg, reg);
+                } else {
+                    MSLog(MSLogLevelError, "MS:Error: Unknown RIP-Relative (%.2x %.2x)", decode.opcode, decode.opcode2);
+                    goto copy;
+                }
+            } else
+#endif
+
+            if (backup[offset] == 0xe8) {
+                int32_t relative(*reinterpret_cast<int32_t *>(backup + offset + 1));
+                if (relative == 0)
+                    MSPushPointer(current, area + offset + decode.len);
+                else {
+                    MSWrite<uint8_t>(current, 0xe8);
+                    MSWrite<int32_t>(current, MSSizeOfSkip());
+                    void *destiny(area + offset + decode.len + relative);
+                    MSWriteSkip(current, MSSizeOfJump(destiny, current + MSSizeOfSkip()));
+                    MSWriteJump(current, destiny);
+                }
+            } else if (backup[offset] == 0xeb)
+                MSWriteJump(current, area + offset + decode.len + *reinterpret_cast<int8_t *>(backup + offset + 1));
+            else if (backup[offset] == 0xe9)
+                MSWriteJump(current, area + offset + decode.len + *reinterpret_cast<int32_t *>(backup + offset + 1));
+            else if (
+                backup[offset] == 0xe3 ||
+                (backup[offset] & 0xf0) == 0x70
+            ) {
+                MSWrite<uint8_t>(current, backup[offset]);
+                MSWrite<uint8_t>(current, 2);
+                MSWrite<uint8_t>(current, 0xeb);
+                void *destiny(area + offset + decode.len + *reinterpret_cast<int8_t *>(backup + offset + 1));
+                MSWrite<uint8_t>(current, MSSizeOfJump(destiny, current + 1));
+                MSWriteJump(current, destiny);
+            } else
+#ifdef __LP64__
+                copy:
+#endif
+            {
+                MSWrite(current, backup + offset, width);
+            }
+        }
+
+        MSWriteJump(current, area + used);
+    }
+
+    if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) {
+        MSLog(MSLogLevelError, "MS:Error:mprotect():%d", errno);
+        goto fail;
+    }
+
+    *result = buffer;
+
+    if (MSDebug) {
+        char name[16];
+        sprintf(name, "%p", *result);
+        MSLogHex(buffer, length, name);
+    }
+
+    }
+
+    {
+        SubstrateHookMemory code(process, area, used);
+
+        uint8_t *current(area);
+        MSWriteJump(current, target);
+        for (unsigned offset(0); offset != blank; ++offset)
+            MSWrite<uint8_t>(current, 0x90);
+    }
+
+    if (MSDebug) {
+        char name[16];
+        sprintf(name, "%p", area);
+        MSLogHex(area, used + sizeof(uint16_t), name);
+    }
+}
+#endif
+
+_extern void MSHookFunction(void *symbol, void *replace, void **result) {
+     SubstrateHookFunction(NULL, symbol, replace, result);
+}
+
+#if defined(__APPLE__) && defined(__arm__)
+_extern void _Z14MSHookFunctionPvS_PS_(void *symbol, void *replace, void **result) {
+    return MSHookFunction(symbol, replace, result);
+}
+#endif
diff --git a/VirtualApp/lib/src/main/jni/Substrate/SubstrateHook.h b/VirtualApp/lib/src/main/jni/Substrate/SubstrateHook.h
new file mode 100755
index 000000000..40a0296cb
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/Substrate/SubstrateHook.h
@@ -0,0 +1,19 @@
+#ifndef __SUBSTRATEHOOK_H__
+#define __SUBSTRATEHOOK_H__
+
+
+#include <stdlib.h>
+
+#define _extern extern "C" __attribute__((__visibility__("default")))
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void MSHookFunction(void *symbol, void *replace, void **result);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/VirtualApp/lib/src/main/jni/Substrate/SubstrateLog.hpp b/VirtualApp/lib/src/main/jni/Substrate/SubstrateLog.hpp
new file mode 100755
index 000000000..3e5728014
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/Substrate/SubstrateLog.hpp
@@ -0,0 +1,40 @@
+/* Cydia Substrate - Powerful Code Insertion Platform
+ * Copyright (C) 2008-2011  Jay Freeman (saurik)
+*/
+
+/* GNU Lesser General Public License, Version 3 {{{ */
+/*
+ * Substrate is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Substrate is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Substrate.  If not, see <http://www.gnu.org/licenses/>.
+**/
+/* }}} */
+
+#ifndef SUBSTRATE_LOG_HPP
+#define SUBSTRATE_LOG_HPP
+
+#if 0
+#include <android/log.h>
+
+#define MSLog(level, format, ...) ((void)__android_log_print(level, "NNNN", format, __VA_ARGS__))
+
+#define MSLogLevelNotice ANDROID_LOG_INFO
+#define MSLogLevelWarning ANDROID_LOG_WARN
+#define MSLogLevelError ANDROID_LOG_ERROR
+
+#else
+
+#define MSLog(level, format, ...) printf(format, __VA_ARGS__)
+
+#endif
+
+#endif//SUBSTRATE_LOG_HPP
diff --git a/VirtualApp/lib/src/main/jni/Substrate/SubstratePosixMemory.cpp b/VirtualApp/lib/src/main/jni/Substrate/SubstratePosixMemory.cpp
new file mode 100755
index 000000000..709cb228e
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/Substrate/SubstratePosixMemory.cpp
@@ -0,0 +1,75 @@
+/* Cydia Substrate - Powerful Code Insertion Platform
+ * Copyright (C) 2008-2011  Jay Freeman (saurik)
+*/
+
+/* GNU Lesser General Public License, Version 3 {{{ */
+/*
+ * Substrate is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Substrate is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Substrate.  If not, see <http://www.gnu.org/licenses/>.
+**/
+/* }}} */
+
+#define SubstrateInternal
+#include "CydiaSubstrate.h"
+#include "SubstrateLog.hpp"
+
+#include <sys/mman.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+extern "C" void __clear_cache (void *beg, void *end);
+
+struct __SubstrateMemory {
+    void *address_;
+    size_t width_;
+
+    __SubstrateMemory(void *address, size_t width) :
+        address_(address),
+        width_(width)
+    {
+    }
+};
+
+extern "C" SubstrateMemoryRef SubstrateMemoryCreate(SubstrateAllocatorRef allocator, SubstrateProcessRef process, void *data, size_t size) {
+    if (allocator != NULL) {
+        MSLog(MSLogLevelError, "MS:Error:allocator != %d", 0);
+        return NULL;
+    }
+
+    if (size == 0)
+        return NULL;
+
+    long page(sysconf(_SC_PAGESIZE)); // Portable applications should employ sysconf(_SC_PAGESIZE) instead of getpagesize
+
+    uintptr_t base(reinterpret_cast<uintptr_t>(data) / page * page);
+    size_t width(((reinterpret_cast<uintptr_t>(data) + size - 1) / page + 1) * page - base);
+    void *address(reinterpret_cast<void *>(base));
+
+    if (mprotect(address, width, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) {
+        MSLog(MSLogLevelError, "MS:Error:mprotect() = %d", errno);
+        return NULL;
+    }
+
+    return new __SubstrateMemory(address, width);
+}
+
+extern "C" void SubstrateMemoryRelease(SubstrateMemoryRef memory) {
+    if (mprotect(memory->address_, memory->width_, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
+        MSLog(MSLogLevelError, "MS:Error:mprotect() = %d", errno);
+
+    __clear_cache(reinterpret_cast<char *>(memory->address_), reinterpret_cast<char *>(memory->address_) + memory->width_);
+
+    delete memory;
+}
diff --git a/VirtualApp/lib/src/main/jni/Substrate/SubstrateX86.hpp b/VirtualApp/lib/src/main/jni/Substrate/SubstrateX86.hpp
new file mode 100755
index 000000000..ffe2b06e3
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/Substrate/SubstrateX86.hpp
@@ -0,0 +1,200 @@
+/* Cydia Substrate - Powerful Code Insertion Platform
+ * Copyright (C) 2008-2011  Jay Freeman (saurik)
+*/
+
+/* GNU Lesser General Public License, Version 3 {{{ */
+/*
+ * Substrate is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Substrate is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Substrate.  If not, see <http://www.gnu.org/licenses/>.
+**/
+/* }}} */
+
+#ifndef SUBSTRATE_X86_HPP
+#define SUBSTRATE_X86_HPP
+
+#include "Buffer.hpp"
+
+#ifdef __LP64__
+static const bool ia32 = false;
+#else
+static const bool ia32 = true;
+#endif
+
+enum I$r {
+    I$rax, I$rcx, I$rdx, I$rbx,
+    I$rsp, I$rbp, I$rsi, I$rdi,
+    I$r8, I$r9, I$r10, I$r11,
+    I$r12, I$r13, I$r14, I$r15,
+};
+
+_disused static bool MSIs32BitOffset(uintptr_t target, uintptr_t source) {
+    intptr_t offset(target - source);
+    return int32_t(offset) == offset;
+}
+
+_disused static size_t MSSizeOfSkip() {
+    return 5;
+}
+
+_disused static size_t MSSizeOfPushPointer(uintptr_t target) {
+    return uint64_t(target) >> 32 == 0 ? 5 : 13;
+}
+
+_disused static size_t MSSizeOfPushPointer(void *target) {
+    return MSSizeOfPushPointer(reinterpret_cast<uintptr_t>(target));
+}
+
+_disused static size_t MSSizeOfJump(bool blind, uintptr_t target, uintptr_t source = 0) {
+    if (ia32 || !blind && MSIs32BitOffset(target, source + 5))
+        return MSSizeOfSkip();
+    else
+        return MSSizeOfPushPointer(target) + 1;
+}
+
+_disused static size_t MSSizeOfJump(uintptr_t target, uintptr_t source) {
+    return MSSizeOfJump(false, target, source);
+}
+
+_disused static size_t MSSizeOfJump(uintptr_t target) {
+    return MSSizeOfJump(true, target);
+}
+
+_disused static size_t MSSizeOfJump(void *target, void *source) {
+    return MSSizeOfJump(reinterpret_cast<uintptr_t>(target), reinterpret_cast<uintptr_t>(source));
+}
+
+_disused static size_t MSSizeOfJump(void *target) {
+    return MSSizeOfJump(reinterpret_cast<uintptr_t>(target));
+}
+
+_disused static void MSWriteSkip(uint8_t *&current, ssize_t size) {
+    MSWrite<uint8_t>(current, 0xe9);
+    MSWrite<uint32_t>(current, size);
+}
+
+_disused static void MSPushPointer(uint8_t *&current, uintptr_t target) {
+    MSWrite<uint8_t>(current, 0x68);
+    MSWrite<uint32_t>(current, target);
+
+    if (uint32_t high = uint64_t(target) >> 32) {
+        MSWrite<uint8_t>(current, 0xc7);
+        MSWrite<uint8_t>(current, 0x44);
+        MSWrite<uint8_t>(current, 0x24);
+        MSWrite<uint8_t>(current, 0x04);
+        MSWrite<uint32_t>(current, high);
+    }
+}
+
+_disused static void MSPushPointer(uint8_t *&current, void *target) {
+    return MSPushPointer(current, reinterpret_cast<uintptr_t>(target));
+}
+
+_disused static void MSWriteCall(uint8_t *&current, I$r target) {
+    if (target >> 3 != 0)
+        MSWrite<uint8_t>(current, 0x40 | (target & 0x08) >> 3);
+    MSWrite<uint8_t>(current, 0xff);
+    MSWrite<uint8_t>(current, 0xd0 | target & 0x07);
+}
+
+_disused static void MSWriteCall(uint8_t *&current, uintptr_t target) {
+    uintptr_t source(reinterpret_cast<uintptr_t>(current));
+
+    if (ia32 || MSIs32BitOffset(target, source + 5)) {
+        MSWrite<uint8_t>(current, 0xe8);
+        MSWrite<uint32_t>(current, target - (source + 5));
+    } else {
+        MSPushPointer(current, target);
+
+        MSWrite<uint8_t>(current, 0x83);
+        MSWrite<uint8_t>(current, 0xc4);
+        MSWrite<uint8_t>(current, 0x08);
+
+        MSWrite<uint8_t>(current, 0x67);
+        MSWrite<uint8_t>(current, 0xff);
+        MSWrite<uint8_t>(current, 0x54);
+        MSWrite<uint8_t>(current, 0x24);
+        MSWrite<uint8_t>(current, 0xf8);
+    }
+}
+
+template <typename Type_>
+_disused static void MSWriteCall(uint8_t *&current, Type_ *target) {
+    return MSWriteCall(current, reinterpret_cast<uintptr_t>(target));
+}
+
+_disused static void MSWriteJump(uint8_t *&current, uintptr_t target) {
+    uintptr_t source(reinterpret_cast<uintptr_t>(current));
+
+    if (ia32 || MSIs32BitOffset(target, source + 5))
+        MSWriteSkip(current, target - (source + 5));
+    else {
+        MSPushPointer(current, target);
+        MSWrite<uint8_t>(current, 0xc3);
+    }
+}
+
+_disused static void MSWriteJump(uint8_t *&current, void *target) {
+    return MSWriteJump(current, reinterpret_cast<uintptr_t>(target));
+}
+
+_disused static void MSWriteJump(uint8_t *&current, I$r target) {
+    if (target >> 3 != 0)
+        MSWrite<uint8_t>(current, 0x40 | (target & 0x08) >> 3);
+    MSWrite<uint8_t>(current, 0xff);
+    MSWrite<uint8_t>(current, 0xe0 | target & 0x07);
+}
+
+_disused static void MSWritePop(uint8_t *&current, uint8_t target) {
+    if (target >> 3 != 0)
+        MSWrite<uint8_t>(current, 0x40 | (target & 0x08) >> 3);
+    MSWrite<uint8_t>(current, 0x58 | target & 0x07);
+}
+
+_disused static size_t MSSizeOfPop(uint8_t target) {
+    return target >> 3 != 0 ? 2 : 1;
+}
+
+_disused static void MSWritePush(uint8_t *&current, I$r target) {
+    if (target >> 3 != 0)
+        MSWrite<uint8_t>(current, 0x40 | (target & 0x08) >> 3);
+    MSWrite<uint8_t>(current, 0x50 | target & 0x07);
+}
+
+_disused static void MSWriteAdd(uint8_t *&current, I$r target, uint8_t source) {
+    MSWrite<uint8_t>(current, 0x83);
+    MSWrite<uint8_t>(current, 0xc4 | target & 0x07);
+    MSWrite<uint8_t>(current, source);
+}
+
+_disused static void MSWriteSet64(uint8_t *&current, I$r target, uintptr_t source) {
+    MSWrite<uint8_t>(current, 0x48 | (target & 0x08) >> 3 << 2);
+    MSWrite<uint8_t>(current, 0xb8 | target & 0x7);
+    MSWrite<uint64_t>(current, source);
+}
+
+template <typename Type_>
+_disused static void MSWriteSet64(uint8_t *&current, I$r target, Type_ *source) {
+    return MSWriteSet64(current, target, reinterpret_cast<uintptr_t>(source));
+}
+
+_disused static void MSWriteMove64(uint8_t *&current, uint8_t source, uint8_t target) {
+    MSWrite<uint8_t>(current, 0x48 | (target & 0x08) >> 3 << 2 | (source & 0x08) >> 3);
+    MSWrite<uint8_t>(current, 0x8b);
+    MSWrite<uint8_t>(current, (target & 0x07) << 3 | source & 0x07);
+}
+
+_disused static size_t MSSizeOfMove64() {
+    return 3;
+}
+
+#endif//SUBSTRATE_X86_HPP
diff --git a/VirtualApp/lib/src/main/jni/Substrate/hde64.c b/VirtualApp/lib/src/main/jni/Substrate/hde64.c
new file mode 100755
index 000000000..d69f0c68e
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/Substrate/hde64.c
@@ -0,0 +1,332 @@
+/*
+ * Hacker Disassembler Engine 64 C
+ * Copyright (c) 2008-2009, Vyacheslav Patkov.
+ * All rights reserved.
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include "hde64.h"
+#include "table64.h"
+
+unsigned int hde64_disasm(const void *code, hde64s *hs)
+{
+    uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0;
+    uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0;
+    uint8_t op64 = 0;
+
+    memset(hs,0,sizeof(hde64s));
+    char *tmp=(char*)hs;
+
+    for (x = 16; x; x--)
+        switch (c = *p++) {
+            case 0xf3:
+                hs->p_rep = c;
+                pref |= PRE_F3;
+                break;
+            case 0xf2:
+                hs->p_rep = c;
+                pref |= PRE_F2;
+                break;
+            case 0xf0:
+                hs->p_lock = c;
+                pref |= PRE_LOCK;
+                break;
+            case 0x26: case 0x2e: case 0x36:
+            case 0x3e: case 0x64: case 0x65:
+                hs->p_seg = c;
+                pref |= PRE_SEG;
+                break;
+            case 0x66:
+                hs->p_66 = c;
+                pref |= PRE_66;
+                break;
+            case 0x67:
+                hs->p_67 = c;
+                pref |= PRE_67;
+                break;
+            default:
+                goto pref_done;
+        }
+  pref_done:
+
+    hs->flags = (uint32_t)pref << 23;
+
+    if (!pref)
+        pref |= PRE_NONE;
+
+    if ((c & 0xf0) == 0x40) {
+        hs->flags |= F_PREFIX_REX;
+        if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8)
+            op64++;
+        hs->rex_r = (c & 7) >> 2;
+        hs->rex_x = (c & 3) >> 1;
+        hs->rex_b = c & 1;
+        if (((c = *p++) & 0xf0) == 0x40) {
+            opcode = c;
+            goto error_opcode;
+        }
+    }
+
+    if ((hs->opcode = c) == 0x0f) {
+        hs->opcode2 = c = *p++;
+        ht += DELTA_OPCODES;
+    } else if (c >= 0xa0 && c <= 0xa3) {
+        op64++;
+        if (pref & PRE_67)
+            pref |= PRE_66;
+        else
+            pref &= ~PRE_66;
+    }
+
+    opcode = c;
+    cflags = ht[ht[opcode / 4] + (opcode % 4)];
+
+    if (cflags == C_ERROR) {
+      error_opcode:
+        hs->flags |= F_ERROR | F_ERROR_OPCODE;
+        cflags = 0;
+        if ((opcode & -3) == 0x24)
+            cflags++;
+    }
+
+    x = 0;
+    if (cflags & C_GROUP) {
+        uint16_t t;
+        t = *(uint16_t *)(ht + (cflags & 0x7f));
+        cflags = (uint8_t)t;
+        x = (uint8_t)(t >> 8);
+    }
+
+    if (hs->opcode2) {
+        ht = hde64_table + DELTA_PREFIXES;
+        if (ht[ht[opcode / 4] + (opcode % 4)] & pref)
+            hs->flags |= F_ERROR | F_ERROR_OPCODE;
+    }
+
+    if (cflags & C_MODRM) {
+        hs->flags |= F_MODRM;
+        hs->modrm = c = *p++;
+        hs->modrm_mod = m_mod = c >> 6;
+        hs->modrm_rm = m_rm = c & 7;
+        hs->modrm_reg = m_reg = (c & 0x3f) >> 3;
+
+        if (x && ((x << m_reg) & 0x80))
+            hs->flags |= F_ERROR | F_ERROR_OPCODE;
+
+        if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) {
+            uint8_t t = opcode - 0xd9;
+            if (m_mod == 3) {
+                ht = hde64_table + DELTA_FPU_MODRM + t*8;
+                t = ht[m_reg] << m_rm;
+            } else {
+                ht = hde64_table + DELTA_FPU_REG;
+                t = ht[t] << m_reg;
+            }
+            if (t & 0x80)
+                hs->flags |= F_ERROR | F_ERROR_OPCODE;
+        }
+
+        if (pref & PRE_LOCK) {
+            if (m_mod == 3) {
+                hs->flags |= F_ERROR | F_ERROR_LOCK;
+            } else {
+                uint8_t *table_end, op = opcode;
+                if (hs->opcode2) {
+                    ht = hde64_table + DELTA_OP2_LOCK_OK;
+                    table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK;
+                } else {
+                    ht = hde64_table + DELTA_OP_LOCK_OK;
+                    table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK;
+                    op &= -2;
+                }
+                for (; ht != table_end; ht++)
+                    if (*ht++ == op) {
+                        if (!((*ht << m_reg) & 0x80))
+                            goto no_lock_error;
+                        else
+                            break;
+                    }
+                hs->flags |= F_ERROR | F_ERROR_LOCK;
+              no_lock_error:
+                ;
+            }
+        }
+
+        if (hs->opcode2) {
+            switch (opcode) {
+                case 0x20: case 0x22:
+                    m_mod = 3;
+                    if (m_reg > 4 || m_reg == 1)
+                        goto error_operand;
+                    else
+                        goto no_error_operand;
+                case 0x21: case 0x23:
+                    m_mod = 3;
+                    if (m_reg == 4 || m_reg == 5)
+                        goto error_operand;
+                    else
+                        goto no_error_operand;
+            }
+        } else {
+            switch (opcode) {
+                case 0x8c:
+                    if (m_reg > 5)
+                        goto error_operand;
+                    else
+                        goto no_error_operand;
+                case 0x8e:
+                    if (m_reg == 1 || m_reg > 5)
+                        goto error_operand;
+                    else
+                        goto no_error_operand;
+            }
+        }
+
+        if (m_mod == 3) {
+            uint8_t *table_end;
+            if (hs->opcode2) {
+                ht = hde64_table + DELTA_OP2_ONLY_MEM;
+                table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM;
+            } else {
+                ht = hde64_table + DELTA_OP_ONLY_MEM;
+                table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM;
+            }
+            for (; ht != table_end; ht += 2)
+                if (*ht++ == opcode) {
+                    if (*ht++ & pref && !((*ht << m_reg) & 0x80))
+                        goto error_operand;
+                    else
+                        break;
+                }
+            goto no_error_operand;
+        } else if (hs->opcode2) {
+            switch (opcode) {
+                case 0x50: case 0xd7: case 0xf7:
+                    if (pref & (PRE_NONE | PRE_66))
+                        goto error_operand;
+                    break;
+                case 0xd6:
+                    if (pref & (PRE_F2 | PRE_F3))
+                        goto error_operand;
+                    break;
+                case 0xc5:
+                    goto error_operand;
+            }
+            goto no_error_operand;
+        } else
+            goto no_error_operand;
+
+      error_operand:
+        hs->flags |= F_ERROR | F_ERROR_OPERAND;
+      no_error_operand:
+
+        c = *p++;
+        if (m_reg <= 1) {
+            if (opcode == 0xf6)
+                cflags |= C_IMM8;
+            else if (opcode == 0xf7)
+                cflags |= C_IMM_P66;
+        }
+
+        switch (m_mod) {
+            case 0:
+                if (pref & PRE_67) {
+                    if (m_rm == 6)
+                        disp_size = 2;
+                } else
+                    if (m_rm == 5)
+                        disp_size = 4;
+                break;
+            case 1:
+                disp_size = 1;
+                break;
+            case 2:
+                disp_size = 2;
+                if (!(pref & PRE_67))
+                    disp_size <<= 1;
+        }
+
+        if (m_mod != 3 && m_rm == 4) {
+            hs->flags |= F_SIB;
+            p++;
+            hs->sib = c;
+            hs->sib_scale = c >> 6;
+            hs->sib_index = (c & 0x3f) >> 3;
+            if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1))
+                disp_size = 4;
+        }
+
+        p--;
+        switch (disp_size) {
+            case 1:
+                hs->flags |= F_DISP8;
+                hs->disp.disp8 = *p;
+                break;
+            case 2:
+                hs->flags |= F_DISP16;
+                hs->disp.disp16 = *(uint16_t *)p;
+                break;
+            case 4:
+                hs->flags |= F_DISP32;
+                hs->disp.disp32 = *(uint32_t *)p;
+        }
+        p += disp_size;
+    } else if (pref & PRE_LOCK)
+        hs->flags |= F_ERROR | F_ERROR_LOCK;
+
+    if (cflags & C_IMM_P66) {
+        if (cflags & C_REL32) {
+            if (pref & PRE_66) {
+                hs->flags |= F_IMM16 | F_RELATIVE;
+                hs->imm.imm16 = *(uint16_t *)p;
+                p += 2;
+                goto disasm_done;
+            }
+            goto rel32_ok;
+        }
+        if (op64) {
+            hs->flags |= F_IMM64;
+            hs->imm.imm64 = *(uint64_t *)p;
+            p += 8;
+        } else if (!(pref & PRE_66)) {
+            hs->flags |= F_IMM32;
+            hs->imm.imm32 = *(uint32_t *)p;
+            p += 4;
+        } else
+            goto imm16_ok;
+    }
+
+
+    if (cflags & C_IMM16) {
+      imm16_ok:
+        hs->flags |= F_IMM16;
+        hs->imm.imm16 = *(uint16_t *)p;
+        p += 2;
+    }
+    if (cflags & C_IMM8) {
+        hs->flags |= F_IMM8;
+        hs->imm.imm8 = *p++;
+    }
+
+    if (cflags & C_REL32) {
+      rel32_ok:
+        hs->flags |= F_IMM32 | F_RELATIVE;
+        hs->imm.imm32 = *(uint32_t *)p;
+        p += 4;
+    } else if (cflags & C_REL8) {
+        hs->flags |= F_IMM8 | F_RELATIVE;
+        hs->imm.imm8 = *p++;
+    }
+
+  disasm_done:
+
+    if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) {
+        hs->flags |= F_ERROR | F_ERROR_LENGTH;
+        hs->len = 15;
+    }
+
+    return (unsigned int)hs->len;
+}
diff --git a/VirtualApp/lib/src/main/jni/Substrate/hde64.h b/VirtualApp/lib/src/main/jni/Substrate/hde64.h
new file mode 100755
index 000000000..2fcc4cb25
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/Substrate/hde64.h
@@ -0,0 +1,112 @@
+/*
+ * Hacker Disassembler Engine 64
+ * Copyright (c) 2008-2009, Vyacheslav Patkov.
+ * All rights reserved.
+ *
+ * hde64.h: C/C++ header file
+ *
+ */
+
+#ifndef _HDE64_H_
+#define _HDE64_H_
+
+/* stdint.h - C99 standard header
+ * http://en.wikipedia.org/wiki/stdint.h
+ *
+ * if your compiler doesn't contain "stdint.h" header (for
+ * example, Microsoft Visual C++), you can download file:
+ *   http://www.azillionmonkeys.com/qed/pstdint.h
+ * and change next line to:
+ *   #include "pstdint.h"
+ */
+#include <stdint.h>
+
+#define F_MODRM         0x00000001
+#define F_SIB           0x00000002
+#define F_IMM8          0x00000004
+#define F_IMM16         0x00000008
+#define F_IMM32         0x00000010
+#define F_IMM64         0x00000020
+#define F_DISP8         0x00000040
+#define F_DISP16        0x00000080
+#define F_DISP32        0x00000100
+#define F_RELATIVE      0x00000200
+#define F_ERROR         0x00001000
+#define F_ERROR_OPCODE  0x00002000
+#define F_ERROR_LENGTH  0x00004000
+#define F_ERROR_LOCK    0x00008000
+#define F_ERROR_OPERAND 0x00010000
+#define F_PREFIX_REPNZ  0x01000000
+#define F_PREFIX_REPX   0x02000000
+#define F_PREFIX_REP    0x03000000
+#define F_PREFIX_66     0x04000000
+#define F_PREFIX_67     0x08000000
+#define F_PREFIX_LOCK   0x10000000
+#define F_PREFIX_SEG    0x20000000
+#define F_PREFIX_REX    0x40000000
+#define F_PREFIX_ANY    0x7f000000
+
+#define PREFIX_SEGMENT_CS   0x2e
+#define PREFIX_SEGMENT_SS   0x36
+#define PREFIX_SEGMENT_DS   0x3e
+#define PREFIX_SEGMENT_ES   0x26
+#define PREFIX_SEGMENT_FS   0x64
+#define PREFIX_SEGMENT_GS   0x65
+#define PREFIX_LOCK         0xf0
+#define PREFIX_REPNZ        0xf2
+#define PREFIX_REPX         0xf3
+#define PREFIX_OPERAND_SIZE 0x66
+#define PREFIX_ADDRESS_SIZE 0x67
+
+#pragma pack(push,1)
+
+typedef struct {
+    uint8_t len;
+    uint8_t p_rep;
+    uint8_t p_lock;
+    uint8_t p_seg;
+    uint8_t p_66;
+    uint8_t p_67;
+    uint8_t rex;
+    uint8_t rex_w;
+    uint8_t rex_r;
+    uint8_t rex_x;
+    uint8_t rex_b; 
+    uint8_t opcode;
+    uint8_t opcode2;
+    uint8_t modrm;
+    uint8_t modrm_mod;
+    uint8_t modrm_reg;
+    uint8_t modrm_rm;
+    uint8_t sib;
+    uint8_t sib_scale;
+    uint8_t sib_index;
+    uint8_t sib_base;
+    union {
+        uint8_t imm8;
+        uint16_t imm16;
+        uint32_t imm32;
+        uint64_t imm64;
+    } imm;
+    union {
+        uint8_t disp8;
+        uint16_t disp16;
+        uint32_t disp32;
+    } disp;
+    uint32_t flags;
+} hde64s;
+
+#pragma pack(pop)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* __cdecl */
+unsigned int hde64_disasm(const void *code, hde64s *hs);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HDE64_H_ */
diff --git a/VirtualApp/lib/src/main/jni/Substrate/table64.h b/VirtualApp/lib/src/main/jni/Substrate/table64.h
new file mode 100755
index 000000000..144f29076
--- /dev/null
+++ b/VirtualApp/lib/src/main/jni/Substrate/table64.h
@@ -0,0 +1,74 @@
+/*
+ * Hacker Disassembler Engine 64 C
+ * Copyright (c) 2008-2009, Vyacheslav Patkov.
+ * All rights reserved.
+ *
+ */
+
+#define C_NONE    0x00
+#define C_MODRM   0x01
+#define C_IMM8    0x02
+#define C_IMM16   0x04
+#define C_IMM_P66 0x10
+#define C_REL8    0x20
+#define C_REL32   0x40
+#define C_GROUP   0x80
+#define C_ERROR   0xff
+
+#define PRE_ANY  0x00
+#define PRE_NONE 0x01
+#define PRE_F2   0x02
+#define PRE_F3   0x04
+#define PRE_66   0x08
+#define PRE_67   0x10
+#define PRE_LOCK 0x20
+#define PRE_SEG  0x40
+#define PRE_ALL  0xff
+
+#define DELTA_OPCODES      0x4a
+#define DELTA_FPU_REG      0xfd
+#define DELTA_FPU_MODRM    0x104
+#define DELTA_PREFIXES     0x13c
+#define DELTA_OP_LOCK_OK   0x1ae
+#define DELTA_OP2_LOCK_OK  0x1c6
+#define DELTA_OP_ONLY_MEM  0x1d8
+#define DELTA_OP2_ONLY_MEM 0x1e7
+
+unsigned char hde64_table[] = {
+  0xa5,0xaa,0xa5,0xb8,0xa5,0xaa,0xa5,0xaa,0xa5,0xb8,0xa5,0xb8,0xa5,0xb8,0xa5,
+  0xb8,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xac,0xc0,0xcc,0xc0,0xa1,0xa1,
+  0xa1,0xa1,0xb1,0xa5,0xa5,0xa6,0xc0,0xc0,0xd7,0xda,0xe0,0xc0,0xe4,0xc0,0xea,
+  0xea,0xe0,0xe0,0x98,0xc8,0xee,0xf1,0xa5,0xd3,0xa5,0xa5,0xa1,0xea,0x9e,0xc0,
+  0xc0,0xc2,0xc0,0xe6,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0xab,
+  0x8b,0x90,0x64,0x5b,0x5b,0x5b,0x5b,0x5b,0x92,0x5b,0x5b,0x76,0x90,0x92,0x92,
+  0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x6a,0x73,0x90,
+  0x5b,0x52,0x52,0x52,0x52,0x5b,0x5b,0x5b,0x5b,0x77,0x7c,0x77,0x85,0x5b,0x5b,
+  0x70,0x5b,0x7a,0xaf,0x76,0x76,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,
+  0x5b,0x5b,0x86,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xd5,0x03,0xcc,0x01,0xbc,
+  0x03,0xf0,0x03,0x03,0x04,0x00,0x50,0x50,0x50,0x50,0xff,0x20,0x20,0x20,0x20,
+  0x01,0x01,0x01,0x01,0xc4,0x02,0x10,0xff,0xff,0xff,0x01,0x00,0x03,0x11,0xff,
+  0x03,0xc4,0xc6,0xc8,0x02,0x10,0x00,0xff,0xcc,0x01,0x01,0x01,0x00,0x00,0x00,
+  0x00,0x01,0x01,0x03,0x01,0xff,0xff,0xc0,0xc2,0x10,0x11,0x02,0x03,0x01,0x01,
+  0x01,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x10,
+  0x10,0x10,0x10,0x02,0x10,0x00,0x00,0xc6,0xc8,0x02,0x02,0x02,0x02,0x06,0x00,
+  0x04,0x00,0x02,0xff,0x00,0xc0,0xc2,0x01,0x01,0x03,0x03,0x03,0xca,0x40,0x00,
+  0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,0x00,0x00,0x00,
+  0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xff,0x00,
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,
+  0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00,
+  0xff,0x40,0x40,0x40,0x40,0x41,0x49,0x40,0x40,0x40,0x40,0x4c,0x42,0x40,0x40,
+  0x40,0x40,0x40,0x40,0x40,0x40,0x4f,0x44,0x53,0x40,0x40,0x40,0x44,0x57,0x43,
+  0x5c,0x40,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
+  0x40,0x40,0x64,0x66,0x6e,0x6b,0x40,0x40,0x6a,0x46,0x40,0x40,0x44,0x46,0x40,
+  0x40,0x5b,0x44,0x40,0x40,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x01,0x06,
+  0x06,0x02,0x06,0x06,0x00,0x06,0x00,0x0a,0x0a,0x00,0x00,0x00,0x02,0x07,0x07,
+  0x06,0x02,0x0d,0x06,0x06,0x06,0x0e,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04,
+  0x04,0x04,0x05,0x06,0x06,0x06,0x00,0x00,0x00,0x0e,0x00,0x00,0x08,0x00,0x10,
+  0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,0x86,0x00,
+  0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,0xf8,0xbb,
+  0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,0xc4,0xff,
+  0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,0x13,0x09,
+  0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,0xb2,0xff,
+  0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,0xe7,0x08,
+  0x00,0xf0,0x02,0x00
+};
