добро пожаловать!
Дело в том,что МФУ считает не количество израсходованного тонера, а количество отпечатанных страниц. Если стартовый картридж я отпечатал с достаточно большой степенью заполнения, то со вторым случился казус. Дело в том, что жена достаточно быстро влилась в местное сообщество англомам и часть работы, касающаяся обеспечения полиграфией, легла на нас. Так вот, всякие там контурные картинки для вырезания / раскрашивания / оригами заполняют тонером, от силы, 1% листа. В результате у меня оказался «пустой» по счетчику, но фактически — полный процентов на 70 картридж. Нет, я не настолько беден, чтоб не позволить себе купить новый, не на столько экологически воспитан, чтоб испытывать моральные мучения, выкидывая исправное изделие на свалку, но от чувства, что тебя законно развели — кресло начало дымиться, что и было порывом к действию.
Честно, не надеялся стать «мамкиным хакером», учитывая что подобную аппаратную защиту очень сложно сломать. Рассчитывал, как минимум, на приличные алгоритмы шифрования и OTP память (однократно программируемая). Но реальность оказалась куда банальней. К счастью, по запросу обнуления картриджа, было много инструкций, а «защитным чипом» оказалась вполне себе распространенная I2C EEPROM AT24C01, распаянная с минимальным «обвесом» не плату. По большому счету, на следующей картинке, можно закончить статью:

Оригинальное видео
Любым программатором читаем содержимое микросхемы, «зануляем» ячейки, обведенные красными рамками и изменяем пару последних цифр серийного номера. Следует обратить внимание, что серийник — текстовая строка, заканчивающаяся пробелом, так что менять надо в диапазоне 0…9 (0x30…0x39). Физический адрес микросхемы, распаянной на плате — 0x03. Но… Встречайте сапожника без сапог. Нет универсального программатора, по этому берем PIC16F819 и PICKit 3, не, для продвижения в массы — Arduino UNO / Nano, пару резисторов на 4.7k (от 3k до 10k для этой задачи — сойдет), метр МГТФа или Вашего любимого провода и собираем следующую «схему»:

В сборе это смотрится так:

Плату «защиты» я снял с картриджа, чтобы удостовериться, что приведенная в мануале распиновка соответствует реальности и получения физического адреса микросхемы на шине:

