wmsuper 发表于 2017-5-5 17:30

对一个手机群控软件的授权流程的分析过程

0x01 抓包
这个软件对连接数进行了限制,要输入授权码进行网络验证。


通过抓包,分析软件提交的数据,serialNumberKey后面跟着一大串字符,看起来很像base64编码,猜测加密后使用了自定义的base64编码。


0x02 调试分析
2.1 发送流程
软件使用了OpenSSL加密库,通过查看那些地方使用了加解密的函数,即可快速定位到发送密文和接受处理的地方,然后就是调试下整个流程。
首先把获取到的mac,序列号及其本地时间组成一个json,加密后添加到serialNumberKey再发送出去。


来看下加密使用的算法,设置对应的秘钥QN01R3M7WPKKORBK

调用OpenSSL里面的库函数进行加密,加密算法为aes_128_ecb,注意IV为16个字节的0,pad方式为PKCS7

将加密后的二进制数据进行编码,编码方式为base64的变形
所谓变形就是使用的编码字符串不同,但是具体算法还是一样的,第一行是软件使用的字符串,第二行是标准base64采用的字符串序列,这就解释了编码后的字符串出现了'@ ! -'的原因。
pri_base64_str="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@!-"
base64_str="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="


2.2接收流程
通过分析代码可以知道,返回内容应该是个json,code的值应该是1,才能进行下一步处理


从encodeStr提取出字符串进行解密


先把字符串进行base64解码转为二进制数据,在进行解密


解密的秘钥是发送的时候的time字段的值,解密算法和发送的加密算法是相同的


解密之后还是一个json,接下来就是json解析,connectNum是最大的连接数,他转化成数字之后对一个全局变量进行赋值,如果想破解的话可以改掉这个全局变量的初值(把5改成其他的)
endTime是到期时间,当为forever的时候是用不过期
最后应该构造一个类似{"connectNum": "50", "endTime": "forever"}的json获取对应的时间进行加密------>>>>>{"code":1,"encodeStr":"Eq/GisQkh7zt8iRo7EgFGKiYEmQh1tAPxien5zjDKPjNcUOFuqrU1YnRzzFU8lXC"}



0x03 编写加解密程序
通过分析加解密算法即可编写出对应的程序
import base64
import binascii
import sys
import crypto
import json
import requests
sys.modules['Crypto']=crypto
fromcrypto.Cipher import AES
class validserno_payload(object):
   def __init__(self,mac,serialNo,time):
      self.mac= mac
      self.serialNo = serialNo
      self.time=time
class validrep_payload(object):
   def __init__(self,connectNum,endTime):
      self.connectNum= connectNum
      self.endTime = endTime

class lb_crypto():
   def __init__(self,key,iv):
      self.key=key
      self.iv=iv
      self.blk_size=16
   def lb_encrypt(self,str,pri):
      in_len=len(str)
      add=self.blk_size-(in_len%self.blk_size)
      str=str+add*chr(add)#处理pad
      cryptor = AES.new(self.key, AES.MODE_ECB,self.iv)
      enc_text = cryptor.encrypt(str)
      print binascii.hexlify(enc_text)
      base64_str=base64.b64encode(enc_text)
      if not pri:
             return base64_str
      encode_str=base64_str.replace('+','@').replace('/','!').replace('=','-')
      return encode_str
   def lb_decrypt(self,encode_str):
      base64_str=encode_str.replace('@','+').replace('!','/').replace('-','=')
      decode_str=base64.b64decode(base64_str)
      cryptor = AES.new(self.key, AES.MODE_ECB,self.iv)
      plain_text = cryptor.decrypt(decode_str)
      str_len=len(plain_text)
      pad=ord(plain_text[-1])
      return plain_text

payload=validrep_payload("50","forever")
lc=lb_crypto('2017050512453200','\0'*16)#时间需要动态获取
print json.dumps(payload.__dict__)
rep_data="encodeStr="+lc.lb_encrypt(json.dumps(payload.__dict__),False)
print rep_data



0x04 验证结果
为了验证分析结果,可修改http返回内容,看是否达到预期目的。

noname.txt内容如下

注意解密时候是根据时间来动态解密的,要想解密正确应该在解密的时候patch时间值秘钥,验证结果如下:


wmsuper 发表于 2017-5-13 18:22

材鸟 发表于 2017-5-13 14:11
看了下,验证流程大概是
POSTencode{postTime , 一堆机器信息(供服务端验证是否授权其使用)}



其实文中已经说了,最大连接次数是保存到一个全局变量里面,初值为5,把5改成其他的可成功秒破。本文的重点不是破解,而是算法分析

材鸟 发表于 2017-5-13 14:11

看了下,验证流程大概是
POSTencode{postTime , 一堆机器信息(供服务端验证是否授权其使用)}

如果不授权则返回
{CODE:-1,错误信息}
授权则返回
{CODE:1,"connectNum":“次数”,"endTime":“到期时间”}

那么不推算法,应该可以 bp recv 下断
当包返回后,在步过解密CALL的时候
对返回的{CODE:-1,?????}明文 替换成{CODE:1,"connectNum":“次数”,"endTime":“到期时间”} 。

当然,十分取巧。除非购买过此程序后进行对正确返回值抓包,否则不知道正确的返回值就是{CODE:1,"connectNum":“次数”,"endTime":“到期时间”}

不过如果不知道的话,可以继续跟,在判断CODE字段值时改跳转,那么可能会出错,因为返回的值没有"connectNum""endTime" 字段,那么再分析错误,可以推论出返回值具有"connectNum""endTime" 字段
纸上谈兵,求轻喷!{:1_907:}

101010 发表于 2017-5-5 17:34

66666666666要的就是这个软件

火山寻冰 发表于 2017-5-5 17:35

牛人·~~~~~

火山寻冰 发表于 2017-5-5 17:36

软件~~再那里~~~

mengqiu 发表于 2017-5-5 17:43

太强悍了,正需要这样的软件

wanglizhou518 发表于 2017-5-5 17:51

破解后的软件发出来啊

www.52pojie.cn 发表于 2017-5-5 17:51

这是高手啊!!膜拜!!

wtuaixk 发表于 2017-5-5 17:59

就算不懂也要顶一下{:301_993:}

mayl8822 发表于 2017-5-5 18:09

厉害了我的哥, 感谢分享

lizhe0608 发表于 2017-5-5 18:09

软件在 哪里呢?
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 对一个手机群控软件的授权流程的分析过程