IOS Reverse Chapter catalog:

  • 1️, RSA encryption principle & cryptography &HASH

  • 2️ principle of application signature and re-signature (re-signing wechat application practice)

  • 3️, shell script automatic re-signature and code injection

  • 4️, resigning application debugging and code modification (Hook)

  • 5️, Mach-O file

  • 6️ principle and symbol table of Hook/fishHook

  • 7️ LLDB from shallow to deep

  • 8️, LLDB Advanced on Chisel and Cycript

  • 9️, prison break

  • 🔟, actual combat – nail punch card plug-in

preface

In the reverse chapter, we have described everything from the leading knowledge to tool use and principle analysis, and consolidated it with practical cases. So, let’s move on to the most important objective of our reverse-learning chapter, applying security attacks and defenses.

This is a big chapter, if the article is too long will be divided into two chapters.

The most important thing to learn about reverse is to know how to protect. This paper will list some common reverse attack methods on the market at present and explain how to protect them. These methods are not unique and may not be the best.

About the protection

With regard to application protection, we should first have a few premise concepts to be clear.

  • 1️ : There is no absolutely safe procedure.

    There is no such thing as an absolutely secure application, and all we have to do is to confuse or as much as possible, waste the attacker’s time and increase the cost of the attack.

  • 2️ discount: For the operation of detecting debugging and injection, try not to do very obvious operation such as withdrawal from application or prompt.

    • For a more experienced (as opposed to a defender) reverse engineer, it is easy to follow the lead and find the appropriate protection or monitoring processing logic and techniques when there is a significant difference between the debug/injection code project running and the legitimate application running.

    • (For example, some students often ask why the project flashes back as soon as it runs after re-signing, so the legitimate application is no problem, so it is easy to know that it is likely to use Ptrace or introduce packet monitoring/packet name monitoring and other processing).

    • Relatively speaking, many large factories on the market use the monitoring of this behavior will record equipment/accounts and other information for reporting and sealing measures are invisible to achieve more effective protection.

1. Dynamic debugging

Attack:

For non-jailbreak environments, re-signing for dynamic debugging, It is most common for Yololib to modify Mach-O to Load Commands and let DYLD Load dynamic libraries written by attackers to hook code injection. If you are not familiar with it, you can refer to 2,3 and 4 in the table of contents for detailed operation demonstration.

Using the characteristics of re-signature engineering, in fact, there are many such solutions, here select a few more representative protection measures listed.

Defense Method 1: Disable lldb-ptrace

By using the debugging feature of re-signed project, we can forbid the development environment to use LLDB to achieve the effect of re-signed project running flash back.

The principle of LLDB is as follows:

  • lldbIt’s essentially oneGUIBreakpoints and command collection tools +debug serverBy attaching the process to the currently running process.

(Debug-Attach to Process in Xcode is implemented according to the DebugServer)

ptrace

Ptrace is a function provided by the command line project and the

in the Mac OS project. It can be used to control process attachment management. It can prevent application processes from being attached. It’s not exposed in iOS, but iOS is available.

The author here will export the header file, (link – password: iAQP) download and import the project can be used.

Use as follows:

/** arg1: what ptrace should do: PT_DENY_ATTACH indicates that the current process is not allowed to attach arg2: the PID of the process to be manipulated, 0 represents itself arg3: the address varies depending on how the first parameter is handled. Arg4: The data varies depending on how the first parameter is handled */
ptrace(PT_DENY_ATTACH, 0.0.0);
Copy the code

Effect after treatment

After the code is added:

  • Running the project, the program flashes back.
  • Click on the app from the phone and it works fine.
  • useXcodebuilt-inDebug - Attach to processFailed to find the attach.

Assessment of means of protection

  • 1️ one: prison break environmentlldb-debug serverIt can also be protected.
  • 2️ discount: This practice is relatively violent, and affects the positive development itself. It can only be not used in the positive development, and be opened when packaged and put on the shelves.
  • 3️ discount: This approach has obvious effect, and it is easy to infer that using this mechanism, a symbol breakpoint can be easily detected.
  • 4️ retail: cracking is also relatively simple, usefishhookIt can be easyhookptraceThis function.

(There is anecdotal evidence that alipay used this method in the early days, but there is no evidence, all as a listen)

prompt

Cycript itself reads data from a running process, is not process-attached and cannot be protected by pTrace.

Protection cracking

