본문 바로가기

40./42. iOS

iOS App 크랙방지

크랙 방지 (Crack prevention)


크랙 작동 원리


재배포


크래커들은 재배포 할 파일들을 얻는다. 대표적인 DRMs(DRM : 디지털 권리 관리)을 무력화하는 대표적인 툴은 Crackulous와 AppCrack이 존재한다. 해당 툴을 이용하여 인증받지 않은 기기에 재 배포한 IPAs을 설치 가능하다.

※ Crackulous와 AppCrack는 Cydia에서 다운 및 설치 가능하다.

AppStore
앱스토어의 모든 앱들은 역분석(reverse engineering)을 방지하기 위해 암호화되어 다운로드된다. 

또한 인증받은 계정을 이용하면 자신의 다른 기기에도 다운로드 및 설치가 가능하다. 하지만 CPU에서는 암호화된 명령어(CPU instructions)들은 작동할 수 없다. 모든 앱을 실행되면 복호화가되어 램에 로딩된다. 크래커들은 이러한 사실을 공격(exploit)하게되며, 해당 앱을 실행시 GDB를 이용하여 복호화된 데이터를 덤프하게된다.


암호화된 앱들은 LC_ENCRYPTION_INFO 명령어를 로드하며, 명령어는 다음과 같다.

#define LC_ENCRYPTION_INFO 0x21
struct encryption_info_command {
  uint32_t cmd;
  uint32_t cmdsize;
  uint32_t cryptoff;   // file offset of first encrypted byte
  uint32_t cryptsize;  // file size of encrypted data
  uint32_t cryptid;    // method of encryption
};

바이너리 파일이 암호화 되었을때 load command는 반드시 존재하며, 아래 crypt으로 시작하는 3개의 필드 값은 0이 아니다. deCrypt, xCrack과 같이 GDB명령어를 이용하여 덤프를 얻는다.

GDB를 이용한 메모리 덤프 작성





CydiaStore와 RockApp

CydiaStore와 RockApp은 간단히 말해 흔히 사용하고 있는 APT/DPKG 시스템므로 인증된 보안 레포지토리(repositories)를 연결하여 사용합니다. AppStore와 같은 암호화/DRM이 없습니다. 그러므로 

모든 크랙커들은 .deb 다운로드 및 획득을 위해서 사용합니다.


크랙방지


일반적인 기술


모든 불법복제 크래킹을 100% 막을 수 없습니다. 왜냐하면 배포되는 모든 앱의 모든 코드에 접근 가능하기 때문이다. 하지만 합법적인 판매 절차를 걸치거나 분석 시 초기 지연을 가능하게 할 수 있습니다.


Multi-pass check

위치를 변경한 다양한 체크방식과 같은 간단한 방법을 이용하여 크래커들의 분석을 복잡하게 만들 수 있다. 편리한 방법으로는 인라인 함수를 정의하는 것이다.

__attribute__((always_inline)) void check_crack(symbol, length, result) {
  if (checksum(symbol, length) != result)
    exit(0);
}
...
check_crack(my_inline_uuid_check, 0x200, 0x12345678);
register int res = my_inline_uuid_check();
...
check_crack(my_inline_serial_number_check, 0x200, 0x87654321);
...

여기서 중요한 점은 항상 _inline을 사용하는 것이다. 크래커는 check_crack() 함수를 바로 패치할 것이며 안티 크랙은 바로 실패 될 수있다. 하지만 너무 CPU 코드 사용을 아끼려하지 말아라.


※ 인라인 함수 vs 일반함수

1. 일반함수 수행방법

프로그램이 함소 호출 명령에 도달하면 다음과 같은 흐름을 갖는다.

함수 호출 후 다음으로 사용할 명령어의 주소를 메모리에 저장 -> 스택에 전달인자를 복사 -> 해당 함수의 시작의 메모리 위치로 점프 -> 함수 수행 -> 함수 리터값을 레지스트리에 저장 


2. 인라인 함수 수행방법

인라인 함수는 프로그램 코드들 가운데 컴파일된 함수 코드가 삽입된다. 이는 컴파일러에 의해 해당 인라인 함수가 함수 코드로 대체된다. 인라인 함수를 사용하면, 프로그램은 해당 코드를 수행하기 위해 위의 일반 함수 수행처럼 메모리에 있는 함수 주소를 찾아 점프할 필요가 없어지게 되어, 일반 함수보다 약간이나마 빠른 수행 속도를 갖을 수 있다. 하지만 크기가 큰 코드를 가진 함수를 인라인 함수로 사용하고, 10번을 호출하게 된다면, 해당 프로그램 코드 사이에 10개의 복사본을 가지게 되어 메모리 효율성면에서 따지면 좋지 않을 수 있다.



Anti-redistribution


카피본이 재배포되는 것을 막을 수 있다.



Anti-analysis


분석되기 전에 바이너리를 피한다.


Malformed Mach-O binaries

otool, gdb, class-dump 등 많은 리버스 엔지니어링 툴은 Mach-O 파일의 형식(format)의 신뢰 아래 작동하게 된다. 잘못된 Mach-O 파일의 형식인 경우 해당 툴들은 작동하지 않는다. 한편 실행할 수 있도록 만들기 위해선 제한적인 커널의 충돌을 막을 수 없다.

