Lexmark Printer Hacking - Page 1
Forum section: electronics / projects

author: mojobojo
author_rank: Newbie
timestamp: December 16, 2013, 02:37:57 pm
content: This is something I wanted to do for a while. I claimed this printer when it stopped working and upon opening it realizing the thing was flooded with ink and beyond economical repair. However the motherboard and scanner module was okay.The victim in question. A Lexmark Pro 805 printer, the last picture of it before I cracked the thing open.I chose it for a few reasons:1. It was firmware upgradable2. The firmware was easy to open and disassemble3. The cpu architecture was ARM4. If I could repurpose it, its basically a free computer.5. I am a beginner to hardware.You can download the firmware upgrade fromhere, they do not have any protection or anything on it so you can just dump it into a disassembler.Well I started like anybody would on the motherboard. You can see in this image I have it almost fully working (asking for the freaking "duplex module") outside of its case.I do not have an oscilloscope, but I wanted to probe around for a serial port. My solution to this was to wire up a speaker and use it as a probe while the thing boots up. Theory behind it would be if I hear varying tones I could be looking at a serial data pad. And wouldn't you know it after a bit of probing around near a port that was labeled "JTAG" (no JTAG hardware folks, sorry) I found some pads near a chip "25l6405dmi-12G". After asking around on the help forum earlier this week (here) I found it was a chip with an SPI bus on it.Datasheet can be found hereI hooked up my logic analyzer and took a look at what was going on.Yeah I had no clue.When I started I did not know what SPI was. A bit of Google magic and a few read articles and I sort of knew what to do so I just went for it. I have two microcontroller boards, MSP430 and an Arduino. The Arduino was my choice because I foundthis. I went through the code and the datasheet and started typing away some code to attempt to do something simple, just read the manufacturing id.After some tinkering, accidentally causing the printer to fail to boot (just a short, no worries) I had something coming out of the serial out on the Arduino serial monitor.40 00 03Well.... That is not what the datasheet says. Okay well maybe I just have a slightly different chip. Lets just go for dumping a sector of the chips memory and see what I get.Well crap, every dump is different. And dumping is sooooo sloooow. Lets come back at this later....*one day later*I thought I should check to see if the printer still boots. I unwired everything and it boots fine, I didn't bust the chip. So I rewire it and I open the serial monitor to find thisC2 20 17Well what the hell its working? I did one of two things, either when I shorted the chip I caused it to go into some mode where I could not read from it, or my wiring was dodgy and when I rewired the thing I fixed whatever what was wrong. I was ready to write my own SPI library too. So lets try to make a dump of the chip.....Ladies and gentlemen I give you the printers bootloader. 4096 Bytes of it at least, like I said it takes forever to dump so I did a small portion. Even better, I did a few dumps and it is consistent!Well where am I going from here?I have some reverse engineering to do. I want to bypass the message that tells me to insert the "duplex unit" first off, the printer wont let me use it until I do that. I had tried to block the sensors that detect it but I had forgotten to take note of which was the right one so finding it has been a pain. If all goes well I could either just continue to use it just as a scanner or I could repurpose the board to do something fun with the motors. Its got a fully working wifi and bluetooth module in it so we will see what I can do provided I don't brick the thing.First things first though, I need to figure out how to speed up the dumping process because its an 8mb chip and I became impatient after a 3 hour dump try.Overall this has been a pretty neat experience so far.Here is a bit of code for you guys too.Code:// MX25L6405 SPI// Datasheet http://www.macronix.com/QuickPlace/hq/PageLibrary4825740B00298A3B.nsf/$defaultview/3F21BAC2E121E17848257639003A3146/$File/MX25L6405,%203V,%2064Mb,%20v1.3.pdf?OpenElement// Some code from http://arduino.cc/en/Tutorial/SPIEEPROM// Pin Definitions#define DATAOUT 11 // MOSI#define DATAIN 12 // MISO#define SPICLOCK 13 // sck#define SLAVESELECT 10 // ss// Command Definitions#define WREN 0x06 // Sets the (WEL) write enable latch bit#define WRDI 0x04 // Reset the (WEL) write enable latch bit#define RDID 0x9F // Output the manufacturer ID and 2-byte device ID (outputs 3 bytes)#define RDSR 0x05 // To read out the status register (outputs 1 byte)#define WRSR 0x01 // To write new values to the status register#define READ 0x03 // N bytes read out until CS# goes high (requires input address of 3 bytes) (outputs ? bytes)#define FAST_READ 0x0B // (requires input address of 3 bytes, optional fourth?) (outputs ? bytes)#define PARALLEL_MODE 0x55 // Enter and stay in parallel mode until power off#define SE 0x20 // Sector erase (input 3 bytes address)#define SE_ALT 0xD8#define CE 0x60 // Chip erase#define CE_ALT 0xC7#define PP 0x02 // Page Program (input 3 byte address)#define DP 0xB9 // Deep Power Down#define EN4K 0xB5 // Enter 4Kb sector#define EX4K 0xB5 // Exit 4Kb sector#define RDP 0xAB // Release from deep power down#define RES 0xAB // Read electronic id (optional 3 byte input?)#define REMS 0x90 // Read electronic manufacturer & device id (2 byte optional? third byte if 0 will// output manufacturer id first, 1 will output device id first)char fmt; // Some place to sprintf intobyte eeprom_output_data = 0;byte eeprom_input_data = 0;byte clr = 0;byte spi_transfer(volatile byte data){SPDR = data; // Start the transmissionwhile (!(SPSR & (1 << SPIF))) // Wait the end of the transmission{};return SPDR; // return the received byte}void setup(){Serial.begin(9600);pinMode(DATAOUT , OUTPUT);pinMode(DATAIN , INPUT);pinMode(SPICLOCK , OUTPUT);pinMode(SLAVESELECT , OUTPUT);// Data sheet says this must be highdigitalWrite(SLAVESELECT, HIGH);// SPCR = 01010000//interrupt disabled, spi enabled, msb 1st, master, clk low when idle,//sample on leading edge of clk, system clock/4 rate (fastest)SPCR = (1 << SPE)|(1 << MSTR);clr = SPSR;clr = SPDR;delay(1000);}void ReadID() {digitalWrite(SLAVESELECT, LOW);spi_transfer(RDID);int b1 = spi_transfer(0xFF);int b2 = spi_transfer(0xFF);int b3 = spi_transfer(0xFF);digitalWrite(SLAVESELECT, HIGH);sprintf(fmt, "%02X %02X %02X\n", b1, b2, b3);Serial.print(fmt);}void ReadElectronicId() {digitalWrite(SLAVESELECT, LOW);spi_transfer(RES);spi_transfer(0x00);spi_transfer(0x00);spi_transfer(0x00);int b1 = spi_transfer(0xFF);digitalWrite(SLAVESELECT, HIGH);sprintf(fmt, "%02X\n", b1);Serial.print(fmt);}void ReadManufactureId() {digitalWrite(SLAVESELECT, LOW);spi_transfer(REMS);spi_transfer(0x00);spi_transfer(0x00);spi_transfer(0x00);int b1 = spi_transfer(0xFF);int b2 = spi_transfer(0xFF);int b3 = spi_transfer(0xFF);digitalWrite(SLAVESELECT, HIGH);sprintf(fmt, "%02X %02X %02X\n", b1, b2, b3);Serial.print(fmt);}void ReadStatusReg() {digitalWrite(SLAVESELECT, LOW);spi_transfer(RDSR);int b1 = spi_transfer(0xFF);digitalWrite(SLAVESELECT, HIGH);sprintf(fmt, "%02X\n", b1);Serial.print(fmt);}void Dump() {int data;digitalWrite(SLAVESELECT, LOW);spi_transfer(READ); // Address auto increments so only need to do a read instruction once// Address 0x000000spi_transfer(0x00);spi_transfer(0x00);spi_transfer(0x00);for (int i = 0, x = 0; i < 4096; i++, x++) {if (x >= 16) {Serial.print('\n');x = 0;}sprintf(fmt, "%02X ", spi_transfer(0xFF));Serial.print(fmt);}digitalWrite(SLAVESELECT, HIGH);}void loop(){ReadID(); // RDID//ReadElectronicId(); // RES//ReadManufactureId(); // REMS//Dump();while (1) {delay(1000);}}