There are many ways to crack this, such as directly modifying binary assembly code (BL -> NOP), hook pTrace and so on.

The simplest way to do this is to insert a dynamic library and load the library with fishhook and drop the ptrace hook directly. I won’t demonstrate it here.

Monkeydev already uses Fishhook to exchange pTrace by default.

Defense mode 2: sySCTL

Sysctl (System control) is a function provided by

. It has many functions, including monitoring whether the current process is attached. But because of its nature, it only monitors whether the current application has been attached.

So in forward development we tend to use timers together, or on a regular/regular/specific time frame.

Use as follows:

#import "ViewController.h"
#import <sys/sysctl.h>
@interface ViewController(a)
@end

@implementation ViewController
BOOL isDebug(){
    int name[4];             // Put bytecode inside. Query information
    name[0] = CTL_KERN;      // kernel query
    name[1] = KERN_PROC;     // Query the process
    name[2] = KERN_PROC_PID; // The argument passed is the process ID
    name[3] = getpid();      // Get the current process ID
    
    struct kinfo_proc info;  // The structure that accepts the query result
    size_t info_size = sizeof(info);  // Structure size
    if(sysctl(name, 4, &info, &info_size, 0.0)) {NSLog(@" Query failed");
        return NO;
    }
    /** the result of the query is the 12th bit of info.kp_proc.p_flag. If the value is 1, it indicates the debugging status. P_flag & P_TRACED) is 0x800 to get the 12th bit */
    return((info.kp_proc.p_flag & P_TRACED) ! =0);
}

static dispatch_source_t timer;
void debugCheck(){
    timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0.0, dispatch_get_global_queue(0.0));
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC.0.0 * NSEC_PER_SEC);
    dispatch_source_set_event_handler(timer, ^{
        if (isDebug()) {// Write what you need to do when you detect debugging
            NSLog("Debug status!");
        }else{
            NSLog("Normal!"); }}); dispatch_resume(timer); } - (void)viewDidLoad {
    [super viewDidLoad];
    debugCheck();
}
Copy the code

Effect after treatment

The effect of this display layer depends on the result you define. After you detect it, you can decide to report or flash back.

Assessment of means of protection

  • 1️ one: prison break environmentlldb-debug serverIt can also be detected, but it will also affect its own forward development.
  • 2️ : This approach is flexible and can customize the treatment results, and reporting is easy to be invisible.
  • 3️ retail: Cracking is also relatively simple and easy to usefishhook hooksysctlThis function (but because the user used sysctl to get the result returned, note herehookAnd then you can’t just do nothing, you have to find what comes backflag12A change to0, please refer to the picture below). But thanks to the effect of the use level, it can be invisible, which is also used by some companies.
  • 4️ : Due to its characteristics, detection is timely and needs to operate and detect according to the demandorTest the logic of how long.

(Rumor has it that it’s back again. It’s said that the Douyin team used this detection in the early days, and after detecting it, they went straight to exit, which is also not verified.)

prompt

It is not recommended to exit directly after debugging is detected.

Because in the reverse process, by adding a sign breakpoint to exit and looking at the function call stack, you can see the address of the function that called exit, and subtract the initial address of the Mach-O from the image list instruction to get the offset of the function. Then, in Hopper, it’s easy to get the function that calls exit, so attackers can find that function where you are using SYscTL to detect it.

Therefore, we say that protection is best not to have obvious traces to the attacker, otherwise it is to give them a good clue and thinking to find your defense logic.

Protection cracking

Sysctl hook approach is as follows:

Also, Monkeydev already uses fishhook by default with sysctl..

The problem

As you may have noticed, the above mentioned two solutions to dynamic debugging seem to be a little less interesting, the cost of cracking is relatively small. Just make sure that the injected code fully resolves this safeguard before sySCTL checks the code or pTrace calls.

In fact, there are many solutions to solve the problem of dynamic debugging protection scheme, such as:

  • ensuresysctlDetection code orptraceThe implementation of thehookBefore you inject code.
  • disabledfishhook .

Protection mode 3: PTrace/SYSCTL optimized version – Advance execution

The principle about

According to the compilation features of LD and LLVM and the loading logic of DYLD. In fact, when we write protection code in load in the framework for forward development, it will be executed earlier than yololib’s injected dynamic library.

  • As we mentioned in the previous article,Mach-O__DATAThere are free memory places behind the segment.yololibAdd thehook frameworkIs added to the vacant position,load commandsIt also inserts backwards.
  • In other words, the users themselves introduced itframeworkIt will be injected againframeworkPreviously linked. (Jailbreak environmentInsert LibraryThis is the same thing, and we’ll talk about how the jailbreak plugin works later).

