OK, here's the main routine which seems to hash the PAL with that Quantel string...
http://pastebin.com/dpjXhTL1
EDIT: The "n" from the Quantel string is missing off the end, as IDA didn't seem to let me set it as an ASCII string properly?
I'm fairly sure the string does just end with that "n" character though, as the 0x4E71 that follows it equates to a "NOP", and the 0x4E56 is an "RTS".
As far as we can tell, it does look like the security PAL is simply mapped directly to 0xFD0000 in the 68K address space.
I guess that when reading it using Word-addressing, the bytes would simply be output in the LSB of each access, with the upper bits floating (or tied High or Low)?
...leaving you with 7 characters/bytes
Quote...leaving you with 7 characters/bytes
7 product terms in the PAL - coincidence..?
Addr 0x00AA -- value 0x7F -- inv 0x80
Addr 0x00F4 -- value 0xFB -- inv 0x04
Addr 0x0154 -- value 0x7F -- inv 0x80
Addr 0x0156 -- value 0x7F -- inv 0x80
Addr 0x0222 -- value 0xF7 -- inv 0x08
Addr 0x0228 -- value 0x7F -- inv 0x80
Addr 0x02C0 -- value 0xEF -- inv 0x10
Addr 0x0304 -- value 0x7F -- inv 0x80
Addr 0x03D2 -- value 0x7F -- inv 0x80
Addr 0x040A -- value 0xBF -- inv 0x40
Addr 0x0478 -- value 0xFE -- inv 0x01
Addr 0x04B0 -- value 0x7F -- inv 0x80
Addr 0x04C2 -- value 0xFD -- inv 0x02
Addr 0x055A -- value 0xEF -- inv 0x10
Addr 0x058C -- value 0xEF -- inv 0x10
Addr 0x06AE -- value 0xF7 -- inv 0x08
Addr 0x06E2 -- value 0xF7 -- inv 0x08
Addr 0x0734 -- value 0xF7 -- inv 0x08
Addr 0x0776 -- value 0xF7 -- inv 0x08
Addr 0x079E -- value 0xFB -- inv 0x04
Addr 0x08E4 -- value 0xBF -- inv 0x40
Addr 0x08FC -- value 0xFE -- inv 0x01
Addr 0x0960 -- value 0xFD -- inv 0x02
Addr 0x09C2 -- value 0xFB -- inv 0x04
Addr 0x0A7C -- value 0xEF -- inv 0x10
Addr 0x0ACA -- value 0xDF -- inv 0x20
Addr 0x0B62 -- value 0xFD -- inv 0x02
Addr 0x0B86 -- value 0xFE -- inv 0x01
Addr 0x0C4A -- value 0xF5 -- inv 0x0A
Addr 0x0C8E -- value 0xFB -- inv 0x04
Addr 0x0D02 -- value 0xBF -- inv 0x40
Addr 0x0D1E -- value 0xF7 -- inv 0x08
Addr 0x0D78 -- value 0xFB -- inv 0x04
Addr 0x0DEC -- value 0xBF -- inv 0x40
Addr 0x0E60 -- value 0xFB -- inv 0x04
Addr 0x0ECC -- value 0xDF -- inv 0x20
Addr 0x0F5E -- value 0xEF -- inv 0x10
Addr 0x103E -- value 0xEF -- inv 0x10
Addr 0x10F8 -- value 0xDF -- inv 0x20
Addr 0x1136 -- value 0xFD -- inv 0x02
Addr 0x121E -- value 0xFD -- inv 0x02
Addr 0x1232 -- value 0xFE -- inv 0x01
Addr 0x12D2 -- value 0xBF -- inv 0x40
Addr 0x1312 -- value 0xFE -- inv 0x01
Addr 0x1326 -- value 0xFB -- inv 0x04
Addr 0x1508 -- value 0xEF -- inv 0x10
Addr 0x15CC -- value 0xDF -- inv 0x20
Addr 0x1704 -- value 0xFD -- inv 0x02
Addr 0x1714 -- value 0xBF -- inv 0x40
Addr 0x17E6 -- value 0xFE -- inv 0x01
Addr 0x1A98 -- value 0xDF -- inv 0x20
Addr 0x1BE0 -- value 0xBF -- inv 0x40
Addr 0x1CC6 -- value 0xFE -- inv 0x01
Addr 0x1F76 -- value 0xDF -- inv 0x20
Addr 0x2040 -- value 0xDF -- inv 0x20
Number of addresses which returned non-FF bytes: 55
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
int main(void)
{
bool A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12,A13;
bool nD0,nD1,nD2,nD3,nD4,nD5,nD6,nD7;
FILE *fp;
unsigned int nNonFFs = 0;
fp = fopen("quansec.bin", "wb");
for (uint32_t addr = 0; addr < 0x4000; addr+=2)
{
uint8_t ch;
A1 = (addr & 2);
A2 = !(addr & 4); // Every single GAL we have samples of seems to have A2 set as inverted in hardware
A3 = (addr & 8);
A4 = (addr & 16);
A5 = (addr & 32);
A6 = (addr & 64);
A7 = (addr & 128);
A8 = (addr & 256);
A9 = (addr & 512);
A10 = (addr & 1024);
A11 = (addr & 2048);
A12 = (addr & 4096);
A13 = (addr & 8192);
#include "12360.c"
ch =
(!nD7 ? 128 : 0) |
(!nD6 ? 64 : 0) |
(!nD5 ? 32 : 0) |
(!nD4 ? 16 : 0) |
(!nD3 ? 8 : 0) |
(!nD2 ? 4 : 0) |
(!nD1 ? 2 : 0) |
(!nD0 ? 1 : 0);
if (ch != 0xFF) {
printf("Addr 0x%04X -- value 0x%02X -- inv 0x%02X\n", addr, ch, ch ^ 0xFF);
nNonFFs++;
}
fwrite(&ch, 1, 1, fp);
}
fclose(fp);
printf("\n\nNumber of addresses which returned non-FF bytes: %u\n", nNonFFs);
}
/*
; JED2EQN -- JEDEC file to Boolean Equations disassembler (Version V063)
; Copyright (c) National Semiconductor Corporation 1990-1993
; Disassembled from 12360.JED. Date: 12-2-116
;$GALMODE MEDIUM
chip 12360 GAL20V8
A13=1 A12=2 A11=3 A10=4 A9=5 A8=6 A7=7 A6=8 A5=9 A4=10 A3=11 GND=12
!A2=13 A1=14 D7=15 D6=16 D5=17 D4=18 D3=19 D2=20 D1=21
D0=22 ALEC=23 VCC=24
@ues 0000000000000000
equations
*/
bool nD0 = (!A12 && !A13 && !A11 && A10 && !A9 && !A8 && !A7 && A6 && A5 && A4 && !A1 && A3 && A2)
|| (!A12 && !A13 && A11 && !A10 && !A9 && !A8 && A7 && A6 && A5 && A4 && !A1 && A3 && !A2)
|| (!A12 && !A13 && A11 && !A10 && A9 && A8 && A7 && !A6 && !A5 && !A4 && A1 && !A3 && !A2)
|| (A12 && !A13 && !A11 && !A10 && A9 && !A8 && !A7 && !A6 && A5 && A4 && A1 && !A3 && A2)
|| (A12 && !A13 && !A11 && !A10 && A9 && A8 && !A7 && !A6 && !A5 && A4 && A1 && !A3 && A2)
|| (A12 && !A13 && !A11 && A10 && A9 && A8 && A7 && A6 && A5 && !A4 && A1 && !A3 && !A2)
|| (A12 && !A13 && A11 && A10 && !A9 && !A8 && A7 && A6 && !A5 && !A4 && A1 && !A3 && !A2);
//D0.oe = !ALEC
bool nD1 = (!A12 && !A13 && !A11 && A10 && !A9 && !A8 && A7 && A6 && !A5 && !A4 && A1 && !A3 && A2)
|| (!A12 && !A13 && A11 && !A10 && !A9 && A8 && !A7 && A6 && A5 && !A4 && !A1 && !A3 && A2)
|| (!A12 && !A13 && A11 && !A10 && A9 && A8 && !A7 && A6 && A5 && !A4 && A1 && !A3 && A2)
|| (!A12 && !A13 && A11 && A10 && !A9 && !A8 && !A7 && A6 && !A5 && !A4 && A1 && A3 && A2)
|| (A12 && !A13 && !A11 && !A10 && !A9 && A8 && !A7 && !A6 && A5 && A4 && A1 && !A3 && !A2)
|| (A12 && !A13 && !A11 && !A10 && A9 && !A8 && !A7 && !A6 && !A5 && A4 && A1 && A3 && !A2)
|| (A12 && !A13 && !A11 && A10 && A9 && A8 && !A7 && !A6 && !A5 && !A4 && !A1 && !A3 && !A2);
//D1.oe = !ALEC
bool nD2 = (!A12 && !A13 && !A11 && !A10 && !A9 && !A8 && A7 && A6 && A5 && A4 && !A1 && !A3 && !A2)
|| (!A12 && !A13 && !A11 && A10 && A9 && A8 && A7 && !A6 && !A5 && A4 && A1 && A3 && !A2)
|| (!A12 && !A13 && A11 && !A10 && !A9 && A8 && A7 && A6 && !A5 && !A4 && A1 && !A3 && A2)
|| (!A12 && !A13 && A11 && A10 && !A9 && !A8 && A7 && !A6 && !A5 && !A4 && A1 && A3 && !A2)
|| (!A12 && !A13 && A11 && A10 && !A9 && A8 && !A7 && A6 && A5 && A4 && !A1 && A3 && A2)
|| (!A12 && !A13 && A11 && A10 && A9 && !A8 && !A7 && A6 && A5 && !A4 && !A1 && !A3 && A2)
|| (A12 && !A13 && !A11 && !A10 && A9 && A8 && !A7 && !A6 && A5 && !A4 && A1 && !A3 && !A2);
//D2.oe = !ALEC
bool nD3 = (!A12 && !A13 && !A11 && !A10 && A9 && !A8 && !A7 && !A6 && A5 && !A4 && A1 && !A3 && A2)
|| (!A12 && !A13 && !A11 && A10 && A9 && !A8 && A7 && !A6 && A5 && !A4 && A1 && A3 && !A2)
|| (!A12 && !A13 && !A11 && A10 && A9 && !A8 && A7 && A6 && A5 && !A4 && A1 && !A3 && A2)
|| (!A12 && !A13 && !A11 && A10 && A9 && A8 && !A7 && !A6 && A5 && A4 && !A1 && !A3 && !A2)
|| (!A12 && !A13 && !A11 && A10 && A9 && A8 && !A7 && A6 && A5 && A4 && A1 && !A3 && !A2)
|| (!A12 && !A13 && A11 && A10 && !A9 && !A8 && !A7 && A6 && !A5 && !A4 && A1 && A3 && A2)
|| (!A12 && !A13 && A11 && A10 && !A9 && A8 && !A7 && !A6 && !A5 && A4 && A1 && A3 && !A2);
//D3.oe = !ALEC
bool nD4 = (!A12 && !A13 && !A11 && !A10 && A9 && !A8 && A7 && A6 && !A5 && !A4 && !A1 && !A3 && A2)
|| (!A12 && !A13 && !A11 && A10 && !A9 && A8 && !A7 && A6 && !A5 && A4 && A1 && A3 && A2)
|| (!A12 && !A13 && !A11 && A10 && !A9 && A8 && A7 && !A6 && !A5 && !A4 && !A1 && A3 && !A2)
|| (!A12 && !A13 && A11 && !A10 && A9 && !A8 && !A7 && A6 && A5 && A4 && !A1 && A3 && !A2)
|| (!A12 && !A13 && A11 && A10 && A9 && A8 && !A7 && A6 && !A5 && A4 && A1 && A3 && !A2)
|| (A12 && !A13 && !A11 && !A10 && !A9 && !A8 && !A7 && !A6 && A5 && A4 && A1 && A3 && !A2)
|| (A12 && !A13 && !A11 && A10 && !A9 && A8 && !A7 && !A6 && !A5 && !A4 && !A1 && A3 && A2);
//D4.oe = !ALEC;
bool nD5 = (!A12 && !A13 && A11 && !A10 && A9 && !A8 && A7 && A6 && !A5 && !A4 && A1 && A3 && A2)
|| (!A12 && !A13 && A11 && A10 && A9 && !A8 && A7 && A6 && !A5 && !A4 && !A1 && A3 && !A2)
|| (A12 && !A13 && !A11 && !A10 && !A9 && !A8 && A7 && A6 && A5 && A4 && !A1 && A3 && A2)
|| (A12 && !A13 && !A11 && A10 && !A9 && A8 && A7 && A6 && !A5 && !A4 && !A1 && A3 && !A2)
|| (A12 && !A13 && A11 && !A10 && A9 && !A8 && A7 && !A6 && !A5 && A4 && !A1 && A3 && A2)
|| (A12 && !A13 && A11 && A10 && A9 && A8 && !A7 && A6 && A5 && A4 && A1 && !A3 && !A2)
|| (!A12 && A13 && !A11 && !A10 && !A9 && !A8 && !A7 && A6 && !A5 && !A4 && !A1 && !A3 && A2);
//D5.oe = !ALEC
bool nD6 = (!A12 && !A13 && !A11 && A10 && !A9 && !A8 && !A7 && !A6 && !A5 && !A4 && A1 && A3 && A2)
|| (!A12 && !A13 && A11 && !A10 && !A9 && !A8 && A7 && A6 && A5 && !A4 && !A1 && !A3 && !A2)
|| (!A12 && !A13 && A11 && A10 && !A9 && A8 && !A7 && !A6 && !A5 && !A4 && A1 && !A3 && A2)
|| (!A12 && !A13 && A11 && A10 && !A9 && A8 && A7 && A6 && A5 && !A4 && !A1 && A3 && !A2)
|| (A12 && !A13 && !A11 && !A10 && A9 && !A8 && A7 && A6 && !A5 && A4 && A1 && !A3 && A2)
|| (A12 && !A13 && !A11 && A10 && A9 && A8 && !A7 && !A6 && !A5 && A4 && !A1 && !A3 && !A2)
|| (A12 && !A13 && A11 && !A10 && A9 && A8 && A7 && A6 && A5 && !A4 && !A1 && !A3 && A2);
//D6.oe = !ALEC
bool nD7 = (!A12 && !A13 && !A11 && !A10 && !A9 && !A8 && A7 && !A6 && A5 && !A4 && A1 && A3 && A2)
|| (!A12 && !A13 && !A11 && !A10 && !A9 && A8 && !A7 && A6 && !A5 && A4 && !A1 && !A3 && !A2)
|| (!A12 && !A13 && !A11 && !A10 && !A9 && A8 && !A7 && A6 && !A5 && A4 && A1 && !A3 && !A2)
|| (!A12 && !A13 && !A11 && !A10 && A9 && !A8 && !A7 && !A6 && A5 && !A4 && !A1 && A3 && A2)
|| (!A12 && !A13 && !A11 && !A10 && A9 && A8 && !A7 && !A6 && !A5 && !A4 && !A1 && !A3 && !A2)
|| (!A12 && !A13 && !A11 && !A10 && A9 && A8 && A7 && A6 && !A5 && A4 && A1 && !A3 && A2)
|| (!A12 && !A13 && !A11 && A10 && !A9 && !A8 && A7 && !A6 && A5 && A4 && !A1 && !A3 && A2);
//D7.oe = !ALEC
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
int main(void)
{
bool A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12,A13;
bool nD0,nD1,nD2,nD3,nD4,nD5,nD6,nD7;
uint16_t Addr0[8];
uint8_t Addr1[8] = {7,7,7,7,7,7,7,0};
uint8_t InputCryptoString[64];
// LOC_52EE loop
// Start by clearing Addr0 to 0xFFFF
for (size_t n=0; n<8; n++) {
Addr0[n] = 0xFFFF;
}
// 52FC:
InputCryptoString[7] = 0; // some form of status response byte I guess?
uint16_t m68k_d1 = 0; // some kind of loop counter?
uint32_t m68k_d7 = 0; // check sum?
for (uint32_t addr = 0; addr < 0x4000; addr+=2) // This is "LoopD0"
{
A1 = (addr & 2);
A2 = !(addr & 4); // Every single GAL we have samples of seems to have A2 set as inverted in hardware
A3 = (addr & 8);
A4 = (addr & 16);
A5 = (addr & 32);
A6 = (addr & 64);
A7 = (addr & 128);
A8 = (addr & 256);
A9 = (addr & 512);
A10 = (addr & 1024);
A11 = (addr & 2048);
A12 = (addr & 4096);
A13 = (addr & 8192);
#include "12360.c"
// Read from PAL into D2
uint8_t m68k_d2 =
(!nD7 ? 128 : 0) |
(!nD6 ? 64 : 0) |
(!nD5 ? 32 : 0) |
(!nD4 ? 16 : 0) |
(!nD3 ? 8 : 0) |
(!nD2 ? 4 : 0) |
(!nD1 ? 2 : 0) |
(!nD0 ? 1 : 0);
// Invert the value
m68k_d2 ^= 0xFF;
if (m68k_d2 == 0) {
goto nextd0_d1plusplus; // TODO: Just put "d1++; continue;" here and leave that d1++ down below
}
for (uint8_t m68k_d3 = 7; m68k_d3 != 0xFF; m68k_d3--) {
if (!(m68k_d2 & (1<<m68k_d3))) {
continue;
}
//uint16_t m68k_d4 = m68k_d3 << 1;
uint16_t m68k_d5 = Addr0[m68k_d3];
Addr0[m68k_d3] = m68k_d1;
// 005328 NEG.W D5
m68k_d5 = -m68k_d5;
// ADD.W D1, D5
m68k_d5 += m68k_d1;
// SUBQ #1, D5
m68k_d5--;
// CMPI.W #$800, D5
if (m68k_d5 >= 0x800) {
printf("CleanVarsAndReturn :(\n");
return -1;
}
// BTST #$A, D5 --> and by 1024
if (m68k_d5 & 0x400) {
// >= 2KB
InputCryptoString[7] ^= m68k_d3;
printf("More than 2KB; CleanVarsAndReturn :(\n");
return -1;
}
// lessThan2KB:
// 005344:
uint16_t m68k_d6 = Addr1[m68k_d3];
if (m68k_d6 >= 7) {
printf("Addr1 >= 7, CleanVarsAndReturn :(\n");
return -1;
}
// 005350:
if (m68k_d5 & 0x200) {
// 005356: bset d3, (a0,d6.w)
InputCryptoString[m68k_d6] |= (1<<m68k_d3);
} else {
// 00535C: bclr d3, (a0,d6.w)
InputCryptoString[m68k_d6] &= ~(1<<m68k_d3);
}
// 005350:
m68k_d6 <<= 3;
m68k_d6 += m68k_d3;
if (m68k_d5 & 0x100) {
// 00536A:
m68k_d7 |= 1<<m68k_d6;
}
// 00536C:
InputCryptoString[m68k_d6 + 8] = m68k_d5;
Addr1[m68k_d3]++;
}
nextd0_d1plusplus:
m68k_d1++;
}
printf("DONE -->\n");
printf("Addr0: ");
for (size_t i=0; i<8; i++) {
printf("%u ", Addr0[i]);
}
printf("\n");
printf("Addr1: ");
for (size_t i=0; i<8; i++) {
printf("%u ", Addr1[i]);
}
printf("\n");
}
Yes i have a TL866, thats what i used to read the GAL/PALs.
I can try wiring it up as a rom, a couple of people mentioned doing that before
I can send you one of the ones i can read if you want, they are only useful to this exercise as i dont have the software keys for these serials anyway
AFS V-series Diagnostics and Painting, version "V8-18-001 Paintbox"
Built on 3-MAY-1994 at 11:21:28.09, file version V7.01
Software generated for Paintbox 1-00
NTSC standard 2133, 1940
Serial number: 10352, 12742
Description : PAINTBOX
EDIT: Now I've done some more mods to my tool... I can tell you what the PALs are for!
12360: Harriet
15462-E: PFrame Paintbox
17624-1: Editbox
17624-2: Editbox
17624-N: Network
awesome work philpem!
your results tie in to a couple of other small points i was about to make...
firstly the two serial numbers. On these machines they have a machine serial number and a group serial number. I think so a group of machines can share a license key. But if there is no group then these two numbers are the same.
secondly, during boot of the main OS just after it reports the serial numbers there is a description in caps, on machine 10352 it's 'PAINTBOX', it's quite possible this is the same text you found that said 'HARRIET'
you can see a short excerpt from the console output here:Code: [Select]AFS V-series Diagnostics and Painting, version "V8-18-001 Paintbox"
Built on 3-MAY-1994 at 11:21:28.09, file version V7.01
Software generated for Paintbox 1-00
NTSC standard 2133, 1940
Serial number: 10352, 12742
Description : PAINTBOX
i am going to have a play wiring the PAL up as a PROM and see if i can get my TL866 to read the PAL that is coded with 12360
EDIT: Now I've done some more mods to my tool... I can tell you what the PALs are for!
12360: Harriet
15462-E: PFrame Paintbox
17624-1: Editbox
17624-2: Editbox
17624-N: Network
Harriet and Paintbox are essentially the same thing, a Harriet is a Paintbox with all the extras. Editbox is their non-linear digital editing machine
many paintboxes, editboxes etc had a network or other interface board fitted inside the machine, these have their own SecurityPAL on which is why there is a separate PAL for that. I am not sure why there are two PALs for the editbox though but i don't know too much about these systems so...
I don't see why the SecurityPAL couldn't implement some kind of FSM since there is a CPU involved. It could certainly be coded as an 8 bit wide LFSR with the CPU mangling the address lines based on previous output from the PAL.
If I had a working system, I would be connecting a logic analyzer to the PAL. I would think that, given the possibility of a LFSR in uC code, trying to simply decode the PROM would be an exercise in futility.
I don't see why the SecurityPAL couldn't implement some kind of FSM since there is a CPU involved. It could certainly be coded as an 8 bit wide LFSR with the CPU mangling the address lines based on previous output from the PAL.
/d0 = /a12 ...
This means, d0 is low if the logic equation is true? But if it's not the case, there are High-Z?philpem,
attached is a .bin of the 12360 PAL as read as a TMS2764 EPROM, it does appear to be a match for your file but if you could double check?
i am quite shocked my wiring worked TBH!
Loaded 12360_Read_As-ROM.bin
DONE -->
Crypted string: --0H--0HHARRIET --------------------------------
Hex crypted string:
00 00 30 48 00 00 30 48 48 41 52 52 49 45 54 20
20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Crypted string checksum (D7 loword): 5176
Clear string checksum (D7 hiword): 1319
AddrBitLastSeen aka Addr0 (internal): 3683 2946 2451 1679 2692 4128 3568 600
BitActiveCounter aka Addr1 - should all be 7 (internal): 7 7 7 7 7 7 7 7
Next up is "How the bleeding hell do I make a PROM to satisfy this thing?!"
Next up is "How the bleeding hell do I make a PROM to satisfy this thing?!"
Brute force ?
There are a lot of permutations but a lot of them can be eliminated.
Looks like the scheme was designed to make it hard, but computing power has come rather a long way since it was designed.
Obvious constraints :
One bit at a time, obviously
Only 7 of 8192 possible addresses can be active.
That's rather a big space, but I'm sure there must be ways to eliminate some based on other constraints.