"""D2 QUICK SELL, batch 5: find the SECOND fcc_discardcoins computation site. THE CONTRADICTION THIS RESOLVES. Measured live (read-only, pid 134663): persistent FUT item objects carry, at item+0x3c, exactly round_half_up(item_rating * price / 100) where price comes from the fcc_discardcoins row (cardtype = item+0x4c, level = item+0x54, rare = item+0x58). Two items with identical cardtype 6 / rare 0 but item+0x54 = 1 and 3 got 3 and 38, which is only explicable if the level key really varies per item. BUT inside FUN_18013fe00 the slot that feeds the "level" argument, [RBP+0x1b4], is provably never written: a whole-DLL byte-pattern search for a modrm with mod=10 rm=101 disp32=0x000001b4 finds exactly one operand in that function and it is the LOAD at 0x18014109a (44 8b 8d b4 01 00 00). So a SECOND site must exist. SEARCHES (all four dispatch forms considered; this is a byte/immediate search, not a "== 0x" grep) A. every function containing the divide-by-100 magic B8 1F 85 EB 51 (MOV EAX,0x51eb851f) or 0x51eb851f in any instruction, cross-referenced with whether it also calls the db-query wrappers. B. xrefs to the four literals "price" 0x1802231e4, "level" 0x180207848, "cardtype" 0x180223208, "rare" 0x18022315c, "fcc_discardcoins" 0x1802231f0. C. every caller of the db wrappers FUN_1801a0000 (from-table) and FUN_18019ff00 (where) and FUN_1801a0080 (get cell). D. what writes item+0x54? search the whole .text for a dword store with disp8/disp32 0x54 is hopeless, so instead: decompile the manager entry mgr->vt[0xa08] target reached from FUN_18013fe00 and look for a level/tier computation, and decompile the tier helper FUN_1800a9fe0 that an earlier agent found returning 1/2/3. CONTROL: search A must report FUN_18013fe00 (it contains MOV EAX,0x51eb851f at 0x180141123). Search B must report the single known xref 0x18014106d for fcc_discardcoins. If either control misses, the search is broken. """ import traceback try: print("##### CONTROL + A: functions containing the /100 magic 0x51eb851f #####") hits = {} it = fm.getFunctions(True) n = 0 while it.hasNext(): f = it.next() n += 1 ii = listing.getInstructions(f.getBody(), True) got = [] while ii.hasNext(): i = ii.next() t = str(i) if "0x51eb851f" in t: got.append((int(i.getAddress().getOffset()), t)) if got: hits[int(f.getEntryPoint().getOffset())] = (f.getName(), got) print(" scanned %d functions, %d contain the magic" % (n, len(hits))) print(" FUN_18013fe00 present: %s" % (0x18013fe00 in hits)) for e in sorted(hits): print(" %#x %s (%d sites)" % (e, hits[e][0], len(hits[e][1]))) print("\n##### B: xrefs to the query literals #####") for nm, a in (("price", 0x1802231e4), ("level", 0x180207848), ("cardtype", 0x180223208), ("rare", 0x18022315c), ("fcc_discardcoins", 0x1802231f0)): xs = xrefs_to(a) print(" %-18s %#x : %d xrefs" % (nm, a, len(xs))) for frm, typ, fn, ent in xs: print(" %#x %s %s %#x" % (frm, typ, fn, ent)) print("\n##### C: callers of the db wrappers #####") for nm, a in (("from-table 0x1801a0000", 0x1801a0000), ("where 0x18019ff00", 0x18019ff00), ("getcell 0x1801a0080", 0x1801a0080), ("rowcount 0x1801a00a0", 0x1801a00a0), ("select 0x1801a0280", 0x1801a0280)): xs = xrefs_to(a) fns = sorted({(ent, fn) for frm, typ, fn, ent in xs if ent}) print(" %-24s %d xrefs from %d functions" % (nm, len(xs), len(fns))) for ent, fn in fns: print(" %#x %s" % (ent, fn)) print("\n##### D: tier helper and the registration entry #####") for a in (0x1800a9fe0,): src = dec(a) print("=" * 78) print("FUN_%x len=%d" % (a, len(src))) print("=" * 78) print(src) print(" xrefs to %#x:" % a) for frm, typ, fn, ent in xrefs_to(a): print(" %#x %s %s %#x" % (frm, typ, fn, ent)) except Exception: traceback.print_exc()