The specific practices

So with this feature, we can add a framework and write protection code in its load. The guard code will be executed earlier than the injected code. In other words, it has been detected that it has been debugged. Sysctl will be hooked later by the injected code, but we have finished monitoring.

And since it is a separate framework, it would be different if you used a symbol breakpoint to get the function address to calculate the head-based offset, because each framework is a separate Mach-O that needs to find the corresponding file header address rather than the main program header address. Parsing Mach-O should also be the framework and not the main program. There is also an additional time drain on the attacker.

(Experienced contrarians can actually forget this, often assuming they miscalculated.)

Assessment of means of protection

This method is suitable for re-signature debugging in non-jailbreak environment and has achieved a better effect.

The attacker can only use static assembly analysis logic modification to attack the framework load, because it is always executed before the injected code is relatively secure. This is more complex and time-consuming than dynamic debugging.

(Also, the jailbreak plugin is not the same principle and can not be protected, we will explain how to protect the jailbreak plugin later)

prompt

  • It is not recommended to open a special one for protectionframework“And give it a very obvious name so an attacker can see it easilyframeworkIt was written to protect it. Combine itloadMethod assembler analysis, found you only made onesysctlStatic modification is also very simple.loadMethod directlyretWill do.
  • You can add this protection logic to a library with actual functional logic for better protection.
  • Based on this mechanism (our guard code can be executed before the injected code), we can do a lot of things, such asruntimeThe method of switching shielding,fishhookShielding. I’ll add that later.

Protection 4: Function address saving bypasses Fishhook

Bypassing Fishhook to call system functions

We had the idea that at the beginning of my project I would get the address of ptrace/sysctl and then call this function directly from that address. It actually works, using the dlSYm function.

In our opinion, the above protection method 3 can guarantee the priority of the implementation of the protection code, but it still seems to treat the symptoms rather than the root cause. So how can we ensure that the system functions we need won’t be killed by Fishhook?

In the article on starting optimization of Clang pegs for binary rearrangement, we mentioned the function address by symbol (DLADDR function), and used the function symbol by function internal address (DLSYM function).

Used to show

#import "MyPtraceHeader.h"
#import <dlfcn.h>
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
   
    // Hide the constant string
    unsigned char str[] = {
        ('a' ^ 'p'),
        ('a' ^ 't'),
        ('a' ^ 'r'),
        ('a' ^ 'a'),
        ('a' ^ 'c'),
        ('a' ^ 'e'),
        ('a' ^ '\ 0')};unsigned char * p = str;
    while (((*p) ^= 'a') != '\ 0') p++;
    
    void * handle = dlopen("/usr/lib/system/libsystem_kernel.dylib", RTLD_LAZY);

    int (*ptrace_p)(int _request, pid_t _pid, caddr_t _addr, int _data);
    // Get the ptrace pointer
    ptrace_p = dlsym(handle,(const char *)str);
    if (ptrace_p) {
        // If it has a value, it can be called smoothly
        ptrace_p(PT_DENY_ATTACH,0.0.0); }}Copy the code

The first use of constant string hiding is described in detail in Section 3 of this article.

This can be done if we want the system functions we use to be unhooked.

prompt

(MonkeyDev has already hooked DLSYm), so it can be used in conjunction with the protection method 3, that is, to ensure that the address method is executed in advance, which is not mentioned here.

Assessment of means of protection

This kind of protection circumvents Fishhook indirectly, but not absolutely. Obviously, we also use the dlopen and DLSYm system functions, so they are also vulnerable to fishhook.

Don’t worry, I’ll talk more about how to better solve this problem.

Defense Mode 5: Bypass symbol breakpoint syscall

Syscall is a function of system-level calling functions. For example, we want to call ptrace, but we don’t want symbolic breakpoints to block ptrace. Then you can call pTrace directly using the syscall function without importing any header files.

Using the demonstration

/** 1. 26 refers to the ptrace function 2. 31 refers to the pTRACE parameter PT_DENY_ATTACH */
syscall(26.31.0.0);
Copy the code

To see what function each ordinal corresponds to, you can import #import

.

ptrace
exit

