吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 917|回复: 3
收起左侧

[Python 原创] 某安卓助手脚本的下载脚本

[复制链接]
flying_bat 发表于 2024-9-2 11:15
本帖最后由 flying_bat 于 2024-9-2 11:25 编辑

两三年前搞的,已经通知过此公司。
主要分两部分,脚本列表的下载在java部分(有混淆,应该是使用AS的默认混淆),脚本的DES解密在so部分(无加密,未去除函数名)。
以下是java部分的功能(域名和IP修改过),前期通过wireshark进行初步的分析,再通过frida插桩okhttp的下载函数和java的字符串和RSA加密函数来获取对应的KEY。

#! /usr/bin/env python3
from urllib.request import quote, unquote
from hashlib import md5
from json import tool
from urllib.parse import urlencode
import requests
import json
import random
import base64
import uuid
import sys
import urllib
import time
from Crypto.Cipher import DES
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import MD5
from Crypto.Hash import SHA1
import zipfile
import os
G_PROXIES = {
    'http': 'http://192.168.0.100:8080',
    'https': 'http://192.168.0.100:8080'
}
G_HEADERS = {
    'Accept-Encoding': 'gzip',
    'User-Agent': 'okhttp/3.12.0',
}
G_DEVICE = {
    'IMEI': '357537081683237',
    'MODEL': 'Pixel 2',
    'SDK_INT': 28
}
SIGN8 = [
    "239bed96-f728-4f2f-b718-8f38dd592951",
    "edd87ac6-1620-4faf-8184-e28a42af6456",
    "fef4b024-9f0f-4373-b15d-e29f7dea7907",
    "0a53aef6-e4bf-41fb-85af-633ed959b0eb",
    "fe8b35ef-7ecf-4d2d-9dc6-bcf6de01553e",
    "88e18ba5-9eca-4480-b24e-fd241a32fad3",
    "8ed432e5-acdf-47ea-9668-c38f216dcdfd",
    "f7b9b929-b74d-4833-bb42-4bc14a7999c9",
]
RSA_PRIVATE_KEY = '''-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAPHzUSeup3iG1NdO
adtST0JGkXqg4xiVktOIZx+oqip6dzSxZN1DsxVcnFZ0sBl3uyySb5n6/l1Zhsmd
Y0hiobR45j3ZpoJSGSMHK6xIE3ETRjehKj/RypvH/8cIFqNzrKeAD7HW1COFoZhA
fsnSi4CTtXw75sUnVVoItHA3e0q5AgMBAAECgYAfK4SKAzMqENy8vCphCfKSDDtT
vARlt2dJ/ymh5328qThQPoZLBxy8JMkzTicdsNIZROhBwxcf+Zemw7L1sVc5g7JX
mVdibgjxmtsBKo8jORwcJaijZ+bzV10eNoRAzIhAavjSalnHZFq1kPvXIZfGNCfr
GRu77q6h5jWHUucDjQJBAP32f54XYZPEa/6t5k3NU2wDgwg5FnAg4r/Wr9JnV5gl
m29ZWvcesCCFJ6/MZ6+VAz+upqOyjY4SK31CZx8ddPsCQQDz5CbJ+ir6sEuU62nQ
qg90pQ8VDWN1yQl2wevU2xH7Nh84qwST1v5aGfxx+CX/N6AMQNSnXprL2dwEDKBD
MyjbAkEA9nviupZozxkp08IGL2bj24bBCx8VJvjT+msstGI2guWlKXopLoVrXfS+
YwJZc/GFeK5S77ghaU07RlRHb8yQNwJAJ1EfY64doZ4sAQzWWRohNbeqL8WhxAot
qWRnpT/PrUSzUcpYCZE+Hb5UsPbFAVixR2hoH7cjkztCubBjYZHswwJBALXMo/tw
msX12qru9HFMiIrnFtLkjTlghhkqfoW0A2NP0DsW/uWMD8fIpGVhBM2WpKaSSc+2
48OImdoJj4kVGb8=
-----END PRIVATE KEY-----
'''
RSA_PUBLIC_KEY = '''-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEHan4MLT48S5bredVgr8WOnk6
y4bN3nUvegA/aH/qKYYFTDT3QV8Af1TqlePSnH+RkBrAmfWOTWMHsCZ6rsfEHQVU
y7KeEa7i9RBUVdbBD+fa+5Ybi3cIYRYjfY+oO3pbTLu2RTukrGKjknTbZYORTTg4
4NaiNlIRn7rA7yvUBwIDAQAB
-----END PUBLIC KEY-----
'''
G_IP = ["103.198.223.195", "103.198.223.199"]
BaseRequest = {
    'a': 'Android',
    'aa': G_DEVICE['SDK_INT'],#Build.VERSION.SDK_INT;
    'ab': '1231233123',
    'ad': 'default',
    'b': G_DEVICE['IMEI'], # IMEI
    'bc': '1000', #Channel
    'd': 2,
    'de': 0, #System.currentTimeMillis();
    'isVa': 1, #不同的URL不一样 MODEL_KEY
    'pg': 'com.angel.helper',
    'pv': '福建福州',
    'vc': 136,
    'vs': '1.3.6'
}
def sort_by_key(d:dict):
    new_d = {}
    keys = sorted(d.keys())
    for k in keys:
        new_d[k] = str(d[k])
    return new_d
