Carol Ward
Carol Ward

Reputation: 731

Does ADR x9, sub_xxxxxx call a subroutine? Why does code use ADR / NOP

loc_10007390C
SUB             X8, X29, #-stuff
LDUR            X10, [X8,#-0x100] ;
LDR             X0, [X10,#0x10] ;
SUB             X8, X29, #-var_1E8
LDUR            X2, [X8,#-0x100]
STP             X0, XZR, [X2]
SUB             X8, X29, #-var_1F0
LDUR            X3, [X8,#-0x100]
NOP
LDR             X8, =__NSConcreteStackBlock
STR             X8, [X3]
STR             D8, [X3,#8]
ADD             X8, X3, #0x10
ADR             X9, sub_100092800
NOP
PACIA           X9, X8
ADR             X8, unk_1001A2658
NOP
STP             X9, X8, [X3,#0x10]
LDUR            X8, [X29,#var_B8]
STR             X8, [X3,#0x20]
LDUR            X8, [X29,#var_C0]
STP             X8, X10, [X3,#0x28]
LDUR            W8, [X29,#anotherStuff]
STR             W8, [X3,#0x38]
SUB             X8, X29, #-var_290
LDUR            W8, [X8,#-0x100]
STR             W8, [X3,#0x3C]
MOV             W1, #0
MOV             W4, #1
BL              do_the_dance
TST             W0, #0xFF00
B.EQ            loc_1000E8730

I'm a little perplexed about what ADR X9, sub_100092800 is doing. It looks like it's loading the address into X9, but does it call call the subroutine as well once it's loaded?

Upvotes: 1

Views: 204

Answers (2)

Ross Ridge
Ross Ridge

Reputation: 39621

The instruction ADR X9, sub_100092800 loads the address of the symbol sub_100092800 into the X9 register. It does not itself call or otherwise branch to the code located there.

The address loaded into the X9 register is then modified by PACIA instruction as a security measure and then stored in memory by the following STP instruction. There's no indication in your code what then happens with modified value stored in memory, but it's likely at some point it gets loaded from memory, restored to the value it had before the PACIA instruction was used, and then called.

The NOP instruction is a red herring. It's the result of the fact that certain pseudo-instructions like ADRL always generate two real instructions even if it's possible use just one instruction to calculate the same result. The ADR instruction only works with addresses that are within one megabyte of the instruction. For symbols that are farther away two instructions are necessary to calculate the address, ADRP and ADD. At some point the assembler or linker determined that address of sub_100092800 was within 1M and replaced the ADRP and ADD instructions with an ADR and a NOP instruction.

Upvotes: 2

Seva Alekseyev
Seva Alekseyev

Reputation: 61388

From the listing, there's no call. It loads the PC-relative offset into the register, then does nothing with it. But look at the ADR/NOP sequences in a couple of places. It looks as if the NOP's are meant to be patched with a call command (BL).

Several possibilities here. It could be that the disassembler is misreading the command. Unlikely, but check the machine code encoding, just in case. There could be a relocation directive that would patch those spots during module loading. Finally, there could be a piece of explicit self patching logic in the program itself, introduced specifically to thwart reverse engineers like yourself. The self patching will definitely be elsewhere in the module, to throw you off the scent.


EDIT: the code has a couple of funny tells. I don't know what is __NSConcreteStackBlock, but it strongly suggests Apple/Cocoa. At the same time, the PACIA command is specific to the ARM8.3 instruction set, and has to do with the pointer authentication logic for jump integrity protection. To the best of my knowledge, this is the stuff of the upcoming ptrauth/ARM64E initiative by Apple, which is supported by the latest Xcode and the latest iDevices, but not accepted by the App Store yet (as of the time of this writing, 5/18/2020). Pointer authentication is potentially fragile, Apple's official line is "try it in dev for now". My point is, it could be that some kind of function call point postprocessing that's a part of the ptrauth-aware code generation pipeline. I don't know enough about ptrauth to tell. :(


EDIT: yet another possibility. Did the assembly come, by any chance, from disassembling an object file, as opposed to a linked executable? Then the NOPs could be placeholders for cross module calls. That would also explain inconsistent label naming.

Upvotes: 1

Related Questions