mirror of
https://github.com/reswitched/CageTheUnicorn.git
synced 2024-05-11 17:24:49 -04:00
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:
parent
9591e6978e
commit
72f548463d
5
LICENSE.txt
Normal file
5
LICENSE.txt
Normal 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
97
README.md
Normal 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
42
addtitle.sh
Normal 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
|
39
bundler.py
39
bundler.py
|
@ -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
10
color.sh
Normal 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
|
44
inlines.py
Normal file
44
inlines.py
Normal 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
707
ipc.py
Normal 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
213
ipcbase.py
Normal 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
211
ipcbridge.py
Normal 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
209
ipcclient.py
Normal 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
1548
ipcstubs.py
Normal file
File diff suppressed because it is too large
Load diff
28
main.py
28
main.py
|
@ -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
32
mergemaps.py
Normal 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
84
mmio.py
Normal 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
101
relocation.py
Normal 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
|
|
@ -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
10
skeletonSample/client.py
Normal 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
4
skeletonSample/load.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
nso:
|
||||
- main
|
||||
maps:
|
||||
Main: [0x7100000000, "main.map"]
|
14
skeletonSample/run.py
Normal file
14
skeletonSample/run.py
Normal 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
607
svc.py
|
@ -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
103
svcHelper.py
Normal 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
68
sync.py
Normal 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
137
systemsettings.yaml
Normal 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
162
threadmanager.py
Normal 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
3
uncolor.sh
Normal file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
cat colorme.idc | sed 's/0x00ff00)/0xffffff)/g;s/0xff00ff)/0xffffff)/g;' > uncolorme.idc
|
539
util.py
539
util.py
|
@ -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)
|
||||
|
|
19300
webkit_wkc.map
19300
webkit_wkc.map
File diff suppressed because it is too large
Load diff
63
wireprotocol.txt
Normal file
63
wireprotocol.txt
Normal 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!
|
Loading…
Reference in a new issue