⭐Монитор трафика для ZennoDroid ⭐ SystemCallAndroidMonitoring - Поддержка автоматической модификации запросов, работа с данными трафика

  • Автор темы Автор темы n0n3mi1y
  • Дата начала Дата начала

n0n3mi1y

Client
Регистрация
08.03.2017
Сообщения
1 453
Реакции
768
Баллы
113
И снова здравствуйте, молодые (и не очень) люди! В этой статьей я хотел бы познакомить вас со своим решением для работы с трафиком напрямую из Android! Название сему, с Вашего позволения, великолепию, было принято решение дать SystemCallAndroidMonitoring. И уже после нейминга проекта я понял, что наделал - аббревиатуре представляет из себя ничто иное как SCAM.

Но я решил его таковым и оставить.

А почему нет?

128415



Дисклеймер: Я не позиционирую себя как профессионального разработчика. Я не проходил профильного обучения в учебных заведениях, а знакомился с языком программирования по мере вовлеченности в автоматизацию различных действий. Решение поставляется по системе “as is”, то есть – как есть. В случае возникновения проблем, трудностей, вопросов, я всё-таки оставлю за собой право не решать их и не отвечать на них. Однако, это не значит, что я полностью пущу все на самотек и закрою глаза на вопросы пользователей. Я буду все решать по мере возможностей и доступного времени. Надеюсь на ваше понимание.

Теперь к тому, что мы имеем. А имеем мы 2 вещи:

1. ASP.NET приложение, которое выступает в качестве API. Его вы можете увидеть на скриншоте выше. С помощью него мы поднимаем инстансы Titanium.Web.Proxy, выполняем добавление данных подмены, получаем и чистим список трафика.
2. Библиотека для работы напрямую из ZennoDroid.

Исходники всего будут прикреплены ниже, как и готовые билды для работы.



Использование

1. Добавляем ссылку на библотеку

128416

А также, добавляем в общий код метод, который пригодится при установке сертификата


Общий код целиком:
Развернуть Свернуть Копировать
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.Text.RegularExpressions;
using ZennoLab.CommandCenter;
using ZennoLab.InterfacesLibrary;
using ZennoLab.InterfacesLibrary.ProjectModel;
using ZennoLab.InterfacesLibrary.ProjectModel.Collections;
using ZennoLab.InterfacesLibrary.ProjectModel.Enums;
using ZennoLab.Macros;
using Global.ZennoExtensions;
using ZennoLab.Emulation;
using ZennoLab.CommandCenter.TouchEvents;
using ZennoLab.CommandCenter.FullEmulation;
using ZennoLab.InterfacesLibrary.Enums;
using ZennoLab.InterfacesLibrary.ZennoDroid;
using ZennoLab.InterfacesLibrary.ZennoDroid.Enums;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;

namespace ZennoLab.OwnCode
{
    /// <summary>
    /// A simple class of the common code
    /// </summary>
    public class CommonCode
    {
        /// <summary>
        /// Lock this object to mark part of code for single thread execution
        /// </summary>
        public static object SyncObject = new object();
        public static string GenerateCertificateHash(string certPath)
        {
            using (var cert = new X509Certificate2(certPath))
            {
                using (var sha1 = System.Security.Cryptography.SHA1.Create())
                {
                    byte[] certHash = sha1.ComputeHash(cert.RawData);
                    return BitConverter.ToString(certHash)
                        .Replace("-", "")
                        .ToLowerInvariant();
                }
            }
        }

        // Insert your code here
    }
}





В общем коде на вкладке "Директивы using" прописываем
Директивы using:
Развернуть Свернуть Копировать
using ScamClient;

2. Подключаем рабочий телефон по WiFi к той же сети, к которой подключен и компьютер, на котором развернут ScamAPI
3. Запускаем ScamAPI
128419
4. Выполняем код для создания нового инстанса Titanium.Web.Proxy (далее: TWP)

Код для создания инстанса TWP:
Развернуть Свернуть Копировать
var client = new ScamClient.ScamClient(project, "http://localhost:65002/");
var newInstanceData = client.NewInstance(project.Variables["proxy"].Value, 3000);

