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

n0n3mi1y

Client
Регистрация
08.03.2017
Сообщения
1 276
Благодарностей
622
Баллы
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();
 
Последнее редактирование:

n0n3mi1y

Client
Регистрация
08.03.2017
Сообщения
1 276
Благодарностей
622
Баллы
113
Дополнительная важная информация

Известные проблемы
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.
 
Последнее редактирование:

n0n3mi1y

Client
Регистрация
08.03.2017
Сообщения
1 276
Благодарностей
622
Баллы
113
Исходники и билды

Шаблон, который был показан в видео. Включает в себя некоторые реализации ручного управление через 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
 

n0n3mi1y

Client
Регистрация
08.03.2017
Сообщения
1 276
Благодарностей
622
Баллы
113
Благодаря @Шива удалось протестировать работоспособность не только в ZDE, но и в ZD. На данный момент времени точно срабатывает, если для проксирования выбирается RedSocks, а не Proxifier.
 
  • Спасибо
Реакции: pir.duha и zarufakis

zarufakis

Client
Регистрация
22.03.2019
Сообщения
1 737
Благодарностей
1 129
Баллы
113
Тема огонь. Мой голос за тебя бро.
 
  • Спасибо
Реакции: n0n3mi1y

Vlad_Curnoi

Client
Регистрация
09.08.2017
Сообщения
474
Благодарностей
200
Баллы
43
Голос за тебя! Молодчик!
 
  • Спасибо
Реакции: n0n3mi1y

FreddyKrueger

Client
Регистрация
06.09.2016
Сообщения
700
Благодарностей
612
Баллы
93
Очень полезное и актуальное решение для дроида. Эта статья может быть хорошей точкой старта для каких то новых разработок. Все написано информативно и доходчиво. Сразу видно n0n3mi1y старался и хочет принести пользу сообществу. Статья на 1 место однозначно!
 

Lest

Client
Регистрация
20.03.2020
Сообщения
76
Благодарностей
78
Баллы
18
Пару месяцев задавался вопросом как подменять отдаваемые заголовки, дроид не меняет некоторые, чатжпт пишет - через прокси, я офигел от того, на сколько это должно быть сложно, но он толково объяснил. Но до теста так и не дошло. А вот сейчас, я так понимаю, пример подъехал.
 
  • Спасибо
Реакции: n0n3mi1y

Moonwalker

Client
Регистрация
16.03.2016
Сообщения
1 672
Благодарностей
1 265
Баллы
113
Вот это можно назвать "статьей на конкурс" )))
Даже если какие-то вещи лично мне пока кажутся непонятными или не сталкивался еще сам, по крайней мере, видна проделанная работа (данный конкурс показал, насколько важно)! Прямо респект!
 
  • Спасибо
Реакции: n0n3mi1y

n0n3mi1y

Client
Регистрация
08.03.2017
Сообщения
1 276
Благодарностей
622
Баллы
113
Пару месяцев задавался вопросом как подменять отдаваемые заголовки, дроид не меняет некоторые, чатжпт пишет - через прокси, я офигел от того, на сколько это должно быть сложно, но он толково объяснил. Но до теста так и не дошло. А вот сейчас, я так понимаю, пример подъехал.
Да. В коде можно увидеть замену заголовков в хендлере BeforeRequests. Делается это для того, чтобы сервер (конечный ресурс) постоянно возвращал нам актуальную версию страницы, вместо того, чтобы она загружалась из кеша.
Всё это можно модифицировать, естественно)
 

n0n3mi1y

Client
Регистрация
08.03.2017
Сообщения
1 276
Благодарностей
622
Баллы
113
Вот это можно назвать "статьей на конкурс" )))
Даже если какие-то вещи лично мне пока кажутся непонятными или не сталкивался еще сам, по крайней мере, видна проделанная работа (данный конкурс показал, насколько важно)! Прямо респект!
Спасибо!
 

Asmus003

Client
Регистрация
25.03.2018
Сообщения
274
Благодарностей
67
Баллы
28
Автор красава! Пусть пока и не нужно мне такое, но за такой тяжелый труд однозначно голос отдам.
 
  • Спасибо
Реакции: n0n3mi1y

n0n3mi1y

Client
Регистрация
08.03.2017
Сообщения
1 276
Благодарностей
622
Баллы
113
4. Необходимость в добавлении заголовков, которые препятствуют загрузке страницы из кеша. Теоретически, может сказаться на трасте целевого URL. Заголовки применяются лишь для доменов, к которым необходимо применить модификацию.
Добавил пункт о известной проблеме. Без заголовков страница будет загружаться из кеша и к ней не будет возможным применить модификации.
 

n0n3mi1y

Client
Регистрация
08.03.2017
Сообщения
1 276
Благодарностей
622
Баллы
113
Если будет изъявлено желание пользователей, то могу провести небольшой стрим, на котором обсудим вопросы по статье и не только.
 

n0n3mi1y

Client
Регистрация
08.03.2017
Сообщения
1 276
Благодарностей
622
Баллы
113
Напоминаю о том, что было объявлено голосование. Если вы заинтересованы в теме или вам кажется, что она достойна призового места - вы можете отдать голос и за меня. Также напоминаю, что вы можете отдать голоса за несколько работ.
Ваши голоса и результаты голосования напрямую повлияют на написание последующих статей от меня, спасибо.
 

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