Android逆向猿人学2022年app比赛第四题grpc与Protobuf使用(步步验证)

news2024/11/27 2:15:17

教程

  • 前言
  • 一、起步
  • 二、抓包
  • 三、分析
  • 四、验证
  • 五、HOOK
  • 借鉴

前言

前面2-3题和第一题解题思路基本上一样的,这里就不出教程了,这篇文章比较繁琐,基本上描述了我做这题的思路,有很多走不通的地方也有对应的方法,所以会比较长,没有耐心的朋友可以看看其他人较为简短的文章(逆向这东西主要看思路,代码量其实对比后端没多少的)

一、起步

先打开APP,打开第四题,可以发现是要计算数字之和的题目
在这里插入图片描述

二、抓包

打开HttpCanary(小黄鸟)抓包
在这里插入图片描述
嗯,上面是一串我们看不懂的返回结果,请求头很明显的告诉了我们这是grpc请求,那我们先去看看这个乱码是怎么传进去的。

三、分析

把jadx给打开,把yuanrenxuem109.apk给拖进去反序列化分析一下。老样子编译好后,进行搜索上面请求的网址关键字段SayHello,看看能不能找到一些什么。
在这里插入图片描述
结果只有一行,是下面这个函数,大致看了一下,太多混淆的变量名了,直接先hook一波这个方法的返回结果看看。
在这里插入图片描述
一样复制为frida片段
在这里插入图片描述
创建test.js文件输入frida代码:

function main() {
    Java.perform(function () {
            let OooO00o = Java.use("oOO00O.OooO00o");
            OooO00o["OooO00o"].implementation = function () {
                console.log(`OooO00o.OooO00o is called`);
                let result = this["OooO00o"]();
                console.log(`OooO00o.OooO00o result=${result}`);
                return result;
            };

        }
    )

}

setImmediate(main)

启动frida

>adb shell
>su
>cd data/local/tmp
>./frida-server-16.0.2-android-arm64  -l 0.0.0.0:8881

转发端口

>adb forward tcp:8881 tcp:8881

frida调用hook代码

> frida  -H 127.0.0.1:8881 -f com.yuanrenxue.match2022 -l test.js

最后输出结果:
在这里插入图片描述
可以看到没有我们想要的参数信息,那就换一个参数搜索分析。回顾上面抓包详情,我们这次搜h2字段。
在这里插入图片描述
点进去分析后可以发现,这实则是一个Protocol协议的内容,没有办法继续跟了。
在这里插入图片描述
那我们捋清思路,回想第一次打开APP第四题的时候会有个第四题的字样,那么这次搜一下第四题字样看看
在这里插入图片描述
点进去分析后,第一个只是字符串信息,没有什么有用的价值,第二个是一个类,我们可以慢慢看看这个类底下的方法
在这里插入图片描述
往下慢慢浏览可以观察到一个很敏感的单词sign
在这里插入图片描述
老样子先验证一下,复制frida片段,然后拖到test.js里面去跑起来看看
在这里插入图片描述
在这里插入图片描述
可以看到是跑出一个加密值了,此时对数据敏感度高的读者应该就能联想到最开始抓包那串乱码后面也有一串数字,看起来和这个长度很像,既然想到了,那就打开抓包和他验证一番。
在这里插入图片描述

  • 可以看到是一模一样的,但是前面那串乱码又是什么东西呢。
  • 从截图可以知道这个以grpc框架为基础发送的请求,grpc是以Protobuf序列化协议开发的,发送的数据包的格式也都是Protobuf协议规定的格式,具体Protobuf的一些基础使用可以看完上篇文章。(grpc详情看这里)
  • 那我们怎么把这个乱码结果给复制出来,并且用Protobuf去解析呢。用HttpCanary的保存按钮,选择保存请求体,给文件夹命名,然后request_body.bin(这个文件就代表了Protobuf数据文件,不懂的可以看我上篇文章的序列化后保存的文件,也是一个bin格式的)就会存在我们的HttpCanary/你命名的文件夹下/
    在这里插入图片描述
    在这里插入图片描述
    在这里我使用手机USB连接电脑,直接打开我们刚刚存bin文件的路径,把它复制出来到自己随便哪一个文件夹。然后用上篇文章Protobuf说的无.proto语法文件进行反编译试一下。
    在这里插入图片描述
    会直接发现,是显示错误输入,按照道理来说是不可能的啊。这个时候我们就需要去看看问文件内容到底长啥样,和我们之前的bin文件差别在哪里。打开request_body.bin和上次自己序列化出来的my_example.bin文件对比一下。我这里用的是010Editor软件中文汉化版打开两个文件。
    在这里插入图片描述
    在这里插入图片描述
    可以大致的看到0801前数据都是不一样的,那么我们就把request_body.bin前面修改成my_example.bin前面一样看看是否符合格式去反序列化。
    在这里插入图片描述
    直接双击就可以修改了,再次运行还是报错。
    在这里插入图片描述
    这个时候我突然想起来了,一份编码规范Protocol Buffers编码详解,例子,图解,在里面介绍了这种情况

