《APP逆向学习》课程介绍和什么是安卓app逆向?

Posted 小白白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《APP逆向学习》课程介绍和什么是安卓app逆向?相关的知识,希望对你有一定的参考价值。

来源【小肩膀 零基础一站式安卓app逆向安全(2021版)】:aHR0cHM6Ly93d3cuYmlsaWJpbGkuY29tL3ZpZGVvL0JWMUFWNDExSjd6aw==

1.课程介绍

安卓逆向用到的语言很多,比如java,c,c++,javascript,python,当然我们接触最多的自然是java和c。

adb与Linux命令行:因为我们是要从pc端操作手机的,推荐真机,但是不管真机还是模拟器,adb的操作都是避不开的,因为安卓系统底层的话,就是基于Linux内核开发的,所以里面的shell命令的话,跟Linux非常相似,所以有必要学

frida hook框架实际上在一定程度上代替动态调试,当然也有比动态调试更方便的地方,比如可以hook系统函数,然后通过这种方式定位到关键代码,或者hook关键函数,动态修改逻辑

协议逆向:基本步骤就是,首先分析app,去hook里面的关键函数,得到关键函数的参数和返回值,然后进行算法还原

frida自吐算法,实际上就是去hook一些系统的函数,系统中关键的一些加密函数,如果app调用这些函数,那自然会把传进来的参数和返回结果都打印出来。

现阶段的app来讲,自吐算法威力已经越来越小了,在18年之前的话,自吐算法可以基本上,吐出很多app的算法,因为现在app基本上都是去向so开发的,那么直接是so算法,要么至少也是带一点so算法的。

objection:是基于frida的命令行hook工具, 可以让你不写代码, 敲几句命令就可以对java函数的高颗粒度hook, 还支持RPC调用。让你不写代码就能去完成java层的hook。

在so开发的话,主要进行c和java的交互,在c和java的交互中间必须进过JNI,所以在NDK开发讲的最多就是JNI相关函数,我们弄懂JNI相关函数有什么作用的话,对于so层的逆向是有非常大的帮助的。

不需要还原算法的方案:

  • 【fiddler 拦截 + uvicorn转发方案】:利用fiddler把app发包过程中的数据包给拦截下来,那发包过程中,数据包肯定有带有我们爬虫需要爬取的一些信息的,我们就把这个拦截下来,转发到我们服务端,然后传到数据库里去,这样我们就不需要逆向app也能够得到我们想要的数据,我们只要抓个包就行了。当然这个方案有个缺点,首先你必须开着app的,它的效率会比较低,其次发包过程中数据是加密的,那你还需要去做逆向的
  • 【frida rpc + uvicorn算法转发方案】:这个方案的话,它的缺点也是必须开着app,效率比较低,它的优点是,你只要逆向一个app,找到一个hook点就可以了,就是找到关键函数的加密点,你去hook函数,主动调用,传进去指定的参数,得到结果,返回给服务端,然后服务端再转发给爬虫端,进行数据的爬取。

unidbg:是基于unicorn开发的一个框架,是一个能在pc端模拟运行so的框架。有了它就不需要逆向so里面的算法了,我们只需要知道如何调用它就可以了,这是最理想的状态,实际上我们在使用unidbg过程中还需要解决很多问题的,比如说so可能限制unidbg,它会去频繁获取系统相关的东西,它会增加so对系统的依赖,但是unidbg并不是真正的安卓系统,假如so里面需要用到很多安卓系统相关的东西,那还需要补很多代码,就相当于补环境,跟网页js逆向的补环境是一个意思,但是这个效率肯定比前面高点,并且也更稳定,因为不需要开着app,直接把so拿过来就可以了。

抓包:毕竟我们是做安卓协议逆向的,那我们第一步就要进行抓包,很多app在第一步就已经限制你了,就是不让你抓到包。可以尝试

【中间人抓包方案】

  1. 【Charles + postern】
  2. 【httpcanary】
  3. 【fiddler】
  4. 【数字证书,证书信息、证书链】
  5. android证书】

安卓系统编译:通过系统编译,我们可以去修改系统源代码,然后把frida内置到系统,或者过掉一些反调试或者脱壳,反正很有用就对了。

那学完这套课程,后续还要学哪些内容呢?就针对安卓逆向协议来说,我们撇开手游什么的,比方说,socket协议也就是TCP相关的内容,VMP加固,OLLVM也就是so混淆相关的,ARM汇编…

2.什么是安卓app逆向

这里有这么个网站

公司可能有需求,给你一个任务,叫你爬取网站里面的,电影的名字,以及对应的评分,那这个时候怎么做呢?

首先第一步肯定要进行抓包,我们对这个网站进行抓包,
用【fiddler】

比如点击【最新】,在左侧会抓到一系列的数据包

上方是提交的数据,下方是服务器的响应。


