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,132 @@
package com.google.firebase.storage.internal;
import android.app.Activity;
import android.util.Log;
import com.google.android.gms.common.api.internal.LifecycleActivity;
import com.google.android.gms.common.api.internal.LifecycleCallback;
import com.google.android.gms.common.api.internal.LifecycleFragment;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/* loaded from: classes3.dex */
public class ActivityLifecycleListener {
private static final ActivityLifecycleListener instance = new ActivityLifecycleListener();
private final Map<Object, LifecycleEntry> cookieMap = new HashMap();
private final Object sync = new Object();
/* loaded from: classes3.dex */
public static class LifecycleEntry {
private final Activity activity;
private final Object cookie;
private final Runnable runnable;
public LifecycleEntry(Activity activity, Runnable runnable, Object obj) {
this.activity = activity;
this.runnable = runnable;
this.cookie = obj;
}
public boolean equals(Object obj) {
if (!(obj instanceof LifecycleEntry)) {
return false;
}
LifecycleEntry lifecycleEntry = (LifecycleEntry) obj;
return lifecycleEntry.cookie.equals(this.cookie) && lifecycleEntry.runnable == this.runnable && lifecycleEntry.activity == this.activity;
}
public Activity getActivity() {
return this.activity;
}
public Object getCookie() {
return this.cookie;
}
public Runnable getRunnable() {
return this.runnable;
}
public int hashCode() {
return this.cookie.hashCode();
}
}
/* loaded from: classes3.dex */
public static class OnStopCallback extends LifecycleCallback {
private static final String TAG = "StorageOnStopCallback";
private final List<LifecycleEntry> listeners;
private OnStopCallback(LifecycleFragment lifecycleFragment) {
super(lifecycleFragment);
this.listeners = new ArrayList();
this.mLifecycleFragment.addCallback(TAG, this);
}
public static OnStopCallback getInstance(Activity activity) {
LifecycleFragment fragment = LifecycleCallback.getFragment(new LifecycleActivity(activity));
OnStopCallback onStopCallback = (OnStopCallback) fragment.getCallbackOrNull(TAG, OnStopCallback.class);
return onStopCallback == null ? new OnStopCallback(fragment) : onStopCallback;
}
public void addEntry(LifecycleEntry lifecycleEntry) {
synchronized (this.listeners) {
this.listeners.add(lifecycleEntry);
}
}
@Override // com.google.android.gms.common.api.internal.LifecycleCallback
public void onStop() {
ArrayList arrayList;
synchronized (this.listeners) {
arrayList = new ArrayList(this.listeners);
this.listeners.clear();
}
Iterator it = arrayList.iterator();
while (it.hasNext()) {
LifecycleEntry lifecycleEntry = (LifecycleEntry) it.next();
if (lifecycleEntry != null) {
Log.d(TAG, "removing subscription from activity.");
lifecycleEntry.getRunnable().run();
ActivityLifecycleListener.getInstance().removeCookie(lifecycleEntry.getCookie());
}
}
}
public void removeEntry(LifecycleEntry lifecycleEntry) {
synchronized (this.listeners) {
this.listeners.remove(lifecycleEntry);
}
}
}
private ActivityLifecycleListener() {
}
public static ActivityLifecycleListener getInstance() {
return instance;
}
public void removeCookie(Object obj) {
synchronized (this.sync) {
try {
LifecycleEntry lifecycleEntry = this.cookieMap.get(obj);
if (lifecycleEntry != null) {
OnStopCallback.getInstance(lifecycleEntry.getActivity()).removeEntry(lifecycleEntry);
}
} catch (Throwable th) {
throw th;
}
}
}
public void runOnActivityStopped(Activity activity, Object obj, Runnable runnable) {
synchronized (this.sync) {
LifecycleEntry lifecycleEntry = new LifecycleEntry(activity, runnable, obj);
OnStopCallback.getInstance(activity).addEntry(lifecycleEntry);
this.cookieMap.put(obj, lifecycleEntry);
}
}
}