在这里插入图片描述
再回顾一下,上一篇文章的proto语法,对照这个int32的key,是不是就代表了my_example.bin文件中08后面的才是真的数据,我们直接把my_example.bin文件中的0A15删除,反编译看一下
在这里插入图片描述
在这里插入图片描述
可以看到成功编译出来了,0A15其实是可以不需要的
在这里插入图片描述
那么我们也可以去把request_body.bin文件的0A15删除,看看能否得到数据
在这里插入图片描述
可以看到成功解析出数据了
在这里插入图片描述
那么之前的0A15是什么呢,这是没去掉0A15的例子,打印出来的是多了一个1{}
在这里插入图片描述
对比了一下之前写的.proto语法文件可以发现,其实0A15对应的是下面框选出来的message!至于request_body.bin的1B是什么暂时不得而知。毕竟是不知道proto语法文件格式反编译出来的。
在这里插入图片描述

四、验证

经过了上面的反编译结果,我们大概是知道了request_body.bin文件只有3个字段,那么我们可以开始编写.proto语法文件了,然后用grpc去尝试请求看看对不对。
在这里插入图片描述

安装一下grpc需要的依赖环境

pip install grpcio
pip install grpcio-tools

创建一个.proto语法文件,根据上面反序列化request_body.bin文件得到的3个字段,键名自己随便取,看他像什么取什么。

// 请求是http://180.76.60.224.9901/challenge.Challenge/SayHello
// 指定版本为proto3,默认为proto2
syntax = "proto3";
// 根据请求定义包名
package challenge;

// 定义请求结构
message RequestMessage {
    // 使用 int32 类型的字段,字段编号为 1
    int32 page = 1;
    // 使用 int64 类型的字段,字段编号为 2
    int64 time = 2;
    // 使用 string 类型的字段,字段编号为 3
    string sign = 3;
}

// 根据请求定义一个名为 Challenge 的服务
service Challenge {
    // 根据请求定义定义一个名为 SayHello 的远程过程调用(RPC)方法
    rpc SayHello(RequestMessage) returns (ResponseMessage) {}
}

// 定义返回内容的数据结构
message ResponseMessage {
    //item为data子集, 可以拥有多个item
    repeated Item data = 1;
}

message Item {
    string value = 1;
}

然后通protoc.exe编译生成_pb2.py文件和_pb2_grpc.py文件

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. myprotobuf.proto

接着编写main.py文件,去将我们之前反序列化出来的数据,组合成编写的.proto语法结构数据,给发送过去,然后打印结果。

import grpc
import myprotobuf_pb2
import myprotobuf_pb2_grpc
from google.protobuf import json_format


def run(page_num, now_time, sign):
    with grpc.insecure_channel('180.76.60.244:9901') as channel:
        setup = myprotobuf_pb2_grpc.ChallengeStub(channel)

        requests_data = myprotobuf_pb2.RequestMessage()
        requests_data.page = page_num
        requests_data.time = now_time
        requests_data.sign = sign

        response = setup.SayHello(requests_data)
        json_string_request = json_format.MessageToJson(response)
    return json_string_request


if __name__ == '__main__':
    data = run(1, 1684980930731, "16ec5c17c7f7eadb")
    print(data)

可以看到数据成功打印出来,并且对应上了。
在这里插入图片描述

五、HOOK

接下来就是使用frida配合grpc实现一个翻页功能了。把上次分析的sign的frida代码拿过来,改写成主动调用。这是未改写前的

function main() {
    Java.perform(function () {
            let ChallengeFourFragment = Java.use("com.yuanrenxue.match2022.fragment.challenge.ChallengeFourFragment");
            ChallengeFourFragment["sign"].implementation = function (str, j) {
                console.log(`ChallengeFourFragment.sign is called: str=${str}, j=${j}`);
                let result = this["sign"](str, j);
                console.log(`ChallengeFourFragment.sign result=${result}`);
                return result;
            };

        }
    )


}

setImmediate(main)

结果如下图所示,很明显参数是1:1684991187938和1684991187938,对应上我们的app,就是(页数:时间戳,时间戳组)成的参数,最后得到sign参数
在这里插入图片描述
那我们接下来主动调用一下:

function main() {
    let result = 0;
    Java.perform(function () {
            let ChallengeFourFragment = Java.use("com.yuanrenxue.match2022.fragment.challenge.ChallengeFourFragment").$new();
            result = ChallengeFourFragment["sign"]("1:1684991187938",1684991187938 )
            console.log(result)
        }
    )
    return result;
    

}

