Contents

Skiddo's Tutorial 2 - Cracking a Mac Program

This is not a note, this is a diary. Briefly explain the knowledge (tao) knowledge (lu) and gadgets needed to crack an OSX (well… it will be called macOS soon) program. After all, as a tool boy, knowing how to use tools is half of success.

0xFF Getting Started

  • There should always be a little bit of x86 compilation. Just print a cheat sheet and look at it.
  • C language requires a certain understanding, after all, the disassembly results are presented in the form of pseudo-C language.
  • The common sense registration button is register in English, activate is called activate, caught rape is revoked, and so on.
  • Routine** read **

0x00 tool

  • IDA one-click decompilation. Static analysis big killer.
  • Hopper Disassembler The disassembly software under OS X, just download the trial version. Have a unique understanding of Obj-C, after all fashionable
  • unsign (optional) A small tool for anonymous masters to forcibly remove the program signature. Digital signatures under mac are available in almost all programs. The following will talk about two ways to bypass signatures, and this is one of them.

0x01 string search

The software selected here is the famous “network acceleration tool” Surge under OS X. Actual combat is the baby’s favorite ho ho ho ho

The software downloaded from the official website is a Surge.app folder named [name].app Folders are how OS X software is organized. exist Surge.app/Contents/MacOS/ Find the real executable file of the software under. Load Hopper and IDA separately.

Shift-F12 Open the IDA strings window, Ctrl-F Search register, accidentally find an important thing registerButtonPressed

/2016/osx-crack/00.png

0x02 Find references

Think about the MVC I learned in my sophomore year and know that this is a button event or a registration button. The function bound to it must be a registration-related key function. Double-click to enter the disassembly window to find data references here. Speaking of IDA here, all string names are automatically named as names related to the string content, such as sel_registerButtonPressed. Very intuitive. ~~Although the CFString string references introduced by Obj-C in the decompiled code are too blind to read…but it is still very useful! ~~

Note that you cannot use x key to search for references to this string directly. Here I have taken a detour. At first I thought that the function it jumped to must be a registration-related function, but when I jumped to xref, I directly named the variable as registerButtonPressed, Later, when I looked at the logic and found that it was a dead end, I took a closer look at the call statement, and it was this sentence.

# IDA
__text:000000010009E6FC                 lea     rax, registerButtonPressed
# Hopper
000000010009e6fc         lea        rax, qword [ds:sub_10009e785]

From the point of view of naming rules, registerButtonPressed This string is regarded as a function! Although this function is also related to registration (a function to obtain information after clicking on the registration information), we just saw that it is a string. Until here, I didn’t notice the difference between code reference and data reference, there is a detailed article on this topic here 1. In short, the general difference is that we click on a line of code x Searching for shu ju yin yong is to search for the “parameter” (operand) of the current assembly statement, and data reference is to search for the reference to the memory address where this statement is located. I usually pop up the xref window on the function name, so I just Can’t see the difference. But on this string, it’s the data reference that should be looked at. To view data references, you can use IDA’s Cross Reference subview. In the assembly comment, one is also displayed by default. The number displayed can be changed in the settings. See the article just mentioned 1 for details.

/2016/osx-crack/01.png

0x03 Modify key functions

After jumping over, it is found that this function is bound to __WindowController_registerButtonPressed__, and that controller stores a function in a variable.

================ B E G I N N I N G   O F   P R O C E D U R E ================



                     -[WindowController registerButtonPressed:]:
000000010009e2bd         push       rbp                                         ; Objective C Implementation defined at 0x10029a338 (instance)
000000010009e2be         mov        rbp, rsp
000000010009e2c1         sub        rsp, 0x30
000000010009e2c5         mov        rax, qword [ds:imp___got___NSConcreteStackBlock]
000000010009e2cc         mov        qword [ss:rbp+var_28], rax
000000010009e2d0         mov        dword [ss:rbp+var_20], 0xc2000000
000000010009e2d7         mov        dword [ss:rbp+var_1C], 0x0
000000010009e2de         lea        rax, qword [ds:sub_10009e322] ; 关键函数(因为整个方法里面只有这个变量类型是函数 ˊ_>ˋ )
000000010009e2e5         mov        qword [ss:rbp+var_18], rax
000000010009e2e9         lea        rax, qword [ds:0x100257d20]
000000010009e2f0         mov        qword [ss:rbp+var_10], rax
000000010009e2f4         call       qword [ds:imp___got__objc_retain]
000000010009e2fa         mov        qword [ss:rbp+var_8], rax
000000010009e2fe         mov        rsi, qword [ds:0x1002ae0b0]                 ; @selector(refresh:), argument "selector" for method imp___got__objc_msgSend
000000010009e305         lea        rdx, qword [ss:rbp+var_28]
000000010009e309         mov        rdi, rax                                    ; argument "instance" for method imp___got__objc_msgSend
000000010009e30c         call       qword [ds:imp___got__objc_msgSend]
000000010009e312         mov        rdi, qword [ss:rbp+var_8]
000000010009e316         call       qword [ds:imp___got__objc_release]
000000010009e31c         add        rsp, 0x30
000000010009e320         pop        rbp
000000010009e321         ret
                        ; endp