View File

@@ -0,0 +1,102 @@
package com.google.firebase.storage.internal;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
/* loaded from: classes3.dex */
public class AdaptiveStreamBuffer {
private static final String TAG = "AdaptiveStreamBuffer";
private static final Runtime runtime = Runtime.getRuntime();
private byte[] buffer;
private final InputStream source;
private int availableBytes = 0;
private boolean adaptiveMode = true;
private boolean reachedEnd = false;
public AdaptiveStreamBuffer(InputStream inputStream, int i) {
this.source = inputStream;
this.buffer = new byte[i];
}
private int resize(int i) {
int max = Math.max(this.buffer.length * 2, i);
Runtime runtime2 = runtime;
long maxMemory = runtime2.maxMemory() - (runtime2.totalMemory() - runtime2.freeMemory());
if (!this.adaptiveMode || max >= maxMemory) {
Log.w(TAG, "Turning off adaptive buffer resizing to conserve memory.");
} else {
try {
byte[] bArr = new byte[max];
System.arraycopy(this.buffer, 0, bArr, 0, this.availableBytes);
this.buffer = bArr;
} catch (OutOfMemoryError unused) {
Log.w(TAG, "Turning off adaptive buffer resizing due to low memory.");
this.adaptiveMode = false;
}
}
return this.buffer.length;
}
public int advance(int i) throws IOException {
int i4 = this.availableBytes;
int i5 = 0;
if (i <= i4) {
int i6 = i4 - i;
this.availableBytes = i6;
byte[] bArr = this.buffer;
System.arraycopy(bArr, i, bArr, 0, i6);
return i;
}
this.availableBytes = 0;
while (i5 < i) {
int skip = (int) this.source.skip(i - i5);
if (skip > 0) {
i5 += skip;
} else if (skip != 0) {
continue;
} else {
if (this.source.read() == -1) {
break;
}
i5++;
}
}
return i5;
}
public int available() {
return this.availableBytes;
}
public void close() throws IOException {
this.source.close();
}
public int fill(int i) throws IOException {
if (i > this.buffer.length) {
i = Math.min(i, resize(i));
}
while (true) {
int i4 = this.availableBytes;
if (i4 >= i) {
break;
}
int read = this.source.read(this.buffer, i4, i - i4);
if (read == -1) {
this.reachedEnd = true;
break;
}
this.availableBytes += read;
}
return this.availableBytes;
}
public byte[] get() {
return this.buffer;
}
public boolean isFinished() {
return this.reachedEnd;
}
}

View File

