5454import re
5555import subprocess
5656import tempfile
57+ import capstone as cs
5758
5859from io import BytesIO
5960
@@ -1143,6 +1144,12 @@ def _populate_kernel_version(self):
11431144
11441145 self .config ['version' ] = self .version
11451146
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+
11461153 @property
11471154 def libc_start_main_return (self ):
11481155 """:class:`int`: Address of the return address into __libc_start_main from main.
@@ -1163,21 +1170,20 @@ def libc_start_main_return(self):
11631170 if 'exit' not in self .symbols :
11641171 return 0
11651172
1166- import capstone as cs
1167-
1173+ eabi = None
11681174 # If there's no delay slot, execution continues on the next instruction after a call.
11691175 call_return_offset = 1
11701176 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
11741180 call_instructions = set ([cs .CS_GRP_CALL ])
11751181 elif self .arch == 'aarch64' :
11761182 call_instructions = set ([cs .CS_GRP_CALL ])
11771183 elif self .arch in ['mips' , 'mips64' ]:
11781184 # 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 ])
11811187 # Account for the delay slot.
11821188 call_return_offset = 2
11831189 elif self .arch in ['i386' , 'amd64' , 'ia64' ]:
@@ -1186,14 +1192,15 @@ def libc_start_main_return(self):
11861192 call_instructions = set ([cs .CS_GRP_CALL ])
11871193
11881194 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 )
11911196 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 ))
11941199 # print("dis:", dis)
11951200
11961201 exit_addr = self .symbols ['exit' ]
1202+ if self .arch == 'arm' and exit_addr & 1 : exit_addr -= 1
1203+ # print("exit:", hex(exit_addr))
11971204
11981205 calls = [(i , x ) for i , x in enumerate (dis ) if call_instructions & set (x .groups )]
11991206 # print("calls:", calls)
@@ -1224,7 +1231,7 @@ def find_ret_main_addr(caller_dis, calls):
12241231 target_addr = op .imm
12251232 # `__libc_start_call_main` is usually smaller than `__libc_start_main`, so
12261233 # 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 ))
12281235 callee_calls = [(i , x ) for i , x in enumerate (callee_dis ) if call_instructions & set (x .groups )]
12291236 ret_addr = find_ret_main_addr (callee_dis , callee_calls )
12301237 if ret_addr :
0 commit comments