This function is the event that is triggered after the button is clicked. After following in, that’s it!

__text:000000010009E322 ; =============== S U B R O U T I N E =======================================
__text:000000010009E322
__text:000000010009E322 ; Attributes: bp-based frame
__text:000000010009E322
__text:000000010009E322 ked_doActivate  proc near               ; DATA XREF: -[WindowController registerButtonPressed:]+21o
__text:000000010009E322
__text:000000010009E322 var_48          = qword ptr -48h
__text:000000010009E322 var_40          = dword ptr -40h
__text:000000010009E322 var_3C          = dword ptr -3Ch
__text:000000010009E322 var_38          = qword ptr -38h
__text:000000010009E322 var_30          = qword ptr -30h
__text:000000010009E322 var_28          = qword ptr -28h
__text:000000010009E322
__text:000000010009E322                 push    rbp
__text:000000010009E323                 ...
__text:000000010009E336                 call    ked_getValid   ; 让这个函数的返回值为2!
__text:000000010009E33B                 cmp     eax, 2
__text:000000010009E33E                 jnz     loc_10009E410  ; 不是2就死了
__text:000000010009E344                 ...
__text:000000010009E39C                 mov     rsi, cs:selRef_initWithTitle_message_cancelButtonTitle_cancelAction_
__text:000000010009E3A3                 lea     rdx, cfstr_SurgeHasBeenAc ; "Surge has been activated. Thanks for your purchase. Enjoy now!"
__text:000000010009E3AA                 lea     r8, cfstr_Ok    ; "OK"
__text:000000010009E3B1                 lea     r9, [rbp+var_48]
__text:000000010009E3B5                 xor     ecx, ecx
__text:000000010009E3B7                 mov     rdi, rbx
__text:000000010009E3BA                 call    r12 ; _objc_msgSend

0x04 Application signature

There should be no doubt that OS X software is “safe”, because by default, Apple’s system only allows一百美元一年 Programs written by programmers certified by Apple for anti-addiction run on the system. Once the programmer goes mad releases malware and reports it to Apple, it will be revoked, and the system’s GateKeeper will stop it The program runs 2. Moreover, the system will unconditionally reject programs with invalid signatures after tampering and programs with false signatures without a whitelist. It’s a small problem. The solution is to either change the signature or delete the signature.

0x0401 Delete digital signature

this appletunsign You can delete the signature certification in a mach-o binary, but the disadvantage is that you cannot re-sign it…it is quite destructive. Although mac refuses fake signatures, Apple has to give in even if we have no money. Programs without signatures can be used by manually adding a whitelist. Use this program to delete all the signature verifications of all the binary files in the application, and give a command to delete all other verification files in the app.

uns(){ ~/unsign "$1";mv "$1" ~ ; mv "$1.unsigned" "$1";}
uns 'Surge.app/Contents/MacOS/Surge'
uns 'Surge.app/Contents/Applications/Surge Dashboard.app/Contents/MacOS/Surge Dashboard'
uns 'Surge.app/Contents/Applications/surge-cli'
find . -name _CodeSignature | xargs -I% rm -R "%"

0x0402 Modify digital signature

This method was originally prepared by Apple for the little bitches who were plotted against by the dark side. It can overwrite the original signature of the program and write a new verification file. I wrote a small batch signature script for surge.** Requirements: $100 a year mac developer ID (), system installation of any version of XCode. **

#!/bin/bash

# https://jbavari.github.io/blog/2015/08/14/codesigning-electron-applications/

app="$PWD/Surge.app"
# iden是钥匙串里相应证书的 Common Name ,课上讲的证书的CN
iden="Mac Developer: thisiscertificatecommonname (XXXXXXXXXX)"

sign(){
    codesign --deep --force --verify --verbose -s "$iden" $1
}

find $app -name _CodeSignature | xargs -I% rm -R "%"

sign "$app/Contents/Frameworks/Sparkle.framework/Sparkle"
sign "$app/Contents/Frameworks/Sparkle.framework/Versions/A"
sign "$app/Contents/Applications/Surge Dashboard.app"

sign "$app"

0x05 Routine

The reverse tutorials I have seen are basically like this, and it ends here. Put on the tools and shell out. Find the entrance, follow the compilation. Next step, next step, next step (clap clap keyboard sound). ok we found it_ key function _ , patch, OK.

What a ghost to learn!