author: ovnr
author_rank: Frequent Contributor
timestamp: December 16, 2013, 02:50:08 pm
content: Well, good luck with that. You'd be better off getting a dev board for something similar, though.On dumping: First off, you're running the serial port at 9600 baud. You need to get it as fast as possible, because that is your main bottleneck.Also, you should dump it as binary, not hex - that alone will double your speeds. Note that you'll need a client on the PC to dump it to file; a lot of terminal clients don't do super well with binary.

author: mojobojo
author_rank: Newbie
timestamp: December 16, 2013, 04:00:17 pm
content: Quote from: ovnr on December 16, 2013, 02:50:08 pmI would be better off with a dev board, but then there is no fun for me. And I swapped the baud rate to 115200 and its much faster (had no idea that such a thing would be a major bottleneck), I think I will just pop open visual studio and make a quick application to receive the data and output a file.

author: mariush
author_rank: Super Contributor
timestamp: December 16, 2013, 04:26:03 pm
content: Well duh, 9600 bauds is just about 1 KB/s ... set it to 10 times that.If you don't want to transfer binary data, get chunks of 32 bytes, add an extra padding byte and then do a base64 encoding of this sequence, converting it into 44 bytes. Stupid simple to do that in a microcontroller.You can then convert the output from the serial port to binary with a few lines in a php script or whatever flavor of programming language you prefer.

