ν‹°μŠ€ν† λ¦¬ λ·°

MOBILE

[iOS] Functions Hooking

🌧: 2021. 9. 24.

What Functions?

iOS 앱을 μ§„λ‹¨ν•˜λ‹€ 보면 νƒˆμ˜₯을 νƒμ§€ν•˜λŠ” νŠΉμ • 버전이 μžˆμ„ μˆ˜λ„ 있고 μ–΄λ–€ ν•¨μˆ˜λ₯Ό 톡해 λ””λ°”μ΄μŠ€ λ‚΄λΆ€λ₯Ό κ²€μ‚¬ν•˜λŠ”μ§€ μ‘°κΈˆμ”© λ‹€λ¦…λ‹ˆλ‹€. κ·Έμ€‘μ—μ„œ λŒ€ν‘œμ μœΌλ‘œ NSFileManager 클래슀의 "fileExistsAtpath:"λ₯Ό 톡해 ν˜„μž¬ μ‹€ν–‰λœ λ””λ°”μ΄μŠ€ 내뢀에 νƒˆμ˜₯κ³Ό κ΄€λ ¨λœ 디렉토리 λ˜λŠ” 파일이 μ‘΄μž¬ν•˜λŠ”μ§€ 체크 ν›„ Boolean λ°˜ν™˜μ„ μ§„ν–‰ν•˜λŠ” 방법과 UIApplication 클래슀의 "canOpenURL:" ν•¨μˆ˜λ₯Ό 톡해 νƒˆμ˜₯κ³Ό κ΄€λ ¨λœ 앱이 μ‘΄μž¬ν•˜λŠ”μ§€ URL Scheme ν˜ΈμΆœν•˜λŠ” 2가지 방식이 μ‘΄μž¬ν•©λ‹ˆλ‹€.

 

Example μ½”λ“œ

NSString *filePath = @"/Applications/Cydia.app";
	if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
	{
		//νƒˆμ˜₯된 λ‹¨λ§μž…λ‹ˆλ‹€.
	}
  if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"cydia://package/com.example.package"]]){
      //νƒˆμ˜₯된 λ‹¨λ§μž…λ‹ˆλ‹€.
    }

μ˜ˆμ‹œ μ½”λ“œλ₯Ό 보면 각각의 ν•¨μˆ˜μ˜ νŠΉμ„±μ„ μ΄μš©ν•΄ 정상적인 단말기인지 검증을 ν•˜κ²Œ λ©λ‹ˆλ‹€. νŒ¨ν„΄μ˜ ν‚€μ›Œλ“œλŠ” λŒ€ν‘œμ μœΌλ‘œ cydia, chimera, frida, cycript, ssh, su λ“± 내뢀에 μ‘΄μž¬ν•˜λŠ” μ•±, 파일, 디렉토리 유무λ₯Ό 톡해 νŒλ³„ν•˜κ²Œ λ©λ‹ˆλ‹€.

 

Objection을 톡해 ν…ŒμŠ€νŠΈν•˜κ³ μž ν•˜λŠ” 앱에 spawnν•˜κ³  νƒˆμ˜₯ 우회λ₯Ό μ‹€ν–‰μ‹œν‚€λ©΄ μœ„μ™€ 같이 κ²€μ¦ν•˜κ³  μžˆλŠ” νŒ¨ν„΄μ˜ 경둜λ₯Ό 보여주고 λ°˜ν™˜λœ Boolean 값을 리턴 μ‹œμΌœ μ€λ‹ˆλ‹€. ν•˜μ§€λ§Œ λŒ€λΆ€λΆ„μ˜ μ•±μ˜ 경우 Objectionμ—μ„œ μš°νšŒμ‹œμΌœμ£ΌλŠ” λΈ”λž™λ¦¬μŠ€νŠΈμ˜ 경둜 이외에도 λ‹€μ–‘ν•œ 탐지 체크λ₯Ό ν•˜κ³  μžˆμ–΄ μš°νšŒκ°€ λΆˆκ°€λŠ₯ν–ˆμŠ΅λ‹ˆλ‹€.

 

