Технологический прорыв в онлайн-обучении или как «умные» школьники могли схитрить в пандемию
Предыстория
Все мы когда-то учились в школе - кто-то хорошо, кто-то не очень, а кто-то и вовсе плохо. Работа педагогов, которые найдут шпаргалки там, где их нет, один на один могла отразить настоящий уровень подготовки ученика, а оттуда и тройки, а то и двойки...но пришла ковидная пора весной 2020 года, и школы РФ массово стали применять образовательные сервисы для организации дистанционного обучения, что сильно повлияло на успеваемость школьников в "лучшую" сторону. Так было и в моей школе, где масса учителей отдали свой выбор в пользу Российской Электронной Школы (РЭШ).
Поначалу, как и все мои одноклассники, я исправно выполнял задания, но давайте будем честны - в какой-то момент времени мне это стало докучать, ибо резкий переход к иной форме обучения был достаточно болезненным, а монотонное выполнение заданий не добавляло интереса к учебе. Гугл особо не помогал в поисках ответов на задания, а предприимчивые люди стали предлагать ответы за деньги, прикидываясь "администрацией" РЭШ. Однако в один прекрасный день я случайно нашел сервис DZ-Helper, который заинтересовал возможностью просмотра ответов на задания, что навело на мысль, что вероятно я нашел медь, а до золота придется покопаться поглубже..
Поиски "золота"
К каждому уроку на сайте прилагаются тренировочные задания и контрольные задания B1 и B2. После выполнения тренировочных заданий, присутствует возможность просмотра ответов, чем мы и воспользуемся:

При нажатии кнопки, как ни странно, происходит запрос (GET https://resh.edu.ru/tests/{task_id}/get-answers), на что возвращается JSON-массив с непонятными "кракозябрами" - идентификаторами полей и значений:

Отправим задания на проверку (POST https://resh.edu.ru/subject/lesson/{lesson_id}/train/result/):

Содержимое массива как-то сильно похоже на то, что мы получили ранее, но имеет несколько иной вид.
Воспользуемся магией программирования и попробуем получить положительную оценку. Я написал небольшой скрипт на Python, который получает ответы и отправляет их в корректном виде:
import requests
import re
import json
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0',
'Cookie': 'Сюда свои куки',
'X-Requested-With': 'XMLHttpRequest'
}
def tasks_type(tasks_type: int = 1):
if tasks_type == 2:
return 'control/1/'
elif tasks_type == 3:
return 'control/2/'
else:
return 'train/'
def get_lesson_tasks(id: int, tasksType: int = 1) -> list[int]:
url = 'https://resh.edu.ru/subject/lesson/' + str(id) + '/' + tasks_type(tasksType)
response = requests.get(url, headers=headers).content.decode('utf-8')
tasks = re.finditer(r"data-test-id=\"(\d+)\"", response, re.UNICODE)
return [int(task.group(1)) for task in tasks]
def get_task_answer(id: int) -> dict[str: bool | list[str]]:
url = 'https://resh.edu.ru/tests/' + str(id) + '/get-answers'
response = requests.get(url, headers=headers).json()
if response == []:
return {'RESPONSE1': True}
out = dict()
for key in response:
if len(response[key]) > 1:
out[key] = [answer['value'] for answer in response[key]]
else:
out[key] = response[key][0]['value']
return out
def get_lesson_answers(id: int, tasksType: int = 1) -> dict[str: dict[str: bool | list[str]]]:
tasks = get_lesson_tasks(id, tasksType)
out = dict()
for task_id in tasks:
out[str(task_id)] = get_task_answer(task_id)
return out
def solve_lesson(id: int, tasksType: int = 1) -> bool:
answers = get_lesson_answers(id, tasksType)
url = 'https://resh.edu.ru/subject/lesson/' + str(id) + '/' + tasks_type(tasksType) + 'result/'
success = False
while not success:
try:
response = requests.post(url, headers=headers, data={'answers': json.dumps(answers)}).json()
success = True
except requests.exceptions.JSONDecodeError:
pass
try:
if response['success']:
return True
except KeyError:
pass
return False
Вдаваться в подробности не буду - кому нужно, тот разберется сам.
Не забываем подставить куки, чтобы авторизоваться, и вызываем функцию solve_lesson(), передавая два аргумента:
ID - Идентификатор урока из URI
Что решаем (1 - тренировочные задания; 2 - контрольные задания B1; 3 - контрольные задания B2)

Заключение
Не торопясь, попивая колу и закусывая чипсами, школьник, который хоть как-то овладел питоном и реверс-инжинирингом, может решить проблему с невыполненными/выполненными неправильно заданиями раз и навсегда, что я и сделал в школьные годы xD

Возможно когда-нибудь разработчики РЭШа одумаются и придумают какой-нибудь способ защиты от шаловливых ручек, а пока что есть то, что есть..