Minor version bump.

Further improvements to overall system stability and other minor adjustments have been made to enhance the user experience.
This commit is contained in:
Cody Brocious 2017-08-17 21:27:48 -06:00
parent 9591e6978e
commit 72f548463d
30 changed files with 5755 additions and 31994 deletions

5
LICENSE.txt Normal file
View file

@ -0,0 +1,5 @@
Copyright 2017 Reswitched Team
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

97
README.md Normal file
View file

@ -0,0 +1,97 @@
Cage The Unicorn
================
CTU is a debugging emulator for the Nintendo Switch. That means that it **does not and will not** play games. In fact, it has no support for graphics, sound, input, or any kind of even remotely performant processing. This is all by design.
With CTU, you can run entire Switch sysmodules or applications, trace and debug the code, test exploits, fuzz, and more.
Installation
============
Install [Unicorn from git](https://github.com/unicorn-engine/unicorn) and make sure that you have Python 2.7.x installed.
Create a directory called `SwitchFS/archives` and copy all the system archive `.bin` files into that, if a sysmodule needs them to run.
Usage
=====
The simplest case is running a sysmodule. To set this up, run:
./addtitle.sh target source
Where `target` is the name of the directory (we generally recommend you include the sysmodule name and system version, e.g. `pctl30`) to create for this, and `source` is the directory containing the binaries.
The target directory will be created along with a `run.py` and `load.yaml` file, and the source binaries will be copied in. To run it, simply run `python target/run.py`
You can see an empty skeleton in the `skeletonSample` directory.
`load.yaml`
===========
The `load.yaml` file defines what all should be loaded into a process. It is a dictionary with the following keys allowable:
- `nxo`/`nro`/`nso` -- Single filename (string) or array of filenames to load. Don't include a file extension.
- `mod` -- Single filename (string) to load.
- `bundle` -- Filename of a memory bundle to load. If `filename.gz` exists and `filename` does not, CTU will automatically decompress the file on first load.
- `maps` -- Dictionary of address class -> map files.
- Each entry should be the `Titlecase` version of the binary the map is for, e.g. `main` becomes `Main`. The value of the entry takes the form `[0xf00b, "something.map"]`, where the address is the loadbase of the map.
API
===
This is the core API off the `CTU` object, usable from `run.py`.
- `call(pc, [X0, [X1, ...]], _start=False)` -- Call a given native function on a newly created thread. Any number of arguments can be passed; return value is X0 on exit. If `_start` is True, X0 is 0 and X1 is the handle of the newly created thread.
- `debugbreak()` -- Breaks into debugger.
- `malloc(size)` -- Allocates `size` bytes inside the guest address space.
- `free(addr)` -- No-op!
- `reg(i, [val])` -- Reads or assigns a register by number or name (`X0`-`X31`, `LR`, and `SP`).
- `pc` -- Property allowing you to read/write the PC value for the current thread.
- `dumpregs()` -- Display all registers.
- `dumpmem(addr, size, check=False)` -- Hexdump a block of memory. If `check` is True and memcheck is enabled, you'll be warned for unmapped memory reads.
- `readmem(addr, size)` and `writemem(addr, value)` -- Reads or writes memory as a byte string.
- `read8/16/32/64(addr)` and `write8/16/32/64(addr, value)` -- Reads or writes memory as an unsigned integer of a given size.
- `readS8/16/32/64(addr)` -- Reads memory as a signed integer of a given size.
- `readstring(addr)` -- Reads a string until null terminator or unmapped memory.
- `newHandle(obj)` -- Assign a new handle to `obj` and returns that handle ID.
- `closeHandle(handle)` -- Closes a handle.
- `map(base, size)` -- Maps a block of memory. If not page aligned, that will happen automatically. Memory will be unmapped if execution restarts.
- `unmap(base, size)` -- Unmaps a block of memory.
- `getmap(addr)` -- Returns the tuple `base, size` for a given address, or `-1, -1` if that memory is unmapped.
- `memregions()` -- Returns a generator providing tuples of `(begin, end, perms)` for all mapped and unmapped regions; unmapped regions have `perms == -1`.
- `hookinsn(insn)` -- Decorator allowing you to hook a given instruction value (as an integer). Decorated function should have arguments `ctu, addr` where addr is the address of the instruction. Hook happens before execution.
- `hookfetch(addr)` -- Decorator allowing you to hook a given instruction address prior to execution. Decorated function takes no arguments.
- `hookread(addr)` and `hookwrite(addr)` -- Decorators allowing you to hook memory read/writes to an address. Decorator should have arguments `ctu, size` or `ctu, addr, size, value` respectively. Read hooks may return a replacement value. If writehook returns non-`None`/`False`, it is deleted.
- `replaceFunction(addr)` -- Decorator allowing you to replace a function in native code at the given address. The decorated function gets `ctu` as its first argument, but may take any number of arguments beyond that (mapped from X0...X30 automatically) and return any number of values (mapped to X0...X30 automatically). The original function will not execute.
IPC Client
==========
Sysmodules expose their IPC interface over the network. The details of that wire protocol are documented in `wireprotocol.txt`, but a Python client is included.
Example usage is in `skeletonSample/client.py`. You create a `Client` object and connect to a CTU instance, then create and send `IPCMessage`s back and forth.
Debugger Reference
==================
The debugger in CTU is roughly based on gdb but has some key differences that will really irritate GDB fans.
- `exit` -- Exit.
- `s/start` -- Start or restart execution.
- `t/trace (i/instruction | b/block | f/function | m/memory)` -- Toggles tracing.
- `mc/memcheck` -- Toggles memory access violation tracking.
- `b/break [name]` -- Without `name`, list breakpoints; otherwise toggle breakpoint for symbol name or address.
- `bt` -- Print the call stack.
- `sym <name>` -- Print the address of a given symbol.
- `c/continue` -- Continues execution.
- `n/next` -- Single step.
- `r/reg/regs [reg [value]]`
- No parameters: Display all registers.
- Reg parameter: Display one register.
- Reg and value: Assign a value (always hex, or a symbol name) to a register.
- `x/exec <code>` -- Evaluates a given line of C.
- `dump <address> [size=0x100]` -- Dumps `size` bytes of memory at an address. If address takes the form `*register` (e.g. `*X1`) then the value of that register will be used.
- `save <address> <size> <fn>` -- Writes `size` bytes of memory to a file. If address or size take the form `*register` (e.g. `*X1`) then the value of that register will be used.
- `ad` -- Toggle address display specialization
- `w/watch [expression]` -- Breaks when expression evaluates to true. Without an expression, list existing watchpoints.
- `mr/memregions` -- Display mapped memory regions

42
addtitle.sh Normal file
View file

@ -0,0 +1,42 @@
#!/bin/bash
TGT="$1"
SRC="$2"
LOAD="$TGT/load.yaml"
mkdir "$TGT"
echo 'nso:' > "$LOAD"
for fn in "$SRC/"*; do
if [ -f "$fn" ] && [[ ${fn} != *".npdm" ]]; then
BARE=$(basename $fn)
cp "$fn" "$TGT/"
echo " - $BARE" >> "$LOAD"
fi
done
echo 'maps:' >> "$LOAD"
for fn in "$SRC/"*; do
if [ -f "$fn" ] && [[ ${fn} != *".npdm" ]]; then
BARE=$(basename $fn)
echo " $(tr '[:lower:]' '[:upper:]' <<< ${BARE:0:1})${BARE:1}: [0x7100000000, \"$BARE.map\"]" >> "$LOAD"
fi
done
cat << EOH > "$TGT/run.py"
import sys
sys.path.append('.')
from ctu import *
#@run#(TRACE_FUNCTION)
@debug(TRACE_MEMCHECK)
def main(ctu):
ctu.load('$TGT')
#@ctu.replaceFunction(MainAddress(unknown))
def memset(ctu, addr, val, size):
ctu.writemem(addr, chr(val) * size, check=False)
ctu.call(MainAddress(0x0), _start=True)
EOH

View file

@ -1,22 +1,23 @@
import struct
import struct, sys
from glob import glob
mainaddr = raw_input('Enter main module address: ')
if mainaddr.startswith('0x'):
mainaddr = mainaddr[2:]
mainaddr = int(mainaddr, 16)
print 'Main at 0x%016x' % mainaddr
wkcaddr = raw_input('Enter wkc module address: ')
if wkcaddr.startswith('0x'):
wkcaddr = wkcaddr[2:]
wkcaddr = int(wkcaddr, 16)
print 'WKC at 0x%016x' % wkcaddr
def main(dumpdir, mainaddr, wkcaddr):
mainaddr = int(mainaddr.replace('0x', ''), 16)
wkcaddr = int(wkcaddr.replace('0x', ''), 16)
with file('membundle.bin', 'wb') as fp:
files = glob('memdumps/*.bin')
fp.write(struct.pack('<IQQ', len(files), mainaddr, wkcaddr))
for fn in files:
addr = int(fn[11:].split(' ')[0], 16)
data = file(fn, 'rb').read()
fp.write(struct.pack('<QI', addr, len(data)))
fp.write(data)
with file('membundle.bin', 'wb') as fp:
files = glob('%s/*.bin' % dumpdir)
fp.write(struct.pack('<IQQ', len(files), mainaddr, wkcaddr))
for fn in files:
addr = int(fn[11:].rsplit('/', 1)[-1].split(' ', 1)[0], 16)
end = int(fn[11:].rsplit('/', 1)[-1].split(' - ')[1], 16)
data = file(fn, 'rb').read()
print '%x size %x -- real %x' % (addr, end - addr, len(data))
if end - addr != len(data):
print 'MISMATCHED SIZE! CORRUPT DUMP'
raw_input()
fp.write(struct.pack('<QI', addr, len(data)))
fp.write(data)
if __name__=='__main__':
main(*sys.argv[1:])

10
color.sh Normal file
View file

@ -0,0 +1,10 @@
#!/bin/bash
echo '#include <idc.idc>' > colorme.idc
echo 'static main(void) {' >> 'colorme.idc'
for x in `cat log.txt | grep 'Block at' | cut -f 4 -d ' ' | sort | uniq | grep '0x'`; do
echo "SetColor($x, CIC_ITEM, 0x00ff00);" >> colorme.idc;
done
for x in `cat log.txt | sed 's/^.*\[0:\(0x.*\)\].*$/\1/' | grep -v ' ' | cut -f 6 -d ' ' | sort | uniq | grep '0x'`; do
echo "SetColor($x, CIC_ITEM, 0xff00ff);" >> colorme.idc;
done
echo '}' >> colorme.idc

858
ctu.py

File diff suppressed because it is too large Load diff

44
inlines.py Normal file
View file

@ -0,0 +1,44 @@
from util import *
magicBase = 1 << 62
functions = {}
reverse = {}
def register(func, override=False):
global magicBase
def sub(ctu):
args = (ctu.reg(i) for i in xrange(func.__code__.co_argcount - 1))
ret = func(ctu, *args)
if ret is None:
return
elif isinstance(ret, tuple):
for i, v in enumerate(ret):
ctu.reg(i, native(v))
else:
ctu.reg(0, native(ret))
functions[func.func_name] = sub, magicBase + 8 * len(reverse), override
reverse[magicBase + 8 * len(reverse)] = sub
return sub
def registerOverride(func):
return register(func, override=True)
@register
def malloc(ctu, size):
return ctu.malloc(size)
@register
def free(ctu, ptr):
return ctu.free(ptr)
@register
def calloc(ctu, num, size):
ptr = ctu.malloc(num * size)
ctu.writemem(ptr, '\0' * (num * size))
return ptr
@registerOverride
def _ZN2nn4diag6detail9AbortImplEPKcS3_S3_iPKNS_6ResultES3_z(ctu, x0, x1, x2, x3, x4, x5):
print '!!!Abort!!!'
print formatString(ctu, ctu.readstring(x5), 6)
ctu.debugbreak()

707
ipc.py Normal file
View file

@ -0,0 +1,707 @@
import os, os.path, shutil, struct, yaml
import ipcbridge
from util import *
from ipcbase import *
from ipcstubs import *
class IPC(object):
def __init__(self, ctu):
self.ctu = ctu
def connectToPort(self, name):
if name == 'sm:':
return self.ctu.newHandle(SmService(self))
else:
print 'Attempt to connect to unknown port: %r' % name
self.ctu.debugbreak()
def recv(self, handle, buffer):
if handle not in self.ctu.handles or not hasattr(self.ctu.handles[handle], 'dispatch'):
print 'BAD HANDLE FOR IPC! %x -- %r' % (handle, self.ctu.handles[handle] if handle in self.ctu.handles else None)
self.ctu.debugbreak()
return 0, IPCMessage(0).setType(0).pack()
obj = self.ctu.handles[handle]
return obj.dispatch(handle, buffer)
@register('sm:')
class SmService(IPCService):
def __init__(self, *args, **kwargs):
IPCService.__init__(self, *args, **kwargs)
self.initialized = False
@command(0)
def Initialize(self, message):
print 'Initialize SM!', message
if self.initialized:
print 'Already initialized'
self.initialized = True
@command(1)
def GetService(self, message):
name = struct.pack('<Q', message.dataBuffer[0]).rstrip('\0')
if name in services:
print 'Successfully got service:', `name`
return IPCMessage().moveHandle(services[name](self.ipc))
else:
print 'Attempted to get unknown service:', `name`
self.ipc.ctu.debugbreak()
@command(2)
def RegisterService(self, message):
name, maxSessions = struct.pack('<Q', message.dataBuffer[0]).rstrip('\0'), message.dataBuffer[1]
print 'Register service!', name, message
svcport = Port(name)
ipcbridge.register(name, svcport)
return IPCMessage().moveHandle(svcport)
@command(3)
def UnregisterService(self, message):
pass
@partial(ILibraryAppletSelfAccessor)
class ImplILibraryAppletSelfAccessor:
GetLibraryAppletInfo = stub().data(0x13)
@partial(IStorageAccessor)
class ImplIStorageAccessor:
def __setup__(self, name):
self.name = name
def GetSize(self, message):
print 'Getting IStorage length for', self.name
if self.name == 'nn::am::service::IProcessWindingController::PopContext':
size = 0x38
else:
print '~~Unknown IStorage size! %s ~~' % self.name
size = 0x2000
return IPCMessage(0).data(size)
def Read(self, message):
addr, size, _ = message.bDescriptors[0]
if size == 0:
return
self.ctu.write16(addr + 0, 0)
self.ctu.write32(addr + 4, 5)
@partial(IStorage)
class ImplIStorage:
def __setup__(self, name):
self.name = name
def Open(self, message):
return IPCMessage(0).moveHandle(IStorageAccessor(self.ipc, self.name))
@partial(ILibraryAppletAccessor)
class ImplILibraryAppletAccessor:
GetAppletStateChangedEvent = stub(0).copyHandle(InstantWaitable)
@partial(IApplicationDisplayService)
class ImplIApplicationDisplayService:
def OpenLayer(self, message):
addr = message.bDescriptors[0][0]
self.ctu.write32(addr + 0x00, 0x30)
self.ctu.write32(addr + 0x04, 0x80)
self.ctu.write32(addr + 0x08, 1 << 2)
self.ctu.write32(addr + 0x0c, 0)
self.ctu.write64(addr + 0x10, 0x40)
self.ctu.write64(addr + 0x90, 0x20)
self.ctu.write32(addr + 0xb0, 4)
return IPCMessage(0).data(0xec069d45)
@partial(ICommonStateGetter)
class ImplICommonStateGetter:
GetEventHandle = stub().copyHandle(Waitable)
@register('arp:r')
class ArpRService(IPCService):
unk0 = stub(0)
unk2 = stub(2)
@register('arp:w')
class ArpWService(IPCService):
class ArpW0Service(IPCService):
unk1 = stub(1)
unk0 = stub(0).moveHandle(ArpW0Service)
@register('bgtc:t')
class BgtcTService(IPCService):
unk2 = stub(2)
@command(3)
def Unknown3(self, message):
print 'bgtc:t(3) unknown', message
return IPCMessage().copyHandle(Waitable())
@command(5)
def Unknown5(self, message):
print 'bgtc:t(5) unknown', message
self.ctu.dumpmem(message.xDescriptors[0][0], message.xDescriptors[0][1])
@command(14)
def Unknown14(self, message):
print 'bgtc:t(14) unknown', message
return IPCMessage().copyHandle(Waitable())
class BgtcT14Service(IPCService):
pass
@register('bsd:s')
class BsdSService(IPCService):
Init = stub(0)
unk1 = stub(1)
@register('btm')
class BtmService(IPCService):
unk2 = stub(2)
@register('caps:a')
class CapsAService(IPCService):
pass
@register('caps:c')
class CapsCService(IPCService):
pass
@register('caps:ss')
class CapsSsService(IPCService):
pass
@register('es')
class EsService(IPCService):
pass
@register('fatal:u')
class FatalUService(IPCService):
@command(2)
def FatalError(self, message):
print '!!! FATAL ERROR: 0x%X !!!' % ( (message.data[0] >> 9) & 0xFFF )
stackSize = self.ipc.ctu.read64(message.aDescriptors[0][0] + 0x240)
print 'Stack trace'
print '-'*28
for i in xrange(0, stackSize):
print '\t[%d] %016x'%(i, self.ipc.ctu.read64(message.aDescriptors[0][0] + 0x130 + (i * 8)))
print '-'*28
self.ipc.ctu.dumpregs()
self.ipc.ctu.debugbreak()
return IPCMessage().data(message.data[0])
@register('fsp-pr')
class FspPrService(IPCService):
pass
@register('fsp-srv')
class FspSrvService(IPCService):
Init = stub(1)
@command(18)
def MountSdCard(self, message):
return IPCMessage(0).moveHandle(IFileSystem(self.ipc, 'sdcard'))
@command(23)
def CreateSystemSaveData(self, message):
id = message.data[3]
dir = 'syssave_%x' % id
print 'CreateSystemSaveData 0x%016x' % id
IFileSystem(self.ipc, dir)
return IPCMessage(0).data(0)
@command(52)
def MountSystemSaveData(self, message):
id = message.data[4]
dir = 'syssave_%x' % id
print 'MountSystemSaveData 0x%016x' % id
if id in (0x8000000000000047, ) and not os.path.exists('SwitchFS/' + dir):
return IPCMessage(0x7d402)
return IPCMessage(0).data(0, 0).moveHandle(IFileSystem(self.ipc, dir))
@command(200)
def OpenHost(self, message):
print 'OpenHost'
return IPCMessage(0).moveHandle(FSIStorage(self.ipc, 'host'))
@command(202)
def OpenDataStorageByDataId(self, message):
print 'OpenDataStorageByDataId', message
fn = 'archives/%016X.bin' % message.data[1]
return IPCMessage(0).moveHandle(FSIStorage(self.ipc, fn))
@command(400)
def OpenDeviceOperator(self, message):
return IPCMessage(0).moveHandle(DeviceOperator(self.ipc))
@command(500)
def OpenSdCardDetectionEventNotifier(self, message):
return IPCMessage(0).moveHandle(IEventNotifier(self.ipc))
@command(501)
def OpenGameCardDetectionEventNotifier(self, message):
return IPCMessage(0).moveHandle(IEventNotifier(self.ipc))
unk620 = stub(620)
DisableAutoSaveDataCreation = stub(1003)
class IEventNotifier(IPCService):
@command(0)
def BindEvent(self, message):
print 'IEventNotifier:BindEvent', message
return IPCMessage(0).copyHandle(Waitable())
class FSIStorage(IPCService):
def __init__(self, ipc, path):
IPCService.__init__(self, ipc)
self.path = 'SwitchFS/' + path
self.fp = file(self.path, 'rb')
def __repr__(self):
return 'FSIStorage(%r)' % self.path
@command(0)
def Read(self, message):
print 'FSIStorage:Read', message
offset, length = message.data[0:2]
addr, size, _ = message.bDescriptors[0]
assert length == size
print 'Reading from %x-%x into %x' % (offset, offset + length, addr)
self.fp.seek(offset, 0)
self.ctu.writemem(addr, self.fp.read(size))
return IPCMessage(0).data(size)
class IFileSystem(IPCService):
def __init__(self, ipc, path):
IPCService.__init__(self, ipc)
self.path = 'SwitchFS/' + path
if not os.path.exists(self.path):
os.makedirs(self.path)
def __repr__(self):
return 'IFileSystem(%r)' % self.path
@command(0)
def CreateFile(self, message):
print 'IFileSystem:CreateFile', message
try:
path = self.ctu.readstring(message.xDescriptors[0][0])
if path:
rpath = self.path + '/' + path
print rpath
file(rpath, 'wb').close()
else:
print 'Empty filename?'
except:
print 'WTF?!?!?!'
@command(1)
def DeleteFile(self, message):
print 'IFileSystem:DeleteFile', message
path = self.ctu.readstring(message.xDescriptors[0][0])
rpath = self.path + '/' + path
print rpath
@command(2)
def CreateDirectory(self, message):
print 'IFileSystem:CreateDirectory', message
path = self.ctu.readstring(message.xDescriptors[0][0])
rpath = self.path + '/' + path
print rpath
if not os.path.exists(rpath):
os.makedirs(rpath)
@command(4)
def DeleteDirectoryRecursively(self, message):
print 'IFileSystem:DeleteDirectoryRecursively', message
path = self.ctu.readstring(message.xDescriptors[0][0])
rpath = self.path + '/' + path
print rpath
if os.path.exists(rpath):
shutil.rmtree(rpath, ignore_errors=True)
@command(7)
def GetEntryType(self, message):
print 'IFileSystem:GetEntryType', message
path = self.ctu.readstring(message.xDescriptors[0][0])
rpath = self.path + '/' + path
print rpath
if os.path.exists(rpath):
return IPCMessage(0).data(0 if os.path.isdir(rpath) else 1)
else:
return IPCMessage(0x7d402)
@command(8)
def OpenFile(self, message):
print 'IFileSystem:OpenFile', message
path = self.ctu.readstring(message.xDescriptors[0][0])
rpath = self.path + '/' + path
mode = message.data[0]
print rpath
if os.path.exists(rpath) or mode & 2:
return IPCMessage(0).moveHandle(IFile(self.ipc, rpath, ['', 'rb', 'wb+', 'wb+', 'ab+', 'ab+', 'ab+', 'ab+'][mode]))
elif path == '/systemseed.dat':
file(rpath, 'wb').close()
return IPCMessage().moveHandle(IFile(self.ipc, rpath, ['', 'rb', 'wb+', 'wb+', 'ab+', 'ab+', 'ab+', 'ab+'][mode]))
else:
return IPCMessage(0x7d402)
@command(10)
def Commit(self, message):
print 'IFileSystem:Commit', message
class IFile(IPCService):
def __init__(self, ipc, path, mode):
IPCService.__init__(self, ipc)
self.path = path
self.mode = mode
if mode.endswith('+'):
if not os.path.exists(path):
file(path, 'w').close()
self.fp = file(path, mode)
def __repr__(self):
return 'IFile(%r, %r)' % (self.path, self.mode)
def close(self):
self.fp.close()
@command(0)
def Read(self, message):
addr, size, _ = message.bDescriptors[0]
print 'Reading 0x%x bytes from %s' % (size, self.path)
self.ctu.writemem(addr, self.fp.read(size))
return IPCMessage(0).data(size)
@command(1)
def Write(self, message):
addr, size, _ = message.aDescriptors[0]
print 'Writing 0x%x bytes to %s' % (size, self.path)
self.fp.write(self.ctu.readmem(addr, size))
return IPCMessage(0).data(size)
@command(2)
def Flush(self, message):
self.fp.flush()
@command(3)
def SetSize(self, message):
cur = self.fp.tell()
size = message.data[0]
print 'Setting size %x for %s with mode %r' % (size, self.path, self.mode)
self.fp.seek(0, 2)
csize = self.fp.tell()
if csize == size:
pass
elif csize < size:
self.fp.write('\0' * (size - csize))
else:
self.fp.seek(0)
data = self.fp.read()
self.fp.close()
self.fp = file(self.path, self.mode)
self.fp.write(data)
if cur >= size:
cur = 0
self.fp.seek(cur)
@command(4)
def GetSize(self, message):
cur = self.fp.tell()
self.fp.seek(0, 2)
ret = IPCMessage(0).data(self.fp.tell())
self.fp.seek(cur)
return ret
class DeviceOperator(IPCService):
@command(0)
def IsSdCardInserted(self, message):
return IPCMessage(0).data(0)
@command(200)
def IsGameCardInserted(self, message):
return IPCMessage(0).data(0)
@register('gpio')
class GpioService(IPCService):
@command(1)
def getPadSessionSubService(self, message):
return IPCMessage(0).moveHandle(PadSession(self.ipc))
class PadSession(IPCService):
unk0 = stub(0)
unk8 = stub(8)
unk9 = stub(0)
@register('ldr:ro')
class LdrRoService(IPCService):
Initialize = stub(4)
@register('ldr:shel')
class LdrShelService(IPCService):
pass
@register('lm')
class LmService(IPCService):
@command(0)
def Initialize(self, message):
print 'Initialize lm'
return IPCMessage(0).moveHandle(ILogger(self.ipc))
class ILogger(IPCService):
@command(0)
def unk0(self, message):
print self.ctu.readmem(message.xDescriptors[0][0], message.xDescriptors[0][1])
return IPCMessage(0)
@register('lr')
class LrService(IPCService):
@command(0)
def Unknown0(self, message):
print 'lr(0) unknown', message
return IPCMessage(0).moveHandle(Lr0Service(self.ipc))
class Lr0Service(IPCService):
@command(2)
def Unknown2(self, message):
print 'lr(0)(2) unknown', message
self.ctu.writemem(message.cDescriptors[0][0], 'omgwtfhax\0')
return IPCMessage(0)
@register('ncm')
class NcmService(IPCService):
unk2 = stub(2)
unk3 = stub(3)
@command(4)
def Unknown4(self, message):
print 'ncm(4) unknown'
return IPCMessage().moveHandle(Ncm4Service(self.ipc))
@command(5)
def Unknown5(self, message):
print 'ncm(5) unknown'
return IPCMessage().moveHandle(Ncm5Service(self.ipc))
unk9 = stub(9)
unk11 = stub(11)
class Ncm4Service(IPCService):
unk10 = stub(10)
@command(13)
def Unknown13(self, message):
return IPCMessage(0).data(0, 0)
class Ncm5Service(IPCService):
@command(5)
def Unknown5(self, message):
return IPCMessage(0).data(0, 0)
unk7 = stub(7)
unk8 = stub(8)
unk15 = stub(15)
@register('nim')
class NimService(IPCService):
class Nim12Service(IPCService):
unk2 = stub(2)
unk2 = stub(2)
unk8 = stub(8)
unk12 = stub(12).copyHandle(Waitable).moveHandle(Nim12Service)
unk40 = stub(40)
@register('npns:s')
class NpnsSService(IPCService):
# nn::ssl::sf::ISslService::SetInterfaceVersion
@command(5)
def SetInterfaceVersion(self, message):
return IPCMessage().copyHandle(Waitable())
@command(7)
def Unknown7(self, message):
return IPCMessage().copyHandle(Waitable())
@command(103)
def Unknown103(self, message):
return IPCMessage().data(0, 0).moveHandle(Waitable())
@register('ns:ec')
class NsEcService(IPCService):
pass
@register('ns:su')
class NsSuService(IPCService):
pass
@register('ns:vm')
class NsVmService(IPCService):
pass
@register('nsd:u')
class NsdUService(IPCService):
@command(11)
def Unknown11(self, message):
print 'nsd:u(11) unknown', message
self.ctu.writemem(message.bDescriptors[0][0], '\0' * message.bDescriptors[0][1])
@register('nvdrv')
@register('nvdrv:a')
@register('nvdrv:s')
@register('nvdrv:t')
class NvdrvService(IPCService):
@command(0)
def Open(self, message):
dev = self.ctu.readstring(message.aDescriptors[0][0])
dev_hnd = self.ctu.newHandle(dev)
print 'Attempting to open device:%s %x'%(dev, dev_hnd)
return IPCMessage(0).data(dev_hnd)
@command(1)
def Ioctl(self, message):
print 'Ioctl:', message
self.ctu.dumpmem(message.xDescriptors[0][0], message.xDescriptors[0][1])
return IPCMessage(0).data(1234)
@command(2)
def Close(self, message):
print 'Close:',message
return IPCMessage(0).data(0)
@command(3)
def Init(self, message):
print 'Init:', message
return IPCMessage(0).data(0, 0)
@command(4)
def QueryEvent(self, message):
print 'QueryEvent:', message
return IPCMessage(0).data(0, 0)
@command(5)
def MapSharedMem(self, message):
print 'MapSharedMem:', message
return IPCMessage(0).data(0, 0)
@command(8)
def BindDisplayService(self, message):
print 'BindDisplayService: ', message
return IPCMessage(0).data(0, 0)
unk13 = stub(13)
@register('ovln:snd')
class OvlnSndService(IPCService):
@command(0)
def Unknown0(self, message):
if message.data[0] == 0x79616c7265766f:
return IPCMessage().moveHandle(OvlnSndOverlay(self.ipc))
else:
self.ctu.debugbreak()
class OvlnSndOverlay(IPCService):
pass
@register('pcie')
class PCIeService(IPCService):
pass
@register('pdm:qry')
class PdmQryService(IPCService):
pass
@register('pl:u')
class PlUService(IPCService):
unk0 = stub(0).data(0, 0, 0, 0)
unk1 = stub(1).data(0, 0, 0, 0)
unk2 = stub(2).data(0, 0, 0, 0)
@register('pm:bm')
class PmBmService(IPCService):
@command(0)
def Init(self, message):
return IPCMessage().data(0, 0)
EnableMaintenanceMode = stub(1)
@register('pm:shell')
class PmShellService(IPCService):
@command(0)
def LaunchTitle(self, message):
print 'Launched title %016X' % message.data[1]
return IPCMessage().data(0x0)
@command(3)
def Unknown3(self, message):
print 'pm:shell(3) unknown'
return IPCMessage().copyHandle(Waitable())
@register('psc:m')
class PscMService(IPCService):
@command(0)
def Unknown0(self, message):
return IPCMessage().moveHandle(PscMSubService(self.ipc))
class PscMSubService(IPCService):
@command(0)
def Unknown0(self, message):
print 'psc:m#sub(0)', message
addr, size, perm = message.aDescriptors[0]
if addr != 0x0:
self.ctu.dumpmem(addr, size)
return IPCMessage(0x0).copyHandle(Waitable())
#return IPCMessage().copyHandle(Waitable())
@partial(ISystemSettingsServer)
class SetSysService:
def __setup__(self):
self.settings = yaml.load(file('systemsettings.yaml'))
def GetSettingsItemValue(self, message):
a, b = message.xDescriptors
scls, snam = self.ctu.readmem(a[0], a[1]).split('\0', 1)[0], self.ctu.readmem(b[0], b[1]).split('\0', 1)[0]
baddr, bsize, _ = message.bDescriptors[0]
setting = scls + '!' + snam
print 'Getting setting', setting
if setting in self.settings:
data = self.settings[setting]
if bsize == 1:
self.ctu.write8(baddr, data)
elif bsize == 4:
self.ctu.write32(baddr, data)
elif bsize == 8:
self.ctu.write64(baddr, data)
else:
print 'Unknown setting size', bsize
return IPCMessage().data(bsize)
else:
print 'Unknown setting'
return IPCMessage().data(0)
@register('spl:')
class SplService(IPCService):
@command(0)
def GetConfig(self, message):
configId = message.data[0]
print 'spl:GetConfig(0x%x)' % configId
if configId == 5: # HardwareType (0=Icosa, 1=Copper)
return IPCMessage(0).data(1)
else:
return IPCMessage(0).data(0)
@command(11)
def GetDevunitFlag(self, message):
return IPCMessage().data(0)
@register('wlan:lcl')
class WlanLclService(IPCService):
class WlanLcl17Service(IPCService):
pass
unk17 = stub(17).moveHandle(WlanLcl17Service)
@register('wlan:lg')
class WlanLgService(IPCService):
pass
@register('wlan:lga')
class WlanLgaService(IPCService):
pass

213
ipcbase.py Normal file
View file

@ -0,0 +1,213 @@
import types
from util import *
services = {}
def register(name):
def sub(cls):
cls.serviceName = name
services[name] = cls
return cls
return sub
def command(request):
def sub(func):
func.cmdId = request
return func
return sub
class stub(object):
def __init__(self, cmdId=None):
self.cmdId = cmdId
self._data = []
self.movedHandles = []
self.copiedHandles = []
def data(self, *data):
self._data += list(data)
return self
def moveHandle(self, cls, *args, **kwargs):
self.movedHandles.append((cls, args, kwargs))
return self
def copyHandle(self, cls, *args, **kwargs):
self.copiedHandles.append((cls, args, kwargs))
return self
def __call__(self, message):
print 'Stub %s handler for %r to %r' % (self.name, message, self.service)
msg = IPCMessage(0)
msg = msg.data(*self._data)
for cls, args, kwargs in self.movedHandles:
if isinstance(cls, type) and issubclass(cls, IPCService):
args = [self.service.ipc] + list(args)
if callable(cls):
msg = msg.moveHandle(cls(*args, **kwargs))
else:
msg = msg.moveHandle(cls)
for cls, args, kwargs in self.copiedHandles:
if isinstance(cls, type) and issubclass(cls, IPCService):
args = [self.service.ipc] + list(args)
if callable(cls):
msg = msg.copyHandle(cls(*args, **kwargs))
else:
msg = msg.copyHandle(cls)
return msg
def partial(origcls):
def sub(newcls):
for x in dir(newcls):
if x != '__setup__' and x.startswith('__'):
continue
func = getattr(newcls, x)
ofunc = getattr(origcls, x)
if isinstance(func, stub):
fsub = func
else:
fsub = func.__func__
if hasattr(func, 'cmdId'):
fsub.cmdId = func.cmdId
if hasattr(fsub, 'cmdId'):
assert fsub.cmdId is None or fsub.cmdId == ofunc.cmdId
if hasattr(ofunc, 'cmdId'):
fsub.cmdId = ofunc.cmdId
setattr(origcls, x, fsub)
return sub
class IPCService(object):
def __init__(self, ipc, *args, **kwargs):
self.ipc = ipc
self.ctu = ipc.ctu
self.commands = {}
for name in dir(self):
func = getattr(self, name)
if hasattr(func, 'cmdId'):
self.commands[func.cmdId] = func
if isinstance(func, stub):
func.service = self
func.name = name
self.__setup__(*args, **kwargs)
def __setup__(self):
pass
def dispatch(self, handle, buffer):
incoming = IPCMessage().unpack(buffer)
print incoming
for addr, size, _ in incoming.bDescriptors:
if size == 0:
continue
self.ctu.checkwrite(addr, size, unset=True, trigger=True)
self.ctu.writemem(addr, '\0' * size, check=False)
for addr, size in incoming.cDescriptors:
if size == 0:
continue
print `addr, size`
self.ctu.checkwrite(addr, size, unset=True, trigger=True)
self.ctu.writemem(addr, '\0' * size, check=False)
print self, incoming
resp = self.handle(handle, incoming)
if isinstance(resp, tuple):
ret, resp = resp
else:
ret, resp = 0, resp
if isinstance(resp, IPCMessage):
#print ', '.join('%08x' % x for x in resp.pack())
if resp.type == -1:
resp.type = 0
return ret, resp.pack()
elif isinstance(resp, int) or isinstance(resp, long) or resp is None:
return ret, IPCMessage(resp if resp is not None else 0).setType(0).pack()
else:
return ret, resp
def handle(self, handle, message):
if message.type == 5:
if (5, message.cmdId) in self.commands:
return self.commands[(5, message.cmdId)](message)
print 'Unhandled message to %s: %r' % (self.__class__.__name__, message)
self.ipc.ctu.debugbreak()
elif message.type == 4:
if message.cmdId in self.commands:
return self.commands[message.cmdId](message)
print 'Unhandled message to %s: %r' % (self.__class__.__name__, message)
self.ipc.ctu.debugbreak()
elif message.type == 2:
print 'Closing handle for', self
self.ctu.closeHandle(handle)
return 0, IPCMessage(0).setType(0).pack()
else:
print 'Unknown message type to %s: %r' % (self.__class__.__name__, message)
self.ipc.ctu.debugbreak()
@command((5, 0))
def ConvertSessionToDomain(self, message):
dd = DomainDispatcher(self.ipc)
id = dd.add(self)
self.ctu.replaceHandle(self, dd)
return IPCMessage(0).data(id)
@command((5, 2))
def DuplicateSession(self, message):
return IPCMessage(0).moveHandle(self)
@command((5, 3))
def QueryPointerBufferSize(self, message):
return IPCMessage(0).data(0x500)
@command((5, 4))
def DuplicateSessionEx(self, message):
return IPCMessage(0).moveHandle(self)
class DomainDispatcher(IPCService):
def __init__(self, ipc):
IPCService.__init__(self, ipc)
self.handles = {}
self.handleIter = 0xefff
def add(self, obj):
self.handleIter += 1
self.handles[self.handleIter] = obj
return self.handleIter
def dispatch(self, handle, buffer):
print 'Domain dispatcher got a buffer!'
incoming = IPCMessage().unpack(buffer, domain=True)
print incoming
if incoming.type == 2:
print 'Closing domain dispatcher'
self.ctu.closeHandle(handle)
return 0, IPCMessage(0).setType(0).pack()
elif incoming.type == 5:
print 'Type 5 to domain dispatch...'
return IPCService.dispatch(self, handle, buffer)
dcmd, sicount, rawsize, objid = incoming.domainParams
if dcmd == 1:
print 'Passthrough message for', self.handles[objid]
ret, buf = self.handles[objid].dispatch(None, incoming.pack() + [None] * 16)
if buf is not None:
msg = IPCMessage().unpack(buf + [None] * 16, request=False)
if len(msg.movedHandles) != 0:
repl = []
for hnd in msg.movedHandles:
repl.append(self.add(self.ctu.handles[hnd]))
del self.ctu.handles[hnd]
msg.movedHandles = []
msg.data = repl + msg.data
buf = msg.pack(domain=True)
print 'Repacked passthrough message for domain'
return ret, buf
elif dcmd == 2:
obj = self.handles[objid]
print 'Close virtual handle', obj
if hasattr(obj, 'close'):
obj.close()
del self.handles[objid]
return 0, IPCMessage(0).setType(0).pack()
else:
print 'Unhandled domain dispatch command: %x %x %x %x' % (dcmd, sicount, rawsize, objid)
self.ctu.debugbreak()

211
ipcbridge.py Normal file
View file

@ -0,0 +1,211 @@
from select import select
from socket import *
from struct import pack, unpack
from threading import Thread
import sys
from util import *
PORT = 31337
buffers = []
nullbuf = '\0' * (1024 * 1024)
def start(ctu):
Thread(target=threadstart, args=(ctu, )).start()
def threadstart(ctu):
def waitSock(sock):
while True:
rl, wl, el = select([sock], [], [], .1)
if ctu.exiting:
sys.exit()
elif len(rl):
break
server = socket(AF_INET, SOCK_STREAM)
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
server.bind(('', PORT))
server.listen(5)
while True:
waitSock(server)
sock, _ = server.accept()
while not ctu.threads.startupCompleted:
time.sleep(0.1)
for i in xrange(24):
addr = (i + 1) * (1 << 20) + (1 << 28)
ctu.map(addr, 1024 * 1024)
buffers.append(addr)
def readint(default=None):
waitSock(sock)
data = sock.recv(8)
if len(data) != 8:
return default
return unpack('<Q', data)[0]
def readdata(default=None):
waitSock(sock)
size = readint()
if size is None:
return default
odata = ''
while len(odata) != size:
data = sock.recv(size - len(odata))
if len(data) == 0:
return None
odata += data
return odata
def writeint(v):
try:
sock.send(pack('<Q', v))
except:
pass
def writedata(v):
try:
writeint(len(v))
sock.send(v)
except:
pass
openHandles = []
while True:
waitSock(sock)
cmd = readint()
if cmd is None: # Connection dropped
break
elif cmd == 0: # Open service
name = readdata()
if name is None:
break
print 'Open service', name
port = servicePorts[name]
port.acquire()
cp, sp = Pipe.new()
cp.accept()
port.push(sp)
port.release()
sp.waitForAccept()
handle = ctu.newHandle(cp)
openHandles.append(handle)
writeint(handle)
elif cmd == 1: # Close handle
handle = readint()
if handle is None:
break
print 'Close handle', handle
openHandles = [x for x in openHandles if x != handle]
ctu.closeHandle(handle)
elif cmd == 2: # IPC Message
request_type = readint()
data = [readint() for i in xrange(readint(0))]
pid = readint()
copy = [readint() for i in xrange(readint(0))]
move = [readint() for i in xrange(readint(0))]
a = [(readdata(), readint()) for i in xrange(readint(0))]
b = [(readdata(), readint()) for i in xrange(readint(0))]
c = [readdata() for i in xrange(readint(0))]
x = [(readdata(), readint()) for i in xrange(readint(0))]
handle = readint()
if handle is None:
break
msg = IPCMessage(data[0])
msg.setType(request_type)
msg.data(*data[1:])
if pid != 0xFFFFFFFFFFFFFFFF:
msg.hasPID(pid)
map(msg.copyHandle, copy)
map(msg.moveHandle, move)
bufI = 0
for data, perms in a:
if len(data) == 0:
addr = 0
else:
addr = buffers[bufI]
ctu.writemem(addr, nullbuf, check=False)
ctu.writemem(addr, data)
print 'A descriptor at %x' % addr
msg.aDescriptor(addr, len(data), perms)
bufI += 1
for data, perms in b:
if len(data) == 0:
addr = 0
else:
addr = buffers[bufI]
ctu.writemem(addr, nullbuf, check=False)
ctu.writemem(addr, data)
print 'B descriptor at %x' % addr
msg.bDescriptor(addr, len(data), perms)
bufI += 1
for data in c:
if len(data) == 0:
addr = 0xf00ba1
else:
addr = buffers[bufI]
ctu.writemem(addr, nullbuf)
ctu.writemem(addr, data)
print 'C descriptor at %x' % addr
msg.cDescriptor(addr, len(data))
bufI += 1
for data, counter in x:
if len(data) == 0:
addr = 0xf00ba3
else:
addr = buffers[bufI]
ctu.writemem(addr, nullbuf)
ctu.writemem(addr, data)
print 'X descriptor at %x' % addr
msg.xDescriptor(addr, len(data), counter)
bufI += 1
msg.request = True
data = msg.pack()
data = pack('<' + 'I' * len(data), *[(x if x is not None else 0) & 0xFFFFFFFF for x in data])
obj = ctu.handles[handle]
print 'IPC message to', obj, obj.other, msg
if obj.closed:
print 'But pipe is closed!'
writeint(0xf601)
continue
obj.push(data)
resp = obj.pop()
if resp is None:
print 'IPC port closed!'
openHandles = [x for x in openHandles if x != handle]
writeint(0xf601)
else:
writeint(0)
resp = IPCMessage().unpack(unpack('<' + 'I' * (len(resp) / 4), resp), request=False)
writeint(len(resp.dataBuffer) + 1)
map(writeint, [resp.cmdId] + resp.dataBuffer)
writeint(len(resp.copiedHandles))
map(writeint, resp.copiedHandles)
writeint(len(resp.movedHandles))
map(writeint, resp.movedHandles)
writeint(len(msg.aDescriptors))
[(writedata(ctu.readmem(addr, size)), writeint(perms)) for addr, size, perms in msg.aDescriptors]
writeint(len(msg.bDescriptors))
[(writedata(ctu.readmem(addr, size)), writeint(perms)) for addr, size, perms in msg.bDescriptors]
writeint(len(msg.cDescriptors))
[writedata(ctu.readmem(addr, size)) for addr, size in msg.cDescriptors]
writeint(len(msg.xDescriptors))
[(writedata(ctu.readmem(addr, size)), writeint(counter)) for addr, size, counter in msg.xDescriptors]
writeint(resp.type)
openHandles += resp.movedHandles
for handle in openHandles:
print 'IPC bridge closing handle %x' % handle
ctu.closeHandle(handle)
servicePorts = {}
def register(name, port):
servicePorts[name] = port

209
ipcclient.py Normal file
View file

@ -0,0 +1,209 @@
from socket import *
from struct import pack, unpack
import math, sys
def dump(data):
data = map(ord, data)
fmt = '%%0%ix |' % (int(math.log(len(data), 16)) + 1)
for i in xrange(0, len(data), 16):
print fmt % i,
ascii = ''
for j in xrange(16):
if i + j < len(data):
print '%02x' % data[i + j],
if 0x20 <= data[i+j] <= 0x7E:
ascii += chr(data[i+j])
else:
ascii += '.'
else:
print ' ',
ascii += ' '
if j == 7:
print '',
ascii += ' '
print '|', ascii
def hexify(obj, name, pname=None):
def sub(v):
if isinstance(v, list) or isinstance(v, tuple):
return '[%s]' % ', '.join(map(sub, v))
elif isinstance(v, str):
return 'buf<0x%x>' % len(v)
else:
return '0x%x' % v
pname = name if pname is None else pname
value = getattr(obj, pname)
if len(value) == 0:
return ''
return ', %s=%s' % (name, sub(value))
class IPCMessage(object):
def __init__(self, cmdId=0, client=None):
self.client = client
self.type = -1
self.cmdId = cmdId
self.request = False
self.pid = -1
self.dataBuffer = []
self.aDescriptors = []
self.bDescriptors = []
self.cDescriptors = []
self.xDescriptors = []
self.copiedHandles = []
self.movedHandles = []
def setType(self, type):
self.type = type
return self
def hasPID(self, pid=0xDEAD):
self.pid = pid
return self
def data(self, *args):
self.dataBuffer += list(args)
return self
def aDescriptor(self, data, perms):
self.aDescriptors.append((data, perms))
return self
def bDescriptor(self, data, perms):
self.bDescriptors.append((data, perms))
return self
def cDescriptor(self, data):
self.cDescriptors.append(data)
return self
def xDescriptor(self, data, counter):
self.xDescriptors.append((data, counter))
return self
def copyHandle(self, handle):
self.copiedHandles.append(handle)
return self
def moveHandle(self, handle):
self.movedHandles.append(handle)
return self
def sendTo(self, handle):
return self.client.sendMsg(handle, self)
def __repr__(self):
return '%s(%s%s%s%s%s%s%s%s%s)' % (
self.__class__.__name__,
'cmdId=%i' % self.cmdId,
', type=%i' % self.type if self.type != 0 else '',
hexify(self, 'data', 'dataBuffer'),
hexify(self, 'aDescriptors'),
hexify(self, 'bDescriptors'),
hexify(self, 'cDescriptors'),
hexify(self, 'xDescriptors'),
hexify(self, 'copiedHandles'),
hexify(self, 'movedHandles'),
)
class Client(object):
def __init__(self, host='127.0.0.1'):
self.sock = socket(AF_INET, SOCK_STREAM)
self.sock.connect((host, 31337))
self.autoHandles = {}
def getService(self, name):
if name not in self.autoHandles:
print 'Getting service', name
self.writeint(0)
self.writedata(name)
self.autoHandles[name] = self.readint()
return self.autoHandles[name]
def closeHandle(self, handle):
print 'Closing handle %x' % handle
self.writeint(1)
self.writeint(handle)
def ipcMsg(self, cmdId):
return IPCMessage(cmdId, client=self)
def sendMsg(self, nameOrHandle, msg):
if isinstance(nameOrHandle, str) or isinstance(nameOrHandle, unicode):
handle = self.getService(nameOrHandle)
name = nameOrHandle
else:
handle = nameOrHandle
name = None
self.writeint(2)
self.writeint(4 if msg.type == -1 else msg.type)
self.writeint(len(msg.dataBuffer) + 1)
map(self.writeint, [msg.cmdId] + list(msg.dataBuffer))
self.writeint(msg.pid)
self.writeint(len(msg.copiedHandles))
map(self.writeint, msg.copiedHandles)
self.writeint(len(msg.movedHandles))
map(self.writeint, msg.movedHandles)
self.writeint(len(msg.aDescriptors))
[(self.writedata(y), self.writeint(z)) for y, z in msg.aDescriptors]
self.writeint(len(msg.bDescriptors))
[(self.writedata(y), self.writeint(z)) for y, z in msg.bDescriptors]
self.writeint(len(msg.cDescriptors))
[self.writedata(y) for y in msg.cDescriptors]
self.writeint(len(msg.xDescriptors))
[(self.writedata(y), self.writeint(z)) for y, z in msg.xDescriptors]
self.writeint(handle)
error_code = self.readint()
if error_code != 0:
if error_code == 0xf601 and name is not None:
del self.autoHandles[name]
return error_code, None
data = [self.readint() for i in xrange(self.readint(0))]
copy = [self.readint() for i in xrange(self.readint(0))]
move = [self.readint() for i in xrange(self.readint(0))]
a = [(self.readdata(), self.readint()) for i in xrange(self.readint(0))]
b = [(self.readdata(), self.readint()) for i in xrange(self.readint(0))]
c = [self.readdata() for i in xrange(self.readint(0))]
x = [(self.readdata(), self.readint()) for i in xrange(self.readint(0))]
request_type = self.readint()
if request_type is None:
return None
msg = IPCMessage(data[0])
msg.setType(request_type)
msg.data(*data[1:])
map(msg.copyHandle, copy)
map(msg.moveHandle, move)
map(lambda v: msg.aDescriptor(*v), a)
map(lambda v: msg.bDescriptor(*v), b)
map(lambda v: msg.cDescriptor(v), c)
map(lambda v: msg.xDescriptor(*v), x)
msg.data = msg.dataBuffer
return 0, msg
def readint(self, default=None):
data = self.sock.recv(8)
if len(data) != 8:
return default
return unpack('<Q', data)[0]
def readdata(self, default=None):
size = self.readint()
if size is None:
return default
odata = ''
while len(odata) != size:
data = self.sock.recv(size - len(odata))
if len(data) == 0:
return None
odata += data
return odata
def writeint(self, v):
self.sock.send(pack('<Q', v & 0xFFFFFFFFFFFFFFFF))
def writedata(self, v):
self.writeint(len(v))
if isinstance(v, str) or isinstance(v, unicode):
self.sock.send(v)
else:
self.sock.send(''.join(map(chr, v)))

1548
ipcstubs.py Normal file

File diff suppressed because it is too large Load diff

12501
main.map

File diff suppressed because it is too large Load diff

28
main.py
View file

@ -1,28 +0,0 @@
from ctu import *
from util import *
from ceval import ceval
@run
@debug
def main(ctu):
fPath = ctu.malloc(36)
ctu.writemem(fPath, 'blacklist:/blacklist.txt\0')
fOption = ctu.malloc(16)
ctu.writemem(fOption, 'rb\0')
print '%016x' % ctu.call(MainAddress(0x43ddb4), fPath, fOption)
#ctu.dumpmem(WKCAddress(0x887828), 0x1000)
#ctu.call(WKCAddress(0x397B3C))
"""sbuf = ctu.malloc(32)
ctu.writemem(sbuf + 4, struct.pack('<I', 0xAABCDDEF))
obuf = ctu.malloc(0x1000)
print 'obuf at %x' % obuf
ctu.call(MainAddress(0x397c68), sbuf, obuf, 0xDEADBEEF01234567, 0xCAFEBABE)"""
"""dname = ctu.malloc(64)
ctu.writemem(dname, '/dev/nvmap')
print '%016x' % ctu.call(MainAddress(0x1a49c4), dname)"""
#print '%016x' % ctu.call(MainAddress(0x1a4b10), 0xdeadbeef, 0xcafebabe, 0x0123456789, 0xf0e0d0c0)
#print '%016x' % ctu.call(MainAddress(0x1a4ae8), 0x6b0001)

32
mergemaps.py Normal file
View file

@ -0,0 +1,32 @@
#!/usr/bin/python
from sys import argv
def get_header_len(syms):
for i in xrange(len(syms)):
if "Publics by Value" in syms[i]:
return i+1
def get_header(symsfile):
with open(symsfile, "rb") as f: mainsyms = f.read().split("\n")
return "\n".join(mainsyms[:get_header_len(mainsyms)]) + "\n\n"
def parse_syms(symsfile):
with open(symsfile, "rb") as f:
syms = f.read().split("\n")
return filter(None, syms[get_header_len(syms):])
def dedupe_syms(syms):
i = 0
while i < len(syms)-1:
if syms[i].split(" ")[1] == syms[i+1].split(" ")[1]:
print syms[i], " | ", syms[i+1]
idx = 1 if (raw_input() == "") else 0
print "==>", syms[i+(idx^1)], "\n------------------\n"
syms = syms[:idx] + syms[idx+1:]
i += 1
return syms
assert len(argv) >= 3
res = dedupe_syms(sorted(list(set(parse_syms(argv[1]))|set(parse_syms(argv[2])))))
with open("merged.map", "wb") as f: f.write(get_header(argv[1]) + "\n".join(res))

84
mmio.py Normal file
View file

@ -0,0 +1,84 @@
from util import *
mmioClasses = []
def register(base, size):
def sub(cls):
cls.physbase = base
cls.size = size
mmioClasses.append(cls)
return cls
return sub
class MmioBase(object):
def __init__(self, ctu):
self.ctu = ctu
self.stored = {}
self.setup()
def setup(self):
pass
def sread(self, addr, size):
pass
def swrite(self, addr, size, value):
return False
def read(self, addr, size):
pc = raw(self.ctu.threads.current.lastinsn)
print '[%s:%s] MMIO Read %x -- %i bytes' % (self.ctu.threadId, pc, addr, size)
val = self.sread(addr, size)
if val is None:
print 'Unknown MMIO read from %x' % addr
if addr in self.stored:
print 'Had stored value'
return self.stored[addr]
else:
return 0
else:
return val
def write(self, addr, size, value):
pc = raw(self.ctu.threads.current.lastinsn)
print '[%s:%s] MMIO Write %x -- %x -- %i bytes' % (self.ctu.threadId, pc, addr, value, size)
if self.swrite(addr, size, value) is False:
print 'Unhandled MMIO write to %x' % addr
self.stored[addr] = value
@register(0x70000000, 0x1000)
class Apb(MmioBase):
pass
@register(0x7000f800, 0x400)
class Fuses(MmioBase):
pass
@register(0x700e3000, 0x100)
class MipiCal(MmioBase):
pass
@register(0x70019000, 0x1000)
class Mc(MmioBase):
def sread(self, addr, size):
if addr == 0x70019670:
return 0xfff00000
elif addr == 0x70019674:
return 0x1000
@register(0x50000000, 0x24000)
class Host1x(MmioBase):
pass
@register(0x54200000, 0x400000)
class Display(MmioBase):
pass
@register(0x57000000, 0x1000000)
class GpuRegs(MmioBase):
pass
@register(0x58000000, 0x100000)
class GpuBar1(MmioBase):
pass

101
relocation.py Normal file
View file

@ -0,0 +1,101 @@
import inlines
(DT_NULL, DT_NEEDED, DT_PLTRELSZ, DT_PLTGOT, DT_HASH, DT_STRTAB, DT_SYMTAB, DT_RELA, DT_RELASZ,
DT_RELAENT, DT_STRSZ, DT_SYMENT, DT_INIT, DT_FINI, DT_SONAME, DT_RPATH, DT_SYMBOLIC, DT_REL,
DT_RELSZ, DT_RELENT, DT_PLTREL, DT_DEBUG, DT_TEXTREL, DT_JMPREL, DT_BIND_NOW, DT_INIT_ARRAY,
DT_FINI_ARRAY, DT_INIT_ARRAYSZ, DT_FINI_ARRAYSZ, DT_RUNPATH, DT_FLAGS) = xrange(31)
DT_GNU_HASH = 0x6ffffef5
DT_VERSYM = 0x6ffffff0
DT_RELACOUNT = 0x6ffffff9
DT_RELCOUNT = 0x6ffffffa
DT_FLAGS_1 = 0x6ffffffb
DT_VERDEF = 0x6ffffffc
DT_VERDEFNUM = 0x6ffffffd
STT_NOTYPE = 0
STT_OBJECT = 1
STT_FUNC = 2
STT_SECTION = 3
STB_LOCAL = 0
STB_GLOBAL = 1
STB_WEAK = 2
R_AARCH64_ABS64 = 257 # *reloc_addr = sym_val + addend
R_AARCH64_GLOB_DAT = 1025 # *reloc_addr = sym_val + addend
R_AARCH64_JUMP_SLOT = 1026 # *reloc_addr = sym_val + addend
R_AARCH64_RELATIVE = 1027 # *reloc_addr += base_addr + addend
def relocate(ctu, loadbase):
def do_rela(roff, size, isJumprel):
for i in xrange(0, size, 0x18):
addr = loadbase + roff + i
offset, info, addend = ctu.read64(addr), ctu.read64(addr+8), ctu.readS64(addr+16)
rtype, rsym = info & 0xFFFFFFFF, info >> 32
ea = loadbase + offset
symname, symval = symbols[rsym]
if rtype == R_AARCH64_RELATIVE:
if symname:
exports[symname] = loadbase + addend
ctu.write64(ea, loadbase + addend)
elif rtype == R_AARCH64_JUMP_SLOT or rtype == R_AARCH64_GLOB_DAT:
if symval is None:
imports[symname] = ea, 0
else:
exports[symname] = symval
ctu.write64(ea, symval)
elif rtype == R_AARCH64_ABS64:
if symval is None:
imports[symname] = ea, addend
else:
exports[symname] = symval + addend
ctu.write64(ea, symval + addend)
modoff = ctu.read32(loadbase + 4)
assert ctu.readmem(loadbase + modoff, 4) == 'MOD0'
dynoff = loadbase + modoff + ctu.read32(loadbase + modoff + 4)
dynamic = {}
while True:
tag, val = ctu.read64(dynoff), ctu.read64(dynoff + 8)
dynoff += 16
if tag == DT_NULL:
break
dynamic[tag] = val
strtabsize = dynamic[DT_STRSZ]
strtab = ctu.readmem(loadbase + dynamic[DT_STRTAB], strtabsize)
symbols = []
imports = {}
exports = {}
addr = loadbase + dynamic[DT_SYMTAB]
while True:
stName, stShndx, stValue = (
ctu.read32(addr),
ctu.read16(addr+6),
ctu.read64(addr+8),
)
addr += 24
if stName >= strtabsize:
break
name = strtab[stName:].split('\0', 1)[0]
if stShndx:
if name in inlines.functions and inlines.functions[name][2]:
exports[name] = inlines.functions[name][1]
symbols.append((name, inlines.functions[name][1]))
else:
exports[name] = loadbase + stValue
symbols.append((name, loadbase + stValue))
else:
if name in inlines.functions and inlines.functions[name][2]:
symbols.append((name, inlines.functions[name][1]))
else:
symbols.append((name, None))
if DT_RELA in dynamic:
do_rela(dynamic[DT_RELA], dynamic[DT_RELASZ], False)
if DT_JMPREL in dynamic:
do_rela(dynamic[DT_JMPREL], dynamic[DT_PLTRELSZ], True)
return imports, exports

View file

@ -1,4 +1,6 @@
unicorn>=1.0.0
capstone>=3.0.0
colorama>=0.3.7
pycparser>=2.17
unicorn>=1.0.0
capstone>=3.0.0
colorama>=0.3.7
pycparser>=2.17
PyYAML>=3.11
lz4>=0.10.0

10
skeletonSample/client.py Normal file
View file

@ -0,0 +1,10 @@
import sys
sys.path.append('.')
import struct, sys
from ipcclient import *
c = Client(*sys.argv[1:]) # Optional hostname -- otherwise defaults to localhost
svcTarget = 'pl:u'
print c.ipcMsg(0).data(0xf00, 5).copyHandle(0x012345678).moveHandle(0xCAFEBABE).aDescriptor('data here', 1).sendTo(svcTarget)

4
skeletonSample/load.yaml Normal file
View file

@ -0,0 +1,4 @@
nso:
- main
maps:
Main: [0x7100000000, "main.map"]

14
skeletonSample/run.py Normal file
View file

@ -0,0 +1,14 @@
import sys
sys.path.append('.')
from ctu import *
#@run#(TRACE_FUNCTION)
@debug(TRACE_MEMCHECK)
def main(ctu):
ctu.load('skeletonSample')
#@ctu.replaceFunction(MainAddress(unknown))
def memset(ctu, addr, val, size):
ctu.writemem(addr, chr(val) * size, check=False)
ctu.call(MainAddress(0x0), _start=True)

607
svc.py
View file

@ -1,47 +1,628 @@
import ipcbridge
from ipc import IPC
from sync import *
from util import *
import struct
from threadmanager import Thread
import struct, threading
from time import sleep
from svcHelper import svcToName
handlers = {}
def handler(num):
def sub(func):
handlers[num] = func
def dsub(self):
args = (self.ctu.reg(i) for i in xrange(func.__code__.co_argcount - 1))
ret = func(self, *args)
if ret is None:
return
if isinstance(ret, tuple):
for i, v in enumerate(ret):
self.ctu.reg(i, native(v))
else:
self.ctu.reg(0, native(ret))
handlers[num] = dsub
return func
return sub
class SvcHandler(object):
def __init__(self, ctu):
self.ctu = ctu
self.ipc = IPC(self.ctu)
ipcbridge.start(self.ctu)
for i in xrange(0x60):
self.mutexes = {}
self.semaphores = {}
for i in xrange(0x80):
ctu.hookinsn(0xD4000001 | (i << 5), (lambda i: lambda _, __: self.svcDispatch(i))(i))
def svcDispatch(self, svc):
if svc in handlers:
print 'svc %x' % svc
print '[%i] %s' %(self.ctu.threads.current.id, svcToName(svc))
handlers[svc](self)
return False
print 'Unhandled: SVC 0x%02x @ %s' % (svc, raw(self.ctu.pc))
print 'Unhandled: SVC %s 0x%02x @ %s' % (svcToName(svc), svc, raw(self.ctu.pc))
self.ctu.debugbreak()
return False
def ipcDispatcher(self, handle, addr, size):
print 'IPC! Handle: %08x' % handle
self.ctu.dumpmem(addr, size)
self.ctu.dumpmem(addr, 0x80)
buffer = struct.unpack('<' + 'I' * (size >> 2), self.ctu.readmem(addr, size, check=False))
ret, buffer = self.ipc.recv(handle, buffer)
self.ctu.writemem(addr, '\0' * 0x100, check=False)
self.ctu.checkwrite(addr, 0x100, unset=True)
if buffer is not None:
obuf = ''
for i, x in enumerate(buffer):
if x is not None:
self.ctu.write32(addr + i * 4, x)
self.ctu.dumpmem(addr, 0x80)
return ret
@handler(0x01)
def SetHeapSize(self, _, size):
self.ctu.usHeapSize = size
addr = 0xaa0000000
self.ctu.map(addr, size)
return 0, addr
@handler(0x03)
def SetMemoryAttribute(self, addr, size, state0, state1):
return 0
@handler(0x04)
def MirrorStack(self, dest, src, size):
print 'Mirror stack: %x %x %x' % (dest, src, size)
self.ctu.map(dest, size)
self.ctu.writemem(dest, self.ctu.readmem(src, size))
return 0
@handler(0x05)
def UnmapMemory(self, dest, src, size):
print 'UnmapMemory %x %x %x' % (dest, src, size)
self.ctu.unmap(dest, size)
return 0
@handler(0x06)
def QueryMemory(self, meminfo, pageinfo, addr):
print 'QueryMemory %x (%x %x)' % (addr, meminfo, pageinfo)
for begin, end, perms in self.ctu.memregions():
if begin <= addr < end:
print 'Found region at %x-%x' % (begin, end)
self.ctu.write64(meminfo + 0x00, begin)
self.ctu.write64(meminfo + 0x08, end - begin)
self.ctu.write64(meminfo + 0x10, 0 if perms == -1 else 3) # FREE or CODE
if perms == -1:
cperm = 0
else:
offset = self.ctu.read32(begin + 4)
if begin + offset + 4 < end and self.ctu.readmem(begin + offset, 4) == 'MOD0':
cperm = 5
else:
cperm = 3
self.ctu.write64(meminfo + 0x18, cperm)
break
return 0, 0
@handler(0x07)
def ExitProcess(self):
print 'EXIT PROCESS'
self.ctu.debugbreak()
return 0,0
@handler(0x08)
def CreateThread(self, out, pc, x0, sp, prio, proc):
print 'Creating thread at %s' % raw(pc)
thread = self.ctu.threads.create(pc, sp, x0)
self.ctu.write64(out, thread.handle)
return 0, thread.handle
@handler(0x09)
def StartThread(self, handle):
thread = self.ctu.handles[handle]
print 'Starting thread %i at %s (SP=%s)' % (thread.id, raw(thread.regs[0]), raw(thread.regs[1]))
thread.active = True
self.ctu.threads.running.append(thread)
return 0
@handler(0x0A)
def ExitThread(self):
print 'Exiting thread %i' % self.ctu.threads.current.id
self.ctu.threads.exit()
@handler(0x0B)
def SleepThread(self, ns):
sec = ns / 1000000000.
print 'Sleeping thread for %f seconds' % sec
thread = self.ctu.threads.current
timer = Timer()
@timer.wait
def waiter(trigger):
while thread.active:
time.sleep(0.01)
thread.resume()
return True
timer.signalIn(sec)
thread.suspend()
@handler(0x0C)
def GetThreadPriority(self, handle):
return 0, 0
@handler(0x0D)
def SetThreadPriority(self, handle, priority):
return 0
@handler(0x0E)
def GetThreadCoreMask(self):
return 0, 0xFF, 0xFF
@handler(0x0F)
def SetThreadCoreMask(self):
return 0
@handler(0x10)
def GetCurrentProcessorNumber(self):
return 0
@handler(0x11)
def SignalEvent(self, handle):
print 'SignalEvent %x' % handle
#print self.ctu.handles[handle]
return 0
@handler(0x12)
def ClearEvent(self, handle):
print 'ClearEvent %x' % handle
print self.ctu.handles[handle]
return 0
@handler(0x13)
def MapMemoryBlock(self, handle, addr, size, perm):
print 'Map memory block: %x %x %x %x' % (handle, addr, size, perm)
obj = self.ctu.handles[handle]
assert obj.size == size
assert obj.addr is None
self.ctu.map(addr, size)
obj.addr = addr
return 0
@handler(0x15)
def CreateTransferMemory(self, addr, size, perm):
print 'CreateTransferMemory %x %x %x' % (addr, size, perm)
return 0, 0
@handler(0x16)
def CloseHandle(self, handle):
self.ctu.closeHandle(handle)
return 0
@handler(0x17)
def ResetSignal(self, handle):
print 'ResetSignal %x' % handle
return 0
@handler(0x18)
def WaitSynchronization(self, out, handles, numHandles, timeout):
print '[%i] WaitSynchronization %x %i %x' % (self.ctu.threads.current.id, handles, numHandles, timeout)
objs = [self.ctu.handles[self.ctu.read32(handles + i * 4)] for i in xrange(numHandles)]
print objs
for i, obj in enumerate(objs):
if isinstance(obj, Thread) and obj.terminated:
return 0, i
"""for obj in objs:
if isinstance(obj, Port) and self.ctu.threads.current.id != 0:
print 'Waiting for ports on non-zero thread? Nope.'
objs = [Waitable()]
break"""
for obj in objs:
obj.acquire()
for i, obj in enumerate(objs):
if isinstance(obj, Pipe) and (obj.closed or len(obj.queue) > 0):
obj.waitable_presignaled = False, None, None
for sobj in objs:
sobj.release()
return 0, i
triggered = [False]
setup = False
thread = self.ctu.threads.current
def waiter(trigger, canceled=False):
if triggered[0]:
return False
triggered[0] = True
while thread.active:
time.sleep(0.01)
thread.blockers = []
print 'WaitSynchronization done! Canceled == %r' % canceled
thread.regs[0+2] = 0xec01 if canceled else 0 # X0 = 0
thread.regs[1+2] = 0 if canceled else objs.index(trigger) # X1 = index of handle
thread.resume()
return True
thread.blockers = objs
for obj in objs:
obj.wait(waiter)
obj.release()
thread.suspend()
@handler(0x19)
def CancelSynchronization(self, handle):
print 'CancelSynchronization %x' % handle
thread = self.ctu.handles[handle]
for blocker in thread.blockers:
blocker.signal(True)
return 0
def ensureMutex(self, ptr):
if isinstance(ptr, Mutex):
return ptr
elif ptr not in self.mutexes:
print 'Making new mutex for %x' % ptr
self.mutexes[ptr] = Mutex(self.ctu, ptr)
return self.mutexes[ptr]
def ensureSema(self, ptr):
if isinstance(ptr, Semaphore):
return ptr
elif ptr not in self.semaphores:
print 'Making new semaphore for %x' % ptr
self.semaphores[ptr] = Semaphore(self.ctu, ptr)
return self.semaphores[ptr]
@handler(0x1A)
def LockMutex(self, curthread, mutexAddr, reqthread):
print 'LockMutex %x %x %x' % (curthread, mutexAddr, reqthread)
mutex = self.ensureMutex(mutexAddr)
owner = mutex.value & 0xBFFFFFFF
thread = self.ctu.threads.current
if owner != 0 and owner is not reqthread:
print 'Could not get mutex lock. Waiting.'
mutex.hasWaiters = 1
@mutex.wait
def waiter(trigger):
while thread.active: # In case we haven't finished suspending when the mutex releases
time.sleep(0.1)
if mutex.owner is None:
mutex.owner = reqthread
thread.regs[0+2] = 0
thread.resume()
return True
else:
mutex.hasWaiters = 1
if thread.active:
thread.suspend()
else:
mutex.owner = reqthread
if not thread.active:
thread.regs[0+2] = 0
thread.resume()
else:
return 0
@handler(0x1B)
def UnlockMutex(self, mutex):
print 'UnlockMutex %x' % mutex
mutex = self.ensureMutex(mutex)
owner = mutex.owner
assert mutex.owner is None or mutex.owner is self.ctu.threads.current
mutex.guestRelease()
@handler(0x1C)
def WaitProcessWideKeyAtomic(self, mutexAddr, sema, threadHandle, timeout):
print 'WaitProcessWideKeyAtomic %x %x %x %i' % (mutexAddr, sema, threadHandle, timeout)
thread = self.ctu.handles[threadHandle]
print 'WaitProcessWideKeyAtomic on thread', thread.id
mutex = self.ensureMutex(mutexAddr)
sema = self.ensureSema(sema)
# Mutex should always be locked on wait!
assert mutex.owner is thread
if sema.value > 0:
sema.decrement()
return 0
@sema.wait
def waiter(trigger):
while thread.active: # In case we haven't finished suspending
time.sleep(0.1)
sema.decrement()
print 'Attempting to wake thread to get mutex back:', thread.id
self.LockMutex(0, mutexAddr, threadHandle)
return True
mutex.guestRelease()
thread.suspend()
@handler(0x1D)
def SignalEvent(self):
self.ctu.reg(0, 0)
def SignalProcessWideKey(self, sema, target):
print 'SignalProcessWideKey %x %x' % (sema, target)
sema = self.ensureSema(sema)
sema.increment()
if target == 1:
sema.signalOne()
elif target == 0xFFFFFFFF:
sema.signal()
return 0
@handler(0x1F)
def ConnectToPort(self, out, name):
handle = self.ipc.connectToPort(self.ctu.readstring(name))
self.ctu.write64(out, handle)
return 0, handle
@handler(0x21)
def SendSyncRequest(self):
return self.ipcDispatcher(self.ctu.reg(0), self.ctu.tlsbase, 0x100)
return self.ipcDispatcher(self.ctu.reg(0), self.ctu.threads.current.tlsbase, 0x100)
@handler(0x22)
def SendSyncRequestEx(self):
return self.ipcDispatcher(self.ctu.reg(2), self.ctu.reg(0), self.ctu.reg(1))
@handler(0x24)
def GetProcessID(self, out, handle):
print 'GetProcessID %x %x' % (out, handle)
process = self.ctu.handles[handle]
self.ctu.write32(out, process.id)
return 0, process.id
@handler(0x25)
def GetThreadId(self):
self.ctu.writemem(self.ctu.reg(0), struct.pack('<Q', 0xf00))
self.ctu.reg(0, 0)
def GetThreadId(self, p_threadid):
self.ctu.write64(p_threadid, self.ctu.threads.current.id)
return 0
@handler(0x26)
def Break(self, X0, X1, info):
print 'svcBreak HIT!'
print 'X0=%016x'%X0
print 'X1=%016x'%X1
print 'X2=%016x'%info
self.ctu.debugbreak()
return 0
@handler(0x27)
def OutputDebugString(self, ptr, size):
print 'Debug string:', self.ctu.readmem(ptr, size).rstrip('\0')
@handler(0x29)
def GetInfo(self, out, id1, handle, id2):
res = None
print 'Get info: %i:%i %x -> %x' % (id1, id2, handle, out)
process = self.ctu.handles[handle if handle != 0 else 0xFFFF8001] # Assume current process for faking
if id1 == 0 and id2 == 0:
res = 0xF
elif id1 == 1 and id2 == 0:
res = 0xfffffffff0000000
elif id1 == 2 and id2 == 0:
res = 0x7100000000
elif id1 == 3 and id2 == 0:
res = 0x1000000000
elif id1 == 4 and id2 == 0:
res = 0xaa0000000 # Heap base?
elif id1 == 5 and id2 == 0:
res = self.ctu.usHeapSize # Heap region size
elif id1 == 6 and id2 == 0:
res = 0x100000
elif id1 == 7 and id2 == 0:
res = 0x10000
elif id1 == 12 and id2 == 0:
res = 0x8000000
elif id1 == 13 and id2 == 0:
res = 0x7ff8000000
elif id1 == 14 and id2 == 0:
res = self.ctu.loadbase
elif id1 == 15 and id2 == 0:
res = self.ctu.loadsize
elif id1 == 18 and id2 == 0:
res = 0x0100000000000036
elif id1 == 11:
res = 0
if res is None:
print 'Unknown getinfo!'
self.ctu.write64(out, 0)
return 1, 0
else:
self.ctu.write64(out, res)
return 0, res
@handler(0x40)
def CreateSession(self, clientOut, serverOut, unk):
print 'Creating session %x %x %x' % (clientOut, serverOut, unk)
a, b = Pipe.new()
ah, bh = self.ctu.newHandle(a), self.ctu.newHandle(b)
self.ctu.write32(clientOut, ah)
self.ctu.write32(serverOut, bh)
return 0, ah, bh
@handler(0x41)
def AcceptSession(self, out, port):
port = self.ctu.handles[port]
print 'Accept session on', port
pipe = port.pop()
pipe.accept()
handle = self.ctu.newHandle(pipe)
self.ctu.write32(out, handle)
return 0, handle
@handler(0x43)
def ReplyAndReceive(self, out, handles, numHandles, replySession, timeout):
#print 'ReplyAndReceive %x %x %i %x %x' % (out, handles, numHandles, replySession, timeout)
addr = self.ctu.threads.current.tlsbase
if replySession != 0:
handle = self.ctu.handles[replySession]
print 'Writing outgoing IPC message:'
self.ctu.dumpmem(addr, 0x100)
handle.push(self.ctu.readmem(addr, 0x100, check=False))
if numHandles == 0:
return 0xf601, 0
objs = [self.ctu.handles[self.ctu.read32(handles + i * 4)] for i in xrange(numHandles)]
assert len(objs) == 1 and isinstance(objs[0], Pipe)
print objs[0]
objs[0].acquire()
if objs[0].closed:
print 'Pipe is closed.'
return 0xf601, 0
self.ctu.write32(out, 0) # Index into handles
data = objs[0].pop()
objs[0].release()
if data is None:
return 0xf601, 0
self.ctu.writemem(addr, data)
print 'Read incoming IPC message:'
self.ctu.dumpmem(addr, len(data))
return 0, 0
@handler(0x45)
def CreateEvent(self, clientOut, serverOut, unk):
print 'Creating event? Totally fake %x %x %x' % (clientOut, serverOut, unk)
a, b = Pipe.new()
ah, bh = self.ctu.newHandle(a), self.ctu.newHandle(b)
self.ctu.write32(clientOut, ah)
self.ctu.write32(serverOut, bh)
return 0, ah, bh
@handler(0x4E)
def ReadWriteRegister(self, out, reg, rwm, val):
print 'ReadWriteRegister %x %x %x %x' % (out, reg, rwm, val)
robj = None
for pbase, vbase, size, obj in self.ctu.mmiomap:
if pbase <= reg < pbase + size:
robj = obj
break
if robj is None:
print '!Unknown physical address!'
self.ctu.debugbreak()
return 0, 0
if rwm == 0xFFFFFFFF:
obj.write(reg, 4, val)
elif rwm == 0x00000000:
val = obj.read(reg, 4)
else:
tval = obj.read(reg, 4)
tval &= (0xFFFFFFFF ^ rwm)
val |= tval
obj.write(reg, 4, val)
return 0, val
@handler(0x50)
def CreateMemoryBlock(self, out, size, perm):
print 'Attempting to create memory block: %x %x' % (size, perm)
handle = self.ctu.newHandle(MemoryBlock(size, perm))
self.ctu.write64(out, handle)
return 0, handle
@handler(0x51)
def MapTransferMemory(self, handle, addr, size, perm):
print 'MapTransferMemory %x %x %x %x' % (handle, addr, size, perm)
self.ctu.map(addr, size)
return 0
@handler(0x52)
def UnmapTransferMemory(self, handle, addr, size):
print 'UnmapTransferMemory %x %x %x' % (handle, addr, size)
self.ctu.unmap(addr, size)
return 0
@handler(0x53)
def CreateInterruptEvent(self, out, irq):
print 'CreateInterruptEvent %x %x' % (out, irq)
interruptWaitable = Waitable()
print interruptWaitable
return 0, self.ctu.newHandle(interruptWaitable)
@handler(0x55)
def QueryIoMapping(self, out, physaddr, size):
print 'QueryIoMapping %x %x' % (physaddr, size)
res = None
for pbase, vbase, msize, obj in self.ctu.mmiomap:
if pbase <= physaddr < pbase + msize:
res = physaddr - pbase + vbase
break
if res is None:
print '!Unknown physical address!'
self.ctu.debugbreak()
res = 0
self.ctu.write64(out, res)
return 0, res
@handler(0x56)
def CreateDeviceAddressSpace(self, out, base, size):
print 'CreateDeviceAddressSpace %x %x %x' % (out, base, size)
obj = DeviceMemory(base, size)
handle = self.ctu.newHandle(obj)
self.ctu.write32(out, handle)
return 0, handle
@handler(0x57)
def AttachDeviceAddressSpace(self, handle, dev, addr):
print 'AttachDeviceAddressSpace %x %x %x' % (handle, dev, addr)
return 0, 0
@handler(0x59)
def MapDeviceAddressSpaceByForce(self, handle, phandle, paddr, size, maddr, perm):
print 'MapDeviceAddressSpaceByForce %x %x %x %x %x %x' % (handle, phandle, paddr, size, maddr, perm)
return 0, 0
@handler(0x5c)
def UnmapDeviceAddressSpace(self, unk0, phandle, maddr, size):
print 'UnmapDeviceAddressSpace %x %x %x %x' % (unk0, phandle, maddr, size)
return 0
@handler(0x74)
def MapProcessMemory(self, dstaddr, handle, srcaddr, size):
print 'MapProcessMemory %x %x %x %x' % (handle, dstaddr, srcaddr, size)
self.ctu.map(dstaddr, size)
self.ctu.writemem(dstaddr, self.ctu.readmem(srcaddr, size))
return 0
@handler(0x75)
def UnmapProcessMemory(self, dstaddr, handle, srcaddr, size):
print 'UnmapProcessMemory %x %x %x %x' % (handle, dstaddr, srcaddr, size)
self.ctu.unmap(dstaddr, size)
return 0
@handler(0x77)
def MapProcessCodeMemory(self, handle, dstaddr, srcaddr, size):
print 'MapProcessCodeMemory %x %x %x %x' % (handle, dstaddr, srcaddr, size)
self.ctu.map(dstaddr, size)
return 0
@handler(0x78)
def UnmapProcessCodeMemory(self, handle, dstaddr, srcaddr, size):
print 'UnmapProcessCodeMemory %x %x %x %x' % (handle, dstaddr, srcaddr, size)
self.ctu.unmap(dstaddr, size)
return 0
class MemoryBlock(object):
def __init__(self, size, perm):
self.size = size
self.perm = perm
self.addr = None
class DeviceMemory(object):
def __init__(self, gaddr, size):
self.gaddr = gaddr
self.caddr = None
self.size = size

103
svcHelper.py Normal file
View file

@ -0,0 +1,103 @@
#!/usr/bin/env python
SVCNameMap = {
0x01: "svcSetHeapSize",
0x02: "svcSetMemoryPermission",
0x03: "svcSetMemoryAttribute",
0x04: "svcMapMemory",
0x05: "svcUnmapMemory",
0x06: "svcQueryMemory",
0x07: "svcExitProcess",
0x08: "svcCreateThread",
0x09: "svcStartThread",
0x0A: "svcExitThread",
0x0B: "svcSleepThread",
0x0C: "svcGetThreadPriority",
0x0D: "svcSetThreadPriority",
0x0E: "svcGetThreadCoreMask",
0x0F: "svcSetThreadCoreMask",
0x10: "svcGetCurrentProcessorNumber",
0x11: "svcSignalEvent",
0x12: "svcClearEvent",
0x13: "svcMapSharedMemory",
0x14: "svcUnmapSharedMemory",
0x15: "svcCreateTransferMemory",
0x16: "svcCloseHandle",
0x17: "svcResetSignal",
0x18: "svcWaitSynchronization",
0x19: "svcCancelSynchronization",
0x1A: "svcArbitrateLock",
0x1B: "svcArbitrateUnlock",
0x1C: "svcWaitProcessWideKeyAtomic",
0x1D: "svcSignalProcessWideKey",
0x1E: "svcGetSystemTick",
0x1F: "svcConnectToNamedPort",
0x20: "svcSendSyncRequestLight",
0x21: "svcSendSyncRequest",
0x22: "svcSendSyncRequestWithUserBuffer",
0x23: "svcSendAsyncRequestWithUserBuffer",
0x25: "svcGetThreadId",
0x26: "svcBreak",
0x27: "svcOutputDebugString",
0x28: "svcReturnFromException",
0x29: "svcGetInfo",
0x2A: "svcFlushEntireDataCache",
0x2C: "svcMapPhysicalMemory",
0x2D: "svcUnmapPhysicalMemory",
0x2E: "svcUnknown(2E)",
0x30: "svcGetResourceLimitLimitValue",
0x32: "svcSetThreadActivity",
0x34: "svcUnknown(34)",
0x36: "svcUnknown(36)",
0x38: "svcUnknown(38)",
0x3A: "svcUnknown(3A)",
0x3C: "svcDumpInfo",
0x3E: "svcUnknown(3E)",
0x40: "svcCreateSession",
0x41: "svcAcceptSession",
0x42: "svcReplyAndReceiveLight",
0x43: "svcReplyAndReceive",
0x44: "svcReplyAndReceiveWithUserBuffer",
0x45: "svcCreateEvent",
0x46: "svcUnknown(46)",
0x48: "svcUnknown(48)",
0x4A: "svcUnknown(4A)",
0x4C: "svcUnknown(4C)",
0x4E: "svcReadWriteRegister",
0x4F: "svcSetProcessActivity",
0x51: "svcMapTransferMemory",
0x52: "svcUnmapTransferMemory",
0x53: "svcCreateInterruptEvent",
0x54: "svcQueryPhysicalAddress",
0x56: "svcCreateDeviceAddressSpace",
0x57: "svcAttachDeviceAddressSpace",
0x58: "svcDetachDeviceAddressSpace",
0x59: "svcMapDeviceAddressSpaceByForce",
0x5A: "svcMapDeviceAddressSpaceAligned",
0x5C: "svcUnmapDeviceAddressSpace",
0x5E: "svcStoreProcessDataCache",
0x60: "svcDebugActiveProcess",
0x62: "svcTerminateDebugProcess",
0x64: "svcContinueDebugEvent",
0x66: "svcGetThreadList",
0x68: "svcSetDebugThreadContext",
0x6A: "svcReadDebugProcessMemory",
0x6C: "svcSetHardwareBreakPoint",
0x6E: "svcUnknown(6E)",
0x70: "svcCreatePort",
0x72: "svcConnectToPort",
0x74: "svcMapProcessMemory",
0x75: "svcUnmapProcessMemory",
0x76: "svcQueryProcessMemory",
0x78: "svcUnmapProcessCodeMemory",
0x79: "svcCreateProcess",
0x7B: "svcTerminateProcess",
0x7D: "svcCreateResourceLimit",
0x7F: "svcCallSecureMonitor"
}
def svcToName(code):
if code in SVCNameMap:
return SVCNameMap[code]
else:
return "svcUnknown(0x%02x)" %code

68
sync.py Normal file
View file

@ -0,0 +1,68 @@
from util import *
from threading import Thread
class Mutex(Waitable):
def __init__(self, ctu, ptr):
self.setup()
self.ctu = ctu
self.ptr = ptr
self.waitable_presignalable = False
@property
def value(self):
return self.ctu.read32(self.ptr)
@property
def owner(self):
handle = self.value & 0xBFFFFFFF
if handle == 0:
return None
return self.ctu.handles[handle]
@owner.setter
def owner(self, handle):
if handle not in self.ctu.handles: # We must have the object
for sh, so in self.ctu.handles.items():
if handle is so:
handle = sh
break
self.ctu.write32(self.ptr, (self.ctu.read32(self.ptr) & 0x40000000) | handle)
@property
def hasWaiters(self):
return self.ctu.read32(self.ptr) >> 28
@hasWaiters.setter
def hasWaiters(self, value):
self.ctu.write32(self.ptr, (self.ctu.read32(self.ptr) & 0xBFFFFFFF) | (int(value) << 30))
def guestRelease(self):
self.owner = 0
Thread(target=self.signal).start()
class Semaphore(Waitable):
def __init__(self, ctu, ptr):
self.setup()
self.ctu = ctu
self.ptr = ptr
self.waitable_presignalable = False
self.value = 0
@property
def value(self):
return self.ctu.read32(self.ptr)
@value.setter
def value(self, value):
self.ctu.write32(self.ptr, value)
def increment(self):
self.acquire()
self.value += 1
self.release()
def decrement(self):
self.acquire()
self.value -= 1
self.release()

137
systemsettings.yaml Normal file
View file

@ -0,0 +1,137 @@
account!na_required_for_network_service: 0x01
account.daemon!background_awaking_periodicity: 0x00002a30
account.daemon!schedule_periodicity: 0x00000e10
account.daemon!profile_sync_interval: 0x00004650
account.daemon!na_info_refresh_interval: 0x0000b6d0
am.display!transition_layer_enabled: 0x01
am.gpu!gpu_scheduling_enabled: 0x01
am.gpu!gpu_scheduling_frame_time_us: 0x0001c7ba
am.gpu!gpu_scheduling_fg_app_us: 0x0001c5c6
am.gpu!gpu_scheduling_bg_app_us: 0x00019834
am.gpu!gpu_scheduling_oa_us: 0x000001f4
am.gpu!gpu_scheduling_fg_sa_us: 0x00002d92
am.gpu!gpu_scheduling_bg_sa_us: 0x00000000
am.gpu!gpu_scheduling_fg_la_us: 0x00002d92
am.gpu!gpu_scheduling_partial_fg_la_us: 0x000007d0
am.gpu!gpu_scheduling_bg_la_us: 0x00000000
audio!audren_log_enabled: 0x00
audio!audout_log_enabled: 0x00
audio!audin_log_enabled: 0x00
audio!hwopus_log_enabled: 0x00
audio!adsp_log_enabled: 0x00
audio!suspend_for_debugger_enabled: 0x00
bgtc!enable_battery_saver: 0x00000001
bgtc!leaving_halfawake_margin: 0x00000003
bgtc!battery_threshold_save: 0x0000001e
bgtc!battery_threshold_stop: 0x0000000f
bgtc!minimum_interval_normal: 0x00000708
bgtc!minimum_interval_save: 0x00015180
boot!force_maintenance: 0x00
capsrv!screenshot_layerstack: 'screenshot'
capsrv!enable_album_screenshot_filedata_verification: 0x01
devmenu!enable_application_update: 0x01
devmenu!enable_exhibition_mode: 0x00
eclct!analytics_override: 0x00
eclct!analytics_pollperiod: 0x00015180
err!applet_auto_close: 0x00
friends!background_processing: 0x01
gpu_core_dump!auto_dump: 0x00
htc!disconnection_emulation: 0x00
idle!dim_level_percent_lcd: 0x0000000a
idle!dim_level_percent_tv: 0x00000046
lm!enable_sd_card_logging: 0x00
lm!sd_card_log_output_directory: 'NxBinLogs'
mii!is_db_test_mode_enabled: 0x00
news!system_version: 0x00000001
nfp!not_locked_tag: 0x01
nfp!play_report: 0x00
nifm!is_communication_control_enabled_for_test: 0x00
nim.install!prefer_delta_evenif_inefficient: 0x00
ns.notification!retry_interval: 0x0000003c
ns.notification!enable_network_update: 0x01
ns.notification!enable_download_task_list: 0x01
ns.notification!enable_version_list: 0x01
ns.sdcard!mount_sdcard: 0x01
omm!operation_mode_policy: 'auto'
omm!sleep_fade_in_ms: 0x00000032
omm!sleep_fade_out_ms: 0x00000064
omm!charging_sign_ms: 0x00000bb8
omm!low_battery_sign_ms: 0x00000bb8
omm!sign_fade_in_ms: 0x00000000
omm!sign_fade_out_ms: 0x00000190
omm!sign_wait_layer_visible_ms: 0x00000064
omm!startup_fade_in_ms: 0x000000c8
omm!startup_fade_out_ms: 0x00000190
omm!backlight_off_ms_on_handheld_switch: 0x00000096
pcm!enable: 0x01
pctl!intermittent_task_interval_seconds: 0x00005460
prepo!devmenu_prepo_page_view: 0x00
prepo!background_processing: 0x01
snap_shot_dump!auto_dump: 0x00
snap_shot_dump!output_dir: '%USERPROFILE%/Documents/Nintendo/NXDMP'
snap_shot_dump!full_dump: 0x00
systemconfig!field_testing: 0x00
systemconfig!exhivision: 0x00
systempowerstate!always_reboot: 0x00
systempowerstate!power_state_message_emulation_trigger_time: 0x00000000
systempowerstate!power_state_message_to_emulate: 0x00000000
target_manager!device_name: ''
time!standard_steady_clock_test_offset_minutes: 0x00000000
vulnerability!needs_update_vulnerability_policy: 0x00000000
apm!performance_mode_policy: 'auto'
apm!sdev_throttling_enabled: 0x01
apm!sdev_throttling_additional_delay_us: 0x00003e80
apm!battery_draining_enabled: 0x00
bcat!production_mode: 0x01
bpc!enable_quasi_off: 0x01
bsp0!usb: 'UDS'
bsp0!tm_transport: 'USB'
bluetooth_debug!skip_boot: 0x00
fatal!transition_to_fatal: 0x01
hid_debug!enables_debugpad: 0x00
hid_debug!manages_devices: 0x01
npns!background_processing: 0x01
npns!logmanager_redirection: 0x01
npns!sleep_processing_timeout: 0x0000001e
npns!sleep_periodic_interval: 0x00002a30
ns.applet!overlay_applet_id: '0x010000000000100c'
ns.applet!system_applet_id: '0x0100000000001000'
nsd!environment_identifier: 'lp1'
nsd!test_mode: 0x00
ntc!is_autonomic_correction_enabled: 0x01
ntc!autonomic_correction_interval_seconds: 0x000d2f00
ntc!autonomic_correction_failed_retry_interval_seconds: 0x00000e10
ntc!autonomic_correction_immediate_try_count_max: 0x00000004
ntc!autonomic_correction_immediate_try_interval_milliseconds: 0x00001388
pdm!save_playlog: 0x01
productinfo!product_name: 'Nintendo Switch'
productinfo!cec_osd_name: 'NintendoSwitch'
systemreport!enabled: 0x01
systemsleep!enter_sleep: 0x01
systemsleep!enter_sc7: 0x01
systemsleep!keep_vdd_core: 0x01
systemsleep!disable_tma_sleep: 0x00
systemsleep!disable_auto_sleep: 0x00
systemsleep!override_auto_sleep_time: 0x00000000
systemsleep!sleep_pending_time_ms: 0x00003a98
systemsleep!hush_time_after_brief_power_button_press_ms: 0x000003e8
systemsleep!transition_timeout_sec: 0x0000003c
systemsleep!dummy_event_auto_wake: 0x00
systemupdate!debug_id: '0x0000000000000000'
systemupdate!debug_version: 0x00000000
systemupdate!enable_network_service: 0x01
systemupdate!bgnup_retry_seconds: 0x0000003c
systemupdate!enable_background_download_stress_testing: 0x00
tc!iir_filter_gain_soc: 0x00000064
tc!iir_filter_gain_pcb: 0x00000064
tc!tskin_soc_coefficients_handheld: '[5464, 174190]'
tc!tskin_soc_coefficients_console: '[6182, 112480]'
tc!tskin_pcb_coefficients_handheld: '[5464, 174190]'
tc!tskin_pcb_coefficients_console: '[6182, 112480]'
tc!tskin_select: 'both'
tc!tskin_rate_table_handheld: '[[-1000000, 40000, 0, 0], [36000, 43000, 51, 51], [43000, 48000, 51, 102], [48000, 53000, 102, 153], [53000, 1000000, 153, 153], [48000, 1000000, 153, 153]]'
tc!tskin_rate_table_console: '[[-1000000, 43000, 51, 51], [43000, 53000, 51, 153], [53000, 58000, 153, 255], [58000, 1000000, 255, 255]]'
tc!rate_select: 'both'
tc!log_enabled: 0x00
tc!sleep_enabled: 0x01
wlan_debug!skip_wlan_boot: 0x00

162
threadmanager.py Normal file
View file

@ -0,0 +1,162 @@
import os, signal, time
from util import *
from ipc import LrService
from unicorn.arm64_const import *
class ThreadManager(object):
def __init__(self, ctu):
self.ctu = ctu
self.clear()
self.startupCompleted = False
def next(self, pcOffset=0):
try:
while True:
if len(self.running) == 0:
if self.current is None:
if self.ctu.terminateOnFullSleep:
self.clear()
self.create(self.ctu.termaddr, 0)
return
elif not self.startupCompleted:
print 'Started!'
self.startupCompleted = True
while len(self.running) == 0:
time.sleep(0.01)
else:
return
new = self.running.pop(0)
if new.active:
break
except KeyboardInterrupt:
self.ctu.stop()
return
if self.current is not None:
self.current.freeze(pcOffset=pcOffset)
if self.current.active:
self.running.append(self.current)
self.current = new
self.ctu.pc = self.ctu.termaddr # Trap out of CTU
self.switched = True
return True
def clear(self):
self.threads = []
self.threadCount = 0
self.threadId = 0
self.running = []
self.current = None
self.switched = False
def create(self, pc, sp, *regs):
thread = Thread(self.ctu, self.threadId, pc, sp, regs)
self.threadId += 1
self.threads.append(thread)
self.threadCount = len(self.threads)
return thread
def exit(self, id=None):
if id is None:
id = self.current.id
for i, thread in enumerate(self.threads):
if thread.id == id:
print 'Terminating thread...'
thread.terminate()
return
class Thread(Waitable):
def __init__(self, ctu, id, pc, sp, regs):
self.ctu = ctu
self.id = id
self.handle = self.ctu.newHandle(self)
self.active = len(self.ctu.threads.threads) == 0
self.terminated = False
self.blx = True
self.regs = [pc, sp] + list(regs) + [0] * (67 - len(regs))
if self.regs[30+2] == 0:
self.regs[30+2] = self.ctu.termaddr # LR == termaddr
self.callstack = []
self.lastinsn = None
self.tlssize = 1024 * 1024 # 1MB
self.tlsbase = (1 << 24) + self.tlssize * self.id
self.ctu.map(self.tlsbase, self.tlssize)
self.ctu.writemem(self.tlsbase, '\0' * self.tlssize)
self.ctu.write64(self.tlsbase + 0x1F8, self.tlsbase + 0x200)
self.ctu.write64(self.tlsbase + 0x3B8, self.handle)
#tname = 'MediaPlayerHandler'
#threadname = self.ctu.malloc(len(tname) + 1)
#self.ctu.writemem(threadname, tname + '\0')
#self.ctu.write64(self.tlsbase + 0x3A8, threadname)
self.blockers = []
if self.active:
self.ctu.threads.current = self
def terminate(self):
if self.ctu.threads.current is self:
self.ctu.threads.current = None
self.signal()
self.active = False
self.terminated = True
self.ctu.threads.threads = [x for x in self.ctu.threads.threads if x is not self]
self.ctu.threads.running = [x for x in self.ctu.threads.running if x is not self]
self.ctu.threads.threadCount -= 1
self.ctu.threads.next()
print 'Killed thread id %i. Switched to %i at 0x%x' % (self.id, self.ctu.threads.current.id, self.ctu.threads.current.regs[0])
def suspend(self):
if not self.active:
return
if self is self.ctu.threads.current:
self.freeze(pcOffset=+4)
self.ctu.threads.current = None
self.active = False
self.ctu.threads.next()
else:
self.active = False
def resume(self):
self.active = True
self.ctu.threads.running.append(self)
def freeze(self, pcOffset=0):
regs = self.regs
mu = self.ctu.mu
regs[0] = mu.reg_read(UC_ARM64_REG_PC) + pcOffset
regs[1] = mu.reg_read(UC_ARM64_REG_SP)
for i in xrange(29):
regs[i+2] = mu.reg_read(UC_ARM64_REG_X0 + i)
regs[29+2] = mu.reg_read(UC_ARM64_REG_X29)
regs[30+2] = mu.reg_read(UC_ARM64_REG_X30)
for i in xrange(32):
regs[i+33] = mu.reg_read(UC_ARM64_REG_Q0 + i)
regs[66] = mu.reg_read(UC_ARM64_REG_NZCV)
#self.ctu.dumpregs()
print 'Thread %i frozen PC=%x, SP=%x, LR=%x' % (self.id, regs[0], regs[1], regs[30+2])
def thaw(self):
regs = self.regs
mu = self.ctu.mu
mu.reg_write(UC_ARM64_REG_PC, regs[0])
mu.reg_write(UC_ARM64_REG_SP, regs[1])
for i in xrange(29):
mu.reg_write(UC_ARM64_REG_X0 + i, regs[i+2])
mu.reg_write(UC_ARM64_REG_X29, regs[29+2])
mu.reg_write(UC_ARM64_REG_X30, regs[30+2])
for i in xrange(32):
mu.reg_write(UC_ARM64_REG_Q0 + i, regs[i+33])
mu.reg_write(UC_ARM64_REG_NZCV, regs[66])
print 'Thread %i thawed PC=%x, SP=%x, LR=%x' % (self.id, regs[0], regs[1], regs[30+2])
#self.ctu.dumpregs()

3
uncolor.sh Normal file
View file

@ -0,0 +1,3 @@
#!/bin/bash
cat colorme.idc | sed 's/0x00ff00)/0xffffff)/g;s/0xff00ff)/0xffffff)/g;' > uncolorme.idc

539
util.py
View file

@ -1,4 +1,5 @@
import re
import os, re, struct, time
from threading import RLock, Thread
class Restart(Exception):
pass
@ -8,10 +9,14 @@ class BadAddr(Exception):
symbols = {}
invsyms = {}
def mapLoader(fn, acls, base):
cut = 'nullsub_', 'def_%s' % ('%x' % base)[:2]
if not os.path.exists(fn):
print 'Missing map file', fn
return
acls = addressTypes[acls + 'Address']
cut = 'nullsub_', 'def_%s' % ('%x' % base)[:2], 'sub_%s' % ('%x' % base)[:2], 'loc_%s' % ('%x' % base)[:2]
with file(fn, 'r') as fp:
for line in fp:
if not line.startswith(' 00000001:'):
if not line.startswith(' 00000'):
continue
addr = acls(int(line[10:26], 16) - base)
name = line[33:].strip().split('(', 1)[0]
@ -20,6 +25,11 @@ def mapLoader(fn, acls, base):
symbols[name] = addr
invsyms[addr] = name
def nukeSymbols():
global symbols, invsyms
symbols = {}
invsyms = {}
class Address(object):
display_specialized = True
@ -68,29 +78,43 @@ class Address(object):
if not isinstance(self, RawAddress):
return self.to(RawAddress).specialize()
if WKCAddress.realbase <= self.addr < WKCAddress.realbase + WKCAddress.realsize:
return self.to(WKCAddress)
elif MainAddress.realbase <= self.addr < MainAddress.realbase + MainAddress.realbase:
return self.to(MainAddress)
for cls in addressTypes.values():
if cls != RawAddress and cls.realbase <= self.addr < cls.realbase + cls.realsize:
return self.to(cls)
return self
class RawAddress(Address):
mname = '%s'
aprefix = '*'
realbase = 0
realsize = 1 << 64
class WKCAddress(Address):
mname = 'WKC(%s)'
# realbase/size assigned at runtime
class MainAddress(Address):
mname = 'Main(%s)'
# realbase/size assigned at runtime
addressTypes = dict(RawAddress=RawAddress)
def defineAddressClass(name, base, size):
cname = name + 'Address'
if cname not in addressTypes:
cls = type(cname, (Address, ), dict(mname=name + '(%s)', aprefix='%s:' % name.lower(), realbase=base, realsize=size))
globals()[cname] = cls
addressTypes[cname] = cls
else:
cls = addressTypes[cname]
cls.realbase = base
cls.realsize = size
def raw(addr, pad=False):
if addr is None:
addr = 0
if isinstance(addr, str) or isinstance(addr, unicode):
if addr in symbols:
return symbols[addr]
else:
raise BadAddr()
if addr.startswith('0x'):
addr = '*' + addr
for cls in addressTypes.values():
if addr.startswith(cls.aprefix):
return cls(parseInt(addr[len(cls.aprefix):], implicitHex=True))
raise BadAddr()
elif isinstance(addr, Address):
return addr.to(RawAddress)
return RawAddress(addr, pad=pad).specialize()
@ -107,3 +131,488 @@ def parseInt(addr, implicitHex=False):
return int(addr)
else:
return None
class Process(object):
def __init__(self, id):
self.id = id
class Waitable(object):
def setup(self):
self.waitable_setup = True
self.waitable_lock = RLock()
self.waitable_waiters = []
self.waitable_presignaled = False, None, None
self.waitable_presignalable = True
def makeHandle(self):
return IPCMessage.ctu.newHandle(self) # XXX: hackhackhack
def acquire(self):
if not hasattr(self, 'waitable_setup'):
self.setup()
self.waitable_lock.acquire()
def release(self):
self.waitable_lock.release()
def wait(self, func):
def presignal():
self.acquire()
if func(self, *pa, **pk) in (True, False):
pass
else:
self.waitable_waiters.append(func)
self.waitable_presignaled = False, None, None
self.release()
self.acquire()
dp, pa, pk = self.waitable_presignaled
if dp:
Thread(target=presignal).start()
else:
self.waitable_waiters.append(func)
self.release()
def unwait(self, func):
self.acquire()
self.waitable_waiters = [x for x in self.waitable_waiters if x is not func]
self.release()
def signal(self, *args, **kwargs):
self.acquire()
if len(self.waitable_waiters) == 0 and self.waitable_presignalable:
self.waitable_presignaled = True, args, kwargs
else:
remove = []
realhit = False
for i, func in enumerate(self.waitable_waiters):
ret = func(self, *args, **kwargs)
if ret == True or ret == False:
remove.append(i)
if ret != False:
realhit = True
for i in remove[::-1]:
del self.waitable_waiters[i]
if not realhit and self.waitable_presignalable:
self.waitable_presignaled = True, args, kwargs
self.release()
def signalOne(self, *args, **kwargs):
self.acquire()
if len(self.waitable_waiters) != 0:
realhit = False
while len(self.waitable_waiters) != 0:
func = self.waitable_waiters[0]
ret = func(self, *args, **kwargs)
if ret == True or ret == False:
del self.waitable_waiters[0]
if ret != False:
realhit = True
break
if not realhit and self.waitable_presignalable:
self.waitable_presignaled = True, args, kwargs
elif self.waitable_presignalable:
self.waitable_presignaled = True, args, kwargs
self.release()
class Timer(Waitable):
def signalIn(self, seconds):
def sleeper():
time.sleep(seconds)
self.signal()
Thread(target=sleeper).start()
class InstantWaitable(Waitable):
def wait(self, func):
Waitable.wait(self, func)
Thread(target=self.signal).start()
class Port(Waitable):
def __init__(self, name):
self.name = name
self.queue = []
def push(self, obj):
self.acquire()
self.queue.append(obj)
self.signalOne()
self.release()
def pop(self):
self.acquire()
v = self.queue.pop(0)
self.release()
return v
def __repr__(self):
return 'Port<%s>' % self.name
class Pipe(Waitable):
@staticmethod
def new():
a = Pipe()
a.other = b = Pipe(a)
return a, b
def __init__(self, other=None):
self.other = other
self.queue = []
self.accepted = False
self.closed = False
def acquire(self):
Waitable.acquire(self)
Waitable.acquire(self.other)
def release(self):
Waitable.release(self)
Waitable.release(self.other)
def accept(self):
self.accepted = True
def waitForAccept(self):
while not self.accepted:
time.sleep(0.1)
def push(self, obj):
self.other.acquire()
self.other.queue.append(obj)
self.other.signal()
self.other.release()
def pop(self):
while not self.closed and not IPCMessage.ctu.exiting and len(self.queue) == 0:
time.sleep(0.1)
if self.closed or IPCMessage.ctu.exiting:
return None
self.acquire()
val = self.queue.pop(0)
self.release()
return val
def close(self):
self.acquire()
print 'Closing pipes', self, self.other
self.closed = True
self.other.closed = True
self.signal()
self.other.signal()
self.release()
def hexify(obj, name, pname=None):
def sub(v):
if isinstance(v, list) or isinstance(v, tuple):
return '[%s]' % ', '.join(map(sub, v))
elif v is None:
return 'None'
else:
return '0x%x' % v
pname = name if pname is None else pname
value = getattr(obj, pname)
if len(value) == 0:
return ''
return ', %s=%s' % (name, sub(value))
class IPCMessage(object):
def __init__(self, cmdId=0):
self.type = -1
self.cmdId = cmdId
self.request = False
self.domainParams = None
self.pid = -1
self.dataBuffer = []
self.aDescriptors = []
self.bDescriptors = []
self.cDescriptors = []
self.xDescriptors = []
self.copiedHandles = []
self.movedHandles = []
def setType(self, type):
self.type = type
return self
def hasPID(self, pid=0x1234):
self.pid = pid
return self
def data(self, *args):
self.dataBuffer += list(args)
return self
def aDescriptor(self, addr, size, perms):
self.aDescriptors.append((addr, size, perms))
return self
def bDescriptor(self, addr, size, perms):
self.bDescriptors.append((addr, size, perms))
return self
def cDescriptor(self, addr, size):
self.cDescriptors.append((addr, size))
return self
def xDescriptor(self, addr, size, counter):
self.xDescriptors.append((addr, size, counter))
return self
def copyHandle(self, handle):
if not isinstance(handle, int) and not isinstance(handle, long):
handle = self.ctu.newHandle(handle)
self.copiedHandles.append(handle)
return self
def moveHandle(self, handle):
if not isinstance(handle, int) and not isinstance(handle, long):
handle = self.ctu.newHandle(handle)
self.movedHandles.append(handle)
return self
def unpack(self, buf, request=True, domain=False, test=True):
self.request = request
self.type = buf[0] & 0xFFFF
xCount, aCount, bCount, wCount = (buf[0] >> 16) & 0xF, (buf[0] >> 20) & 0xF, (buf[0] >> 24) & 0xF, buf[0] >> 28
assert wCount == 0
wlen, hasC, hasHD = buf[1] & 0x3FF, ((buf[1] >> 10) & 0x3) in (2, 3), (buf[1] >> 31) == 1
pos = 2
if hasHD:
hd = buf[pos]
pos += 1
hasPID, copyCount, moveCount = bool(hd & 1), (hd >> 1) & 0xF, hd >> 5
if hasPID:
self.pid = buf[pos] | (buf[pos + 1] << 32) # I don't think this is ever populated by the app, this would be done by kernel
pos += 2
for i in xrange(copyCount):
self.copiedHandles.append(buf[pos])
pos += 1
for i in xrange(moveCount):
self.movedHandles.append(buf[pos])
pos += 1
for i in xrange(xCount):
a, b = buf[pos:pos+2]
pos += 2
if a is None or b is None:
addr, size, counter = None, None, None
else:
addr = b | ((((a >> 12) & 0xF) | ((a >> 2) & 0x70)) << 32)
size = a >> 16
counter = a & 0xE3F
self.xDescriptors.append((addr, size, counter))
for i in xrange(aCount + bCount):
a, b, c = buf[pos:pos+3]
pos += 3
if a is None or b is None or c is None:
addr, size, perm = None, None, None
else:
addr = b | (((((c >> 2) << 4) & 0x70) | ((c >> 28) & 0xF)) << 32)
size = a | (((c >> 24) & 0xF) << 32)
perm = c & 0x3
if i < aCount:
self.aDescriptors.append((addr, size, perm))
else:
self.bDescriptors.append((addr, size, perm))
end = pos + wlen
if pos & 3:
pos += 4 - (pos & 3)
if self.type != 5 and domain:
domainData = buf[pos:pos+4]
if None in domainData:
self.domainParams = None, None, None, None
else:
dcmd, sicount, rawsize, objid = domainData[0] & 0xFF, (domainData[0] >> 8) & 0xFF, domainData[0] >> 16, domainData[1]
self.domainParams = dcmd, sicount, rawsize, objid
pos += 4
term = 0x49434653 if request else 0x4f434653
if self.type != 2 and (not domain or self.type == 5 or self.domainParams[0] != 2) and buf[pos] != term:
print '!SFCO/SFCI not found!'
pos += 2
if hasC:
end -= 2
while pos < end:
a, b = buf[pos:pos+2]
if a is None and b is None:
self.dataBuffer.append(None)
elif a is None and b is not None:
self.dataBuffer.append(b << 32)
elif a is not None and b is None:
self.dataBuffer.append(a)
else:
self.dataBuffer.append(a | (b << 32))
pos += 2
if len(self.dataBuffer) != 0:
self.cmdId, self.dataBuffer = self.dataBuffer[0], self.dataBuffer[1:]
else:
self.cmdId = 0
if hasC:
a, b = buf[pos], buf[pos+1]
if a is None or b is None:
addr, size = None, None
else:
addr = a | ((b & 0xFFFF) << 32)
size = b >> 16
self.cDescriptors.append((addr, size))
pos += 2
self.data = self.dataBuffer
return self
def pack(self, domain=False):
rbuf = []
rbuf.append(0x49434653 if self.request else 0x4f434653)
rbuf.append(0)
rbuf.append(self.cmdId)
rbuf.append(0)
if isinstance(self.data, list):
self.dataBuffer = self.data
for x in self.dataBuffer:
if x is None:
rbuf.append(None)
else:
rbuf.append(x & 0xFFFFFFFF)
rbuf.append((x >> 32) & 0xFFFFFFFF)
for addr, size in self.cDescriptors:
if addr is None or size is None:
rbuf.append(None)
rbuf.append(None)
else:
laddr, haddr = addr & 0xFFFFFFFF, addr >> 32
rbuf.append(laddr)
rbuf.append((haddr & 0xFFFF) | (size << 16))
hdbuf = []
if self.pid != -1 or len(self.copiedHandles) != 0 or len(self.movedHandles) != 0:
hdbuf.append(
(1 if self.pid != -1 else 0) |
(len(self.copiedHandles) << 1) |
(len(self.movedHandles) << 5)
)
if self.pid != -1:
hdbuf.append(self.pid & 0xFFFFFFFF)
hdbuf.append((self.pid >> 32) & 0xFFFFFFFF)
hdbuf += self.copiedHandles
hdbuf += self.movedHandles
dbuf = []
for addr, size, counter in self.xDescriptors:
if addr is None or size is None or counter is None:
dbuf.append(None)
dbuf.append(None)
else:
laddr, haddr = addr & 0xFFFFFFFF, addr >> 32
dbuf.append(
(counter & 0x3F) |
(((haddr & 0x70) >> 4) << 6) |
(counter & 0xE00) |
((haddr & 0xF) << 12) |
(size << 16)
)
dbuf.append(laddr)
for addr, size, perms in (self.aDescriptors + self.bDescriptors):
if addr is None or size is None or perms is None:
dbuf.append(None)
dbuf.append(None)
dbuf.append(None)
else:
laddr, haddr = addr & 0xFFFFFFFF, addr >> 32
lsize, hsize = size & 0xFFFFFFFF, size >> 32
dbuf.append(lsize)
dbuf.append(laddr)
dbuf.append(
perms |
(((haddr & 0x70) >> 4) << 2) |
((hsize & 0xF) << 24) |
((haddr & 0xF) << 28)
)
hbuf = [
(
self.type |
(len(self.xDescriptors) << 16) |
(len(self.aDescriptors) << 20) |
(len(self.bDescriptors) << 24)
),
((1 << 31) if len(hdbuf) else 0) |
((2 << 10) if len(self.cDescriptors) else 0)
]
tbuf = hbuf + hdbuf + dbuf
slen = len(tbuf)
while (len(tbuf) & 3) != 0:
tbuf.append(None)
if domain:
tbuf += [0, 0, 0, 0]
tbuf += rbuf
tbuf[1] |= len(tbuf) - slen - (len(self.cDescriptors) << 1) + 2
return tbuf
def __repr__(self):
return '%s(%s%s%s%s%s%s%s%s%s%s)' % (
self.__class__.__name__,
'cmdId=%i' % self.cmdId,
', type=%i' % self.type if self.type != 0 else '',
', pid=0x%x' % self.pid if self.pid != -1 else '',
hexify(self, 'data', 'dataBuffer'),
hexify(self, 'aDescriptors'),
hexify(self, 'bDescriptors'),
hexify(self, 'cDescriptors'),
hexify(self, 'xDescriptors'),
hexify(self, 'copiedHandles'),
hexify(self, 'movedHandles'),
)
def formatString(ctu, fmts, startreg=None, stack=None, values=None):
cfmt='''\
( # start of capture group 1
% # literal "%"
(?: # first option
(?:[-+0 #]{0,5}) # optional flags
(?:\d+|\*)? # width
(?:\.(?:\d+|\*))? # precision
(?:h|l|ll|w|I|I32|I64)? # size
[cCdiouxXeEfgGaAnpsSZ] # type
) | # OR
%%) # literal "%%"
'''
args = []
replacements = []
for m in re.finditer(cfmt, fmts, flags=re.X):
fmt = m.group(1)
if startreg is not None:
val = ctu.reg(startreg)
startreg += 1
elif stack is not None:
val = ctu.read64(stack)
stack += 8
else:
val = values.pop(0)
if fmt == '%s':
args.append(ctu.readstring(val))
elif fmt == '%p':
args.append(val)
replacements.append((m.start(1), m.end(1), '%016x'))
elif fmt == '%llx':
args.append(val)
replacements.append((m.start(1), m.end(1), '%016x'))
else:
args.append(val)
replacements.reverse()
for s, e, r in replacements:
fmts = fmts[:s] + r + fmts[e:]
return fmts % tuple(args)

File diff suppressed because it is too large Load diff

63
wireprotocol.txt Normal file
View file

@ -0,0 +1,63 @@
Open session
- cmd: 0
- name: data
Response:
- handle: int
Close handle
- cmd: 1
- handle: int
Response:
none
IPC message
- cmd: 2
- request_type: int
- data_count: int
- data: int[data_count]
- copy_count: int
- copied_handles: int[copy_count]
- move_count: int
- moved_handles: int[move_count]
- a_count: int
- a_buffers:
- blob: data
- perms: int
- b_count: int
- b_buffers:
- blob: data
- perms: int
- c_count: int
- c_buffers:
- blob: data
- x_count: int
- x_buffers:
- blob: data
- counter: int
- handle: int
Response:
- error_code: int
- data_count: int
- data: int[data_count]
- copy_count: int
- copied_handles: int[copy_count]
- move_count: int
- moved_handles: int[move_count]
- a_count: int
- a_buffers:
- blob: data
- perms: int
- b_count: int
- b_buffers:
- blob: data
- perms: int
- c_count: int
- c_buffers:
- blob: data
- x_count: int
- x_buffers:
- blob: data
- counter: int
- response_type: int
NOTE: If error_code is nonzero, no fields will follow!