├── 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 |
--------------------------------------------------------------------------------