project.Variables["scam_endPoint"].Value = newInstanceData.InstanceData.ProxyEndpoint;
project.Variables["scam_certPEM"].Value = newInstanceData.InstanceData.CertificatePemData;
project.Variables["scam_certPath"].Value = newInstanceData.InstanceData.CertificatePath;


И разбиваем на нужные переменные через split

128426

5. Устанавливаем сертификат
Установка сертификата:
Развернуть Свернуть Копировать
IDroidInputAPI input = instance.DroidInstance.Input;

try
{
    // Проверка root-доступа
    input.Shell("su -c 'touch /data/_zd_test'");
}
catch (Exception ex)
{
    throw new Exception("Нет root-доступа", ex);
}

// Проверка root-доступа
if (!"ok".Equals(input.Shell("su -c 'test -f /data/_zd_test && echo ok && rm -f /data/_zd_test || echo false'")))
    throw new Exception("Нет root-доступа");

// Путь к сертификату
string str = project.Variables["scam_certPath"].Value;

// Генерация хеша сертификата
string certHash = CommonCode.GenerateCertificateHash(str);

// Отправка сертификата на устройство
if (!input.Shell($"adb push \"{str}\" /data/local/tmp/proxy-cert.crt").Contains("1 file pushed"))
    throw new Exception("Не удалось отправить сертификат");

try
{
    // Создание пользовательской папки для сертификатов, если не существует
    input.Shell("su -c 'mkdir -p /data/misc/user/0/cacerts-added'");
    input.Shell("su -c 'chmod 755 /data/misc/user/0/cacerts-added'");

    // Копирование сертификата в пользовательскую папку
    input.Shell($"su -c 'cp /data/local/tmp/proxy-cert.crt /data/misc/user/0/cacerts-added/{certHash}.0'");
   
    // Установка корректных прав доступа
    input.Shell($"su -c 'chmod 644 /data/misc/user/0/cacerts-added/{certHash}.0'");
    input.Shell($"su -c 'chown system:system /data/misc/user/0/cacerts-added/{certHash}.0'");

    // Альтернативный путь для некоторых устройств
    input.Shell("su -c 'mkdir -p /data/misc/certificates'");
    input.Shell($"su -c 'cp /data/local/tmp/proxy-cert.crt /data/misc/certificates/{certHash}.0'");
    input.Shell($"su -c 'chmod 644 /data/misc/certificates/{certHash}.0'");
    input.Shell($"su -c 'chown system:system /data/misc/certificates/{certHash}.0'");

    // Дополнительные проверки
    if (!"ok".Equals(input.Shell($"su -c 'test -f /data/misc/user/0/cacerts-added/{certHash}.0 && echo ok || echo false'")))
        throw new Exception("Не удалось скопировать сертификат в пользовательскую папку");

    // Перезагрузка менеджера сертификатов (может потребоваться перезагрузка устройства)
    input.Shell("su -c 'am broadcast -a android.intent.action.CERTIFICATE_CHANGED'");

    project.SendInfoToLog("Сертификат успешно установлен в пользовательскую папку");
}
catch (Exception ex)
{
    project.SendInfoToLog($"Ошибка установки сертификата: {ex.Message}");
    throw;
}

6. Запускаем браузер с примененными скриптами Frida
128427



7. Применяем нужные нам методы

[AddDomainForChange] Подмена URL (2ip.ru -> ipify.org):
Развернуть Свернуть Копировать
// Объявляем клиент
var client = new ScamClient.ScamClient(project, "http://localhost:65002/");

// Объявляем в рамках клиента инстанс для работы
client.AttachTitaniumInstance(project.Variables["scam_endPoint"].Value);

client.AddDomainForChange(new DomainsForReplace(@"https://2ip.ru", @"2ip.ru", "ipify.org"));

Подмена фрагмента URL (Ручной_труд -> Автоматизация):
Развернуть Свернуть Копировать
// Объявляем клиент
var client = new ScamClient.ScamClient(project, "http://localhost:65002/");

// Объявляем в рамках клиента инстанс для работы
client.AttachTitaniumInstance(project.Variables["scam_endPoint"].Value);