Of course, syscall is also a system function, so in addition to assembly, you can combine guard 4 with Guard 3 to solve problems that fishhook will kill. And syscall’s symbolic breakpoints can also be broken, and then you can read the register to see if you called ptrace, exit, etc.

How do you solve it? And then we look down.

Defense 6: Assembly Call (recommended version)

None of the defenses written above seem perfect, and there is a risk that FishHook will hook any method you use (although you may use framework early execution, bypass symbolic breakpoints, use system-level calls, But it still leaves a few ‘telltale signs’).

How to solve it?

We can call Syscall directly from assembly.

Using drill

- (void)viewDidLoad {
    [super viewDidLoad];
    Call syscall with assembly to call pTrace
    #ifdef __arm64__
    asm volatile(
                 "mov x0,#26\n"
                 "mov x1,#31\n"
                 "mov x2,#0\n"
                 "mov x3,#0\n"
                 "mov x16,#0\n"
                 "svc #0x80\n"// This command triggers an interrupt (system-level jump!).
    );
    #endif
    
    // Call ptrace directly using assembly
    #ifdef __arm64__
    asm volatile(
                 "mov x0,#31\n"
                 "mov x1,#0\n"
                 "mov x2,#0\n"
                 "mov x16,#26\n"
                 "svc #0x80\n"
                 );
    #endif
}
Copy the code

The x16 register will place the corresponding number of the function that needs to be called to syscall. Of course, register instructions vary from schema to schema. For example, to call exit, we could write:

#ifdef __arm64__
    asm volatile(
                 "mov x0,#0\n"
                 "mov x16,#1\n"
                 "svc #0x80\n"
                 );
#endif
# ifdef __arm__ / / 32
    asm volatile(
                 "mov r0,#0\n"
                 "mov r16,#1\n"
                 "svc #80\n"
                 );
#endif
Copy the code

Evaluation of protection scheme

Call syscall using assembly instructions

  • Can prevent system functions from beingfishhookTake out.
  • Adding a symbolic breakpoint does not break.
  • Attacker static analysis is also difficult to find.

This method is recommended.

Dynamic debugging other defense modes

Dynamic debugging in addition to the above protection methods, there are many programs, here list a few for your reference.

  • Introduce dynamic library monitoring. Use whitelist to monitor the third-party libraries currently introduced into our project and find whether there is unknown library injection.
    • Note: Due to the program itselfmach-oIt can also be detected here, and it’s the first one, so the cycle should start from1At the beginning, that’s the elimination itselfmach-o .
    • This method can also monitor the jailbreak environmentDYLD_INSERT_LIBRARIESDynamically injected plug-ins.
    • This method effectively detects Cycript jailbroken and non-jailbroken debugging.
  • Bundle IDDetection, re-signature project is the need to modify the package name, can be monitored.

2. Code confusion

Because it is easy to see our class and method names using LLDB, class-dump, or Mach-O viewing tools after breaking the shell and applying the restore symbol, forward developers who follow the code specification would normally have standard hump and English names. This provides the attacker with great convenience and searchability.

Code obfuscation can obfuscate the method name and the class name without affecting the forward developer, making classes and methods that do not remove symbols in the generated binary unreferential.

use

The mechanism of macro definition can help us meet the need of obfuscation. For a project that has been developed, we can easily obfuscate it.

For example:

- (void)viewDidLoad {
    [super viewDidLoad];
    LBObjectClass * objc = [LBObjectClass new];
    [objc LBObjectTestFunc];
    BOOL result = [objc checkIsVipWithkeyString:@ "12311" Token:@"jfkfqwe"];
    NSLog(@"%d",result);
}

@interface LBObjectClass : NSObject
- (void)LBObjectTestFunc;
- (BOOL)checkIsVipWithkeyString:(NSString *)string Token:(NSString *)token;
@end

@implementation LBObjectClass
- (void)LBObjectTestFunc{
    NSLog(@"this is a confusion func");
}

- (BOOL)checkIsVipWithkeyString:(NSString *)string Token:(NSString *)token{
    if ([string containsString:@ "111"] && (token ! =nil))return YES;
    return NO;
}
@end
Copy the code

In the example above, we have a class called LBObject that has two methods, one with no parameters and no return value, and one with parameters and a return value.

How do you mix it up?

It is easy to define the corresponding macro. Here I add the following to the PCH file:

#define LBObject HDJSNWOIJNWPKFWD
#define LBObjectTestFunc LKNWFMWJFNJMSLW
#define checkIsVipWithkeyString IWRNWKJNDS
#define Token NFKAOWRL
Copy the code

