app:2.66.0
接口:https://app.api.ke.com/api/secondhand/resblock/framegroup/list?resblockId=2413600823455693
需求:获取贝壳小区里面的户型图数据

接口分析:
通过抓包可以发现,里面有很多的字段都看不太懂,我们可以通过撰写请求来确定哪些字段是需要携带,哪些是不需要携带的,其中 Cookie
,Device-id-s
,Authorization
字段是一定需要携带的
cookie可以固定,只需要一个 lianjia_udid
字段就可以了
Device-id-s
跟设备有关,这里也可以写死
wll-kgsa
这个字段在高版本的app中,需要携带,使用低版本就不需要携带,请求头中还需要携带 Lianjia-Version
等跟版本有关的 字段才行
Authorization
这个字段必须携带,基本贝壳app所有的接口都必须携带,这个接口应该是跟请求体有关,相同的请求参数该字段相同

请求代码:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| list_headers = { 'x-req-id': '3726645a-df8c-464b-9341-3ca5d345a820', 'Page-Schema': 'community%2Fhuxing%2Flist', 'Referer': 'communitydetail', 'Cookie': 'lianjia_udid=0fc0690f43831ba1;', 'Dynamic-SDK-VERSION': '1.1.0', 'Lianjia-City-Id': '440300', 'parentSceneId': '6530638482656836097', 'source-global': '{}', 'User-Agent': 'Beike2.89.0;samsung SM-G973N; Android 9', 'Lianjia-Device-Id': '0fc0690f43831ba1', 'Lianjia-Version': '2.66.0', 'Lianjia-Im-Version': '2.34.0', 'Lianjia-Recommend-Allowable': '1', 'Authorization': 'MjAxODAxMTFfYW5kcm9pZDpiY2YzYTI4ZDE2MWU3Yzc3NTBiYzQ5OGFmZjQyZjNhNGRlYWVlOGQ1', 'Device-id-s': '0fc0690f43831ba1;;020102Sl74XvwW/Nx/ybVLmW6etX19zcPY6VWmllYXVAk+iYbWtHbEJNSVm9jF72ngVm1xRYM2JLG7wRzFcludQvy5RA==', 'Channel-s': 'Android_ke_tencentt', 'AppInfo-s': 'Beike;2.66.0;2660100', 'Hardware-s': 'samsung;SM-G973N', 'SystemInfo-s': 'android;9', 'Host': 'app.api.ke.com', 'Connection': 'Keep-Alive', 'Accept-Encoding': 'gzip', } list_url = 'https://app.api.ke.com/api/secondhand/resblock/framegroup/list?resblockId={}'.format(house_id) print(list_url) res = requests.get(url=list_url, headers=list_headers, timeout=8) res.encoding = 'utf-8' res_json = json.loads(res.text) print(res_json) frame_list = res_json['data']['list'] if len(frame_list): for img_list in frame_list: for data in img_list['list']: item = {} item['unit'] = data['title'].split('/')[0] item['area'] = data['title'].split('/')[1] item['orientation'] = data['title'].split('/')[2] item['title'] = data['title'] item['img_url'] = data['picUrl'] print(item)
|
字段获取:
使用jadx进行反编译,搜索关键字 Authorization
或者 "Authorization"
搜出来的结果不算多,可以挨个点进去看一下。我们这里直接看这个拦截器,猜测一下,既然每个接口都会携带,那应该都需要经过某个拦截器才对,我们进来看一下

进来之后,我们可以看到这里应该是添加了一个okhttp的的拦截器,我们在业内搜索 signRequest
关键字,最终在下面我们可以看到对应的拦截器方法


我们采用firda hook的形式去看一下这个方法返回什么,结果竟然报错了,猜测是反编译导致方法名的问题,我们换个版本的jadx重新编译一下

换了个 1.2.0版本的jadx,这次反编译没有问题, 也是成功的验证了这个方法返回的就是我们要的 "Authorization"
字段


我们来看一下这个方法,第一个判断条件是判断请求url是否是布尔类型,第二个判断条件是请求方式是否为get, 我们可以直接看 com.bk.base.netimpl.a.ji().getSignString(uri, hashMap);
下面为判断是否是post请求,已经还对请求体做了处理,猜测如果是post请求,那么请求体应该也会被参与计算中去

我们直接右键 跳到声明