def sign(values:str, idx:int) -> str:
    ct = values+SIGN8[idx]
    return md5(ct.encode('utf-8')).hexdigest()
def sort_and_sign(query:dict) -> dict:
    query['de'] = int(time.time()*1000)
    sorted_query = sort_by_key(BaseRequest | query)
    r = random.randint(0, 7)
    k = random.randint(0, 7)
    values = ''.join(sorted_query.values())
    sorted_query['Sign'] = sign(values, r)
    sorted_query['R'] = r
    sorted_query['K'] = k
    return sorted_query
def pkcs7(data:bytes, block:int) -> bytes:
    end = (block - len(data) % block)
    return data + end * chr(end).encode()
def pkcs7_clear(data:bytes, block:int) -> bytes:
    if(data[-1] > block or data[-1] > len(data)):
        return data
    return data[0:len(data)-data[-1]]
def des_cbc_encrypt(data:bytes, key:bytes, iv:bytes) -> bytes:
    cipher = DES.new(key=key, iv=iv, mode=DES.MODE_CBC)
    return cipher.encrypt(pkcs7(data, 8))
def des_cbc_decrypt(data:bytes, key:bytes, iv:bytes) -> bytes:
    cipher = DES.new(key=key, iv=iv, mode=DES.MODE_CBC)
    return pkcs7_clear(cipher.decrypt(data), 8)
def read_file(filename:str) -> bytes:
    with open(filename, 'rb') as f:
        return f.read()
    return None
def write_file(filename:str, data:bytes) -> None:
    with open(filename, 'wb') as f:
        f.write(data)
def rsa_sign(data:bytes) -> str:
    priKey = RSA.import_key(RSA_PRIVATE_KEY)
    signer = PKCS1_v1_5.new(priKey)
    digest = MD5.new(data)
    bs = signer.sign(digest)
    return base64.b64encode(bs).decode('utf-8')

def rsa_verify(data:bytes, sign:str) -> bool:
    pubKey = RSA.import_key(RSA_PUBLIC_KEY)
    verifier = PKCS1_v1_5.new(pubKey)
    digest = MD5.new(data)
    signbs = base64.b64decode(sign)
    return verifier.verify(digest, signbs)
def key_xor(key:str, sessionId:str) -> bytes:
    keybytes = base64.b64decode(key)
    sessionIdBytes = sessionId.encode('utf-8')
    x = sessionIdBytes[len(sessionIdBytes)-1]
    return bytes([i^x for i in keybytes])
def app_init() -> str:
    '''
    获取api的IP
    '''
    resp = requests.post('http://www.helper001.com/api/app/init', data=urlencode(sort_and_sign({
        'pv': quote('福建福州'),
    })), headers=G_HEADERS, proxies=G_PROXIES)
    if(resp.status_code != 200):
        return None

    respStr = resp.content.decode('utf-8')
    respJson = json.loads(respStr)
    return respJson['data']['rdata']['IPpool']
def register(username:str, password:str) -> bool:
    '''
    注册
    '''
    resp = requests.get('http://'+G_IP[1]+':5054/al', params=sort_and_sign({
        'devicecode': G_DEVICE['IMEI'],
        'devicemodel': G_DEVICE['MODEL'],
        'username': username,
        'password': md5(password.encode('utf-8')).hexdigest()
    }), headers=G_HEADERS, proxies=G_PROXIES)
    if(resp.status_code != 200):
        return False

    respStr = resp.content.decode('utf-8')
    respJson = json.loads(respStr)
    return respJson['data'] != None and respJson['data']['UserId'] > 0
def login(username:str, password:str) -> dict:
    '''
    登录
    '''
    resp = requests.get('http://'+G_IP[1]+':5054/x', params=sort_and_sign({
        'devicecode': G_DEVICE['IMEI'],
        'devicemodel': G_DEVICE['MODEL'],
        'logintype': 0,
        'username': username,
        'password': md5(password.encode('utf-8')).hexdigest(),
        'uuid': str(uuid.uuid1())
    }), headers=G_HEADERS, proxies=G_PROXIES)
    if(resp.status_code != 200):
        return None
    respStr = resp.content.decode('utf-8')
    respJson = json.loads(respStr)
    if(respJson['data'] == None or respJson['data']['UserInfo'] == None):
        return None

    return {
        'userName': respJson['data']['UserInfo']['UserName'],
        'userId': respJson['data']['UserInfo']['UserID'],
        'userSessionId': respJson['data']['UserInfo']['UserSessionId'],
        'userSecret': respJson['data']['UserInfo']['ToolSecret'],
        'userToken': respJson['data']['AutoLoginToken']
    }