NSA에 λ°°ν¬ν•œ κΈ°λ“œλΌλ₯Ό 톡해 λŒ€μƒ λ°”μ΄λ„ˆλ¦¬ νŒŒμΌμ„ λΆ„μ„ν•˜μ—¬ λˆ„λ½λœ 체크 κ²½λ‘œκ°€ μžˆλŠ”μ§€ 확인해봐야 λ©λ‹ˆλ‹€. 얼핏 보기만 해도 λ‹€μˆ˜μ˜ νƒˆμ˜₯검증을 ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. 

 

λ””μ»΄νŒŒμΌλœ μ†ŒμŠ€λ‘œμ§μ„ 보면 UIApplication 클래슀λ₯Ό 톡해 canOpenURL ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜κ³  μžˆμ§€λ§Œ cydia://package. 앱이 μ •ν™•ν•˜κ²Œ λͺ…μ‹œλ˜μ–΄ μžˆμ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 보톡 Array ν˜•μ‹μœΌλ‘œ νƒμ§€ν•˜κ³ μž ν•˜λŠ” 리슀트λ₯Ό ν˜ΈμΆœν•˜κΈ°λ„ ν•˜μ§€λ§Œ μ°Έμ‘° ν˜•μ‹μœΌλ‘œ ν‚€μ›Œλ“œλ₯Ό ν˜ΈμΆœν•˜λŠ” κ²½μš°λ„ μžˆμŠ΅λ‹ˆλ‹€.

 

두 번째 λ””μ»΄νŒŒμΌ λΆ„μ„μ—μ„œλŠ” NSFileManager 클래슀의 fileExistsAtPath ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜κ³  μžˆμ§€λ§Œ λ§ˆμ°¬κ°€μ§€λ‘œ μ†ŒμŠ€ 내뢀에 직접 λͺ…μ‹œλœ ν‚€μ›Œλ“œλŠ” λ‚˜μ˜€μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— KeyWord Search -> μœ„μ•„λž˜λ‘œ μ°Έμ‘°ν•˜κ³  μžˆλŠ” ν‚€μ›Œλ“œ 경둜 μˆ˜μ§‘ ν˜•νƒœλ‘œ 좔츑을 ν•΄μ•Ό λ©λ‹ˆλ‹€.

 

파일 경둜 기반 νƒˆμ˜₯ μ—¬λΆ€λ₯Ό μ²΄ν¬ν•˜λŠ” νŒ¨ν„΄μ€ 크게 /Applications/, /private/, /usr/sbin/, /usr/bin/, /System/Library/LaunchDaemons/, /etc/, /bin/λ“±μœΌλ‘œ λ‚˜λ‰©λ‹ˆλ‹€.

 

/private/λ””λ ‰ν† λ¦¬μ—μ„œλ„ λ§ˆμ°¬κ°€μ§€λ‘œ μƒŒλ“œλ°•μŠ€ μ˜μ—­μ˜ μ œν•œμ΄ ν’€λ¦° 경둜둜 λ„˜μ–΄κ°€ ν•΄λ‹Ή 파일이 μ‘΄μž¬ν•˜λŠ”μ§€ μ²΄ν¬ν•©λ‹ˆλ‹€. λ˜ν•œ JailbreakTest.txtκ°€ ν™•μΈλ˜λŠ”λ° 이것은 ν•΄λ‹Ή κ²½λ‘œμ— root κΆŒν•œμ„ ν†΅ν•œ 파일 생성을 μ‹œλ„ν•˜μ—¬ μžμ—°μŠ€λ ˆ λ””λ°”μ΄μŠ€μ˜ κΆŒν•œμ΄ μ–΄λ–»κ²Œ λ˜μ–΄ μžˆλŠ”μ§€λ„ μ²΄ν¬ν•˜λŠ” 것이라 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€. 정상적인 단말이라면 /private/κ²½λ‘œμ— μž„μ˜ νŒŒμΌμ„ μƒμ„±ν•œλ‹€λŠ” 것은 λΆˆκ°€λŠ₯ν•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

 