author: mobilegamer999
author_rank: Newbie
timestamp: May 15, 2014, 02:26:46 pm
content: Hey, I upped the serial speed and re-wrote some of it so it should hopefully transfer a lot faster, try this code.NOTE: You will need to set your receive baud to 115200 instead of 9600.Code:// MX25L6405 SPI// Datasheet http://www.macronix.com/QuickPlace/hq/PageLibrary4825740B00298A3B.nsf/$defaultview/3F21BAC2E121E17848257639003A3146/$File/MX25L6405,%203V,%2064Mb,%20v1.3.pdf?OpenElement// Some code from http://arduino.cc/en/Tutorial/SPIEEPROM// Pin Definitions#define DATAOUT 11 // MOSI#define DATAIN 12 // MISO#define SPICLOCK 13 // sck#define SLAVESELECT 10 // ss// Command Definitions#define WREN 0x06 // Sets the (WEL) write enable latch bit#define WRDI 0x04 // Reset the (WEL) write enable latch bit#define RDID 0x9F // Output the manufacturer ID and 2-byte device ID (outputs 3 bytes)#define RDSR 0x05 // To read out the status register (outputs 1 byte)#define WRSR 0x01 // To write new values to the status register#define READ 0x03 // N bytes read out until CS# goes high (requires input address of 3 bytes) (outputs ? bytes)#define FAST_READ 0x0B // (requires input address of 3 bytes, optional fourth?) (outputs ? bytes)#define PARALLEL_MODE 0x55 // Enter and stay in parallel mode until power off#define SE 0x20 // Sector erase (input 3 bytes address)#define SE_ALT 0xD8#define CE 0x60 // Chip erase#define CE_ALT 0xC7#define PP 0x02 // Page Program (input 3 byte address)#define DP 0xB9 // Deep Power Down#define EN4K 0xB5 // Enter 4Kb sector#define EX4K 0xB5 // Exit 4Kb sector#define RDP 0xAB // Release from deep power down#define RES 0xAB // Read electronic id (optional 3 byte input?)#define REMS 0x90 // Read electronic manufacturer & device id (2 byte optional? third byte if 0 will// output manufacturer id first, 1 will output device id first)char fmt; // Some place to sprintf intobyte eeprom_output_data = 0;byte eeprom_input_data = 0;byte clr = 0;byte spi_transfer(volatile byte data){SPDR = data; // Start the transmissionwhile (!(SPSR & (1 << SPIF))) // Wait the end of the transmission{};return SPDR; // return the received byte}void setup(){Serial.begin(115200);pinMode(DATAOUT , OUTPUT);pinMode(DATAIN , INPUT);pinMode(SPICLOCK , OUTPUT);pinMode(SLAVESELECT , OUTPUT);// Data sheet says this must be highdigitalWrite(SLAVESELECT, HIGH);// SPCR = 01010000//interrupt disabled, spi enabled, msb 1st, master, clk low when idle,//sample on leading edge of clk, system clock/4 rate (fastest)SPCR = (1 << SPE)|(1 << MSTR);clr = SPSR;clr = SPDR;delay(1000);}void ReadID() {digitalWrite(SLAVESELECT, LOW);spi_transfer(RDID);int b1 = spi_transfer(0xFF);int b2 = spi_transfer(0xFF);int b3 = spi_transfer(0xFF);digitalWrite(SLAVESELECT, HIGH);Serial.print(fmt);Serial.print(b1, HEX);Serial.print(" ");Serial.print(b2, HEX);Serial.print(" ");Serial.println(b3, HEX);}void ReadElectronicId() {digitalWrite(SLAVESELECT, LOW);spi_transfer(RES);spi_transfer(0x00);spi_transfer(0x00);spi_transfer(0x00);int b1 = spi_transfer(0xFF);digitalWrite(SLAVESELECT, HIGH);Serial.println(b1);}void ReadManufactureId() {digitalWrite(SLAVESELECT, LOW);spi_transfer(REMS);spi_transfer(0x00);spi_transfer(0x00);spi_transfer(0x00);int b1 = spi_transfer(0xFF);int b2 = spi_transfer(0xFF);int b3 = spi_transfer(0xFF);digitalWrite(SLAVESELECT, HIGH);Serial.print(fmt);Serial.print(b1, HEX);Serial.print(" ");Serial.print(b2, HEX);Serial.print(" ");Serial.println(b3, HEX);}void ReadStatusReg() {digitalWrite(SLAVESELECT, LOW);spi_transfer(RDSR);int b1 = spi_transfer(0xFF);digitalWrite(SLAVESELECT, HIGH);sprintf(fmt, "%02X\n", b1);Serial.print(fmt);}void Dump() {int data;digitalWrite(SLAVESELECT, LOW);spi_transfer(READ); // Address auto increments so only need to do a read instruction once// Address 0x000000spi_transfer(0x00);spi_transfer(0x00);spi_transfer(0x00);for (int i = 0, x = 0; i < 4096; i++, x++) {if (x >= 16) {Serial.println();x = 0;}Serial.print(spi_transfer(0xFF));Serial.print(" ");}digitalWrite(SLAVESELECT, HIGH);}void loop(){ReadID(); // RDID//ReadElectronicId(); // RES//ReadManufactureId(); // REMS//Dump();while (1) {delay(1000);}}