@@ -0,0 +1,88 @@
package com.google.firebase.storage.internal;
import android.content.Context;
import android.util.Log;
import com.google.android.gms.common.internal.Preconditions;
import com.google.android.gms.common.util.Clock;
import com.google.android.gms.common.util.DefaultClock;
import com.google.firebase.appcheck.interop.InteropAppCheckTokenProvider;
import com.google.firebase.auth.internal.InternalAuthProvider;
import com.google.firebase.storage.network.NetworkRequest;
import java.util.Random;
/* loaded from: classes3.dex */
public class ExponentialBackoffSender {
private static final int MAXIMUM_WAIT_TIME_MILLI = 30000;
private static final int NETWORK_STATUS_POLL_INTERVAL = 1000;
public static final int RND_MAX = 250;
private static final String TAG = "ExponenentialBackoff";
private final InteropAppCheckTokenProvider appCheckProvider;
private final InternalAuthProvider authProvider;
private volatile boolean canceled;
private final Context context;
private long retryTime;
private static final Random random = new Random();
static Sleeper sleeper = new SleeperImpl();
static Clock clock = DefaultClock.getInstance();
public ExponentialBackoffSender(Context context, InternalAuthProvider internalAuthProvider, InteropAppCheckTokenProvider interopAppCheckTokenProvider, long j4) {
this.context = context;
this.authProvider = internalAuthProvider;
this.appCheckProvider = interopAppCheckTokenProvider;
this.retryTime = j4;
}
public void cancel() {
this.canceled = true;
}
public boolean isRetryableError(int i) {
return (i >= 500 && i < 600) || i == -2 || i == 429 || i == 408;
}
public void reset() {
this.canceled = false;
}
public void sendWithExponentialBackoff(NetworkRequest networkRequest) {
sendWithExponentialBackoff(networkRequest, true);
}
public void sendWithExponentialBackoff(NetworkRequest networkRequest, boolean z3) {
Preconditions.checkNotNull(networkRequest);
long elapsedRealtime = clock.elapsedRealtime() + this.retryTime;
if (z3) {
networkRequest.performRequest(Util.getCurrentAuthToken(this.authProvider), Util.getCurrentAppCheckToken(this.appCheckProvider), this.context);
} else {
networkRequest.performRequestStart(Util.getCurrentAuthToken(this.authProvider), Util.getCurrentAppCheckToken(this.appCheckProvider));
}
int i = 1000;
while (clock.elapsedRealtime() + i <= elapsedRealtime && !networkRequest.isResultSuccess() && isRetryableError(networkRequest.getResultCode())) {
try {
sleeper.sleep(random.nextInt(250) + i);
if (i < MAXIMUM_WAIT_TIME_MILLI) {
if (networkRequest.getResultCode() != -2) {
i *= 2;
Log.w(TAG, "network error occurred, backing off/sleeping.");
} else {
Log.w(TAG, "network unavailable, sleeping.");
i = 1000;
}
}
if (this.canceled) {
return;
}
networkRequest.reset();
if (z3) {
networkRequest.performRequest(Util.getCurrentAuthToken(this.authProvider), Util.getCurrentAppCheckToken(this.appCheckProvider), this.context);
} else {
networkRequest.performRequestStart(Util.getCurrentAuthToken(this.authProvider), Util.getCurrentAppCheckToken(this.appCheckProvider));
}
} catch (InterruptedException unused) {
Log.w(TAG, "thread interrupted during exponential backoff.");
Thread.currentThread().interrupt();
return;
}
}
}
}

View File

@@ -0,0 +1,39 @@
package com.google.firebase.storage.internal;
import android.net.Uri;
import android.text.TextUtils;
import com.google.android.gms.common.internal.Preconditions;
import com.google.firebase.sessions.settings.RemoteSettings;
/* loaded from: classes3.dex */
public class Slashes {
public static String normalizeSlashes(String str) {
if (TextUtils.isEmpty(str)) {
return "";
}
if (!str.startsWith(RemoteSettings.FORWARD_SLASH_STRING) && !str.endsWith(RemoteSettings.FORWARD_SLASH_STRING) && !str.contains("//")) {
return str;
}
StringBuilder sb = new StringBuilder();
for (String str2 : str.split(RemoteSettings.FORWARD_SLASH_STRING, -1)) {
if (!TextUtils.isEmpty(str2)) {
if (sb.length() > 0) {
sb.append(RemoteSettings.FORWARD_SLASH_STRING);
sb.append(str2);
} else {
sb.append(str2);
}
}
}
return sb.toString();
}
public static String preserveSlashEncode(String str) {
return TextUtils.isEmpty(str) ? "" : slashize(Uri.encode(str));
}
public static String slashize(String str) {
Preconditions.checkNotNull(str);
return str.replace("%2F", RemoteSettings.FORWARD_SLASH_STRING);
}
}

View File

@@ -0,0 +1,6 @@
package com.google.firebase.storage.internal;
/* loaded from: classes3.dex */
public interface Sleeper {
void sleep(int i) throws InterruptedException;
}

View File

@@ -0,0 +1,9 @@
package com.google.firebase.storage.internal;
/* loaded from: classes3.dex */
public class SleeperImpl implements Sleeper {
@Override // com.google.firebase.storage.internal.Sleeper
public void sleep(int i) throws InterruptedException {
Thread.sleep(i);
}
}

View File