After the addition, we found that the class name and method name color changed. The original code does not need to be modified.

Results show

MachOView view before confusion:

MachOView view after confusion:

Class dump query before confusion:

prompt

Class-dump is a method of reading headers from Mach-o for someone else’s unwrapped application to get definitions of classes and their methods and properties. (Use method reference relabasing to apply debugging and code changes.)

Class dump error:

The Hopper view method assembly implementation has the same effect if it is obfuscated. I’m not going to show it here.

Macro definition code obfuscation scheme evaluation

Advantages:

  • 1️ code confusion can effectively increase the analysis time of attackers and increase the interference.
  • 2️ code confusion can be easily processed on existing projects.
  • 3️ : The addition of confusion has basically no influence on normal development of positive staff.
  • 4️ : Macro definition mechanism can be fully used for flexible processing, such as class name method name is random string every time running.

disadvantages

  • 1️ : code confusion uses macro definition mechanism, so it will increase certain time in the pre-compilation stage, but it has no impact on users during operation, and can target some important codesorFeatures add confusion.
  • 2️ discount: The use of macro definition needs to pay attention to the effect of other same strings in the project. (for example, I used one in the above caseTokenMacros, then be sure to pay attention to other usesTokenUse a unique string instead of.)

There are also a number of third-party libraries for code obfuscation that are implemented using compilers. They are implemented automatically, and the idea is generally the same. Macro definitions are done manually. But also more flexible and simple, a higher degree of customization.

3. String constants are hidden

In reverse development, some constant strings in our project will be written directly after assembly instructions in the form of comments when viewing assembly using Hopper, which will provide great clues and guidance for attackers.

For example:

#define STRING_ENCRYPT_KEY "demo_AES_key"
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    
    LBObject * objc = [LBObject new];
    BOOL result = [objc checkIsVipWithkeyString:@ "12311" Token:@"jfkfqwe"];
    lbCFunc(STRING_ENCRYPT_KEY);
}

void lbCFunc(const char * str){
    printf("%s",str);
}
@end
Copy the code

When using the string constant or macro definition, an attacker uses hopper to view assembly as follows:

(Note: Hopper is already in production.)

Even the assembly restore pseudocode that Hopper comes with can do the following:

I’m afraid to think about it.

So how do you hide a constant string?

The simple way to do this is to replace the string constant with a method that returns the desired string, for example:

For example, in many students’ projects, symmetric encryption keys may be written locally, and some important keys/secret/tokens may also be written locally (here is just an example, to illustrate some important constant strings, Now similar to the key is currently generally issued by the server), so for this kind of more obvious and more important string, we had better do a bit of hiding processing.

Simple demonstration

#define STRING_ENCRYPT_KEY @"demo_AES_key"
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    //[self uploadDataWithKey:STRING_ENCRYPT_KEY]; // Use macro/constant strings
    [self uploadDataWithKey:AES_KEY()]; // Use functions instead of strings
}

- (void)uploadDataWithKey:(NSString *)key{
    NSLog(@ "% @",key);
}

static NSString * AES_KEY(){
    unsigned char key[] = {
        'd'.'e'.'m'.'o'.'_'.'A'.'E'.'S'.'_'.'k'.'e'.'y'.'\ 0'};return [NSString stringWithUTF8String:(const char *)key];
}
@end
Copy the code

Results show

Use constant strings/macros

Using the function

You can see that the undisplayed string is written directly, which would be better with method obfuscation.

But some students might ask what happens if an attacker statically analyzes the function that returns key.

Function hide string upgrade version

#define STRING_ENCRYPT_KEY @"demo_AES_key"
#define ENCRYPT_KEY 0xAC
@interface ViewController(a)
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
// [self uploadDataWithKey:STRING_ENCRYPT_KEY]; // Use macro/constant strings
    [self uploadDataWithKey:AES_KEY()]; // Use functions instead of strings
}

- (void)uploadDataWithKey:(NSString *)key{
    NSLog(@ "% @",key);
}