μ΄λŸ¬ν•œ λͺ¨λ“  디렉토리λ₯Ό λͺ¨λ‘ κ²€μƒ‰ν•˜μ—¬ μˆ˜μ§‘ν•˜λŠ” 방법은 μ‹œκ°„μ΄ 였래 μ†Œμš”λ˜λ―€λ‘œ 퍼징 리슀트λ₯Ό λ§Œλ“€μ–΄ 일괄 μ²΄ν¬ν•˜λŠ” 것이 훨씬 μ •ν™•ν•˜κ³  λΉ λ¦…λ‹ˆλ‹€.

 

var fileExistsAtPath = ObjC.classes.NSFileManager["- fileExistsAtPath:"];
var hideFile = 0;
var paths = [
	"/Applications/blackra1n.app",
    "/Applications/crackerxi.app",
    "/Applications/Cydia.app",
    "/Applications/FakeCarrier.app",
    "/Applications/Icy.app",
    "/Applications/IntelliScreen.app",
    "/Applications/MxTube.app",
    "/Applications/RockApp.app",
    "/Applications/SBSettings.app",
    "/Applications/WinterBoard.app",
    "/bin/bash",
    "/bin/sh",
    "/bin/su",
    "/etc/alternatives/sh",
    "/etc/apt/",
    "/etc/apt",
    "/etc/apt/sources.list.d/cydia.list",
    "/etc/apt/sources.list.d/electra.list",
    "/etc/apt/sources.list.d/sileo.sourcs",
    "/etc/apt/undecimus/undecimus.list",
    "/etc/ssh/sshd_config",
    "/jb/amfid_payload.dylib",
    "/jb/jailbreakd.plist",
    "/jb/libjailbreak.dylib",
    "/jb/lzma",
    "/jb/offsets.plists",
    "/Library/MobileSubstrate/CydiaSubstrate.dylib",
    "/Library/MobileSubstrate/DynamicLibraries/*",
    "/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist",
    "/Library/MobileSubstrate/DynamicLibraries/Veency.plist",
    "/Library/MobileSubstrate/MobileSubstrate.dylib",
    "/pguntether",
    "/private/var/cache/apt",
    "/private/var/lib/apt",
    "/private/var/lib/cydia",
    "/private/var/log/syslog",
    "/private/var/mobile/Library/SBSettings/Themes",
    "/private/var/stash",
    "/private/var/tmp/cydia.log",
    "/private/var/tmp/frida-*.dylib",
    "/private/var/Users",
    "/System/Library/LaunchDaemons/com.ikey.bbot.plist",
    "/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist",
    "/usr/bin/cycript",
    "/usr/bin/ssh",
    "/usr/bin/sshd",
    "/usr/lib/libjailbreak.dylib",
    "/usr/libexec/cydia/firmware.sh",
    "/usr/libexec/sftp-server",
    "/usr/libexec/sshd-keygen-wrapper",
    "/usr/libexec/ssh-keysign",
    "/usr/sbin/frida-server",
    "/usr/sbin/sshd",
    "/usr/share/jailbreak/injectme.plist",
    "/var/cache/apt",
    "/var/lib/apt",
    "/var/lib/cydia",
    "/var/lib/dpkg/info/mobilesubstrate.dylib",
    "/var/log/apt",
    "/var/mobile/Library/Caches/com.saurik.Cydia/sources.list",
    "/var/mobile/Media/.evasi0n7_installed",
    "/var/tmp/cydia.log",
    "/.bootstrapped_electra",
    "/.cydia_no_stash",
    "/.installed_unc0ver"
	];

