国税局验证码识别 | 不讲武德篇

1. 有人说我不讲武德

在这里插入图片描述
上个图,各位自己细品,不信的可以核实。

根据和客户交易的经验,总结下面几点建议(核心):

  1. 当面交易:
    1)在对方电脑里看到源码;
    2)现场对接官网核实识别率;
    3)看着源码从他的设备转移到你的设备上,并且成功运行起来。

  2. 合同约束,在合同上注明验收指标,以我为例:在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    我一般都会写的很清楚,总结为:如果识别率达不到验收指标必须全额退款。

  3. 对于小号而言,最好接受第一条建议,防止跑路。 远程交易必须添加微信或者有年限和等级正常有使用痕迹的QQ,微信的朋友圈起码是超过其意图开始(打算卖模型)的年限的,能客观观察到对方是个真实的存在,信息暴露越少越不真实,毕竟万一被骗,这是能报警维权唯一的手段,诉讼文书才能知道该寄给谁。

综合以上,如果对方能接受以上全部要求,恭喜你们,不会花冤枉钱,我十分赞成你们交易,如果以各种理由回避或拒绝暴露真实的自己,建议大家要小心了。还有很重要的一个途径,岁月的痕迹不会骗人,看看项目开发的历史进程和发展轨迹:
在这里插入图片描述
去年我在云栖社区也做过技术分享直播,都有痕迹可以查的。

2. 那便不讲武德一回


本文将通过一个案例,来向大家展示代码安全的重要性。

首先事情的经过是这样的,我几百年前在知乎发的一个评论突然给举报了,我已经放弃了最终的挣扎,但是我还是好奇,究竟是什么仇什么怨,然而我发现对方是一个小号,就存粹注册过来发这些信息的小号,下面来看看事件经过。
在这里插入图片描述
然后我看到这个沉了一个世纪的文章多了一条钓鱼评论:
在这里插入图片描述
顺藤摸瓜,找到作者,好家伙,上来也开始说自己98识别率了,我当时实测过他接口也就90左右, 跟他发起了好友请求,我一共申请过两次,第一次是他刚在github发链接的时候推送给我的,顺着找到了他的文章,那时我没把他当对手,我只是出于好奇,确认对方是不是找到了我在生成器里面埋的陷阱,这对我来说就像是埋宝藏的人看人闯关一样。第二次申请是间隔许久了,对方始终认为我不怀好意。 甚至于,现在还一直把截图挂着呢。通过作者的知乎文章,找到一个链接。
在这里插入图片描述
通过链接,打开作者的网站:
在这里插入图片描述
直接送模型,下载下来,
在这里插入图片描述
好家伙,就这?
在这里插入图片描述
解压可见,pyinstaller打包,老哥估计初学者,不懂得混淆代码。拿到颜色处理的函数,是不是熟悉的味道,你们猜的没错。
在这里插入图片描述
这段代码可见(源码在文末),出自okfu老哥手笔:https://blog.csdn.net/okfu_DL/article/details/90379583
最早, 是源自我这篇方案里面的思路:https://blog.csdn.net/kerlomz/article/details/105974823
对比一下那个老哥博客写的:
在这里插入图片描述
我在这篇文章写过作者生成器的出处:https://blog.csdn.net/kerlomz/article/details/110676114。作者说自己没用,全部自己写的,那么我是相信作者是偶然路过这个包含生成代码的项目并且留下足迹的。
不过作者的口气似乎在表达,“自己并没有参考任何人的方法和思路”,并且英数的识别率很像是在暗示总体识别率很高。

在这里插入图片描述
看他的模型,还是比较入门水准的,Placeholder_1:0 说明了一切,懂得都懂。我不信不同识别率版本的模型用的还不是同一套训练代码,如果说不懂得代码复用那就更……
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里不多做评论了,还有模型加载等等的问题。在这里插入图片描述
反编译这些没有任何技术含量,首先90识别率是达到商用水准的最低标准了,作者不懂得保护模型,这里该引以为戒,以为简单打包,几行代码就能做限制了,需要更正这种错误的观念。

方便你们比对,附上企业级训练和部署框架
训练:https://github.com/kerlomz/captcha_trainer
部署:https://github.com/kerlomz/captcha_platform

事实证明,轻易就拿到了源码:

#! /usr/bin/env python 3.6 (3379)
#coding=utf-8
# Compiled at: 1969-12-31 18:00:00