// @"/wiki/Ручной_труд" - regex для поиска изначального URL
// @"Ручной_труд" - regex для замены
// "Автоматизация" - текст, НА который заменяем вхождение по regex для замены
client.AddDomainForChange(new DomainsForReplace(@"/wiki/Ручной_труд", @"Ручной_труд", "Автоматизация"));

[GetInstances] Запрос перечня активных инстансов и вывод их эндпоинтов в лог:
Развернуть Свернуть Копировать
var client = new ScamClient.ScamClient(project, "http://localhost:65002/");
var instances = client.GetInstances();
foreach (var titaniumInstance in instances)
{
    project.SendInfoToLog($"Активный инстанс: {titaniumInstance.EndPoint}");
}

[AddChangeResponse] Подменяем картинки на википедии на иконку ZennoLab:
Развернуть Свернуть Копировать
// Объявляем клиент
var client = new ScamClient.ScamClient(project, "http://localhost:65002/");

// Объявляем в рамках клиента инстанс для работы
client.AttachTitaniumInstance(project.Variables["scam_endPoint"].Value);

client.AddChangeResponse(new ChangeBody(@"wikipedia\.org/wiki/",
    new string[] {
        @"/wiki/[^/]+:[^/]+\.(jpg|jpeg|png|PNG)",
        @"//upload\.wikimedia\.org/[^""'\s]+\.(jpg|jpeg|png|PNG)"
    },
    new string[] {
        @"https://zenno.club/discussion/data/avatars/m/31/31536.jpg?1548149173",
        @"https://zenno.club/discussion/data/avatars/m/31/31536.jpg?1548149173"
    })
);

[AddChangeRequest] Подменяем тело запроса. Заставляем FunCaptcha загрузиться на русском языке:
Развернуть Свернуть Копировать
// Объявляем клиент
var client = new ScamClient.ScamClient(project, "http://localhost:65002/");

// Объявляем в рамках клиента инстанс для работы
client.AttachTitaniumInstance(project.Variables["scam_endPoint"].Value);

client.AddChangeRequest(new ChangeBody(@"/fc/gfct/",
    new string[] {
        @"lang=[a-z]{0,2}"
    },
    new string[] {
        @"lang=ru"
    })
);


[GetTraffic] Получаем весь собранный трафик в качестве JSON массива:
Развернуть Свернуть Копировать
// Объявляем клиент
var client = new ScamClient.ScamClient(project, "http://localhost:65002/");

// Объявляем в рамках клиента инстанс для работы
client.AttachTitaniumInstance(project.Variables["scam_endPoint"].Value);

var traf = client.GetTraffic().TrafficRecords;
return Global.ZennoLab.Json.JsonConvert.SerializeObject(traf);

[GetTraffic] Получаем картинку-задание FunCaptcha в base64 в переменную:
Развернуть Свернуть Копировать
// Объявляем клиент
var client = new ScamClient.ScamClient(project, "http://localhost:65002/");

// Объявляем в рамках клиента инстанс для работы
client.AttachTitaniumInstance(project.Variables["scam_endPoint"].Value);

var traf = client.GetTraffic().TrafficRecords;
foreach (var t in traf)
{
   
    if (t.RequestUrl.Contains("image?challenge"))
    {
        // В этой переменной будет тело картинки в base64
        project.Variables["response_base64"].Value = t.ResponseBodyBase64;
        // В этой переменной будет текстовое представление ответа
        project.Variables["response_raw"].Value = t.ResponseBodyRaw;
        return "ok";
    }
}

[GetTraffic] Получение текстового значения ответа. В данном случае JSON, хранящий в себе задание FunCaptcha:
Развернуть Свернуть Копировать
// Объявляем клиент
var client = new ScamClient.ScamClient(project, "http://localhost:65002/");

// Объявляем в рамках клиента инстанс для работы
client.AttachTitaniumInstance(project.Variables["scam_endPoint"].Value);

var traf = client.GetTraffic().TrafficRecords;
foreach (var t in traf)
{
    if (t.RequestUrl.Contains("gfct"))
    {
        // Массив байт ответа в base64 (не интересует в данном варианте)
        project.Variables["response_base64"].Value = t.ResponseBodyBase64;
        // Текстовое представление ответа
        project.Variables["response_raw"].Value = t.ResponseBodyRaw;
        return "ok";
    }
   
}

