Generating Proto File For Bangdream 4.0.0 (V2)
You can check this post for some background. BangDream now use “on demand” delivery and I can get only arm64 blobs now. They are so different with old armv7 instrcutions so I have to rewrite some code to get correct tag numbers.
At first, use latest il2cppdumper
, or you may have some errors when running the script. I tried immediately with my old script, and all tag numbers are reported None. It’s pretty annoying but it should have something to do with new instructions of arm64 (aarch64). Now let’s check what happend.
Disassembly
Like in armv7, protobuf-net codes are also compiled into two kinds of instructions. Use UserAuthRequest
as an example. userId
is compiled into:
1 | 08 04 40 F9 E1 03 00 32 E2 03 1F AA 00 01 40 F9 6D 70 78 14 |
With a online disassembler you can get following instructions:
1 | 0x0000000000000000: 08 04 40 F9 ldr x8, [x0, #8] |
and attestationErrorMsg
is encoded as:
1 | F3 0F 1E F8 FD 7B 01 A9 FD 43 00 91 08 04 40 F9 21 01 80 52 ... |
and processed by disassembler:
1 | 0x0000000000000000: F3 0F 1E F8 str x19, [sp, #-0x20]! |
The most important instructions are:
1 | E1 03 00 32 orr w1, wzr, #1 |
Instruction Encoding
But how are the immediate encoded? By checking the reference and the instruction encoding I figured out that MOVZ
use direct immediate and ORR
use bitmask immediate. Although they are aarch64 instructions, both of them use 32 bit immediate.
The direct immediate is pretty direct, just read the immediate and it’s finished:
1 | x10x 0010 1xxi iiii iiii iiii iiid dddd |
But how about bitmask immediate? They are like the rotating encoding of immediate in armv7 but have some different. ORR
immediate instruction looks like this:
1 | x01x 0010 0Nii iiii iiii iinn nnnd dddd |
N
together with first x (as known as sf
) refers to bit length (sf==0 AND N==0
=> 32bit or sf==1
=> 64bit), first six binary digits of immediate are immr
and last six binary digits of immediate are imms
. You can find the code to decode bitmask immediate in the arm official reference:
1 | // DecodeBitMasks() |
But it’s pretty hard to understand. LLVM
gives code which is way more clear:
1 | static inline uint64_t decodeLogicalImmediate(uint64_t val, unsigned regSize) { |
OK we can now rewrite our code to get correct tag number.
New getTag
Function
Attention that in following code we use Little-Endian. In our case, N
and sf
always equal to 0 so we don’t have to care about length, it’s fixed to 32 bits.
1 | def getTag(address): |
For whole code please see this gist.
Happy hacking!