当前位置:网站首页>Embedded assembly in IOS
Embedded assembly in IOS
2020-11-09 13:44:00 【osc_zvjde550】
Python The actual combat community
Java The actual combat community
Long press to identify the QR code below , Add as needed
Scan code, pay attention to add customer service
Into the Python community ▲
Scan code, pay attention to add customer service
Into the Java community ▲
author | sindrilin
source | sindrilin The nest
http://sindrilin.com/2019/10/23/write_assembly_in_ios.html
Click to read the original to see more articles by the author
Write an article in iOS The idea of using compiled articles has been in my mind for a long time , But I didn't do it . Although earlier in the start-up time-consuming optimization work , I've done it by intercepting objc_msgSend And insert assembly instructions to count the time-consuming work of method calls , But that's all . Just recently, the project is doing security reinforcement , More assembly needs to be written to improve security ( The assembly instruction set in this paper is ARM64), So there is this article
Embedded assembly format
__asm__ [ key word ](
Instructions
: [ Output the list of operands ]
: [ Input the list of operands ]
: [ Contaminated register list ]
);
For example, there is a、b、c Three variables , To achieve a = b + c This code , The assembly code is as follows :
__asm__ volatile(
"mov x0, %[b]\n"
"mov x1, %[c]\n"
"add x2, x0, x1\n"
"mov %[a], x2\n"
: [a]"=r"(a)
: [b]"r"(b), [c]"r"(c)
);
volatile
volatile Keyword means that the compiler is not allowed to re optimize the assembly code , But basically, it doesn't make any difference whether or not to declare the compiled instructions
Operands
The format of the operands is "[limits]constraint", It is divided into two parts: permission and qualifier . such as "=r" Indicates that the parameter is only written and stored in a general register
limits
keyword | To signify |
---|---|
= | Just write , General used to output operands |
+ | Reading and writing , Can only be used for output operands |
& | Declaration registers can only be used for output |
constraint
keyword | To signify |
---|---|
f | Floating point register f0~f7 |
G/H | Floating point constant immediate |
I/L/K | An immediate number used in data processing |
J | The value is -4095~4095 The index of |
l/r | register r0~r15 |
M | 0~32/2 Constant to the power of |
m | Memory address |
w | Vector register s0~s31 |
X | Any type of operands |
Instructions
because ARM64 Too many instructions for , The instructions can be read at the end of the text , Here only explain some key words in the instruction :
%0~%N / %[param]
In the use of C In the case of mixed code and assembly ,% The beginning is used to correlate parameters , adopt %[param] You can declare parameter names , You can also use the anonymous parameter format %N The order of the mode corresponds to the parameter (abc The parameters will follow 012 In order to match ):
__asm__ volatile(
"mov x0, %1\n"
"mov x1, %2\n"
"add x2, x0, x1\n"
"mov %0, x2\n"
: "=r"(a)
: "r"(b), "r"(c)
);
In practice , The device does not necessarily support %N Anonymous parameter format of , It is recommended to use %[param] Make it more readable
[reg]
Most of the time a program runs , The register stores the address where the data is stored , Use [] Wrap the register , Represents that the stored value of a register is used as an address to access data . The following instructions are to take out the address 0x10086 The stored data is stored in x1 Register , And put it in the address 0x100086 The memory of the :
"mov x0, #0x10086\n"
"mov x1, [x0]\n"
"mov x2, #0x100086\n"
"str x1, [x2]\n"
#1 / #0x1
Use # At the beginning, it means an immediate number ( constant ), It is recommended to use 16 Write in hexadecimal
Call specification
ARM64 The calling convention uses AAPCS64, Parameters are stored from left to right in x0~x7 In the register , Parameter out of 8 Time , The rest goes right to left , According to the size of the return value, it is stored in x0/x8 return . The register rules are as follows :
register | Special name | The rules |
---|---|---|
r31 | SP | Storage stack top address |
r30 | LR | Store function return address |
r29 | FP | The storage function uses the stack frame address |
r19~r28 | Registers that the callee needs to protect | |
r18 | Platform register , It is not recommended to use as a temporary register | |
r17 | IP1 | In process use registers , It is not recommended to use as a temporary register |
r16 | IP0 | Same as r17, At the same time as a soft interrupt svc System call parameters in |
r9~r15 | Temporary register ( When the function address parameter is embedded in the assembly instruction , Will be used to save the function address ) | |
r8 | Return value register ( The same at other times r9~r15) | |
r0~r7 | Passing store call parameters ,r0 Can be used as a return value register | |
NZCV | Status register |
actual combat
Debug detection
stay iOS In the application of security reinforcement , adopt sysctl + kinfo_proc Can detect whether the application is debugged :
__attribute__((__always_inline)) bool checkTracing() {
size_t size = sizeof(struct kinfo_proc);
struct kinfo_proc proc;
memset(&proc, 0, size);
int name[4];
name[0] = CTL_KERN;
name[1] = KERN_PROC;
name[2] = KERN_PROC_PID;
name[3] = getpid();
sysctl(name, 4, &proc, &size, NULL, 0);
return proc.kp_proc.p_flag & P_TRACED;
}
But because of fishhook This kind of direct modification lazy symbol address scheme exists , Use it directly sysctl It's not safe , As a result, most developers will replace this call with an embedded assembly solution execution :
size_t size = sizeof(struct kinfo_proc);
struct kinfo_proc proc;
memset(&proc, 0, size);
int name[4];
name[0] = CTL_KERN;
name[1] = KERN_PROC;
name[2] = KERN_PROC_PID;
name[3] = getpid();
__asm__(
"mov x0, %[name_ptr]\n"
"mov x1, #4\n"
"mov x2, %[proc_ptr]\n"
"mov x3, %[size_ptr]\n"
"mov x4, #0x0\n"
"mov x5, #0x0\n"
"mov w16, #202\n"
"svc #0x80\n"
:
:[name_ptr]"r"(&name), [proc_ptr]"r"(&proc), [size_ptr]"r"(&size)
);
return proc.kp_proc.p_flag & P_TRACED;
Step on the pit
Use C Code embedded assembly development , The fatal problem is that function entries put temporary variables on the stack , And put these variables in registers . The actual runtime of the above hybrid code , The following will happen :
// Temporary variable code generated by function entry
add x0, sp, #0x24 // x0 Deposit name
add x1, sp, #0x34 // x1 Deposit proc
add x2, sp, #020 // x2 Deposit size
......
// Built in assembly
mov x0, x0 // name Normal assignment
mov x1, #4 // proc Data is corrupted
mov x2, x1 // size Data is corrupted
mov x3, x2
mov x4, #0x0
mov x5, #0x0
mov x12, #0xca
svc #0x80
The compiled code is due to the temporary variable order problem , Led to svc Interrupt call sysctl Unable to pass in the correct parameter , Finally, the application is stuck
Repair
Insert temporary variables
Through the compiled instructions, we get a corresponding table :
Variable | register | Input parameter register |
---|---|---|
name | x0 | x0 |
proc | x1 | x2 |
size | x2 | X3 |
If you can make registers that store temporary variables and svc The input parameter registers of interrupt are consistent , It won't be destroyed
ARM64 Calling convention , Parameters are put on the stack from right to left
Because the detection function has no input parameters , Therefore, the temporary parameters are stored in the following order x0~x2 In the register , In sequence name、proc、size, So just need to be in name and proc Insert a useless temporary variable into the , You can make the parameters correspond to :
size_t size = sizeof(struct kinfo_proc);
struct kinfo_proc proc;
memset(&proc, 0, size);
int placeholder;
int name[4];
name[0] = CTL_KERN;
name[1] = KERN_PROC;
name[2] = KERN_PROC_PID;
name[3] = getpid();
After compiling, the instruction becomes :
// Temporary variable code generated by function entry
add x0, sp, #0x24 // x0 Deposit name
add x1, sp, #0x34 // x1 Deposit placeholder
add x2, sp, 0x38 // x2 Deposit proc
add x3, sp, #020 // x3 Deposit size
......
// Built in assembly
mov x0, x0
mov x1, #4
mov x2, x2
mov x3, x3
mov x4, #0x0
mov x5, #0x0
mov x12, #0xca
svc #0x80
Modify the order of instructions
The instruction that sets the input parameter will destroy the existing value in the register , Before entering the settings, then guarantee , It's ok if the register is not destroyed :
__asm__(
"mov x0, %[name_ptr]\n"
"mov x3, %[size_ptr]\n"
"mov x2, %[proc_ptr]\n"
"mov x1, #4\n"
"mov x4, #0x0\n"
"mov x5, #0x0\n"
"mov w16, #202\n"
"svc #0x80\n"
:
:[name_ptr]"r"(&name), [proc_ptr]"r"(&proc), [size_ptr]"r"(&size)
);
The compiled instructions are as follows :
// Built in assembly
mov x0, x0 // x0 preservation name
mov x3, x2 // x3 preservation size
mov x2, x1 // x2 preservation proc
mov x1, #4
mov x4, #0x0
mov x5, #0x0
mov x12, #0xca
svc #0x80
Full assembly implementation
In the and C In the case of mixed code , There is no guarantee which registers will be destroyed , So it's a good choice to use assembly directly to implement the whole logic , We need to pay attention to 2 A question :
Ensure that there is no entry and exit instruction generated before and after the function call , Use __attribute__((naked)) To deal with it
All variables are stored on the stack , You need to control the use of the stack
Use safe registers (r19~r28)
First, determine how much stack space is needed , According to the function sysctl(name, 4, &proc, &size, NULL, 0) Judge
Parameters name Total occupancy 4 * int Space , Write it down as 0x10
Parameters proc stay arm64 Next ,sizof() The calculated length is 0x288
Parameters &size The length of the pointer is 0x8
total 0x2a0
Entry function , Need to be right FP/LR Register to stack , Ensure that the function exits correctly . in addition r19~r28 total 10 Registers need to be stack protected , Finally, we get the stack space graph when the function is running :
----------
| FP |
---------- sp + 0x2f8
| LR |
---------- sp + 0x2f0
| r20 |
---------- sp + 0x2e8
| r19 |
---------- sp + 0x2e0
| r22 |
---------- sp + 0x2d8
| r21 |
---------- sp + 0x2d0
| r24 |
---------- sp + 0x2c8
| r23 |
---------- sp + 0x2c0
| r26 |
---------- sp + 0x2b8
| r25 |
---------- sp + 0x2b0
| r28 |
---------- sp + 0x2a8
| r27 |
---------- sp + 0x2a0
| p_size |
---------- sp + 0x298
| proc |
---------- sp + 0x10
| name |
---------- sp
In preservation r19~r28 After the register is put on the stack , Use five of these registers to hold some parameters :
------------------
| Parameters | register |
------------------
| name | r19 |
------------------
| proc | r20 |
------------------
| p_size | r21 |
------------------
| size | r22 |
------------------
| sp | r23 |
------------------
| temp | r24 |
------------------
After confirming the use of space on the stack , You can start to implement it step by step :
Function entry and exit
There are two things to do at the entry and exit of a function :FP/LR In and out of the warehouse 、r19~r28 In and out of the warehouse
__asm__ volatile(
"stp x29, x30, [sp, #-0x10]!\n"
"stp x19, x20, [sp, #-0x10]!\n"
"stp x21, x22, [sp, #-0x10]!\n"
"stp x23, x24, [sp, #-0x10]!\n"
"stp x25, x26, [sp, #-0x10]!\n"
"stp x27, x28, [sp, #-0x10]!\n"
......
"ldp x19, x20, [sp], #0x10\n"
"ldp x21, x22, [sp], #0x10\n"
"ldp x23, x24, [sp], #0x10\n"
"ldp x25, x26, [sp], #0x10\n"
"ldp x27, x28, [sp], #0x10\n"
"ldp x29, x30, [sp], #0x10\n"
);
Stacks open up space
Temporary variables are always shared with 0x2a0 Space , And need to use 5 Registers hold variables
__asm__ volatile(
......
"sub sp, sp, #0x2a0\n"
// Open up stack space , Variable save register
"mov x19, sp\n" // x19 = name
"add, x20, sp, #0x10\n" // x20 = proc
"add, x21, sp, #0x298\n" // x21 = p_size
"mov x22, #0x288\n" // x22 = size
"mov x23, sp\n" // x23 = sp
"str x22, [x21]\n" // p_size = &size
"add sp, sp, #0x2a0\n"
......
);
kinfo_proc
determine proc After the memory of , Need to put :
size_t size = sizeof(struct kinfo_proc);
struct kinfo_proc proc;
memset(&proc, 0, size);
Convert to the corresponding assembly , among proc Stored in x20,x22 Store size,memset Three parameters are needed , Enter into the reference respectively :
__asm__ volatile(
......
"mov x24, %[memset_ptr]\n"
"mov x0, x20\n"
"mov x1, #0x0\n"
"mov x2, x12\n"
"blr x24\n"
......
:
:[memset_ptr]"r"(memset)
);
name
because name yes int Array , In the case of clear storage location , We need to separate 4 individual 4 Byte parameters are stored in the corresponding memory location , Its location is as follows :
-------------
| name[3] |
------------- sp + 0xc
| name[2] |
------------- sp + 0x8
| name[1] |
------------- sp + 0x4
| name[0] |
------------- sp
in addition name Need to be used getpid() To configure parameters , adopt svc The interrupt can get this parameter (svc System call parameters can refer to... In the extended reading Kernel Syscalls)
#define CTL_KERN 1
#define KERN_PROC 14
#define KERN_PROC_PID 1
__asm__ volatile(
......
// getpid
"mov x0, #0\n"
"mov w16, #20\n"
"mov x3, x0\n" // name[3]=getpid()
// Set parameters and store
"mov x0, #0x1\n"
"mov x1, #0xe\n"
"mov x2, #0x1\n"
"str w0, [x23, 0x0]\n"
"str w1, [x23, 0x4]\n"
"str w2, [x23, 0x8]\n"
"str w3, [x23, 0xc]\n"
......
);
sysctl
Finally, call sysctl, According to the corresponding relationship between the parameters and registers, you can call the parameters :
__asm__ volatile(
......
"mov x0, x19\n"
"mov x1, #0x4\n"
"mov x2, x20\n"
"mov x3, x21\n"
"mov x4, #0x0\n"
"mov x5, #0x0\n"
"mov w16, #202\n"
"svc #0x80\n"
......
);
flag testing
Finally, we need to go back p_flag and P_TRACED And comparative testing , Here we need to get p_flag Offset in the structure to access data ,struct extern_proc The structure is as follows :
struct extern_proc {
union {
struct {
struct proc *__p_forw; /* Doubly-linked run/sleep queue. */
struct proc *__p_back;
} p_st1;
struct timeval __p_starttime; /* process start time */
} p_un;
#define p_forw p_un.p_st1.__p_forw
#define p_back p_un.p_st1.__p_back
#define p_starttime p_un.__p_starttime
struct vmspace *p_vmspace; /* Address space. */
struct sigacts *p_sigacts; /* Signal actions, state (PROC ONLY). */
int p_flag; /* P_* flags. */
char p_stat; /* S* process status. */
pid_t p_pid; /* Process identifier. */
pid_t p_oppid; /* Save parent pid during ptrace. XXX */
int p_dupfd; /* Sideways return value from fdopen. XXX */
/* Mach related */
caddr_t user_stack; /* where user stack was allocated */
void *exit_thread; /* XXX Which thread is exiting? */
int p_debugger; /* allow to debug */
boolean_t sigwait; /* indication to suspend */
/* scheduling */
u_int p_estcpu; /* Time averaged value of p_cpticks. */
int p_cpticks; /* Ticks of cpu time. */
fixpt_t p_pctcpu; /* %cpu for this process during p_swtime */
void *p_wchan; /* Sleep address. */
char *p_wmesg; /* Reason for sleep. */
u_int p_swtime; /* Time swapped in or out. */
u_int p_slptime; /* Time since last blocked. */
struct itimerval p_realtimer; /* Alarm timer. */
struct timeval p_rtime; /* Real time. */
u_quad_t p_uticks; /* Statclock hits in user mode. */
u_quad_t p_sticks; /* Statclock hits in system mode. */
u_quad_t p_iticks; /* Statclock hits processing intr. */
int p_traceflag; /* Kernel trace points. */
struct vnode *p_tracep; /* Trace to vnode. */
int p_siglist; /* DEPRECATED. */
struct vnode *p_textvp; /* Vnode of executable. */
int p_holdcnt; /* If non-zero, don't swap. */
sigset_t p_sigmask; /* DEPRECATED. */
sigset_t p_sigignore; /* Signals being ignored. */
sigset_t p_sigcatch; /* Signals being caught by user. */
u_char p_priority; /* Process priority. */
u_char p_usrpri; /* User-priority based on p_cpu and p_nice. */
char p_nice; /* Process "nice" value. */
char p_comm[MAXCOMLEN + 1];
struct pgrp *p_pgrp; /* Pointer to process group. */
struct user *p_addr; /* Kernel virtual addr of u-area (PROC ONLY). */
u_short p_xstat; /* Exit status for wait; also stop signal. */
u_short p_acflag; /* Accounting flags. */
struct rusage *p_ru; /* Exit information. XXX */
};
among union p_un Of size by 0x10, as well as p_flag The first two pointers occupy 0x8, You can confirm the memory consumption diagram of the structure :
-------------------
| p_flag |
------------------- kinfo_proc + 0x20
| p_sigacts |
------------------- kinfo_proc + 0x18
| p_vmspace |
------------------- kinfo_proc + 0x10
| union p_un |
------------------- kinfo_proc
Compare the marks and store the test results in x0 Back in :
#define P_TRACED 0x00000800
__asm__ volatile(
......
"ldr, x24, [x20, #0x20]\n" // x24 = proc.kp_proc.p_flag
"mov x25, #0x800\n" // x25 = P_TRACED
"blc x0, x24, x25\n" // x0 = x24 & x25
......
);
Extended reading
[1]https://www.theiphonewiki.com/wiki/Kernel_Syscalls
[2]https://juejin.im/post/5cadeda55188251ad87b0eed
[3]https://juejin.im/post/5a786c555188257a6854b18c
[4]http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
Programmer column Scan code and pay attention to customer service Press and hold to recognize the QR code below to enter the group
Recent highlights are recommended :
My girlfriend thinks the annual salary is 50 Ten thousand is the average level , What do I do ?
The sexy goddess of the biggest straight men forum in China overturned
IntelliJ IDEA Fully optimized settings , Efficiency bars !
Here's a look Good articles to share with more people ↓↓
版权声明
本文为[osc_zvjde550]所创,转载请带上原文链接,感谢
边栏推荐
- SEO建设者,有哪些说不出的苦?
- 跟我一起学.NetCore之EF Core 实战入门,一看就会
- 真正拖垮你的,是沉没成本
- Lazy to write a document, swagger document does not smell
- Spark Learning (2) -- job scheduling and shuffle analysis
- 分库分表的 9种分布式主键ID 生成方案,挺全乎的
- Why I strongly recommend custom development of small programs, these benefits you need to understand
- android studio AIDL的使用
- Online course of tutorial system processing is in progress
- Technology and beauty are so expensive, it's better to find consultants | aalab enterprise consulting business
猜你喜欢
技美那么贵,不如找顾问 | AALab企业顾问业务
移动安全加固助力 App 实现全面、有效的安全防护
PAT_甲级_1074 Reversing Linked List
SEO见风使舵,是对还是错?
Where should wild card SSL certificate register and apply
SEO builders, what are the unspeakable hardships?
要我说,多线程事务它必须就是个伪命题!
The third way to realize webrtc in embedded devices
Is SEO right or wrong?
Android studio import customized framework classess.jar As 4.0.1 version is valid for pro test
随机推荐
How to use function framework to develop large web application
Interface tests how to pass files in post requests
Nine kinds of distributed primary key ID generation schemes of sub database and sub table are quite comprehensive
CCF BDCI热门赛题:非结构化商业文本信息中隐私信息识别
分库分表的 9种分布式主键ID 生成方案,挺全乎的
Four steps of Android integrated payment
Rainbow sorting | Dutch flag problem
To me, multithreading transaction must be a pseudo proposition!
Detailed explanation of [golang] GC
Kubernetes business log collection and monitoring
Tidb x micro banking reduces time consumption by 58%, and distributed architecture helps to realize inclusive finance
AndroidStudio导入定制化的framework classess.jar AS 4.0.1版本亲测有效
导师制Processing网课 双十一优惠进行中
Android Studio Avd「真·小白食用方法」
要我说,多线程事务它必须就是个伪命题!
Learn with me. NETCORE EF core practical introduction, a look will
Android byte beat one side, was hanged by the interviewer! Fortunately, we got the offer from three sides
c语言(循环链表)实现贪吃蛇的基本功能
JVM learning (6) - memory model and thread
IDEA解决yml配置文件中文输出乱码问题