Hacking the FingerPrint Sensor

 

 

This is a summary of my progress in hacking the FIM5360N-LV fingerprint scanner.  I am not yet able to register fingerprints, but otherwise feel very close to having it work. I solved some issues that I couldn’t find answers for online, so I wanted to document this in detail for future reference.

I used a MAX3232  and an Arduino USB to FTDI to connect the device to my computer like this:

A USB to FTDI on the right goes through the MAX3232 to the JP2 connector of the fingerprint sensor, where the red and black wires happen to be counterintuitively reversed

 

Serial communication to the device requires one start byte, followed by 24 bytes of data that comprise the Header. This consists of four parts, the first is the four byte Command, then four bytes for Parameter 1, then four bytes for Parameter 2, four bytes for Data Size, four bytes for Error Code, and four bytes for the Header Checksum. The data bytes and four bytes for data checksum follow in some communications. All of this is explained in the serial protocol datasheet . The byte sequence looks like this:

byte [] CMD_REQUEST_CONNECTION   = {0x7e, // Start Byte
0x00, 0x00, 0x00, 0x01, // Command
0x00, 0x00, 0x00, 0x00, // Parameter 1
0x00, 0x00, 0x00, 0x00, // Parameter 2
0x00, 0x00, 0x00, 0x00, // Data Size
0x00, 0x00, 0x00, 0x00, // Error Code
0x00, 0x00, 0x00, 0x01, // Header Checksum
};

I wrote a processing code for communicating with the sensor. With it I am able to activate it, and put it in auto mode where it is scanning my finger, but something is still wrong with my attempts to register fingerprints. I think the problem is in the way I am sending the ‘data’ part. I don’t understand why I am sending data if there is nothing but retrieval from the device.

Here is the sketch:

/*
This code interacts with the FIM5360N-LV Fingerprint Scanner
My setup is a USB - serial via MAX3232 to the FIM5360N-LV.
more info at http://www.mariarabinovich.com/blog/archives/1061
Command info and responses are explained in the serial protocol
datasheet: http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Sensors/Biometric/EN%20FIM%20ComProtocol%20V2.01.pdf
Serial protocol I'm using is explained in this datasheet.
Only some of the commands are in this code.

KeyPresses Activate the following commands:

c- CMD_REQUEST_CONNECTION
m- CMD_ENTER_MASTER_MODE2
n- CMD_LEAVE_MASTER_MODE
l- CMD_GET_FP_LIST2
e- CMD_CHG_EMULMODE

v- CMD_VERIFY_FP (0x11)
r- CMD_REGISTER_FP (0x33)
t- CMD_REGISTER_FP_SECOND_CAPTURE (0x33)
i- CMD_IDENTIFY_FP (0x12)
a- CMD_START_AUTO_IDENTIFY (0x1A)
s- CMD_STOP_AUTO_IDENTIFY (0x1A)

*/

import processing.serial.*;
int counter=0;
Serial myPort;

//DEFINE THE COMMANDS BY STORING THEM IN ARRAYS:
byte [] CMD_REQUEST_CONNECTION   = {0x7e,
0x00, 0x00, 0x00, 0x01, // Command
0x00, 0x00, 0x00, 0x00, // Parameter 1
0x00, 0x00, 0x00, 0x00, // Parameter 2
0x00, 0x00, 0x00, 0x00, // Data Size
0x00, 0x00, 0x00, 0x00, // Error Code
0x00, 0x00, 0x00, 0x01, // Header Checksum
};

byte [] CMD_ENTER_MASTER_MODE2   = {0x7e,
0x00, 0x00, 0x00, 0x2F, // Command
0x00, 0x00, 0x00, 0x03, // Parameter 1 is set here to NO AUTHORIZATION
0x00, 0x00, 0x00, 0x00, // Parameter 2
0x00, 0x00, 0x00, 0x00, // Data Size
0x00, 0x00, 0x00, 0x00, // Error Code
0x00, 0x00, 0x00, 0x32, // Header Checksum
};

byte [] CMD_LEAVE_MASTER_MODE   = {0x7e,
0x00, 0x00, 0x00, 0x26, // Command
0x00, 0x00, 0x00, 0x00, // Parameter 1
0x00, 0x00, 0x00, 0x00, // Parameter 2
0x00, 0x00, 0x00, 0x00, // Data Size
0x00, 0x00, 0x00, 0x00, // Error Code
0x00, 0x00, 0x00, 0x26, // Header Checksum
};

byte [] CMD_GET_FP_LIST2   = {0x7e,
0x00, 0x00, 0x00, 0x30, // Command
0x00, 0x00, 0x00, 0x00, // Parameter 1 // this lists the user count and IDs, use 0x01 for just the count
0x00, 0x00, 0x00, 0x00, // Parameter 2
0x00, 0x00, 0x00, 0x00, // Data Size
0x00, 0x00, 0x00, 0x00, // Error Code
0x00, 0x00, 0x00, 0x30, // Header Checksum
};