@@ -0,0 +1,33 @@
package com.google.firebase.storage.internal;
import android.annotation.SuppressLint;
import com.google.android.gms.common.internal.Preconditions;
import com.google.firebase.storage.StorageTaskScheduler;
import java.util.concurrent.Executor;
/* loaded from: classes3.dex */
public class SmartHandler {
static boolean testMode = false;
private final Executor executor;
@SuppressLint({"ThreadPoolCreation"})
public SmartHandler(Executor executor) {
if (executor != null) {
this.executor = executor;
} else if (testMode) {
this.executor = null;
} else {
this.executor = StorageTaskScheduler.getInstance().getMainThreadExecutor();
}
}
public void callBack(Runnable runnable) {
Preconditions.checkNotNull(runnable);
Executor executor = this.executor;
if (executor != null) {
executor.execute(runnable);
} else {
StorageTaskScheduler.getInstance().scheduleCallback(runnable);
}
}
}

View File

@@ -0,0 +1,46 @@
package com.google.firebase.storage.internal;
import android.net.Uri;
import com.google.firebase.emulators.EmulatedServiceSettings;
import com.google.firebase.sessions.settings.RemoteSettings;
import com.google.firebase.storage.network.NetworkRequest;
/* loaded from: classes3.dex */
public class StorageReferenceUri {
private final Uri gsUri;
private final Uri httpBaseUri;
private final Uri httpUri;
public StorageReferenceUri(Uri uri) {
this(uri, null);
}
public Uri getGsUri() {
return this.gsUri;
}
public Uri getHttpBaseUri() {
return this.httpBaseUri;
}
public Uri getHttpUri() {
return this.httpUri;
}
public StorageReferenceUri(Uri uri, EmulatedServiceSettings emulatedServiceSettings) {
Uri parse;
this.gsUri = uri;
if (emulatedServiceSettings == null) {
parse = NetworkRequest.PROD_BASE_URL;
} else {
parse = Uri.parse("http://" + emulatedServiceSettings.getHost() + ":" + emulatedServiceSettings.getPort() + "/v0");
}
this.httpBaseUri = parse;
Uri.Builder appendEncodedPath = parse.buildUpon().appendPath("b").appendEncodedPath(uri.getAuthority());
String normalizeSlashes = Slashes.normalizeSlashes(uri.getPath());
if (normalizeSlashes.length() > 0 && !RemoteSettings.FORWARD_SLASH_STRING.equals(normalizeSlashes)) {
appendEncodedPath = appendEncodedPath.appendPath("o").appendPath(normalizeSlashes);
}
this.httpUri = appendEncodedPath.build();
}
}

View File

