入门级,无壳app登录算法还原并实现发包

如题本文主要讲某app登录算法还原并实现发包的全过程,此app非常适合小白入门时拿来练手,

是一个无壳无混淆的app,软件链接我放在文章最下面,建议先看一下文章后,再去下载上手。

目录

追踪关键代码

上frida刺刀,开始动刀子。

继续追踪关键代码

frida再次出刀

python代码复现

​编辑最终登录算法发包代码的python实现: 

总结:


追踪关键代码

从jadx分析得到上图代码,是手机号加密码的发包位置,其中可见手机号和密码都被传入了login,跟踪进去后,可见的相关代码如下图。

 这里login又调用的私有方法doLogin,将手机号和密码传给了UserService里面,我们进行追踪,得到如下图代码。

 看到了密码登录参数,手机号和密码被传入了这里,密码被传进了getMD5后,加密后存入了params又传入了getRSAParams,然后传入request,先继续追踪getMD5,

 是一个普通的MD5,用python进行一下复现,代码如下。

import hashlib
 
def get_md5(val):
    m = hashlib.md5()
    m.update(val.encode('utf-8'))
    result = m.hexdigest()
    return result
 
print(get_md5("qwerty"))
# 结果:d8578edf8458ce06fbc5bb76a58c5ca4

然后追进getRSAParams里面得到下图代码,写的还挺多,又一层base64加密,还有RSA加密

上frida刺刀,开始动刀子。

先用frida来对这个对象动刀子,得结果如下

  1. getRSAParams is called, params: {password=d8578edf8458ce06fbc5bb76a58c5ca4, os=android, mobile=15536263522, version=2.2.3}
  2. getRSAParams ret value is {data=eyJwYXNzd29yZCI6ImQ4NTc4ZWRmODQ1OGNlMDZmYmM1YmI3NmE1OGM1Y2E0Iiwib3MiOiJhbmRyb2lkIiwibW9iaWxlIjoiMTU1MzYyNjM1MjIiLCJ2ZXJzaW9uIjoiMi4yLjMifQ==, 
  3. sign=DmxjCCvf8aJnZNve4BQkcy6turIGzkE13DkIu9JSnJF7yUDp3ZUxANRnSn6+BCN2nEogZsHFOm0fzzTU/NMnEqijA8lklHoxZspzVOe6Hkp8jYRrzvf0PQIh25lEL2GGWSslgzEK710opNDoQUVHA95ArOv9FQN95HxZuj7ywio=, 
  4. timestamp=1687264102}

传入之前的密码,和python复现的MD5结果相同,传入getRSAParams后成了后面的三项,data   sign   和   timestamp,然后这些数据和charles抓包得到的login数据相同。

继续追踪关键代码

先解决data,进行追踪得到下图代码。Base64加密与解密,

 对其进行python复现得到以下代码,运行结果与frida打印信息相同,

import base64
import json
 
def encode(data, charset='UTF-8'):
    datas=json.dumps(data,separators=(',', ':'))
    try:
       return base64.b64encode(datas.encode(charset)).decode(charset)
    except Exception as e:
        print('Error', e)
        return None
 
data = {"password":"d8578edf8458ce06fbc5bb76a58c5ca4","os":"android","mobile":"15536263522","version":"2.2.3"}
result = encode(data)
print(result)
#结果:eyJwYXNzd29yZCI6ImQ4NTc4ZWRmODQ1OGNlMDZmYmM1YmI3NmE1OGM1Y2E0Iiwib3MiOiJhbmRyb2lkIiwibW9iaWxlIjoiMTU1MzYyNjM1MjIiLCJ2ZXJzaW9uIjoiMi4yLjMifQ==

接着追踪sign的相关代码,得到下图代码 

frida再次出刀