from gevent import monkey
from gevent.pywsgi import WSGIServer
monkey.patch_all()
import os, cv2, re, io, base64, numpy as np, tensorflow as tf, json, http.client, time
from PIL import Image
from flask import Flask, request, Response
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
os.environ['CUDA_VISIBLE_DEVICES'] = '2'
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
model_path = './model_frozen/model_win.pb'
app = Flask(__name__)
batch_size = 1
h = 64
w = 164
chanel = 3
graph = tf.compat.v1.Graph()
sess = tf.compat.v1.Session(graph=graph)
with tf.io.gfile.GFile(model_path, 'rb') as (f):
    graph_def = tf.compat.v1.GraphDef()
    graph_def.ParseFromString(f.read())
with graph.as_default():
    tensors = tf.import_graph_def(graph_def, return_elements=['Placeholder:0', 'Placeholder_1:0',
     'decode_conversion/SparseToDense:0'])
 
def get_web_time(host='42.192.80.101:8089'):
    try:
        conn = http.client.HTTPConnection(host)
        conn.request('GET', '/')
        res = conn.getresponse()
        ts = res.getheader('date')
        ltime = time.strptime(ts[5:25], '%d %b %Y %H:%M:%S')
        return list(ltime)
    except ConnectionError:
        return 'network_error'
 
 
def get_charsets():
    charsets = u'*123456789ABCDEFGHIJKLMNPQRSTUVWXYZ...此处省略几千字'
    charsets = re.sub('\n|\t|', '', charsets)
    charsets = list(set(list(charsets)))
    charsets = sorted(charsets)
    charsets = ('').join(charsets)
    charsets = charsets.encode('utf-8').decode('utf-8')
    return charsets
 
 
def color_convert(image, src_color):
    if src_color == 'b':
        dst_image = cv2.cvtColor(image.copy(), cv2.COLOR_RGB2BGR)
    else:
        if src_color == 'e':
            dst_image = image[:, :, :].copy()
            dst_image[:, :, 2] = 255 - dst_image[:, :, 2]
        else:
            if src_color == 'y':
                dst_image = cv2.bitwise_not(image.copy())
                dst_image = cv2.cvtColor(dst_image, cv2.COLOR_RGB2BGR)
            else:
                raise ValueError
            return dst_image
 
 
def recognize(batch_image):
    result = sess.run(tensors[2], {tensors[0]: batch_image, tensors[1]: [w]})
    text = ('').join([get_charsets()[v] for v in result[0] if v != -1])
    text = text.replace('*', '')
    return text
 
 
@app.route('/captcha', methods=['POST'])
def captcha_outer():
    web_time = get_web_time()
    if web_time == 'network_error':
        return Response(json.dumps({'code': 'network error'}, ensure_ascii=False), mimetype='application/json')
    year = web_time[0]
    month = web_time[1]
    day = web_time[2]
    if year == 2020:
        pass
    else:
        if year == 2021:
            if month == 1:
                if day <= 11:
                    pass
                return Response(json.dumps({'code': 'Probation Expired,QQ3421366528'}, ensure_ascii=False), mimetype='application/json')
        try:
            res = request.data[0:int(request.content_length)]
            res = json.loads(res, encoding='utf-8')
            key = res['key']
            image_bin = base64.b64decode(str(res['image']))
            image_byte = io.BytesIO(image_bin)
            image = Image.open(image_byte)
            image_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
        except ValueError:
            return Response(json.dumps({'code': 'data error'}, ensure_ascii=False), mimetype='application/json')
 
        if key == '00':
            image = Image.fromarray(cv2.cvtColor(color_convert(image_cv, 'e'), cv2.COLOR_BGR2RGB))
        else:
            if key == '01':
                pass
            else:
                if key == '02':
                    image = Image.fromarray(cv2.cvtColor(color_convert(image_cv, 'y'), cv2.COLOR_BGR2RGB))
                else:
                    if key == '03':
                        image = Image.fromarray(cv2.cvtColor(color_convert(image_cv, 'b'), cv2.COLOR_BGR2RGB))
                    else:
                        return Response(json.dumps({'code': 'key error'}, ensure_ascii=False), mimetype='application/json')
                    batch_images = np.zeros(shape=(batch_size, h, w, chanel), dtype=np.float32)
                    batch_image_widths = []
                    image = image.resize((164, 64), Image.ANTIALIAS)
                    image = np.array(image, np.float32)
                    image = image / 255
                    batch_images[0, :, :, :] = image
                    batch_image_widths.append(image.shape[1])
                    text = recognize(batch_images)
                    return Response(json.dumps({'code': text}, ensure_ascii=False), mimetype='application/json')
 
 
if __name__ == '__main__':
    http_server = WSGIServer(('127.0.0.1', 8808), app)
    http_server.serve_forever()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值