Improvements

- Better logic in prop hook.
- Extract common fields from fingerprint.
- Other changes.
This commit is contained in:
chiteroman 2024-10-23 13:06:15 +02:00
parent 51350088ae
commit ac9d4d385c
3 changed files with 99 additions and 35 deletions

View File

@ -53,9 +53,15 @@ static void modify_callback(void *cookie, const char *name, const char *value, u
if (!cookie || !name || !value || !o_callback) return; if (!cookie || !name || !value || !o_callback) return;
const char *oldValue = value;
std::string_view prop(name); std::string_view prop(name);
if (prop.ends_with("api_level")) { if (prop == "init.svc.adbd") {
value = "stopped";
} else if (prop == "sys.usb.state") {
value = "mtp";
} else if (prop.ends_with("api_level")) {
if (!DEVICE_INITIAL_SDK_INT.empty()) { if (!DEVICE_INITIAL_SDK_INT.empty()) {
value = DEVICE_INITIAL_SDK_INT.c_str(); value = DEVICE_INITIAL_SDK_INT.c_str();
} }
@ -69,7 +75,11 @@ static void modify_callback(void *cookie, const char *name, const char *value, u
} }
} }
if (DEBUG) LOGD("[%s]: '%s'", name, value); if (strcmp(oldValue, value) == 0) {
if (DEBUG) LOGD("[%s]: %s (unchanged)", name, oldValue);
} else {
LOGD("[%s]: %s -> %s", name, oldValue, value);
}
return o_callback(cookie, name, value, serial); return o_callback(cookie, name, value, serial);
} }
@ -82,12 +92,10 @@ static void my_system_property_read_callback(prop_info *pi, T_Callback callback,
} }
static bool doHook() { static bool doHook() {
LOGD("loaded Dobby version: %s", DobbyGetVersion());
void *ptr = DobbySymbolResolver(nullptr, "__system_property_read_callback"); void *ptr = DobbySymbolResolver(nullptr, "__system_property_read_callback");
if (ptr && !DobbyHook(ptr, (void *) my_system_property_read_callback, if (ptr && DobbyHook(ptr, (void *) my_system_property_read_callback,
(void **) &o_system_property_read_callback)) { (void **) &o_system_property_read_callback) == 0) {
LOGD("hook __system_property_read_callback successful at %p", ptr); LOGD("hook __system_property_read_callback successful at %p", ptr);
return true; return true;
} }
@ -166,6 +174,9 @@ public:
bool trickyStore = false; bool trickyStore = false;
xread(fd, &trickyStore, sizeof(trickyStore)); xread(fd, &trickyStore, sizeof(trickyStore));
bool testSignedRom = false;
xread(fd, &testSignedRom, sizeof(testSignedRom));
close(fd); close(fd);
LOGD("Dex file size: %d", dexSize); LOGD("Dex file size: %d", dexSize);
@ -178,6 +189,11 @@ public:
spoofProvider = false; spoofProvider = false;
spoofProps = false; spoofProps = false;
} }
if (testSignedRom) {
LOGD("--- ROM IS SIGNED WITH TEST KEYS ---");
spoofSignature = true;
}
} }
void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override { void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override {
@ -213,8 +229,8 @@ private:
JNIEnv *env = nullptr; JNIEnv *env = nullptr;
std::vector<uint8_t> dexVector; std::vector<uint8_t> dexVector;
nlohmann::json json; nlohmann::json json;
bool spoofProps = false; bool spoofProps = true;
bool spoofProvider = false; bool spoofProvider = true;
bool spoofSignature = false; bool spoofSignature = false;
void dlclose() { void dlclose() {
@ -225,14 +241,6 @@ private:
void parseJSON() { void parseJSON() {
if (json.empty()) return; if (json.empty()) return;
if (json.contains("ID") && json["ID"].is_string()) {
BUILD_ID = json["ID"].get<std::string>();
}
if (json.contains("SECURITY_PATCH") && json["SECURITY_PATCH"].is_string()) {
SECURITY_PATCH = json["SECURITY_PATCH"].get<std::string>();
}
if (json.contains("DEVICE_INITIAL_SDK_INT")) { if (json.contains("DEVICE_INITIAL_SDK_INT")) {
if (json["DEVICE_INITIAL_SDK_INT"].is_string()) { if (json["DEVICE_INITIAL_SDK_INT"].is_string()) {
DEVICE_INITIAL_SDK_INT = json["DEVICE_INITIAL_SDK_INT"].get<std::string>(); DEVICE_INITIAL_SDK_INT = json["DEVICE_INITIAL_SDK_INT"].get<std::string>();
@ -246,18 +254,57 @@ private:
if (json.contains("spoofProvider") && json["spoofProvider"].is_boolean()) { if (json.contains("spoofProvider") && json["spoofProvider"].is_boolean()) {
spoofProvider = json["spoofProvider"].get<bool>(); spoofProvider = json["spoofProvider"].get<bool>();
json.erase("spoofProvider");
} }
if (json.contains("spoofProps") && json["spoofProps"].is_boolean()) { if (json.contains("spoofProps") && json["spoofProps"].is_boolean()) {
spoofProps = json["spoofProps"].get<bool>(); spoofProps = json["spoofProps"].get<bool>();
json.erase("spoofProps");
} }
if (json.contains("spoofSignature") && json["spoofSignature"].is_boolean()) { if (json.contains("spoofSignature") && json["spoofSignature"].is_boolean()) {
spoofSignature = json["spoofSignature"].get<bool>(); spoofSignature = json["spoofSignature"].get<bool>();
json.erase("spoofSignature");
} }
if (json.contains("DEBUG") && json["DEBUG"].is_boolean()) { if (json.contains("DEBUG") && json["DEBUG"].is_boolean()) {
DEBUG = json["DEBUG"].get<bool>(); DEBUG = json["DEBUG"].get<bool>();
json.erase("DEBUG");
}
if (json.contains("FINGERPRINT") && json["FINGERPRINT"].is_string()) {
std::string fingerprint = json["FINGERPRINT"].get<std::string>();
std::vector<std::string> vector;
auto parts = fingerprint | std::views::split('/');
for (const auto &part: parts) {
auto subParts = std::string(part.begin(), part.end()) | std::views::split(':');
for (const auto &subPart: subParts) {
vector.emplace_back(subPart.begin(), subPart.end());
}
}
if (vector.size() == 8) {
json["BRAND"] = vector[0];
json["PRODUCT"] = vector[1];
json["DEVICE"] = vector[2];
json["RELEASE"] = vector[3];
json["ID"] = vector[4];
json["INCREMENTAL"] = vector[5];
json["TYPE"] = vector[6];
json["TAGS"] = vector[7];
} else {
LOGE("Error parsing fingerprint values!");
}
}
if (json.contains("SECURITY_PATCH") && json["SECURITY_PATCH"].is_string()) {
SECURITY_PATCH = json["SECURITY_PATCH"].get<std::string>();
}
if (json.contains("ID") && json["ID"].is_string()) {
BUILD_ID = json["ID"].get<std::string>();
} }
} }
@ -378,6 +425,26 @@ static std::vector<uint8_t> readFile(const char *path) {
return vector; return vector;
} }
static bool checkOtaZip() {
std::array<char, 128> buffer{};
std::string result;
bool found = false;
std::unique_ptr<FILE, decltype(&pclose)> pipe(
popen("unzip -l /system/etc/security/otacerts.zip", "r"), pclose);
if (!pipe) return false;
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
if (result.find("test") != std::string::npos) {
found = true;
break;
}
}
return found;
}
static void companion(int fd) { static void companion(int fd) {
std::vector<uint8_t> dex, json; std::vector<uint8_t> dex, json;
@ -403,6 +470,9 @@ static void companion(int fd) {
bool trickyStore = std::filesystem::exists(TS_PATH); bool trickyStore = std::filesystem::exists(TS_PATH);
xwrite(fd, &trickyStore, sizeof(trickyStore)); xwrite(fd, &trickyStore, sizeof(trickyStore));
bool testSignedRom = checkOtaZip();
xwrite(fd, &testSignedRom, sizeof(testSignedRom));
} }
REGISTER_ZYGISK_MODULE(PlayIntegrityFix) REGISTER_ZYGISK_MODULE(PlayIntegrityFix)

View File

@ -181,15 +181,16 @@ public final class EntryPoint {
jsonObject.keys().forEachRemaining(key -> { jsonObject.keys().forEachRemaining(key -> {
Field field = getBuildField(key); Field field = getBuildField(key);
if (field == null) return; if (field == null) return;
field.setAccessible(true);
String value;
try { try {
value = jsonObject.getString(key); String value = jsonObject.getString(key);
if (value.isBlank()) {
Log.w(TAG, "Field '" + key + "' have an empty value!");
} else {
map.put(field, value);
}
} catch (Throwable t) { } catch (Throwable t) {
Log.e(TAG, "init", t); Log.e(TAG, "init", t);
return;
} }
map.putIfAbsent(field, value);
}); });
Log.i(TAG, "Parsed " + map.size() + " fields from JSON"); Log.i(TAG, "Parsed " + map.size() + " fields from JSON");
@ -200,9 +201,14 @@ public final class EntryPoint {
public static void spoofFields() { public static void spoofFields() {
map.forEach((field, value) -> { map.forEach((field, value) -> {
try { try {
field.setAccessible(true);
String oldValue = (String) field.get(null); String oldValue = (String) field.get(null);
if (value.equals(oldValue)) return; if (value.equals(oldValue)) {
field.setAccessible(false);
return;
}
field.set(null, value); field.set(null, value);
field.setAccessible(false);
Log.i(TAG, "Set '" + field.getName() + "' to '" + value + "'"); Log.i(TAG, "Set '" + field.getName() + "' to '" + value + "'");
} catch (Throwable t) { } catch (Throwable t) {
Log.e(TAG, "spoofFields", t); Log.e(TAG, "spoofFields", t);

View File

@ -1,19 +1,7 @@
{ {
"TYPE": "user",
"TAGS": "release-keys",
"ID": "AP41.240823.009",
"BRAND": "google",
"DEVICE": "tokay",
"FINGERPRINT": "google/tokay_beta/tokay:15/AP41.240823.009/12329489:user/release-keys", "FINGERPRINT": "google/tokay_beta/tokay:15/AP41.240823.009/12329489:user/release-keys",
"MANUFACTURER": "Google", "MANUFACTURER": "Google",
"MODEL": "Pixel 9", "MODEL": "Pixel 9",
"PRODUCT": "tokay_beta",
"INCREMENTAL": "12329489",
"RELEASE": "15",
"SECURITY_PATCH": "2024-09-05", "SECURITY_PATCH": "2024-09-05",
"DEVICE_INITIAL_SDK_INT": 25, "DEVICE_INITIAL_SDK_INT": 21
"spoofProvider": true,
"spoofProps": true,
"spoofSignature": false,
"DEBUG": false
} }