[iOS] How to Detect Non-Encrypted Mach-O

koo00·2024년 1월 17일
0

I. Intro

앱스토어에 업로드된 앱은 모두 암호화되어 있다.
따라서 암호화되지 않은 앱은 애플에 의해 검수되지 않은 앱이라고 볼 수 있고 위험하다고 판단할 수 있다.

구글에 검색하면 iOS 앱 분석을 위해 암호화된 Mach-O 파일을 복호화하는 방법을 많이 찾아볼 수 있으며, 복호화를 위한 자동화 툴도 다양하게 존재한다.

본 포스팅헤서는 암호화된 바이너리를 복호화하는 원리를 통해 암호화되지 않은 Mach-O 파일을 탐지하는 방법에 대해 다루려 한다.
(최대한 간단하게..)


II. Mach-O File Format

먼저 Mach-O 파일의 구조를 확인하지 않을 수 없다.
전체적인 구조는 검색하면 찾아볼 수 있으니 지금은 확인이 필요한 부분에 대해서만 언급하겠다. (wikipedia, mach-o/loader.h)

  • 0x0 ~ 0x4 : Magic number > 32-bit (0xFEEDFACE) or 64-bit (0xFEEDFACF)
  • 0x10 ~ 0x14 : Number of load commands
  • 0x20 ~ : load command's structure

load command 구조체의 시작 0 바이트부터 8 바이트까지는 cmd 와 cmdsize 인자가 반드시 존재한다.
아래에 loader.h 파일에 있는 구조체의 일부를 가져왔으나 추가 확인 필요 시 헤더 파일을 확인해보면 된다.

struct segment_command_64 {
	uint32_t	cmd;			/* LC_SEGMENT_64 */
	uint32_t	cmdsize;		/* includes sizeof section_64 structs */
	char		segname[16];	/* segment name */
	uint64_t	vmaddr;			/* memory address of this segment */
	uint64_t	vmsize;			/* memory size of this segment */
	uint64_t	fileoff;		/* file offset of this segment */
	uint64_t	filesize;		/* amount to map from the file */
	vm_prot_t	maxprot;		/* maximum VM protection */
	vm_prot_t	initprot;		/* initial VM protection */
	uint32_t	nsects;			/* number of sections in segment */
	uint32_t	flags;			/* flags */
};
struct dyld_info_command {
	uint32_t   cmd;				/* LC_DYLD_INFO or LC_DYLD_INFO_ONLY */
	uint32_t   cmdsize;			/* sizeof(struct dyld_info_command) */
    uint32_t   rebase_off;		/* file offset to rebase info */
    uint32_t   rebase_size;		/* size of rebase info */
    uint32_t   bind_off;		/* file offset to binding info */
    uint32_t   bind_size;		/* size of binding info */
    uint32_t   weak_bind_off;	/* file offset to weak binding info */
    uint32_t   weak_bind_size;  /* size of weak binding info */
    uint32_t   lazy_bind_off;	/* file offset to lazy binding info */
    uint32_t   lazy_bind_size;  /* size of lazy binding infs */
    uint32_t   export_off;		/* file offset to lazy binding info */
    uint32_t   export_size;		/* size of lazy binding infs */
};

III. LC_ENCRYPTION_INFO_64

복호화의 핵심은 LC_ENCRYPTION_INFO Command 이며 64비트의 경우 LC_ENCRYPTION_INFO_64 Command 로 존재한다.
해당 구조체 형태와 설명은 아래와 같다. (대부분의 앱은 64비트니까 64비트로 진행)

/*
 * The encryption_info_command_64 contains the file offset and size of an
 * of an encrypted segment (for use in x86_64 targets).
 */
struct encryption_info_command_64 {
   uint32_t	cmd;		/* LC_ENCRYPTION_INFO_64 */
   uint32_t	cmdsize;	/* sizeof(struct encryption_info_command_64) */
   uint32_t	cryptoff;	/* file offset of encrypted range */
   uint32_t	cryptsize;	/* file size of encrypted range */
   uint32_t	cryptid;	/* which enryption system, 0 means not-encrypted yet */
   uint32_t	pad;		/* padding to make this struct's size a multiple of 8 bytes */
};

cryptid 인자가 0 이라면 아직 암호화되지 않았다는 것을 의미한다.


따라서 이 값을 통해 현재 실행 파일의 암호화 유무를 확인할 수 있다.


IV. Conclusion

위 내용을 정리하여 암호화되지 않은 Mach-O 파일을 탐지하는 방법은 아래와 같다.

  1. Mach-O File (or Module) 로드 (아마도 mainBundle)
  2. 불러온 파일이 32-bit or 64-bit 인지 확인
  3. 각각의 아키텍처에 맞는 LC_ENCRYPTION_INFO Command (0x2C) 확인
  4. cryptid 가 1이면 Encrypted Mach-O, 0이면 Non-Encrypted Mach-O
profile
JFDI !

2개의 댓글

comment-user-thumbnail
2024년 1월 27일

연락하고 싶은데 혹시 가능하실까요

1개의 답글