-
[Frida] Uncrackable level 3 풀이Learning/Android 2023. 12. 29. 15:41
레벨 3이다!
이번에도 루팅을 탐지했다!
이번에도 똑같겠거니...하고 앞선 1,2레벨과 같은 코드를 사용해봤다
이번에는 아예 앱이 꺼져버리는 것을 볼 수 있다 다른 검증코드가 있나보다
오... 뭔가.... 많다.... SIGABRT로 종료된 것을 봐서는 비정상 종료가 확실하다
메인액티비티에서 foo 라이브러리를 불러오기 때문에 라이브러리를 봐야겠다.
sub_3080 함수가 정말정말 수상해보인다.
/proc/self/maps 에서 frida나 xposed 문자열이 탐지되면 프로그램을 종료한다!
우린 frida를 사용하니 frida를 탐지하나보다... 음...
그럼 뭐 /proc/self/maps를 열지 않도록 시키면 된다
저기 만만해보이는 status를 대신 열게 하도록 해보자
android native hooking은 이번 문제로 처음 알게되었다...! 뭔가 되게 복잡했다
setImmediate(function() { Java.perform(function() { console.log("[*] Script start"); let AnonymousClass1 = Java.use("sg.vantagepoint.uncrackable3.MainActivity$1"); AnonymousClass1["onClick"].implementation = function (dialogInterface, i) { console.log("exit func in onClick"); }; nativeTrace("fopen"); console.log("[*] Script finish"); }); }); function nativeTrace(nativefunc) { var nativefunc_addr = Module.getExportByName(null, nativefunc) var func = ptr(nativefunc_addr); Interceptor.attach(func, { // set hook onEnter: function (args) { console.log("\n[+] " + nativefunc + " called"); // before call if (nativefunc == "fopen") { if(args[0].readUtf8String() == "/proc/self/maps"){ Memory.protect(args[0], 16, 'rwx'); args[0].writeUtf8String("/proc/self/stat"); } console.log("\nargs[0]: " + args[0].readUtf8String() + ", Type: "); } }, onLeave: function (retval) { if(nativefunc == "fopen"){ console.log(nativefunc + " ret: " + retval.toString() ); // after call } } }); } Interceptor.attach(Module.getExportByName('libfoo.so', 'fopen'), { onEnter: function(argv) { console.log("original = ", argv); args = "/proc/self/status"; }, onLeave: function(retval) { } });
그냥 /proc/self/maps대신 /proc/self/stat을 fopen의 인자로 넘기면 에러가 난다!!!
권한과 관련한 에러라 권한을 세팅해줘야한다.
Memory.protect(arg, 16, rwx)를 통해 권한을 세팅했다.
이제 앱이 강제종료되지 않는 것을 볼 수 있다.
이제 Secret String을 알아내보자
입력값에 대한 검증은 CodeCheck.check_code에서 진행한다
해당 검증 로직도 라이브러리를 봐야할 것 같다
으윽 리버싱...
우선 26번째 줄의 반복문이 검증 로직일 것이다.
dword_601C의 정체부터 살펴보자
메인 액티비티의 init에서도 호출되는 것이 보인다
init에서는 strncpy를 통해 v4의 값을 24글자만큼 복사해주는 것이 보인다.
uncrackable3에서 init 함수는 단 한 번 호출되며 xorkey.getBytes가 인자로 넘어간다
dword_601C는 pizzapizzapizzapizzapizz이다!
이제 Secret String을 구하는 데 필요한 v9를 알아내야 한다. v9는 sub_FA0 함수로 값을 받아오는데,,,,
sub_FA0 함수가 많이 길지만 마지막에 인자로 넘어간 v9에 대한 값 할당이 하나 있다
2313번째 줄부터 3줄인데
xmmword_3480에 대한 값은 이렇게 된다
리틀엔디안으로 바꾼다면,,,
a1에 대한 배열은 [0x1D,0x08,0x11,0x13,0x0F,0x17,0x49,0x15,0x0D,0x00,0x03,0x19,0x5A,0x1D,0x13,0x15,0x08,0x0E,0x5A,0x00,0x17,0x08,0x13,0x14]가 될 것이다
이제 연산만 하면 된다!
우리의 입력값과 a1에 대한 배열과 한글자씩 탐색하며 xor 연산을 해준다
이를 js로 옮기면
function solve() { var key = "pizzapizzapizzapizzapizz"; var a1 = [0x1D,0x08,0x11,0x13,0x0F,0x17,0x49,0x15,0x0D,0x00,0x03,0x19,0x5A, 0x1D,0x13,0x15,0x08,0x0E,0x5A,0x00,0x17,0x08,0x13,0x14]; var str = ""; for(var i=0; i<25; i++) { str += String.fromCharCode(key.charCodeAt(i%24) ^ a1[i]); } console.warn('solve: ' + str); }
답은 making owasp great again 이다!
native 후킹을 알아보는 과정이 좀 빡셌다.
'Learning > Android' 카테고리의 다른 글
[Frida] Uncrackable 4 (r2pay) 풀이2 (0) 2024.03.15 [Frida] Uncrackable 4 (r2pay) 풀이 (1) 2024.01.20 [Frida] Uncrackable level 2 풀이 (0) 2023.12.18 [Frida] Uncrackable level 1 풀이 (0) 2023.12.18 [Frida] FridaLab 1~8 풀이 (1) 2023.12.16