/* Emulation Modes define how the general input/output pins (GPIO's)
are used. I think FIM30 is the way to go:

VCC
RX
TX
0  Output high when authentication, registration and deletion are succeeded.
1  Output high when authentication, registration and deletion are failed.
2  Do registration when the port level goes from high to low.
3  Do deletion when the port level goes from high to low.
4  Do identification when the port level goes from high to low.
GRND
*/

byte [] CMD_CHG_EMULMODE    = {0x7e,
0x00, 0x00, 0x00, 0x51, // Command
0x00, 0x00, 0x00, 0x02, // Parameter 1 // this sets it to FIM30 emulation mode
0x00, 0x00, 0x00, 0x00, // Parameter 2
0x00, 0x00, 0x00, 0x00, // Data Size
0x00, 0x00, 0x00, 0x00, // Error Code
0x00, 0x00, 0x00, 0x53, // Header Checksum
};

//BUT USE IDENTIFY I THINK
byte [] CMD_VERIFY_FP      = {0x7e,
0x00, 0x00, 0x00, 0x11, // Command
0x00, 0x00, 0x00, 0x00, // Parameter 1 // no password
0x00, 0x00, 0x00, 0x00, // Parameter 2 //packet index
0x00, 0x00, 0x00, 0x0A, // Data Size //fpid(10)+password(0)
0x00, 0x00, 0x00, 0x00, // Error Code
0x00, 0x00, 0x00, 0x1B, // Header Checksum
};

byte [] CMD_IDENTIFY_FP      = {0x7e,
0x00, 0x00, 0x00, 0x12, // Command
0x00, 0x00, 0x00, 0x00, // Parameter 1
0x00, 0x00, 0x00, 0x00, // Parameter 2
0x00, 0x00, 0x00, 0x00, // Data Size
0x00, 0x00, 0x00, 0x00, // Error Code
0x00, 0x00, 0x00, 0x12, // Header Checksum
}; // response  12, 01, 00, 0A, 00, 1D means
   //successful and user ID is returned

/*FIM30 emulation mode: (p91)
LENGTH_OF_FPID = 10 //this is a string, so the last byte is 0x00, available size of password is LENGTH-OF-PASSWD-1
LENGTH_OF_PASSWD = 16
LENGTH_OF_TEMPLATE_HEADER=0
LENGTH_OF_TEMPLATE_DATA = 400*/

//automatically tries to identify if place a finger on device:
byte [] CMD_START_AUTO_IDENTIFY       = {0x7e,
0x00, 0x00, 0x00, 0x1A, // Command
0x00, 0x00, 0x00, 0x01, // Parameter 1  //0=stop, 1=start
0x00, 0x00, 0x00, 0x00, // Parameter 2
0x00, 0x00, 0x00, 0x00, // Data Size
0x00, 0x00, 0x00, 0x00, // Error Code
0x00, 0x00, 0x00, 0x1B, // Header Checksum //1A or 1B
};
byte [] CMD_STOP_AUTO_IDENTIFY       = {0x7e,
0x00, 0x00, 0x00, 0x1A, // Command
0x00, 0x00, 0x00, 0x00, // Parameter 1  //0=stop, 1=start
0x00, 0x00, 0x00, 0x00, // Parameter 2
0x00, 0x00, 0x00, 0x00, // Data Size
0x00, 0x00, 0x00, 0x00, // Error Code
0x00, 0x00, 0x00, 0x1A, // Header Checksum //1A or 1B
};
/*returns CMD_AUTO_IDENTIFY_RESULT:
0x1B
0x01(0r2=failed)
0
size of FPID
error code
FPID if successful, else, 0
*/

//IN MASTER MODE ONLY: RUN REGISTER in two parts:
byte [] CMD_REGISTER_FP      = {0x7e,
0x00, 0x00, 0x00, 0x33, // Command
0x00, 0x00, 0x00, 0x00, // Parameter 1 //  0= registers user 1=master
0x00, 0x00, 0x00, 0x10, // Parameter 2 // auto generate id
0x00, 0x00, 0x00, 0x1A, // Data Size //sends 16 bytes null-data as password and length of FPID
0x00, 0x00, 0x00, 0x00, // Error Code
0x00, 0x00, 0x00, 0x5D, // Header Checksum
//data:
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00 //Data Checksum
};
//If the first time returns success (param1= 1) send a second capture:
byte [] CMD_REGISTER_FP_SECOND_CAPTURE  = {0x7e,
0x00, 0x00, 0x00, 0x33, // Command //same as above, 2nd run
0x00, 0x00, 0x00, 0x00, // Parameter 1 // 0=user 1=master
0x00, 0x00, 0x00, 0x01, // Parameter 2 // get read and save
0x00, 0x00, 0x00, 0x1A, // Data Size //sends 16 bytes null-data as password
0x00, 0x00, 0x00, 0x00, // Error Code
0x00, 0x00, 0x00, 0x4E, // Header Checksum
//data:
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00 //Data Checksum
};
//Should get another param1=1, param2=0B. . . checksum3F
//Then User is Added to the Device