세그먼터 명령어 안에 잘못된 세션값을 세팅하기 위한 한가지 방법이 존재하지만, 바이너리끼리 연결하거나 실행하게 만든다해도 Idid나 dyId 모두 이러한 에러에 복구가 불가능하거나 어렵다.  

하지만 dylib/executable working: Idid -S

But you can do the following to get your dylib/executable working: ldid -S the binary, modify nsects and then recreate the SHA with ldid -s. After that the binary is fully usable on the iDevice.

크래커는 앱의 분석하고 수행방식을 계삭하여 간단하게 고칠 수 있다.


PT_DENY_ATTACH

PT_DENY_ATTACH는 커널 레벨에서 바이너리를 디버깅할때 해당 디버거(GDB, DTrace, etc)들을 방지 할 수있는 애플의 특수한 상수입니다.

ptrace(PT_DENY_ATTACH, 0, 0, 0);

부모 트레킹에게 SEGFAULT를 보낼 것이다. 그럼에도 불구하고, ptrace는 잘 정의된 주소를 가지고 있어서, 간단한 GDB 메크로도 ptrace를 충분히 깰 수있다.

break ptrace
commands 1
   return
   continue
end

다음과 같은 어셈블리와 유사한 코드로 syscall 26이 수행될 때만 ptrace는 사용자공간(userspace) 인터페이스가 커널 안에서 구현된다.

mov r0, #31
mov r1, #0
mov r2, #0
mov r3, #0
mov ip, #26
svc #0x80

PT_DENY_ATTACH가 설치되어도 GDB를 해결할 방법은 없습니다. 크래커는 여전히 SVC 번호 0x80으로 명령어를 NOP하는 패치 기술을 사용할 있지만, 체크섬이 이 방법을 조금이나마 도와줄 수 있다. 또한 바이너리를 thumb에서 컴파일 하지말아라, thumb 모드가 레지스트리의 가용성을 제한하여 컴파일 실패의 원인이 된다.



Obfuscation


Strip symbols

Stripping symbols은 루틴의 목적을 추축하니 어렵게 만든다.


Minimize use of Objective-C

To support the runtime features, Objective-C-based binaries need to retain a lot of class information, which is enough to rebuild the class interface. These information cannot be stripped away. Therefore, all essential stuff should be done using C or C++.


Generate strings dynamically

Even if you have stripped the binary, there is must still be a constant string pool. If you use some visual technique to inform the user they're using a cracked version, the crackers can quickly track down where the view is generated with strings and disable your check.



Legitimacy check


Check if encryption is intact

이 방법은 오직 스토어 앱에 대해서만 의미가 있다. 바이너리가 복호화가 되지 않은경우, LC_ENCRYPTION_INFO 로드 명령어 존재하고 모든 필드가 0이 아니여야한다.

해당 코드는 아래와 같다.

#import <mach-o/dyld.h>

#import <TargetConditionals.h>

 

/* The encryption info struct and constants are missing from the iPhoneSimulator SDK, but not from the iPhoneOS or

 * Mac OS X SDKs. Since one doesn't ever ship a Simulator binary, we'll just provide the definitions here. */

#if TARGET_IPHONE_SIMULATOR && !defined(LC_ENCRYPTION_INFO)

#define LC_ENCRYPTION_INFO 0x21

struct encryption_info_command {

    uint32_t cmd;

    uint32_t cmdsize;

    uint32_t cryptoff;

    uint32_t cryptsize;

    uint32_t cryptid;

};

#endif

  

int main (int argc, char *argv[]);

  

static BOOL is_encrypted () {

    const struct mach_header *header;

    Dl_info dlinfo;

      

    /* Fetch the dlinfo for main() */

    if (dladdr(main, &dlinfo) == 0 || dlinfo.dli_fbase == NULL) {

        NSLog(@"Could not find main() symbol (very odd)");

        return NO;

    }

    header = dlinfo.dli_fbase;

 

    /* Compute the image size and search for a UUID */

    struct load_command *cmd = (struct load_command *) (header+1);

     

    for (uint32_t i = 0; cmd != NULL && i < header->ncmds; i++) {

        /* Encryption info segment */

        if (cmd->cmd == LC_ENCRYPTION_INFO) {

            struct encryption_info_command *crypt_cmd = (struct encryption_info_command *) cmd;

            /* Check if binary encryption is enabled */

            if (crypt_cmd->cryptid < 1) {

                /* Disabled, probably pirated */

                return NO;

            }

             

            /* Probably not pirated? */

            return YES;

        }

         

        cmd = (struct load_command *) ((uint8_t *) cmd + cmd->cmdsize);

    }

     

    /* Encryption info not found */

    return NO;

}

http://landonf.bikemonkey.org/2009/02/index.html


Deprecated or not working methods


Kali Anti-Riracy는 RIP 개발자에 의해 개발된 칼리 불법 복제 방지이다. 첫 발표는 일반 앱 스토어의 크랙 방지 매커니즘을 발표하였다.  이후 RiP-Dev는 닫았다. 칼리는 혀내 폐기 된것으로 간주한다.

http://kaliap.com/

칼리는 3레벨의 방어를 한다

- Anti-debugging

- Anti-dumping

- Integrity check and dynamic code generation


※ 참조 http://iphonedevwiki.net/index.php/Crack_prevention


결론은 크랙은 못막음.