说明这些信息,就在这个包中。

那如果我们能通过软件去得到这些信息,是不是就完成这个任务了?

说白了,你在浏览器上做的点击的动作,实际上是发送数据包,去下载一系列的图片,前台通过js,css,html把这里面的信息显示在网页上,供你方便查看,可以理解为就是图形化显示,实际上,真正服务器上关心的是什么东西呢?它关心你怎么点击这个东西吗?肯定不是。它关心的是你发送给它的数据包,是不是符合它的要求,只要你发送的数据包符合它的要求,那么它就会返回给你想要的信息。如果不符合,就返回错误信息。

服务器是不会管你怎么点击的,不管是自动化工具还是按键精灵等等点击的,还是你真正人工点的,它看的都是你发送给它的数据包


也就是说,我们只要把上面的数据包伪造出来,并且认为就是我们浏览器真实发送的,那我们就可以得到想要的信息,实际上,确实是这样子的。

也可以重放

可以相当于这个东西就是postman


就是发送数据包给服务器。

这就需要涉及到HTTP协议了

此时我们可以看到,除了cookie以外,其它都是明文,相关的知识,后面再说。
也就是如果我们要去伪造http协议,相当于选中的信息都是已知的。

https://movie.xxx.xxx/j/search_tags?type=movie&tag=%E6%9C%80%E6%96%B0&source=index

在上方我们发现,除了tag之外都是固定的值,只有这里是我们看不懂的东西,实际上这个就是URL编码


伪造这个数据包 就可以得到我们想要的电影的名字以及评分
这个数据包里面都是明文,这时候需不需要逆向呢?不需要。

在了解完这些事情后,我们再来了解下安卓逆向在做什么事情。


此时我们安装一个app

然后打开后,在登录页面

比如点击【登录】后提示【该帐号还未注册】,实际上这里的操作,你可以想象一下,输入帐号和密码以及点击【登录】,这一步操作是不是也会像服务器发送请求?如果我们帐号和密码符合它的要求的话,那么我们就可以登录成功,如果不正确就会登录失败等等,这时候我们第一反应是不是也要去抓个包呀?

这里用httpcanary进行抓包


打开后,在右下角启动,然后切回去登录


然后停止抓包

然后这里就有相关login的链接,我们点开


这里就有结果,说明这个确实是我们登录请求。


说白了,我们刚才点击的操作,是像服务器发送了个POST请求,然后这里面的信息,我们就不能随意伪造了

比如上面两个,我们不知道是什么,下面有password,我们输入的密码也不是这样的,说明加密了。其中username是知道的,是手机号,timestamp是时间戳

这时候我们就发现了一个问题,当我们抓到的数据包里面,有一些数据没法伪造的时候,这时候就需要逆向了。

逆向是什么呢,就是从app里面,去把它生成 加密值 的算法给它逆向出来,给它还原出来,就是说 password 是怎么样从我输入的 a12345678 变成 c5371…92df 的 给它找出来,这时候我们把算法还原出来了,就可以给算法传递这么个值,让它计算得到这么个结果,再提交给服务器,那是不是就可以伪造这个数据包了,就可以完成我们的登录了。

安卓逆向,就是做这些事情,就是从app里面,把一系列算法给它还原出来,也可以不还原,总之最后要得到这个密文,就是我们给它传递一个明文,它给我们返回一个密文。

至于何种方式,不管。

我们可以通过hook主动调用进行算法转发 或者 也可以做算法还原,也可以把响应的 dex,so文件拿过来调用里面的算法也可以,最终就是把原文传过去得到真正的密文,这样服务器就认可了,认为你数据包没问题,就给你登录了。

App逆向入门

day05 App逆向快速上手

app逆向课程:

  • 环境搭建 & Java语法基础 & 安卓开发 & NDK
  • 案例

平台:

  • Web框架

今日概要:app逆向的快速上手和必备环境搭建(上)。

  • 安装模拟器(网易mumu)
  • 抓包工具 charles 、Drony
  • 反编译工具:jeb、jadx、GDA(Java代码)

1.安装模式器

PC上的安卓模拟器有很多。

  • windows,逍遥、夜神、网易mumu 等。
  • mac,网易mumu。注意:暂不匹配mac的m1。

下载地址:https://mumu.163.com/mac/index.html

注意:安装后请开启root权限。

2.抓包工具 charles

2.1 下载并安装

  • mac用户
  访问网址 https://xclient.info/s/charles.html 根据提示下载并破解。
  • windows用户:
  下载地址:( charles v4.5.6,大家也可去网上自行下载和破解 )
  	链接: https://pan.baidu.com/s/1gedTGrFsB1SnNwTvPLoZPg 提取码: cjfe 
  	
  注册码
  	Registered Name:  https://zhile.io
  	License Key:      48891cf209c6d32bf4