setImmediate(main)

结果如下,和上面的数据一模一样,那么就可以开始进行python的rpc调用了。
在这里插入图片描述

function getSign(str, j) {
    let result = 0;
    Java.perform(function () {
            let ChallengeFourFragment = Java.use("com.yuanrenxue.match2022.fragment.challenge.ChallengeFourFragment").$new();
            result = ChallengeFourFragment["sign"](str, j)
        }
    )
    return result;
}


rpc.exports = {
    getsign: getSign
};

import frida
import sys

host = "127.0.0.1:8881"
def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)

with open('./test.js') as f:
    test_js = f.read()

# 启动方式1
manager = frida.get_device_manager()
# process =  manager.add_remote_device(host).get_frontmost_application()
# print(process)
process = manager.add_remote_device(host).attach("猿人学2022")
script = process.create_script(test_js)
script.on('message', on_message)
script.load()
print(script.exports_sync.getsign("1:1684991187938",1684991187938))
sys.stdin.read()

完成了rpc调用后得到sign后,我们可以开始加上我们的grpc代码进行翻页功能了。

import sys
import time
import myprotobuf_pb2
import myprotobuf_pb2_grpc
import frida
import grpc
from google.protobuf import json_format

host = "127.0.0.1:8881"
def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)

def run(page_num, now_time, sign):
    """
    gprc连接
    :param page_num:
    :param now_time:
    :param sign:
    :return:
    """
    with grpc.insecure_channel('180.76.60.244:9901') as channel:
        setup = myprotobuf_pb2_grpc.ChallengeStub(channel)

        requests_data = myprotobuf_pb2.RequestMessage()
        requests_data.page = page_num
        requests_data.time = now_time
        requests_data.sign = sign

        response = setup.SayHello(requests_data)
        json_string_request = json_format.MessageToJson(response)
    return json_string_request

def load_frida():
    """
    加载frida
    :return:
    """
    with open('./test.js') as f:
        test_js = f.read()
    # 启动方式1
    manager = frida.get_device_manager()
    process = manager.add_remote_device(host).attach("猿人学2022")
    script = process.create_script(test_js)
    script.on('message', on_message)
    script.load()
    return script


if __name__ == '__main__':
    script = load_frida()
    for page in range(1, 3):
        now_time = int(time.time() * 1000)
        sign = script.exports_sync.getsign(f"{page}:{now_time}",now_time)
        print(f"page: {page}, now_time: {now_time}, sign: {sign}")
        data = run(page, now_time, sign)
        print(data)

成功翻页,至此第四题结束:

在这里插入图片描述

借鉴

Protocol Buffers编码详解,例子,图解
猿人学-Android端爬虫比赛第四关【grpc】解题笔记

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/577862.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux网络初探

linux网络 1.1查看本机ip IP地址 IP地址网络地址主机地址,网络地址(网络号)相同的主机为本地网络中的主机,可以直接相互通信,而网络地址不同的主机为远程网络中的主机,相互通信必须通过本地网关&#xf…

Flutter Windows开发环境搭建教程与学习资料推荐

Windows应用软件开发有很多框架可以选择,例如比较流行的Electron、Qt、CEF、WPF、WinForm、MFC、DuiLib、SOUI等等。Flutter是近几年流行的全平台应用开发框架,可以进行Android、IOS、Web、MacOS、Windows、Linux等平台的应用软件开发。 一、Flutter介绍…

启动U盘制作工具Rufus 4.0.2035

Rufus是是一款小巧实用免费开源的帮助格式化和创建可启动USB闪存驱动器的工具,如USB钥匙/软盘、记忆棒等,可快速制作linux系统或者win启动u盘,可快速的将ISO镜像文件制作成可引导启动的USB启动盘,支持ISO镜像、GPT和UEFI&#xff…

设置参考文献编号与文中插入引用的具体步骤

目录 一、前言 二、操作步骤 (一)参考文献设置编号 (二)文章中引用参考文献方式 一、前言 本教程使用的软件是WPS 二、操作步骤 (一)参考文献设置编号 1.把引用文献的这个编号全部删掉 2.右键点击段…

学习笔记——vue中使用el-dropdown组件报错