Очистка массива трафика:
Развернуть Свернуть Копировать
// Объявляем клиент
var client = new ScamClient.ScamClient(project, "http://localhost:65002/");

// Объявляем в рамках клиента инстанс для работы
client.AttachTitaniumInstance(project.Variables["scam_endPoint"].Value);
client.ClearTraffic();

 
Последнее редактирование модератором:
Дополнительная важная информация

Известные проблемы
1. Утечка WebRtc, пробивается на https://browserleaks.com/ip
2. Сложность в установке прокси и невозможность на данный момент использовать Gnirehtet, Proxifier. Только RedSocks. И то в этом отпадает необходимость, т.к. при работе с использованием монитора трафика происходит жесткое переопределение прокси на уровне системы.
3. Обязательное подключение по WiFi в ту же сеть, что и компьютер, на котором запускается ScamAPI
4. Необходимость в добавлении заголовков, которые препятствуют загрузке страницы из кеша. Теоретически, может сказаться на трасте целевого URL. Заголовки применяются лишь для доменов, к которым необходимо применить модификацию.


Благодарности
Выражаю свою искреннюю благодарность @zarufakis за то, что натолкнул меня на старую идею. Без него мы бы не увидели результата!
Выражаю свою искреннюю благодарность @Proxy-Hobbit.ru за предоставление качественного Польского прокси, который я несколько раз спалил в видео.
Выражаю свою искреннюю благодарность @FreddyKrueger и @Vlad_Curnoi за огромную поддержку и веру в результат!
Выражаю свою искреннюю благодарность @Шива за тестирование и конструктивную критику.
Выражаю свою благодарность
разработчику скрипта для Frida


Подробный разбор методов и объектов
[ScamClient] - объявление нового клиента
[ScamClient] Создание нового клиента:
Развернуть Свернуть Копировать
// project - объект проекта. Обязателен для передачи, нужен для вывода логов,
// ошибок, а также переопределения и чтения  значений переменных
//
// http://localhost:65002/ - url эндпоинта ScamAPI. Если не вносили изменений - таким и останется
var client = new ScamClient.ScamClient(project, "http://localhost:65002/");

[client.NewInstance] - создание нового инстанса
client.NewInstance:
Развернуть Свернуть Копировать
// В project.Variables["proxy"].Value хранится прокси в формате
// Zennoposter (Например: protocol://log:pas@host:port)
// 3000 - таймаут бездействия инстанса перед уничтножением.
// Если проще, то существующий инстанс будет уничтожен через 3000 секунд с момента
// последнего с ним взаимодействия (любая интернет-активность)
var newInstanceData = client.NewInstance(project.Variables["proxy"].Value, 3000);

// Можно передать без параметров. Тогда будет использоваться интернет-подключение компьютера
// Таймаут уничтожения по дефолту - 300 секунд.
var newInstanceData = client.NewInstance();

Возвращает объект, хранящий в себе два других обхекта - InstanceData и IsSuccess.
IsSuccess - булевое (true / false) значение, знаменующее об успешности создания инстанса
InstanceData хранит 3 строки:
ProxyEndpoint - эндпоинт созданной прокси TWP
CertificatePemData - сертификат в формате PEM
CertificatePath - локальный путь к сертификату


[GetInstances] - Запрос перечня инстансов

GetInstances:
Развернуть Свернуть Копировать
var client = new ScamClient.ScamClient(project, "http://localhost:65002/");
var instances = client.GetInstances();
foreach (var titaniumInstance in instances)
{
    project.SendInfoToLog($"Активный инстанс: {titaniumInstance.EndPoint}");
}
Возвращает массив объектов, содержащий в себе строку EndPoint, а также InstanceData, который описан выше

[AttachTitaniumInstance] - Подключение существующего инстанса TWP к интерфейсу клиента

C#:
Развернуть Свернуть Копировать
// Объявляем клиент
var client = new ScamClient.ScamClient(project, "http://localhost:65002/");