这里用frida进行hook,得到信息如下,可见这里是data与timestamp的拼接被传入了sign,被privateKey进行了加密,然后得到的sign

  1. sign is called, content: data=eyJwYXNzd29yZCI6ImQ4NTc4ZWRmODQ1OGNlMDZmYmM1YmI3NmE1OGM1Y2E0Iiwib3MiOiJhbmRyb2lkIiwibW9iaWxlIjoiMTU1MzYyNjM1MjIiLCJ2ZXJzaW9uIjoiMi4yLjMifQ==&timestamp=1687276955, 
  2. privateKey: MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMNGABIfN+iron2hbwB7mLK1Dm05V1qLZBILTDj7dypr+GJzQ9fk0V7gIIchpFG7pDQEXMbb2nj8VkNAIIDBaw7UY1h9n+sCBT+Xzz6BB2UxLBBMQVwOwv55tJkZ2YBcHFQDGz51HjxAonKJdHwGpjIp7bwdx375gybn2ic4qNuFAgMBAAECgYEAmcha7eqgASCKCx5DaMHtc2+bOPFblfcIjB1Rnd6L7mCxb/cOisutB2bCtykLW0LHAiAdYI5r87Ply3iJIF0yjU35I8aieDVmeaQXXQfpisimXLOmz6p4VlBzAkz493oXPEH81cHqbwnFkiFE3VVtHbCNoZqXlFWthIdae2kpjlECQQDyCMl09eyDBNGuzg1r4tAQ4CeZe7aCkEFwK2at76Raqz9NKrynBiZHsKLU3JedRm2eZ7JimUhsuKbbkS/mcxBrAkEAzop7/PyddSXGDFDECyuXtuEKyzzUvdGiyNmOexhSwTmTZ7QdQqe5p382yCQcY8RXxZ6W9CLjuukfa9I6Tcz/zwJAQbjpG318D8fLOHBzbIxWe36iwia51JJfcpoWc7zTIFvIAKhOOfyNgIISdULBWM+7DHyUD/oXlI4/oPe3zhgIqQJAQd3gFJnrDQTy19KZ8oYAaA30h0PrBG3qX+shiRgErCJUY+oIus0KY+Qp8EGz3A0tgJRGx6you17E6nmspksN+QJBAKhaBGeHqs0+Z5wtFcunuqc6hV7WlhBYCe5YSxBNSiaohXDr6nQwjiOY22Q3m8aInp9KS+lDwW0o4C58VARKyo8=
  3. sign ret value is Z5dDahgsBp06lh76v1fos8GSqX4HdcB+vJBJgw4qWN6wJ52T2LkX9e/WqtLrb+biSg10J23/f0KIQW57GFLIWEgLwQRWgKaacvJJVMtoeETAXhgMvYU9LWmjFzrtd1EuRLyHLVkWik7IbJM5mySllm+bG/nfcPvohXp6ATRqy7k=

sign的python还原,生成的结果与frida的hook结果相同:

from Crypto.Hash import SHA
from Crypto.Signature import pkcs1_15
from Crypto.PublicKey import RSA
import base64
 
def sign(content, private_key):
    private_key_value = base64.b64decode(private_key)
    key = RSA.import_key(private_key_value)
    # 计算 SHA-1 哈希值
    hash_value = SHA.new(content.encode('utf-8'))
    # RSA 的钥长度为 1024 位
    k = 1024
    # 计算最大签名长度,根据 Java 的实现方式计算
    em_len = k // 4
    h_len = 20
    t_len = 3
    s_len = em_len - h_len - t_len - 1
    # 进行签名
    signature_value = pkcs1_15.new(key).sign(hash_value)[:s_len]
    # 返回 Base64 编码的结果
    return base64.b64encode(signature_value).decode()