static NSString * AES_KEY(){
    unsigned char key[] = {
        (ENCRYPT_KEY ^ 'd'),
        (ENCRYPT_KEY ^ 'e'),
        (ENCRYPT_KEY ^ 'm'),
        (ENCRYPT_KEY ^ 'o'),
        (ENCRYPT_KEY ^ '_'),
        (ENCRYPT_KEY ^ 'A'),
        (ENCRYPT_KEY ^ 'E'),
        (ENCRYPT_KEY ^ 'S'),
        (ENCRYPT_KEY ^ '_'),
        (ENCRYPT_KEY ^ '\ 0'),};unsigned char * p = key;
    while(((*p) ^= ENCRYPT_KEY) ! ='\ 0') {
        p++;
    }
    return [NSString stringWithUTF8String:(const char *)key];
}
@end
Copy the code

In this way, these characters do not enter the character constants area. The compiler converts them directly to xor results.

4. Jailbreak environment protection

In a jailbreak environment, Monkey also provides a plugin for Xcode that makes it easy to write your own plugin. (for those who are interested, read the full reverse exploration and writing process in my article on Tweaking and Tweaking.)

How plug-ins work

In my iOS low-level overview of the DYLD loading process, we learned that an environment variable, DYLD_INSERT_LIBRARIES, exists. Dyld uses this identifier to determine whether to load the insert dynamic library.

  • The cornerstones of Cydia in jailbreak: MobileSubstrate will hook SpringBoard’s [FBApplicationInfo environmentVariables] function Add the environment variable DYLD_INSERT_LIBRARIES setting to the plug-in (dynamic library) that you want to load. The binary package of the application does not need to be changed, but dyLD will load the application with the specified insert library due to the mechanism of DYLD_INSERT_LIBRARIES.

  • The plug-in needs to specify the process to attach at development time, so you can know which application needs to be inserted when loading the plug-in.

This is how jailbreak plugins work. Tweak plugins compiled through.o and other intermediates actually end up generating a dylib, which is then packaged to generate a.deb package, which is changed to zip and you’ll see our dylib.

Monkey
theos
dylib
openssh
/Library/MobileSubstrate/DynamicLibraries
dyld
dyld

It is different from the code injection of re-signed applications, that is, by modifying the load Commands of the application. Although both are injected through dynamic libraries, it can be said that the principle is completely different. The jailbreak environment plug-in does not need to modify the target file.

Now that we know how the jailbreak environment plugin works, let’s talk about how the plugin can be protected.

Escape Environment protection method 1: __RESTRICT

The principle of analysis

In the dyLD source code, we found the following code:

if ( gLinkContext.processIsRestricted ) {
    pruneEnvironmentVariables(envp, &apple);
    // set again because envp and apple may have changed or moved
    setContext(mainExecutableMH, argc, argv, envp, apple);
}
Copy the code

When processIsRestricted is set to true, the environment variable is removed, which means that DYLD_INSERT_LIBRARIES can be ignored. Continue searching and see the following:

if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) {
    gLinkContext.processIsRestricted = true;
}
Copy the code

That is, with hasRestrictedSegment, the processIsRestricted flag is set to true.

mach-o
__RESTRICT section
__restrict section
true
__RESTRICT section
__restrict section

Operation demo

Add Other Linker Flags: -wl,-sectcreate,__RESTRICT, __RESTRICT, /dev/null.

Add and compile to look at Mach-o.

After adding, all jailbreak plugins will add invalid, I will not demonstrate here.

Assessment of means of protection

  • This scheme can effectively shield all plug-ins in jailbreak environment.
  • The effect of the scheme is relatively detailed, so it is easier to detect.
  • The program may exist becausedyldAs the system is updated to modify this mechanism, there is a risk that the protection will fail.

Protection cracking

Since this scheme can block all plug-ins, it is easy for attackers to assume that __RESTRICT is used to prevent them. Open Mach-O and check to see if you have the section or section.

Due to the dyLD loading mechanism, segments and sections that are not named by this name are not considered restricted processes. So, you just need to change the binary. There are many ways to change the binary. There is a special binary modifier called Synalize It! Even MachOView, Hopper, IDER and other visualization tools can be modified directly.

In fact, we have talked about the principle of iOS application signature and verification in the principle of application signature and re-signature (re-signature wechat application actual fight). The root of the above problem is that when we modify the binary, we need to re-sign the application. Otherwise, the application will be modified in the iOS signature check, and the hash value will change. Then we can’t get through the inspection.

We won’t cover the re-signing process, which is demonstrated in detail in the above article. Here’s how to protect against this practice.

Cracking means protection