// ОБЯЗАТЕЛЬНО объявляем в рамках клиента инстанс для работы
client.AttachTitaniumInstance(project.Variables["scam_endPoint"].Value);

// Выполняем дальнейшие действия с инстансом. Например, подменяем URL
client.AddDomainForChange(new DomainsForReplace(@"https://2ip.ru", @"2ip.ru", "ipify.org"));

[AddDomainForChange] - Добавление домена для подмены
AddDomainForChange:
Развернуть Свернуть Копировать
string urlRegex = @"https://2ip.ru"; // Регулярное выражение для определения целевой ссылки
string regexForReplace = @"2ip.ru"; // Регулярное выражение, для поиска фрагмента текста, который надо заменить в строке
string textToReplace = "ipify.org"; // Текст, на который надо заменить найденный фрагмент

client.AddDomainForChange(new DomainsForReplace(urlRegex, regexForReplace, textToReplace));

Класс DomainsForReplace:
Развернуть Свернуть Копировать
public class DomainsForReplace
{
    public string DomainRegex { get; set; } = string.Empty;
    public string RegexFromReplace { get; set; } = string.Empty;
    public string TextToReplace { get; set; } = string.Empty;

    /// <summary>
    /// Данные для подмены
    /// </summary>
    /// <param name="domainRegex">Регулярное выражение для определения url, который необходимо заменить</param>
    /// <param name="regexFromReplace">Регулярное выражение для участка строки, который надо подменить</param>
    /// <param name="textToReplace">Текст, на который необходимо заменить участок строки</param>
    public DomainsForReplace(string domainRegex, string regexFromReplace, string textToReplace)
    {
        this.DomainRegex = domainRegex;
        this.RegexFromReplace = regexFromReplace;
        this.TextToReplace = textToReplace;
    }
}


[Class ChangeBody] - Объект, передаваемый при подмене Request и Response
Класс ChangeBody:
Развернуть Свернуть Копировать
public class ChangeBody
{
    public string DomainRegex { get; set; } = string.Empty;
    public List<string> RegexesFromReplace { get; set; } = new List<string>();
    public List<string> TextToReplace { get; set; } = new List<string>();

    /// <summary>
    /// Объект данных, хранящий в себе данные для подмены запроса или ответа. Количество
    /// </summary>
    /// <param name="domainRegex">Регулярное выражение для поиска URL, к которому необходимо применить изменение</param>
    /// <param name="regexFromReplace">Список регулярных выражений для поиска данных подмены</param>
    /// <param name="textToReplace">Список текстовых значений, на которые необходимо подменить</param>
    public ChangeBody(string domainRegex, IEnumerable<string> regexFromReplace, IEnumerable<string> textToReplace)
    {
        this.DomainRegex = domainRegex;
        this.RegexesFromReplace = regexFromReplace?.ToList() ?? new List<string>();
        this.TextToReplace = textToReplace?.ToList() ?? new List<string>();
    }

}

Важно!
Количество элементов в regexFromReplace и textToReplace должно быть ОДИНАКОВЫМ. Происходит сопоставление 1 к 1 по индексам!


[AddChangeResponse] - Подмена текстовых данных в ответе HTTP-запроса
AddChangeResponse:
Развернуть Свернуть Копировать
// ....

// Regex для поиска целевой ссылки
var urlRegex = @"wikipedia\.org/wiki/";

// Массив регулярных выражений для поиска фрагментов, которые нужно заменить
var regexListForReplace = new string[] {
        @"/wiki/[^/]+:[^/]+\.(jpg|jpeg|png|PNG)",
        @"//upload\.wikimedia\.org/[^""'\s]+\.(jpg|jpeg|png|PNG)"
    };

// Массив текстовых переменных, на которые нужно заменить
// Замена происходит 1 к 1 по индексам. То есть первая строка из списка выше заменится
// на первую строку из списка ниже. Вторая - на вторую и так далее по списку.
// По этой причине важно, чтобы количество элементов в обоих списках было одинаковым.
var textListToReplace = new string[] {
        @"https://zenno.club/discussion/data/avatars/m/31/31536.jpg?1548149173",
        @"https://zenno.club/discussion/data/avatars/m/31/31536.jpg?1548149173"
    };