content = "data=eyJwYXNzd29yZCI6ImQ4NTc4ZWRmODQ1OGNlMDZmYmM1YmI3NmE1OGM1Y2E0Iiwib3MiOiJhbmRyb2lkIiwibW9iaWxlIjoiMTU1MzYyNjM1MjIiLCJ2ZXJzaW9uIjoiMi4yLjMifQ==&timestamp=1687276955"
private_key = 'MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMNGABIfN+iron2hbwB7mLK1Dm05V1qLZBILTDj7dypr+GJzQ9fk0V7gIIchpFG7pDQEXMbb2nj8VkNAIIDBaw7UY1h9n+sCBT+Xzz6BB2UxLBBMQVwOwv55tJkZ2YBcHFQDGz51HjxAonKJdHwGpjIp7bwdx375gybn2ic4qNuFAgMBAAECgYEAmcha7eqgASCKCx5DaMHtc2+bOPFblfcIjB1Rnd6L7mCxb/cOisutB2bCtykLW0LHAiAdYI5r87Ply3iJIF0yjU35I8aieDVmeaQXXQfpisimXLOmz6p4VlBzAkz493oXPEH81cHqbwnFkiFE3VVtHbCNoZqXlFWthIdae2kpjlECQQDyCMl09eyDBNGuzg1r4tAQ4CeZe7aCkEFwK2at76Raqz9NKrynBiZHsKLU3JedRm2eZ7JimUhsuKbbkS/mcxBrAkEAzop7/PyddSXGDFDECyuXtuEKyzzUvdGiyNmOexhSwTmTZ7QdQqe5p382yCQcY8RXxZ6W9CLjuukfa9I6Tcz/zwJAQbjpG318D8fLOHBzbIxWe36iwia51JJfcpoWc7zTIFvIAKhOOfyNgIISdULBWM+7DHyUD/oXlI4/oPe3zhgIqQJAQd3gFJnrDQTy19KZ8oYAaA30h0PrBG3qX+shiRgErCJUY+oIus0KY+Qp8EGz3A0tgJRGx6you17E6nmspksN+QJBAKhaBGeHqs0+Z5wtFcunuqc6hV7WlhBYCe5YSxBNSiaohXDr6nQwjiOY22Q3m8aInp9KS+lDwW0o4C58VARKyo8='
signature = sign(content, private_key)
print(signature)
#Z5dDahgsBp06lh76v1fos8GSqX4HdcB+vJBJgw4qWN6wJ52T2LkX9e/WqtLrb+biSg10J23/f0KIQW57GFLIWEgLwQRWgKaacvJJVMtoeETAXhgMvYU9LWmjFzrtd1EuRLyHLVkWik7IbJM5mySllm+bG/nfcPvohXp6ATRqy7k=

sign完成,然后就是将已经实现的python代码进行拼接,并完成发包。

部分代码复现

首先实现手机号与密码的输入,传入parems,实现相同的login。

python实现代码如下 :

import hashlib
def login(mobile, word):
    params = {
        "password": get_md5(word),
        "os": "android",
        "mobile": mobile,
        "version": "2.2.3",
    }
    return params
 
def get_md5(val):
    m = hashlib.md5()
    m.update(val.encode('utf-8'))
    result = m.hexdigest()
    return result
 
mobile = input("请输入16手机号:")
parem = input("请输入密码:")
params = login(mobile,parem)
print(params)
#运行结果:{'password': 'a7026e07535acfd00f0a8a4a97992291', 'os': 'android', 'mobile': '13685446446', 'version': '2.2.3'}

接着实现下图代码: 

python实现代码如下: 

def jiaparams(params):
    # 将参数转换为 JSON 字符串,并进行 Base64 编码
    params_json = json.dumps(params,separators=(',', ':'))
    print("json转成功:",params_json)
    data = base64.b64encode(params_json.encode('utf-8')).decode('utf-8')
    print("data成功:",data)
 
    # 对加密后的参数进行签名
    signstr = f"data={data}&timestamp={times()}"
    print("时间戳:",times())
    print("拼接成功",signstr)
 
    signss = sign(signstr, pey)#sign加密的地方
    print("sign成功",signss)
 
    # 创建一个空字典用于存储加密后的参数
    result = {}
    result['data'] = data
    result['sign'] = signss
    result['timestamp'] = times()
    return result

最终实现发包代码如下图: 

最终登录算法发包代码的python实现: 



import requests

import json

import time

import hashlib

from Crypto.Hash import SHA

from Crypto.Signature import pkcs1_15

from Crypto.PublicKey import RSA

import base64



