@@ -1170,41 +1170,57 @@ def libc_start_main_return(self):
11701170 if 'exit' not in self .symbols :
11711171 return 0
11721172
1173+ func = self .functions ['__libc_start_main' ]
1174+ exit_addr = self .symbols ['exit' ]
11731175 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+
11741183 # If there's no delay slot, execution continues on the next instruction after a call.
11751184 call_return_offset = 1
1185+ call_instructions = set ([cs .CS_GRP_CALL ])
11761186 if self .arch in ['arm' , 'thumb' ]:
11771187 if b'armhf' in self .linker :
11781188 eabi = 'hf'
1179- call_instructions = set ([ cs . CS_GRP_CALL ])
1189+ if exit_addr & 1 : exit_addr -= 1
11801190 elif self .arch == 'aarch64' :
1181- call_instructions = set ([ cs . CS_GRP_CALL ])
1191+ pass
11821192 elif self .arch in ['mips' , 'mips64' ]:
11831193 # 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 )
11861195 # Account for the delay slot.
11871196 call_return_offset = 2
11881197 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
11901208 else :
11911209 log .error ('Unsupported architecture %s in ELF.libc_start_main_return' , self .arch )
11921210 return 0
11931211
11941212 from pwnlib .asm import get_cs_disassembler
11951213 md = get_cs_disassembler (arch = self .arch , endian = self .endian , bits = self .bits , eabi = eabi )
1196- func = self .functions ['__libc_start_main' ]
11971214 dis = list (self .cs_disasm (md , func .address , func .size ))
11981215
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 ))
12031218
12041219 def find_ret_main_addr (caller_dis , calls ):
12051220 call_to_main = - 1
12061221 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
12081224 call_to_main = i
12091225 else :
12101226 return 0
@@ -1218,17 +1234,19 @@ def find_ret_main_addr(caller_dis, calls):
12181234 if ret_addr :
12191235 return ret_addr
12201236
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+
12211240 # `__libc_start_main` -> `__libc_start_call_main` -> `main`
12221241 # Find a direct call which calls `exit` once. That's probably `__libc_start_call_main`.
12231242 for _ , insn in calls :
1224- op = insn .operands [0 ]
1243+ op = insn .operands [imm_index ]
12251244 if op .type != cs .CS_OP_IMM : continue
12261245
12271246 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+
12321250 ret_addr = find_ret_main_addr (callee_dis , callee_calls )
12331251 if ret_addr :
12341252 return ret_addr
0 commit comments