def auto_login(old_token:int) -> dict:
    '''
    使用token自动登录
    '''
    resp = requests.get('http://'+G_IP[1]+':5054/c', params=sort_and_sign({
        'devicecode': G_DEVICE['IMEI'],
        'devicemodel': G_DEVICE['MODEL'],
        'token':    old_token,
    }), headers=G_HEADERS, proxies=G_PROXIES)
    if(resp.status_code != 200):
        return None
    respStr = resp.content.decode('utf-8')
    respJson = json.loads(respStr)
    return {
        'userName': respJson['data']['UserInfo']['UserName'],
        'userId': respJson['data']['UserInfo']['UserID'],
        'userSessionId': respJson['data']['UserInfo']['UserSessionId'],
        'userSecret': respJson['data']['UserInfo']['ToolSecret'],
        'userToken': respJson['data']['AutoLoginToken']
    }
def query_all_topics(maxTopicId:int=780) -> str:
    '''
    查看所有TopicId的脚本列表
    '''
    resp = requests.get('http://'+G_IP[1]+':5054/m', params=sort_and_sign({
        'authorid': 0,
        'topicid':  maxTopicId,
    }), headers=G_HEADERS, proxies=G_PROXIES)
    return resp.content.decode('utf-8')
def query_topic(userId:int, topicId:int) -> list:
    '''
    查看TopicId的脚本列表
    '''
    resp = requests.get('http://'+G_IP[1]+':5054/t', params=sort_and_sign({
        'isVa': 2,
        'authorid': 0,
        'currentpage':  0,
        'pagesize': 999,
        'topicid':  topicId,
        "userid":   userId
    }), headers=G_HEADERS, proxies=G_PROXIES)
    if resp.status_code != 200:
        return None
    respStr = resp.content.decode('utf-8')
    respJson = json.loads(respStr)
    return respJson['data']['rdata']
def query_script_info(userId:int, sessionId:str, toolKey:str, topicId:int, scriptId:int, onlyId:str) -> dict:
    '''
    查看脚本的下载路径及解密key
    '''
    desKey = key_xor(toolKey, sessionId)
    jsondata = json.dumps({
        "AppId":"default",
        "ApppSign":"786927904D5FDB182E46B66018D658DD082BC766",
        "ChannelName":"1000",
        "CpuInfo":" Qualcomm Technologies, Inc MSM8998\n",
        "DeviceType":"Android",
        "IsInternalTool":0,
        "IsNeedChooseGold":1,
        "IsVa":1,
        "KernelInfo":"",
        "MemoryInfo":3744240,
        "NetworkType":1,
        "OnlyId":onlyId,
        "PackageName":"com.angel.helper",
        "SessionId":sessionId,
        "ToolId":scriptId,
        "ToolKey":"",
        "TopicId":topicId,
        "UserID":userId,
        "UserKey":"0",
        "VersionCode":136,
        "VersionName":"1.3.6",
        "imei":G_DEVICE['IMEI']
    }, separators=(',',':'))+'\n'
    resp = requests.post('http://'+G_IP[1]+':5055/api/GetRunPerm',
        data=sessionId[0:16].encode('utf-8') + des_cbc_encrypt((json.dumps({
            'data': jsondata,
            'sign': rsa_sign(jsondata.encode('utf-8'))
        }, separators=(',',':'))+'\n').encode('utf-8'), desKey[0:8], desKey[8:16]) + sessionId[16:].encode('utf-8'), headers=G_HEADERS, proxies=G_PROXIES)
    if(resp.status_code != 200 or resp.content == None or len(resp.content) == 0):
        return None

    respBytes = des_cbc_decrypt(resp.content[1:], desKey[0:8], desKey[8:16])
    respStr = respBytes.decode('utf-8')
    respJson = json.loads(respStr)
    return json.loads(respJson['data'])
def download_script(url:str, referer:str, outzip:str):
    '''
    下载脚本
    '''
    resp = requests.get(url, headers={ 'Referer': referer })
    write_file(outzip, resp.content)
    print('下载脚本成功: ' + url)
if __name__ == '__main__':
    outzip = os.path.dirname(os.path.realpath(__file__)) + '/script_dir/script.zip'
    topicId = 89195
    username = '-------'
    password = '-------'
    # app_init()  #获取IP
    # topicstr = query_all_topics() #获取topicId, 部分topic存在assets文件里 t.txt\xs.txt\zsc.txt
    # register(username, password) #注册
    user = login(username, password) #登录
    # 查询topicId下面的脚本信息
    scriptList = query_topic(user['userId'], topicId)
    script = scriptList[0]
    # 查询脚本下载路径及解密key
    scriptInfo = query_script_info(user['userId'], user['userSessionId'], user['userSecret'],
        topicId, script['ScriptID'], script['OnlyID'])
    scriptInfo = scriptInfo['ScriptInfo']
    # 下载脚本
    download_script(scriptInfo['ScriptPath'], scriptInfo['Referer'], outzip)

免费评分

参与人数 2吾爱币 +8 热心值 +1 收起 理由
wushaominkk + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
dcsrwa + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

asdasxzca 发表于 2024-9-2 16:11
牛逼!!!
shepherdglen 发表于 2024-9-2 21:17
tianya0908 发表于 2024-9-13 10:15
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-12 17:32

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表