People who are proficient in assembly still need to read tutorials? How can people who need to read tutorials understand assembly? Um? Why do I post compilation? Because~~ Pretentious and beautiful~~ The most accurate way to describe the program is through compilation! And things like pseudo-code seem to require skills like compilation, and the baby doesn’t actually read compilation, so I will try my best to hold back a little dry stuff.

The main content of this black-broadcast is as follows:

  • What You Need to Know About Finding Strings
  • How to use magicconch F5 Read the disassembled source code
  • How to identify whether the result of decompilation is correct
  • How to Identify “Critical Jumps”
  • How to ~~ sort out the function call relationship by guessing~~

0x0501 String

I may not have mentioned it in the last article, but there is a problem that may sometimes be encountered, that is, Chinese strings will be displayed as garbled characters. This problem can be solved by adjusting the code through the method of this post 3. This seems is slightly better than OD’s Chinese search engine, because there are more setting options that can be identified.

_The following words are all my wild guesses. _

If the string is encrypted, the idea that can be adopted can be, for example, dump the memory in the virtual machine, or analyze the function that is called a lot outside the standard library, it may be an encryption and decryption function. Of course, the version of libc and other libraries must be identified accurately. After a long time, it is embarrassing to find that the target function is printf.

There is also a small routine, an article I read, the source of which cannot be tested. In order to counter static analysis, some programs will add some garbage before and after the string, and then quote the string from the middle to avoid the reference check. For example, in a program using const char* helloStr="fuckyouHello world";, and then call it with printf(helloStr+7); Output, if the compiler optimization is turned on, the address of helloStr will not appear in the reference. When IDA recognizes a string, it will only recognize the beginning of the string, and will not search for references from the middle. Similarly, replace the fuckyou in the above example with non-ascii junk characters, and the reverse tool will not treat it as part of the string. At this time The call to helloStr itself would not be detected, thereby confusing the software.

0x0502 Use decompilation with caution

The usefulness of pseudocode is actually very limited, but it will save brain power if used well, especially for judging code segments with too many jump statements, IDA will be more powerful than we imagined.

In general, a large part of the code in a GUI program is generated by the IDE, and most of these codes are useless, only the part where the view interacts with the controller, that is, calling or referencing functions in the program is useful. Generally, vigilance is required only where there are input and output. There are many tricks and tricks on the Internet to teach us how to capture button events and other things. Finding this is usually more than half of the success. These codes roughly scan the assembly code, and use xref graph to find the jump of the target in the code area.

The assembly source code we need to read is probably the context of the statement that needs to be changed after finding the key judgment. For complex algorithms, the average developer either uses a ready-made library such as openssl or implements it with a program by himself, and it is almost impossible to fine-tune the generated assembly code when writing the code himself, so he can only hope for the compiler, and routine compilation The compiler is exactly IDA’s strong point. The more I use it, the more I feel that it is really strong. IDA can almost recognize the if else judgment that the hopper cannot recognize.

0x0503 Authentication code

As a fake driver who kills and does not read the compilation, it is a bit of a trivial job. For disassembly software, like us, they also rely on guessing, but it is just a huge collection of experience. First, the compiler’s guesses about types are mostly wrong. What’s more, there are some enums, structs, and objects that will irretrievably lose information after compilation. The string ends up with ints, and the contents of the aggregate are disassembled into memory addresses and variables. This is not wrong, just like when a person speaks, grammatical errors do not affect the meaning of the expression. The most distressing thing is that the meaning is wrong. such as above registerButtonPressed function, Hopper’s disassembly looks like this,

void -[WindowController registerButtonPressed:](void * self, void * _cmd, void * arg2) {
    rax = [self retain];
    var_8 = rax;
    [rax refresh:__NSConcreteStackBlock];
    [var_8 release];
    return;
}

The function we want is gone… So just see if it is disassembled correctly, and see if there are more or less variables in the middle. As for the logic, even if the decompiler cannot recognize the use of goto, it will not mess up the structure.

0x0504 “Critical Jump”

Probably two directions. When one is suitable for dynamic debugging, set a breakpoint after input or output. If you encounter a big loop, you are basically accessing strings in a loop. The call after the loop is generally worth noting. Some CTF questions are like this. If you look at the code directly, it is also searching for nearby function calls. The other is to search for strings, so strings are very important. In the above example, the function containing it can be found by directly using the reference of the string. This routine is used in many places.

0x0505 call relationship

In the article 1 cited above, there is a special section on how to make a flowchart by analyzing the calling relationship between a piece of code. In the final compiled program, only one or several programs are user codes, and the remaining storage areas include constant tables, statically linked library functions, and some weakly typed languages 4 also retain runtime functions binding information. Seeking in these fixed segments is much faster. The string is also one aspect, so I won’t mention it in detail.

I’m so sleepy that it’s all written casually.