├── LICENSE ├── Makefile ├── README.md ├── bin └── r2lldb ├── etc ├── lldb │ ├── ios │ ├── local │ ├── nonjb │ ├── remote │ └── watchos └── r2 │ ├── ios │ ├── local │ ├── nonjb │ ├── osx │ └── watchos ├── ios-debugserver ├── Makefile ├── README.md └── entitlements.plist ├── lint.py └── r2lldb ├── __init__.py ├── backend ├── __init__.py ├── bp.py ├── gdb │ ├── __init__.py │ ├── dbg.py │ └── loop.py ├── lldb │ ├── __init__.py │ ├── dbg.py │ └── loop.py └── trace.py └── r2rap.py /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Sergi Alvarez 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 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DESTDIR=/ 2 | PREFIX=/usr/local 3 | BINDIR=${PREFIX}/bin 4 | 5 | PYPATH=$(shell python -c 'import sys;print sys.path[-1]') 6 | LINT=import sys;import py_compile;py_compile.compile(sys.argv[0]) 7 | CWD=$(shell pwd) 8 | 9 | all: 10 | @echo 11 | -pkill r2 12 | -pkill debugserver 13 | -pkill lldb 14 | 15 | install: 16 | ln -fs "${CWD}/bin/r2lldb" "${DESTDIR}/${BINDIR}"/r2lldb 17 | rm -rf "${DESTDIR}/${PYPATH}"/r2lldb* 18 | mkdir -p "${DESTDIR}/${PYPATH}"/r2lldb* 19 | ln -fs "${CWD}/r2lldb" "${DESTDIR}/${PYPATH}/r2lldb" 20 | 21 | uninstall: 22 | rm -f ${DESTDIR}/${BINDIR}/r2lldb 23 | rm -rf ${DESTDIR}/${PYPATH}/r2lldb* 24 | 25 | lint: 26 | for a in `find r2lldb -iname '*.py'` ; do \ 27 | echo $$a ; \ 28 | python -c '${LINT}' $$a ; \ 29 | done 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | r2lldb 2 | ====== 3 | 4 | radare2-(lldb|gdb) integration 5 | 6 | Description 7 | ----------- 8 | 9 | This repository contains all the necessary scripts required to debug 10 | and manipulate anything running behind an LLDB the LLVM debugger from 11 | an interactive radare2 session. 12 | 13 | It has been developed for debugging iOS applications running on non 14 | jailbroken devices. And it has been tested on arm32, arm64, i386 15 | (simulator), and real OSX applications (x86-64). 16 | 17 | The `debugserver` required to debug on jailbroken devices can be 18 | obtained by using the `viatools/salvarez/ios-debugserver` repository. 19 | 20 | In theory it shuold be possible to use r2lldb on Linux and debug 21 | Android devices running gdbserver, but this hasn't been tested yet. 22 | 23 | Features 24 | -------- 25 | 26 | This is the list of features that r2lldb brings: 27 | 28 | * Debug self-signed apps non-jailbroken devices with a low level debugger 29 | * Trace calls to any address in memory 30 | * Show backtrace 31 | * List symbols 32 | * List memory regions allocated 33 | * List ObjC classes (using code injection) 34 | * Inject dynamic libraries 35 | * Step-in/over in r2's Visual mode 36 | * Set breakpoints on objc methods 37 | * Change process environment variables 38 | * Run r2 commands on every traced breakpoint 39 | 40 | Local example 41 | ------------- 42 | 43 | Using r2 as a frontend for local lldb is as easy as this: 44 | 45 | $ r2lldb /bin/ls 46 | 47 | Then in another terminal: 48 | 49 | $ r2lldb -R localhost:9999 50 | 51 | The same syntax applies to remote connections or crossplatform ones. 52 | 53 | 54 | Example 55 | ------- 56 | Start with `XCode` or `ios-deploy` an lldb session in the target process and type: 57 | 58 | (lldb) script del sys.modules["r2lldb"] # force module reload 59 | (lldb) script import r2lldb # start r2rap server 60 | 61 | 62 | Another Example 63 | --------------- 64 | 65 | Target: 66 | 67 | $ r2lldb -l 1234 /bin/ls 68 | 69 | or 70 | 71 | $ while : ; do debugserver *:1234 /bin/ls ; done 72 | Listening to port 1234 for a connection from *... 73 | Got a connection, launched process /bin/ls (pid = 82363). 74 | 75 | Host: Terminal 1 76 | 77 | $ r2lldb -c :1234 78 | 79 | $ while : ; do lldb -s lldb/local ; done 80 | Listening for r2rap connections at port 9999 81 | 82 | Host: Terminal 2 83 | 84 | $ r2lldb -r ios localhost:9999 85 | 86 | or 87 | 88 | $ r2 -i r2/ios rap://localhost:9999// 89 | 90 | r2lldb's help from r2 91 | --------------------- 92 | 93 | [0x00000000]> =!? 94 | Usage: =![cmd] ... # r2lldb integration 95 | =!? # show r2lldb's help (this one) 96 | =!help # show lldb's help 97 | =!i # target information 98 | =!is # list symbols 99 | =!dfv # show frame variables (arguments + locals) 100 | =!up,down,list # lldb's command to list select frames and show source 101 | =!dks # stop debugged process 102 | =!dm # show maps (image list) 103 | =!dr # show registers 104 | =!dra # show all registers 105 | =!dr* # "" "" in r2 commands 106 | =!dr-* # remove all breakpoints 107 | =!db # list breakpoints 108 | =!db 0x12924 # set breakpoint at address 109 | =!db objc_msgSend # set breakpoint on symbol 110 | =!dbo NSString init: # set objc breakpoint 111 | =!dbt # show backtrace 112 | =!ds # step 113 | =!dcue # continue until entrypoint 114 | =!dso # step over 115 | =!dt # list all trace points 116 | =!dt 0x804040 =!dr # add tracepoint for this address 117 | =!dc # continue 118 | =!dct # continue with tracing 119 | =!env # show process environment 120 | =!objc # list all objc classes 121 | =!setenv k v # set variable in target process 122 | =!dlopen /path/to/lib # dlopen lib (libr2.so, frida?) 123 | 124 | GDB notes 125 | --------- 126 | 127 | Recently, this project got the ability to use `gdb` as target for r2, 128 | so the project name will probably change to make this clear. 129 | 130 | In order to run r2lldb in `ndk-gdb` you may do the following: 131 | 132 | $ prebuilt/darwin-x86_64/bin/gdb 133 | (gdb) python-interactive 134 | >>> import sys 135 | >>> sys.path.append('/path/to/r2lldb') 136 | >>> sys.path.append('/path/to/r2lldb/r2lldb') 137 | >>> sys.path.append('/path/to/r2lldb/r2lldb/backend/gdb/') 138 | >>> sys.path.append('/path/to/radare2-r2pipe/python/') 139 | >>> import r2lldb 140 | 141 | In another terminal: 142 | 143 | $ r2 rap://localhost:9999 144 | 145 | Android 146 | ------- 147 | 148 | To debug Android apps in the target device you will need to find a copy of the `gdbserver` binary. which can be found in Termux if you install the `gdb` package, but also, it can be found inside the NDK under the `prebuilt/` directory. 149 | 150 | iOS 151 | --- 152 | 153 | The `debugserver` is deployed by XCode in the debugger image. If you have a jailbroken device you can just extract this binary from the dmg and copy it via SSH. 154 | 155 | --pancake 156 | -------------------------------------------------------------------------------- /bin/r2lldb: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | R2LLDB_VERSION="0.3" 4 | 5 | ME=`dirname $0`/r2lldb 6 | LN=`readlink $ME` 7 | if [ -n "${LN}" ]; then 8 | CWD=`dirname ${LN}`/.. 9 | else 10 | CWD=`dirname ${ME}`/.. 11 | fi 12 | export PYTHONPATH="${CWD}" 13 | 14 | Help() { 15 | echo "Usage: r2lldb [-vh] [-l[l] port] [-R] [[-p[p],-c] t] [-|pid|program|host:port]" 16 | if [ -n "$1" ]; then 17 | echo " -h show this help message" 18 | echo " -l [port] start debugserver" 19 | echo " -ll [port] start debugserver forever" 20 | echo " -p t [h:p] proxy lldb-debugserver and listen for r2lldb" 21 | echo " -pp t [h:p] lldb->r2 like above, but forever" 22 | echo " -r t [h:p] connect r2 to an r2lldb rap server (t=ios,osx)" 23 | echo " -R connect to rap://localhost:9999" 24 | echo " -v show version information" 25 | echo " - python oneliner to run r2lldb server in lldb" 26 | echo "Example:" 27 | echo " (lldb) script import r2lldb # in lldb shell to start r2rap server" 28 | echo " target$ r2lldb -l 1234 /bin/ls # local debugserver" 29 | echo " host$ r2lldb -p ios localhost:1234 # proxy between debugserver and r2rap" 30 | echo " host$ r2lldb -r ios localhost:9999 # connect to the rap server" 31 | echo "Local:" 32 | echo " host$ export PYTHONPATH=$CWD" 33 | echo " host$ r2lldb /bin/ls # start program and wait for r2rap connection" 34 | echo " host$ r2lldb -R # connect to localhost:9999 rap server" 35 | exit 0 36 | fi 37 | exit 1 38 | } 39 | 40 | ControlC() { 41 | exit 1 42 | } 43 | 44 | DEBUGSERVER="" 45 | 46 | FindDebugServer() { 47 | DEBUGSERVER="/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/debugserver" 48 | if [ -x "${DEBUGSERVER}" ]; then 49 | return 0 50 | fi 51 | FS=: 52 | for a in ${PATH} ; do 53 | DEBUGSERVER="$a/debugserver" 54 | if [ -x "${DEBUGSERVER}" ]; then 55 | return 0 56 | fi 57 | done 58 | DEBUGSERVER="" 59 | return 1 60 | } 61 | 62 | case "$1" in 63 | '-v') 64 | echo "${R2LLDB_VERSION}" 65 | ;; 66 | '-ll') 67 | shift 68 | while : ; do 69 | $0 -l $* 70 | sleep 1 71 | done 72 | ;; 73 | '-l') 74 | if [ -z "$2" ]; then 75 | echo "Missing -l port" 76 | exit 1 77 | fi 78 | if [ -z "$3" ]; then 79 | echo "Missing bin" 80 | exit 1 81 | fi 82 | FindDebugServer 83 | if [ -z "${DEBUGSERVER}" ]; then 84 | echo "Cannot find debugserver" 85 | else 86 | echo "LISTEN $2" 87 | test "$3" -gt 0 > /dev/null 2>&1 88 | if [ $? = 0 ]; then 89 | echo "ATTACH $3" 90 | ${DEBUGSERVER} "*:$2" --attach="$3" 91 | else 92 | echo "LAUNCH $3" 93 | echo ${DEBUGSERVER} "*:$2" "$3" 94 | ${DEBUGSERVER} "*:$2" "$3" 95 | fi 96 | if [ $? = 0 ]; then 97 | echo "DebugServer error $?" 98 | fi 99 | fi 100 | ;; 101 | '-h') 102 | Help 1 103 | ;; 104 | '-') 105 | echo "script import sys;sys.path.append('${CWD}');import r2lldb" 106 | echo or 107 | echo "script import r2lldb" 108 | ;; 109 | '-pp') 110 | shift 111 | while : ; do 112 | $0 -p $* 113 | sleep 1 114 | done 115 | ;; 116 | '-p') 117 | trap ControlC 2 118 | C="lldb" 119 | case "$2" in 120 | ios) 121 | C="$C -o 'platform select remote-ios'" 122 | ;; 123 | osx|local|unix|linux) 124 | : # nothing 125 | ;; 126 | *) 127 | echo "Unknown remote target $2" 128 | (cd $CWD/etc/lldb ; ls | cat) 129 | exit 1 130 | ;; 131 | esac 132 | if [ -z "$3" ]; then 133 | echo "Missing host:port to connect" 134 | exit 1 135 | else 136 | C="$C -o 'process connect connect://"$3"'" 137 | fi 138 | C="$C -o 'command script import r2lldb' -o quit" 139 | echo $C 140 | eval $C 141 | ;; 142 | '-R') 143 | $0 -r local localhost:9999 144 | ;; 145 | '-r') 146 | echo "Launching r2 to connect to rap:// lldb" 147 | if [ -z "$2" ]; then 148 | echo "Missing argument. Use:" 149 | (cd $CWD/etc/r2 ; ls | cat) 150 | exit 1 151 | fi 152 | if [ -z "$3" ]; then 153 | HOST="localhost:9999" 154 | else 155 | HOST="$3" 156 | fi 157 | if [ -f "$CWD/etc/r2/$2" ]; then 158 | r2 -wi "$CWD/etc/r2/$2" rap://"$HOST"// 159 | else 160 | r2 -wn rap://"$3"// 161 | fi 162 | ;; 163 | '') 164 | Help 165 | ;; 166 | *) 167 | if [ -n "`echo '$1' | grep :`" ]; then 168 | echo "Connecting to remote lldb..." 169 | else 170 | echo "Open program: $* .." 171 | lldb -s ${CWD}/etc/lldb/local $* 172 | fi 173 | shift 174 | exit 0 175 | ;; 176 | esac 177 | -------------------------------------------------------------------------------- /etc/lldb/ios: -------------------------------------------------------------------------------- 1 | platform select remote-ios 2 | process connect connect://192.168.1.34:1234 3 | script del sys.modules["r2lldb"] 4 | script import r2lldb 5 | quit 6 | -------------------------------------------------------------------------------- /etc/lldb/local: -------------------------------------------------------------------------------- 1 | process launch --stop-at-entry 2 | script del sys.modules["r2lldb"] 3 | script import r2lldb 4 | quit 5 | -------------------------------------------------------------------------------- /etc/lldb/nonjb: -------------------------------------------------------------------------------- 1 | platform select remote-ios 2 | process connect connect://127.0.0.1:60109 3 | script del sys.modules["r2lldb"] 4 | script import r2lldb 5 | quit 6 | -------------------------------------------------------------------------------- /etc/lldb/remote: -------------------------------------------------------------------------------- 1 | process connect connect://localhost:1234 2 | script del sys.modules["mod"] 3 | script import r2lldb 4 | quit 5 | -------------------------------------------------------------------------------- /etc/lldb/watchos: -------------------------------------------------------------------------------- 1 | platform select remote-watchos 2 | process connect connect://192.168.1.34:1234 3 | script del sys.modules["r2lldb"] 4 | command script import r2lldb 5 | quit 6 | -------------------------------------------------------------------------------- /etc/r2/ios: -------------------------------------------------------------------------------- 1 | # r2lldb script for iPhone 2 | e asm.os=darwin 3 | e asm.arch=arm 4 | e asm.bits=64 5 | e asm.cmt.col=50 6 | e asm.bytes=false 7 | e key.f6=$s 8 | e key.f7=$so 9 | (regs,.=!dr*,.dr*) 10 | (step,=!ds,.=!dr*,.dr*) 11 | "$s=.(step) 2>/dev/null" 12 | "$so==!dso;.(regs)" 13 | $r=.(regs) 14 | $b==!db 15 | $bt==!dbt 16 | $m==!dm 17 | $c==!dc 18 | $c==!dc 19 | $v==!dfv 20 | $t==!dt 21 | -------------------------------------------------------------------------------- /etc/r2/local: -------------------------------------------------------------------------------- 1 | # r2lldb script for OSX 2 | e asm.os=darwin 3 | e asm.arch=x86 4 | e asm.bits=64 5 | e asm.cmt.col=50 6 | e asm.bytes=false 7 | e key.f6=$s 8 | e key.f7=$so 9 | (regs,.=!dr*,.dr*) 10 | (step,=!ds,.=!dr*,.dr*) 11 | "$s=.(step) 2>/dev/null" 12 | "$so==!dso;.(regs)" 13 | =$dr 14 | =$dm 15 | =$ds 16 | =$db 17 | =$dt 18 | e cmd.prompt=.dr* 19 | =$dc 20 | $r=.(regs) 21 | $b==!db 22 | $bt==!dbt 23 | $m==!dm 24 | $c==!dc 25 | $c==!dc 26 | $v==!dfv 27 | $t==!dt 28 | $ta==!dta 29 | # Load stuff 30 | ?e [r2lldb] Loading symbols... 31 | .=!is 32 | ?e [r2lldb] Loading registers... 33 | .=!dr* 34 | .dr* 35 | sr pc 36 | # "e key.s=$s;$r" 37 | # pid 18 38 | # $b `?v $$+0xa` 39 | # $t `?v $$+0xa` 40 | # $b `?v $$+0x15` 41 | # =!dct 42 | -------------------------------------------------------------------------------- /etc/r2/nonjb: -------------------------------------------------------------------------------- 1 | # r2lldb script for iPhone 2 | e asm.os=darwin 3 | e asm.arch=arm 4 | e asm.bits=64 5 | e asm.cmt.col=50 6 | e asm.bytes=false 7 | e key.f6=$s 8 | e key.f7=$so 9 | (regs,.=!dr*,.dr*) 10 | (step,=!ds,.=!dr*,.dr*) 11 | "$s=.(step) 2>/dev/null" 12 | "$so==!dso;.(regs)" 13 | $r=.(regs) 14 | $b==!db 15 | $bt==!dbt 16 | $m==!dm 17 | $c==!dc 18 | $c==!dc 19 | $v==!dfv 20 | $t==!dt 21 | -------------------------------------------------------------------------------- /etc/r2/osx: -------------------------------------------------------------------------------- 1 | # r2lldb script for OSX 2 | e asm.os=darwin 3 | e asm.arch=x86 4 | e asm.bits=64 5 | e asm.cmt.col=50 6 | e asm.bytes=false 7 | e key.f6=$s 8 | e key.f7=$so 9 | (regs,.=!dr*,.dr*) 10 | (step,=!ds,.=!dr*,.dr*) 11 | "$s=.(step) 2>/dev/null" 12 | "$so==!dso;.(regs)" 13 | =$dr 14 | =$dm 15 | =$ds 16 | =$db 17 | =$dt 18 | e cmd.prompt=.dr* 19 | =$dc 20 | $r=.(regs) 21 | $b==!db 22 | $bt==!dbt 23 | $m==!dm 24 | $c==!dc 25 | $c==!dc 26 | $v==!dfv 27 | $t==!dt 28 | $ta==!dta 29 | # Load stuff 30 | ?e [r2lldb] Loading symbols... 31 | .=!is 32 | ?e [r2lldb] Loading registers... 33 | .=!dr* 34 | .dr* 35 | sr pc 36 | # "e key.s=$s;$r" 37 | # pid 18 38 | # $b `?v $$+0xa` 39 | # $t `?v $$+0xa` 40 | # $b `?v $$+0x15` 41 | # =!dct 42 | -------------------------------------------------------------------------------- /etc/r2/watchos: -------------------------------------------------------------------------------- 1 | # r2lldb script for iPhone 2 | e asm.os=darwin 3 | e asm.arch=arm 4 | e asm.bits=32 5 | e asm.cmt.col=50 6 | e asm.bytes=false 7 | e key.f6=$s 8 | e key.f7=$so 9 | (regs,.=!dr*,.dr*) 10 | (step,=!ds,.=!dr*,.dr*) 11 | "$s=.(step) 2>/dev/null" 12 | "$so==!dso;.(regs)" 13 | $r=.(regs) 14 | $b==!db 15 | $bt==!dbt 16 | $m==!dm 17 | $c==!dc 18 | $c==!dc 19 | $v==!dfv 20 | $t==!dt 21 | -------------------------------------------------------------------------------- /ios-debugserver/Makefile: -------------------------------------------------------------------------------- 1 | OSXDBGS=/Applications/Xcode.app/Contents//SharedFrameworks/LLDB.framework/Versions/A/Resources/debugserver 2 | SDKROOT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport 3 | DISKIMG=/Volumes/DeveloperDiskImage 4 | 5 | all: 6 | @echo 7 | @echo "make ls # list all available versions" 8 | @echo "make debugserver IOS=8.1 # build debugserver for iOS 8.1" 9 | @echo 10 | 11 | ios ls: 12 | @cd $(SDKROOT) ; ls 13 | 14 | check: 15 | @if [ -z "$(IOS)" ]; then echo "IOS=xxx required" ; false ; fi 16 | 17 | mount: 18 | hdiutil detach $(DISKIMG) || true 19 | hdiutil attach $(SDKROOT)/$(IOS)*/DeveloperDiskImage.dmg 20 | 21 | debugserver: check 22 | hdiutil detach $(DISKIMG) || true 23 | hdiutil attach $(SDKROOT)/$(IOS)*/DeveloperDiskImage.dmg 24 | cp $(DISKIMG)/usr/bin/debugserver . 25 | codesign -s - --entitlements entitlements.plist -f debugserver 26 | lipo -extract armv7 debugserver -o debugserver-$(IOS)-armv7 27 | lipo -extract arm64 debugserver -o debugserver-$(IOS)-arm64 28 | rm debugserver 29 | hdiutil detach $(DISKIMG) 30 | $(MAKE) debugserver-x86_64 31 | 32 | debugserver-x86_64: 33 | cp -f $(OSXDBGS) debugserver-x86_64 34 | -------------------------------------------------------------------------------- /ios-debugserver/README.md: -------------------------------------------------------------------------------- 1 | LLDB debugserver for iOS 2 | ======================== 3 | 4 | This makefile will extract the `debugserver` binary for iOS from 5 | the Apple Developer Disk Image and process it to make it usable 6 | for remote debugging on jailbroken devices. 7 | 8 | For more information see http://iphonedevwiki.net/index.php/Debugserver 9 | 10 | Usage 11 | ----- 12 | 13 | List all available iOS targets: 14 | 15 | $ make ls 16 | 4.2 5.1 7.0 8.1 17 | 4.3 6.0 7.1 8.2 18 | 5.0 6.1 8.0 8.3 19 | 20 | 21 | Build debugserver from specific iOS version: 22 | 23 | $ make debugserver IOS=8.1 24 | hdiutil detach /Volumes/DeveloperDiskImage || true 25 | hdiutil: detach failed - No such file or directory 26 | hdiutil attach /Applications/Xcode.app/.../8.1*/DeveloperDiskImage.dmg 27 | Checksumming whole disk (Apple_HFS : 0)… 28 | ................................................................... 29 | whole disk (Apple_HFS : 0): verified CRC32 $1718D76D 30 | verified CRC32 $03C50CCF 31 | /dev/disk3 /Volumes/DeveloperDiskImage 32 | cp /Volumes/DeveloperDiskImage/usr/bin/debugserver . 33 | codesign -s - --entitlements entitlements.plist -f debugserver 34 | debugserver: replacing existing signature 35 | lipo -extract armv7 debugserver -o debugserver-8.1-armv7 36 | lipo -extract arm64 debugserver -o debugserver-8.1-arm64 37 | rm debugserver 38 | hdiutil detach /Volumes/DeveloperDiskImage 39 | "disk3" unmounted. 40 | "disk3" ejected. 41 | 42 | Installation 43 | ------------ 44 | 45 | At this point you'll get two binaries named as `debugserver-8.1-armv7` and `debugserver-8.1-arm64`. 46 | 47 | $ scp debugserver-8.1-arm64 root@192.168.1.35:. 48 | 49 | Meanwhile in the iDevice: 50 | 51 | # ./debugserver-8.1-armv7 *:1234 -a Calculator 52 | 53 | In the host run the following lines: 54 | 55 | $ lldb 56 | (lldb) platform select remote-ios 57 | (lldb) process connect connect://192.168.1.35:1234 58 | 59 | Final notes 60 | ----------- 61 | 62 | Note that the LLDB disassembler misses a *LOT* of instructions, so I would recommend using `radare2`, `r2pipe` or `lldb-capstone-arm`: 63 | 64 | https://github.com/upbit/lldb-capstone-arm 65 | 66 | Enjoy 67 | -------------------------------------------------------------------------------- /ios-debugserver/entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.springboard.debugapplications 6 | 7 | run-unsigned-code 8 | 9 | get-task-allow 10 | 11 | task_for_pid-allow 12 | 13 | com.apple.backboardd.debugapplications 14 | 15 | com.apple.backboardd.launchapplications 16 | 17 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /lint.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import py_compile 3 | py_compile.compile(sys.argv[0]) 4 | -------------------------------------------------------------------------------- /r2lldb/__init__.py: -------------------------------------------------------------------------------- 1 | # http://www.objc.io/issue-19/lldb-debugging.html 2 | 3 | 4 | # (lldb) 5 | # command script clear 6 | # command script import /Users/pancake/prg/nowsecure/r2lldb/main.py 7 | 8 | from r2rap import RapServer 9 | try: 10 | from backend.lldb import dbg 11 | from backend.lldb import loop 12 | except: 13 | from backend.gdb import dbg 14 | from backend.gdb import loop 15 | from backend import trace 16 | try: 17 | from exceptions import * 18 | except: 19 | pass 20 | import traceback 21 | import r2pipe 22 | import sys 23 | import re 24 | 25 | def run_script(file): 26 | execfile(file) 27 | 28 | def rap(debugger, command, result, dict): 29 | def r2cmd(c): 30 | print ("_____(%s)___"%c) 31 | if c == "": 32 | return 33 | if c[0:2] == ". ": 34 | return run_script(c[2:]) 35 | if c[0] == ":": 36 | return dbg.cmd(c[1:]) 37 | if c == "q": 38 | print ("STOP") 39 | rs.stop() 40 | return "OK" 41 | elif c[0:7] == "setenv ": 42 | a = c[7:].strip().split(" ",1) 43 | return dbg.setenv(a[0],a[1]) 44 | elif c == "env": 45 | return dbg.cmd("print $environ") 46 | elif c[0:7] == "dlopen ": 47 | return dbg.dlopen(a[7:].strip()) 48 | elif c[0:2] == "o ": 49 | return dbg.cmd("target create %s"%c[2:]) 50 | elif c[0] == "o": 51 | return "TODO: show target" 52 | elif c == "objc": 53 | return dbg.objcListClasses() 54 | elif c == "run": 55 | return dbg.cmd("run") 56 | elif c[0:5] == "call ": 57 | return dbg.cmd("call "+ c[4:]) 58 | elif c[0:5] == "lldb ": 59 | return dbg.cmd(c[5:]) 60 | elif c[0:4] == "gdb ": 61 | return dbg.cmd(c[4:]) 62 | elif c == "dc": 63 | return dbg.cont() 64 | elif c == "ds": 65 | return dbg.cmd("stepi") 66 | elif c == "dso": 67 | return dbg.cmd("next") # thread step-over 68 | elif c == "dbt": 69 | res = '' 70 | for a in dbg.frames(): 71 | line = "%d %s %s %s\n"%(a['index'], a['addr'], a['file'], a['meth']) 72 | res = res + line 73 | return res 74 | elif c == "i": 75 | print ("NAME ERR") 76 | #if dbg.isThumb(): 77 | # s = s + "e asm.bits=16 # thumb\n" 78 | # TODO 79 | #(lldb) target list 80 | #Current targets: 81 | #* target #0: path-to-bin ( arch=i386-apple-ios, platform=ios-simulator, pid=21617, state=stopped ) 82 | try: 83 | return cmd("target list") 84 | except: 85 | return "cmd(target list)\n" 86 | elif c == "dbc": 87 | return "TODO: dbc" 88 | elif c[0:3] == "dt ": 89 | try: 90 | args = c[3:].split(' ', 1) 91 | if len(args)>1: 92 | if trace.add (args[0], args[1]): 93 | return "Trace added" 94 | else: 95 | if not trace.add(args[0], "?e trace"): 96 | return "Trace add fail" 97 | except: 98 | return "Trace exception" 99 | return "" 100 | elif c == "dT": 101 | return loop.listTracePoints() 102 | elif c[0:3] == "dT ": 103 | return loop.setTracePoint(c[4:]) 104 | elif c == "dT?": 105 | return """Usage: 106 | dT list all debugloop traces 107 | dT- remove all tracepoints 108 | dT A add specific address for tracing 109 | dTc run/continue into the debugloop 110 | """ 111 | elif c == "dTc": 112 | return loop.runLoop() 113 | elif c == "quit": 114 | rs.disconnect() 115 | del sys.modules["r2lldb"] 116 | return "Disconnected. Please Quit\n" 117 | elif c[0:3] == "ls ": 118 | return dbg.system_ls(c[3:]) 119 | elif c == "ls": 120 | return dbg.system_ls(".") 121 | elif c[0:4] == "cat ": 122 | return dbg.system_cat(c[4:]) 123 | elif c == "cat": 124 | return "cat: Missing file" 125 | elif c[0:5] == "head ": 126 | return dbg.system_cat(c[4:], True) 127 | elif c == "head": 128 | return "head: Missing file" 129 | elif c == "dt": 130 | return trace.list() 131 | elif c == "dcta": 132 | print(s) 133 | return "Set 0 traces" 134 | elif c == "dct": 135 | while True: 136 | try: 137 | dbg.cmd("continue") 138 | pc = dbg.getRegister("pc") 139 | if pc == '0': 140 | break 141 | t = None 142 | try: 143 | t = trace.get(pc) 144 | except: 145 | pass 146 | if not t: 147 | print ("Address not traced",pc) 148 | break 149 | rs.system(t) 150 | except e: 151 | print(e) 152 | traceback.print_stack() 153 | return "Exception happens" 154 | print ("Trace Done") 155 | return "Trace Done" 156 | elif c == "dks": 157 | dbg.stop() 158 | elif c == "is": 159 | syms = dbg.symbols() 160 | symbols = "" 161 | for a in syms: 162 | name = a['name'] 163 | # XXX: filter flag name 164 | name = name.replace("'",'_') 165 | name = name.replace(' ','_') 166 | name = name.replace(' ','_') 167 | name = name.replace('-','_') 168 | name = name.replace('~','_') 169 | name = name.replace('+','_') 170 | name = name.replace('$','_') 171 | name = name.replace('&','_') 172 | name = name.replace('@','_') 173 | name = name.replace('|','_') 174 | name = name.replace('%','_') 175 | name = name.replace(';','_') 176 | name = name.replace('!','_') 177 | name = name.replace('`','_') 178 | name = name.replace(',','_') 179 | name = name.replace('/','_') 180 | name = name.replace('*','_') 181 | name = name.replace('(','_') 182 | name = name.replace(')','_') 183 | name = name.replace('[','_') 184 | name = name.replace(']','_') 185 | name = name.replace('<','_') 186 | name = name.replace('>','_') 187 | # TODO: many symbols are defined multiple times 188 | if name[0:2]!='0x': 189 | line = "f sym.%s = %s\n"%(name,a['addr']) 190 | symbols = symbols + line 191 | return symbols 192 | elif c == "db-*": 193 | return dbg.bp_clear() 194 | elif c[0:5] == "db 0x": 195 | return dbg.bp_addr(c[3:]) 196 | elif c[0:3] == "db ": 197 | return dbg.bp_symbol(c[3:]) 198 | elif c[0:4] == "dbo ": 199 | a = c[4:].strip().split(' ') 200 | if len(a) != 2: 201 | return "Usage: dbo OBJCLASS OBJCMETHOD" 202 | return dbg.bp_obj(a[0], a[1]) 203 | elif c == "db": 204 | bps = dbg.bp_list() 205 | n = 0 206 | out = '' 207 | for a in bps: 208 | line = ("%d %s %s\n"%(n, a['type'], a[a['type']])) 209 | n = n + 1 210 | out = out + line 211 | #print(dbg.bp_list()) 212 | return out + "\nFound %d breakpoints"%n 213 | #dbg.cmd("break list") 214 | elif c == "dm?": 215 | return """Usage: dm" 216 | dm list maps 217 | dm [addr] show address information 218 | """ 219 | elif c == "dm": 220 | return dbg.cmd('image list') 221 | elif c[0:3] == "dm ": 222 | return dbg.cmd('image lookup --address %s'%c[4:]) 223 | elif c == "dfv": 224 | return dbg.cmd("fr v") # -a 225 | elif c == "dcue": 226 | return dbg.run_to_entry() 227 | elif c == "dr=": 228 | try: 229 | s = "" + dbg.cmd("reg read") 230 | nl = s.find("\n") 231 | if nl != -1: 232 | s = s[nl+1:] 233 | res = "" 234 | col = 0 235 | while True: 236 | nl = s.find("\n") 237 | if nl == -1: 238 | break 239 | #s = s.replace("\n", "") + "\n" 240 | line = s[0:nl] 241 | col = col + 1 242 | res = res + line 243 | if col>1: 244 | col = 0 245 | res = res + "\n" 246 | s = s[nl+1:] 247 | s = res 248 | #s = s.split("\n").join(" ") 249 | return s #s.split("\n").join(" ") + "\n" 250 | except: 251 | print("ERRER") 252 | return "ERROR" 253 | elif c == "dr": 254 | return dbg.cmd('reg read') 255 | elif c == "dra": 256 | return dbg.cmd('reg read -a') 257 | elif c == "dr*": 258 | regs = dbg.cmd("reg read").strip().split("\n") 259 | res = "" 260 | for a in regs: 261 | a = a.strip() 262 | if a.find(" = ") == -1: 263 | next 264 | mo = re.match( r'(.*) = ([^ ]*)', a , re.M|re.I) 265 | if mo: 266 | line = "f %s = %s\n"%(mo.group(1), mo.group(2)) 267 | line = line + "ar %s = %s\n"%(mo.group(1), mo.group(2)) 268 | res = res + line 269 | #regs = dbg.getRegister("pc") 270 | return res 271 | elif c == "?": 272 | return """Usage: =![cmd] ... # r2lldb integration 273 | =!? # show r2lldb's help (this one) 274 | =!help # show lldb's help 275 | =!i # target information 276 | =!is # list symbols 277 | =!dfv # show frame variables (arguments + locals) 278 | =!ls [path] # list files from remote device 279 | =!cat [path] # show contents of file 280 | =!call (int)getuid() # inject and run code in target process 281 | =!lldb ..command.. # run lldb command 282 | =!up,down,list # lldb's command to list select frames and show source 283 | =!dks # stop debugged process 284 | =!dm # show maps (image list) 285 | =!dr # show registers 286 | =!dra # show all registers 287 | =!dr* # "" "" in r2 commands 288 | =!dr-* # remove all breakpoints 289 | =!db # list breakpoints 290 | =!db 0x12924 # set breakpoint at address 291 | =!db objc_msgSend # set breakpoint on symbol 292 | =!dbo NSString init: # set objc breakpoint 293 | =!dbt # show backtrace 294 | =!ds # step 295 | =!dcue # continue until entrypoint 296 | =!dso # step over 297 | =!dt # list all trace points 298 | =!dt 0x804040 =!dr # add tracepoint for this address 299 | =!dc # continue 300 | =!dct # continue with tracing 301 | =!env # show process environment 302 | =!objc # list all objc classes 303 | =!setenv k v # set variable in target process 304 | =!dlopen /path/to/lib # dlopen lib (libr2.so, frida?) 305 | =!quit # quit r2lldb server loop 306 | """ 307 | return None 308 | port = int(command) 309 | rs = RapServer() 310 | def __read(sz): 311 | return dbg.read(rs.offset, sz) 312 | def __write(buf): 313 | return dbg.write(rs.offset, buf) 314 | def __seek(off,when): 315 | if when == 2: 316 | return 0xffffffffffffffff 317 | rs.offset = off 318 | return dbg.seek(off, when) 319 | def __cmd(c): 320 | c = c[0:len(c)-1].strip() 321 | res = r2cmd(c) 322 | if res: 323 | return res 324 | return dbg.cmd(c) 325 | rs.handle_system = __cmd 326 | rs.handle_cmd = __cmd 327 | rs.handle_read = __read 328 | rs.handle_write = __write 329 | rs.handle_seek = __seek 330 | rs.listen_tcp (port) 331 | 332 | def handle_signal(): 333 | import signal 334 | import sys 335 | def signal_handler(signal, frame): 336 | print('') 337 | # TODO: close rap server here 338 | sys.exit(0) 339 | 340 | signal.signal(signal.SIGINT, signal_handler) 341 | #signal.pause() 342 | #handle_signal() 343 | 344 | PORT = "9999" 345 | 346 | # sys.argv not defined inside lldbb 347 | def main(): 348 | try: 349 | rap(0, PORT, "", "") 350 | except SystemExit: 351 | pass 352 | except: 353 | print("Unexpected error:", sys.exc_info()[0]) 354 | print("Rap exception cannot listen") 355 | 356 | # Register r2rap command in the lldb shell 357 | #def __lldb_init_module (debugger, dict): 358 | # debugger.HandleCommand('command script add -f main.rap r2rap') 359 | #print 'The r2rap command has been installed' 360 | 361 | main() 362 | -------------------------------------------------------------------------------- /r2lldb/backend/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nowsecure/r2lldb/29cf97bf96f68d5a78060ea63504112b4a2fb3df/r2lldb/backend/__init__.py -------------------------------------------------------------------------------- /r2lldb/backend/bp.py: -------------------------------------------------------------------------------- 1 | class Breakpoint: 2 | # - 3 | def __init__(self): 4 | a = None 5 | 6 | def add(): 7 | return False 8 | 9 | def list(): 10 | return False 11 | 12 | def isBreakAddr(x): 13 | return False 14 | -------------------------------------------------------------------------------- /r2lldb/backend/gdb/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nowsecure/r2lldb/29cf97bf96f68d5a78060ea63504112b4a2fb3df/r2lldb/backend/gdb/__init__.py -------------------------------------------------------------------------------- /r2lldb/backend/gdb/dbg.py: -------------------------------------------------------------------------------- 1 | try: 2 | import gdb 3 | except: 4 | print("") 5 | print("ERROR: import gdb only works in the gdb shell") 6 | print("") 7 | raise 8 | 9 | import traceback 10 | 11 | 12 | # gdb.find_pc_line (pc) 13 | 14 | def cont(): 15 | return cmd("continue") 16 | 17 | def run_to_entry(): 18 | return cmd("run") 19 | 20 | def system_cat(path, head=False): 21 | ret = "" 22 | try: 23 | fd = runCode('(void*)open((char*)"'+path+'",0);') 24 | print ("FD "+fd) 25 | if int(fd,16) == 4294967295: 26 | return "Cannot open file" 27 | buf = runCode('(void*)malloc((int)10240)') 28 | print("open("+buf+")") 29 | i = 0 30 | while True: 31 | de = runCode('(int)read((int)'+str(fd)+',(void*)'+str(buf)+',(int)1024);'); 32 | count = int (de, 16) 33 | print ("read("+str(i)+")="+str(count)) 34 | try: 35 | data = read(int(buf,16), count) 36 | ret = ret + str(data) 37 | except: 38 | traceback.print_last() 39 | ret = ret + ".\n" 40 | if count != 1024 and count != 0x1024: 41 | break 42 | if head: 43 | break 44 | i = i + 1 45 | except: 46 | traceback.print_last() 47 | ret = ret + "ERR\n"; 48 | cmd('e (void)free((void*)'+buf+')') 49 | cmd('e (int)close((int)'+fd+')') 50 | return ret 51 | 52 | def system_ls(path): 53 | ret = "" 54 | print ("LS("+path+")") 55 | try: 56 | ptr = runCode('(void*)opendir((char*)"'+path+'");') 57 | if int(ptr,16) == 0: 58 | return "Cannot find directory" 59 | print("opendir("+ptr+")") 60 | while True: 61 | de = runCode('(void*)readdir((void*)'+ptr+');'); 62 | #print ("readdir()="+de) 63 | if int(de,16) == 0: 64 | break 65 | row = cmd('x/1s '+de+'+0x15') 66 | print (row.strip()) 67 | ret = ret + row 68 | runCode('(int)closedir((void*)'+ptr+')') 69 | except: 70 | traceback.print_last() 71 | ret = ret + "ERR\n"; 72 | return ret 73 | 74 | def setenv(x,y): 75 | # TODO: if process not running 76 | # dbg.cmd("set env %s %s"%(a[0],a[1]) 77 | runC("(void)setenv(\"%s\",\"%s\",1)"%(x,y)) 78 | 79 | def dlopen(x): 80 | runC("(void)dlopen(\"%s\")"%x) 81 | 82 | def cmd(x): 83 | return gdb.execute(x, False, True) 84 | 85 | #(lldb) e char *$str = (char *)malloc(8) 86 | #(lldb) e (void)strcpy($str, "munkeys") 87 | #(lldb) e $str[1] = 'o' 88 | #(char) $0 = 'o' 89 | #(lldb) p $str 90 | #(char *) $str = 0x00007fd04a900040 "monkeys" 91 | def getString(x): 92 | return cmd("print $%s"%x) #x/s $%s"%x) 93 | try: 94 | return cmd("x/s $%s"%x).split(":",1)[1].strip() 95 | except: 96 | return "" 97 | 98 | def getValue(x): 99 | return cmd("print $%s"%x) 100 | 101 | def setValue(x,y): 102 | runC("int $%s = %s"%(x,y)) 103 | 104 | def getRegister(r): 105 | try: 106 | return cmd("print %s"%r).strip().split(' ')[2] 107 | except: 108 | print("FAILED TO GET REG %s"%r) 109 | return '0' 110 | 111 | def setRegister(r,v): 112 | cmd("reg write %s %s"%(r,v)) 113 | 114 | # TODO : preprocessor here 115 | def runC(code): 116 | for a in code.split("\n"): 117 | if a != '': 118 | print(a) 119 | cmd("e "+a) 120 | def runCode(code): 121 | res = cmd("e "+code) 122 | try: 123 | return res.split("=")[1].strip() 124 | except: 125 | print("EXCEPTION") 126 | return res 127 | 128 | # runC(""" 129 | # (void)sleep(2) 130 | # void *$fd = (void*)fopen ("/tmp/test.txt", "w") 131 | # (void)fputs ("Hi\\n", $fd) 132 | # (void)fclose ($fd) 133 | # """) 134 | # 135 | # e for ($i = 0; $i<$count; $i++) { printf ("%s\\n", (char*)class_getName($classes[$i])); } 136 | 137 | def objcListClasses(): 138 | cmd('e int $count = (int)objc_getClassList(NULL, 0);') 139 | cmd('e Class *$classes = (Class*)malloc(sizeof(Class)*$count);') 140 | cmd('e (void)objc_getClassList($classes, $count);') 141 | cmd('e void *$dst = (void*)calloc($count, 128);') 142 | cmd('e int $i = 0;') 143 | cmd('e for ($i = 0; $i<$count; $i++) { (void)strcat ($dst, (char*)class_getName($classes[$i])); (void)strcat($dst,"\\n"); }') 144 | cmd('e (void)free($classes);') 145 | return cmd('print $dst') 146 | # runC(""" 147 | #e int $count = (int)objc_getClassList(NULL, 0); 148 | #e Class *$classes = (Class*)malloc(sizeof(Class)*$count); 149 | #e (void)objc_getClassList($classes, $count); 150 | #e void *$dst = (void*)calloc($count, 128); 151 | #e int $i = 0; 152 | #e for ($i = 0; $i<$count; $i++) { (void)strcat ($dst, (char*)class_getName($classes[$i])); (void)strcat($dst,"\\n"); } 153 | #e (void)free($classes); 154 | # """) 155 | # res = cmd("print $dst") 156 | # return "RESULT %s"%res 157 | # TODO: fix memleak 158 | #return getValue("dst") 159 | # runC(""" 160 | #e (void)free($dst); $dst = NULL; 161 | #""") 162 | 163 | # Global seek address 164 | curoff = 0 165 | 166 | def seek(off, when): 167 | curoff = off 168 | return off 169 | 170 | BSZ=1024 171 | 172 | def read(addr, size): 173 | try: 174 | return gdb.inferiors[0].read_memory(addr, size) 175 | except: 176 | return "" 177 | 178 | def write(addr, buf): 179 | try: 180 | return gdb.inferiors[0].write_memory(addr, size) 181 | except: 182 | return False 183 | 184 | def maps(): 185 | maps = cmd ("image list").split("\n") 186 | res = [] 187 | index = 0 188 | for a in maps: 189 | try: 190 | obj = {} 191 | line = a.split('] ', 1)[1].split(' ') 192 | if not line[0]: 193 | continue 194 | obj['index'] = index 195 | obj['uuid'] = line[0] 196 | obj['addr'] = line[1] 197 | obj['file'] = line[2] 198 | res.append(obj) 199 | index = index + 1 200 | except: 201 | pass 202 | return res 203 | 204 | def frames(): 205 | res = [] 206 | frames = cmd ("bt").strip().split("\n")[1:] 207 | index = len(frames)-1 208 | for a in frames: 209 | line = a.replace(' * ','').strip().replace('`',' ').split(' ') 210 | if len(line)<4: 211 | break 212 | print(line) 213 | obj = {} 214 | obj['index'] = index 215 | obj['addr'] = line[2] 216 | obj['file'] = line[3] 217 | obj['meth'] = line[4:] 218 | index = index - 1 219 | res.append (obj) 220 | return res 221 | 222 | def bp_list(): 223 | bps = cmd ("break list").split('\n') 224 | addr = '' 225 | name = '' 226 | cunt = 0 227 | res = [] 228 | for a in bps: 229 | print(a) 230 | try: 231 | indx = a.index(": name = '") 232 | name = a.split("'")[1] 233 | #print ("%d %s", cunt, name) 234 | cunt = cunt + 1 235 | obj = {} 236 | obj['type'] = 'name' 237 | obj['name'] = name 238 | res.append(obj) 239 | except: 240 | # Fails if no name in line 241 | try: 242 | if a[0] != ' ': 243 | indx = a.index(": address = 0x") 244 | addr = a.split(",")[0][indx+12:] 245 | #print ("---- %d %s"%(cunt, addr)) 246 | cunt = cunt + 1 247 | obj = {} 248 | obj['type'] = 'addr' 249 | obj['addr'] = addr 250 | res.append(obj) 251 | except: 252 | # Fails if no name in line 253 | pass 254 | pass 255 | return res 256 | 257 | def bp_clear(): 258 | cmd ("br del -f") 259 | 260 | def bp_selector(name): 261 | bpinfo = cmd ("br set -S %s"%name) 262 | # Breakpoint 2: 613 locations. 263 | 264 | def bp_addr(addr): 265 | bpinfo = cmd ("br set -a %s"%addr) 266 | 267 | def bp_symbol(name): 268 | bpinfo = cmd ("br set -F %s"%name) 269 | 270 | def bp_objc(cls,sel): 271 | bpinfo = cmd ("br set -n -[%s %s]"%(cls,sel)) 272 | # Breakpoint 3: where = libobjc.A.dylib`objc_msgSend, address = 0x01bbc0a4 273 | 274 | def wp_add(): 275 | pass 276 | 277 | def traceLoop(): 278 | while True: 279 | cmd ("continue") 280 | pcinfo = cmd ("print $pc") 281 | 282 | def symbols(): 283 | syms = [] 284 | for a in cmd("image dump symtab").split("\n"): 285 | try: 286 | sym = a[27:].split() 287 | obj = {} 288 | if sym[1][0:2] != '0x': 289 | continue 290 | obj['addr'] = sym[1] 291 | obj['base'] = sym[0] 292 | obj['size'] = sym[2] 293 | obj['name'] = '_'.join(sym[4:]) 294 | if obj['name'] == '': 295 | continue 296 | if obj['name'][0:2] != '0x': 297 | syms.append(obj) 298 | except: 299 | pass 300 | return syms 301 | 302 | def stop(): 303 | cmd("process interrupt") 304 | 305 | print ("") 306 | print ("Running r2lldb script...") 307 | 308 | #print(backtrace()) 309 | 310 | #bp_objc('NSString', 'stringWithFormat:') 311 | #traceLoop() 312 | 313 | #maps = lldb_maps() 314 | #print(maps) 315 | # runC(""" 316 | # (void)sleep(2) 317 | # void *$fd = (void*)fopen ("/tmp/test.txt", "w") 318 | # (void)fputs ("Hi\\n", $fd) 319 | # (void)fclose ($fd) 320 | # """) 321 | 322 | 323 | # 324 | # target methods 325 | # 326 | # ['AddModule', 'Attach', 'AttachToProcessWithID', 'AttachToProcessWithName', 'BreakpointCreateByAddress', 'BreakpointCreateByLocation', 'BreakpointCreateByName', 'BreakpointCreateByNames', 'BreakpointCreateByRegex', 'BreakpointCreateBySourceRegex', 'BreakpointCreateForException', 'BreakpointDelete', 'Clear', 'ClearModuleLoadAddress', 'ClearSectionLoadAddress', 'ConnectRemote', 'CreateValueFromAddress', 'CreateValueFromData', 'CreateValueFromExpression', 'DeleteAllBreakpoints', 'DeleteAllWatchpoints', 'DeleteWatchpoint', 'DisableAllBreakpoints', 'DisableAllWatchpoints', 'EnableAllBreakpoints', 'EnableAllWatchpoints', 'EvaluateExpression', 'FindBreakpointByID', 'FindFirstGlobalVariable', 'FindFirstType', 'FindFunctions', 'FindGlobalFunctions', 'FindGlobalVariables', 'FindModule', 'FindSymbols', 'FindTypes', 'FindWatchpointByID', 'GetAddressByteSize', 'GetBasicType', 'GetBreakpointAtIndex', 'GetBroadcaster', 'GetBroadcasterClassName', 'GetByteOrder', 'GetCodeByteSize', 'GetDataByteSize', 'GetDebugger', 'GetDescription', 'GetExecutable', 'GetInstructions', 'GetInstructionsWithFlavor', 'GetModuleAtIndex', 'GetNumBreakpoints', 'GetNumModules', 'GetNumWatchpoints', 'GetPlatform', 'GetProcess', 'GetSourceManager', 'GetStackRedZoneSize', 'GetTriple', 'GetWatchpointAtIndex', 'Install', 'IsValid', 'Launch', 'LaunchSimple', 'LoadCore', 'ReadInstructions', 'ReadMemory', 'RemoveModule', 'ResolveFileAddress', 'ResolveLoadAddress', 'ResolvePastLoadAddress', 'ResolveSymbolContextForAddress', 'SetModuleLoadAddress', 'SetSectionLoadAddress', 'WatchAddress', 327 | 328 | def parseCPSR(frame): 329 | """ Check Thumb flag from CPSR """ 330 | try: 331 | regs = frame.GetRegisters()[0] # general purpose registers 332 | cpsr = [reg for reg in regs if reg.GetName()=='cpsr'][0] 333 | thumb_bit = int(cpsr.GetValue(), 16) & 0x20 334 | if thumb_bit >> 5 != 0: 335 | print("5: thumb") 336 | else: 337 | print("5: arm") 338 | return True 339 | except: 340 | pass 341 | return False 342 | 343 | def isThumb(frame): 344 | """ Check Thumb flag from CPSR """ 345 | try: 346 | regs = frame.GetRegisters()[0] # general purpose registers 347 | cpsr = [reg for reg in regs if reg.GetName()=='cpsr'][0] 348 | thumb_bit = int(cpsr.GetValue(), 16) & 0x20 349 | if thumb_bit >> 5 != 0: 350 | return True 351 | except: 352 | pass 353 | return False 354 | 355 | # list methods 356 | #int unsigned numMethods; 357 | #Method *methods = class_copyMethodList(objc_getMetaClass("NSArray"), &numMethods); 358 | #for (int i = 0; i < numMethods; i++) { 359 | # NSLog(@"%@", NSStringFromSelector(method_getName(methods[i]))); 360 | #} 361 | -------------------------------------------------------------------------------- /r2lldb/backend/gdb/loop.py: -------------------------------------------------------------------------------- 1 | try: 2 | import gdb 3 | except: 4 | print("") 5 | print("ERROR: import gdb only works in the gdb shell") 6 | print("") 7 | raise 8 | 9 | import time 10 | import dbg 11 | 12 | dead = False 13 | running = False 14 | traces = [] 15 | print("LOOP INIT") 16 | 17 | def memWrite(addr, buf): 18 | data = '' 19 | target = lldb.debugger.GetSelectedTarget() 20 | error = lldb.SBError() 21 | res = target.process.WriteMemory (addr, buf, error) 22 | #if not error.Success() or res != 1: 23 | if res == 0: 24 | print(error) 25 | print ("WRITE FAIL AT 0x%x"%(addr)) 26 | return res 27 | 28 | class Tracepoint: 29 | def __init__(self): 30 | nothing = True 31 | 32 | def cmd(x): 33 | res = lldb.SBCommandReturnObject() 34 | lldb.debugger.GetCommandInterpreter().HandleCommand(x, res) 35 | return res.GetOutput() 36 | 37 | def getAddressForSymbol(symname): 38 | try: 39 | res = cmd("image lookup -s %s"%(symname)).split("\n")[1] 40 | res = res.replace("]","[").split("[")[1] 41 | return res 42 | except: 43 | return None 44 | 45 | def setTracepoint(symname): 46 | a = Tracepoint() 47 | a.name = symname 48 | #res = cmd("breakpoint set -n %s"%(symname)) 49 | addr = getAddressForSymbol(symname) 50 | if addr is None: 51 | print("Cant find address for %s"%(symname)) 52 | return None 53 | a.addr = addr 54 | res = cmd("breakpoint set -a %s"%(addr)) 55 | print(res) 56 | traces.append(a) 57 | print("SET TRACE %s at %s"%(symname, a.addr)) 58 | return a 59 | # try: 60 | # a.addr = res.split("= ")[2].split("\n")[0] 61 | # except: 62 | # print("Cant find address for %s"%(symname)) 63 | # return None 64 | # return a 65 | 66 | def listTracepoints(): 67 | out = "" 68 | for a in traces: 69 | s = "0x%x %s\n"%(a.addr, a.name) 70 | out = out + s 71 | return out 72 | 73 | def getTracepoint(addr): 74 | for a in traces: 75 | if a.addr == addr: 76 | return a 77 | return None 78 | 79 | def getCurrentPC(): 80 | res = "" 81 | try: 82 | res = cmd ("print $rip").split("= ")[1].split(" ")[0] 83 | except: 84 | return None 85 | return res 86 | 87 | def mainLoop(): 88 | global dead, running 89 | print(time.time()) 90 | if not running: 91 | running = True 92 | o=cmd("run") 93 | else: 94 | o=cmd("continue") 95 | if o.find("EXC_BAD") != -1: 96 | print ("IS DEAD") 97 | dead = True 98 | print (o) 99 | print(time.time()) 100 | pc = getCurrentPC() 101 | if not pc: 102 | dead = True 103 | t = getTracepoint(pc) 104 | if t is not None: 105 | print (cmd("bt")) 106 | print ("TRACE %s"%(t.name)) 107 | if hasattr(t, "cmd"): 108 | print ("RUNNING COMMAND") 109 | t.cmd () 110 | print("STOP AT (%s)"%(pc)) 111 | 112 | # ERR 113 | # XXX. this must be defined by the user or something 114 | RETADDR="0x1000bb484" 115 | 116 | # OK 117 | #RETADDR="0x1000bb074" 118 | 119 | def PatchReturn0(): 120 | cmd("register write rax 0") 121 | cmd("register write rip %s"%(RETADDR)) 122 | print("PATCHED RETURN VALUE TO 0") 123 | 124 | def PatchReturn1(): 125 | cmd("register write rax 1") 126 | cmd("register write rip %s"%(RETADDR)) 127 | print("PATCHED RETURN VALUE TO 1") 128 | 129 | #t = setTracepoint("strcmp") 130 | #t.cmd = PatchReturn0 131 | 132 | def runLoop(): 133 | while not dead: 134 | #memWrite(0x10008c657, "\x90\x90\x90\x90\x90\x90") 135 | mainLoop() 136 | -------------------------------------------------------------------------------- /r2lldb/backend/lldb/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nowsecure/r2lldb/29cf97bf96f68d5a78060ea63504112b4a2fb3df/r2lldb/backend/lldb/__init__.py -------------------------------------------------------------------------------- /r2lldb/backend/lldb/dbg.py: -------------------------------------------------------------------------------- 1 | try: 2 | import lldb 3 | except: 4 | print 5 | print "ERROR: import lldb only works in the lldb shell" 6 | print 7 | raise 8 | 9 | import traceback 10 | 11 | # (lldb) process launch --stop-at-entry -- -program_arg value 12 | 13 | #from backend import bp 14 | 15 | def cont(): 16 | return cmd("continue") 17 | 18 | def run_to_entry(): 19 | return cmd("run") 20 | 21 | def system_cat(path, head=False): 22 | ret = "" 23 | try: 24 | fd = runCode('(void*)open((char*)"'+path+'",0);') 25 | print ("FD "+fd) 26 | if int(fd,16) == 4294967295: 27 | return "Cannot open file" 28 | buf = runCode('(void*)malloc((int)10240)') 29 | print "open("+buf+")" 30 | i = 0 31 | while True: 32 | de = runCode('(int)read((int)'+str(fd)+',(void*)'+str(buf)+',(int)1024);'); 33 | count = int (de, 16) 34 | print ("read("+str(i)+")="+str(count)) 35 | try: 36 | data = read(int(buf,16), count) 37 | ret = ret + str(data) 38 | except: 39 | traceback.print_last() 40 | ret = ret + ".\n" 41 | if count != 1024 and count != 0x1024: 42 | break 43 | if head: 44 | break 45 | i = i + 1 46 | except: 47 | traceback.print_last() 48 | ret = ret + "ERR\n"; 49 | cmd('e (void)free((void*)'+buf+')') 50 | cmd('e (int)close((int)'+fd+')') 51 | return ret 52 | 53 | def system_ls(path): 54 | ret = "" 55 | print ("LS("+path+")") 56 | try: 57 | ptr = runCode('(void*)opendir((char*)"'+path+'");') 58 | if int(ptr,16) == 0: 59 | return "Cannot find directory" 60 | print "opendir("+ptr+")" 61 | while True: 62 | de = runCode('(void*)readdir((void*)'+ptr+');'); 63 | #print ("readdir()="+de) 64 | if int(de,16) == 0: 65 | break 66 | row = cmd('x/1s '+de+'+0x15') 67 | print (row.strip()) 68 | ret = ret + row 69 | runCode('(int)closedir((void*)'+ptr+')') 70 | except: 71 | traceback.print_last() 72 | ret = ret + "ERR\n"; 73 | return ret 74 | 75 | def setenv(x,y): 76 | # TODO: if process not running 77 | # dbg.cmd("set env %s %s"%(a[0],a[1]) 78 | runC("(void)setenv(\"%s\",\"%s\",1)"%(x,y)) 79 | 80 | def dlopen(x): 81 | runC("(void)dlopen(\"%s\")"%x) 82 | 83 | def cmd(x): 84 | res = lldb.SBCommandReturnObject() 85 | lldb.debugger.GetCommandInterpreter().HandleCommand(x, res) 86 | return res.GetOutput() 87 | 88 | #(lldb) e char *$str = (char *)malloc(8) 89 | #(lldb) e (void)strcpy($str, "munkeys") 90 | #(lldb) e $str[1] = 'o' 91 | #(char) $0 = 'o' 92 | #(lldb) p $str 93 | #(char *) $str = 0x00007fd04a900040 "monkeys" 94 | def getString(x): 95 | return cmd("print $%s"%x) #x/s $%s"%x) 96 | try: 97 | return cmd("x/s $%s"%x).split(":",1)[1].strip() 98 | except: 99 | return "" 100 | 101 | def getValue(x): 102 | return cmd("print $%s"%x) 103 | 104 | def setValue(x,y): 105 | runC("int $%s = %s"%(x,y)) 106 | 107 | def getRegister(r): 108 | try: 109 | return cmd("reg read %s"%r).strip().split(' ')[2] 110 | except: 111 | print("FAILED TO GET REG %s"%r) 112 | return '0' 113 | 114 | def setRegister(r,v): 115 | cmd("reg write %s %s"%(r,v)) 116 | 117 | # TODO : preprocessor here 118 | def runC(code): 119 | for a in code.split("\n"): 120 | if a != '': 121 | print(a) 122 | cmd("e "+a) 123 | def runCode(code): 124 | res = cmd("e "+code) 125 | try: 126 | return res.split("=")[1].strip() 127 | except: 128 | print "EXCEPTION" 129 | return res 130 | 131 | # runC(""" 132 | # (void)sleep(2) 133 | # void *$fd = (void*)fopen ("/tmp/test.txt", "w") 134 | # (void)fputs ("Hi\\n", $fd) 135 | # (void)fclose ($fd) 136 | # """) 137 | # 138 | # e for ($i = 0; $i<$count; $i++) { printf ("%s\\n", (char*)class_getName($classes[$i])); } 139 | 140 | def objcListClasses(): 141 | cmd('e int $count = (int)objc_getClassList(NULL, 0);') 142 | cmd('e Class *$classes = (Class*)malloc(sizeof(Class)*$count);') 143 | cmd('e (void)objc_getClassList($classes, $count);') 144 | cmd('e void *$dst = (void*)calloc($count, 128);') 145 | cmd('e int $i = 0;') 146 | cmd('e for ($i = 0; $i<$count; $i++) { (void)strcat ($dst, (char*)class_getName($classes[$i])); (void)strcat($dst,"\\n"); }') 147 | cmd('e (void)free($classes);') 148 | return cmd('print $dst') 149 | # runC(""" 150 | #e int $count = (int)objc_getClassList(NULL, 0); 151 | #e Class *$classes = (Class*)malloc(sizeof(Class)*$count); 152 | #e (void)objc_getClassList($classes, $count); 153 | #e void *$dst = (void*)calloc($count, 128); 154 | #e int $i = 0; 155 | #e for ($i = 0; $i<$count; $i++) { (void)strcat ($dst, (char*)class_getName($classes[$i])); (void)strcat($dst,"\\n"); } 156 | #e (void)free($classes); 157 | # """) 158 | # res = cmd("print $dst") 159 | # return "RESULT %s"%res 160 | # TODO: fix memleak 161 | #return getValue("dst") 162 | # runC(""" 163 | #e (void)free($dst); $dst = NULL; 164 | #""") 165 | 166 | # Global seek address 167 | curoff = 0 168 | 169 | def seek(off, when): 170 | curoff = off 171 | return off 172 | 173 | BSZ=1024 174 | 175 | def read(addr, size): 176 | i = 0 177 | data = '' 178 | if sizesize: 186 | bs = size-i 187 | res = target.process.ReadMemory (addr+i, bs, error) 188 | if len (res) == 0: 189 | print(error) 190 | #print ("READ FAIL AT 0x%x"%(addr+i)) 191 | i = i + bs 192 | continue 193 | if data == None: 194 | data = res 195 | elif res: 196 | data = data + res 197 | i = i + bs 198 | return data 199 | 200 | def write(addr, buf): 201 | i = 0 202 | data = '' 203 | target = lldb.debugger.GetSelectedTarget() 204 | error = lldb.SBError() 205 | res = target.process.WriteMemory (addr+i, buf, error) 206 | #if not error.Success() or res != 1: 207 | print ("RES") 208 | print (res) 209 | if res == 0: 210 | print(error) 211 | #print ("WRITE FAIL AT 0x%x"%(addr+i)) 212 | return 0 213 | return res 214 | 215 | #[ 99] 29886CD7-2AC8-3578-8389-7D5BEE405F53 0x08a38000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/PrivateFrameworks/FaceCore.framework/FaceCore 216 | 217 | def maps(): 218 | maps = cmd ("image list").split("\n") 219 | res = [] 220 | index = 0 221 | for a in maps: 222 | try: 223 | obj = {} 224 | line = a.split('] ', 1)[1].split(' ') 225 | if not line[0]: 226 | continue 227 | obj['index'] = index 228 | obj['uuid'] = line[0] 229 | obj['addr'] = line[1] 230 | obj['file'] = line[2] 231 | res.append(obj) 232 | index = index + 1 233 | except: 234 | pass 235 | return res 236 | 237 | def frames(): 238 | res = [] 239 | frames = cmd ("bt").strip().split("\n")[1:] 240 | index = len(frames)-1 241 | for a in frames: 242 | line = a.replace(' * ','').strip().replace('`',' ').split(' ') 243 | if len(line)<4: 244 | break 245 | print(line) 246 | obj = {} 247 | obj['index'] = index 248 | obj['addr'] = line[2] 249 | obj['file'] = line[3] 250 | obj['meth'] = line[4:] 251 | index = index - 1 252 | res.append (obj) 253 | return res 254 | 255 | def bp_list(): 256 | bps = cmd ("break list").split('\n') 257 | addr = '' 258 | name = '' 259 | cunt = 0 260 | res = [] 261 | for a in bps: 262 | print(a) 263 | try: 264 | indx = a.index(": name = '") 265 | name = a.split("'")[1] 266 | #print ("%d %s", cunt, name) 267 | cunt = cunt + 1 268 | obj = {} 269 | obj['type'] = 'name' 270 | obj['name'] = name 271 | res.append(obj) 272 | except: 273 | # Fails if no name in line 274 | try: 275 | if a[0] != ' ': 276 | indx = a.index(": address = 0x") 277 | addr = a.split(",")[0][indx+12:] 278 | #print ("---- %d %s"%(cunt, addr)) 279 | cunt = cunt + 1 280 | obj = {} 281 | obj['type'] = 'addr' 282 | obj['addr'] = addr 283 | res.append(obj) 284 | except: 285 | # Fails if no name in line 286 | pass 287 | pass 288 | return res 289 | 290 | def bp_clear(): 291 | cmd ("br del -f") 292 | 293 | def bp_selector(name): 294 | bpinfo = cmd ("br set -S %s"%name) 295 | # Breakpoint 2: 613 locations. 296 | 297 | def bp_addr(addr): 298 | bpinfo = cmd ("br set -a %s"%addr) 299 | 300 | def bp_symbol(name): 301 | bpinfo = cmd ("br set -F %s"%name) 302 | 303 | def bp_objc(cls,sel): 304 | bpinfo = cmd ("br set -n -[%s %s]"%(cls,sel)) 305 | # Breakpoint 3: where = libobjc.A.dylib`objc_msgSend, address = 0x01bbc0a4 306 | 307 | def wp_add(): 308 | pass 309 | 310 | def traceLoop(): 311 | while True: 312 | cmd ("continue") 313 | pcinfo = cmd ("reg read pc") 314 | 315 | def symbols(): 316 | syms = [] 317 | for a in cmd("image dump symtab").split("\n"): 318 | try: 319 | sym = a[27:].split() 320 | obj = {} 321 | if sym[1][0:2] != '0x': 322 | continue 323 | obj['addr'] = sym[1] 324 | obj['base'] = sym[0] 325 | obj['size'] = sym[2] 326 | obj['name'] = '_'.join(sym[4:]) 327 | if obj['name'] == '': 328 | continue 329 | if obj['name'][0:2] != '0x': 330 | syms.append(obj) 331 | except: 332 | pass 333 | return syms 334 | 335 | def stop(): 336 | cmd("process interrupt") 337 | 338 | print ("") 339 | print ("Running r2lldb script...") 340 | 341 | #print(backtrace()) 342 | 343 | #bp_objc('NSString', 'stringWithFormat:') 344 | #traceLoop() 345 | 346 | #maps = lldb_maps() 347 | #print(maps) 348 | # runC(""" 349 | # (void)sleep(2) 350 | # void *$fd = (void*)fopen ("/tmp/test.txt", "w") 351 | # (void)fputs ("Hi\\n", $fd) 352 | # (void)fclose ($fd) 353 | # """) 354 | 355 | 356 | # 357 | # target methods 358 | # 359 | # ['AddModule', 'Attach', 'AttachToProcessWithID', 'AttachToProcessWithName', 'BreakpointCreateByAddress', 'BreakpointCreateByLocation', 'BreakpointCreateByName', 'BreakpointCreateByNames', 'BreakpointCreateByRegex', 'BreakpointCreateBySourceRegex', 'BreakpointCreateForException', 'BreakpointDelete', 'Clear', 'ClearModuleLoadAddress', 'ClearSectionLoadAddress', 'ConnectRemote', 'CreateValueFromAddress', 'CreateValueFromData', 'CreateValueFromExpression', 'DeleteAllBreakpoints', 'DeleteAllWatchpoints', 'DeleteWatchpoint', 'DisableAllBreakpoints', 'DisableAllWatchpoints', 'EnableAllBreakpoints', 'EnableAllWatchpoints', 'EvaluateExpression', 'FindBreakpointByID', 'FindFirstGlobalVariable', 'FindFirstType', 'FindFunctions', 'FindGlobalFunctions', 'FindGlobalVariables', 'FindModule', 'FindSymbols', 'FindTypes', 'FindWatchpointByID', 'GetAddressByteSize', 'GetBasicType', 'GetBreakpointAtIndex', 'GetBroadcaster', 'GetBroadcasterClassName', 'GetByteOrder', 'GetCodeByteSize', 'GetDataByteSize', 'GetDebugger', 'GetDescription', 'GetExecutable', 'GetInstructions', 'GetInstructionsWithFlavor', 'GetModuleAtIndex', 'GetNumBreakpoints', 'GetNumModules', 'GetNumWatchpoints', 'GetPlatform', 'GetProcess', 'GetSourceManager', 'GetStackRedZoneSize', 'GetTriple', 'GetWatchpointAtIndex', 'Install', 'IsValid', 'Launch', 'LaunchSimple', 'LoadCore', 'ReadInstructions', 'ReadMemory', 'RemoveModule', 'ResolveFileAddress', 'ResolveLoadAddress', 'ResolvePastLoadAddress', 'ResolveSymbolContextForAddress', 'SetModuleLoadAddress', 'SetSectionLoadAddress', 'WatchAddress', 360 | 361 | def parseCPSR(frame): 362 | """ Check Thumb flag from CPSR """ 363 | try: 364 | regs = frame.GetRegisters()[0] # general purpose registers 365 | cpsr = [reg for reg in regs if reg.GetName()=='cpsr'][0] 366 | thumb_bit = int(cpsr.GetValue(), 16) & 0x20 367 | if thumb_bit >> 5 != 0: 368 | print "5: thumb" 369 | else: 370 | print "5: arm" 371 | return True 372 | except: 373 | pass 374 | return False 375 | 376 | def isThumb(frame): 377 | """ Check Thumb flag from CPSR """ 378 | try: 379 | regs = frame.GetRegisters()[0] # general purpose registers 380 | cpsr = [reg for reg in regs if reg.GetName()=='cpsr'][0] 381 | thumb_bit = int(cpsr.GetValue(), 16) & 0x20 382 | if thumb_bit >> 5 != 0: 383 | return True 384 | except: 385 | pass 386 | return False 387 | 388 | # list methods 389 | #int unsigned numMethods; 390 | #Method *methods = class_copyMethodList(objc_getMetaClass("NSArray"), &numMethods); 391 | #for (int i = 0; i < numMethods; i++) { 392 | # NSLog(@"%@", NSStringFromSelector(method_getName(methods[i]))); 393 | #} 394 | -------------------------------------------------------------------------------- /r2lldb/backend/lldb/loop.py: -------------------------------------------------------------------------------- 1 | try: 2 | import lldb 3 | except: 4 | print 5 | print "ERROR: import lldb only works in the lldb shell" 6 | print 7 | raise 8 | 9 | import time 10 | # for dbg.read, etc.. must reuse instead of reimplement here 11 | import dbg 12 | 13 | dead = False 14 | running = False 15 | traces = [] 16 | print "LOOP INIT" 17 | 18 | def memWrite(addr, buf): 19 | data = '' 20 | target = lldb.debugger.GetSelectedTarget() 21 | error = lldb.SBError() 22 | res = target.process.WriteMemory (addr, buf, error) 23 | #if not error.Success() or res != 1: 24 | if res == 0: 25 | print(error) 26 | print ("WRITE FAIL AT 0x%x"%(addr)) 27 | return res 28 | 29 | class Tracepoint: 30 | def __init__(self): 31 | nothing = True 32 | 33 | def cmd(x): 34 | res = lldb.SBCommandReturnObject() 35 | lldb.debugger.GetCommandInterpreter().HandleCommand(x, res) 36 | return res.GetOutput() 37 | 38 | def getAddressForSymbol(symname): 39 | try: 40 | res = cmd("image lookup -s %s"%(symname)).split("\n")[1] 41 | res = res.replace("]","[").split("[")[1] 42 | return res 43 | except: 44 | return None 45 | 46 | def setTracepoint(symname): 47 | a = Tracepoint() 48 | a.name = symname 49 | #res = cmd("breakpoint set -n %s"%(symname)) 50 | addr = getAddressForSymbol(symname) 51 | if addr is None: 52 | print("Cant find address for %s"%(symname)) 53 | return None 54 | a.addr = addr 55 | res = cmd("breakpoint set -a %s"%(addr)) 56 | print res 57 | traces.append(a) 58 | print "SET TRACE %s at %s"%(symname, a.addr) 59 | return a 60 | # try: 61 | # a.addr = res.split("= ")[2].split("\n")[0] 62 | # except: 63 | # print("Cant find address for %s"%(symname)) 64 | # return None 65 | # return a 66 | 67 | def listTracepoints(): 68 | out = "" 69 | for a in traces: 70 | s = "0x%x %s\n"%(a.addr, a.name) 71 | out = out + s 72 | return out 73 | 74 | def getTracepoint(addr): 75 | for a in traces: 76 | if a.addr == addr: 77 | return a 78 | return None 79 | 80 | def getCurrentPC(): 81 | res = "" 82 | try: 83 | res = cmd ("register read rip").split("= ")[1].split(" ")[0] 84 | except: 85 | return None 86 | return res 87 | 88 | def mainLoop(): 89 | global dead, running 90 | print time.time() 91 | if not running: 92 | running = True 93 | o=cmd("run") 94 | else: 95 | o=cmd("continue") 96 | if o.find("EXC_BAD") != -1: 97 | print ("IS DEAD") 98 | dead = True 99 | print (o) 100 | print time.time() 101 | pc = getCurrentPC() 102 | if not pc: 103 | dead = True 104 | t = getTracepoint(pc) 105 | if t is not None: 106 | print (cmd("bt")) 107 | print ("TRACE %s"%(t.name)) 108 | if hasattr(t, "cmd"): 109 | print ("RUNNING COMMAND") 110 | t.cmd () 111 | print "STOP AT (%s)"%(pc) 112 | 113 | # ERR 114 | # XXX. this must be defined by the user or something 115 | RETADDR="0x1000bb484" 116 | 117 | # OK 118 | #RETADDR="0x1000bb074" 119 | 120 | def PatchReturn0(): 121 | cmd("register write rax 0") 122 | cmd("register write rip %s"%(RETADDR)) 123 | print "PATCHED RETURN VALUE TO 0" 124 | 125 | def PatchReturn1(): 126 | cmd("register write rax 1") 127 | cmd("register write rip %s"%(RETADDR)) 128 | print "PATCHED RETURN VALUE TO 1" 129 | 130 | #t = setTracepoint("strcmp") 131 | #t.cmd = PatchReturn0 132 | 133 | def runLoop(): 134 | while not dead: 135 | #memWrite(0x10008c657, "\x90\x90\x90\x90\x90\x90") 136 | mainLoop() 137 | -------------------------------------------------------------------------------- /r2lldb/backend/trace.py: -------------------------------------------------------------------------------- 1 | # TODO: implement tracing helpers here 2 | 3 | traces = {} 4 | 5 | def add(at,cmd): 6 | try: 7 | #at = "0x%x"%int(at, 16) 8 | print("ADD",at) 9 | print(traces[at]) 10 | if traces[at]: 11 | return False 12 | except: 13 | pass 14 | traces[at] = cmd 15 | return True 16 | 17 | def get(at): 18 | try: 19 | try: 20 | at = "0x%x"%int(at, 16) 21 | except: 22 | pass 23 | print("GET",at) 24 | return traces[at] 25 | except e: 26 | print(e) 27 | return None 28 | 29 | def contains(at): 30 | at = "0x%x"%int(at, 16) 31 | print("CHK",at) 32 | return get(at) != None 33 | 34 | def list(): 35 | s = "" 36 | for a in traces.keys(): 37 | line = "" 38 | try: 39 | line = "dt 0x%x %s\n"%(int(a,16),traces[a]) 40 | except: 41 | line = "dt "+a+" "+traces[a] + "\n" 42 | s = s + line 43 | return s 44 | 45 | -------------------------------------------------------------------------------- /r2lldb/r2rap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Python implementation of the radare remote protocol 4 | # 5 | 6 | ##===================================================0 7 | ## server api 8 | ##===================================================0 9 | 10 | from socket import * 11 | from struct import * 12 | import traceback 13 | import sys 14 | 15 | RAP_OPEN = 1 16 | RAP_READ = 2 17 | RAP_WRITE = 3 18 | RAP_SEEK = 4 19 | RAP_CLOSE = 5 20 | RAP_SYSTEM = 6 21 | RAP_CMD = 7 22 | RAP_REPLY = 0x80 23 | 24 | # TODO: Add udp 25 | # TODO: allow to init funptrs with a tuple 26 | class RapServer(): 27 | def __init__(self): 28 | self.offset = 0 29 | self.size = 0 30 | self.fd = None 31 | self.handle_eof = None 32 | self.handle_system = None 33 | self.handle_cmd = None 34 | self.handle_seek = None 35 | self.handle_read = None 36 | self.handle_write = None 37 | self.handle_open = None 38 | self.handle_close = None 39 | 40 | # copypasta from client 41 | def system(self, cmd): 42 | buf = pack(">Bi", RAP_SYSTEM, len(str(cmd))) 43 | self.fd.send(buf) 44 | self.fd.send(cmd) 45 | # read response 46 | buf = self.fd.recv(5) 47 | (c,l) = unpack(">Bi", buf) 48 | if c != RAP_SYSTEM | RAP_REPLY: 49 | print("rmt-system: Invalid response packet") 50 | return "" 51 | if l>0: 52 | buf = self.fd.recv(l) 53 | else: 54 | buf = "" 55 | return buf 56 | 57 | def _handle_packet(self, c, key): 58 | self.fd = c 59 | ret = "" 60 | if key == RAP_OPEN: 61 | buf = c.recv(2) 62 | (flags, length) = unpack(">BB", buf) 63 | file = c.recv(length) 64 | if self.handle_open != None: 65 | fd = self.handle_open(file, flags) 66 | else: fd = 3434 67 | buf = pack(">Bi", key|RAP_REPLY, fd) 68 | c.send(buf) 69 | elif key == RAP_READ: 70 | buf = c.recv(4) 71 | (length,) = unpack(">I", buf) 72 | if self.handle_read != None: 73 | try: 74 | ret = str(self.handle_read(length)) 75 | lon = len(ret) 76 | except: 77 | ret = "" 78 | lon = 0 79 | else: 80 | print("PUTAA") 81 | ret = "" 82 | lon = 0; 83 | print("PACKING REPLY") 84 | buf = pack(">Bi", key | RAP_REPLY, lon) 85 | print("SENDING RAP READ") 86 | c.send(buf+ret) 87 | elif key == RAP_WRITE: 88 | buf = c.recv(4) 89 | (length,) = unpack(">I", buf) 90 | buf = c.recv(length) 91 | # TODO: get buffer and length 92 | if self.handle_write != None: 93 | length = self.handle_write (buf) 94 | buf = pack(">Bi", key|RAP_REPLY, length) 95 | c.send(buf) 96 | elif key == RAP_SEEK: 97 | buf = c.recv(9) 98 | (type, off) = unpack(">BQ", buf) 99 | seek = 0 100 | if self.handle_seek != None: 101 | seek = self.handle_seek(off, type) 102 | else: 103 | if type == 0: # SET 104 | seek = off; 105 | elif type == 1: # CUR 106 | seek = seek + off 107 | elif type == 2: # END 108 | seek = self.size; 109 | self.offset = seek 110 | buf = pack(">BQ", key|RAP_REPLY, seek) 111 | c.send(buf) 112 | elif key == RAP_CLOSE: 113 | if self.handle_close != None: 114 | length = self.handle_close (fd) 115 | elif key == RAP_CMD: 116 | buf = c.recv(4) 117 | (length,) = unpack(">i", buf) 118 | ret = c.recv(length) 119 | if self.handle_cmd != None: 120 | reply = self.handle_cmd(ret) 121 | else: reply = "" 122 | buf = pack(">Bi", key|RAP_REPLY, len(str(reply))) 123 | c.send(buf+reply) 124 | elif key == RAP_SYSTEM: 125 | buf = c.recv(4) 126 | (length,) = unpack(">i", buf) 127 | ret = c.recv(length) 128 | if self.handle_system != None: 129 | reply = self.handle_system(ret) 130 | else: reply = "" 131 | buf = pack(">Bi", key|RAP_REPLY, len(str(reply))) 132 | c.send(buf+reply) 133 | else: 134 | print("Unknown command %x"%key) 135 | c.close() 136 | 137 | def _handle_client(self, c): 138 | while True: 139 | try: 140 | buf = c.recv(1) 141 | if buf == "" and self.handle_eof is not None: 142 | self.handle_eof(c) 143 | break 144 | if len(buf) == 0: 145 | print("Connection closed\n") 146 | break 147 | self._handle_packet(c, ord(buf)) 148 | except KeyboardInterrupt: 149 | break 150 | 151 | def listen_tcp(self, port): 152 | s = socket(); 153 | s.bind(("0.0.0.0", port)) 154 | s.listen(999) 155 | print("Listening at port %d"%port) 156 | self.running = True 157 | while self.running: 158 | (c, (addr,port)) = s.accept() 159 | print("New client %s:%d"%(addr,port)) 160 | self._handle_client(c) 161 | 162 | def stop(self): 163 | self.running = False 164 | 165 | 166 | ##===================================================0 167 | ## client api 168 | ##===================================================0 169 | 170 | class RapClient(): 171 | def __init__(self, host, port): 172 | self.connect_tcp(host, port) 173 | 174 | def connect_tcp(self, host, port): 175 | fd = socket(); 176 | fd.connect((host, port)) 177 | self.fd = fd 178 | 179 | def disconnect(self): 180 | self.fd.close() 181 | self.fd = None 182 | 183 | def open(self, file, flags): 184 | b = pack(">BBB", RAP_OPEN, flags, len(file)) 185 | self.fd.send(b) 186 | self.fd.send(file) 187 | # response 188 | buf = self.fd.recv(5) 189 | (c,l) = unpack(">Bi", buf) 190 | if c != (RAP_REPLY|RAP_OPEN): 191 | print("rmt-open: Invalid response packet 0x%02x"%c) 192 | return l 193 | 194 | def read(self, count): 195 | b = pack(">Bi", RAP_READ, count) #len(buf)) 196 | self.fd.send(b) 197 | # response 198 | buf = self.fd.recv(5) 199 | (c,l) = unpack(">Bi", buf) 200 | buf = self.fd.recv(l) 201 | return buf 202 | 203 | # TODO: not tested 204 | def write(self, buf): 205 | #self.fd.send(buf) 206 | b = pack(">Bi", RAP_WRITE, len(buf)) 207 | self.fd.send(b+buf) 208 | # response 209 | buf = self.fd.recv(5) 210 | (c,l) = unpack(">Bi", buf) 211 | if c != (RAP_REPLY|RAP_WRITE): 212 | print("rmt-write: Invalid response packet 0x%02x"%c) 213 | 214 | def lseek(self, type, addr): 215 | # WTF BBQ? 216 | buf = pack(">BBQ", RAP_SEEK, type, addr) 217 | self.fd.send(buf) 218 | # read response 219 | buf = self.fd.recv(5) # XXX READ 5!?!?!? shouldnt be 9 ?!?!? WTF 220 | (c,l) = unpack(">Bi", buf) 221 | #print "Lseek : %d"%l 222 | return l 223 | 224 | def close(self, fd): 225 | buf = pack(">Bi", RAP_CLOSE, fd) 226 | self.fd.send(buf) 227 | # read response 228 | buf = self.fd.recv(5) 229 | (c,l) = unpack(">Bi", buf) 230 | if c != RAP_REPLY | RAP_CLOSE: 231 | print("rmt-close: Invalid response packet") 232 | 233 | def cmd(self, cmd): 234 | buf = pack(">Bi", RAP_CMD, len(str(cmd))) 235 | self.fd.send(buf + cmd) 236 | # read response 237 | buf = self.fd.recv(5) 238 | (c,l) = unpack(">Bi", buf) 239 | if c != RAP_CMD | RAP_REPLY: 240 | print(c) 241 | print("rmt-cmd: Invalid response packet") 242 | return "" 243 | buf = "" 244 | if l>0: 245 | read = 0 246 | while readBi", RAP_SYSTEM, len(str(cmd))) 254 | self.fd.send(buf) 255 | self.fd.send(cmd) 256 | # read response 257 | buf = self.fd.recv(5) 258 | (c,l) = unpack(">Bi", buf) 259 | if c != RAP_SYSTEM | RAP_REPLY: 260 | print("rmt-system: Invalid response packet") 261 | return "" 262 | if l>0: 263 | buf = self.fd.recv(l) 264 | return buf 265 | --------------------------------------------------------------------------------