@@ -1170,41 +1170,57 @@ def libc_start_main_return(self):
1170
1170
if 'exit' not in self .symbols :
1171
1171
return 0
1172
1172
1173
+ func = self .functions ['__libc_start_main' ]
1174
+ exit_addr = self .symbols ['exit' ]
1173
1175
eabi = None
1176
+ # `__libc_start_call_main` is usually smaller than `__libc_start_main`,
1177
+ # (except for powerpc which uses a bigger `generic_start_main`), so
1178
+ # we might disassemble a bit too much, but it's a good dynamic estimate.
1179
+ callee_size = func .size
1180
+ # most arch's call instruction has the first operands as an intermidiate, except s390
1181
+ imm_index = 0
1182
+
1174
1183
# If there's no delay slot, execution continues on the next instruction after a call.
1175
1184
call_return_offset = 1
1185
+ call_instructions = set ([cs .CS_GRP_CALL ])
1176
1186
if self .arch in ['arm' , 'thumb' ]:
1177
1187
if b'armhf' in self .linker :
1178
1188
eabi = 'hf'
1179
- call_instructions = set ([ cs . CS_GRP_CALL ])
1189
+ if exit_addr & 1 : exit_addr -= 1
1180
1190
elif self .arch == 'aarch64' :
1181
- call_instructions = set ([ cs . CS_GRP_CALL ])
1191
+ pass
1182
1192
elif self .arch in ['mips' , 'mips64' ]:
1183
1193
# FIXME: `bal` was not included in CS_GRP_CALL. This is fixed on capstone v6.alpha
1184
- #call_instructions = set([cs.CS_GRP_CALL])
1185
- call_instructions = set ([cs .CS_GRP_CALL , cs .CS_GRP_BRANCH_RELATIVE ])
1194
+ call_instructions = call_instructions .add (cs .CS_GRP_BRANCH_RELATIVE )
1186
1195
# Account for the delay slot.
1187
1196
call_return_offset = 2
1188
1197
elif self .arch in ['i386' , 'amd64' , 'ia64' ]:
1189
- call_instructions = set ([cs .CS_GRP_CALL ])
1198
+ pass
1199
+ elif self .arch in ['ppc' , 'powerpc' , 'powerpc64' ]:
1200
+ callee_size *= 2
1201
+ if exit_addr & 1 == 0 :
1202
+ # powepc often jumps to the local entry point after TOC setup
1203
+ exit_addr += 8
1204
+ pass
1205
+ elif self .arch in ['em_s390' , 's390' ]:
1206
+ imm_index = 1
1207
+ pass
1190
1208
else :
1191
1209
log .error ('Unsupported architecture %s in ELF.libc_start_main_return' , self .arch )
1192
1210
return 0
1193
1211
1194
1212
from pwnlib .asm import get_cs_disassembler
1195
1213
md = get_cs_disassembler (arch = self .arch , endian = self .endian , bits = self .bits , eabi = eabi )
1196
- func = self .functions ['__libc_start_main' ]
1197
1214
dis = list (self .cs_disasm (md , func .address , func .size ))
1198
1215
1199
- exit_addr = self .symbols ['exit' ]
1200
- if self .arch == 'arm' and exit_addr & 1 : exit_addr -= 1
1201
-
1202
- calls = [(i , x ) for i , x in enumerate (dis ) if call_instructions & set (x .groups )]
1216
+ filter_calls = lambda dis : ((i , x ) for i , x in enumerate (dis ) if call_instructions & set (x .groups ))
1217
+ calls = list (filter_calls (dis ))
1203
1218
1204
1219
def find_ret_main_addr (caller_dis , calls ):
1205
1220
call_to_main = - 1
1206
1221
for i , insn in calls :
1207
- if insn .operands [0 ].imm == exit_addr : break
1222
+ if cs .CS_GRP_CALL in insn .groups and insn .operands [imm_index ].imm == exit_addr :
1223
+ break
1208
1224
call_to_main = i
1209
1225
else :
1210
1226
return 0
@@ -1218,17 +1234,19 @@ def find_ret_main_addr(caller_dis, calls):
1218
1234
if ret_addr :
1219
1235
return ret_addr
1220
1236
1237
+ if self .arch in ['ppc' , 'powerpc' , 'powerpc64' ]:
1238
+ filter_calls = lambda dis : ((i , x ) for i , x in enumerate (dis ) if set ([x .mnemonic ]) & set (['bctrl' , 'bl' ]))
1239
+
1221
1240
# `__libc_start_main` -> `__libc_start_call_main` -> `main`
1222
1241
# Find a direct call which calls `exit` once. That's probably `__libc_start_call_main`.
1223
1242
for _ , insn in calls :
1224
- op = insn .operands [0 ]
1243
+ op = insn .operands [imm_index ]
1225
1244
if op .type != cs .CS_OP_IMM : continue
1226
1245
1227
1246
target_addr = op .imm
1228
- # `__libc_start_call_main` is usually smaller than `__libc_start_main`, so
1229
- # we might disassemble a bit too much, but it's a good dynamic estimate.
1230
- callee_dis = list (self .cs_disasm (md , target_addr , func .size ))
1231
- callee_calls = [(i , x ) for i , x in enumerate (callee_dis ) if call_instructions & set (x .groups )]
1247
+ callee_dis = list (self .cs_disasm (md , target_addr , callee_size ))
1248
+ callee_calls = filter_calls (callee_dis )
1249
+
1232
1250
ret_addr = find_ret_main_addr (callee_dis , callee_calls )
1233
1251
if ret_addr :
1234
1252
return ret_addr
0 commit comments