54
54
import re
55
55
import subprocess
56
56
import tempfile
57
+ import capstone as cs
57
58
58
59
from io import BytesIO
59
60
@@ -1143,6 +1144,12 @@ def _populate_kernel_version(self):
1143
1144
1144
1145
self .config ['version' ] = self .version
1145
1146
1147
+ def cs_disasm (self , md : cs .Cs , address , n_bytes ):
1148
+ if self .arch == 'arm' and address & 1 :
1149
+ address -= 1
1150
+
1151
+ return md .disasm (self .read (address , n_bytes ), address )
1152
+
1146
1153
@property
1147
1154
def libc_start_main_return (self ):
1148
1155
""":class:`int`: Address of the return address into __libc_start_main from main.
@@ -1163,21 +1170,20 @@ def libc_start_main_return(self):
1163
1170
if 'exit' not in self .symbols :
1164
1171
return 0
1165
1172
1166
- import capstone as cs
1167
-
1173
+ eabi = None
1168
1174
# If there's no delay slot, execution continues on the next instruction after a call.
1169
1175
call_return_offset = 1
1170
1176
if self .arch in ['arm' , 'thumb' ]:
1171
- # if b'armhf' in self.linker:
1172
- # self.arch = 'armhf '
1173
- # self.bits = 16
1177
+ if b'armhf' in self .linker :
1178
+ eabi = 'hf '
1179
+ # self.bits = 16
1174
1180
call_instructions = set ([cs .CS_GRP_CALL ])
1175
1181
elif self .arch == 'aarch64' :
1176
1182
call_instructions = set ([cs .CS_GRP_CALL ])
1177
1183
elif self .arch in ['mips' , 'mips64' ]:
1178
1184
# FIXME: `bal` was not included in CS_GRP_CALL. This is fixed on capstone v6.alpha
1179
- call_instructions = set ([cs .CS_GRP_CALL , cs .CS_GRP_BRANCH_RELATIVE ])
1180
- # call_instructions = set([cs.CS_GRP_CALL])
1185
+ # call_instructions = set([cs.CS_GRP_CALL, cs.CS_GRP_BRANCH_RELATIVE])
1186
+ call_instructions = set ([cs .CS_GRP_CALL ])
1181
1187
# Account for the delay slot.
1182
1188
call_return_offset = 2
1183
1189
elif self .arch in ['i386' , 'amd64' , 'ia64' ]:
@@ -1186,14 +1192,15 @@ def libc_start_main_return(self):
1186
1192
call_instructions = set ([cs .CS_GRP_CALL ])
1187
1193
1188
1194
from pwnlib .asm import get_cs_disassembler
1189
- md = get_cs_disassembler (arch = self .arch , endian = self .endian , bits = self .bits )
1190
- md .detail = True
1195
+ md = get_cs_disassembler (arch = self .arch , endian = self .endian , bits = self .bits , eabi = eabi )
1191
1196
func = self .functions ['__libc_start_main' ]
1192
- code = self . read ( func . address , func . size )
1193
- dis = list (md . disasm ( code , func .address ))
1197
+ # print(" func:" , func)
1198
+ dis = list (self . cs_disasm ( md , func .address , func . size ))
1194
1199
# print("dis:", dis)
1195
1200
1196
1201
exit_addr = self .symbols ['exit' ]
1202
+ if self .arch == 'arm' and exit_addr & 1 : exit_addr -= 1
1203
+ # print("exit:", hex(exit_addr))
1197
1204
1198
1205
calls = [(i , x ) for i , x in enumerate (dis ) if call_instructions & set (x .groups )]
1199
1206
# print("calls:", calls)
@@ -1224,7 +1231,7 @@ def find_ret_main_addr(caller_dis, calls):
1224
1231
target_addr = op .imm
1225
1232
# `__libc_start_call_main` is usually smaller than `__libc_start_main`, so
1226
1233
# we might disassemble a bit too much, but it's a good dynamic estimate.
1227
- callee_dis = list (md . disasm ( self .read ( target_addr , func .size ), target_addr ))
1234
+ callee_dis = list (self .cs_disasm ( md , target_addr , func .size ))
1228
1235
callee_calls = [(i , x ) for i , x in enumerate (callee_dis ) if call_instructions & set (x .groups )]
1229
1236
ret_addr = find_ret_main_addr (callee_dis , callee_calls )
1230
1237
if ret_addr :
0 commit comments