@@ -0,0 +1,120 @@
package com.google.firebase.storage.internal;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import com.google.android.gms.common.internal.Objects;
import com.google.android.gms.common.internal.Preconditions;
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.FirebaseApp;
import com.google.firebase.appcheck.AppCheckTokenResult;
import com.google.firebase.appcheck.interop.InteropAppCheckTokenProvider;
import com.google.firebase.auth.GetTokenResult;
import com.google.firebase.auth.internal.InternalAuthProvider;
import com.google.firebase.sessions.settings.RemoteSettings;
import com.google.firebase.storage.network.NetworkRequest;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/* loaded from: classes3.dex */
public class Util {
public static final String ISO_8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
private static final int MAXIMUM_TOKEN_WAIT_TIME_MS = 30000;
public static final int NETWORK_UNAVAILABLE = -2;
private static final String TAG = "StorageUtil";
public static boolean equals(Object obj, Object obj2) {
return Objects.equal(obj, obj2);
}
public static String getCurrentAppCheckToken(InteropAppCheckTokenProvider interopAppCheckTokenProvider) {
if (interopAppCheckTokenProvider == null) {
return null;
}
try {
AppCheckTokenResult appCheckTokenResult = (AppCheckTokenResult) Tasks.await(interopAppCheckTokenProvider.getToken(false), 30000L, TimeUnit.MILLISECONDS);
if (appCheckTokenResult.getError() != null) {
Log.w(TAG, "Error getting App Check token; using placeholder token instead. Error: " + appCheckTokenResult.getError());
}
return appCheckTokenResult.getToken();
} catch (InterruptedException | ExecutionException | TimeoutException e4) {
Log.e(TAG, "Unexpected error getting App Check token: " + e4);
return null;
}
}
public static String getCurrentAuthToken(InternalAuthProvider internalAuthProvider) {
String str;
if (internalAuthProvider != null) {
try {
str = ((GetTokenResult) Tasks.await(internalAuthProvider.getAccessToken(false), 30000L, TimeUnit.MILLISECONDS)).getToken();
} catch (InterruptedException | ExecutionException | TimeoutException e4) {
Log.e(TAG, "error getting token " + e4);
}
} else {
str = null;
}
if (!TextUtils.isEmpty(str)) {
return str;
}
Log.w(TAG, "no auth token for request");
return null;
}
public static Uri normalize(FirebaseApp firebaseApp, String str) throws UnsupportedEncodingException {
String substring;
if (TextUtils.isEmpty(str)) {
return null;
}
Uri uri = NetworkRequest.PROD_BASE_URL;
if (str.toLowerCase().startsWith("gs://")) {
return Uri.parse("gs://" + Slashes.preserveSlashEncode(Slashes.normalizeSlashes(str.substring(5))));
}
Uri parse = Uri.parse(str);
String scheme = parse.getScheme();
if (scheme == null || !(equals(scheme.toLowerCase(), "http") || equals(scheme.toLowerCase(), "https"))) {
Log.w(TAG, "FirebaseStorage is unable to support the scheme:" + scheme);
throw new IllegalArgumentException("Uri scheme");
}
int indexOf = parse.getAuthority().toLowerCase().indexOf(uri.getAuthority());
String slashize = Slashes.slashize(parse.getEncodedPath());
if (indexOf == 0 && slashize.startsWith(RemoteSettings.FORWARD_SLASH_STRING)) {
int indexOf2 = slashize.indexOf("/b/", 0);
int i = indexOf2 + 3;
int indexOf3 = slashize.indexOf(RemoteSettings.FORWARD_SLASH_STRING, i);
int indexOf4 = slashize.indexOf("/o/", 0);
if (indexOf2 == -1 || indexOf3 == -1) {
Log.w(TAG, "Firebase Storage URLs must point to an object in your Storage Bucket. Please obtain a URL using the Firebase Console or getDownloadUrl().");
throw new IllegalArgumentException("Firebase Storage URLs must point to an object in your Storage Bucket. Please obtain a URL using the Firebase Console or getDownloadUrl().");
}
substring = slashize.substring(i, indexOf3);
slashize = indexOf4 != -1 ? slashize.substring(indexOf4 + 3) : "";
} else {
if (indexOf <= 1) {
Log.w(TAG, "Firebase Storage URLs must point to an object in your Storage Bucket. Please obtain a URL using the Firebase Console or getDownloadUrl().");
throw new IllegalArgumentException("Firebase Storage URLs must point to an object in your Storage Bucket. Please obtain a URL using the Firebase Console or getDownloadUrl().");
}
substring = parse.getAuthority().substring(0, indexOf - 1);
}
Preconditions.checkNotEmpty(substring, "No bucket specified");
return new Uri.Builder().scheme("gs").authority(substring).encodedPath(slashize).build();
}
public static long parseDateTime(String str) {
if (str == null) {
return 0L;
}
String replaceAll = str.replaceAll("Z$", "-0000");
try {
return new SimpleDateFormat(ISO_8601_FORMAT, Locale.getDefault()).parse(replaceAll).getTime();
} catch (ParseException e4) {
Log.w(TAG, "unable to parse datetime:" + replaceAll, e4);
return 0L;
}
}
}

View File

@@ -0,0 +1,2 @@
package com.google.firebase.storage.internal;