pey = 'MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMNGABIfN+iron2hbwB7mLK1Dm05V1qLZBILTDj7dypr+GJzQ9fk0V7gIIchpFG7pDQEXMbb2nj8VkNAIIDBaw7UY1h9n+sCBT+Xzz6BB2UxLBBMQVwOwv55tJkZ2YBcHFQDGz51HjxAonKJdHwGpjIp7bwdx375gybn2ic4qNuFAgMBAAECgYEAmcha7eqgASCKCx5DaMHtc2+bOPFblfcIjB1Rnd6L7mCxb/cOisutB2bCtykLW0LHAiAdYI5r87Ply3iJIF0yjU35I8aieDVmeaQXXQfpisimXLOmz6p4VlBzAkz493oXPEH81cHqbwnFkiFE3VVtHbCNoZqXlFWthIdae2kpjlECQQDyCMl09eyDBNGuzg1r4tAQ4CeZe7aCkEFwK2at76Raqz9NKrynBiZHsKLU3JedRm2eZ7JimUhsuKbbkS/mcxBrAkEAzop7/PyddSXGDFDECyuXtuEKyzzUvdGiyNmOexhSwTmTZ7QdQqe5p382yCQcY8RXxZ6W9CLjuukfa9I6Tcz/zwJAQbjpG318D8fLOHBzbIxWe36iwia51JJfcpoWc7zTIFvIAKhOOfyNgIISdULBWM+7DHyUD/oXlI4/oPe3zhgIqQJAQd3gFJnrDQTy19KZ8oYAaA30h0PrBG3qX+shiRgErCJUY+oIus0KY+Qp8EGz3A0tgJRGx6you17E6nmspksN+QJBAKhaBGeHqs0+Z5wtFcunuqc6hV7WlhBYCe5YSxBNSiaohXDr6nQwjiOY22Q3m8aInp9KS+lDwW0o4C58VARKyo8='



url = 'https://api.langshiyu.com/user/v2/account/login'



def md5(pwd_str):

    md5_obj = hashlib.md5()

    md5_obj.update(pwd_str.encode('utf-8'))

    return md5_obj.hexdigest()



def times():

    return str(int(time.time()))



def sign(content, private_key):

    private_key_value = base64.b64decode(private_key)

    key = RSA.import_key(private_key_value)



    # 计算 SHA-1 哈希值

    hash_value = SHA.new(content.encode('utf-8'))



    # RSA 的密钥长度为 1024 位

    k = 1024



    # 计算最大签名长度,根据 Java 的实现方式计算

    em_len = k // 4

    h_len = 20

    t_len = 3

    s_len = em_len - h_len - t_len - 1



    # 进行签名

    signature_value = pkcs1_15.new(key).sign(hash_value)[:s_len]



    # 返回 Base64 编码的结果

    return base64.b64encode(signature_value).decode()



def login(mobile, param):

    params = {

        "password": md5(param),

        "os": "android",

        "mobile": mobile,

        "version": "2.2.3",

    }

    return params



def jiaparams(params):

    # 将参数转换为 JSON 字符串,并进行 Base64 编码

    params_json = json.dumps(params,separators=(',', ':'))

    print("json转成功:",params_json)

    data = base64.b64encode(params_json.encode('utf-8')).decode('utf-8')

    print("data成功:",data)



    # 对加密后的参数进行签名

    signstr = f"data={data}&timestamp={times()}"

    print("时间戳:",times())

    print("拼接成功",signstr)



    signss = sign(signstr, pey)#sign加密的地方

    print("sign成功",signss)



    # 创建一个空字典用于存储加密后的参数

    result = {}

    result['data'] = data

    result['sign'] = signss

    result['timestamp'] = times()

    return result



mobile = input("请输入16位手机号:")

parem = input("请输入密码:")

params = login(mobile,parem)

paramss = jiaparams(params)

print("发包内容:",paramss)

fb= requests.post(url, paramss)

print("结果:",fb.text)bjsdm



#结果:{"code":0,"message":"该账户不存在","reqdata":{"data":0},"reqtime":"0.015238"}

​

总结:

有部分简单的代码实现,以及开头通过objection的SSL屏蔽后charles进行抓包进行找的关键点,都没有在文章里面写,不过这些都是不必要的,所以文章是有一小部分省略。
整体对小白非常友好的软件,难度不算很高,只要花时间就可以掌握关键点,进行复现。

app放在有道云文章里面,想尝试一下的小白可以进去拿一下。
算法还原过程有道云文章链接:https://note.youdao.com/s/Yf9jGzqk

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值