mirror of
https://github.com/yellows8/dsi.git
synced 2025-06-19 03:35:34 -04:00
236 lines
6.6 KiB
Python
236 lines
6.6 KiB
Python
from __future__ import with_statement
|
|
import sys
|
|
import os
|
|
import httplib, mimetypes
|
|
import urllib
|
|
import hashlib
|
|
import struct
|
|
from urlparse import urlparse
|
|
|
|
|
|
# script for getting patch blobs for dsi sav
|
|
# version v1.1
|
|
|
|
current_version=1110
|
|
host='bootmii.org'
|
|
path_base='/dsiexploits/inject/'
|
|
script_path='upload.php'
|
|
outfile=''
|
|
extra_params=''
|
|
|
|
|
|
if (len(sys.argv) < 2) or (len(sys.argv) > 3):
|
|
print 'need one or two options <inputfile> [extra_params]'
|
|
sys.exit(1)
|
|
|
|
outfilesplit = sys.argv[1].split('.')
|
|
outfile=outfilesplit[0]
|
|
for index in range(1,len(outfilesplit)-1):
|
|
outfile = '%s.%s' % (outfile, outfilesplit[index])
|
|
if (len(outfilesplit[len(outfilesplit)-1].split(os.sep)) > 1):
|
|
if (len(outfilesplit) > 1):
|
|
outfile='%s.%shax' % (outfile,outfilesplit[len(outfilesplit)-1])
|
|
else:
|
|
outfile='%shax' % outfile
|
|
else:
|
|
outfile='%shax' % outfile
|
|
if (len(outfilesplit) > 1):
|
|
outfile='%s.%s' % (outfile,outfilesplit[len(outfilesplit)-1])
|
|
|
|
|
|
if len(sys.argv) == 3:
|
|
extra_params='&%s' % sys.argv[2]
|
|
print 'extra_params=%s' % extra_params
|
|
|
|
|
|
|
|
## from http://code.activestate.com/recipes/146306/
|
|
|
|
def post_multipart(host, selector, fields, files):
|
|
"""
|
|
Post fields and files to an http host as multipart/form-data.
|
|
fields is a sequence of (name, value) elements for regular form fields.
|
|
files is a sequence of (name, filename, value) elements for data to be uploaded as files
|
|
Return the server's response page.
|
|
"""
|
|
content_type, body = encode_multipart_formdata(fields, files)
|
|
h = httplib.HTTPConnection(host)
|
|
headers = {
|
|
'User-Agent': 'dsisavpatch/1.1',
|
|
'Content-Type': content_type
|
|
}
|
|
h.request('POST', selector, body, headers)
|
|
res = h.getresponse()
|
|
return res.status, res.reason, res.getheaders(), res.read()
|
|
|
|
def encode_multipart_formdata(fields, files):
|
|
"""
|
|
fields is a sequence of (name, value) elements for regular form fields.
|
|
files is a sequence of (name, filename, value) elements for data to be uploaded as files
|
|
Return (content_type, body) ready for httplib.HTTP instance
|
|
"""
|
|
BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$'
|
|
CRLF = '\r\n'
|
|
L = []
|
|
for (key, value) in fields:
|
|
L.append('--' + BOUNDARY)
|
|
L.append('Content-Disposition: form-data; name="%s"' % key)
|
|
L.append('')
|
|
L.append(value)
|
|
for (key, filename, value) in files:
|
|
L.append('--' + BOUNDARY)
|
|
L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
|
|
L.append('Content-Type: %s' % get_content_type(filename))
|
|
L.append('')
|
|
L.append(value)
|
|
L.append('--' + BOUNDARY + '--')
|
|
L.append('')
|
|
body = CRLF.join(L)
|
|
content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
|
|
return content_type, body
|
|
|
|
def get_content_type(filename):
|
|
return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
|
|
## end of from http://code.activestate.com/recipes/146306/
|
|
|
|
|
|
|
|
with open(sys.argv[1], 'rb') as f:
|
|
contents = f.read()
|
|
|
|
footer = contents[0x40f4:0x4554]
|
|
|
|
print 'opening http://%s%s%s' %(host, path_base, script_path)
|
|
print 'footerhash: %s' % (hashlib.sha1(footer).hexdigest(),)
|
|
|
|
status, reason, headers, response = post_multipart(host, '%s%s?footerhash=%s%s' % (path_base, script_path, hashlib.sha1(footer).hexdigest(), extra_params), [], [('footer', 'footer.bin', footer)])
|
|
|
|
if status != 200 and status != 302:
|
|
print 'error getting: %s' % script_path
|
|
print status, reason
|
|
|
|
location='';
|
|
for a in headers:
|
|
if a[0] == 'location':
|
|
location = a[1]
|
|
if a[0] == 'x-protocolver':
|
|
server_version = int(a[1])
|
|
if server_version > current_version:
|
|
print 'The client software was updated and this update is required, now aborting.'
|
|
sys.exit(1)
|
|
else:
|
|
print 'ProtocolVer %d is good!' % server_version
|
|
|
|
if location=='':
|
|
print 'did not get location redirect! %d as status' % status
|
|
print response
|
|
sys.exit(0)
|
|
|
|
|
|
if location[0:5] != 'http:':
|
|
print 'not a http redirect, bad luck'
|
|
sys.exit(0)
|
|
|
|
|
|
url = urlparse(location)
|
|
redirect_params = dict([part.split('=') for part in url[4].split('&')])
|
|
redirect_host = url[1]
|
|
redirect_path = url[2]
|
|
params = urllib.urlencode(redirect_params)
|
|
headers = {"Content-type": "application/x-www-form-urlencoded", "User-Agent": "dsisavpatch/1.1"}
|
|
attempts = 20
|
|
payload = ''
|
|
while attempts > 0:
|
|
print 'trying %s with hash as %s'%(redirect_path, redirect_params['hash'])
|
|
h = httplib.HTTPConnection(redirect_host)
|
|
h.request('POST', redirect_path, params, headers)
|
|
res = h.getresponse()
|
|
if res.status == 200:
|
|
payload = res.read()
|
|
# print 'got %d, and length %d' %(res.status, len(payload))
|
|
if payload[0:5] == 'ERROR':
|
|
print 'error: %s' % payload[5:]
|
|
attempts = -1
|
|
elif payload[0:7] == 'INFOMSG':
|
|
print 'info: %s' % payload[7:]
|
|
else:
|
|
break
|
|
attempts = attempts - 1
|
|
else:
|
|
print 'bad times, we got %d reply' % res.status
|
|
payload = res.read()
|
|
# print 'got %d, and length %d' %(res.status, len(payload))
|
|
if payload[0:5] == 'ERROR':
|
|
print 'error: %s' % payload[5:]
|
|
sys.exit(0)
|
|
|
|
if attempts == 0:
|
|
print 'too many attempts, not sure what happened!'
|
|
sys.exit(0)
|
|
|
|
# process payload
|
|
|
|
with open('debug.out', 'wb') as f:
|
|
f.write(payload)
|
|
|
|
if server_version < 1110:
|
|
start_of_field=0
|
|
end_of_field=payload.find('\0', start_of_field)
|
|
if end_of_field == -1:
|
|
print 'whoops, end of field fail!'
|
|
sys.exit(0)
|
|
|
|
notes_size = int(payload[start_of_field:end_of_field], 16)
|
|
start_of_field = end_of_field+1
|
|
end_of_field=payload.find('\0', start_of_field)
|
|
if end_of_field == -1:
|
|
print 'whoops, end of field fail!'
|
|
sys.exit(0)
|
|
num_objs = int(payload[start_of_field:end_of_field], 16)
|
|
else:
|
|
(notes_size,num_objs) = struct.unpack("<2L", payload[0:8])
|
|
end_of_field = 7
|
|
print 'noteslen: %d' % notes_size
|
|
print 'found %d objs' % num_objs
|
|
|
|
patch_blobs = []
|
|
for i in range(0,num_objs):
|
|
start_of_field = end_of_field+1
|
|
if server_version < 1110:
|
|
end_of_field = payload.find('\0', start_of_field)
|
|
if end_of_field == -1:
|
|
print 'whoops, end of field fail!'
|
|
sys.exit(0)
|
|
offset = int(payload[start_of_field:end_of_field], 16)
|
|
|
|
start_of_field = end_of_field+1
|
|
end_of_field = payload.find('\0', start_of_field)
|
|
if end_of_field == -1:
|
|
print 'whoops, end of field fail!'
|
|
sys.exit(0)
|
|
size = int(payload[start_of_field:end_of_field], 16)
|
|
else:
|
|
(offset,size) = struct.unpack("<2L", payload[start_of_field:start_of_field+8])
|
|
end_of_field = start_of_field+7
|
|
patch_blobs.append((offset,size))
|
|
print 'queued patch_blob'
|
|
|
|
start_of_field = end_of_field + 1
|
|
|
|
notes = payload[start_of_field:start_of_field+notes_size]
|
|
start_of_field = start_of_field + notes_size
|
|
|
|
for (offset,size) in patch_blobs:
|
|
contents = contents[0:offset] + payload[start_of_field:start_of_field+size] + contents[offset+size:]
|
|
print 'applied patch_blob!'
|
|
start_of_field = start_of_field + size
|
|
|
|
|
|
with open(outfile, 'wb') as f:
|
|
f.write(contents)
|
|
|
|
print 'notes: \n%s' % notes
|
|
print 'done!'
|
|
|
|
|