Interceptor.attach(fileExistsAtPath.implementation, {
    onEnter: function(args) {
	var path = ObjC.Object(args[2]);

	if (paths.indexOf(path.toString()) > -1) {
	    console.log("Found Jailbreak Check: " + path.toString());
	    hideFile = 1;
	}},

    onLeave: function(retval) {
	if (hideFile) {
	    console.log("Return Value!!(0x0)");
	    retval.replace(0);
	    hideFile = 0;
	}}
});

κ°„λ‹¨ν•˜κ²Œ μ½”λ“œλ₯Ό μ„€λͺ…ν•˜μžλ©΄pathsλΌλŠ” λ³€μˆ˜μ— κ΅¬κΈ€μ—μ„œ μˆ˜μ§‘ν•œ νƒˆμ˜₯ 체크 경둜λ₯Ό μΆ”κ°€ ν›„ Interceptorλ₯Ό 톡해 ꡬ동 쀑인 ν”„λ‘œμ„ΈμŠ€μ— 후킹을 μ‹œμž‘ν•©λ‹ˆλ‹€.

후킹이 μ‹œμž‘λ˜λ©΄ indexOf ν•¨μˆ˜λ₯Ό 톡해 paths에 μž…λ ₯ν•΄λ‘” λ¬Έμžμ—΄μ„ μ°ΎκΈ° μ‹œμž‘ν•˜λŠ”λ°, λ§Œμ•½ μ°ΎλŠ” λ¬Έμžμ—΄μ΄ μžˆκ±°λ‚˜ ν¬ν•¨λ˜κΈ°λΌλ„ ν–ˆμ„ 경우 0 λ˜λŠ” 1을 좜λ ₯ν•˜μ—¬ Console에 찍히게 ν•˜λŠ” λ™μ‹œμ— Valueλ₯Ό Return μ‹œμΌœ 파일 μ‹œμŠ€ν…œ 경둜λ₯Ό 체크λ₯Ό μš°νšŒμ‹œμΌœμ€λ‹ˆλ‹€.

 

μœ„μ˜ μ½”λ“œλ₯Ό 톡해 앱을 spawn ν•˜μ—¬ 경둜 체크λ₯Ό 진행해보면 μ‹€μ‹œκ°„μœΌλ‘œ 검사할 수 μžˆλ‹€. λ§Œμ•½ μœ„μ˜ μ½”λ“œλ₯Ό 톡해 체크 및 우회λ₯Ό 진행해도 탐지에 κ±Έλ¦°λ‹€λ©΄ canOpenURL 체크 λ˜λŠ” νƒμ§€ν•˜λŠ” μƒˆλ‘œμš΄ κ²½λ‘œκ°€ μ‘΄μž¬ν•œλ‹€λŠ” 것을 μ˜μ‹¬ν•΄μ•Όλ©λ‹ˆλ‹€.

 

νƒμ§€ν•˜κ³  μžˆλŠ” ν‚€μ›Œλ“œλ₯Ό paths λ³€μˆ˜μ— λ„£μ–΄μ£Όκ³  ν•΄λ‹Ή 경둜λ₯Ό 체크 μ‹œ Return Value μ‹œμΌœμ£ΌλŠ” κ³Ό λ™μ‹œμ— canOpnURL ν•¨μˆ˜μ— λ‹΄κΈ΄ cydia ν˜ΈμΆœκΉŒμ§€ Return Value ν•˜μ—¬ μ΅œμ’…μ μœΌλ‘œ νƒˆμ˜₯ 검증을 ν”Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