client.AddChangeResponse(new ChangeBody(
    urlRegex,
    regexListForReplace,
    textListToReplace
));

C#:
Развернуть Свернуть Копировать
// ...
// Регулярное выражение для поиска целевой ссылки
var urlRegex = @"/fc/gfct/";
// Массив регулярных выражений для поиска фрагментов, которые надо заменить в теле запроса
var regexListForReplace = new string[] {
        @"lang=[a-z]{0,2}"
    };

// Массив строк с текстом, на которые надо заменить найденные фрагменты.
// Работает идентично AddChangeResponse, количество элементов в массивах также должно быть одинаковым!
var textListToReplace = new string[] {
        @"lang=ru"
    };
client.AddChangeRequest(new ChangeBody(
    urlRegex,
    regexListForReplace,
    textListToReplace
    ));

Данные методы возвращают объект типа AddChangeBodyResponse

C#:
Развернуть Свернуть Копировать
public class AddChangeBodyResponse
{
    /// <summary>
    /// Удачное ли добавление
    /// </summary>
    public bool IsSuccess { get; set; } = false;
    /// <summary>
    /// Текст ошибки
    /// </summary>
    public string ErrorText { get; set; } = string.Empty;
    /// <summary>
    /// Перечень всех объектов для подмены
    /// </summary>
    public List<ChangeBody> ChangeBodyArray { get; set; } = new List<ChangeBody>();
}

То есть, вы сразу же можете получить после добавления перечень всех существующих подмен в рамках данного инстанса


[GetTraffic] - Получение массива с информацией о зафиксированном трафике

C#:
Развернуть Свернуть Копировать
//...
var traf = client.GetTraffic();


Возвращает объект типа GetTrafficResponse

C#:
Развернуть Свернуть Копировать
/// <summary>
/// Объект с данными трафика
/// </summary>
public class GetTrafficResponse
{
    /// <summary>
    /// Удачный ли запрос
    /// </summary>
    public bool IsSuccess { get; set; } = false;

    /// <summary>
    /// Список с данными инстанса
    /// </summary>
    public List<TrafficRecord> TrafficRecords { get; set; } = new List<TrafficRecord>();
}

Хранит в себе свойство TrafficRecords, которое и представляет из себя перечень данных о каждом зарегистрированном запросе. Перечень возвращается от самого позднего запроса к самому раннему. В 0 индексе будет лежать самый последний запрос. Пока не поступил ответ - запрос не появится в перечне.
C#:
Развернуть Свернуть Копировать
public class TrafficRecord
{
    public Guid Id { get; set; } = Guid.NewGuid();
    public string RequestUrl { get; set; }
    public string RequestMethod { get; set; }
    public DateTime RequestTime { get; set; } // UTC
    public DateTime ResponseTime { get; set; } // UTC
    public TimeSpan ResponseDuration { get; set; }
    public int ResponseStatusCode { get; set; }
    public string ResponseStatusDescription { get; set; }

    // Заголовки
    public Dictionary<string, string> RequestHeaders { get; set; }
    public Dictionary<string, string> ResponseHeaders { get; set; }

    // Тела запроса и ответа
    public string RequestBodyRaw { get; set; }
    public string RequestBodyBase64 { get; set; }
    public string ResponseBodyRaw { get; set; }
    public string ResponseBodyBase64 { get; set; }

    // Дополнительная информация
    public string ContentType { get; set; }
    public long ContentLength { get; set; }
    public string ClientIpAddress { get; set; }
}

[ClearTraffic] - Очистка массива трафика
C#:
Развернуть Свернуть Копировать
//..
client.ClearTraffic();
Возвращает объект типа GetTrafficResponse.
Q&A

Q: На каком языке написано решение
A: C#.

Q: Можно ли протестировать запросы вручную?
A: http://localhost:65002/swagger/index.html (только в режиме отладки)

Q: Что делать с утечкой WebRTC?
A: Перепробовал множество способов, но не нашел пока действенного варианта. Возможно, кто-то всё-таки подскажет решение

Q: Планируется ли расширение методов?
A: Да, но точно не сейчас, а позднее и по мере необходимости.