2.2 配置

打开 【Proxy】>【Proxy Settings】设置代理IP端口:

查看本地charles的IP地址:【Help】>【Local IP Address】

模拟器连接 charles代理:填写IP和端口。

默认这样设置charles只能抓http的包,无法抓取Https包,所以需要在手机上安装证书。

设置SSL代理:【Proxy】->【SSL Proxy Settings】

手机上安装证书:




提醒:

  • 在模拟器上配置charles,安装证书。
  • 在安卓手机上配置charles
    • 手机 android7以下,可以用上面的方法 ----> 受信任的证书。
    • 手机不能用上面的方法(获取root权限)----> 用户级别、系统级别。

安装成功后,就可以在charles中进行抓包了。

案例:链家APP

import requests

res = requests.get(
    url="https://app.api.lianjia.com/Rentplat/v2/house/list",
    params=
        "city_id": "110000",
        "condition": "shahe2%2F",
        "offset": "0",
        "limit": "30",
        "scene": "list",
        "isMyCompany": "0",
        "is_second_filter": "0",
        "request_ts": "1632724797"
    
)

print(res.json())

3.抓包工具 Drony

有些安卓开发在OKHttp设置Proxy.NO_PROXY来屏蔽系统代理。

例如:得物app无法抓到包,无法看到有用数据。

3.1 安装

  • Drony-102.apk ,模拟器上可以;手机提示要升级。(英文)

    链接: https://pan.baidu.com/s/1qvWuuvazhbmJ3x0YdIWvNQ 提取码: g403 
    
  • Drony-1.3.154.apk,手机上(繁体中文)

    链接: https://pan.baidu.com/s/1b5iDxb8lmKfn2qzBxxT_kA 提取码: sa09 
    

3.2 配置








3.3 成功抓到

4.案例:B站app播放

  • 在模拟器安装B站 v6.24.0 版本

    https://www.wandoujia.com/apps/281291/history
    
  • 运行并抓包

  • 请求头中的4个算法,找到。

  • 请求体加密2个算法,找到。【今天来搞它,我来搞,你们看。】

  安卓开发 Java  --> bili v6240.apk   ---> 反编译   ---> Java代码(部分)

发现请求体发送的是密文,这是怎么回事呢?

这不是没抓到包,而是B站内部在发送数据时对请求体进行了加密,所以我们看到得就是密文了。

那么问题来了?如果我们要模拟B站发送这个请求,是不是就需要了解他这个请求体到底是用什么方式加密的,然后才能去伪造。

此时怎么办呢?接下来就需要一个反编译工具,将B站的apk文件反编译成安卓代码(Java代码),我们通过研究Java代码来查看他的内部算法,从而用Python来直接实现。

5.反编译工具

现在比较好用且方便的反编译工具:JEB、JADX、GDA(win)。(依赖JRE)

5.1 安装JDK

其实此处安装JRE即可,由于后续我们要学习Java开发,直接安装JDK。(JDK中包含JRE)。

https://www.oracle.com/java/technologies/downloads/

# 推荐:JDK8(后期工具需要)
https://www.oracle.com/java/technologies/downloads/#java8

5.2 jadx

下载地址:链接: https://pan.baidu.com/s/1urJFD5mMp7W1mlH6UAMlJA 提取码: k9p7

免安装,直接解压并打开即可。


反编译出来之后,就需要根据 关键字、调用关系、Hook(后期讲)的方式找到相关代码。

5.3 jeb

下载:链接: https://pan.baidu.com/s/15JAJBLROsSG4VEsOL8QoMA 提取码: ucv0

免安装,解压后直接打开:

5.4 GDA

下载:链接: https://pan.baidu.com/s/1sJdUOgiRA9G_2a8oOed12g 提取码: wwaw

仅支持window版本。

6.案例:B站app播放

现在,请求包抓到了,反编译的代码也有了,接下来,就要定位到代码具体在哪里了。

那么问题来了,怎么才能定位到代码的位置呢?

  • 根据关键字搜索
  • 看得懂Java代码,掌握安卓开发的基础。
  • 根据经验去猜测 并 验证(配合后期的Hook)

jeb3.0 版本反编译的代码( B站v6.42版本)

jeb3.24 版本反编译代码(B站6.24版本)

分页和验证过程:

  • 按照key排序进行拼接
  • 将拼接后的内容进行 sha256加密(盐:9cafa6466a028bfb)
  • 再拼接 sign=加密后的值
  • 对最后加密的值再进行 AES加密(key=fd6b639dbcff0c2a1b03b389ec763c4b、iv=77b07a672d57d64c)

用Python代码实现。

import base64
import requests
import re
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import string
import random
import time
import hashlib

SALT = "9cafa6466a028bfb"
KEY = "fd6b639dbcff0c2a1b03b389ec763c4b"
IV = "77b07a672d57d64c"