Контактные площадки, слева направо: GND, +5V, SCL. SDA.
Можно не мудрить с изготовлением переходника, а подпаять провода непосредственно на плату, не снимая ее с картриджа. Далее — копируем мой говнокод в среду Arduino IDE:
#include <stdint.h> #include <Wire.h> //---------------------------------------------------------------- #define EERROM_SZ (0x80) #define EERROM_PHY_ADDR (0x03) #define EERROM_HEAD (0x50) #define PRN_BUFF_SZ (0x08) #define SER_START_ADDR (0x20) #define SER_END_ADDR (0x2B) #define SER_MOD_ADDR0 (0x2A) #define SER_MOD_ADDR1 (0x29) #define SER_MOD_ADDR2 (0x28) //---------------------------------------------------------------- static uint8_t eeprom_data[EERROM_SZ]; static bool erased; static bool z_filled; //---------------------------------------------------------------- static uint8_t ee_read(uint8_t phy_addr, uint8_t addr) { uint8_t res; Wire.beginTransmission(EERROM_HEAD | phy_addr); Wire.write(addr); Wire.endTransmission(); Wire.requestFrom(EERROM_HEAD | phy_addr, 1); res = Wire.read(); return res; } //---------------------------------------------------------------- static void ee_write(uint8_t phy_addr, uint8_t addr, uint8_t data) { Wire.beginTransmission(EERROM_HEAD | phy_addr); Wire.write(addr); Wire.write(data); Wire.endTransmission(); delay(5); } //---------------------------------------------------------------- static void read_data(uint8_t phy_addr) { uint8_t addr; uint8_t data; erased = true; z_filled = true; Serial.print("Read from phy addr "); Serial.print(phy_addr); for (addr = 0; addr < EERROM_SZ; addr++) { if (0 == (addr & 0x03)) { Serial.print("."); } data = ee_read(phy_addr, addr); eeprom_data[addr] = data; if (0xFF != data) { erased = false; } if (0x00 != data) { z_filled = false; } } Serial.println("Ok"); } //---------------------------------------------------------------- static void write_data(uint8_t phy_addr) { uint8_t addr; Serial.print("Write to phy addr "); Serial.print(phy_addr); for (addr = 0; addr < EERROM_SZ; addr++) { if (0 == (addr & 0x03)) { Serial.print("."); } ee_write(phy_addr, addr, eeprom_data[addr]); } Serial.println("Ok"); } //---------------------------------------------------------------- static bool check_data(uint8_t phy_addr) { uint8_t addr; uint8_t data; Serial.print("Check from phy addr "); Serial.print(phy_addr); for (addr = 0; addr < EERROM_SZ; addr++) { if (0 == (addr & 0x03)) { Serial.print("."); } data = ee_read(phy_addr, addr); if (eeprom_data[addr] != data) { Serial.println("FAILED"); return false; } } Serial.println("Ok"); return true; } //---------------------------------------------------------------- static void print_data(void) { uint16_t addr; char prn_buff[PRN_BUFF_SZ]; for(addr = 0; addr < EERROM_SZ; addr++) { if (0x00 == (addr & 0x0F)) { snprintf(prn_buff, PRN_BUFF_SZ, "%4X: ", addr); Serial.print(prn_buff); } snprintf(prn_buff, PRN_BUFF_SZ, "%2X ", eeprom_data[addr]); Serial.print(prn_buff); if (0x0F == (addr & 0x0F)) { Serial.print("\n\r"); } } Serial.print("\n\r"); } //---------------------------------------------------------------- static void prn_serial(void) { Serial.print("Serial #: "); Serial.write(&eeprom_data[SER_START_ADDR], 1 + SER_END_ADDR - SER_START_ADDR); Serial.print("\n\r"); } //---------------------------------------------------------------- static void mod_serial(void) { eeprom_data[SER_MOD_ADDR0]++; if (eeprom_data[SER_MOD_ADDR0] > '9') { eeprom_data[SER_MOD_ADDR0] = '2'; } eeprom_data[SER_MOD_ADDR1]++; if (eeprom_data[SER_MOD_ADDR1] > '9') { eeprom_data[SER_MOD_ADDR1] = '3'; eeprom_data[SER_MOD_ADDR2]++; if (eeprom_data[SER_MOD_ADDR2] > '9') { eeprom_data[SER_MOD_ADDR2] = '1'; } } } //---------------------------------------------------------------- static void reset_mileage(void) { uint8_t i; for (i = 0x12; i <= 0x1F; i++) { eeprom_data[i] = 0; } for (i = 0x2C; i <= 0x7F; i++) { eeprom_data[i] = 0; } } //---------------------------------------------------------------- static bool test_magic(void) { if (0x32 != eeprom_data[0]) return false; if (0x00 != eeprom_data[1]) return false; if (0x01 != eeprom_data[2]) return false; if (0x03 != eeprom_data[3]) return false; return true; } //---------------------------------------------------------------- void setup() { int key; Serial.begin(9600); Wire.begin(); Serial.println("\tSP 150 cartridge mileage resetter"); Serial.println("Connect like this:"); Serial.println(" TOP"); Serial.println("______________________________"); Serial.println("|o |GND| |+5V| |SCL| |SDA| <="); Serial.println("| |GND| | 5V| | A5| | A4| "); Serial.println("------------------------------"); Serial.println(" cartridge roller"); Serial.println("\n\r\n\r\tTo start, press 'm' or any button for test (not prog)...\n\r"); do { key = Serial.read(); } while(-1 == key); #if 0 for (uint8_t paddr = 0; paddr < 8; paddr++) { Serial.print("Scan phy "); Serial.println(paddr); for (uint8_t i = 0; i < 5; i++) { Serial.print("Read from "); Serial.print(i); Serial.print("........."); Serial.println(ee_read(paddr, i)); } } return; #endif read_data(EERROM_PHY_ADDR); Serial.println("Read:"); print_data(); if (true == erased) { Serial.println("ERROR! The EEPROM is erased or the connection / phy addr is incorrect."); return; } if (true == z_filled) { Serial.println("ERROR! The EEPROM is Z filled."); return; } if (false == test_magic()) { Serial.println("ERROR! Invalid magic number."); return; } prn_serial(); mod_serial(); reset_mileage(); Serial.println("\n\rModified:"); print_data(); prn_serial(); if ('m' != (char)key) { Serial.println("WARNING! The data was not modified in the EEPROM"); return; } write_data(EERROM_PHY_ADDR); if (false == check_data(EERROM_PHY_ADDR)) { return; } Serial.println("Fin"); } void loop() { //do nothing }
(Про говнокод — да, не оптимизировал ни по памяти (читаются все данные в массив), ни по производительности (чтение и запись выполняется побайтно), ни по функционалу, но для такой простой задачи — и так сойдет!)
Прошиваем Ардуинку, открываем любой терминал (I18n, 9600бод), подойдет встроенный в Arduino IDE, сбрасываем платку, жмем любую кнопку:

После этого содержимое EEPROM будет прочитано, модифицировано, но не записано. Если процедура произошла без ошибок — снова сбрасываем плату, нажимаем m, после чего выполнятся все этапы и модифицированные данные будут записаны. Если что пошло не так, — повторно сверьтесь со схемой и повторите попытку. После удачного обнуления отпаиваем провода и устанавливаем картридж на место. Уровень тонера должен быть 100%.
Прошу прощения за «воду», инструкция из 3х строчек — очень мало для поста. Надеюсь, информация была полезной, и еще — автор не несет ответственности за возможные повреждения техники, лишение гарантийного обслуживания, все что Вы делаете — на Ваш страх и риск…
Еще один апдейт, дамп, полученный с моего картриджа:
Read from phy addr 3................................Ok
0: 32 0 1 3 2 1 1 0 0 0 34 30 38 30 31 30
10: 16 5 4D 4D 1 2 11 70 0 0 0 0 14 14 5 21
20: 43 37 30 36 4D 39 30 33 31 39 35 20 0 45 0 0
30: 39 1 0 0 0 0 0 0 3E 4 0 0 0 0 0 0
40: 5 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0
50: 0 0 0 0 0 0 0 0 14 E 5 1B 14 E 5 1B
60: 0 0 0 0 0 0 0 0 77 2 0 0 0 0 0 0
70: C3 23 2A 0 16 0 0 55 0 0 0 0 0 0 0 0
ссылка на оригинал статьи https://habr.com/ru/post/541674/
Добавить комментарий