我的小工具,用C和python实现远程读卡器,远程读写消费卡片
这个远程读卡器就是一普通usb口或串口的读卡器,只不过配合一个电脑软件作为tcp服务器。这样,程序员可以在公司电脑上运行程序连到服务器上。服务器端操作控制现场的读卡器。
晚上睡不着,想着白天工作上的事,让现场同事配合抓包,读取一下卡上数据,但现场同事连串口助手都没听说过,指望不上了。想到可以做一个远程读卡器。
对调试现场读卡之类的程序太有用了。想抓包现场卡上数据,很easy了。封装一些读卡接口。甚至可以实现远程仿真调试程序读卡消费逻辑 。或者实现一完全软件模拟的pos机。
这想法可行吧,我抽空打算实现一个。至少对我的工作很有帮助吧。目前已实现了部分电脑程序模拟车载pos机通信。可以模拟pos机与采集前置服务通信,下载票价,传消费记录。后续加进去读卡功能。跟采集前置服务调通信我一般电脑上先调通,再移植到车载机上,效率提高不少。省得找机器,找流量卡,开外网端口映射,接仿真器。现场再也不用担心车载机记录全采出来咋入库了。用这个模拟程序直接解析模拟车载机通信就传上去。
说做就做。由于我是终端组的,不像平台他们有现成的读卡器。于是想到用车载POS机的读卡模块配合串口做一个。作为读卡器用。服务器端考虑用python,有很多模块可以用,实现起来快。
一、功能简介:
本工具实现一个在电脑上运行的模拟车载机终端。只是功能上的模拟,并不是与车载机完全一样。用现场的B502机器作为模拟终端的读卡模块,在公司远程读、写、消费现场的M1卡,CPU卡,并按照车载机中的记录格式存储记录到电脑上,然后通过与采集前置通信把记录传给采集前置服务。全采的车载机记录也可以拿过来用,通过此工具传给采集前置。
实现了供本地电脑调用的读卡库,通信库和记录存储库,所有库的接口均与嵌入式终端程序中的文件库,卡库,通信库接口一致。此工具只是利用卡库,通信库和文件库的一个例子。若需要在某个具体项目中读写卡,抓包,测卡,消费,上传记录等,需根据提供的接口单独做自己的应用。
二、实现原理:
1.移植嵌入式程序中的文件库到电脑上,实现参数保存和记录存储模块。
2.用电脑上的socket通信封装嵌入式终端上的通信接口。
3.修改B502机器程序,把B502机器做成一个读卡器。
4.做一个TCP服务程序,开启服务端口,运行在远程的电脑上,服务程序通过电脑串口控制B502读卡器并提供读写卡服务接口,供客户端调用。
5.客户端根据提供的接口做应用,实现远程读写卡,消费,记录存储,上传。
三、操作说明
现场操作:
1.升级B502车载机程序,把B502机器当作读卡器使用,串口连接到电脑上。
2.在电脑上安装并运行读写卡服务程序,为客户端提供读写卡服务。
3.开启外网端口映射,没有固定IP也不是问题。只要现场的电脑能连外网,就可以把现场电脑的端口映射到外网,可以使用免费的端口映射工具。
4.提供现场映射过的公网IP地址和端口给开发人员。
5.把待读取的卡片放到机器上。
缺点:
现场必须有B502机器,且有串口线。B502机器还需要接12V的电源,体积大也不方便携带。所以用 B502机器做读卡器,有局限性,不通用。不过公司现有E711读卡器,该读卡器只需软件改造一下,升级下程序,就可以配合后台运行的TcpServer读写卡服务实现远程读卡器。消费速度上受网络原因有影响,不过远程读卡器只作为调试测试的手段,不是专门用来消费卡片的。实测消费完M1卡大概需要6秒钟。
客户端接口:
文件库libmyfile.a
通信库libmycom.a
读卡库libmycard.a
算法库libmycalc.a
客户端接口:
文件库libmyfile.a
通信库libmycom.a
读卡库libmycard.a
算法库libmycalc.a
接口和车载机程序的保持一致,参见头文件。
读卡库libmycard.a
CPU卡操作接口:
ICC_APDU_Exchange(……)
M1卡操作接口:
ICC_MiOne_LoadKey(…… );
ICC_MiOne_CheckKey(…… )
ICC_MiOne_BlkRead(……)
ICC_MiOne_BlkWrite(……)
ICC_MiOne_Value(……)
……
若是CPU卡,客户端可直接用TCP调试工具发指令调试卡片。
例如:
选择卡片3F00目录,直接在TCP助手上文本发送:00A40000023F00
若是选择现场PSAM卡的3F00目录,直接在TCP助手上文本发送:P00A40000023F00,前面多加了一个字母P
若是M1卡,指令有点儿复杂,客户端最好写个小程序,调用接口读写卡。
一天时间,初步实现了。原理就是TCP的socket通信转串口通信。源码如下;
#coding=utf-8
#author:yangyongzhen
#QQ:534117529
import sys,threading,time;
import serial;
import binascii,encodings;
import re;
import os;
from socket import *
from struct import *;
from myutil import *;
#''代表服务器为localhost
#在一个非保留端口号上进行监听
class ComThread:
def __init__(self, Port=0):
self.l_serial = None;
self.alive = False;
self.waitEnd = None;
self.port = Port;
#TCP部分
self.myHost = ''
self.myPort = 5050
self.sockobj = socket(AF_INET, SOCK_STREAM)
self.connection = None
#数据
self.snddata = ''
self.rcvdata = ''
def waiting(self):
if not self.waitEnd is None:
self.waitEnd.wait();
def SetStopEvent(self):
if not self.waitEnd is None:
self.waitEnd.set();
self.alive = False;
self.stop();
def start(self):
self.l_serial = serial.Serial();
self.l_serial.port = self.port;
self.l_serial.baudrate = 115200;
self.l_serial.timeout = 2; #秒
self.l_serial.open();
if self.l_serial.isOpen():
self.waitEnd = threading.Event();
self.alive = True;
self.thread_read = None;
self.thread_read = threading.Thread(target=self.FirstReader);
self.thread_read.setDaemon(1);
self.thread_read.start();
self.thread_write = None;
self.thread_write = threading.Thread(target=self.FirstWriter);
self.thread_write.setDaemon(1);
self.thread_write.start();
#TCP部分
self.thread_TcpServer = None;
self.thread_TcpServer = threading.Thread(target=self.TcpServer);
self.thread_TcpServer.setDaemon(1);
self.thread_TcpServer.start();
self.thread_TcpSend = None;
self.thread_TcpSend = threading.Thread(target=self.TcpSend);
self.thread_TcpSend.setDaemon(1);
self.thread_TcpSend.start();
return True;
else:
return False;
def FirstReader(self):
while self.alive:
# 接收间隔
time.sleep(0.1);
try:
data = '';
n = self.l_serial.inWaiting();
if n:
data = self.l_serial.read(n);
#for l in xrange(len(data)):
#print '%02X' % ord(data[l]),
# 发送数据
print 'serial recv:'
print data;
self.snddata = data
#print_hex(data);
# 判断结束
except Exception, ex:
print str(ex);
self.waitEnd.set();
self.alive = False;
def FirstWriter(self):
while self.alive:
# 接收间隔
time.sleep(0.1);
try:
#snddata = raw_input('nenter data send:n')
if self.rcvdata!='':
self.l_serial.write(self.rcvdata);
print 'serial send:'
print self.rcvdata;
self.rcvdata = '';
#print_hex(snddata);
except Exception, ex:
print str(ex);
self.waitEnd.set();
self.alive = False;
def TcpServer(self):
self.sockobj.bind((self.myHost, self.myPort))
self.sockobj.listen(1)
print 'nTcpServer listen at 5050 oK!n'
while True:
# 接收间隔
time.sleep(0.5);
self.connection, address = self.sockobj.accept()
print 'Server connected by', address
self.snddata = ''
self.rcvdata = ''
while True:
#读取客户端套接字的下一行
data = self.connection.recv(1024)
#如果没有数量的话,那么跳出循环
if not data: break
#发送一个回复至客户端
self.rcvdata = data
#connection.send('Echo=>' + data)
self.connection.close()
self.waitEnd.set();
self.alive = False;
def TcpSend(self):
while True:
# 接收间隔
time.sleep(0.5);
while True:
time.sleep(0.5);
try:
if not self.connection is None:
if self.snddata != '':
self.connection.send(self.snddata)
self.snddata = ''
except Exception, ex:
pass
def stop(self):
self.alive = False;
self.thread_read.join();
if self.l_serial.isOpen():
self.l_serial.close();
#测试用部分
if __name__ == '__main__':
print 'CardTest TcpServer - Simple Test Card Tool 1.00n'
print 'Copyright (c) Newcapec 2015-2016.n'
com =raw_input('please enter com port(1-9):')
rt = ComThread(int(com)-1);
try:
if rt.start():
rt.waiting();
rt.stop();
else:
pass;
except Exception,se:
print str(se);
if rt.alive:
rt.stop();
os.system("pause")
print '';
print 'End OK .';
del rt;
实现的效果图:
用C语言封装一下读写卡接口,可以做个客户端测试了:
- 窃取Facebook用户信息:利用Android同源策略漏洞的恶意应用被发现
- 关于React Native 安卓首屏白屏优化
- 浅谈spring security 403机制一、无权限访问二、匿名访问三、有权限访问原因机制指定AccessDeniedHandler指定error-page情景原因结论
- Python之numpy数组学习(二)
- Intent 属性详解(上)
- 复仇行动:Notepad++官网被圣战组织黑了
- 四大组件的纽带——Intent
- 利用HTC One漏洞破解手机PIN密码
- Android NDk环境配置
- 谷歌再曝Windows8.1漏洞,微软怒了
- Python机器学习的生态系统
- TP-link TL-WR840N系列路由器存在CSRF漏洞,可修改任意配置(含POC测试过程)
- JavaScript严格模式
- 微软修复8个安全漏洞,包括谷歌披露的0day漏洞
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- linux nslookup命令使用详解
- Go 数据存储篇(五):建立数据库连接并进行增删改查操作
- Laravel 路由使用入门
- Go 数据存储篇(六):数据表之间的关联关系和关联查询
- 搭建nextcloud私有云存储网盘的教程详解
- Laravel 路由使用进阶
- 使用dig/nslookup命令查看dns解析的方法步骤
- Go 数据存储篇(七):GORM 使用入门
- 在 Linux 上使用 Multitail命令的教程
- 深入理解linux执行文件提示No such file or directory的背后原因
- Laravel 控制器:从 MVC 模式聊起
- 基于yolo5工地安全帽和禁入危险区域识别系统,附数据集
- Go 常见并发模式实现(一):调度后台处理任务的作业程序
- 路由使用进阶(二)
- linux No space left on device由索引节点(inode)爆满引发500问题