Inspired by the fishhook source code and Alibaba’s open source component-based BeeHive framework, we can do what Dyld is doing ourselves. In other words, given that we have added __RESTRICT sections and sections, we can read the names of the Mach -O sections and sections ourselves at runtime to meet the need to check whether __RESTRICT sections and __RESTRICT sections have been modified.

I don’t know if you have clear idea of this idea, explain it:

The __RESTRICT and __RESTRICT sections have been modified to protect our binary. Let’s check it out ourselves at runtime.

  • Some are not modified.
  • If not, it means it has been modified.
Demonstration of use of protective means
#import <mach-o/loader.h>
#import <mach-o/dyld.h>

#if __LP64__
#define LC_SEGMENT_COMMAND LC_SEGMENT_64
#define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT
#define LC_ENCRYPT_COMMAND LC_ENCRYPTION_INFO
#define macho_segment_command segment_command_64
#define macho_section section_64
#define macho_header mach_header_64
#else
#define macho_header mach_header
#define LC_SEGMENT_COMMAND LC_SEGMENT
#define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT_64
#define LC_ENCRYPT_COMMAND LC_ENCRYPTION_INFO_64
#define macho_segment_command segment_command
#define macho_section section
#endif

@implementation ViewController+ (void)load
{
    // The 0th column in the imagelist is our own executable
    const struct mach_header * header = _dyld_get_image_header(0);
    
    if (hasRestrictedSegment(header)) {
        NSLog(@" No problem!");
    }else{
        NSLog("Detected!!");
        // Exit the program, can report or record..
        #ifdef __arm64__
            asm volatile(
                         "mov x0,#0\n"
                         "mov x16,#1\n"
                         "svc #0x80\n"
                         );
        #endif
        # ifdef __arm__ / / 32
            asm volatile(
                         "mov r0,#0\n"
                         "mov r16,#1\n"
                         "svc #80\n"
                         );
        #endif}}static bool hasRestrictedSegment(const struct macho_header* mh)
{
    const uint32_t cmd_count = mh->ncmds;
    const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(struct macho_header));
    const struct load_command* cmd = cmds;
    for (uint32_t i = 0; i < cmd_count; ++i) {
        switch (cmd->cmd) {
            case LC_SEGMENT_COMMAND:
            {
                const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
                
                if (strcmp(seg->segname, "__RESTRICT") = =0) {
                    const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
                    const struct macho_section* const sectionsEnd = &sectionsStart[seg->nsects];
                    for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
                        if (strcmp(sect->sectname, "__restrict") = =0)
                            return true; }}}break;
        }
        cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
    }
    
    return false;
}
@end
Copy the code

Of course, to also prevent our hasRestrictedSegment code from getting hooked, you can use a combination of the confused-ahead execution mechanism (this article’s dynamic debugging – Defense mode 3: PTrace/SYSCTL optimized version – Ahead execution).

Again, it is not recommended to exit the application directly and leave no obvious traces of protection. Otherwise, attackers will not first suspect that they have not made a clear point, but will analyze this point.

Eat the melon link

Where did this protection come from? I don’t know if anyone still knows’ Nian Qian ‘, a reverse engineer of Alipay and an active senior in CSDN. She started to share reverse knowledge at the beginning of 2014. We are very grateful to those senior engineers who paved the way for us and let us stand on the shoulders of giants.

Protection method 2: Monitor environmental variables

DYLD_INSERT_LIBRARIES (ld_insert_libraries) Therefore, it is possible to detect whether there are any jailbroken mods, or jailbroken environments (since jailbroken environments already have Mobile Substrate mods by default).

// Jailbreak detection
char * dlname = getenv("DYLD_INSERT_LIBRARIES");
if (dlname) {
    NSLog("Jailbreak your phone, turn off some features");
}else{
    NSLog(@" Normal phone!");
}
Copy the code

Defense mode 3: Image List

This means of protection is in fact our dynamic debugging of other protection methods in the first. Use a whitelist to monitor the currently imported third-party libraries in your project and look for unknown library injections.

Dyld insert_libraries is a dynamic library injection that is loaded dynamically by dyLD without modifying the binary itself.

We can also use the following:

conclusion

Protection tips:

  • A single means of protection is often not enough to ensure safety, and the combination of multiple means of protection can play a better effect.
  • Do not leave visible traces of the guard code, especially inUILayers (e.g. flashbacks, bounces, etc.).
  • There is no absolute security of protection, increase the attacker’s search and analysis costs can achieve the purpose of protection.
  • If you don’t know how to attack, you can’t defend.