今天在工作中,发现使用el-select做的下拉框,下拉菜单展开后,鼠标点击下拉框之外的区域时,下拉菜单没有收起。然后,我打开控制台,发现了这个错误。 Uncaught TypeError: Cannot read properties of null (re…

《Linux0.11源码解读》理解(四) head之重新设置IDT/GDT

上节提到,现在cs:ip指向0地址,此处存储着作为操作系统核心代码的system模块,是由head.s和 main.c以及后面所有源代码文件编译链接而成。head.s(以下简称head)紧挨着main.c,我们先执行head。 重新设置内核栈 _pg_dir: _startup_3…

堆(堆排序 模拟堆)

目录 一、堆的数据结构二、堆的操作方法往下调整的示意图往上调整的示意图相关功能的实现思路1.插入一个数2.求最小值3.删除最小值4.删除任意一个元素5.修改任意一个元素 三、堆的实战运用堆排序模拟堆 一、堆的数据结构 堆是一个完全二叉树:除了最后一层结点以外&…

C语言三子棋,五子棋,n子棋的代码实现

C语言三子棋,五子棋,n子棋的代码实现 这里以五子棋为例,来说明开发过程开发思路菜单打印棋盘的打印棋子的打印电脑下棋(随机数)判断输赢代码整合注意事项 这里以五子棋为例,来说明开发过程 其中该项目包含…

《用户增长方法论》从产品、渠道、营销创意等多个维度,搭建了一套完整的用户增长方法体系

关于作者 黄永鹏,目前在阿里巴巴担任高级用户增长专家。黄永鹏是一个典型的 “ 斜杠青年 ” ,十年前从广告咨询行业转战互联网,在 BAT 三家 公司都待过,负责过多款用户和日活过亿的产品,比如腾讯手机管家、百度地图…

chatgpt赋能python:Python练手:提高你的SEO技能

Python练手:提高你的SEO技能 在当今数字化时代,搜索引擎优化(SEO)成为了网站和企业在线成功的关键。优化技巧既可以提高网站的排名,还可以增加网站的可见性,从而吸引更多的流量和潜在客户。Python是一个适…

网络通信协议-ARP协议

目录 一、ARP协议 二、ARP协议通信过程 应用情景一:同一广播域内通信 (1)第一步:ARP协议通信 1.交换机接受消息 2.电脑2接收到广播消息 3.电脑2回复 4.交换机转发回复给电脑1 5.电脑1记录 (2)第二…

Go快速上手之基础语法 | 青训营笔记

Go快速上手之基础语法 | 青训营笔记 文章目录 Go快速上手之基础语法 | 青训营笔记系列介绍本文摘要1. Go 介绍2. Go 的环境配置2.1 :sparkles: IDE2.2 Gitpod 和 Jetbrians Gateway 的使用 3. Go的基础语法3.1 Hello World3.2 变量与常量3.3 条件控制语句…

Linux(进程间通信)

目录: 1.进程间通信的介绍 2.管道通信 3.管道的原理 ------------------------------------------------------------------------------------------------------------------------------- 1.进程间通信的介绍 2.管道通信 当我们在创建子进程时,我们的…

chatgpt赋能python:Python生成pyc文件的介绍

Python生成pyc文件的介绍 Python是一种解释型语言,但是在执行某些操作时,它会生成缓存文件,以便提高执行效率。这些缓存文件以 .pyc 扩展名保存在同一目录中。 在本文中,我们将重点介绍Python生成pyc文件,并探讨它们…

使用Python绘制M2货币供应率曲线

M2广义货币供应量:流通于银行体系之外的现金加上企业存款、居民储蓄存款以及其他存款,它包括了一切可能成为现实购买力的货币形式,通常反映的是社会总需求变化和未来通胀的压力状态。近年来,很多国家都把M2作为货币供应量的调控目…

Fedora安装并配置开启SSH服务相关命令

Ubuntu参考我这篇:虚拟机里安装ubuntu-23.04-beta-desktop-amd64,开启SSH(换源、备份),配置中文以及中文输入法等 一、过程 1、检测是否安装了openssh-server $ rpm -qa | grep openssh-serveropenssh-server-7.9p1-5.fc30.x86_642、如果上…

【web框架】——Django——如桃花来

目录索引 web框架介绍:常见软件的架构:*CS架构:**BS架构:* 网络通信:socket知识复习:*服务端代码逻辑:**客户端代码逻辑:* socket代码演示:*服务端代码演示:*…

chatgpt赋能python:Python生成:深入了解Python编程中的生成

Python 生成:深入了解Python编程中的生成 简介 Python是一门多用途编程语言,广泛应用于 Web 开发,数据分析,人工智能和科学计算等领域。在Python编程中,生成是一个强大而又常用的概念。本文将介绍Python编程中的生成…

如何在华为OD机试中获得满分?Java实现【输入n个整数,输出其中最小的k个】一文详解!

✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 🌟专栏地址: Java华为OD机试真题(2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述…

Java 的 String、StringBuffer 和 StringBuilder(一文讲透)

提到 String、StringBuffer 和 StringBuilder,就不得不谈及它们的历史,在了解它们的历史之后,我们对它们的理解将更上一级台阶! 发展历史 String 与 StringBuffer 的出现 String 和 StringBuffer 在 Java1.0 中就已经有了&…