Q: Где ручное уничтожение инстанса?
A: Я о нем забыл и уже поздняк метаться, добавлю по возможности позднее :az:

Q: Что за Frida-скрипт?
A: Сборная солянка отсюда

Q: Что за код для установки сертификата?
A: Адаптированный код из библиотеки, скачанный отсюда

Q: Использовалась ли нейросеть при создании?
A: Да, преимущественно Claude 3.5.
 
Последнее редактирование модератором:
Исходники и билды

Шаблон, который был показан в видео. Включает в себя некоторые реализации ручного управление через HTTP запросы, а также C# сниппеты для работы
Билд ScamAPI для win-x64. Перед запуском установить .net 8.0

Библиотека ScamClient для работы через C# из ZennoDroid. Положить по пути C:\Program Files\ZennoLab\RU\ZennoDroid Enterprise\x.x.x.x\Progs\ExternalAssemblies

Исходник ScamAPI
Исходник ScamClient
 
Благодаря @Шива удалось протестировать работоспособность не только в ZDE, но и в ZD. На данный момент времени точно срабатывает, если для проксирования выбирается RedSocks, а не Proxifier.
 
Тема огонь. Мой голос за тебя бро.
 
  • Спасибо
Реакции: n0n3mi1y
Очень полезное и актуальное решение для дроида. Эта статья может быть хорошей точкой старта для каких то новых разработок. Все написано информативно и доходчиво. Сразу видно n0n3mi1y старался и хочет принести пользу сообществу. Статья на 1 место однозначно!
 
Пару месяцев задавался вопросом как подменять отдаваемые заголовки, дроид не меняет некоторые, чатжпт пишет - через прокси, я офигел от того, на сколько это должно быть сложно, но он толково объяснил. Но до теста так и не дошло. А вот сейчас, я так понимаю, пример подъехал.
 
  • Спасибо
Реакции: n0n3mi1y
Вот это можно назвать "статьей на конкурс" )))
Даже если какие-то вещи лично мне пока кажутся непонятными или не сталкивался еще сам, по крайней мере, видна проделанная работа (данный конкурс показал, насколько важно)! Прямо респект!
 
  • Спасибо
Реакции: n0n3mi1y
Пару месяцев задавался вопросом как подменять отдаваемые заголовки, дроид не меняет некоторые, чатжпт пишет - через прокси, я офигел от того, на сколько это должно быть сложно, но он толково объяснил. Но до теста так и не дошло. А вот сейчас, я так понимаю, пример подъехал.
Да. В коде можно увидеть замену заголовков в хендлере BeforeRequests. Делается это для того, чтобы сервер (конечный ресурс) постоянно возвращал нам актуальную версию страницы, вместо того, чтобы она загружалась из кеша.
Всё это можно модифицировать, естественно)
 
Вот это можно назвать "статьей на конкурс" )))
Даже если какие-то вещи лично мне пока кажутся непонятными или не сталкивался еще сам, по крайней мере, видна проделанная работа (данный конкурс показал, насколько важно)! Прямо респект!
Спасибо!
 
Автор красава! Пусть пока и не нужно мне такое, но за такой тяжелый труд однозначно голос отдам.
 
  • Спасибо
Реакции: n0n3mi1y
4. Необходимость в добавлении заголовков, которые препятствуют загрузке страницы из кеша. Теоретически, может сказаться на трасте целевого URL. Заголовки применяются лишь для доменов, к которым необходимо применить модификацию.
Добавил пункт о известной проблеме. Без заголовков страница будет загружаться из кеша и к ней не будет возможным применить модификации.
 
Если будет изъявлено желание пользователей, то могу провести небольшой стрим, на котором обсудим вопросы по статье и не только.
 
Напоминаю о том, что было объявлено голосование. Если вы заинтересованы в теме или вам кажется, что она достойна призового места - вы можете отдать голос и за меня. Также напоминаю, что вы можете отдать голоса за несколько работ.
Ваши голоса и результаты голосования напрямую повлияют на написание последующих статей от меня, спасибо.
 

Кто просматривает тему: (Всего: 0, Пользователи: 0, Гости: 0)