var paths = [
     	"/Applications/Cydia.app",
     	"/Applications/blackra1n.app",
     	"/Applications/FakeCarrier.app",
     	"/Applications/Icy.app",
	"/Applications/IntelliScreen.app",
	"/Applications/MxTube.app",
	"/Applications/RockApp.app",
	"/Applications/SBSettings.app",
	"/Applications/WinterBoard.app",
	"/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist",
	"/Library/MobileSubstrate/DynamicLibraries/Veency.plist",
	"/private/var/lib/apt",
	"/private/var/lib/apt/",
	"/private/var/lib/cydia",
	"/private/var/mobile/Library/SBSettings/Themes",
	"/private/var/stash",
	"/private/var/tmp/cydia.log",
	"/System/Library/LaunchDaemons/com.ikey.bbot.plist",
	"/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist",
	"/usr/bin/sshd",
	"/usr/libexec/sftp-server",
	"/usr/sbin/sshd",
	"/etc/apt",
	"/bin/bash",
	"/Library/MobileSubstrate/MobileSubstrate.dylib"
];

try {
    var resolver = new ApiResolver('objc');

    resolver.enumerateMatches('*[* fileExistsAtPath*]', {
        onMatch: function(match) {
            var ptr = match["address"];
            Interceptor.attach(ptr, {
                onEnter: function(args) {
                    var path = ObjC.Object(args[2]).toString();
                    this.jailbreakCall = false;
                    for (var i = 0; i < paths.length; i++) {
                        if (paths[i] == path) {
                            this.jailbreakCall = true;
                        }
                    }
                },
                onLeave: function(retval) {
                    if (this.jailbreakCall) {
                        retval.replace(0x0);
                    }
                }
            });
        },
        onComplete: function() {}
    });

    resolver.enumerateMatches('*[* canOpenURL*]', {
        onMatch: function(match) {
            var ptr = match["address"];
            Interceptor.attach(ptr, {
                onEnter: function(args) {
                    var url = ObjC.Object(args[2]).toString();
                    this.jailbreakCall = false;
                    if (url.indexOf("cydia") >= 0) {
                        this.jailbreakCall = true;
                    }
                },
                onLeave: function(retval) {
                    if (this.jailbreakCall) {
                        retval.replace(0x0);
                    }
                }
            });
        },
        onComplete: function() {}
    });

    var response = {
        type: 'sucess',
        data: {
            message: "[!] Jailbreak Bypass success"
        }
    };
    send(response);
} catch (e) {
    var message = {
        type: 'exception',
        data: {
            message: '[!] Jailbreak bypass script error: '
        }
    };
    send(message);
}

μ΅œμ’… ν›„ν‚Ή μ½”λ“œλ₯Ό 보면. 첫번째둜 μ²΄ν¬ν•˜λŠ” 파일 경둜의 λ¬Έμžμ—΄μ„ pathsλ³€μˆ˜μ— λͺ¨λ‘ μΆ”κ°€ν•œλ‹€. 참고둜 indexOf 없이 경둜 체크λ₯Ό ν•  경우 λ¬Έμžμ—΄μ€ λŒ€μ†Œλ¬Έμžλ₯Ό κ΅¬λΆ„ν•˜λ©° “/” κΈ°ν˜Έλ„ μ •ν™•ν•˜κ²Œ κΈ°μž…ν•΄μ•Ό λ©λ‹ˆλ‹€.

 

ν•΄λ‹Ή 앱은 파일 경둜 체크 이외에 canOpenURL λ©”μ†Œλ“œ ν•¨μˆ˜λ₯Ό 톡해 URL Scheme을 μ‹€ν–‰ν•˜μ—¬ μ‹œλ””μ•„ νŠΈμœ…μ„ ν˜ΈμΆœν•˜λŠ” 뢀뢄도 μ‘΄μž¬ν–ˆμŠ΅λ‹ˆλ‹€. 이뢀뢄은 enumerateMatche(query, callbacks) ν•¨μˆ˜λ₯Ό 톡해 μ§€μ •λœ ν•¨μˆ˜λ₯Ό λ¨Όμ € ν˜ΈμΆœμ‹œμΌœ “cydia”λΌλŠ” λ¬Έμžμ—΄μ΄ μ‘΄μž¬ν•˜κ±°λ‚˜ ν¬ν•¨λ˜μ–΄ μžˆμ„ 경우 0 λ˜λŠ” 1을 좜λ ₯μ‹œν‚€λ©° κ·Έ 이후 retval둜 값을 λ³€μ‘°(true -> false(0x0))ν•˜μ—¬ μš°νšŒν•©λ‹ˆλ‹€.

 