author: mobilegamer999
author_rank: Newbie
timestamp: May 15, 2014, 02:31:21 pm
content: Another way you might be able to increase speed would be to send it over as bytes of data instead of ASCII which will net you up to about a 2x speedup.

author: mojobojo
author_rank: Newbie
timestamp: May 15, 2014, 07:44:35 pm
content: Quote from: mobilegamer999 on May 15, 2014, 02:26:46 pmThank you very much for this. I will definitely have to try this out later.

author: manu
author_rank: Regular Contributor
timestamp: May 16, 2014, 12:02:04 pm
content: Hello,In addition to what it was already said to speed up the uart baudrate to 115200+ (230400 is easy to get on Arduino, some tries may be neccessary at 460800 and 921600) and transfer data in binary rather than ASCII, you may speed up the SPI clock from 4MHz to 8MHz (according to the eeprom datasheet, you can set the spi clock up to 50MHz) by using SPI.setClockDivider to 2 :http://arduino.cc/en/Reference/SPISetClockDividerIf you have a more powerful board than the Arduino, you may speed up the process, but hey, what's the fun in that!You could save the dump of the memory with a serial client like Teraterm, but you may already have the necessary tools to do that.I just get a quick look at your post, so excuse me if I had made any mistake. Good luck in you reverse engineering.

author: flextard
author_rank: Contributor
timestamp: September 11, 2014, 01:11:42 am
content: Hi! I cannot stress how happy I am to find this thread!!!I have been given a similar printer that wouldn't print. Somehow I managed to make it print unreliably, but by then I had already disassembled it twice and built interest in the ARM platforms inside.That was September last year, and after a few weeks of getting nowhere (having minimal reverse engineering experience) I put the project on hold.Tonight, however, I grabbed the dusty boards and power supply out of the shelf and started dicking around.In my case (can't remember the printer model), the LCD board is a 'fully' independent system, with ARM processor, 512Mbit DDR400 (http://www.samsung.com/global/business/semiconductor/file/2011/product/2009/10/22/083716ds_k4h51xx38g_fbga_rev10.pdf, 512Mbit flash (H27U518S2CTP-BC), capacitive touch-screen controller, and own power regulation down from the printer's 30VDC input apparently.The 'mainboard' looks just like the one on the pic above, but without the NIC and connector.What bugged me for a while, and still surprises me, is that they used USB as a data link between the boards.So of course I traced the USB TX/RX lanes through the boards and concluded the mainboard's processor hosts the WiFi module and the card reader controller. That controller then hosts the PictBridge USB port and also the USB-Mini that links to the LCD/UI board.Once the printer booted, I quickly swapped the miniA cable for one that was plugged to my Linux desktop (PC to LCD/UI board), and got a RNDIS link!Gave it an IP, pinged fine at .6ms average, port scanned with SSH and Telnet active but filtered, one open port that I have to check again, and another open port which is UPNP 1900 which replied 404 to an http request.A bit confusing, but this is how far I think I am now. Will get some sleep and eagerly wait to hear from you guys!Cheers.

author: BonesMax
author_rank: Newbie
timestamp: September 29, 2019, 04:43:53 pm
content: I am so glad I found this siteIt just so happens I have stripped a Lexmark Laser printer 968 with the idea of having some fun .This one has the 6203 motherboard made in 2007 with a mini usb connecter so looks like I'm going to have some once the rain stops pouring down and I can get the boards inside.I'm hoping yo guys will post more as you go along.