def get_video_info(bvid):
    session = requests.Session()
    res = session.get(
        url="https://api.bilibili.com/x/player/pagelist?bvid=&jsonp=jsonp".format(bvid),
    )
    cid = res.json()['data'][0]['cid']

    res = session.get(
        url="https://api.bilibili.com/x/web-interface/view?cid=&bvid=".format(cid, bvid),
    )
    res_json = res.json()
    aid = res_json['data']['aid']
    view_count = res_json['data']['stat']['view']
    # total_duration = res_json['data']['duration'] # 总时长
    duration = res_json['data']['pages'][0]['duration']  # 当前视频长度

    return aid, bvid, cid, view_count, duration


def create_random_mac(sep=":"):
    """ 随机生成mac地址 """

    def mac_same_char(mac_string):
        v0 = mac_string[0]
        index = 1
        while index < len(mac_string):
            if v0 != mac_string[index]:
                return False
            index += 1
        return True

    data_list = []
    for i in range(1, 7):
        part = "".join(random.sample("0123456789ABCDEF", 2))
        data_list.append(part)
    mac = sep.join(data_list)

    if not mac_same_char(mac) and mac != "00:90:4C:11:22:33":
        return mac

    return create_random_mac(sep)


def create_device_id(mac):
    """
    根据mac地址生成 3.device_id
    :param mac: 传入参数的格式是 00:00:00:00:00
    :return:
    """

    def gen_sn():
        return "".join(random.sample("123456789" + string.ascii_lowercase, 10))

    def base64_encrypt(data_string):
        data_bytes = bytearray(data_string.encode('utf-8'))
        data_bytes[0] = data_bytes[0] ^ (len(data_bytes) & 0xFF)
        for i in range(1, len(data_bytes)):
            data_bytes[i] = (data_bytes[i - 1] ^ data_bytes[i]) & 0xFF
        res = base64.encodebytes(bytes(data_bytes))
        return res.strip().strip(b"==").decode('utf-8')

    # 1. 生成mac地址(保证mac中的每个元素是不重复的,例如:0000000000)
    mac_str = mac

    # 2. 去除IP地址中的符号,只保留 48e1e828e02e(变小写)
    mac_str = re.sub("[^0-9A-Fa-f]", "", mac_str)
    mac_str = mac_str.lower()

    # 3. 获取手续序列号
    sn = gen_sn()

    # 4. 拼接并进行base64加密
    total_string = "|||".format(mac_str, sn)
    return base64_encrypt(total_string)


def sha_256_encrypt(data_string):
    sha = hashlib.sha256()
    sha.update(data_string.encode('utf-8'))
    sha.update(SALT.encode('utf-8'))
    return sha.hexdigest()


def aes_encrypt(data_string):
    aes = AES.new(
        key=KEY.encode('utf-8'),
        mode=AES.MODE_CBC,
        iv=IV.encode('utf-8')
    )
    raw = pad(data_string.encode('utf-8'), 16)
    return aes.encrypt(raw)


def run():
    mac_string = create_random_mac().upper()
    device_id = create_device_id(mac_string)

    aid, bvid, cid, view_count, duration = get_video_info("BV1Mb4y1X73e")

    ctime = int(time.time())
    info = 
        'aid': aid,
        'cid': cid,
        'part': 1,
        'mid': 0,
        'lv': 0,
        'ftime': ctime - random.randint(100, 1000),
        'stime': ctime,
        'did': device_id, # 设备ID
        'type': 3,
        'sub_type': 0,
        'sid': '0',
        'epid': '',
        'auto_play': 0,
        'build': 6240300,
        'mobi_app': 'android',
        'spmid': 'main.ugc-video-detail.0.0',
        'from_spmid': 'search.search-result.0.0'
    
    # 根据key进行排序
    data = "&".join(["=".format(key, info[key]) for key in sorted(info.keys())])
    # sha_256_encrypt加密
    sign = sha_256_encrypt(data).lower()
    
    data = "&sign=".format(data, sign)
    
    # AES加密
    aes_string = aes_encrypt(data)
    print(aes_string)


if __name__ == '__main__':
    run()

总结

  • 爬虫开发
    • js玩的6
    • Java和安卓看不懂
  • 一定要来听课。

今日安装安装包汇总地址:

链接: https://pan.baidu.com/s/1OF6-5y3GHz1nX3oJUzkG0g 提取码: unwk 

以上是关于《APP逆向学习》课程介绍和什么是安卓app逆向?的主要内容,如果未能解决你的问题,请参考以下文章

什么是Android逆向?如何学习安卓逆向?Android逆向自学笔记入门到实战

App逆向入门

App逆向入门

App逆向入门

一文了解安卓APP逆向分析与保护机制

安卓逆向之某省回头车App最新版vartmp加解密算法