Magisk v27.0 이후로 많은 부분이 업데이트 되었지만 주목할 만한 것은, Zygisk의 Zygote Injection 방식이 달라진 것입니다.
이전 버전에서는 LD_PRELOAD 방식을 이용하여 Injection 하였습니다. 이 방식은 linker 에 침투 흔적을 남겨서 zygisk 활성화 여부를 탐지 할 수 있었습니다.
v27.0 에서는 Zygote Injection 으로 native bridge 방식이 도입되었습니다.
Zygisk 가 없던 시절 Riru (https://github.com/RikkaApps/Riru?tab=readme-ov-file) 가 해당 방식으로 Zygote Injection 을 구현하였는데, Zygisk 도 해당 방식을 차용한 것으로 보입니다.
Native bridge 방식이 어떤 흔적을 남기는지 간략히 확인해보겠습니다.
Magisk 에서 Zygisk 기능을 활성화하고 재부팅을 해보면 "Unsupported native bridge API..." 로그를 볼 수 있습니다.
해당 로그는 안드로이드 소스코드의 native_bridge.cc 에서 찾을 수 있습니다.
소스 코드를 보면 해당 로그를 찍고, callbacks = nullptr; 로 설정합니다.
그 결과 CloseNativeBridge(true); 가 호출됩니다.
bool LoadNativeBridge(const char* nb_library_filename,
const NativeBridgeRuntimeCallbacks* runtime_cbs) {
// We expect only one place that calls LoadNativeBridge: Runtime::Init. At that point we are not
// multi-threaded, so we do not need locking here.
if (state != NativeBridgeState::kNotSetup) {
// Setup has been called before. Ignore this call.
if (nb_library_filename != nullptr) { // Avoids some log-spam for dalvikvm.
ALOGW("Called LoadNativeBridge for an already set up native bridge. State is %s.",
GetNativeBridgeStateString(state));
}
// Note: counts as an error, even though the bridge may be functional.
had_error = true;
return false;
}
if (nb_library_filename == nullptr || *nb_library_filename == 0) {
CloseNativeBridge(false);
return false;
} else {
if (!NativeBridgeNameAcceptable(nb_library_filename)) {
CloseNativeBridge(true);
} else {
// Try to open the library. We assume this library is provided by the
// platform rather than the ART APEX itself, so use the system namespace
// to avoid requiring a static linker config link to it from the
// com_android_art namespace.
void* handle = OpenSystemLibrary(nb_library_filename, RTLD_LAZY);
if (handle != nullptr) {
callbacks = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle,
kNativeBridgeInterfaceSymbol));
if (callbacks != nullptr) {
if (isCompatibleWith(NAMESPACE_VERSION)) {
// Store the handle for later.
native_bridge_handle = handle;
} else {
ALOGW("Unsupported native bridge API in %s (is version %d not compatible with %d)",
nb_library_filename, callbacks->version, NAMESPACE_VERSION);
callbacks = nullptr;
dlclose(handle);
}
} else {
dlclose(handle);
ALOGW("Unsupported native bridge API in %s: %s not found",
nb_library_filename, kNativeBridgeInterfaceSymbol);
}
} else {
ALOGW("Failed to load native bridge implementation: %s", dlerror());
}
// Two failure conditions: could not find library (dlopen failed), or could not find native
// bridge interface (dlsym failed). Both are an error and close the native bridge.
if (callbacks == nullptr) {
CloseNativeBridge(true);
} else {
runtime_callbacks = runtime_cbs;
state = NativeBridgeState::kOpened;
}
}
return state == NativeBridgeState::kOpened;
}
}
CloseNativeBridge 함수의 소스코드는 다음과 같은데, CloseNativeBridge(true); 호출 결과 had_error 값이 true 로 설정되는 것을 확인할 수 있습니다.
static void CloseNativeBridge(bool with_error) {
state = NativeBridgeState::kClosed;
had_error |= with_error;
ReleaseAppCodeCacheDir();
}
그렇다면 had_error 가 메모리의 어디에 위치해 있나가 궁금한데, 그것은 libnativebridge.so 파일을 IDA 로 까보면 알 수 있습니다. 음 소스코드와 IDA 를 비교해보니 had_error 변수가 byte_5288 에 있군요.
byte_5288 은 .bss segment 의 시작부분입니다.
이는 앱 프로세스에서도 확인 가능합니다. Zygisk 활성화 후 아무앱이나 실행시키고 Frida 로 libnativebridge.so 의 오프셋 0x5288 로 이동해봅니다. 0x1 이 설정되어 있습니다.
Zygisk 가 비활성화된 상태에서는 had_error 값이 false 이므로 앱 프로세스에서 확인하면 0x0 이 설정되어 있습니다.
헛?! 그렇다면 이것으로 Zygisk 를 탐지할 수 있는것인가!! 라는 생각이 들 수 있지만, 매지스크 개발자 topjohnwoo 도 알고 있겠죠. 아마 이정도 사소한 것은 모듈단에서 알아서 처리하라고 내비두지 않았을까 생각됩니다.
Shamiko (https://github.com/LSPosed/LSPosed.github.io/releases) 나 Zygisk-Assistant (https://github.com/snake-4/Zygisk-Assistant/releases) 모듈을 설치하면 해당 값을 0x0 으로 패치해줍니다. 따라서 유의미한 탐지 방식이 될 수는 없겠죠.
음 여기까지 봤을때 제 짧은 식견으로는 Zygisk 자체를 어떻게 탐지해야 할지 아이디어가 떠오르지 않더군요.
이미 뛰어난 해커, 솔루션 개발자들은 탐지하고 있겠죠?? 저도 더욱 분발해야 겠네요 :)
'Information Security > Android' 카테고리의 다른 글
Android SO File Dump (0) | 2024.08.31 |
---|---|
Frida Build & Debug Using Logs For Android (0) | 2024.08.31 |
Proxy Android Flutter Apps (4) | 2024.05.19 |
버sucker 키우기 (0) | 2024.01.27 |
Frida-portal 사용법 (0) | 2023.12.01 |