You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: _posts/2019-12-23-fixing-opcodes.md
+255-1Lines changed: 255 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1067,4 +1067,258 @@ And just to compare, here's the 5.15 equivalent:
1067
1067
}
1068
1068
```
1069
1069
1070
-
As mentioned already, there's a few ways this can be improved but for now this should work as a proof of concept.
1070
+
As mentioned already, there's a few ways this can be improved but for now this should work as a proof of concept.
1071
+
1072
+
#### Automating Client Opcode Correction
1073
+
1074
+
This is the part where it gets spicy. But we'll quickly refresh a couple things we learnt a while back:
1075
+
1076
+
* The order of switch cases is mostly preserved
1077
+
* Switch case code doesn't change much (or at all)
1078
+
1079
+
For a quick proof of concept, we'll only attempt to do the first one now and see if we can get something usable. Make sure you've got the JSON that the script spits out saved somewhere. I'm gonna do the thing where I post a bunch of ~~surprisingly~~ working code and then go through it.
With that out of the way, we can talk about cool shit now. Does it work? Well, yes and no. What it gives you already is pretty accurate and I think there's only a couple that it 'finds' that aren't correct, though I haven't gone and actually checked said opcodes myself.
This uses patent pending technology to find a case in the new executable schema which has the smallest delta between RVAs. One of the differences between the 5.0 and 5.15 executable is that some cases grew by 1~5 bytes, so you can't look up a case by its RVA alone, so we need to find the closest one. We also calculate the size difference between the distance match and the origin case in the original executable. It's _probably_ unlikely that something that grew by more than a few bytes is the same handler.
1237
+
1238
+
```python
1239
+
order_match = new_schema['cases'][k]
1240
+
```
1241
+
1242
+
This is pretty dumb but it's in there so why not, but basically we grab the case in the new executable which is in the same place, so order still matters but nothing else does. This probably needs fixing though because I'm not sure if python will reliably spit out arrays in the same order. Seems to work though. Probably crashes if you have less elements in the new schema, but I like to live on the edge.
1243
+
1244
+
```python
1245
+
# see if the rva matches for the cases found by the distance and order
1246
+
if dist_match['rel_ea'] == order_match['rel_ea'] and size_diff < max_size_diff:
This is the first _real_ check we do and it's actually pretty decent in terms of it giving you good results. First we check if the distance match is also the order match and discard any others (for now) and that the size hasn't changed more than 10 bytes. Reason for this is that I was getting slightly more inconsistent results both just by checking the distance alone, so I figured it'd be a decent idea to check the order as well. `size_diff` also filters out a couple bad cases that looked obviously wrong.
1258
+
1259
+
Following that there's a quick check to see if the nested call count matches, though it doesn't serve any purpose at the moment other than a quick test. Currently, everything that this finds has an exact nested call count match, which is pretty nice. The idea I have in the back of my mind is that you have a bunch of isolated checks which will find the 'best' matching candidates with a confidence score, then you loop over each matching candidate, pick the best scoring one and then print those opcodes out.
1260
+
1261
+
Example:
1262
+
1263
+
```
1264
+
branch 2
1265
+
- old: 0x77 (Logout)
1266
+
- new: 0x12d
1267
+
branch 3
1268
+
- old: 0x100 (Playtime)
1269
+
- new: 0x2fa
1270
+
branch 4
1271
+
- old: 0x104 (Chat)
1272
+
- new: 0x1d0
1273
+
...
1274
+
branch 10
1275
+
- old: 0x17f (PlayerSpawn)
1276
+
- new: 0xdc
1277
+
branch 11
1278
+
- old: 0x180 (NpcSpawn)
1279
+
- new: 0x219
1280
+
branch 12
1281
+
- old: 0x181 (NpcSpawn2)
1282
+
- new: 0x304
1283
+
branch 13
1284
+
- old: 0x191 (ActorFreeSpawn)
1285
+
- new: 0x32b
1286
+
branch 14
1287
+
- old: 0x165 (PersistantEffect)
1288
+
- new: 0x339
1289
+
branch 15
1290
+
- old: 0x184 (ActorSetPos)
1291
+
- new: 0x296
1292
+
branch 16
1293
+
- old: 0x182 (ActorMove)
1294
+
- new: 0x1ad
1295
+
...
1296
+
branch 27
1297
+
- old: 0x15e (Effect)
1298
+
- new: 0x2aa
1299
+
branch 28
1300
+
- old: 0x161 (AoeEffect8)
1301
+
- new: 0xb3
1302
+
branch 29
1303
+
- old: 0x162 (AoeEffect16)
1304
+
- new: 0xe6
1305
+
branch 30
1306
+
- old: 0x163 (AoeEffect24)
1307
+
- new: 0x10a
1308
+
branch 31
1309
+
- old: 0x164 (AoeEffect32)
1310
+
- new: 0x1c8
1311
+
branch 32
1312
+
- old: 0x142 (ActorControl)
1313
+
- new: 0x12f
1314
+
branch 33
1315
+
- old: 0x144 (ActorControlTarget)
1316
+
- new: 0x1b3
1317
+
branch 34
1318
+
- old: 0x143 (ActorControlSelf)
1319
+
- new: 0x201
1320
+
...
1321
+
found 73/401 opcode branches!
1322
+
```
1323
+
1324
+
Hand picked quite a few here, but these ones are actually correct which is honestly pretty impressive for such a naive implementation. That said, the ones I think are 'wrong' probably need to be manually checked, but I'd say that 90% of the ones it finds, so ~67 or so opcode cases are correct and it's selected the right handler which is honestly impressive for how awful this garbage is.
0 commit comments