IVE GOTTEN FURTHER and I figured out how MORE of it works. I can say this time for real WHAT I need to do. Its gotten further and on top of that what is wrong I know how to fix.
View attachment 8546233
Ok.. FINALLY. I have implemented it.. FULLY...
And ill tell you FIGURING out this shit was a pain.
The solution was a FUCKING pain but I figured out there are also MULTIPLE chekcsums within the spare area that all need to be calculated.
C:
uint8_t fixed_oob[64];
memset(fixed_oob, 0, sizeof(fixed_oob));
memcpy(fixed_oob, oob, 64);
//FIX THE OOB AREAS. People from russia and the middle east or wherever your neighborhood looks straight out of Call of duty 4 use external NAND programmers to dump the NAND.
//However during acutal DMA reads these bytes are SLIGHTLY different and the NAND controller changes it. Undocumented of course because my life is never easy.
if (fixed_oob[2] == 0x61){
printf("SPECIAL HANLDER!");
switch (logical_block) {
default:
break;
}
}
uint8_t *checksum21000byteoffset = s->flash_data + page_start + 512;
uint8_t *yesANOTHERchecksum = s->flash_data + page_start + 0x700;
uint8_t *yesANOTHERANOTHERchecksum = s->flash_data + page_start + 0x300;
uint8_t ecc_input[8];
uint8_t page_input[256];
uint8_t thousandbyteoffsetpageinput[256];
uint8_t yesANOTHERchecksuminput[256];
uint8_t yesANOTHERANOTHERchecksuminput[256];
memcpy(page_input, page_data, 256);
memcpy(thousandbyteoffsetpageinput, checksum21000byteoffset, 256);
memcpy(yesANOTHERchecksuminput, yesANOTHERchecksum, 256);
memcpy(yesANOTHERANOTHERchecksuminput, yesANOTHERANOTHERchecksum, 256);
printf("Calculated PAGE checksum with 1000 byte offset: %02X %02X %02X\n", checksum21000byteoffset[1], checksum21000byteoffset[2], checksum21000byteoffset[3]);
//12
read_oob_data(fixed_oob, ecc_input);
ecc_bytes_t stored_struct = checksum_or_ecc(ecc_input, 8);
ecc_bytes_t stored_page_struct = checksum_or_ecc(page_input, 256);
ecc_bytes_t stored_1000bytes_inchecksum = checksum_or_ecc(thousandbyteoffsetpageinput, 256);
ecc_bytes_t yesANOTHERchecksum2 = checksum_or_ecc(yesANOTHERchecksuminput, 256);
ecc_bytes_t yesANOTHERANOTHERchecksum2 = checksum_or_ecc(yesANOTHERchecksuminput, 256);
printf("Calculated ECC: %02X %02X %02X\n", stored_struct.b0, stored_struct.b1, stored_struct.b2);
printf("Calculated PAGE checksum: %02X %02X %02X\n", stored_page_struct.b0, stored_page_struct.b1, stored_page_struct.b2);
printf("Calculated PAGE checksum with 1000 byte offset: %02X %02X %02X\n", stored_1000bytes_inchecksum.b0, stored_1000bytes_inchecksum.b1, stored_1000bytes_inchecksum.b2);
printf("Other checksum: %02X %02X %02X\n", yesANOTHERANOTHERchecksum2.b0, yesANOTHERANOTHERchecksum2.b1, yesANOTHERANOTHERchecksum2.b2);
printf("Start off ECC: %02X %02X %02X\n", fixed_oob[0], fixed_oob[1], fixed_oob[2]);
printf("Fixed ECC: %02X %02X %02X\n", fixed_oob[36], fixed_oob[37], fixed_oob[38]);
//copy stored checksum into the block
fixed_oob[36] = stored_struct.b0;
fixed_oob[37] = stored_struct.b1;
fixed_oob[38] = stored_struct.b2;
//copy page checksum to the block.(still in the ECC spare area)
fixed_oob[12] = stored_page_struct.b0;
fixed_oob[13] = stored_page_struct.b1;
fixed_oob[14] = stored_page_struct.b2;
fixed_oob[33] = yesANOTHERANOTHERchecksum2.b0;
fixed_oob[34] = yesANOTHERANOTHERchecksum2.b1;
fixed_oob[35] = yesANOTHERANOTHERchecksum2.b2;
fixed_oob[18] = stored_1000bytes_inchecksum.b0;
fixed_oob[19] = stored_1000bytes_inchecksum.b1;
fixed_oob[20] = stored_1000bytes_inchecksum.b2;
fixed_oob[40] = yesANOTHERchecksum2.b0;
fixed_oob[41] = yesANOTHERchecksum2.b1;
fixed_oob[42] = yesANOTHERchecksum2.b2;
//Now also write to the PHSYICALL buffer so if it gets called via a method that is NOT just reading the spare area it will still show the correct changes.
s->flash_data[page_start + 2048 + 36] = fixed_oob[36];
s->flash_data[page_start + 2048 + 37] = fixed_oob[37];
s->flash_data[page_start + 2048 + 38] = fixed_oob[38];
s->flash_data[page_start + 2048 + 12] = fixed_oob[12];
s->flash_data[page_start + 2048 + 13] = fixed_oob[13];
s->flash_data[page_start + 2048 + 14] = fixed_oob[14];
s->flash_data[page_start + 2048 + 33] = fixed_oob[33];
s->flash_data[page_start + 2048 + 34] = fixed_oob[34];
s->flash_data[page_start + 2048 + 35] = fixed_oob[35];
s->flash_data[page_start + 2048 + 18] = fixed_oob[18];
s->flash_data[page_start + 2048 + 19] = fixed_oob[19];
s->flash_data[page_start + 2048 + 20] = fixed_oob[20];
s->flash_data[page_start + 2048 + 40] = fixed_oob[40];
s->flash_data[page_start + 2048 + 41] = fixed_oob[41];
s->flash_data[page_start + 2048 + 42] = fixed_oob[42];
//IF THE SYSTEM does a FULL page read instead of just a OOB FIRST it will get the OLD checksum...
And yes that also means remaking the checksum function from the bootloader that is psudeocode from ghirda into REAL c code that we can then use. That is what the Checksum or ecc function is. And YES im aware the 1000 byte var name is actually NOT 1000 bytes and yes I was to lazy to change it. And let me tell you FIGURING all of this out on my own is fucking hard, Like I dont even really know what a ground is in electrical engineering. I was not able to read the circut diagrams or anything. Hell I barely understand binary.
My question is WHY is every checksum in the NAND for this TV repair fourm have ALMOST the correct checksum but is slightly off for every single one. Ive noticed this with MULTIPLE different ones. Which makes no sense.. How come ALL of them are slightly off. You may say that these dumpers are idiots and most likely FUCKED something up.. But then how come people have left positive reviews saying they work? How come these dumps WORK despite having WRONG checksums that are slightly off.. Because you NEED all the checksums to be right to simply BOOT UP the TV. I have a theory that MAY explain it.
When the PNX processor does a DMA transfer or direct access memory it may ADD or subtract or using a mathematical algorithm changes the stored checksum values to make it match the calculated checksum. Im assuming the way it calculates is using the hardware IDS of certain parts. Let me give you a example of my theory
So the stored checksum is 01 00 00, but the calculated checksum is 15 00 00. This is a obvious mismatch that if returned and placed into ram to compare will result in a NON match. However if the bytes sent are intercepted and changed before placed in ram to then be compared with the calculated checksum then it would work.
The processor may using the HARDWARE ids of certain parts(Lets say that the NAND id is 10 and the display is 4) to change the number to 15(in this example of course) when doing a DMA read(Not physically writing to the NAND just returning different bytes) and then place it in ram as requested. It uses the stored checksum in its calculation so that if its 02 00 00 or anything other than 01 00 00(in this simplified example of course) it wont work. This would explain WHY these dumps despite having every checksum wrong work perfectly on the TVS, because a external programmer does not follow those rules. And it makes sense WHY it would do such a thing. Its to prevent anti counterfiet parts. Or for example prevent someone who works at the docks or shipping lines from taking apart the TV, removing the high quailty parts inside of it and then putting it shitty parts and sending it back to the shipping process onto shore shelves before selling the High quailty parts. Its a Security measure, and it aligns up really well.
Because we don't know WHAT this pattern is or if this theory is even true, We have to make the DECOMPILED code of the checksum function In c and MANUALLY change every checksum requested by the tvs bootloader to match.
And I can say it passes EVERY checksum perfectly. Except for one type. There are special pages that seem to be different that I need to figure out ones that work differently and in a way that I cant understand.
You see this function. Yes I am TERRIBLE at naming things. But THIS function handles MOST of the checksums. There is a special path that fucks everything up that it can take it seems.
- Take the SPARE area of the page and move it to ram.
- Calculate the checksum of some bytes in ram and compare it against a STORED checksum in the spare area. If it does not match mark it as bad.
- Then scan the ENTIRE page and if the checksum generated MATCHES the stored checksum in the spare area.
- Do this with a few MORE stored checksums in the spared area for different parts of the file if its a SPECIAL page or holds extremely important information
Now this is what this SPECIAL path does
- It takes the SPARE area of the page and moves it into ram.
- Then ZEROS THAT SECTION IN RAM SO THAT ITS ALL FUCKING ZERO.
- And then does the checksum(keep in mind its physically impossiable for the checksum function to generate a area with Pure 0s)
- It then takes the calculated Checksum and then compares it to the memory that is fucking ALL zeros like a stored checksum and then when it does not match it bitches.
What the HELL. Again. 00 00 00 is PHYSICALLY impossible to generate. So why would it ZERO that area in memory and then complain that it does not match the ACUTAL checksum of the page. Obviously I account for this, and I cant just CHANGE the stored Checksum data, because it fucking ZEROS it in ram.
SO what the hell am I suppose to do? What is happening?.. Well this is where I say its time for ANOTHER REVERSE ENGINEERING session. yeaahhhhh.
Alright so this is the function that it calls that ZEROS it.
(Ignore the function name that was just a guess I have no Idea what it does)
Alright so its trying to READ something in the NAND or RAM version of the NAND without spare bytes.
You see these numbers These numbers are mostly bullshit throw em out the window. Ghirda is trying to map it to a address space in the programs memory because it cant understand that the program is trying to talk to the hardware.
So what it does is UNKNOWN at this time. ill get back when I have more free time today and dissect it. But we can make a few guesses.
In the TV there are technically THREE different processor. One is the pnx 5100 which handles image output decoding and image quality adjustments like motion blur reduction and more, The first is the PNX 85xx family processor that handles the main processing and handles the actual TV and linux. And finally a 8045 1980s like processor, this is because the TV when turned off still needs to process remote control commands like Power on, and because of TV regulations stating that they must use a ABSURDLY little amount of power they use a extremely old processor to do that and turn off the other processors when put in standby mode. When a power button gets pressed it sets up everything and then turns on the pnx85xx processor which starts from the bootloader to begin executing instructions and then start up Linux,
Im betting that maybe those 00s its pulling it are data that the 16 bit 8045 processor would fill into RAM before starting the pnx85xx processor as maybe a calculation that cant be stored or maybe security feature.
OK so I found out what ZEROS that part of memory that fucks up the checksum. Its here in this function
Good thing is that with GDB we can tell it to STOP qemu when it lands exactly on this part and then see WHAT its doing. Doing that we see that its reading from a random piece of memory that is almost ALL zeros.. Which means that its suppose to be filled by the hardware with something. What that is I have no clue. We can just fill that part of memory with the checksum that would be calculated so it matches. As for if that works you will have to see because im busy with other things right now and will try it out tomorrow.