Primer paso de la investigacion. Se aportan el .apk, las carpetas con el apk extraido y el apk descompilado. El archivo API_DOCUMENTATION.md es un archivo donde se anotaran los descubrimientos del funcionamiento de la API, y los .py son scripts para probar la funcionalidad de la API con los métodos que vayamos encontrando. Finalmente, los archivos .js son scripts de Frida para extraer informacion de la APP durante la ejecucion.

This commit is contained in:
2025-12-04 13:59:54 +01:00
parent f2fd1c3bf5
commit e0133d2ca2
10432 changed files with 1019085 additions and 1 deletions

View File

@@ -0,0 +1,280 @@
package com.google.firebase.remoteconfig.internal;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
import androidx.annotation.Keep;
import com.google.android.gms.common.util.AndroidUtilsLight;
import com.google.android.gms.common.util.Hex;
import com.google.firebase.remoteconfig.BuildConfig;
import com.google.firebase.remoteconfig.FirebaseRemoteConfig;
import com.google.firebase.remoteconfig.FirebaseRemoteConfigClientException;
import com.google.firebase.remoteconfig.FirebaseRemoteConfigException;
import com.google.firebase.remoteconfig.FirebaseRemoteConfigServerException;
import com.google.firebase.remoteconfig.RemoteConfigConstants;
import com.google.firebase.remoteconfig.internal.ConfigContainer;
import com.google.firebase.remoteconfig.internal.ConfigFetchHandler;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/* loaded from: classes3.dex */
public class ConfigFetchHttpClient {
private static final String API_KEY_HEADER = "X-Goog-Api-Key";
private static final String ETAG_HEADER = "ETag";
private static final Pattern GMP_APP_ID_PATTERN = Pattern.compile("^[^:]+:([0-9]+):(android|ios|web):([0-9a-f]+)");
private static final String IF_NONE_MATCH_HEADER = "If-None-Match";
private static final String INSTALLATIONS_AUTH_TOKEN_HEADER = "X-Goog-Firebase-Installations-Auth";
private static final String ISO_DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
private static final String X_ANDROID_CERT_HEADER = "X-Android-Cert";
private static final String X_ANDROID_PACKAGE_HEADER = "X-Android-Package";
private static final String X_GOOGLE_GFE_CAN_RETRY = "X-Google-GFE-Can-Retry";
private final String apiKey;
private final String appId;
private final long connectTimeoutInSeconds;
private final Context context;
private final String namespace;
private final String projectNumber;
private final long readTimeoutInSeconds;
public ConfigFetchHttpClient(Context context, String str, String str2, String str3, long j4, long j5) {
this.context = context;
this.appId = str;
this.apiKey = str2;
this.projectNumber = extractProjectNumberFromAppId(str);
this.namespace = str3;
this.connectTimeoutInSeconds = j4;
this.readTimeoutInSeconds = j5;
}
private boolean backendHasUpdates(JSONObject jSONObject) {
try {
return true ^ jSONObject.get(RemoteConfigConstants.ResponseFieldKey.STATE).equals("NO_CHANGE");
} catch (JSONException unused) {
return true;
}
}
private String convertToISOString(long j4) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(ISO_DATE_PATTERN, Locale.US);
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
return simpleDateFormat.format(Long.valueOf(j4));
}
private JSONObject createFetchRequestBody(String str, String str2, Map<String, String> map, Long l4) throws FirebaseRemoteConfigClientException {
HashMap hashMap = new HashMap();
if (str == null) {
throw new FirebaseRemoteConfigClientException("Fetch failed: Firebase installation id is null.");
}
hashMap.put(RemoteConfigConstants.RequestFieldKey.INSTANCE_ID, str);
hashMap.put(RemoteConfigConstants.RequestFieldKey.INSTANCE_ID_TOKEN, str2);
hashMap.put(RemoteConfigConstants.RequestFieldKey.APP_ID, this.appId);
Locale locale = this.context.getResources().getConfiguration().locale;
hashMap.put(RemoteConfigConstants.RequestFieldKey.COUNTRY_CODE, locale.getCountry());
int i = Build.VERSION.SDK_INT;
hashMap.put(RemoteConfigConstants.RequestFieldKey.LANGUAGE_CODE, locale.toLanguageTag());
hashMap.put(RemoteConfigConstants.RequestFieldKey.PLATFORM_VERSION, Integer.toString(i));
hashMap.put(RemoteConfigConstants.RequestFieldKey.TIME_ZONE, TimeZone.getDefault().getID());
try {
PackageInfo packageInfo = this.context.getPackageManager().getPackageInfo(this.context.getPackageName(), 0);
if (packageInfo != null) {
hashMap.put(RemoteConfigConstants.RequestFieldKey.APP_VERSION, packageInfo.versionName);
hashMap.put(RemoteConfigConstants.RequestFieldKey.APP_BUILD, Long.toString(E.a.b(packageInfo)));
}
} catch (PackageManager.NameNotFoundException unused) {
}
hashMap.put(RemoteConfigConstants.RequestFieldKey.PACKAGE_NAME, this.context.getPackageName());
hashMap.put(RemoteConfigConstants.RequestFieldKey.SDK_VERSION, BuildConfig.VERSION_NAME);
hashMap.put(RemoteConfigConstants.RequestFieldKey.ANALYTICS_USER_PROPERTIES, new JSONObject(map));
if (l4 != null) {
hashMap.put(RemoteConfigConstants.RequestFieldKey.FIRST_OPEN_TIME, convertToISOString(l4.longValue()));
}
return new JSONObject(hashMap);
}
private static ConfigContainer extractConfigs(JSONObject jSONObject, Date date) throws FirebaseRemoteConfigClientException {
JSONObject jSONObject2;
JSONArray jSONArray;
JSONObject jSONObject3;
try {
ConfigContainer.Builder withFetchTime = ConfigContainer.newBuilder().withFetchTime(date);
JSONArray jSONArray2 = null;
try {
jSONObject2 = jSONObject.getJSONObject(RemoteConfigConstants.ResponseFieldKey.ENTRIES);
} catch (JSONException unused) {
jSONObject2 = null;
}
if (jSONObject2 != null) {
withFetchTime = withFetchTime.replaceConfigsWith(jSONObject2);
}
try {
jSONArray = jSONObject.getJSONArray(RemoteConfigConstants.ResponseFieldKey.EXPERIMENT_DESCRIPTIONS);
} catch (JSONException unused2) {
jSONArray = null;
}
if (jSONArray != null) {
withFetchTime = withFetchTime.withAbtExperiments(jSONArray);
}
try {
jSONObject3 = jSONObject.getJSONObject(RemoteConfigConstants.ResponseFieldKey.PERSONALIZATION_METADATA);
} catch (JSONException unused3) {
jSONObject3 = null;
}
if (jSONObject3 != null) {
withFetchTime = withFetchTime.withPersonalizationMetadata(jSONObject3);
}
String string = jSONObject.has(RemoteConfigConstants.ResponseFieldKey.TEMPLATE_VERSION_NUMBER) ? jSONObject.getString(RemoteConfigConstants.ResponseFieldKey.TEMPLATE_VERSION_NUMBER) : null;
if (string != null) {
withFetchTime.withTemplateVersionNumber(Long.parseLong(string));
}
try {
jSONArray2 = jSONObject.getJSONArray(RemoteConfigConstants.ResponseFieldKey.ROLLOUT_METADATA);
} catch (JSONException unused4) {
}
if (jSONArray2 != null) {
withFetchTime = withFetchTime.withRolloutMetadata(jSONArray2);
}
return withFetchTime.build();
} catch (JSONException e4) {
throw new FirebaseRemoteConfigClientException("Fetch failed: fetch response could not be parsed.", e4);
}
}
private static String extractProjectNumberFromAppId(String str) {
Matcher matcher = GMP_APP_ID_PATTERN.matcher(str);
if (matcher.matches()) {
return matcher.group(1);
}
return null;
}
private JSONObject getFetchResponseBody(URLConnection uRLConnection) throws IOException, JSONException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(uRLConnection.getInputStream(), "utf-8"));
StringBuilder sb = new StringBuilder();
while (true) {
int read = bufferedReader.read();
if (read == -1) {
return new JSONObject(sb.toString());
}
sb.append((char) read);
}
}
private String getFetchUrl(String str, String str2) {
return "https://firebaseremoteconfig.googleapis.com/v1/projects/" + str + "/namespaces/" + str2 + ":fetch";
}
private String getFingerprintHashForPackage() {
try {
Context context = this.context;
byte[] packageCertificateHashBytes = AndroidUtilsLight.getPackageCertificateHashBytes(context, context.getPackageName());
if (packageCertificateHashBytes != null) {
return Hex.bytesToStringUppercase(packageCertificateHashBytes, false);
}
Log.e(FirebaseRemoteConfig.TAG, "Could not get fingerprint hash for package: " + this.context.getPackageName());
return null;
} catch (PackageManager.NameNotFoundException e4) {
Log.e(FirebaseRemoteConfig.TAG, "No such package: " + this.context.getPackageName(), e4);
return null;
}
}
private void setCommonRequestHeaders(HttpURLConnection httpURLConnection, String str) {
httpURLConnection.setRequestProperty(API_KEY_HEADER, this.apiKey);
httpURLConnection.setRequestProperty(X_ANDROID_PACKAGE_HEADER, this.context.getPackageName());
httpURLConnection.setRequestProperty(X_ANDROID_CERT_HEADER, getFingerprintHashForPackage());
httpURLConnection.setRequestProperty(X_GOOGLE_GFE_CAN_RETRY, "yes");
httpURLConnection.setRequestProperty(INSTALLATIONS_AUTH_TOKEN_HEADER, str);
httpURLConnection.setRequestProperty("Content-Type", "application/json");
httpURLConnection.setRequestProperty("Accept", "application/json");
}
private void setCustomRequestHeaders(HttpURLConnection httpURLConnection, Map<String, String> map) {
for (Map.Entry<String, String> entry : map.entrySet()) {
httpURLConnection.setRequestProperty(entry.getKey(), entry.getValue());
}
}
private void setFetchRequestBody(HttpURLConnection httpURLConnection, byte[] bArr) throws IOException {
httpURLConnection.setFixedLengthStreamingMode(bArr.length);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(httpURLConnection.getOutputStream());
bufferedOutputStream.write(bArr);
bufferedOutputStream.flush();
bufferedOutputStream.close();
}
private void setUpUrlConnection(HttpURLConnection httpURLConnection, String str, String str2, Map<String, String> map) {
httpURLConnection.setDoOutput(true);
TimeUnit timeUnit = TimeUnit.SECONDS;
httpURLConnection.setConnectTimeout((int) timeUnit.toMillis(this.connectTimeoutInSeconds));
httpURLConnection.setReadTimeout((int) timeUnit.toMillis(this.readTimeoutInSeconds));
httpURLConnection.setRequestProperty(IF_NONE_MATCH_HEADER, str);
setCommonRequestHeaders(httpURLConnection, str2);
setCustomRequestHeaders(httpURLConnection, map);
}
public HttpURLConnection createHttpURLConnection() throws FirebaseRemoteConfigException {
try {
return (HttpURLConnection) new URL(getFetchUrl(this.projectNumber, this.namespace)).openConnection();
} catch (IOException e4) {
throw new FirebaseRemoteConfigException(e4.getMessage());
}
}
@Keep
public ConfigFetchHandler.FetchResponse fetch(HttpURLConnection httpURLConnection, String str, String str2, Map<String, String> map, String str3, Map<String, String> map2, Long l4, Date date) throws FirebaseRemoteConfigException {
setUpUrlConnection(httpURLConnection, str3, str2, map2);
try {
try {
setFetchRequestBody(httpURLConnection, createFetchRequestBody(str, str2, map, l4).toString().getBytes("utf-8"));
httpURLConnection.connect();
int responseCode = httpURLConnection.getResponseCode();
if (responseCode != 200) {
throw new FirebaseRemoteConfigServerException(responseCode, httpURLConnection.getResponseMessage());
}
String headerField = httpURLConnection.getHeaderField(ETAG_HEADER);
JSONObject fetchResponseBody = getFetchResponseBody(httpURLConnection);
try {
httpURLConnection.getInputStream().close();
} catch (IOException unused) {
}
ConfigContainer extractConfigs = extractConfigs(fetchResponseBody, date);
return !backendHasUpdates(fetchResponseBody) ? ConfigFetchHandler.FetchResponse.forBackendHasNoUpdates(date, extractConfigs) : ConfigFetchHandler.FetchResponse.forBackendUpdatesFetched(extractConfigs, headerField);
} catch (IOException | JSONException e4) {
throw new FirebaseRemoteConfigClientException("The client had an error while calling the backend!", e4);
}
} finally {
httpURLConnection.disconnect();
try {
httpURLConnection.getInputStream().close();
} catch (IOException unused2) {
}
}
}
public long getConnectTimeoutInSeconds() {
return this.connectTimeoutInSeconds;
}
public long getReadTimeoutInSeconds() {
return this.readTimeoutInSeconds;
}
}