Android XX验证码SDK验证流程分析

写在前面

验证码是我们做攻防对抗过程中经常会遇到的东西,在大部分核心场景,包括用户登陆,支付等环节,简单的会做个无感验证,但是如果发现异常用户,则会弹出图形验证码等形式。最近因为工作需要,对GeeTest验证码做了分析,今天抽空把整套流程再重新梳理一遍,供有兴趣的同学参考。

整体流程

XX验证码验证流程如下图:

暂时无法在飞书文档外展示此内容

  1. 发出注册请求Register.php,由服务端生成cid,challenge等参数,其中challenge是主要id,用于后续验证。
  2. 拉取验证码信息(get.php),根据注册时获取到的cid,challenge等信息,加密生成参数w,再将w作为固定请求的body数据,验证码服务器在接收到注册请求时,解密w数据,得到cid以及challenge,并生成验证码数据,再将验证码数据返回到客户端。抓包如下图,上面内容为request信息,下面内容为response信息。
  1. 验证码结果验证(ajax.php),客户端将用户处理的验证结果以及challenge,cid等信息一起加密生成参数w,再将参数w作为固定的body数据,返回到验证码服务器进行验证。
  2. 客户端会保存好challenge(具备有一定时效性,超过一定时间将会失效),后续的业务请求中会将challenge值一起携带到后端,后端再根据challenge到验证码服务器进行查询,确认第三部中验证是否合法。如果合法,则正常返回;否则,将返回相应错误码。

w参数计算

java层逆向,通过逐步调试分析,发现最终在调用com.geetest.sdk.a.a函数后,生成的值会作为参数"w"。然而该函数是个native函数,因此需要继续分析so层。(sdk版本:4.0.8)

直接hook native函数com.geetest.sdk.a.a可以发现,get.php请求时的w参数内容为:

json 复制代码
{"challenge":"ffac10aa5b0937bae3cbd895b3ec39a4","cid":"0c326bd5edd071ba9b9dd10df201013f","client_type":"android","lang":"zh-CN"}

无感验证模式下,ajax.php请求时w参数内容为:

python 复制代码
             
{"cid":"0c326bd5edd071ba9b9dd10df201013f","challenge":"ffac10aa5b0937bae3cbd895b3ec39a4","client_type":"android","light":"","serial":"xxxxxxxx","insight":"{"build":"1","release":"1.0","br":"100.0","bs":{"br":"1.0","bs":"5","plugState":"1","health":"2"},"coun":"CN","dh":"2203","dm":"google","dw":"1080","dn":"Pixel6","lang":"zh","mems":"7.90GB","ostype":"android","osver":"13","py":"1","ts":"1706768800000","vendor":"xxxxxx","app":"xxxx","gt3":"4.0.8","uuid":"xxxxxx"}","accessibility":{"voice":0}}

通过frida hook函数RegisterNatives可以知道对应的函数地址,偏移为0x54f4(不同版本sdk的偏移有所不同)。

yaml 复制代码
[INFO][fridaRegstNtv]: name: a, signature: (Ljava/lang/String;)Ljava/lang/String;, fnPtr: 0x78ae9114f4, modulename: libgee-lib.so -> base: 0x78ae90c000, offset: 0x54f4

ida逆向分析,发现可以直接F5,不过代码看着是做了一些简单的混淆,但是大体上逻辑还是能够猜得出来,入口函数如下:

  1. 随机生成16字节密钥。进入61行的函数,可以看出来gen_rand_array16函数是随机生成16字节大小的字符。

  1. 明文数据填充16字节对齐,对于长度少于16个字节,需要补满16个字节,补(16-len)个(16-len),从这特征可以看出来大概率是AES加密。
  1. 上图76行发现对前面随机生成的aes_key做加密处理,并且返回结果为128字节,由于RSA加密结果就是128字节,所以大概率就是用的RSA加密,之后结合动态hook方式获取RSA关键密钥即可。
  2. 字符串拼接,将加密后的数据以及RSA的128位密钥拼一起,再做base64运算。
相关推荐
学习ing小白1 小时前
JavaWeb - 5 - 前端工程化
前端·elementui·vue
真的很上进2 小时前
【Git必看系列】—— Git巨好用的神器之git stash篇
java·前端·javascript·数据结构·git·react.js
胖虎哥er2 小时前
Html&Css 基础总结(基础好了才是最能打的)三
前端·css·html
qq_278063712 小时前
css scrollbar-width: none 隐藏默认滚动条
开发语言·前端·javascript
.ccl2 小时前
web开发 之 HTML、CSS、JavaScript、以及JavaScript的高级框架Vue(学习版2)
前端·javascript·vue.js
小徐不会写代码2 小时前
vue 实现tab菜单切换
前端·javascript·vue.js
2301_765347542 小时前
Vue3 Day7-全局组件、指令以及pinia
前端·javascript·vue.js
ch_s_t2 小时前
新峰商城之分类三级联动实现
前端·html
辛-夷2 小时前
VUE面试题(单页应用及其首屏加载速度慢的问题)
前端·javascript·vue.js
田哥coder2 小时前
充电桩项目:前端实现
前端