docs(market): recover the FIFA17 sold wire contract from CardsDLL (Ghidra)
Task A, static phase. Ghidra 12.1.2 headless via the repo's own pyghidra harness
over CardsDLL_Win64_retail.dll (13,382 functions). Queries and raw decompiler
output committed under docs/evidence/market-sold-re-2026-08-17/.
RECOVERED FROM THE BINARY
1. No sold token, now EXHAUSTIVELY: both vocabularies dumped to their sentinels
rather than sampled. tradeState is exactly 4 rows; itemState is exactly 12
(invalid/free/WAITING_FOR_GAME/inGame/forSale/offered/activeBadge/
activeHomeKit/activeAwayKit/activeBall/activeStadium/active=255). A sold row
MUST therefore be a combination of existing atoms.
2. What closed does, complete, from the auctionInfo deserializer 0x18013e410:
IS_GLOW = (tradeState==closed) ? bidState != none
: bidState in {outbid, buyNow}
INBOX = bidState in {highest, buyNow}
3. The full record -> Flash map from the publisher 0x1801bf030, superseding the
partial list. The prize: record +0xbf is published as COINS_AWARDED, fed by the
coinsProcessed atom 0x2f4. The corpus had recorded that atom's type and noted
its consumer was never found; it is now traced. DURATION also renders the
localised FUT_AUCTION_EXPIRED when expires underflows.
4. highest vs buyNow on a closed row is UNDECIDABLE from CardsDLL, by proof: both
yield IS_GLOW=1/INBOX=1, bit-identical. But bidState is ALSO published verbatim
as YOURBID alongside STATE and COINS_AWARDED, so the movie does receive the raw
values - the discrimination exists and lives entirely in unread ActionScript.
This retires the question as a static target, and it contradicts the
third-party lore that a seller's sold row is closed+buyNow (the corpus's own
lifecycle table says closed+highest and assigns buyNow to the buyer).
5. The clear-sold verb EXISTS. Builder 0x1801647c0 emits "/sold" when the tradeId
field is zero and "/%lld" otherwise, on route base ut/delete/%s/trade, response
class RS4 FutISRemoveTradeServerResponse. Confirmed by the client's own
request-name table entry RemoveAllSoldFromTradePile. A BULK clear-sold verb only
makes sense if sold rows PERSIST in the seller's pile until cleared, which is
incompatible with our Fix A invariant - so the sold path will require revisiting
it under live validation.
6. The seller's SOLD counter is real, proven end to end with no inference: the hub
tradePile sub-deserializer 0x18013ead0 writes atom sold 0x2c9 to +0x1d8, and the
tile publisher 0x1800b1dc0 renders +0x1d8 as Flash TEXT3 under the localised
caption FUT_TF_SOLD. Siblings: selling -> +0x1d2 -> FUT_TF_SELLING,
count -> +0x1d4 -> FUT_UC_ITEMS, plus FUT_TF_WINNING/FUT_TF_OUTBID on the
Transfer Targets tile. We and the Python oracle both hardcode sold:0, so that
bucket can never fill.
7. Reusable method: an atom id is the INDEX into the alphabetical atom-name pointer
table at base 0x1802d2760. Validated 12/12 against the known auctionInfo atoms
and cross-checked against fifa17-recon/docs/fut_atoms.tsv. Documented gotcha:
resolve a name by the pointer slot INSIDE the table, never by the first matching
string in the binary, or you get confident nonsense.
8. An auction-outcome vocabulary exists (auctionSoldBid 0x39, auctionSoldBuyNow
0x3a, auctionWon*/auctionLost*) but NO deserializer consumes it - every
candidate function was checked for the value-SKIP/atom-loop signature and none
qualifies. Server-side or telemetry only; it does not carry sold state here.
TASK B IS UNDECIDABLE FROM THE CLIENT, and this is a proof of absence: no 0.95 or
0.05 constant of either width, no tax/fee/net/proceeds caption, and no fee
arithmetic anywhere. The client never computes or displays a net, so no experiment
against our own server can measure the rounding - whatever we credit is what it
displays, and there is no oracle. Only an original EA-era seller-balance capture
could settle it. The rule stays an explicit CHOICE (floor the fee, so
fee + proceeds == gross exactly) and is now pinned at the requested boundaries
100/101/119/120/149/150/151/199/200 plus 15,000 and i64::MAX.
Settlement NOT promoted. No production process, port or database was touched.
This commit is contained in:
@@ -0,0 +1,241 @@
|
|||||||
|
# FIFA 17 seller-facing SOLD state — static recovery from CardsDLL
|
||||||
|
|
||||||
|
Reverse engineering of `CardsDLL_Win64_retail.dll`
|
||||||
|
(md5 `4de3493131d7d2ff7f8b360c5ac9b655`), Ghidra 12.1.2 headless via pyghidra,
|
||||||
|
13,382 functions, image base `0x180000000`. Queries and raw output:
|
||||||
|
`docs/evidence/market-sold-re-2026-08-17/`.
|
||||||
|
|
||||||
|
Confidence tags: **[PE]** read from the binary · **[PE-PROOF]** a proven *negative*
|
||||||
|
(an exhaustive absence) · **[PLAN]** corpus prose, not a decompile · **[UNDECIDABLE]**
|
||||||
|
proven not answerable from this binary.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. There is no `sold` token, and now it is exhaustive
|
||||||
|
|
||||||
|
Both vocabularies dumped in full to their sentinels, not sampled. **[PE]**
|
||||||
|
|
||||||
|
`tradeState` table `0x180229e40` — exactly 4 rows, then a `{0,0}` terminator:
|
||||||
|
|
||||||
|
```text
|
||||||
|
active=1 inactive=2 expired=3 closed=4
|
||||||
|
```
|
||||||
|
|
||||||
|
`itemState` table `0x180229cc0` — exactly 12 rows, then a `{0,-1}` terminator:
|
||||||
|
|
||||||
|
```text
|
||||||
|
invalid=0 free=1 WAITING_FOR_GAME=2 inGame=2 forSale=5 offered=6
|
||||||
|
activeBadge=100 activeHomeKit=101 activeAwayKit=102 activeBall=103
|
||||||
|
activeStadium=104 active=255
|
||||||
|
```
|
||||||
|
|
||||||
|
Neither contains `sold`. So the seller's sold state **must** be a combination of
|
||||||
|
existing atoms. This closes the question that previously rested on a partial dump.
|
||||||
|
|
||||||
|
## 2. What `closed` actually does — the complete flag computation
|
||||||
|
|
||||||
|
Decompiled from the `auctionInfo` deserializer `0x18013e410`. `local_70` is
|
||||||
|
`tradeState`, `local_40` is `bidState`. **[PE]**
|
||||||
|
|
||||||
|
```c
|
||||||
|
if (local_70 == 4) { /* tradeState == closed */
|
||||||
|
local_3b = local_40 != 0; /* bidState != none */
|
||||||
|
} else {
|
||||||
|
local_3b = (local_40 - 1U & 0xfffffffd) == 0; /* bidState in {1,3} */
|
||||||
|
}
|
||||||
|
local_3a = local_40 - 2U < 2; /* bidState in {2,3} */
|
||||||
|
```
|
||||||
|
|
||||||
|
Evaluated:
|
||||||
|
|
||||||
|
| | `none`(0) | `outbid`(1) | `highest`(2) | `buyNow`(3) |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `local_3b`, tradeState==closed | 0 | **1** | **1** | **1** |
|
||||||
|
| `local_3b`, otherwise | 0 | 1 | 0 | 1 |
|
||||||
|
| `local_3a` (any tradeState) | 0 | 0 | **1** | **1** |
|
||||||
|
|
||||||
|
## 3. The complete record → Flash mapping
|
||||||
|
|
||||||
|
From the publisher `0x1801bf030`, every property it sets, with its record offset.
|
||||||
|
This supersedes the previous partial list. **[PE]**
|
||||||
|
|
||||||
|
| Flash property | record | meaning |
|
||||||
|
|---|---|---|
|
||||||
|
| `TRADEID_LOWER` / `TRADEID_UPPER` | +0x38 | tradeId, split into two 32-bit halves |
|
||||||
|
| `DURATION` | +0x90 | formatted; **`FUT_AUCTION_EXPIRED`** when the value underflows (i.e. `expires == 0`) |
|
||||||
|
| `TIME_REMAINING` | +0x90 | expires, seconds |
|
||||||
|
| `MIN_CREDITS` | +0x78 | currentBid |
|
||||||
|
| `MAX_CREDITS` | +0x70 | buyNowPrice |
|
||||||
|
| `RESERVEDPRICE` | +0x74 | startingBid |
|
||||||
|
| `YOURBID` | +0xb8 | **bidState, passed through verbatim** |
|
||||||
|
| `STATE` | +0x88 | **tradeState, passed through verbatim** |
|
||||||
|
| **`COINS_AWARDED`** | **+0xbf** | **the `coinsProcessed` atom (0x2f4), u8** |
|
||||||
|
| `UUID_UPPER` / `UUID_LOWER` | itemData+8 | |
|
||||||
|
| `CARD_ID` / `FIFA_ID` | itemData+0x18 | `FIFA_ID` masks `& 0xffffff` |
|
||||||
|
| `CARD_TYPE` | itemData+0x4c | |
|
||||||
|
| `CARD_OFFERSTATE` | itemData+0x5c | itemState |
|
||||||
|
| `IS_WATCHED` | +0xbc | `watched` atom |
|
||||||
|
| `INBOX` | +0xbe | `local_3a` — bidState ∈ {highest, buyNow} |
|
||||||
|
| `IS_GLOW` | +0xbd | `local_3b` — the table in §2 |
|
||||||
|
| `TRADE_DATA_AVAILABLE` | — | constant 1 |
|
||||||
|
|
||||||
|
**`coinsProcessed`'s consumer is now traced.** The corpus recorded its type and
|
||||||
|
noted that no consumer had ever been found; it is published to the movie as
|
||||||
|
**`COINS_AWARDED`**. That is a settlement/"you have been paid" signal, exactly as
|
||||||
|
the corpus guessed but never demonstrated.
|
||||||
|
|
||||||
|
## 4. `highest` vs `buyNow` on a closed row is UNDECIDABLE from CardsDLL
|
||||||
|
|
||||||
|
**[UNDECIDABLE]**, and this is a proof, not a failed search.
|
||||||
|
|
||||||
|
For `tradeState == closed`, §2 gives `IS_GLOW = (bidState != none)` and
|
||||||
|
`INBOX = (bidState ∈ {highest, buyNow})`. Both `highest`(2) and `buyNow`(3)
|
||||||
|
therefore produce **`IS_GLOW=1, INBOX=1`** — bit-identical. No native consumer can
|
||||||
|
tell them apart.
|
||||||
|
|
||||||
|
But §3 sharpens *why* it is undecidable: `bidState` is **not** consumed only through
|
||||||
|
those flags. It is published verbatim as `YOURBID`, alongside `STATE` and
|
||||||
|
`COINS_AWARDED`. The movie receives the raw values. So the discrimination exists —
|
||||||
|
it just lives entirely in the APT/ActionScript front end, which is unread.
|
||||||
|
|
||||||
|
Consequence: **no amount of further CardsDLL work can answer "which `bidState` does
|
||||||
|
a seller see".** Only an AVM1 read of `external/ion_fut/screens/trading/tradepile`,
|
||||||
|
or a live behavioural A/B, can. This retires the question as a static target.
|
||||||
|
|
||||||
|
The corpus's own lifecycle table (`fifa17-recon/docs/plan-2026-08-06-transfer-market.md:720-729`)
|
||||||
|
asserts the seller sees `closed` / **`highest`** with `coinsProcessed 1`, and assigns
|
||||||
|
`closed` / `buyNow` to the *buyer*. That is **[PLAN]**, and it **contradicts** the
|
||||||
|
common third-party lore that a sold seller row is `closed` + `buyNow`. Given §4 the
|
||||||
|
contradiction cannot be resolved statically — but note the corpus reading is the one
|
||||||
|
that leaves `buyNow` meaning "*I* bought it now", which is self-consistent with
|
||||||
|
`YOURBID` being a property about the viewer's own bid.
|
||||||
|
|
||||||
|
## 5. The clear-sold verb EXISTS — PE-proven
|
||||||
|
|
||||||
|
The request builder `0x1801647c0`: **[PE]**
|
||||||
|
|
||||||
|
```c
|
||||||
|
if (*(longlong *)(param_1 + 0x10) == 0) {
|
||||||
|
FUN_180007f80(&local_38, 0x20, "/sold"); /* no tradeId -> bulk */
|
||||||
|
} else {
|
||||||
|
FUN_180007f80(&local_38, 0x20, "/%lld"); /* one specific tradeId */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
One builder, two forms, on route base `ut/delete/%s/trade` (`DELETETRADE`), response
|
||||||
|
class `RS4:FutISRemoveTradeServerResponse` (`0x180228bc8`, with the `/sold` literal
|
||||||
|
at `0x180228bec` immediately after it):
|
||||||
|
|
||||||
|
```text
|
||||||
|
DELETE ut/delete/{ns}/trade/{tradeId} remove one trade
|
||||||
|
DELETE ut/delete/{ns}/trade/sold remove ALL sold trades
|
||||||
|
```
|
||||||
|
|
||||||
|
Corroborated by the client's own request-name table, where
|
||||||
|
**`RemoveAllSoldFromTradePile`** (`0x1801efae8`) sits beside `RemoveFromTradePile`,
|
||||||
|
`AddToWatchList` and `RemoveFromWatchList`.
|
||||||
|
|
||||||
|
**Architectural consequence.** A bulk "remove all sold" verb only makes sense if
|
||||||
|
sold rows **persist in the seller's pile until explicitly cleared**. That is
|
||||||
|
incompatible with our current host, where a sold/cancelled listing leaves
|
||||||
|
`/tradePile` the instant the CAS lands (the Fix A invariant). Implementing the sold
|
||||||
|
path will require revisiting that invariant — and doing so needs live validation,
|
||||||
|
because Fix A itself was a live-confirmed correction.
|
||||||
|
|
||||||
|
## 6. The seller-facing SOLD counter is real, and we hardcode it to 0
|
||||||
|
|
||||||
|
A complete chain, wire atom → struct → Flash → localised caption, with no inference
|
||||||
|
at any step. **[PE]**
|
||||||
|
|
||||||
|
Hub `tradePile` sub-deserializer `0x18013ead0`:
|
||||||
|
|
||||||
|
| atom | id | writes | Flash slot (publisher `0x1800b1dc0`, tile `0x1c0`) |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `count` | 0xbc | +0x1d4 | `TEXT0` with caption `FUT_UC_ITEMS` |
|
||||||
|
| `notification` | 0x1da | +0x1d6 | `NOTIFICATION`, capped at 99 |
|
||||||
|
| `selling` | 0x2b8 | +0x1d2 (**and** counts-struct +0x36) | `TEXT2` with caption `FUT_TF_SELLING` |
|
||||||
|
| **`sold`** | **0x2c9** | **+0x1d8** | **`TEXT3` with caption `FUT_TF_SOLD`** |
|
||||||
|
|
||||||
|
The sibling tile `0x1d0` (Transfer Targets) uses `FUT_TF_WINNING` (+0x1c8) and
|
||||||
|
`FUT_TF_OUTBID`. `selling` writing two structs independently re-confirms the
|
||||||
|
corpus's counts-struct offset for `selling`.
|
||||||
|
|
||||||
|
So FIFA 17 renders a **SOLD** count to the seller, sourced from atom `sold` (0x2c9).
|
||||||
|
Our host and the Python oracle both hardcode `sold: 0`, so that bucket can never
|
||||||
|
populate. This is direct client evidence about `sold` — the thing
|
||||||
|
`MARKET_SOLD_SETTLEMENT.md` required before touching `/tradePile/counts`. It
|
||||||
|
establishes that `sold` is *displayed*; it does **not** yet establish what should be
|
||||||
|
counted in it (rows awaiting clear? rows sold this session?).
|
||||||
|
|
||||||
|
## 7. Atom-name decoder (method note, reusable)
|
||||||
|
|
||||||
|
Atom IDs are **indices into an alphabetically sorted pointer table** of atom-name
|
||||||
|
strings, base `0x1802d2760`. Validated against all twelve known `auctionInfo`
|
||||||
|
atoms — 12/12 agree — and cross-checked against `fifa17-recon/docs/fut_atoms.tsv`.
|
||||||
|
**[PE]**
|
||||||
|
|
||||||
|
```text
|
||||||
|
atom_id = (pointer_slot_address - 0x1802d2760) / 8
|
||||||
|
```
|
||||||
|
|
||||||
|
Newly resolved: `sold`=0x2c9, `count`=0xbc, `offered`=0x1e5, `selling`=0x2b8,
|
||||||
|
`maxAuctionsAllowed`=0x1bf, `credits`=0xc0, `auctionInfo`=0x35, `total`=0x325,
|
||||||
|
`duplicateItemIdList`=0xec, `itemState`=0x172, `offers`=0x1e6, `coins`=0x95.
|
||||||
|
|
||||||
|
Caution when using it: look up a name by finding the pointer slot **inside the table
|
||||||
|
range**, not by taking the first matching string in the binary. Common words such as
|
||||||
|
`sold`, `offered` and `count` appear in several unrelated tables, and taking the
|
||||||
|
first hit produces confident nonsense (it initially reported `sold` as ABSENT and
|
||||||
|
`offered` as a negative index).
|
||||||
|
|
||||||
|
## 8. An auction-outcome vocabulary exists but this client ignores it
|
||||||
|
|
||||||
|
The atom table contains a 7-value outcome vocabulary: **[PE]**
|
||||||
|
|
||||||
|
```text
|
||||||
|
0x36 auctionLostBidRejected 0x39 auctionSoldBid 0x3b auctionWonBid
|
||||||
|
0x37 auctionLostOutbid 0x3a auctionSoldBuyNow 0x3c auctionWonBuyNow
|
||||||
|
0x38 auctionLostOutbidSelf
|
||||||
|
```
|
||||||
|
|
||||||
|
These would distinguish seller-sold-by-bid from seller-sold-by-buy-now explicitly.
|
||||||
|
**No CardsDLL deserializer consumes them.** Every candidate function that compares
|
||||||
|
against three or more of `0x36..0x3c` was checked and none is an atom dispatcher
|
||||||
|
(no value-SKIP `0x180135ff0`, no atom loop `0x1801c7f10`) — they are small-immediate
|
||||||
|
coincidences. **[PE-PROOF]** So the vocabulary is server-side or telemetry, and it
|
||||||
|
does not carry the sold state to this client.
|
||||||
|
|
||||||
|
## 9. The 5% fee is NOT in the client — Task B is undecidable from here
|
||||||
|
|
||||||
|
**[PE-PROOF]**, three independent absences:
|
||||||
|
|
||||||
|
* no `0.95` or `0.05` constant, `double` or `float`, anywhere in the binary;
|
||||||
|
* no localisation key for tax/fee/net/proceeds/commission/"you will receive" — the
|
||||||
|
only `FUT_TF_*` keys in the binary are `SELLING`, `SOLD`, `WINNING`, `OUTBID`;
|
||||||
|
* the 17 functions using both `5`/`95` and `100` as immediates are all unrelated
|
||||||
|
(they include the item deserializer) — no fee arithmetic exists.
|
||||||
|
|
||||||
|
The client therefore never computes or displays a net. It learns the seller's
|
||||||
|
balance only from `credits`.
|
||||||
|
|
||||||
|
**This makes the rounding rule unmeasurable by experiment against our own server.**
|
||||||
|
Whatever we credit is what the client displays; there is no client-side expectation
|
||||||
|
to compare against, so there is no oracle. The only evidence that could settle
|
||||||
|
floor-the-fee (150 → 143) versus floor-the-proceeds (150 → 142) is an original
|
||||||
|
EA-era capture of a seller's balance across a known-price sale, which we do not
|
||||||
|
have.
|
||||||
|
|
||||||
|
Accordingly the rule stays a **documented choice**: fee = `floor(gross × 5 / 100)`,
|
||||||
|
proceeds = `gross − fee`, chosen because `fee + proceeds == gross` holds exactly at
|
||||||
|
every input. It is pinned by regression tests at 0, 1, 19, 20, 21, 39, 40, 100, 101,
|
||||||
|
119, 120, 149, 150, 151, 199, 200, 1 000, 15 000, 15 000 000 and `i64::MAX`, so it
|
||||||
|
cannot drift silently.
|
||||||
|
|
||||||
|
## 10. What would close the remaining gaps
|
||||||
|
|
||||||
|
| Gap | Only remaining route |
|
||||||
|
|---|---|
|
||||||
|
| `highest` vs `buyNow` on a seller's sold row | AVM1 disassembly of `tradepile.isInActiveAuction` / `PreCheckCardOptions`, or a live behavioural A/B (the movie gets `YOURBID` verbatim, so the two ARE separable by the client's behaviour) |
|
||||||
|
| What `sold` should count | live observation with a real sold row |
|
||||||
|
| Whether sold rows persist until cleared | live: does the client issue `DELETE .../trade/sold`? |
|
||||||
|
| The 5% rounding rule | an original EA-era seller-balance capture; nothing in reach |
|
||||||
@@ -344,8 +344,8 @@ Two things it found that the unit tests had not:
|
|||||||
| A sold card leaves the seller's lineup | **TESTED** |
|
| A sold card leaves the seller's lineup | **TESTED** |
|
||||||
| Invalid sales leave the economy unchanged | **TESTED** |
|
| Invalid sales leave the economy unchanged | **TESTED** |
|
||||||
| The fee rate is 5% of gross | **FIFA17-HISTORICAL** |
|
| The fee rate is 5% of gross | **FIFA17-HISTORICAL** |
|
||||||
| The fee is floored (150 → 143, not 142) | **NEEDS-LIVE** |
|
| The fee is floored (150 → 143, not 142) | **UNDECIDABLE from available evidence** — CardsDLL contains no fee arithmetic, no `0.95`/`0.05` constant and no tax/net caption, so the client never computes or displays a net and there is no oracle to measure against. See `FIFA17_SOLD_WIRE_RE.md` §9. Remains a pinned CHOICE. |
|
||||||
| The seller-facing sold wire state | **NEEDS-LIVE** — not implemented, not guessed |
|
| The seller-facing sold wire state | **PARTIALLY RECOVERED** from the binary — see `FIFA17_SOLD_WIRE_RE.md`. Proven: no `sold` token in either vocabulary (exhaustive); `coinsProcessed` is published to Flash as `COINS_AWARDED`; a bulk clear-sold verb `DELETE …/trade/sold` exists; the seller's SOLD counter is real (atom `sold` 0x2c9 → `FUT_TF_SOLD`). Still UNDECIDABLE statically: `highest` vs `buyNow`. |
|
||||||
| The client renders a settled sale correctly | **NEEDS-LIVE** — never exercised |
|
| The client renders a settled sale correctly | **NEEDS-LIVE** — never exercised |
|
||||||
|
|
||||||
No Core test here establishes anything about the FIFA 17 wire contract. They
|
No Core test here establishes anything about the FIFA 17 wire contract. They
|
||||||
|
|||||||
@@ -0,0 +1,308 @@
|
|||||||
|
==============================================================================
|
||||||
|
== auctionInfo deserializer 0x18013e410 — full decompilation
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
/* WARNING: Function: __security_check_cookie replaced with injection: security_check_cookie */
|
||||||
|
|
||||||
|
undefined4 FUN_18013e410(undefined8 param_1,longlong param_2,undefined8 param_3)
|
||||||
|
|
||||||
|
{
|
||||||
|
longlong *plVar1;
|
||||||
|
undefined1 *puVar2;
|
||||||
|
undefined1 *puVar3;
|
||||||
|
undefined ***pppuVar4;
|
||||||
|
undefined ***pppuVar5;
|
||||||
|
char cVar6;
|
||||||
|
int iVar7;
|
||||||
|
int iVar8;
|
||||||
|
undefined8 uVar9;
|
||||||
|
longlong *plVar10;
|
||||||
|
undefined4 uVar11;
|
||||||
|
undefined1 auStack_138 [32];
|
||||||
|
undefined **local_118;
|
||||||
|
undefined8 local_110;
|
||||||
|
longlong local_108;
|
||||||
|
undefined8 local_100;
|
||||||
|
undefined **local_f8;
|
||||||
|
undefined1 local_f0 [32];
|
||||||
|
undefined1 local_d0;
|
||||||
|
longlong *local_c8;
|
||||||
|
undefined8 local_c0;
|
||||||
|
undefined1 local_b0 [36];
|
||||||
|
undefined4 local_8c;
|
||||||
|
undefined4 local_88;
|
||||||
|
undefined4 local_84;
|
||||||
|
undefined4 local_80;
|
||||||
|
int local_70;
|
||||||
|
undefined8 local_68;
|
||||||
|
undefined1 local_58 [8];
|
||||||
|
longlong local_50;
|
||||||
|
longlong *local_48;
|
||||||
|
int local_40;
|
||||||
|
undefined1 local_3c;
|
||||||
|
undefined1 local_3b;
|
||||||
|
undefined1 local_3a;
|
||||||
|
undefined1 local_39;
|
||||||
|
ulonglong local_38;
|
||||||
|
|
||||||
|
local_100 = 0xfffffffffffffffe;
|
||||||
|
local_38 = DAT_1802db6e0 ^ (ulonglong)auStack_138;
|
||||||
|
iVar8 = 0x38c;
|
||||||
|
FUN_18010f5f0(&local_f8);
|
||||||
|
iVar7 = FUN_1801c7f10(param_2);
|
||||||
|
while (iVar7 != 10) {
|
||||||
|
if (*(int *)(param_2 + 0xd0) == 0xb) {
|
||||||
|
iVar8 = FUN_180180d00(*(undefined8 *)(param_2 + 0xf8));
|
||||||
|
iVar7 = FUN_1801c7f10(param_2);
|
||||||
|
if (iVar7 != 6) goto LAB_18013e498;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LAB_18013e498:
|
||||||
|
if (iVar8 < 0x2b8) {
|
||||||
|
if (iVar8 == 0x2b7) {
|
||||||
|
uVar9 = FUN_1801c7aa0(param_2);
|
||||||
|
FUN_180008120(local_b0,uVar9,0x1e);
|
||||||
|
}
|
||||||
|
else if (iVar8 < 0x117) {
|
||||||
|
if (iVar8 == 0x116) {
|
||||||
|
local_68 = FUN_1801c79d0(param_2);
|
||||||
|
}
|
||||||
|
else if (iVar8 == 0x57) {
|
||||||
|
uVar9 = FUN_1801c7aa0(param_2);
|
||||||
|
local_40 = FUN_180166380(uVar9);
|
||||||
|
}
|
||||||
|
else if (iVar8 == 0x65) {
|
||||||
|
uVar9 = FUN_1801c79d0(param_2);
|
||||||
|
local_88 = FUN_1800d7b30(uVar9);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (iVar8 != 0xc1) goto LAB_18013e59b;
|
||||||
|
uVar9 = FUN_1801c79d0(param_2);
|
||||||
|
local_80 = FUN_1800d7b30(uVar9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (iVar8 == 0x16b) {
|
||||||
|
FUN_18013fe00(local_58,param_2,0);
|
||||||
|
}
|
||||||
|
else if (iVar8 == 0x2b6) {
|
||||||
|
uVar9 = FUN_1801c79d0(param_2);
|
||||||
|
local_8c = FUN_1800d7b30(uVar9);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LAB_18013e59b:
|
||||||
|
FUN_180135ff0(iVar8,param_2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (iVar8 == 0x2e6) {
|
||||||
|
uVar9 = FUN_1801c79d0(param_2);
|
||||||
|
local_84 = FUN_1800d7b30(uVar9);
|
||||||
|
}
|
||||||
|
else if (iVar8 == 0x2f4) {
|
||||||
|
uVar9 = FUN_1801c79d0(param_2);
|
||||||
|
local_39 = FUN_1800d7b50(uVar9);
|
||||||
|
}
|
||||||
|
else if (iVar8 == 0x331) {
|
||||||
|
local_c0 = FUN_1801c79d0(param_2);
|
||||||
|
}
|
||||||
|
else if (iVar8 == 0x335) {
|
||||||
|
uVar9 = FUN_1801c7aa0(param_2);
|
||||||
|
local_70 = FUN_180166bd0(uVar9);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (iVar8 != 0x380) goto LAB_18013e59b;
|
||||||
|
cVar6 = FUN_1801c7620(param_2);
|
||||||
|
local_3c = cVar6 != '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iVar7 = FUN_1801c7f10(param_2);
|
||||||
|
}
|
||||||
|
if (local_70 == 4) {
|
||||||
|
local_3b = local_40 != 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
local_3b = (local_40 - 1U & 0xfffffffd) == 0;
|
||||||
|
}
|
||||||
|
local_3a = local_40 - 2U < 2;
|
||||||
|
local_d0 = 1;
|
||||||
|
FUN_180007f60(local_f0,param_3,1);
|
||||||
|
plVar10 = (longlong *)FUN_18011a830();
|
||||||
|
if (local_48 == (longlong *)0x0) {
|
||||||
|
local_110 = 0;
|
||||||
|
local_118 = &PTR_LAB_180214de8;
|
||||||
|
local_108 = 0;
|
||||||
|
(**(code **)(*plVar10 + 0xa00))(plVar10,&local_118,local_c0);
|
||||||
|
if (local_108 == 0) {
|
||||||
|
uVar11 = 1;
|
||||||
|
goto LAB_18013e795;
|
||||||
|
}
|
||||||
|
if ((local_58 != (undefined1 *)(local_108 + 0xa0)) &&
|
||||||
|
(plVar1 = *(longlong **)(local_108 + 0xb0), local_48 != plVar1)) {
|
||||||
|
if (local_48 != (longlong *)0x0) {
|
||||||
|
puVar2 = (undefined1 *)*local_48;
|
||||||
|
puVar3 = *(undefined1 **)(puVar2 + 8);
|
||||||
|
if (local_58 == puVar2) {
|
||||||
|
*local_48 = (longlong)puVar3;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (; puVar3 != (undefined1 *)0x0; puVar3 = *(undefined1 **)(puVar3 + 8)) {
|
||||||
|
if (puVar3 == local_58) {
|
||||||
|
*(undefined8 *)(puVar2 + 8) = *(undefined8 *)(puVar3 + 8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
puVar2 = puVar3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
local_48 = plVar1;
|
||||||
|
if (plVar1 != (longlong *)0x0) {
|
||||||
|
local_50 = *plVar1;
|
||||||
|
*plVar1 = (longlong)local_58;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
local_118 = &PTR_LAB_180214de8;
|
||||||
|
if (local_108 != 0) {
|
||||||
|
pppuVar4 = *(undefined ****)(local_108 + 0x30);
|
||||||
|
pppuVar5 = (undefined ***)pppuVar4[1];
|
||||||
|
if (&local_118 == pppuVar4) {
|
||||||
|
*(undefined ****)(local_108 + 0x30) = pppuVar5;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (; pppuVar5 != (undefined ***)0x0; pppuVar5 = (undefined ***)pppuVar5[1]) {
|
||||||
|
if (pppuVar5 == &local_118) {
|
||||||
|
pppuVar4[1] = pppuVar5[1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pppuVar4 = pppuVar5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(**(code **)(*plVar10 + 0x9f8))(plVar10,param_1,&local_f8);
|
||||||
|
uVar11 = 10;
|
||||||
|
LAB_18013e795:
|
||||||
|
FUN_18000a3c0(local_58);
|
||||||
|
local_f8 = &PTR_FUN_18021b520;
|
||||||
|
while (local_c8 != (longlong *)0x0) {
|
||||||
|
plVar10 = (longlong *)local_c8[1];
|
||||||
|
(**(code **)(*local_c8 + 8))();
|
||||||
|
local_c8 = plVar10;
|
||||||
|
}
|
||||||
|
return uVar11;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== Flash publisher 0x1801bf030 — full decompilation
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
void FUN_1801bf030(longlong *param_1,longlong param_2,undefined8 param_3,undefined8 param_4)
|
||||||
|
|
||||||
|
{
|
||||||
|
longlong lVar1;
|
||||||
|
longlong *plVar2;
|
||||||
|
longlong lVar3;
|
||||||
|
int iVar4;
|
||||||
|
undefined4 local_res8 [2];
|
||||||
|
undefined4 local_res10 [2];
|
||||||
|
undefined4 local_res18 [2];
|
||||||
|
undefined4 local_res20 [2];
|
||||||
|
undefined1 *local_b0;
|
||||||
|
undefined1 *local_a8;
|
||||||
|
undefined1 *local_a0;
|
||||||
|
longlong *local_98;
|
||||||
|
undefined4 local_90;
|
||||||
|
char *local_88;
|
||||||
|
longlong local_80 [2];
|
||||||
|
longlong local_70;
|
||||||
|
longlong *local_68;
|
||||||
|
|
||||||
|
(**(code **)(*param_1 + 8))
|
||||||
|
(param_1,(*(longlong *)(param_2 + 0x38) - *(longlong *)(param_2 + 0x30)) / 0x18,*param_1
|
||||||
|
,param_4,0xfffffffffffffffe);
|
||||||
|
iVar4 = 0;
|
||||||
|
lVar3 = *(longlong *)(param_2 + 0x30);
|
||||||
|
if (lVar3 != *(longlong *)(param_2 + 0x38)) {
|
||||||
|
do {
|
||||||
|
lVar1 = *(longlong *)(lVar3 + 0x10);
|
||||||
|
FUN_1800d8fa0(*(undefined8 *)(lVar1 + 0x38),local_res10,local_res8);
|
||||||
|
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"TRADEID_LOWER",local_res8[0]);
|
||||||
|
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"TRADEID_UPPER",local_res10[0]);
|
||||||
|
FUN_1801a9200(local_80,*(undefined4 *)(lVar1 + 0x6c));
|
||||||
|
local_98 = (longlong *)FUN_1800d7370();
|
||||||
|
local_90 = 1;
|
||||||
|
local_88 = "FUT String";
|
||||||
|
local_b0 = &DAT_1802e13a0;
|
||||||
|
local_a8 = &DAT_1802e13a0;
|
||||||
|
local_a0 = &DAT_1802e13a1;
|
||||||
|
if (*(longlong *)(lVar1 + 0x90) - 1U < 0x20f5800) {
|
||||||
|
FUN_1801c2e50(&local_b0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
plVar2 = (longlong *)FUN_18019e320();
|
||||||
|
(**(code **)(*plVar2 + 400))(plVar2,&local_b0,"FUT_AUCTION_EXPIRED",1);
|
||||||
|
}
|
||||||
|
(**(code **)(*param_1 + 0x30))(param_1,iVar4,"DURATION",local_b0);
|
||||||
|
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"TIME_REMAINING",*(undefined4 *)(lVar1 + 0x90));
|
||||||
|
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"MIN_CREDITS",*(undefined4 *)(lVar1 + 0x78));
|
||||||
|
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"MAX_CREDITS",*(undefined4 *)(lVar1 + 0x70));
|
||||||
|
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"RESERVEDPRICE",*(undefined4 *)(lVar1 + 0x74));
|
||||||
|
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"YOURBID",*(undefined4 *)(lVar1 + 0xb8));
|
||||||
|
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"STATE",*(undefined4 *)(lVar1 + 0x88));
|
||||||
|
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"COINS_AWARDED",*(undefined1 *)(lVar1 + 0xbf));
|
||||||
|
FUN_1800d8fa0(*(undefined8 *)(*(longlong *)(lVar1 + 0xb0) + 8),local_res18,local_res20);
|
||||||
|
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"UUID_UPPER",local_res18[0]);
|
||||||
|
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"UUID_LOWER",local_res20[0]);
|
||||||
|
(**(code **)(*param_1 + 0x20))
|
||||||
|
(param_1,iVar4,"CARD_ID",*(undefined4 *)(*(longlong *)(lVar1 + 0xb0) + 0x18));
|
||||||
|
(**(code **)(*param_1 + 0x20))
|
||||||
|
(param_1,iVar4,"FIFA_ID",*(uint *)(*(longlong *)(lVar1 + 0xb0) + 0x18) & 0xffffff);
|
||||||
|
(**(code **)(*param_1 + 0x20))
|
||||||
|
(param_1,iVar4,"CARD_TYPE",*(undefined4 *)(*(longlong *)(lVar1 + 0xb0) + 0x4c));
|
||||||
|
(**(code **)(*param_1 + 0x20))
|
||||||
|
(param_1,iVar4,"CARD_OFFERSTATE",*(undefined4 *)(*(longlong *)(lVar1 + 0xb0) + 0x5c)
|
||||||
|
);
|
||||||
|
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"IS_WATCHED",*(undefined1 *)(lVar1 + 0xbc));
|
||||||
|
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"INBOX",*(undefined1 *)(lVar1 + 0xbe));
|
||||||
|
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"IS_GLOW",*(undefined1 *)(lVar1 + 0xbd));
|
||||||
|
(**(code **)(*param_1 + 0x18))(param_1,iVar4,"TRADE_DATA_AVAILABLE",1);
|
||||||
|
if (1 < (longlong)local_a0 - (longlong)local_b0) {
|
||||||
|
if (local_b0 != (undefined1 *)0x0) {
|
||||||
|
(**(code **)(*local_98 + 0x18))(local_98,local_b0,(int)local_a0 - (int)local_b0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((1 < local_70 - local_80[0]) && (local_80[0] != 0)) {
|
||||||
|
(**(code **)(*local_68 + 0x18))(local_68,local_80[0],(int)local_70 - (int)local_80[0]);
|
||||||
|
}
|
||||||
|
lVar3 = lVar3 + 0x18;
|
||||||
|
iVar4 = iVar4 + 1;
|
||||||
|
} while (lVar3 != *(longlong *)(param_2 + 0x38));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== strings: sold / clear / inbox / glow / collect
|
||||||
|
==============================================================================
|
||||||
|
sold 0x180228bed 'sold'
|
||||||
|
sold 0x18022f524 'sold'
|
||||||
|
Sold 0x1801efaf1 'SoldFromTradePile'
|
||||||
|
Sold 0x1802302cf 'SoldBid'
|
||||||
|
Sold 0x1802302df 'SoldBuyNow'
|
||||||
|
SOLD 0x18020a0c7 'SOLD'
|
||||||
|
clearSold ABSENT
|
||||||
|
ClearSold ABSENT
|
||||||
|
INBOX 0x180215044 'INBOX'
|
||||||
|
inbox ABSENT
|
||||||
|
IS_GLOW 0x1802151a0 'IS_GLOW'
|
||||||
|
glow ABSENT
|
||||||
|
Glow ABSENT
|
||||||
|
collect 0x1802307e0 'collector'
|
||||||
|
Collect 0x1801ec3c1 'CollectedCards'
|
||||||
|
Collect 0x1801ec3f8 'Collection'
|
||||||
|
Collect 0x1801ec422 'Collection'
|
||||||
|
Collect 0x1801ec449 'Collection'
|
||||||
|
unclaimed 0x18022fd68 'unclaimedPrizesChampionEvents'
|
||||||
|
claim 0x1802099d1 'claimedPack'
|
||||||
|
claim 0x1802099e9 'claimedPack2'
|
||||||
|
claim 0x18022fd6a 'claimedPrizesChampionEvents'
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
"""Q1 — what does FIFA17 do with tradeState == closed(4)?
|
||||||
|
|
||||||
|
Targets the ONE native tradeState consumer branch (cmp [rbp-0x38],0x4 at
|
||||||
|
0x18013e619) inside the auctionInfo deserializer 0x18013e410, and the Flash
|
||||||
|
publisher 0x1801bf030.
|
||||||
|
"""
|
||||||
|
|
||||||
|
print("=" * 78)
|
||||||
|
print("== auctionInfo deserializer 0x18013e410 — full decompilation")
|
||||||
|
print("=" * 78)
|
||||||
|
print(dec(0x18013E410))
|
||||||
|
|
||||||
|
print("=" * 78)
|
||||||
|
print("== Flash publisher 0x1801bf030 — full decompilation")
|
||||||
|
print("=" * 78)
|
||||||
|
print(dec(0x1801BF030))
|
||||||
|
|
||||||
|
print("=" * 78)
|
||||||
|
print("== strings: sold / clear / inbox / glow / collect")
|
||||||
|
print("=" * 78)
|
||||||
|
for needle in (b"sold", b"Sold", b"SOLD", b"clearSold", b"ClearSold",
|
||||||
|
b"INBOX", b"inbox", b"IS_GLOW", b"glow", b"Glow",
|
||||||
|
b"collect", b"Collect", b"unclaimed", b"claim"):
|
||||||
|
hits = find_all(needle, blocks=(".rdata", ".data", ".text"))
|
||||||
|
for h in hits[:14]:
|
||||||
|
s = rd_str(h, 90)
|
||||||
|
if s and len(s) > 2:
|
||||||
|
print(f" {needle.decode():12s} 0x{h:x} {s!r}")
|
||||||
|
if not hits:
|
||||||
|
print(f" {needle.decode():12s} ABSENT")
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
==============================================================================
|
||||||
|
== neighbourhood of SoldBid/SoldBuyNow (0x1802302cf) — read the whole table
|
||||||
|
==============================================================================
|
||||||
|
0x180230280 'auctionLostBidRejected'
|
||||||
|
0x180230298 'auctionLostOutbid'
|
||||||
|
0x1802302b0 'auctionLostOutbidSelf'
|
||||||
|
0x1802302c8 'auctionSoldBid'
|
||||||
|
0x1802302d8 'auctionSoldBuyNow'
|
||||||
|
0x1802302f0 'auctionWonBid'
|
||||||
|
0x180230300 'auctionWonBuyNow'
|
||||||
|
0x180230318 'authToken'
|
||||||
|
0x180230328 'available'
|
||||||
|
0x180230338 'awaykit'
|
||||||
|
0x180230340 'awardCount'
|
||||||
|
0x180230350 'awardedPrizes'
|
||||||
|
0x180230360 'awardItemData'
|
||||||
|
0x180230370 'awardMappings'
|
||||||
|
0x180230380 'awardType'
|
||||||
|
0x180230390 'awardSet'
|
||||||
|
0x1802303a0 'awardSetId'
|
||||||
|
0x1802303ac 'awards'
|
||||||
|
0x1802303b8 'awardValue'
|
||||||
|
0x1802303c8 'badgeDBid'
|
||||||
|
0x1802303d4 'badges'
|
||||||
|
0x1802303dc 'balls'
|
||||||
|
0x1802303e8 'BEGINNER'
|
||||||
|
0x1802303f8 'bestBuilderScore'
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== xrefs to each sold-ish string
|
||||||
|
==============================================================================
|
||||||
|
sold@228bed 0 xref(s)
|
||||||
|
sold@22f524 1 xref(s)
|
||||||
|
from 0x1802d3da8 DATA in ? (entry 0x0)
|
||||||
|
SoldFromTradePile 0 xref(s)
|
||||||
|
SoldBid 0 xref(s)
|
||||||
|
SoldBuyNow 0 xref(s)
|
||||||
|
SOLD@20a0c7 0 xref(s)
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== who reads record +0xbf (COINS_AWARDED) / +0xbe (INBOX) / +0xbd (IS_GLOW)
|
||||||
|
== i.e. the deserializer's coinsProcessed + the two computed flags
|
||||||
|
==============================================================================
|
||||||
|
-- decoder for atom 0x2f4 (coinsProcessed): FUN_1800d7b50
|
||||||
|
|
||||||
|
ulonglong FUN_1800d7b50(longlong param_1)
|
||||||
|
|
||||||
|
{
|
||||||
|
ulonglong in_RAX;
|
||||||
|
byte bVar1;
|
||||||
|
|
||||||
|
if (0 < param_1) {
|
||||||
|
bVar1 = 0xff;
|
||||||
|
if (param_1 < 0xff) {
|
||||||
|
bVar1 = (byte)param_1;
|
||||||
|
}
|
||||||
|
return (ulonglong)bVar1;
|
||||||
|
}
|
||||||
|
return in_RAX & 0xffffffffffffff00;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== route table 0x18021df80 — all 45 rows, looking for a clear/sold route
|
||||||
|
==============================================================================
|
||||||
|
[ 0] 0x18021df80 -> 0x18021e308 'ut/%s/auctionhouse'
|
||||||
|
[ 1] 0x18021df88 -> 0x18021e320 'AUCTIONHOUSE'
|
||||||
|
[ 2] 0x18021df90 -> 0x18021e330 'ut/%s/clubUser'
|
||||||
|
[ 3] 0x18021df98 -> 0x18021e340 'CLUB_USER'
|
||||||
|
[ 4] 0x18021dfa0 -> 0x18021e350 'ut/%s/user/list'
|
||||||
|
[ 5] 0x18021dfa8 -> 0x1801fa0b8 'CLUB_INFO'
|
||||||
|
[ 6] 0x18021dfb0 -> 0x18021e360 'ut/%s/club'
|
||||||
|
[ 7] 0x18021dfb8 -> 0x1801f6438 'CLUB'
|
||||||
|
[ 8] 0x18021dfc0 -> 0x18021e370 'ut/%s/defid'
|
||||||
|
[ 9] 0x18021dfc8 -> 0x18021e37c 'DREAM'
|
||||||
|
[10] 0x18021dfd0 -> 0x18021e388 'ut/%s/squad'
|
||||||
|
[11] 0x18021dfd8 -> 0x18021e394 'SQUAD'
|
||||||
|
[12] 0x18021dfe0 -> 0x18021e3a0 'ut/delete/%s/squad'
|
||||||
|
[13] 0x18021dfe8 -> 0x18021e3b8 'DELETE_SQUAD'
|
||||||
|
[14] 0x18021dff0 -> 0x18021e3c8 'ut/%s/leaderboards/options'
|
||||||
|
[15] 0x18021dff8 -> 0x18021e3e8 'LBOPTIONS'
|
||||||
|
[16] 0x18021e000 -> 0x18021e3f8 'ut/%s/leaderboards'
|
||||||
|
[17] 0x18021e008 -> 0x18021e410 'LBDEFAULT'
|
||||||
|
[18] 0x18021e010 -> 0x18021e420 'ut/%s/activeMessage'
|
||||||
|
[19] 0x18021e018 -> 0x18021e438 'PAFPRACTICE'
|
||||||
|
[20] 0x18021e020 -> 0x18021e444 'ut/%s'
|
||||||
|
[21] 0x18021e028 -> 0x18021e44c 'UT'
|
||||||
|
[22] 0x18021e030 -> 0x18021e450 'ut/%s/user'
|
||||||
|
[23] 0x18021e038 -> 0x18021e45c 'USER'
|
||||||
|
[24] 0x18021e040 -> 0x18021e468 'ut/delete/%s/user'
|
||||||
|
[25] 0x18021e048 -> 0x18021e480 'DELETEUSER'
|
||||||
|
[26] 0x18021e050 -> 0x18021e490 'ut/%s/item'
|
||||||
|
[27] 0x18021e058 -> 0x18021e49c 'ITEMS'
|
||||||
|
[28] 0x18021e060 -> 0x18021e4a8 'ut/%s/item/resource'
|
||||||
|
[29] 0x18021e068 -> 0x18021e4c0 'ITEMS_BY_RES'
|
||||||
|
[30] 0x18021e070 -> 0x18021e4d0 'ut/delete/%s/item'
|
||||||
|
[31] 0x18021e078 -> 0x18021e4e8 'DELETEITEMS'
|
||||||
|
[32] 0x18021e080 -> 0x18021e4f8 'ut/%s/match'
|
||||||
|
[33] 0x18021e088 -> 0x1801ecc34 'MATCH'
|
||||||
|
[34] 0x18021e090 -> 0x18021e508 'ut/%s/sbs'
|
||||||
|
[35] 0x18021e098 -> 0x18021e514 'SBC'
|
||||||
|
[36] 0x18021e0a0 -> 0x18021e518 'ut/%s/tournament'
|
||||||
|
[37] 0x18021e0a8 -> 0x18021e530 'TOURNAMENT'
|
||||||
|
[38] 0x18021e0b0 -> 0x18021e540 'ut/%s/tournament/user'
|
||||||
|
[39] 0x18021e0b8 -> 0x18021e558 'TOURNAMENTUSER'
|
||||||
|
[40] 0x18021e0c0 -> 0x18021e568 'ut/delete/%s/tournament/user'
|
||||||
|
[41] 0x18021e0c8 -> 0x18021e588 'TOURNAMENTQUIT'
|
||||||
|
[42] 0x18021e0d0 -> 0x18021e598 'ut/%s/season'
|
||||||
|
[43] 0x18021e0d8 -> 0x180209fc4 'SEASON'
|
||||||
|
[44] 0x18021e0e0 -> 0x18021e5a8 'ut/%s/season/user'
|
||||||
|
[45] 0x18021e0e8 -> 0x18021e5c0 'SEASONUSER'
|
||||||
|
[46] 0x18021e0f0 -> 0x18021e5d0 'ut/%s/season/%%s/user'
|
||||||
|
[47] 0x18021e0f8 -> 0x18021e5e8 'SEASONUSER_ALTER'
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
"""Q2 — the sold vocabulary: SoldBid / SoldBuyNow / SoldFromTradePile / sold,
|
||||||
|
the coinsProcessed -> COINS_AWARDED path, and the route table (clear-sold?).
|
||||||
|
"""
|
||||||
|
|
||||||
|
print("=" * 78)
|
||||||
|
print("== neighbourhood of SoldBid/SoldBuyNow (0x1802302cf) — read the whole table")
|
||||||
|
print("=" * 78)
|
||||||
|
a = 0x180230280
|
||||||
|
while a < 0x180230400:
|
||||||
|
s = rd_str(a, 60)
|
||||||
|
if s and len(s) > 1 and s.isprintable():
|
||||||
|
print(f" 0x{a:x} {s!r}")
|
||||||
|
a += len(s.encode()) + 1
|
||||||
|
else:
|
||||||
|
a += 1
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 78)
|
||||||
|
print("== xrefs to each sold-ish string")
|
||||||
|
print("=" * 78)
|
||||||
|
for name, sa in (("sold@228bed", 0x180228BED), ("sold@22f524", 0x18022F524),
|
||||||
|
("SoldFromTradePile", 0x1801EFAF1), ("SoldBid", 0x1802302CF),
|
||||||
|
("SoldBuyNow", 0x1802302DF), ("SOLD@20a0c7", 0x18020A0C7)):
|
||||||
|
xs = xrefs_to(sa)
|
||||||
|
print(f" {name:20s} {len(xs)} xref(s)")
|
||||||
|
for x in xs[:8]:
|
||||||
|
print(f" from 0x{x[0]:x} {x[1]} in {x[2]} (entry 0x{x[3]:x})")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 78)
|
||||||
|
print("== who reads record +0xbf (COINS_AWARDED) / +0xbe (INBOX) / +0xbd (IS_GLOW)")
|
||||||
|
print("== i.e. the deserializer's coinsProcessed + the two computed flags")
|
||||||
|
print("=" * 78)
|
||||||
|
print("-- decoder for atom 0x2f4 (coinsProcessed): FUN_1800d7b50")
|
||||||
|
print(dec(0x1800D7B50))
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 78)
|
||||||
|
print("== route table 0x18021df80 — all 45 rows, looking for a clear/sold route")
|
||||||
|
print("=" * 78)
|
||||||
|
for i in range(48):
|
||||||
|
ea = 0x18021DF80 + i * 8
|
||||||
|
try:
|
||||||
|
p = qword(ea)
|
||||||
|
except Exception:
|
||||||
|
break
|
||||||
|
s = ""
|
||||||
|
if 0x180000000 < p < 0x180400000:
|
||||||
|
s = rd_str(p, 120)
|
||||||
|
print(f" [{i:2d}] 0x{ea:x} -> 0x{p:x} {s!r}")
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
==============================================================================
|
||||||
|
== route table continued from 0x18021e0f8 until it stops looking like one
|
||||||
|
==============================================================================
|
||||||
|
0x18021e100 -> 0x18021e600 'ut/%s/season/%%s/reset'
|
||||||
|
0x18021e108 -> 0x18021e618 'SEASONRESET'
|
||||||
|
0x18021e110 -> 0x18021e628 'ut/%s/season/friendly'
|
||||||
|
0x18021e118 -> 0x18021e640 'FRIENDLYSEASON'
|
||||||
|
0x18021e120 -> 0x18021e650 'ut/%s/purchased'
|
||||||
|
0x18021e128 -> 0x18021e660 'PURCHASED'
|
||||||
|
0x18021e130 -> 0x18021e670 'ut/%s/store'
|
||||||
|
0x18021e138 -> 0x18021e67c 'STORE'
|
||||||
|
0x18021e140 -> 0x18021e688 'ut/%s/watchList'
|
||||||
|
0x18021e148 -> 0x18021e698 'WATCHLIST'
|
||||||
|
0x18021e150 -> 0x18021e6a8 'ut/delete/%s/watchList'
|
||||||
|
0x18021e158 -> 0x18021e6c0 'DELETEWATCHLIST'
|
||||||
|
0x18021e160 -> 0x18021e6d0 'ut/%s/tradePile'
|
||||||
|
0x18021e168 -> 0x18021e6e0 'TRADEPILE'
|
||||||
|
0x18021e170 -> 0x18021e6f0 'ut/%s/trade'
|
||||||
|
0x18021e178 -> 0x18021e6fc 'TRADE'
|
||||||
|
0x18021e180 -> 0x18021e708 'ut/delete/%s/trade'
|
||||||
|
0x18021e188 -> 0x18021e720 'DELETETRADE'
|
||||||
|
0x18021e190 -> 0x18021e730 'ut/%s/marketdata'
|
||||||
|
0x18021e198 -> 0x18021e748 'MARKETDATA'
|
||||||
|
0x18021e1a0 -> 0x18021e758 'ut/%s/clientdata'
|
||||||
|
0x18021e1a8 -> 0x18021e770 'CLIENTDATA'
|
||||||
|
0x18021e1b0 -> 0x18021e780 'ut/auth'
|
||||||
|
0x18021e1b8 -> 0x18021e788 'AUTH'
|
||||||
|
0x18021e1c0 -> 0x18021e790 'ut/delete/auth'
|
||||||
|
0x18021e1c8 -> 0x18021e7a0 'DELETE_AUTH'
|
||||||
|
0x18021e1d0 -> 0x18021e7b0 'ut/%s/phishing'
|
||||||
|
0x18021e1d8 -> 0x18021e7c0 'PHISHING'
|
||||||
|
0x18021e1e0 -> 0x18021e7d0 'ut/%s/captcha'
|
||||||
|
0x18021e1e8 -> 0x18021e7e0 'CAPTCHA'
|
||||||
|
0x18021e1f0 -> 0x18021e7e8 'ut/%s/tfa'
|
||||||
|
0x18021e1f8 -> 0x18021e7f4 'TFA'
|
||||||
|
0x18021e200 -> 0x18021e7f8 'ut/%s/squad/mode'
|
||||||
|
0x18021e208 -> 0x18021e810 'SQUADMODE'
|
||||||
|
0x18021e210 -> 0x18021e820 'ut/%s/draft/mode'
|
||||||
|
0x18021e218 -> 0x18021e834 'DRAFT'
|
||||||
|
0x18021e220 -> 0x18021e840 'ut/%s/champion'
|
||||||
|
0x18021e228 -> 0x18021e850 'CHAMPIONS'
|
||||||
|
0x18021e230 -> 0x18021e860 'ut/v2/%s/store'
|
||||||
|
0x18021e238 -> 0x18021e870 'V2STORE'
|
||||||
|
0x18021e240 -> 0x18021e878 'ut/%s/livemessage'
|
||||||
|
0x18021e248 -> 0x18021e890 'LIVEMESSAGE'
|
||||||
|
0x18021e258 -> 0x18021e89c 'ADMIN'
|
||||||
|
0x18021e268 -> 0x18021e8a4 'DEBUG'
|
||||||
|
0x18021e278 -> 0x18021e8b0 'MAINTENANCE'
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== itemState table 0x180229cc0 (prior work: 12 rows) — dump generously
|
||||||
|
==============================================================================
|
||||||
|
[ 0] 0x180229cc0 str=0x18022a2a0 'invalid' val=0 (0x0)
|
||||||
|
[ 1] 0x180229cd0 str=0x18022a2a8 'free' val=1 (0x1)
|
||||||
|
[ 2] 0x180229ce0 str=0x18022a2b0 'WAITING_FOR_GAME' val=2 (0x2)
|
||||||
|
[ 3] 0x180229cf0 str=0x18022a2c4 'inGame' val=2 (0x2)
|
||||||
|
[ 4] 0x180229d00 str=0x18022a2d0 'forSale' val=5 (0x5)
|
||||||
|
[ 5] 0x180229d10 str=0x18022a2d8 'offered' val=6 (0x6)
|
||||||
|
[ 6] 0x180229d20 str=0x18022a2e0 'activeBadge' val=100 (0x64)
|
||||||
|
[ 7] 0x180229d30 str=0x18022a2f0 'activeHomeKit' val=101 (0x65)
|
||||||
|
[ 8] 0x180229d40 str=0x18022a300 'activeAwayKit' val=102 (0x66)
|
||||||
|
[ 9] 0x180229d50 str=0x18022a310 'activeBall' val=103 (0x67)
|
||||||
|
[10] 0x180229d60 str=0x18022a320 'activeStadium' val=104 (0x68)
|
||||||
|
[11] 0x180229d70 str=0x180221c04 'active' val=255 (0xff)
|
||||||
|
[12] 0x180229d80 str=0x0 '' val=4294967295 (0xffffffff)
|
||||||
|
[13] 0x180229d90 str=0x18022a330 'none' val=8 (0x8)
|
||||||
|
[14] 0x180229da0 str=0x18022a338 'head' val=0 (0x0)
|
||||||
|
[15] 0x180229db0 str=0x18022a340 'shoulder' val=1 (0x1)
|
||||||
|
[16] 0x180229dc0 str=0x18022a34c 'arm' val=2 (0x2)
|
||||||
|
[17] 0x180229dd0 str=0x1802009d0 'back' val=3 (0x3)
|
||||||
|
[18] 0x180229de0 str=0x18022a350 'hip' val=4 (0x4)
|
||||||
|
[19] 0x180229df0 str=0x18022a354 'leg' val=5 (0x5)
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== tradeState table 0x180229e40 — dump generously
|
||||||
|
==============================================================================
|
||||||
|
[ 0] 0x180229e40 str=0x180221c04 'active' val=1 (0x1)
|
||||||
|
[ 1] 0x180229e50 str=0x18022a360 'inactive' val=2 (0x2)
|
||||||
|
[ 2] 0x180229e60 str=0x18022a370 'expired' val=3 (0x3)
|
||||||
|
[ 3] 0x180229e70 str=0x18022a378 'closed' val=4 (0x4)
|
||||||
|
[ 4] 0x180229e80 str=0x0 '' val=0 (0x0)
|
||||||
|
[ 5] 0x180229e90 str=0x4e4f5f53544f4853 '' val=1380013151 (0x5241545f)
|
||||||
|
[ 6] 0x180229ea0 str=0x4653534543435553 '' val=1415531605 (0x545f4c55)
|
||||||
|
[ 7] 0x180229eb0 str=0x5345 '' val=1314017091 (0x4e524f43)
|
||||||
|
[ 8] 0x180229ec0 str=0x4548534e41454c43 '' val=5461061 (0x535445)
|
||||||
|
[ 9] 0x180229ed0 str=0x5f474e4953534150 '' val=1129465168 (0x43524550)
|
||||||
|
[10] 0x180229ee0 str=0x4547 '' val=1397968720 (0x53534f50)
|
||||||
|
[11] 0x180229ef0 str=0x45435245505f4e4f '' val=1195463758 (0x4741544e)
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== pointer arrays containing the sold vocabulary
|
||||||
|
==============================================================================
|
||||||
|
auctionSoldBid pointer found at: ['0x1802d2928']
|
||||||
|
auctionSoldBuyNow pointer found at: ['0x1802d2930']
|
||||||
|
auctionWonBuyNow pointer found at: ['0x1802d2940']
|
||||||
|
sold@22f524 pointer found at: ['0x1802d3da8']
|
||||||
|
sold@228bed pointer found at: NOWHERE
|
||||||
|
SoldFromTradePile pointer found at: NOWHERE
|
||||||
|
SOLD@20a0c7 pointer found at: NOWHERE
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== the .data array around 0x1802d3da8 (the one DATA xref to 'sold')
|
||||||
|
==============================================================================
|
||||||
|
0x1802d3d68 -> 0x1801e98ac 'shots'
|
||||||
|
0x1802d3d70 -> 0x18022f4c8 'shotsOnTarget'
|
||||||
|
0x1802d3d78 -> 0x18022f4d8 'silhouetteName'
|
||||||
|
0x1802d3d80 -> 0x18022f4e8 'silName'
|
||||||
|
0x1802d3d88 -> 0x1801ec108 'silver'
|
||||||
|
0x1802d3d90 -> 0x18022f4f0 'silverQuantity'
|
||||||
|
0x1802d3d98 -> 0x18022f500 'sizeBeforeEncode'
|
||||||
|
0x1802d3da0 -> 0x18022f518 'slotIndex'
|
||||||
|
0x1802d3da8 -> 0x18022f524 'sold' <<<
|
||||||
|
0x1802d3db0 -> 0x18022f52c 'sort'
|
||||||
|
0x1802d3db8 -> 0x18022f538 'sortPriority'
|
||||||
|
0x1802d3dc0 -> 0x18022ed30 'source'
|
||||||
|
0x1802d3dc8 -> 0x18022f548 'squad'
|
||||||
|
0x1802d3dd0 -> 0x18022f550 'squadActives'
|
||||||
|
0x1802d3dd8 -> 0x18022f560 'squadBuildingSetsClientData'
|
||||||
|
0x1802d3de0 -> 0x18022f580 'squadBuildingSetsGracePeriodMinutes'
|
||||||
|
0x1802d3de8 -> 0x18022f5a8 'squadChallenge'
|
||||||
|
0x1802d3df0 -> 0x18022f5b8 'squadId'
|
||||||
|
0x1802d3df8 -> 0x18022f5c0 'squadName'
|
||||||
|
0x1802d3e00 -> 0x18022f5d0 'squadList'
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
"""Q3 — full route table, the itemState/tradeState enum tables, and the pointer
|
||||||
|
arrays that reference the auctionSold* vocabulary and the bare 'sold' string.
|
||||||
|
"""
|
||||||
|
import struct
|
||||||
|
|
||||||
|
print("=" * 78)
|
||||||
|
print("== route table continued from 0x18021e0f8 until it stops looking like one")
|
||||||
|
print("=" * 78)
|
||||||
|
a = 0x18021E100
|
||||||
|
misses = 0
|
||||||
|
while a < 0x18021E400 and misses < 6:
|
||||||
|
p = qword(a)
|
||||||
|
s = rd_str(p, 120) if 0x180000000 < p < 0x180400000 else ""
|
||||||
|
if s and s.isprintable():
|
||||||
|
print(f" 0x{a:x} -> 0x{p:x} {s!r}")
|
||||||
|
misses = 0
|
||||||
|
else:
|
||||||
|
misses += 1
|
||||||
|
a += 8
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 78)
|
||||||
|
print("== itemState table 0x180229cc0 (prior work: 12 rows) — dump generously")
|
||||||
|
print("=" * 78)
|
||||||
|
for i in range(20):
|
||||||
|
ea = 0x180229CC0 + i * 16
|
||||||
|
p, v = qword(ea), dword(ea + 8)
|
||||||
|
s = rd_str(p, 40) if 0x180000000 < p < 0x180400000 else ""
|
||||||
|
print(f" [{i:2d}] 0x{ea:x} str=0x{p:x} {s!r:24s} val={v} (0x{v:x})")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 78)
|
||||||
|
print("== tradeState table 0x180229e40 — dump generously")
|
||||||
|
print("=" * 78)
|
||||||
|
for i in range(12):
|
||||||
|
ea = 0x180229E40 + i * 16
|
||||||
|
p, v = qword(ea), dword(ea + 8)
|
||||||
|
s = rd_str(p, 40) if 0x180000000 < p < 0x180400000 else ""
|
||||||
|
print(f" [{i:2d}] 0x{ea:x} str=0x{p:x} {s!r:24s} val={v} (0x{v:x})")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 78)
|
||||||
|
print("== pointer arrays containing the sold vocabulary")
|
||||||
|
print("=" * 78)
|
||||||
|
for label, target in (("auctionSoldBid", 0x1802302C8),
|
||||||
|
("auctionSoldBuyNow", 0x1802302D8),
|
||||||
|
("auctionWonBuyNow", 0x180230300),
|
||||||
|
("sold@22f524", 0x18022F524),
|
||||||
|
("sold@228bed", 0x180228BED),
|
||||||
|
("SoldFromTradePile", 0x1801EFAF1),
|
||||||
|
("SOLD@20a0c7", 0x18020A0C7)):
|
||||||
|
pat = struct.pack("<Q", target)
|
||||||
|
hits = find_all(pat, blocks=(".rdata", ".data", ".text"))
|
||||||
|
print(f" {label:20s} pointer found at: {[hex(h) for h in hits] or 'NOWHERE'}")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 78)
|
||||||
|
print("== the .data array around 0x1802d3da8 (the one DATA xref to 'sold')")
|
||||||
|
print("=" * 78)
|
||||||
|
for i in range(-8, 12):
|
||||||
|
ea = 0x1802D3DA8 + i * 8
|
||||||
|
try:
|
||||||
|
p = qword(ea)
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
s = rd_str(p, 50) if 0x180000000 < p < 0x180400000 else ""
|
||||||
|
mark = " <<<" if i == 0 else ""
|
||||||
|
print(f" 0x{ea:x} -> 0x{p:x} {s!r}{mark}")
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
"""Q4 — is the atom ID the index into the alphabetical atom-name pointer table?
|
||||||
|
|
||||||
|
Validate against the twelve KNOWN auctionInfo atoms. If all twelve agree on one
|
||||||
|
base, the table is the atom dictionary and we can read the ID of any name.
|
||||||
|
"""
|
||||||
|
import struct
|
||||||
|
|
||||||
|
KNOWN = { # atom id -> name, from the confirmed auctionInfo deserializer
|
||||||
|
0x57: "bidState", 0x65: "buyNowPrice", 0xC1: "currentBid", 0x116: "expires",
|
||||||
|
0x16B: "itemData", 0x2B6: "sellerEstablished", 0x2B7: "sellerName",
|
||||||
|
0x2E6: "startingBid", 0x2F4: "coinsProcessed", 0x331: "tradeId",
|
||||||
|
0x335: "tradeState", 0x380: "watched",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def str_addr(name):
|
||||||
|
"""Address of the exact NUL-terminated string `name`."""
|
||||||
|
for h in find_all(name.encode() + b"\x00", blocks=(".rdata", ".data")):
|
||||||
|
return h
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def ptr_addr(sa):
|
||||||
|
hits = find_all(struct.pack("<Q", sa), blocks=(".rdata", ".data"))
|
||||||
|
return hits[0] if hits else None
|
||||||
|
|
||||||
|
|
||||||
|
print("=" * 78)
|
||||||
|
print("== solve for the table base using the twelve known atoms")
|
||||||
|
print("=" * 78)
|
||||||
|
bases = {}
|
||||||
|
for aid, name in sorted(KNOWN.items()):
|
||||||
|
sa = str_addr(name)
|
||||||
|
pa = ptr_addr(sa) if sa else None
|
||||||
|
if pa is None:
|
||||||
|
print(f" 0x{aid:03x} {name:20s} string=0x{sa or 0:x} pointer=NOT FOUND")
|
||||||
|
continue
|
||||||
|
base = pa - 8 * aid
|
||||||
|
bases[base] = bases.get(base, 0) + 1
|
||||||
|
print(f" 0x{aid:03x} {name:20s} str=0x{sa:x} ptr=0x{pa:x} -> base 0x{base:x}")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print(" base histogram:", {hex(b): n for b, n in sorted(bases.items(), key=lambda kv: -kv[1])})
|
||||||
|
if not bases:
|
||||||
|
raise SystemExit("no bases resolved")
|
||||||
|
BASE = max(bases, key=bases.get)
|
||||||
|
print(f" CONSENSUS BASE = 0x{BASE:x} ({bases[BASE]}/{len(KNOWN)} atoms agree)")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 78)
|
||||||
|
print("== read the atom id of every sold/counts-related name")
|
||||||
|
print("=" * 78)
|
||||||
|
for name in ("sold", "selling", "offered", "count", "maxAuctionsAllowed",
|
||||||
|
"auctionSoldBid", "auctionSoldBuyNow", "auctionWonBid",
|
||||||
|
"auctionWonBuyNow", "auctionLostOutbid", "auctionLostOutbidSelf",
|
||||||
|
"auctionLostBidRejected", "credits", "coins", "tradeIdStr",
|
||||||
|
"itemState", "offers", "bids", "watched", "expires"):
|
||||||
|
sa = str_addr(name)
|
||||||
|
pa = ptr_addr(sa) if sa else None
|
||||||
|
if pa is None:
|
||||||
|
print(f" {name:24s} ABSENT from the table")
|
||||||
|
continue
|
||||||
|
off = pa - BASE
|
||||||
|
if off % 8:
|
||||||
|
print(f" {name:24s} ptr=0x{pa:x} misaligned (off {off})")
|
||||||
|
continue
|
||||||
|
print(f" {name:24s} ptr=0x{pa:x} ATOM ID = 0x{off // 8:x} ({off // 8})")
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
==============================================================================
|
||||||
|
== atom ids, resolved against the table range only
|
||||||
|
==============================================================================
|
||||||
|
sold str=0x18022f524 ptr=0x1802d3da8 ATOM ID = 0x2c9 (713)
|
||||||
|
count str=0x180230abc ptr=0x1802d2d40 ATOM ID = 0xbc (188)
|
||||||
|
offered str=0x18022a2d8 ptr=0x1802d3688 ATOM ID = 0x1e5 (485)
|
||||||
|
selling str=0x18022f460 ptr=0x1802d3d20 ATOM ID = 0x2b8 (696)
|
||||||
|
maxAuctionsAllowed str=0x1802318a8 ptr=0x1802d3558 ATOM ID = 0x1bf (447)
|
||||||
|
credits str=0x180230af8 ptr=0x1802d2d60 ATOM ID = 0xc0 (192)
|
||||||
|
auctionInfo str=0x180230270 ptr=0x1802d2908 ATOM ID = 0x35 (53)
|
||||||
|
itemData str=0x180231438 ptr=0x1802d32b8 ATOM ID = 0x16b (363)
|
||||||
|
bidState str=0x180230458 ptr=0x1802d2a18 ATOM ID = 0x57 (87)
|
||||||
|
tradeState str=0x18022fb48 ptr=0x1802d4108 ATOM ID = 0x335 (821)
|
||||||
|
coinsProcessed str=0x18022f768 ptr=0x1802d3f00 ATOM ID = 0x2f4 (756)
|
||||||
|
offers str=0x180231ae4 ptr=0x1802d3690 ATOM ID = 0x1e6 (486)
|
||||||
|
duplicateItemIdList str=0x180230d18 ptr=0x1802d2ec0 ATOM ID = 0xec (236)
|
||||||
|
total str=0x18022fa4c ptr=0x1802d4088 ATOM ID = 0x325 (805)
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== functions that compare against >=3 of the auction-outcome atoms 0x36..0x3c
|
||||||
|
==============================================================================
|
||||||
|
scanned 13308 functions
|
||||||
|
0x180043b90 FUN_180043b90 sees ['0x36', '0x37', '0x38', '0x3c']
|
||||||
|
0x1800455b0 FUN_1800455b0 sees ['0x37', '0x38', '0x39']
|
||||||
|
0x18006b4c0 FUN_18006b4c0 sees ['0x38', '0x39', '0x3c']
|
||||||
|
0x180094ce0 FUN_180094ce0 sees ['0x36', '0x37', '0x38']
|
||||||
|
0x180097c70 FUN_180097c70 sees ['0x36', '0x37', '0x38']
|
||||||
|
0x1800cabb0 FUN_1800cabb0 sees ['0x38', '0x39', '0x3a']
|
||||||
|
0x1800cf3b0 FUN_1800cf3b0 sees ['0x36', '0x38', '0x3a', '0x3c']
|
||||||
|
0x1800e6410 FUN_1800e6410 sees ['0x36', '0x37', '0x38', '0x39']
|
||||||
|
0x1800ee7c0 FUN_1800ee7c0 sees ['0x36', '0x37', '0x38', '0x39', '0x3a', '0x3c']
|
||||||
|
0x1800fb5b0 FUN_1800fb5b0 sees ['0x38', '0x39', '0x3a', '0x3b']
|
||||||
|
0x1801071f0 FUN_1801071f0 sees ['0x36', '0x37', '0x38', '0x3a', '0x3b', '0x3c']
|
||||||
|
0x18012b370 FUN_18012b370 sees ['0x36', '0x37', '0x38', '0x39', '0x3a', '0x3b', '0x3c']
|
||||||
|
0x18012fd40 FUN_18012fd40 sees ['0x36', '0x37', '0x38', '0x3c']
|
||||||
|
0x18013fe00 FUN_18013fe00 sees ['0x36', '0x38', '0x3a', '0x3b']
|
||||||
|
0x180163770 FUN_180163770 sees ['0x36', '0x38', '0x3a']
|
||||||
|
0x180172d00 FUN_180172d00 sees ['0x38', '0x3a', '0x3c']
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== string neighbourhoods: SoldFromTradePile / SOLD / sold
|
||||||
|
==============================================================================
|
||||||
|
-- SoldFromTradePile
|
||||||
|
0x1801efa40 'CardsRequestStaffBonuses'
|
||||||
|
0x1801efa60 'CardsGetStaffBonuses'
|
||||||
|
0x1801efa78 'WatchListRequest'
|
||||||
|
0x1801efa90 'WatchListResultsRequest'
|
||||||
|
0x1801efaa8 'AddToWatchList'
|
||||||
|
0x1801efab8 'RemoveFromWatchList'
|
||||||
|
0x1801efad0 'RemoveFromTradePile'
|
||||||
|
0x1801efae8 'RemoveAllSoldFromTradePile'
|
||||||
|
0x1801efb08 'GetPhishingQuestion'
|
||||||
|
0x1801efb20 'SetPhishingAnswer'
|
||||||
|
0x1801efb38 'ValidatePhishingAnswer'
|
||||||
|
0x1801efb50 'RetrieveTrustedConsoleList'
|
||||||
|
0x1801efb70 'EnableLiveStreaming'
|
||||||
|
-- SOLD
|
||||||
|
0x18020a040 'D_PRIZES'
|
||||||
|
0x18020a050 'EVENT_ID'
|
||||||
|
0x18020a05c 'TEXT0'
|
||||||
|
0x18020a068 'FUT_UC_SQUAD'
|
||||||
|
0x18020a078 'TEXT1'
|
||||||
|
0x18020a080 'FUT_TOTAL_AUCTIONS'
|
||||||
|
0x18020a098 'FUT_UC_ITEMS'
|
||||||
|
0x18020a0a8 'FUT_TF_SELLING'
|
||||||
|
0x18020a0b8 'TEXT2'
|
||||||
|
0x18020a0c0 'FUT_TF_SOLD'
|
||||||
|
0x18020a0cc 'TEXT3'
|
||||||
|
0x18020a0d8 'FUT_TF_WINNING'
|
||||||
|
0x18020a0e8 'FUT_TF_OUTBID'
|
||||||
|
0x18020a0f8 'FUT_GH_TOTAL_PLAYERS_0'
|
||||||
|
0x18020a110 'FUT_GH_TOTAL_PLAYERS_1'
|
||||||
|
0x18020a128 'FUT_GH_TOP_SCORER'
|
||||||
|
-- sold@228bed
|
||||||
|
0x180228bc8 'RS4:FutISRemoveTradeServerResponse'
|
||||||
|
0x180228bec '/sold'
|
||||||
|
0x180228c00 '\\�&'
|
||||||
|
0x180228c11 "�'"
|
||||||
|
0x180228c28 "X�'"
|
||||||
|
0x180228c38 "`�'"
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
"""Q5 — fix the atom lookup, then find the CONSUMER of atoms 0x36..0x3c
|
||||||
|
(auctionLost*/auctionSold*/auctionWon*) and read the sold-related string
|
||||||
|
neighbourhoods.
|
||||||
|
"""
|
||||||
|
import struct
|
||||||
|
|
||||||
|
BASE = 0x1802D2760
|
||||||
|
TABLE_LO, TABLE_HI = 0x1802D2760, 0x1802D4800
|
||||||
|
|
||||||
|
|
||||||
|
def atom_id(name):
|
||||||
|
"""Every string with this exact spelling, then the pointer that lies INSIDE
|
||||||
|
the atom table. Taking the first string match is wrong: common words appear
|
||||||
|
in several unrelated tables."""
|
||||||
|
out = []
|
||||||
|
for sa in find_all(name.encode() + b"\x00", blocks=(".rdata", ".data")):
|
||||||
|
for pa in find_all(struct.pack("<Q", sa), blocks=(".rdata", ".data")):
|
||||||
|
if TABLE_LO <= pa < TABLE_HI and (pa - BASE) % 8 == 0:
|
||||||
|
out.append((sa, pa, (pa - BASE) // 8))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
print("=" * 78)
|
||||||
|
print("== atom ids, resolved against the table range only")
|
||||||
|
print("=" * 78)
|
||||||
|
for name in ("sold", "count", "offered", "selling", "maxAuctionsAllowed",
|
||||||
|
"credits", "auctionInfo", "itemData", "bidState", "tradeState",
|
||||||
|
"coinsProcessed", "offers", "duplicateItemIdList", "total"):
|
||||||
|
res = atom_id(name)
|
||||||
|
if not res:
|
||||||
|
print(f" {name:22s} ABSENT from the atom table")
|
||||||
|
for sa, pa, aid in res:
|
||||||
|
print(f" {name:22s} str=0x{sa:x} ptr=0x{pa:x} ATOM ID = 0x{aid:x} ({aid})")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 78)
|
||||||
|
print("== functions that compare against >=3 of the auction-outcome atoms 0x36..0x3c")
|
||||||
|
print("=" * 78)
|
||||||
|
WANT = set(range(0x36, 0x3D))
|
||||||
|
hits = {}
|
||||||
|
fi = fm.getFunctions(True)
|
||||||
|
n = 0
|
||||||
|
for f in fi:
|
||||||
|
n += 1
|
||||||
|
ent = int(f.getEntryPoint().getOffset())
|
||||||
|
body = f.getBody()
|
||||||
|
seen = set()
|
||||||
|
it = listing.getInstructions(body, True)
|
||||||
|
while it.hasNext():
|
||||||
|
ins = it.next()
|
||||||
|
m = ins.getMnemonicString()
|
||||||
|
if m not in ("CMP", "SUB", "MOV", "LEA"):
|
||||||
|
continue
|
||||||
|
for i in range(ins.getNumOperands()):
|
||||||
|
for o in ins.getOpObjects(i):
|
||||||
|
try:
|
||||||
|
v = int(o.getValue())
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if v in WANT:
|
||||||
|
seen.add(v)
|
||||||
|
if len(seen) >= 3:
|
||||||
|
hits[ent] = (f.getName(), sorted(hex(v) for v in seen))
|
||||||
|
print(f" scanned {n} functions")
|
||||||
|
for ent, (nm, vals) in sorted(hits.items()):
|
||||||
|
print(f" 0x{ent:x} {nm:28s} sees {vals}")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 78)
|
||||||
|
print("== string neighbourhoods: SoldFromTradePile / SOLD / sold")
|
||||||
|
print("=" * 78)
|
||||||
|
for label, lo, hi in (("SoldFromTradePile", 0x1801EFA40, 0x1801EFB80),
|
||||||
|
("SOLD", 0x18020A040, 0x18020A140),
|
||||||
|
("sold@228bed", 0x180228B60, 0x180228C60)):
|
||||||
|
print(f"-- {label}")
|
||||||
|
a = lo
|
||||||
|
while a < hi:
|
||||||
|
s = rd_str(a, 70)
|
||||||
|
if s and len(s) > 1 and s.isprintable():
|
||||||
|
print(f" 0x{a:x} {s!r}")
|
||||||
|
a += len(s.encode()) + 1
|
||||||
|
else:
|
||||||
|
a += 1
|
||||||
@@ -0,0 +1,469 @@
|
|||||||
|
==============================================================================
|
||||||
|
== (a) xrefs: request names + the /sold path suffix
|
||||||
|
==============================================================================
|
||||||
|
RemoveAllSoldFromTradePile 1 xref(s)
|
||||||
|
0x1800293fe DATA in FUN_180028b50 (entry 0x180028b50)
|
||||||
|
RemoveFromTradePile 1 xref(s)
|
||||||
|
0x1800293d4 DATA in FUN_180028b50 (entry 0x180028b50)
|
||||||
|
AddToWatchList 1 xref(s)
|
||||||
|
0x180029380 DATA in FUN_180028b50 (entry 0x180028b50)
|
||||||
|
/sold suffix 1 xref(s)
|
||||||
|
0x180164813 DATA in FUN_1801647c0 (entry 0x1801647c0)
|
||||||
|
RS4:FutISRemoveTradeServerResponse 2 xref(s)
|
||||||
|
0x1801defe9 DATA in Unwind@1801defd0 (entry 0x1801defd0)
|
||||||
|
0x18016488d DATA in FUN_180164860 (entry 0x180164860)
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== (b) xrefs: the Transfer List caption keys
|
||||||
|
==============================================================================
|
||||||
|
FUT_TOTAL_AUCTIONS 1 xref(s)
|
||||||
|
0x1800b1ef8 DATA in FUN_1800b1dc0 (entry 0x1800b1dc0)
|
||||||
|
FUT_UC_ITEMS 4 xref(s)
|
||||||
|
0x1800fbbdc DATA in FUN_1800fbae0 (entry 0x1800fbae0)
|
||||||
|
0x1800fbe5a DATA in FUN_1800fbae0 (entry 0x1800fbae0)
|
||||||
|
0x1800b1fe7 DATA in FUN_1800b1dc0 (entry 0x1800b1dc0)
|
||||||
|
0x1800b21a9 DATA in FUN_1800b1dc0 (entry 0x1800b1dc0)
|
||||||
|
FUT_TF_SELLING 1 xref(s)
|
||||||
|
0x1800b2023 DATA in FUN_1800b1dc0 (entry 0x1800b1dc0)
|
||||||
|
FUT_TF_SOLD 1 xref(s)
|
||||||
|
0x1800b208e DATA in FUN_1800b1dc0 (entry 0x1800b1dc0)
|
||||||
|
FUT_TF_WINNING 1 xref(s)
|
||||||
|
0x1800b21e5 DATA in FUN_1800b1dc0 (entry 0x1800b1dc0)
|
||||||
|
FUT_TF_OUTBID 1 xref(s)
|
||||||
|
0x1800b2250 DATA in FUN_1800b1dc0 (entry 0x1800b1dc0)
|
||||||
|
|
||||||
|
caption publisher candidates (entry -> #caption refs): {'0x1800b1dc0': 7, '0x1800fbae0': 2}
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== decompiled caption publisher 0x1800b1dc0
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
void FUN_1800b1dc0(undefined8 param_1,longlong *param_2)
|
||||||
|
|
||||||
|
{
|
||||||
|
char cVar1;
|
||||||
|
undefined8 uVar2;
|
||||||
|
longlong lVar3;
|
||||||
|
longlong *plVar4;
|
||||||
|
ushort uVar5;
|
||||||
|
longlong *local_res10;
|
||||||
|
undefined1 *local_90;
|
||||||
|
undefined1 *local_88;
|
||||||
|
undefined1 *local_80;
|
||||||
|
longlong *local_78;
|
||||||
|
undefined4 local_70;
|
||||||
|
char *local_68;
|
||||||
|
undefined1 *local_60;
|
||||||
|
undefined1 *local_58;
|
||||||
|
undefined1 *local_50;
|
||||||
|
longlong *local_48;
|
||||||
|
undefined4 local_40;
|
||||||
|
char *local_38;
|
||||||
|
|
||||||
|
uVar2 = FUN_1800d7170();
|
||||||
|
FUN_180009c80(&local_res10,uVar2);
|
||||||
|
lVar3 = (**(code **)(*local_res10 + 0x1f8))(local_res10);
|
||||||
|
local_78 = (longlong *)FUN_1800d7370();
|
||||||
|
local_70 = 1;
|
||||||
|
local_68 = "FUT String";
|
||||||
|
local_90 = &DAT_1802e13a0;
|
||||||
|
local_88 = &DAT_1802e13a0;
|
||||||
|
local_80 = &DAT_1802e13a1;
|
||||||
|
local_48 = (longlong *)FUN_1800d7370();
|
||||||
|
local_40 = 1;
|
||||||
|
local_38 = "FUT String";
|
||||||
|
local_60 = &DAT_1802e13a0;
|
||||||
|
local_58 = &DAT_1802e13a0;
|
||||||
|
local_50 = &DAT_1802e13a1;
|
||||||
|
(**(code **)(*param_2 + 0x58))(param_2,4);
|
||||||
|
(**(code **)(*param_2 + 0x78))(param_2,0,"UPDATE_TYPE",PTR_s_PANEL_UPDATE_1802a4fb0);
|
||||||
|
(**(code **)(*param_2 + 0x70))(param_2,0,"TILE_ID",0x1b0);
|
||||||
|
(**(code **)(*param_2 + 0x70))(param_2,0,"ADDON_ENUM",5);
|
||||||
|
plVar4 = (longlong *)FUN_18019e320();
|
||||||
|
(**(code **)(*plVar4 + 0xb8))(plVar4,&local_90,*(undefined4 *)(lVar3 + 0x38));
|
||||||
|
(**(code **)(*param_2 + 0x78))(param_2,0,"TEXT0",local_90);
|
||||||
|
plVar4 = (longlong *)FUN_18019e320();
|
||||||
|
(**(code **)(*plVar4 + 400))(plVar4,&local_90,"FUT_TOTAL_AUCTIONS",1);
|
||||||
|
(**(code **)(*param_2 + 0x78))(param_2,0,"TEXT1",local_90);
|
||||||
|
(**(code **)(*param_2 + 0x78))(param_2,1,"UPDATE_TYPE",PTR_s_PANEL_UPDATE_1802a4fb0);
|
||||||
|
(**(code **)(*param_2 + 0x70))(param_2,1,"TILE_ID",0x1c0);
|
||||||
|
uVar5 = *(ushort *)(lVar3 + 0x1d6);
|
||||||
|
if (0x62 < uVar5) {
|
||||||
|
uVar5 = 99;
|
||||||
|
}
|
||||||
|
(**(code **)(*param_2 + 0x70))(param_2,1,"NOTIFICATION",uVar5);
|
||||||
|
(**(code **)(*param_2 + 0x70))(param_2,1,"ADDON_ENUM");
|
||||||
|
plVar4 = (longlong *)FUN_18019e320();
|
||||||
|
(**(code **)(*plVar4 + 0xb8))(plVar4,&local_90,*(undefined2 *)(lVar3 + 0x1d4));
|
||||||
|
(**(code **)(*param_2 + 0x78))(param_2,1,"TEXT0",local_90);
|
||||||
|
plVar4 = (longlong *)FUN_18019e320();
|
||||||
|
(**(code **)(*plVar4 + 400))(plVar4,&local_90,"FUT_UC_ITEMS",1);
|
||||||
|
(**(code **)(*param_2 + 0x78))(param_2,1,"TEXT1",local_90);
|
||||||
|
plVar4 = (longlong *)FUN_18019e320();
|
||||||
|
(**(code **)(*plVar4 + 400))(plVar4,&local_90,"FUT_TF_SELLING",1);
|
||||||
|
plVar4 = (longlong *)FUN_18019e320();
|
||||||
|
(**(code **)(*plVar4 + 0xb8))(plVar4,&local_60,*(undefined2 *)(lVar3 + 0x1d2));
|
||||||
|
FUN_18000fbd0(&local_90,local_90,local_60);
|
||||||
|
(**(code **)(*param_2 + 0x78))(param_2,1,"TEXT2",local_90);
|
||||||
|
plVar4 = (longlong *)FUN_18019e320();
|
||||||
|
(**(code **)(*plVar4 + 400))(plVar4,&local_90,"FUT_TF_SOLD",1);
|
||||||
|
plVar4 = (longlong *)FUN_18019e320();
|
||||||
|
(**(code **)(*plVar4 + 0xb8))(plVar4,&local_60,*(undefined2 *)(lVar3 + 0x1d8));
|
||||||
|
FUN_18000fbd0(&local_90,local_90,local_60);
|
||||||
|
(**(code **)(*param_2 + 0x78))(param_2,1,"TEXT3",local_90);
|
||||||
|
(**(code **)(*param_2 + 0x78))(param_2,2,"UPDATE_TYPE",PTR_s_PANEL_UPDATE_1802a4fb0);
|
||||||
|
(**(code **)(*param_2 + 0x70))(param_2,2,"TILE_ID",0x1d0);
|
||||||
|
uVar5 = 99;
|
||||||
|
if (*(ushort *)(lVar3 + 0x1ce) < 99) {
|
||||||
|
uVar5 = *(ushort *)(lVar3 + 0x1ce);
|
||||||
|
}
|
||||||
|
(**(code **)(*param_2 + 0x70))(param_2,2,"NOTIFICATION",uVar5);
|
||||||
|
(**(code **)(*param_2 + 0x70))(param_2,2,"ADDON_ENUM");
|
||||||
|
plVar4 = (longlong *)FUN_18019e320();
|
||||||
|
(**(code **)(*plVar4 + 0xb8))(plVar4,&local_90,*(undefined2 *)(lVar3 + 0x1ca));
|
||||||
|
(**(code **)(*param_2 + 0x78))(param_2,2,"TEXT0",local_90);
|
||||||
|
plVar4 = (longlong *)FUN_18019e320();
|
||||||
|
(**(code **)(*plVar4 + 400))(plVar4,&local_90,"FUT_UC_ITEMS",1);
|
||||||
|
(**(code **)(*param_2 + 0x78))(param_2,2,"TEXT1",local_90);
|
||||||
|
plVar4 = (longlong *)FUN_18019e320();
|
||||||
|
(**(code **)(*plVar4 + 400))(plVar4,&local_90,"FUT_TF_WINNING",1);
|
||||||
|
plVar4 = (longlong *)FUN_18019e320();
|
||||||
|
(**(code **)(*plVar4 + 0xb8))(plVar4,&local_60,*(undefined2 *)(lVar3 + 0x1c8));
|
||||||
|
FUN_18000fbd0(&local_90,local_90,local_60);
|
||||||
|
(**(code **)(*param_2 + 0x78))(param_2,2,"TEXT2",local_90);
|
||||||
|
plVar4 = (longlong *)FUN_18019e320();
|
||||||
|
(**(code **)(*plVar4 + 400))(plVar4,&local_90,"FUT_TF_OUTBID",1);
|
||||||
|
plVar4 = (longlong *)FUN_18019e320();
|
||||||
|
(**(code **)(*plVar4 + 0xb8))(plVar4,&local_60,*(undefined2 *)(lVar3 + 0x1cc));
|
||||||
|
FUN_18000fbd0(&local_90,local_90,local_60);
|
||||||
|
(**(code **)(*param_2 + 0x78))(param_2,2,"TEXT3",local_90);
|
||||||
|
uVar2 = FUN_1800264e0();
|
||||||
|
cVar1 = FUN_180026510(uVar2);
|
||||||
|
if (cVar1 == '\0') {
|
||||||
|
(**(code **)(*param_2 + 0x78))(param_2,3,"UPDATE_TYPE",PTR_s_PANEL_UPDATE_1802a4fb0);
|
||||||
|
(**(code **)(*param_2 + 0x70))(param_2,3,"TILE_ID",0x200);
|
||||||
|
(**(code **)(*param_2 + 0x70))(param_2,3,"ADDON_ENUM");
|
||||||
|
}
|
||||||
|
if ((1 < (longlong)local_50 - (longlong)local_60) && (local_60 != (undefined1 *)0x0)) {
|
||||||
|
(**(code **)(*local_48 + 0x18))(local_48,local_60,(int)local_50 - (int)local_60);
|
||||||
|
}
|
||||||
|
if ((1 < (longlong)local_80 - (longlong)local_90) && (local_90 != (undefined1 *)0x0)) {
|
||||||
|
(**(code **)(*local_78 + 0x18))(local_78,local_90,(int)local_80 - (int)local_90);
|
||||||
|
}
|
||||||
|
(**(code **)(*local_res10 + 8))(local_res10);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== decompiled caption publisher 0x1800fbae0
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
/* WARNING: Function: __security_check_cookie replaced with injection: security_check_cookie */
|
||||||
|
/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */
|
||||||
|
|
||||||
|
uint FUN_1800fbae0(undefined8 param_1,undefined4 param_2,undefined8 *param_3)
|
||||||
|
|
||||||
|
{
|
||||||
|
undefined1 *puVar1;
|
||||||
|
longlong lVar2;
|
||||||
|
uint uVar3;
|
||||||
|
int iVar4;
|
||||||
|
longlong *plVar5;
|
||||||
|
undefined8 uVar6;
|
||||||
|
longlong *plVar7;
|
||||||
|
longlong lVar8;
|
||||||
|
size_t sVar9;
|
||||||
|
size_t sVar10;
|
||||||
|
undefined1 auStack_2e8 [32];
|
||||||
|
undefined4 local_2c8;
|
||||||
|
undefined1 local_2b8;
|
||||||
|
uint local_2b4;
|
||||||
|
undefined4 local_2b0;
|
||||||
|
undefined4 uStack_2ac;
|
||||||
|
undefined1 *local_2a8;
|
||||||
|
undefined1 *local_2a0;
|
||||||
|
undefined1 *local_298;
|
||||||
|
longlong *local_290;
|
||||||
|
undefined4 local_288;
|
||||||
|
char *local_280;
|
||||||
|
undefined1 *local_278;
|
||||||
|
undefined1 *local_270;
|
||||||
|
undefined1 *local_268;
|
||||||
|
longlong *local_260;
|
||||||
|
undefined4 local_258;
|
||||||
|
char *local_250;
|
||||||
|
undefined1 *local_248;
|
||||||
|
undefined1 *local_240;
|
||||||
|
undefined1 *local_238;
|
||||||
|
longlong *local_230;
|
||||||
|
undefined4 local_228;
|
||||||
|
char *local_220;
|
||||||
|
longlong local_218;
|
||||||
|
undefined8 local_210;
|
||||||
|
longlong local_208;
|
||||||
|
longlong *local_200;
|
||||||
|
undefined4 local_1f8;
|
||||||
|
char *local_1f0;
|
||||||
|
void *local_1e8;
|
||||||
|
longlong local_1e0;
|
||||||
|
longlong local_1d8;
|
||||||
|
longlong *local_1d0;
|
||||||
|
undefined8 local_1b8;
|
||||||
|
longlong *local_1a8;
|
||||||
|
undefined8 local_1a0;
|
||||||
|
undefined8 local_198;
|
||||||
|
undefined8 local_190;
|
||||||
|
undefined4 local_188;
|
||||||
|
undefined2 local_184;
|
||||||
|
undefined8 local_180;
|
||||||
|
undefined8 local_178;
|
||||||
|
undefined8 local_170;
|
||||||
|
undefined8 local_168;
|
||||||
|
undefined2 local_160;
|
||||||
|
undefined4 local_15c;
|
||||||
|
ulonglong local_158;
|
||||||
|
undefined8 uStack_150;
|
||||||
|
undefined4 local_148;
|
||||||
|
undefined2 local_144;
|
||||||
|
undefined4 local_140;
|
||||||
|
undefined **local_138;
|
||||||
|
undefined8 local_130;
|
||||||
|
undefined8 local_128;
|
||||||
|
undefined8 local_120;
|
||||||
|
undefined8 local_118;
|
||||||
|
undefined8 local_110;
|
||||||
|
undefined8 local_108;
|
||||||
|
undefined8 local_100;
|
||||||
|
undefined4 local_f8;
|
||||||
|
undefined2 local_f4;
|
||||||
|
undefined1 local_f2;
|
||||||
|
undefined1 local_f0 [168];
|
||||||
|
ulonglong local_48;
|
||||||
|
|
||||||
|
local_1b8 = 0xfffffffffffffffe;
|
||||||
|
local_48 = DAT_1802db6e0 ^ (ulonglong)auStack_2e8;
|
||||||
|
local_200 = (longlong *)FUN_1800d7370();
|
||||||
|
local_218 = 0;
|
||||||
|
local_208 = 0;
|
||||||
|
local_1f8 = 1;
|
||||||
|
local_1f0 = "FUT Vector";
|
||||||
|
FUN_18004fff0(0,0,local_2b8);
|
||||||
|
local_210 = 0;
|
||||||
|
local_2b0 = 0;
|
||||||
|
uVar3 = FUN_1800fb8c0(param_1,param_2,&local_218,&local_2b0);
|
||||||
|
if ((undefined1 *)*param_3 != (undefined1 *)param_3[1]) {
|
||||||
|
*(undefined1 *)*param_3 = 0;
|
||||||
|
param_3[1] = *param_3;
|
||||||
|
}
|
||||||
|
local_2b4 = uVar3;
|
||||||
|
local_260 = (longlong *)FUN_1800d7370();
|
||||||
|
lVar2 = local_218;
|
||||||
|
local_258 = 1;
|
||||||
|
local_250 = "FUT String";
|
||||||
|
local_278 = &DAT_1802e13a0;
|
||||||
|
local_270 = &DAT_1802e13a0;
|
||||||
|
local_268 = &DAT_1802e13a1;
|
||||||
|
if (uVar3 < 2) {
|
||||||
|
if (uVar3 == 1) {
|
||||||
|
local_1a8 = (longlong *)0x0;
|
||||||
|
local_1a0 = 0;
|
||||||
|
local_198 = 0;
|
||||||
|
local_190 = 0;
|
||||||
|
local_188 = 0;
|
||||||
|
local_184 = 0;
|
||||||
|
local_180 = 0;
|
||||||
|
local_178 = 0;
|
||||||
|
local_170 = 0;
|
||||||
|
local_168 = 0;
|
||||||
|
local_160 = 0x100;
|
||||||
|
lVar8 = -1;
|
||||||
|
local_15c = 0xffffffff;
|
||||||
|
local_158 = _DAT_1801f66a0;
|
||||||
|
uStack_150 = _UNK_1801f66a8;
|
||||||
|
local_148 = 0;
|
||||||
|
local_144 = 0;
|
||||||
|
local_140 = 0;
|
||||||
|
local_130 = 0;
|
||||||
|
local_138 = &PTR_LAB_1801eaac0;
|
||||||
|
local_128 = 0;
|
||||||
|
local_120 = 0;
|
||||||
|
local_118 = 0;
|
||||||
|
local_f4 = 0;
|
||||||
|
local_f2 = 0;
|
||||||
|
local_110 = 0;
|
||||||
|
local_108 = 0;
|
||||||
|
local_100 = 0;
|
||||||
|
local_f8 = 0;
|
||||||
|
memset(local_f0,0,0xa0);
|
||||||
|
local_190 = CONCAT44(local_190._4_4_,*(undefined4 *)(lVar2 + 4)) & 0xffffffff00ffffff;
|
||||||
|
FUN_1801362e0(&local_1a8);
|
||||||
|
FUN_180141660(&local_1a8);
|
||||||
|
uVar6 = FUN_1800d7170();
|
||||||
|
FUN_180009c80(&local_2b0,uVar6);
|
||||||
|
plVar5 = (longlong *)CONCAT44(uStack_2ac,local_2b0);
|
||||||
|
local_2c8 = (undefined4)local_190;
|
||||||
|
(**(code **)(*plVar5 + 0x490))(plVar5,&local_1e8,local_15c,local_158 & 0xffffffff);
|
||||||
|
sVar10 = local_1e0 - (longlong)local_1e8;
|
||||||
|
sVar9 = sVar10;
|
||||||
|
if (0 < (longlong)sVar10) {
|
||||||
|
sVar9 = 0;
|
||||||
|
}
|
||||||
|
iVar4 = memcmp(local_1e8,&DAT_1801e9caf,sVar9);
|
||||||
|
if (((iVar4 == 0) && (-1 < (longlong)sVar10)) && ((longlong)sVar10 < 1)) {
|
||||||
|
local_230 = (longlong *)FUN_1800d7370();
|
||||||
|
local_228 = 1;
|
||||||
|
local_220 = "FUT String";
|
||||||
|
local_248 = &DAT_1802e13a0;
|
||||||
|
local_240 = &DAT_1802e13a0;
|
||||||
|
local_238 = &DAT_1802e13a1;
|
||||||
|
local_290 = (longlong *)FUN_1800d7370();
|
||||||
|
local_288 = 1;
|
||||||
|
local_280 = "FUT String";
|
||||||
|
local_2a8 = &DAT_1802e13a0;
|
||||||
|
local_2a0 = &DAT_1802e13a0;
|
||||||
|
local_298 = &DAT_1802e13a1;
|
||||||
|
FUN_18000fbd0(&local_248,"AWARD_LABEL_%i",*(undefined4 *)(lVar2 + 8));
|
||||||
|
plVar7 = (longlong *)FUN_18019e320();
|
||||||
|
puVar1 = local_248;
|
||||||
|
(**(code **)(*plVar7 + 400))(plVar7,&local_2a8,local_248,1);
|
||||||
|
sVar10 = (longlong)local_2a0 - (longlong)local_2a8;
|
||||||
|
sVar9 = sVar10;
|
||||||
|
if (0 < (longlong)sVar10) {
|
||||||
|
sVar9 = 0;
|
||||||
|
}
|
||||||
|
iVar4 = memcmp(local_2a8,&DAT_1801e9caf,sVar9);
|
||||||
|
if (((iVar4 == 0) && (-1 < (longlong)sVar10)) && ((longlong)sVar10 < 1)) {
|
||||||
|
plVar7 = (longlong *)FUN_18019e320();
|
||||||
|
(**(code **)(*plVar7 + 400))(plVar7,&local_2a8,"FUT_UC_ITEMS",1);
|
||||||
|
}
|
||||||
|
FUN_18000fbd0(param_3,&DAT_18021940c,*(undefined4 *)(lVar2 + 0xc));
|
||||||
|
do {
|
||||||
|
lVar8 = lVar8 + 1;
|
||||||
|
} while (local_2a8[lVar8] != '\0');
|
||||||
|
FUN_18000f650(param_3,local_2a8,local_2a8 + lVar8);
|
||||||
|
if (1 < (longlong)local_298 - (longlong)local_2a8) {
|
||||||
|
if (local_2a8 != (undefined1 *)0x0) {
|
||||||
|
(**(code **)(*local_290 + 0x18))(local_290,local_2a8,(int)local_298 - (int)local_2a8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (1 < (longlong)local_238 - (longlong)puVar1) {
|
||||||
|
if (puVar1 != (undefined1 *)0x0) {
|
||||||
|
(**(code **)(*local_230 + 0x18))(local_230,puVar1,(int)local_238 - (int)local_248);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
FUN_18000fbd0(param_3,&DAT_18021940c,*(undefined4 *)(lVar2 + 0xc));
|
||||||
|
do {
|
||||||
|
lVar8 = lVar8 + 1;
|
||||||
|
} while (*(char *)((longlong)local_1e8 + lVar8) != '\0');
|
||||||
|
FUN_18000f650(param_3,local_1e8,(longlong)local_1e8 + lVar8);
|
||||||
|
}
|
||||||
|
if ((1 < local_1d8 - (longlong)local_1e8) && (local_1e8 != (void *)0x0)) {
|
||||||
|
(**(code **)(*local_1d0 + 0x18))(local_1d0,local_1e8,(int)local_1d8 - (int)local_1e8);
|
||||||
|
}
|
||||||
|
(**(code **)(*plVar5 + 8))(plVar5);
|
||||||
|
FUN_18000a3c0(&local_138);
|
||||||
|
plVar5 = local_1a8;
|
||||||
|
while (uVar3 = local_2b4, plVar5 != (longlong *)0x0) {
|
||||||
|
plVar7 = (longlong *)plVar5[1];
|
||||||
|
(**(code **)(*plVar5 + 8))();
|
||||||
|
plVar5 = plVar7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
plVar5 = (longlong *)FUN_18019e320();
|
||||||
|
(**(code **)(*plVar5 + 400))(plVar5,&local_278,"FUT_UC_ITEMS",1);
|
||||||
|
FUN_18000fbd0(param_3,"%i %s",local_2b0,local_278);
|
||||||
|
}
|
||||||
|
if (1 < (longlong)local_268 - (longlong)local_278) {
|
||||||
|
if (local_278 != (undefined1 *)0x0) {
|
||||||
|
(**(code **)(*local_260 + 0x18))(local_260,local_278,(int)local_268 - (int)local_278);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FUN_18004fff0(lVar2,local_210,local_2b8);
|
||||||
|
if (lVar2 != 0) {
|
||||||
|
(**(code **)(*local_200 + 0x18))(local_200,lVar2,((local_208 - lVar2) / 0x38) * 0x38);
|
||||||
|
}
|
||||||
|
return uVar3;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== (c) GetAuctionCount deserializer FUN_180163770
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
/* WARNING: Function: __security_check_cookie replaced with injection: security_check_cookie */
|
||||||
|
|
||||||
|
undefined8 FUN_180163770(undefined8 param_1,undefined8 *param_2)
|
||||||
|
|
||||||
|
{
|
||||||
|
undefined2 uVar1;
|
||||||
|
int iVar2;
|
||||||
|
undefined4 uVar3;
|
||||||
|
undefined8 uVar4;
|
||||||
|
longlong *plVar5;
|
||||||
|
longlong lVar6;
|
||||||
|
undefined1 auStack_168 [32];
|
||||||
|
int local_148 [2];
|
||||||
|
undefined8 local_140;
|
||||||
|
undefined1 local_138 [288];
|
||||||
|
ulonglong local_18;
|
||||||
|
|
||||||
|
local_140 = 0xfffffffffffffffe;
|
||||||
|
local_18 = DAT_1802db6e0 ^ (ulonglong)auStack_168;
|
||||||
|
FUN_1801c63e0(local_138,0,0);
|
||||||
|
local_148[0] = 0x38c;
|
||||||
|
uVar4 = FUN_180008130(*param_2);
|
||||||
|
FUN_1801c8270(local_138,*param_2,uVar4,0);
|
||||||
|
FUN_1801c7f10(local_138);
|
||||||
|
FUN_1801c7f10(local_138);
|
||||||
|
plVar5 = (longlong *)FUN_18011a830();
|
||||||
|
lVar6 = (**(code **)(*plVar5 + 0x130))(plVar5);
|
||||||
|
*(undefined4 *)(lVar6 + 0x30) = 0xffffffff;
|
||||||
|
*(undefined8 *)(lVar6 + 0x34) = 0;
|
||||||
|
iVar2 = FUN_1801c7f10(local_138);
|
||||||
|
while (iVar2 != 10) {
|
||||||
|
iVar2 = FUN_180141ee0(local_148,local_138);
|
||||||
|
if (iVar2 != 6) {
|
||||||
|
if (local_148[0] == 0xbc) {
|
||||||
|
uVar4 = FUN_1801c79d0(local_138);
|
||||||
|
uVar1 = FUN_1800d7b10(uVar4);
|
||||||
|
*(undefined2 *)(lVar6 + 0x34) = uVar1;
|
||||||
|
}
|
||||||
|
else if (local_148[0] == 0x1bf) {
|
||||||
|
uVar4 = FUN_1801c79d0(local_138);
|
||||||
|
uVar3 = FUN_1800d7af0(uVar4);
|
||||||
|
*(undefined4 *)(lVar6 + 0x30) = uVar3;
|
||||||
|
}
|
||||||
|
else if (local_148[0] == 0x1e5) {
|
||||||
|
uVar4 = FUN_1801c79d0(local_138);
|
||||||
|
uVar1 = FUN_1800d7b10(uVar4);
|
||||||
|
*(undefined2 *)(lVar6 + 0x3a) = uVar1;
|
||||||
|
}
|
||||||
|
else if (local_148[0] == 0x2b8) {
|
||||||
|
uVar4 = FUN_1801c79d0(local_138);
|
||||||
|
uVar1 = FUN_1800d7b10(uVar4);
|
||||||
|
*(undefined2 *)(lVar6 + 0x36) = uVar1;
|
||||||
|
}
|
||||||
|
else if (local_148[0] == 0x2c9) {
|
||||||
|
uVar4 = FUN_1801c79d0(local_138);
|
||||||
|
uVar1 = FUN_1800d7b10(uVar4);
|
||||||
|
*(undefined2 *)(lVar6 + 0x38) = uVar1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
FUN_180135ff0(local_148[0],local_138);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iVar2 = FUN_1801c7f10(local_138);
|
||||||
|
}
|
||||||
|
*(undefined1 *)(lVar6 + 0x28) = 1;
|
||||||
|
FUN_1801c6560(local_138);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
"""Q6 — the decisive reads:
|
||||||
|
(a) who builds the /sold request (clear-all-sold) and what it is called;
|
||||||
|
(b) who publishes FUT_TF_SELLING / FUT_TF_SOLD / FUT_TF_WINNING / FUT_TF_OUTBID,
|
||||||
|
i.e. which count feeds the seller's SOLD bucket;
|
||||||
|
(c) the GetAuctionCount deserializer, to pin the five counts atoms.
|
||||||
|
"""
|
||||||
|
|
||||||
|
print("=" * 78)
|
||||||
|
print("== (a) xrefs: request names + the /sold path suffix")
|
||||||
|
print("=" * 78)
|
||||||
|
for label, a in (("RemoveAllSoldFromTradePile", 0x1801EFAE8),
|
||||||
|
("RemoveFromTradePile", 0x1801EFAD0),
|
||||||
|
("AddToWatchList", 0x1801EFAA8),
|
||||||
|
("/sold suffix", 0x180228BEC),
|
||||||
|
("RS4:FutISRemoveTradeServerResponse", 0x180228BC8)):
|
||||||
|
xs = xrefs_to(a)
|
||||||
|
print(f" {label:36s} {len(xs)} xref(s)")
|
||||||
|
for x in xs[:10]:
|
||||||
|
print(f" 0x{x[0]:x} {x[1]:12s} in {x[2]} (entry 0x{x[3]:x})")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 78)
|
||||||
|
print("== (b) xrefs: the Transfer List caption keys")
|
||||||
|
print("=" * 78)
|
||||||
|
caps = (("FUT_TOTAL_AUCTIONS", 0x18020A080), ("FUT_UC_ITEMS", 0x18020A098),
|
||||||
|
("FUT_TF_SELLING", 0x18020A0A8), ("FUT_TF_SOLD", 0x18020A0C0),
|
||||||
|
("FUT_TF_WINNING", 0x18020A0D8), ("FUT_TF_OUTBID", 0x18020A0E8))
|
||||||
|
owners = {}
|
||||||
|
for label, a in caps:
|
||||||
|
xs = xrefs_to(a)
|
||||||
|
print(f" {label:20s} {len(xs)} xref(s)")
|
||||||
|
for x in xs[:10]:
|
||||||
|
print(f" 0x{x[0]:x} {x[1]:12s} in {x[2]} (entry 0x{x[3]:x})")
|
||||||
|
owners[x[3]] = owners.get(x[3], 0) + 1
|
||||||
|
|
||||||
|
print()
|
||||||
|
print(" caption publisher candidates (entry -> #caption refs):",
|
||||||
|
{hex(k): v for k, v in sorted(owners.items(), key=lambda kv: -kv[1])})
|
||||||
|
|
||||||
|
for ent in sorted(owners, key=lambda e: -owners[e])[:2]:
|
||||||
|
print()
|
||||||
|
print("=" * 78)
|
||||||
|
print(f"== decompiled caption publisher 0x{ent:x}")
|
||||||
|
print("=" * 78)
|
||||||
|
print(dec(ent))
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 78)
|
||||||
|
print("== (c) GetAuctionCount deserializer FUN_180163770")
|
||||||
|
print("=" * 78)
|
||||||
|
print(dec(0x180163770))
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
==============================================================================
|
||||||
|
== (a) hub tradePile sub-deserializer 0x18013ead0
|
||||||
|
== atom ids of interest: count=0xbc notification=0x1da selling=0x2b8 sold=0x2c9
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
undefined4 FUN_18013ead0(longlong param_1,longlong param_2,longlong param_3)
|
||||||
|
|
||||||
|
{
|
||||||
|
undefined2 uVar1;
|
||||||
|
int iVar2;
|
||||||
|
int iVar3;
|
||||||
|
undefined8 uVar4;
|
||||||
|
|
||||||
|
iVar3 = 0x38c;
|
||||||
|
iVar2 = FUN_1801c7f10(param_3);
|
||||||
|
do {
|
||||||
|
if (iVar2 == 10) {
|
||||||
|
*(undefined1 *)(param_1 + 0x1da) = 1;
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
if (*(int *)(param_3 + 0xd0) == 0xb) {
|
||||||
|
iVar3 = FUN_180180d00(*(undefined8 *)(param_3 + 0xf8));
|
||||||
|
iVar2 = FUN_1801c7f10(param_3);
|
||||||
|
if (iVar2 != 6) goto LAB_18013eb2e;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LAB_18013eb2e:
|
||||||
|
if (iVar3 == 0xbc) {
|
||||||
|
uVar4 = FUN_1801c79d0(param_3);
|
||||||
|
uVar1 = FUN_1800d7b10(uVar4);
|
||||||
|
*(undefined2 *)(param_1 + 0x1d4) = uVar1;
|
||||||
|
}
|
||||||
|
else if (iVar3 == 0x1da) {
|
||||||
|
uVar4 = FUN_1801c79d0(param_3);
|
||||||
|
uVar1 = FUN_1800d7b10(uVar4);
|
||||||
|
*(undefined2 *)(param_1 + 0x1d6) = uVar1;
|
||||||
|
}
|
||||||
|
else if (iVar3 == 0x2b8) {
|
||||||
|
uVar4 = FUN_1801c79d0(param_3);
|
||||||
|
uVar1 = FUN_1800d7b10(uVar4);
|
||||||
|
*(undefined2 *)(param_1 + 0x1d2) = uVar1;
|
||||||
|
uVar4 = FUN_1801c79d0(param_3);
|
||||||
|
uVar1 = FUN_1800d7b10(uVar4);
|
||||||
|
*(undefined2 *)(param_2 + 0x36) = uVar1;
|
||||||
|
}
|
||||||
|
else if (iVar3 == 0x2c9) {
|
||||||
|
uVar4 = FUN_1801c79d0(param_3);
|
||||||
|
uVar1 = FUN_1800d7b10(uVar4);
|
||||||
|
*(undefined2 *)(param_1 + 0x1d8) = uVar1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
FUN_180135ff0(iVar3,param_3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iVar2 = FUN_1801c7f10(param_3);
|
||||||
|
} while( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== (b) /sold request builder 0x1801647c0
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
/* WARNING: Function: __security_check_cookie replaced with injection: security_check_cookie */
|
||||||
|
|
||||||
|
void FUN_1801647c0(longlong param_1,undefined8 param_2,undefined4 param_3)
|
||||||
|
|
||||||
|
{
|
||||||
|
undefined1 auStack_58 [32];
|
||||||
|
undefined1 local_38;
|
||||||
|
undefined8 local_37;
|
||||||
|
undefined8 local_2f;
|
||||||
|
undefined8 local_27;
|
||||||
|
undefined4 local_1f;
|
||||||
|
undefined2 local_1b;
|
||||||
|
undefined1 local_19;
|
||||||
|
ulonglong local_18;
|
||||||
|
|
||||||
|
local_18 = DAT_1802db6e0 ^ (ulonglong)auStack_58;
|
||||||
|
local_38 = 0;
|
||||||
|
local_37 = 0;
|
||||||
|
local_2f = 0;
|
||||||
|
local_27 = 0;
|
||||||
|
local_1f = 0;
|
||||||
|
local_1b = 0;
|
||||||
|
local_19 = 0;
|
||||||
|
if (*(longlong *)(param_1 + 0x10) == 0) {
|
||||||
|
FUN_180007f80(&local_38,0x20,"/sold");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
FUN_180007f80(&local_38,0x20,"/%lld");
|
||||||
|
}
|
||||||
|
FUN_180008100(param_2,&local_38,param_3);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== (c) FutGetAuctionCount deserializer 0x180163770
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
/* WARNING: Function: __security_check_cookie replaced with injection: security_check_cookie */
|
||||||
|
|
||||||
|
undefined8 FUN_180163770(undefined8 param_1,undefined8 *param_2)
|
||||||
|
|
||||||
|
{
|
||||||
|
undefined2 uVar1;
|
||||||
|
int iVar2;
|
||||||
|
undefined4 uVar3;
|
||||||
|
undefined8 uVar4;
|
||||||
|
longlong *plVar5;
|
||||||
|
longlong lVar6;
|
||||||
|
undefined1 auStack_168 [32];
|
||||||
|
int local_148 [2];
|
||||||
|
undefined8 local_140;
|
||||||
|
undefined1 local_138 [288];
|
||||||
|
ulonglong local_18;
|
||||||
|
|
||||||
|
local_140 = 0xfffffffffffffffe;
|
||||||
|
local_18 = DAT_1802db6e0 ^ (ulonglong)auStack_168;
|
||||||
|
FUN_1801c63e0(local_138,0,0);
|
||||||
|
local_148[0] = 0x38c;
|
||||||
|
uVar4 = FUN_180008130(*param_2);
|
||||||
|
FUN_1801c8270(local_138,*param_2,uVar4,0);
|
||||||
|
FUN_1801c7f10(local_138);
|
||||||
|
FUN_1801c7f10(local_138);
|
||||||
|
plVar5 = (longlong *)FUN_18011a830();
|
||||||
|
lVar6 = (**(code **)(*plVar5 + 0x130))(plVar5);
|
||||||
|
*(undefined4 *)(lVar6 + 0x30) = 0xffffffff;
|
||||||
|
*(undefined8 *)(lVar6 + 0x34) = 0;
|
||||||
|
iVar2 = FUN_1801c7f10(local_138);
|
||||||
|
while (iVar2 != 10) {
|
||||||
|
iVar2 = FUN_180141ee0(local_148,local_138);
|
||||||
|
if (iVar2 != 6) {
|
||||||
|
if (local_148[0] == 0xbc) {
|
||||||
|
uVar4 = FUN_1801c79d0(local_138);
|
||||||
|
uVar1 = FUN_1800d7b10(uVar4);
|
||||||
|
*(undefined2 *)(lVar6 + 0x34) = uVar1;
|
||||||
|
}
|
||||||
|
else if (local_148[0] == 0x1bf) {
|
||||||
|
uVar4 = FUN_1801c79d0(local_138);
|
||||||
|
uVar3 = FUN_1800d7af0(uVar4);
|
||||||
|
*(undefined4 *)(lVar6 + 0x30) = uVar3;
|
||||||
|
}
|
||||||
|
else if (local_148[0] == 0x1e5) {
|
||||||
|
uVar4 = FUN_1801c79d0(local_138);
|
||||||
|
uVar1 = FUN_1800d7b10(uVar4);
|
||||||
|
*(undefined2 *)(lVar6 + 0x3a) = uVar1;
|
||||||
|
}
|
||||||
|
else if (local_148[0] == 0x2b8) {
|
||||||
|
uVar4 = FUN_1801c79d0(local_138);
|
||||||
|
uVar1 = FUN_1800d7b10(uVar4);
|
||||||
|
*(undefined2 *)(lVar6 + 0x36) = uVar1;
|
||||||
|
}
|
||||||
|
else if (local_148[0] == 0x2c9) {
|
||||||
|
uVar4 = FUN_1801c79d0(local_138);
|
||||||
|
uVar1 = FUN_1800d7b10(uVar4);
|
||||||
|
*(undefined2 *)(lVar6 + 0x38) = uVar1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
FUN_180135ff0(local_148[0],local_138);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iVar2 = FUN_1801c7f10(local_138);
|
||||||
|
}
|
||||||
|
*(undefined1 *)(lVar6 + 0x28) = 1;
|
||||||
|
FUN_1801c6560(local_138);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== callers of the hub tradePile sub-deserializer (which response embeds it)
|
||||||
|
==============================================================================
|
||||||
|
0x180139610 FUN_180139610
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
"""Q7 — close it:
|
||||||
|
(a) hub tradePile sub-deserializer 0x18013ead0: which ATOM writes +0x1d8 (the
|
||||||
|
offset the FUT_TF_SOLD caption reads)?
|
||||||
|
(b) the /sold request builder 0x1801647c0 — bulk clear, or per-trade?
|
||||||
|
(c) FutGetAuctionCount deserializer 0x180163770 — the five counts atoms.
|
||||||
|
"""
|
||||||
|
|
||||||
|
print("=" * 78)
|
||||||
|
print("== (a) hub tradePile sub-deserializer 0x18013ead0")
|
||||||
|
print("== atom ids of interest: count=0xbc notification=0x1da selling=0x2b8 sold=0x2c9")
|
||||||
|
print("=" * 78)
|
||||||
|
print(dec(0x18013EAD0))
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 78)
|
||||||
|
print("== (b) /sold request builder 0x1801647c0")
|
||||||
|
print("=" * 78)
|
||||||
|
print(dec(0x1801647C0))
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 78)
|
||||||
|
print("== (c) FutGetAuctionCount deserializer 0x180163770")
|
||||||
|
print("=" * 78)
|
||||||
|
print(dec(0x180163770))
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 78)
|
||||||
|
print("== callers of the hub tradePile sub-deserializer (which response embeds it)")
|
||||||
|
print("=" * 78)
|
||||||
|
for a, n in callers(0x18013EAD0):
|
||||||
|
print(f" 0x{a:x} {n}")
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
"""Q8 — are atoms 0x36..0x3c (auctionLost*/auctionSold*/auctionWon*) real response
|
||||||
|
keys? If a deserializer dispatches on them, the server tells the client HOW an
|
||||||
|
auction concluded via its own atom, and bidState is not the carrier.
|
||||||
|
|
||||||
|
Signature of a genuine atom dispatcher: the SAME local compared against many atom
|
||||||
|
ids, with FUN_180135ff0 (value-SKIP) as the else, and FUN_1801c7f10 driving the loop.
|
||||||
|
"""
|
||||||
|
|
||||||
|
CANDIDATES = (0x18012B370, 0x1801071F0, 0x1800EE7C0, 0x1800FB5B0, 0x1800CF3B0)
|
||||||
|
|
||||||
|
for ent in CANDIDATES:
|
||||||
|
print("=" * 78)
|
||||||
|
print(f"== 0x{ent:x} callees include value-SKIP? "
|
||||||
|
f"{any(c[0] == 0x180135FF0 for c in callees(ent))}"
|
||||||
|
f" atom-loop? {any(c[0] == 0x1801C7F10 for c in callees(ent))}")
|
||||||
|
print("=" * 78)
|
||||||
|
body = dec(ent)
|
||||||
|
# Only print if it smells like an atom dispatcher, else just report.
|
||||||
|
if "FUN_180135ff0" in body or "FUN_1801c7f10" in body:
|
||||||
|
print(body[:6000])
|
||||||
|
else:
|
||||||
|
print(" NOT an atom dispatcher (no value-SKIP, no atom loop) — small-immediate noise")
|
||||||
|
print(f" size {len(body)} chars; first 400:")
|
||||||
|
print(body[:400])
|
||||||
|
print()
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
==============================================================================
|
||||||
|
== localisation / string keys about tax, fee, net proceeds
|
||||||
|
==============================================================================
|
||||||
|
TAX (no printable hits)
|
||||||
|
tax (no printable hits)
|
||||||
|
Tax (no printable hits)
|
||||||
|
FEE 0x18023a3a8 'FEET_ATTEMPTED'
|
||||||
|
FEE 0x18023a3c0 'FEET_COMPLETED'
|
||||||
|
_FEE 0x18023a3a7 '_FEET_ATTEMPTED'
|
||||||
|
_FEE 0x18023a3bf '_FEET_COMPLETED'
|
||||||
|
fee (no printable hits)
|
||||||
|
NET 0x180209b1c 'NETOURNAMENT_0'
|
||||||
|
NET 0x180209b3c 'NETOURNAMENT_1'
|
||||||
|
NET 0x18020dd10 'NET'
|
||||||
|
_NET 0x18020dd0f '_NET'
|
||||||
|
RECEIVE 0x18023933f 'RECEIVER'
|
||||||
|
RECEIVE 0x180239350 'RECEIVER'
|
||||||
|
RECEIVE 0x18023983f 'RECEIVER'
|
||||||
|
RECEIVE 0x18023990d 'RECEIVER'
|
||||||
|
RECEIVE 0x180239a0b 'RECEIVER'
|
||||||
|
RECEIVE 0x18023a06f 'RECEIVED'
|
||||||
|
Receive (no printable hits)
|
||||||
|
PROCEEDS (no printable hits)
|
||||||
|
COMMISSION (no printable hits)
|
||||||
|
EA_TAX (no printable hits)
|
||||||
|
FUT_TF_ 0x18020a0a8 'FUT_TF_SELLING'
|
||||||
|
FUT_TF_ 0x18020a0c0 'FUT_TF_SOLD'
|
||||||
|
FUT_TF_ 0x18020a0d8 'FUT_TF_WINNING'
|
||||||
|
FUT_TF_ 0x18020a0e8 'FUT_TF_OUTBID'
|
||||||
|
AFTER_TAX (no printable hits)
|
||||||
|
earnings (no printable hits)
|
||||||
|
Earnings (no printable hits)
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== float constants 0.95 / 0.05 / 0.95f / 0.05f
|
||||||
|
==============================================================================
|
||||||
|
double 0.95 ABSENT
|
||||||
|
double 0.05 ABSENT
|
||||||
|
float 0.95f ABSENT
|
||||||
|
float 0.05f ABSENT
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
== functions using BOTH 95 (or 5) and 100 as immediates — integer fee math
|
||||||
|
==============================================================================
|
||||||
|
17 candidate(s)
|
||||||
|
0x18000b5c0 FUN_18000b5c0 immediates [5, 100]
|
||||||
|
0x180051cd0 FUN_180051cd0 immediates [5, 20, 100]
|
||||||
|
0x180057b00 FUN_180057b00 immediates [5, 100]
|
||||||
|
0x180082c30 FUN_180082c30 immediates [5, 100]
|
||||||
|
0x180088cb0 FUN_180088cb0 immediates [5, 100]
|
||||||
|
0x1800a3cb0 FUN_1800a3cb0 immediates [5, 100]
|
||||||
|
0x1800a47b0 FUN_1800a47b0 immediates [5, 100]
|
||||||
|
0x1800d5050 FUN_1800d5050 immediates [5, 20, 100]
|
||||||
|
0x1800d5450 FUN_1800d5450 immediates [5, 20, 100]
|
||||||
|
0x1801129f0 FUN_1801129f0 immediates [5, 100]
|
||||||
|
0x18013af30 FUN_18013af30 immediates [5, 20, 100]
|
||||||
|
0x18013ec10 FUN_18013ec10 immediates [5, 100]
|
||||||
|
0x18013fe00 FUN_18013fe00 immediates [5, 100]
|
||||||
|
0x180147070 FUN_180147070 immediates [5, 100]
|
||||||
|
0x180180ea0 FUN_180180ea0 immediates [5, 100]
|
||||||
|
0x1801adae0 FUN_1801adae0 immediates [5, 100]
|
||||||
|
0x1801b9e60 FUN_1801b9e60 immediates [5, 100]
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
"""Task B — does FIFA17.exe itself compute the transfer fee / net proceeds?
|
||||||
|
|
||||||
|
If the list-item confirmation screen shows "you will receive N", the rounding rule
|
||||||
|
is IN THE BINARY and needs no live measurement. Look for:
|
||||||
|
* localisation keys mentioning tax/fee/net/receive
|
||||||
|
* float constants 0.95 / 0.05
|
||||||
|
* integer 95 or 5 used with 100 in the same function
|
||||||
|
"""
|
||||||
|
import struct
|
||||||
|
|
||||||
|
print("=" * 78)
|
||||||
|
print("== localisation / string keys about tax, fee, net proceeds")
|
||||||
|
print("=" * 78)
|
||||||
|
for needle in (b"TAX", b"tax", b"Tax", b"FEE", b"_FEE", b"fee", b"NET", b"_NET",
|
||||||
|
b"RECEIVE", b"Receive", b"PROCEEDS", b"COMMISSION", b"EA_TAX",
|
||||||
|
b"FUT_TF_", b"AFTER_TAX", b"earnings", b"Earnings"):
|
||||||
|
hits = find_all(needle, blocks=(".rdata", ".data"))
|
||||||
|
shown = 0
|
||||||
|
for h in hits:
|
||||||
|
s = rd_str(h, 70)
|
||||||
|
if s and len(s) > 2 and s.isprintable():
|
||||||
|
print(f" {needle.decode():10s} 0x{h:x} {s!r}")
|
||||||
|
shown += 1
|
||||||
|
if shown >= 12:
|
||||||
|
break
|
||||||
|
if not shown:
|
||||||
|
print(f" {needle.decode():10s} (no printable hits)")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 78)
|
||||||
|
print("== float constants 0.95 / 0.05 / 0.95f / 0.05f")
|
||||||
|
print("=" * 78)
|
||||||
|
for label, pat in (("double 0.95", struct.pack("<d", 0.95)),
|
||||||
|
("double 0.05", struct.pack("<d", 0.05)),
|
||||||
|
("float 0.95f", struct.pack("<f", 0.95)),
|
||||||
|
("float 0.05f", struct.pack("<f", 0.05))):
|
||||||
|
hits = find_all(pat, blocks=(".rdata", ".data"))
|
||||||
|
print(f" {label:12s} {[hex(h) for h in hits[:10]] or 'ABSENT'}")
|
||||||
|
for h in hits[:6]:
|
||||||
|
xs = xrefs_to(h)
|
||||||
|
for x in xs[:4]:
|
||||||
|
print(f" used at 0x{x[0]:x} in {x[2]} (entry 0x{x[3]:x})")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 78)
|
||||||
|
print("== functions using BOTH 95 (or 5) and 100 as immediates — integer fee math")
|
||||||
|
print("=" * 78)
|
||||||
|
found = []
|
||||||
|
for f in fm.getFunctions(True):
|
||||||
|
ent = int(f.getEntryPoint().getOffset())
|
||||||
|
vals = set()
|
||||||
|
it = listing.getInstructions(f.getBody(), True)
|
||||||
|
while it.hasNext():
|
||||||
|
ins = it.next()
|
||||||
|
if ins.getMnemonicString() not in ("IMUL", "MOV", "CMP", "ADD", "SUB", "LEA", "SHL"):
|
||||||
|
continue
|
||||||
|
for i in range(ins.getNumOperands()):
|
||||||
|
for o in ins.getOpObjects(i):
|
||||||
|
try:
|
||||||
|
v = int(o.getValue())
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if v in (95, 100, 5, 20):
|
||||||
|
vals.add(v)
|
||||||
|
if 100 in vals and (95 in vals or 5 in vals):
|
||||||
|
found.append((ent, f.getName(), sorted(vals)))
|
||||||
|
print(f" {len(found)} candidate(s)")
|
||||||
|
for ent, nm, vals in found[:40]:
|
||||||
|
print(f" 0x{ent:x} {nm:26s} immediates {vals}")
|
||||||
@@ -139,8 +139,14 @@ mod tests {
|
|||||||
(39, 1, 38), // 1.95 -> 1
|
(39, 1, 38), // 1.95 -> 1
|
||||||
(40, 2, 38), // exactly 2.0
|
(40, 2, 38), // exactly 2.0
|
||||||
(100, 5, 95),
|
(100, 5, 95),
|
||||||
(150, 7, 143), // 7.5 -> 7: THE discriminating case (alternative: 8/142)
|
(101, 5, 96), // 5.05 -> 5
|
||||||
(200, 10, 190),
|
(119, 5, 114), // 5.95 -> 5, last price paying 5
|
||||||
|
(120, 6, 114), // exactly 6.0
|
||||||
|
(149, 7, 142), // 7.45 -> 7
|
||||||
|
(150, 7, 143), // 7.5 -> 7: THE discriminating case (alternative: 8/142)
|
||||||
|
(151, 7, 144), // 7.55 -> 7 (a HALF-UP rule would pay 8 here too)
|
||||||
|
(199, 9, 190), // 9.95 -> 9
|
||||||
|
(200, 10, 190), // exactly 10.0
|
||||||
(1_000, 50, 950),
|
(1_000, 50, 950),
|
||||||
(15_000, 750, 14_250), // the canonical fixture
|
(15_000, 750, 14_250), // the canonical fixture
|
||||||
(15_000_000, 750_000, 14_250_000), // FUT's practical price ceiling
|
(15_000_000, 750_000, 14_250_000), // FUT's practical price ceiling
|
||||||
|
|||||||
Reference in New Issue
Block a user