/*Some commands that may be useful:

CMD_SET_MASTER (0x24)
CMD_READ_USER_DATA (0x2B)
CMD_WRITE_USER_DATA (0x2C)
CMD_READ_LOG_DATA2 (0x32)
CMD_GET_IMAGE_QUALITY (0x68)
This command returns image quality after using the following commands.
CMD_VERIFY_FP, CMD_IDENTIFY_FP, CMD_INSTANT_MATCHING
CMD_GET_TEMPLATE, CMD_GET_FP_IMAGE2, CMD_ENTER_MASTER_MODE2
CMD_REGISTER_FP, CMD_CHANGE_FP, CMD_FEGISTER_MULTI_F*/

void setup(){
println(Serial.list());
myPort=new Serial(this, Serial.list()[0], 9600);
}

void draw(){
}

void serialEvent(Serial myPort){

counter++;
int inByte=myPort.read();
background(255);

//CONVERTING DEC INPUT TO HEX TO MATCH SERIAL DATASHEET
String inByteString=hex(inByte);
String noZeros = inByteString.substring(6);
println(noZeros);

//ADDING LABELS JUST TO READ EASIER:

if(counter==1){println("Command:");}
else if (counter==5){println("Param 1:");}
 else if (counter==9){println("Param 2:");}
  else if (counter==13){println("Data Size:");}
   else if (counter==17){println("Error Code:");}
    else if (counter==21){println("Header Checksum:");}
     else if (counter==25){println("Data::");}
//if(counter==24){counter=0;}
}

void keyReleased(){
println();
counter=0;
println("Begin response:");

//CMD_REQUEST_CONNECTION 0x36
if (key=='c'){
for (int i=0; i<CMD_REQUEST_CONNECTION .length; i++){
myPort.write(CMD_REQUEST_CONNECTION [i]);
println("CMD_REQUEST_CONNECTION");}}  

//Command to Enter Master Mode: 0X2F
if (key=='m'){
for (int i=0; i<CMD_ENTER_MASTER_MODE2.length; i++){
myPort.write(CMD_ENTER_MASTER_MODE2[i]);
println("CMD_ENTER_MASTER_MODE2");}}  

//Command to Leave Master Mode: 0x26
if (key=='n'){
for (int i=0; i<CMD_LEAVE_MASTER_MODE.length; i++){
myPort.write(CMD_LEAVE_MASTER_MODE[i]);
println("CMD_LEAVE_MASTER_MODE");}} 

//Command to Get Fingerprint List: 0x30
if (key=='l'){
for (int i=0; i<CMD_GET_FP_LIST2.length; i++){
myPort.write(CMD_GET_FP_LIST2[i]);
println("CMD_GET_FP_LIST2");}} 

if (key=='e'){
for (int i=0; i<CMD_CHG_EMULMODE.length; i++){
myPort.write(CMD_CHG_EMULMODE[i]);
println("CMD_CHG_EMULMODE");}} 

if (key=='v'){
for (int i=0; i<CMD_VERIFY_FP.length; i++){
myPort.write(CMD_VERIFY_FP[i]);
println("CMD_VERIFY_FP");}} 

if (key=='r'){
for (int i=0; i<CMD_REGISTER_FP.length; i++){
myPort.write(CMD_REGISTER_FP[i]);
println("CMD_REGISTER_FP");}} 

if (key=='t'){
for (int i=0; i<CMD_REGISTER_FP_SECOND_CAPTURE.length; i++){
myPort.write(CMD_REGISTER_FP_SECOND_CAPTURE[i]);
println("CMD_REGISTER_FP_SECOND_CAPTURE");}} 

if (key=='i'){
for (int i=0; i<CMD_IDENTIFY_FP.length; i++){
myPort.write(CMD_IDENTIFY_FP[i]);
println("CMD_IDENTIFY_FP");}} 

if (key=='a'){
for (int i=0; i<CMD_START_AUTO_IDENTIFY.length; i++){
myPort.write(CMD_START_AUTO_IDENTIFY[i]);
println("CMD_START_AUTO_IDENTIFY");}} 

if (key=='s'){
for (int i=0; i<CMD_STOP_AUTO_IDENTIFY.length; i++){
myPort.write(CMD_STOP_AUTO_IDENTIFY[i]);
println("CMD_STOP_AUTO_IDENTIFY");}} 

}