Editable PIC Serial Number in HEX File
I've done the serial number (s/n for short) in a way similar to what Joel is describing.  I was using PIC18F4620 and CCS compiler.  The location of s/n in Flash memory was forces to the last 4 bytes.  Since I was using only 80% of Flash, my compiler and linker wouldn't write executable code in the last 4 bytes.

Then I had 2 alternative ways for actually writing s/n into individual units:


CCS in-circuit debugger (ICD) had a feature which allowed to manipulate any location inside Flash.  It was a useful hack.  In later revisions they have removed it, unfortunately.
PIC had a serial link to a PC. s/n was uploaded through it.  Firmware had a routine which would receive the s/n and store it in Flash.


Reply to Joel's comment

Don't know about C18, but CCS compiler comes with library functions write_program_eeprom(...) andread_program_eeprom(...) .  Here's what they look like in assembly.


....................    write_program_eeprom(i_ADDR, iWord);
EF84:  BSF    FD0.6
EF86:  CLRF   FF8
EF88:  MOVLW  7F
EF8A:  MOVWF  FF7
EF8C:  MOVLW  F0
EF8E:  MOVWF  FF6
EF90:  MOVLB  0
EF92:  BRA    EF24
EF94:  MOVLW  F0
EF96:  MOVWF  FF6
EF98:  MOVFF  490,FF5
EF9C:  TBLWT*+
EF9E:  MOVFF  491,FF5
EFA2:  TBLWT*
EFA4:  BCF    FA6.6
EFA6:  BSF    FA6.4
EFA8:  RCALL  EF3C
EFAA:  RCALL  EF3C
EFAC:  CLRF   FF8
EFAE:  CLRF   FF8



....................    iWord = read_program_eeprom(i_ADDR); 
EF60:  CLRF   FF8
EF62:  MOVLW  7F
EF64:  MOVWF  FF7
EF66:  MOVLW  F0
EF68:  MOVWF  FF6
EF6A:  TBLRD*+
EF6C:  MOVF   FF5,W
EF6E:  TBLRD*
EF70:  MOVFF  FF5,03
EF74:  CLRF   FF8
EF76:  MOVLB  4
EF78:  MOVWF  x90
EF7A:  MOVFF  03,491