frida -U -f "Identifier" -l jailbreak.js --no-pause

프리닀λ₯Ό 톡해 앱을 ν˜ΈμΆœν•˜λŠ” 방법은 2가지 μ‘΄μž¬ν•©λ‹ˆλ‹€. 첫 번째둜 App Name을 톡해 ν˜ΈμΆœν•˜μ—¬ μ½”λ“œλ₯Ό ν›„ν‚Ή ν•˜κΈ° μœ„ν•΄μ„œλŠ” νƒˆμ˜₯을 νƒμ§€ν•˜κΈ° 이전에 곡백 타이밍이 ν•„μš”ν•©λ‹ˆλ‹€(ex: κ΄‘κ³ μ„± μ—¬λΆ€, μ•Œλ¦Ό μ—¬λΆ€, κΆŒν•œ λ™μ˜ λ“±) 졜초 μ•± μ‹€ν–‰ μ‹œ 곡백 타이밍이 μ‘΄μž¬ν•œλ‹€λ©΄ ν•΄λ‹Ή μ‹œμ μ— μ½”λ“œλ₯Ό μ‹€ν–‰μ‹œν‚€λ©΄ λ˜μ§€λ§Œ 졜초 μ‹€ν–‰ν•˜μžλ§ˆμž λ°”λ‘œ 체크 ν›„ μ’…λ£Œμ‹œν‚€λ©΄ "-f" μ˜΅μ…˜μ„ μ΄μš©ν•˜μ—¬ 앱을 μ‹€ν–‰ν•˜μžλ§ˆμž λ°”λ‘œ μ½”λ“œ 싀행을 ν•  수 μžˆλ„λ‘ ν•΄μ•Ό λ©λ‹ˆλ‹€.

 

νƒˆμ˜₯ 탐지λ₯Ό μ™„λ²½ν•˜κ²Œ μˆ˜ν–‰ν•  수 μžˆλŠ” 방법은 ꡉμž₯히 νž˜λ“€λ‹€κ³  μƒκ°λ©λ‹ˆλ‹€. 아무리 λ‹€μˆ˜μ˜ 체크λ₯Ό μ§„ν–‰ν•œλ‹€ ν•˜λ”λΌλ„ μˆ™λ ¨λœ ν•΄μ»€λŠ” λ””μ»΄νŒŒμΌλœ μ†ŒμŠ€λ₯Ό 톡해 정적 뢄석이 κ°€λŠ₯ν•˜λ―€λ‘œ λ‚œλ…ν™” 뢀뢄도 μ€‘μš”ν•œ μ˜ˆλ°©λ²•μ— μ†ν•©λ‹ˆλ‹€. λ˜ν•œ 좔츑이 κ°€λŠ₯ν•œ λ©”μ†Œλ“œ isJailbroken 같은 ν‚€μ›Œλ“œλŠ” λ˜λ„λ‘ μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€. μ™œλƒν•˜λ©΄ Objectionμ΄λ‚˜ cycript 같은 κΈ°λŠ₯듀이 ν•¨μˆ˜ Tracing에 μ΅œμ ν™”λ˜μ–΄μžˆκΈ° λ•Œλ¬Έμ— μ†μ‰½κ²Œ λ³€μ‘°κ°€ κ°€λŠ₯ν•΄μ§€λ―€λ‘œ μ™„λ²½ν•  순 없어도 ν˜Όλž€μŠ€λŸ½κ²Œ κ΅¬ν˜„ν•΄λ‘λŠ” 것이 μ˜¬λ°”λ₯Έ λ°©ν–₯이라고 λ³Ό 수 μžˆκ² μŠ΅λ‹ˆλ‹€.

κ³΅μœ ν•˜κΈ° 링크
Comment