我们可以继续hook一下这个方法,看一下传入的两个值是什么

接收的就是请求的url链接,第二个参数如果是post请求,则传入的是请求体,我们再看一下这个方法做了什么

我们从下往上看 我们最终要 encodeToString
encodeToString
是 str2 和 SHA1ToString 组合后加工而成
str2 是跟前后两个参数有关,一个是 httpAppId 一个是httpAppSecret
SHA1ToString 是对sb参数做了处理
sb视乎是跟请求参数有关
最为重要的是它的数据是经过base64处理后得到的,那我们看一下base64处理之前是什么样子的

这下我们可以确定str2就是 20180111_android 这个值固定, 接下来 我们只要知道 DeviceUtil.SHA1ToString(sb.toString())
做了哪些事情 我们就完成了


同样的,hook一下这个方法,可以看到传入的值当中,后面的是请求体,前面一部分看上去也像是md5加密后的结果,他们拼接之后做了sha1加密
1
| https://app.api.ke.com/api/secondhand/resblock/framegroup/list?resblockId=2411048421652
|


我们多翻几个页面,看一下这个值会不会发生变化,经过测试之后,我们可以看到,这就是个固定值,即便重启app,这个值也不会发生变化

现在 我们可以总结一下, 有两个固定值,一个是 d5e343d453aecca8b14b2dc687c381ca
拼接请求体后进行sha1加密,另一个固定值是
20180111_android:
加上上面sha1加密后在进行base64编码,就得到了 Authorization字段
改写一下代码,也是完全没有问题,可以成功获取到我们想要的数据

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| import hmac import multiprocessing import string import urllib
import requests, re, random, json, time from lxml import etree import pymysql import hashlib from base64 import b64encode, b64decode
def get_img_list(): time_stamp = int(time.time()) house_id = '2413600823455693' secret_str = 'd5e343d453aecca8b14b2dc687c381caresblockId={}'.format(house_id) hash_secret = hashlib.sha1(secret_str.encode("utf-8")).hexdigest() base_secret = '20180111_android:' + hash_secret base64_secret = b64encode(base_secret.encode()).decode('utf-8') print(base64_secret) list_headers = { 'x-req-id': '3726645a-df8c-464b-9341-3ca5d345a820', 'Page-Schema': 'community%2Fhuxing%2Flist', 'Referer': 'communitydetail', 'Cookie': 'lianjia_udid=0fc0690f43831ba1;', 'Dynamic-SDK-VERSION': '1.1.0', 'Lianjia-City-Id': '440300', 'parentSceneId': '6530638482656836097', 'source-global': '{}', 'User-Agent': 'Beike2.89.0;samsung SM-G973N; Android 9', 'Lianjia-Device-Id': '0fc0690f43831ba1', 'Lianjia-Version': '2.66.0', 'Lianjia-Im-Version': '2.34.0', 'Lianjia-Recommend-Allowable': '1', 'Authorization': base64_secret, 'Device-id-s': '0fc0690f43831ba1;;020102Sl74XvwW/Nx/ybVLmW6etX19zcPY6VWmllYXVAk+iYbWtHbEJNSVm9jF72ngVm1xRYM2JLG7wRzFcludQvy5RA==', 'Channel-s': 'Android_ke_tencentt', 'AppInfo-s': 'Beike;2.66.0;2660100', 'Hardware-s': 'samsung;SM-G973N', 'SystemInfo-s': 'android;9', 'Host': 'app.api.ke.com', 'Connection': 'Keep-Alive', 'Accept-Encoding': 'gzip', } list_url = 'https://app.api.ke.com/api/secondhand/resblock/framegroup/list?resblockId={}'.format(house_id) print(list_url) res = requests.get(url=list_url, headers=list_headers, timeout=8) res.encoding = 'utf-8' res_json = json.loads(res.text) print(res_json) frame_list = res_json['data']['list'] if len(frame_list): for img_list in frame_list: for data in img_list['list']: item = {} item['unit'] = data['title'].split('/')[0] item['area'] = data['title'].split('/')[1] item['orientation'] = data['title'].split('/')[2] item['title'] = data['title'] item['img_url'] = data['picUrl'] print(item)
get_img_list()
''' lianjiabeike://ershou/community/frameList?resblockId=2411049483671&roomKey=l2 https://apps.api.ke.com/house/resblock/detailpart1v2?id={} '''
|