├── LICENSE ├── README.md ├── binutils ├── README.md ├── findmagic.py ├── getinfo.py ├── nm.py ├── nxo64.py └── strings.py ├── ipcclient ├── README.md ├── demangling.py ├── genidaclientscript.py ├── ipcclient.py ├── itanium_demangler.py └── nxo64.py ├── ipcserver ├── README.md ├── demangling.py ├── hashes.py ├── ipcserver_classic.py ├── ipcserver_modern.py ├── itanium_demangler.py ├── nxo64.py └── unicornhelpers.py ├── loader ├── README.md └── hthh_nxo64.py ├── pattern ├── README.md ├── applypattern.py ├── arm64.py ├── makepattern.py └── nxo64.py └── swipc-gen ├── README.md ├── data610.py ├── demangling.py ├── generatehashes.py ├── hashes.py ├── hashes_gen.py ├── ipcclient.py ├── ipcserver.py ├── itanium_demangler.py ├── nxo64.py └── unicornhelpers.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2019 hthh and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # switch-reversing 2 | 3 | These are my personal Switch reversing scripts. 4 | 5 | This is all a bit messy and chaotic, but I'm going to try to push my personal RE scripts and tools a bit more, since they can be pretty useful. Use at your own risk, no warranty, etc. 6 | 7 | * `swipc-gen` is code for automated extraction of IPC information used to generate these diffs: https://gist.github.com/hthh/bb896c743878a2c0c337f41febdc0426 8 | * `binutils` has simple utilities for dumping information from Switch binaries. 9 | * `pattern` is a pair of scripts for identifying and naming common library code in 64-bit binaries (where you have one file with the code with symbols, and another with the same code, but without symbols). 10 | * `ipcserver` contains scripts for automatically finding the implementations of IPC commands in sysmodule binaries. 11 | * `ipcclient` contains a script I use to generate vtable structures for IPC client code using IDA and the Hex-Rays decompiler. 12 | * `loader` contains my personal fork of the ReSwitched loader with a number of other useful features. -------------------------------------------------------------------------------- /binutils/README.md: -------------------------------------------------------------------------------- 1 | # binutils 2 | 3 | Useful scripts for inspecting at switch binaries. 4 | 5 | `nxo64.py` as always does the hard work of parsing the binaries, so each of the other scripts is under 100 lines and should be easy to modify if needed. 6 | 7 | 8 | ## getinfo.py 9 | 10 | This is my most used one. I use it to check an `sdk` version, check if it's 64-bit or not, check what library a `subsdk` binary is, or check what static libraries are linked into a game: 11 | 12 | ``` 13 | % python getinfo.py sdk 14 | File: sdk 15 | Module: nnSdk 16 | 64-bit 17 | FS SDK Version: 3.5.1 18 | Number of Symbols: 19850 19 | SDK MW+Nintendo+NintendoSDK_libz-3_5_1-Release 20 | SDK MW+Nintendo+NintendoSdk_nnSdk-3_5_1-Release 21 | SDK MW+Nintendo+NintendoSDK_NVN-3_5_1-Release 22 | 23 | % python getinfo.py main 24 | File: main 25 | Module: D:\home\Project\Blitz_MasterVer27\App\Rom\NX64\Product\code\Blitz.nss 26 | 64-bit 27 | Number of Symbols: 24 28 | SDK MW+Nintendo+PiaCommon-5_9_1 29 | SDK MW+Nintendo+Pia-5_9_1 30 | SDK MW+Nintendo+PiaInet-5_9_1 31 | SDK MW+Nintendo+PiaNat-5_9_1 32 | SDK MW+Nintendo+PiaLan-5_9_1 33 | SDK MW+Nintendo+PiaSession-5_9_1 34 | SDK MW+Nintendo+PiaTransport-5_9_1 35 | SDK MW+Nintendo+NEX-4_3_10-appblz 36 | SDK MW+Nintendo+NintendoSDK_libcurl-3_4_1-Release 37 | SDK MW+Nintendo+NintendoSDK_gfx-3_4_1-Release 38 | SDK MW+Nintendo+NintendoWare_G3d-3_4_1-Release 39 | SDK MW+Nintendo+NintendoWare_Vfx-3_4_1-Release 40 | SDK MW+Nintendo+NintendoWare_Atk-3_4_1-Release 41 | SDK MW+Nintendo+NintendoSDK_libz-3_4_1-Release 42 | SDK MW+Nintendo+NEX_MM-4_3_10 43 | SDK MW+Nintendo+NEX_DS-4_3_10 44 | SDK MW+Nintendo+NEX_RK-4_3_10 45 | SDK MW+Nintendo+NEX_UT-4_3_10 46 | SDK MW+Nintendo+NEX_CO-4_3_10 47 | SDK MW+Nintendo+PiaChat-5_9_1 48 | SDK MW+Nintendo+PiaClone-5_9_1 49 | SDK MW+Nintendo+PiaLocal-5_9_1 50 | SDK MW+Nintendo+NintendoWare_Ui2d-3_4_1-Release 51 | SDK MW+Nintendo+NintendoWare_Font-3_4_1-Release 52 | 53 | % python getinfo.py subsdk0 54 | File: subsdk0 55 | Module: libz 56 | 64-bit 57 | Number of Symbols: 66 58 | ``` 59 | 60 | ## findmagic.py 61 | 62 | Search for a 4-byte magic number or an error code in binaries. Often misses things, but can save a lot of time when it finds them. 63 | 64 | Note that you have to manually look for pairs with both halves of the value close together: 65 | 66 | ``` 67 | % python findmagic.py NCA3 3.0.0-10.0/* 68 | 3.0.0-10.0/fs(7100049E64): MOVK W9, 0x434E # 'NC' 69 | 3.0.0-10.0/fs(7100049E74): MOVK W9, 0x434E # 'NC' 70 | 3.0.0-10.0/fs(7100049E88): MOVK W9, 0x434E # 'NC' 71 | 3.0.0-10.0/fs(7100049E94): MOVZ W9, 0x33410000 # 'A3' 72 | 3.0.0-10.0/fs(7100049E98): MOVK W9, 0x434E # 'NC' 73 | 3.0.0-10.0/fs(710004BCF4): MOVZ W9, 0x33410000 # 'A3' 74 | 3.0.0-10.0/fs(710004BCF8): MOVK W9, 0x434E # 'NC' 75 | 76 | % python findmagic.py 0x234E02 3.0.0-10.0/fs 77 | 3.0.0-10.0/fs(7100018C50): MOVZ W21, 0x230000 # '#\x00' 78 | 3.0.0-10.0/fs(710001BC54): MOVZ W8, 0x230000 # '#\x00' 79 | 3.0.0-10.0/fs(7100036630): MOVZ W19, 0x230000 # '#\x00' 80 | 3.0.0-10.0/fs(710003727C): MOVZ W22, 0x230000 # '#\x00' 81 | 3.0.0-10.0/fs(7100038B34): MOVZ W26, 0x230000 # '#\x00' 82 | 3.0.0-10.0/fs(710003DBEC): MOVK W24, 0x4E02 # '\x02N' 83 | 3.0.0-10.0/fs(7100049E44): MOVZ W23, 0x230000 # '#\x00' 84 | 3.0.0-10.0/fs(710004A040): MOVZ W8, 0x230000 # '#\x00' 85 | 3.0.0-10.0/fs(710004A048): MOVK W8, 0x4E02 # '\x02N' 86 | [snip] 87 | ``` 88 | 89 | 90 | ## strings.py 91 | 92 | This dumps strings with a filename and address prefix. I dump strings from every binary from a version to one huge file and grep for whatever I'm looking for: 93 | 94 | ``` 95 | % python strings.py 4.0.1/*/main > 4.0.1-strings.txt 96 | % grep http 4.0.1-strings.txt 97 | 4.0.1/010000000000000C-bcat/main(71001E0C61): https://%s/api/nx/v1/list/%s 98 | 4.0.1/010000000000000C-bcat/main(71001E1349): https://%s/api/nx/v1/list/%s?l=%s%s%s 99 | 4.0.1/010000000000000C-bcat/main(71001E18C4): https://%s/api/nx/v1/titles/%016llx/topics?l=%s%s%s 100 | [snip] 101 | 4.0.1/0100000000001013-myPage/main(7100524878): # http://curl.haxx.se/docs/http-cookies.html 102 | 4.0.1/0100000000001013-myPage/main(710052908B): https://mii-secure-%%.cdn.nintendo.net/%s_normal_face.png 103 | 4.0.1/0100000000001013-myPage/main(7100583FA0): N2nn2sf4cmif6client6detail13CmifProxyImplINS_7account4http15IOAuthProcedureENS2_15CmifDomainProxyINS0_4hipc6client38Hipc2ClientSessionManagedProxyKindBaseINSA_18Hipc2ProxyKindBaseILNS9_6detail11MessageTypeE4EEEEEEENS0_25StatelessAllocationPolicyINS0_22ExpHeapStaticAllocatorILm2048ENS5_6detail12ObjectHolder24ObjectHolderAllocatorTagEEEEENS5_3nas40IOAuthProcedureForNintendoAccountLinkageEEE 104 | 4.0.1/0100000000001013-myPage/main(7100584170): N2nn7account4http15IOAuthProcedureE 105 | ``` 106 | 107 | 108 | ## nm.py 109 | 110 | Just lists symbols: 111 | 112 | ``` 113 | % python nm.py 3.0.0-10.0/fs 114 | DEFAULT NOTYPE LOCAL 0 0 115 | 00000000 T DEFAULT SECTION LOCAL 1 0 116 | DEFAULT NOTYPE WEAK 0 0 __rel_plt_start 117 | DEFAULT NOTYPE WEAK 0 0 __got_end 118 | DEFAULT NOTYPE WEAK 0 0 __rela_dyn_end 119 | DEFAULT NOTYPE WEAK 0 0 __rela_plt_end 120 | [snip] 121 | 00e887d0 B DEFAULT OBJECT WEAK 21 4030 _ZN2nn2sf22ExpHeapStaticAllocatorILm16384ENS_8settings22IFactorySettingsServerEE8_globalsE 122 | 00e8c840 B DEFAULT OBJECT GLOBAL 21 10 _ZN3nne7prfile212pf_allocatorE 123 | 00e8c850 B DEFAULT OBJECT GLOBAL 21 10 _ZN3nne7prfile214pf_deallocatorE 124 | 00e8c860 B DEFAULT OBJECT GLOBAL 21 490 _ZN3nne7prfile212pdm_disk_setE 125 | 00e8ccf0 B DEFAULT OBJECT GLOBAL 21 4 _ZN3nne7prfile210pf_sys_setE 126 | 00e8ce48 B DEFAULT OBJECT GLOBAL 21 8 __cxa_new_handler 127 | 00e8dda0 B DEFAULT OBJECT GLOBAL 21 4 __horizon_max_num_modules 128 | 00e8dda8 B DEFAULT OBJECT GLOBAL 21 8 nnmuslThreadPointerInitializer 129 | 00e8ddb0 B DEFAULT OBJECT GLOBAL 21 8 nnmuslThreadPointerDestroyer 130 | 00e8e000 ? DEFAULT NOTYPE GLOBAL 21 0 end 131 | ``` -------------------------------------------------------------------------------- /binutils/findmagic.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import struct 3 | import re 4 | from nxo64 import load_nxo 5 | 6 | def get_hw(d): return (d >> 21) & 3 7 | def get_imm16(d): return (d >> 5) & 0xFFFF 8 | 9 | def is_MOVZ_32_movewide(d): return (d & 0xff800000) == 0x52800000 10 | def is_MOVK_32_movewide(d): return (d & 0xff800000) == 0x72800000 11 | def get_Rd(d): return (d >> 0) & 0x1F 12 | 13 | def main(magicstr, filenames): 14 | for filename in filenames: 15 | find_magic(magicstr, filename) 16 | 17 | def find_magic_in_file(filename, text, data_as_string, f, magic): 18 | 19 | magic_high = magic >> 16 20 | magic_low = magic & 0xFFFF 21 | 22 | load_base = 0x7100000000 23 | 24 | for i in xrange(0, len(text), 4): 25 | d = struct.unpack_from('I'): 59 | magic = struct.unpack(fmt, magicstr)[0] 60 | find_magic_in_file(filename, text, data_as_string, f, magic) 61 | 62 | #print hex(magic) 63 | 64 | if __name__ == '__main__': 65 | main(sys.argv[1], sys.argv[2:]) -------------------------------------------------------------------------------- /binutils/getinfo.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import struct 3 | import re 4 | from nxo64 import load_nxo 5 | 6 | def main(filenames): 7 | for filename in filenames: 8 | print 'File:', filename 9 | with open(filename, 'rb') as li: 10 | f = load_nxo(li) 11 | 12 | path = None 13 | for off, end, name, class_ in f.sections: 14 | if name == '.rodata' and end-off < 0x1000 and end-off > 8: 15 | id_ = f.binfile.read_from(end-off, off) 16 | length = struct.unpack_from('