bilibili-API-collect icon indicating copy to clipboard operation
bilibili-API-collect copied to clipboard

查 up 空间的 gRPC Proto 可能有问题?会报错 `-352`

Open djkcyl opened this issue 1 year ago • 31 comments

具体实现在这儿:https://github.com/SK-415/bilireq/blob/main/bilireq/grpc/utils/metadata.py

无论怎么发,这个 DynSpaceReq 都会报 -352 错误。

await grpc_get_user_dynamics(uid=491589125, auth=auth)
Traceback (most recent call last):
  File "/root/bilireq/bilireq/grpc/utils/__init__.py", line 48, in wrapper
    result = await func(
  File "/root/bilireq/bilireq/grpc/dynamic/__init__.py", line 26, in grpc_get_user_dynamics
    return await stub.DynSpace(req, **kwargs)
  File "/root/bilireq/.venv/lib/python3.10/site-packages/grpc/aio/_call.py", line 290, in __await__
    raise _create_rpc_error(self._cython_call._initial_metadata,
grpc.aio._call.AioRpcError: <AioRpcError of RPC that terminated with:
        status = StatusCode.UNKNOWN
        details = "-352"
        debug_error_string = "UNKNOWN:Error received from peer  {grpc_message:"-352", grpc_status:2, created_time:"2023-08-28T09:28:45.915381792+08:00"}"
>

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/root/bilireq/test/test_grpc.py", line 91, in <module>
    asyncio.run(main())
  File "/usr/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
    return future.result()
  File "/root/bilireq/test/test_grpc.py", line 74, in main
    dynamics = await grpc_get_user_dynamics(uid=491589125, auth=auth)
  File "/root/bilireq/bilireq/grpc/utils/__init__.py", line 66, in wrapper
    raise GrpcError(
bilireq.exceptions.GrpcError: 错误码: -352, 信息: -352

djkcyl avatar Aug 28 '23 01:08 djkcyl

device 改 android, 版本号改 7380300, channel 改 master 看看 image

cxw620 avatar Aug 28 '23 04:08 cxw620

我之前爬关注列表每次报错-352后,进去都要求做一个验证码,做了之后就不报错了

Werewolf-Wu avatar Aug 28 '23 05:08 Werewolf-Wu

安卓客户端吗?

djkcyl avatar Aug 28 '23 06:08 djkcyl

device 改 android, 版本号改 7380300, channel 改 master 看看 image

修改后无效,仍然 -352 image

djkcyl avatar Aug 28 '23 06:08 djkcyl

UA 改了没, 你抓个包看看和正常 APP 请求的差别. 还有发送时 gzip 要关掉.

cxw620 avatar Aug 28 '23 06:08 cxw620

UA 改了没, 你抓个包看看和正常 APP 请求的差别. 还有发送时 gzip 要关掉.

用的是grpc的请求库,其他所有接口都没问题,只有这一个接口是报错的

djkcyl avatar Aug 28 '23 06:08 djkcyl

最近风控是紧一点.

应该是可以自定义 UA, 关闭 gzip 的.

cxw620 avatar Aug 28 '23 06:08 cxw620

貌似没找到关闭 gzip 的方法,好像这个 python 的 grpc 库没提供 ua 的话用的是手机抓包抓来的,修改了 build 也依然不行

djkcyl avatar Aug 28 '23 06:08 djkcyl

image

djkcyl avatar Aug 28 '23 06:08 djkcyl

找到个 example, Ref: CSDN, Google

// Example: Change client compression at the call level
response, call = client.t_client.with_call(
        pb2.t_client_requests(data=str_random),
        compression=grpc.Compression.Gzip, // 这儿改 grpc.Compression.Deflate 看看
        metadata=((('client_key',"clinet_value"),)),
        timeout=10,
        # wait_for_ready=True,
    )  # timeout超出时长抛出异常

eid 那里怎么有个 \u003d, no padding 的. UA 里面也有版本号, 相对应改一下. access_key 截取前32位. grpc.biliapi.net 改 app.bilibili.com.

看看可不可行.

还有你 access_key, mid 漏了

cxw620 avatar Aug 28 '23 06:08 cxw620

上面的 json 是手机小黄鸟抓包抓来的, no padding 不清楚是为啥 ua的话版本号对上了,

 'user-agent': 'Dalvik/2.1.0 (Linux; U; Android 13; KB2003 '
               'Build/RKQ1.211119.001) 7.38.0 os/android model/KB2003 '
               'mobi_app/android build/7380300 channel/master innerVer/7380300 '
               'osVer/13 network/2 grpc-java-cronet/1.36.1',

截取了前 32 位也改了host,仍然报错

djkcyl avatar Aug 29 '23 03:08 djkcyl

发送端 Gzip 关了没, 我记得有个逆向大佬明确指出这玩意就是风控点

抓个包比对一下正常请求, 或者说 APP 的请求和你 python 程序发出的请求的差异, 包括请求头和发送的二进制内容. 可以抓包, APP 随意打开个动态, 然后拿 proto 解析一下 APP 请求的内容, 然后复制过来, 确保请求的 DynSpaceReq 是一致的, 然后比对发送的二进制内容.

如果还不行我就不太清楚为什么了. 参考 https://github.com/SocialSisterYi/bilibili-API-collect/issues/686, Web 端有信息上报的机制, 客户端也有信息上报的机制, 如果是这里的原因就相当麻烦了.

cxw620 avatar Aug 29 '23 10:08 cxw620

无法复现, 甚至请求头鉴权部分只剩空的x-bili-device-bin都可以 你可以试试手写grpc协议, 或者试试换个ip python:

import requests
from bilibili_pb2 import DynSpaceReq

msg = DynSpaceReq()
msg.host_uid = 491589125
# msg.local_time = 8
# msg.page = 1
proto = msg.SerializeToString()
print(proto)

data = b'\0' + len(proto).to_bytes(4) + proto

headers = {
    'User-Agent': 'Dalvik/2.1.0 (Linux; Android) os/android',
    'Content-Type': 'application/grpc',
    'x-bili-device-bin': '',
}

resp = requests.post('https://app.bilibili.com/bilibili.app.dynamic.v2.Dynamic/DynSpace', data=data, headers=headers)
resp.raise_for_status()

print(len(resp.content) > 1000)
# True

proto:

syntax = "proto3";

package bilibili.app.dynamic.v2;

message DynSpaceReq {
    int64 host_uid = 1;
    int32 local_time = 4;
    int64 page = 5;
}

My-Responsitories avatar Aug 29 '23 13:08 My-Responsitories

无法复现, 甚至请求头鉴权部分只剩空的x-bili-device-bin都可以 你可以试试手写grpc协议, 或者试试换个ip python:

我瞅着就像 gzip 暴露了客户端, 要不然就是 IP 黑了或者 access_key 黑了...

不过手搓 gRPC 消息就真的猛了 😂

cxw620 avatar Aug 29 '23 14:08 cxw620

前几天我爬太猛了估计 b站就加了这个-352 换个ip就好了

stmtc233 avatar Aug 30 '23 00:08 stmtc233

无法复现, 甚至请求头鉴权部分只剩空的x-bili-device-bin都可以 你可以试试手写grpc协议, 或者试试换个ip python:

我瞅着就像 gzip 暴露了客户端, 要不然就是 IP 黑了或者 access_key 黑了...

不过手搓 gRPC 消息就真的猛了 😂

那更不对了啊,因为加不加 access_key,甚至换ip,加代理都没用,该不会真是这个python库的问题吧(

djkcyl avatar Aug 30 '23 00:08 djkcyl

无法复现,甚至请求头鉴权部分空间的x-bili-device-bin都可以你可以尝试手写grpc协议,或者尝试换个ip python:

我瞧着就像gzip暴露了客户端,要不然就是IP黑了或者access_key黑了... 手搓gRPC消息就真的猛了😂

那更不用了啊,因为加不加access_key,甚至换ip,加代理都没用,该不会真是这个python库的问题吧(

-352应该是23号加的 我感觉就是ip问题 23号之前我爬动态爬了10多t流量 好像就是那天早上加了-352 在此之前是没有的 我用了代理池就没这个问题 库问题应该是4xx 你可以用go试下 如果还是-352那应该就是ip问题 开tun模式试下

stmtc233 avatar Aug 30 '23 01:08 stmtc233

我试了下,删掉了所有的metadata,只留了ua和device bin,出了个新的报错:

这是开了 grpc.Compression.Deflate 的

(bilireq-3.10) root@as:~/bilireq# python test/test_grpc.py 
{'user-agent': 'Dalvik/2.1.0 (Linux; Android) os/android',
 'x-bili-device-bin': b''}

grpc.aio._call.AioRpcError: <AioRpcError of RPC that terminated with:
        status = StatusCode.UNIMPLEMENTED
        details = "grpc: Decompressor is not installed for grpc-encoding "deflate""
        debug_error_string = "UNKNOWN:Error received from peer  {grpc_message:"grpc: Decompressor is not installed for grpc-encoding \"deflate\"", grpc_status:12, created_time:"2023-08-30T09:06:53.605232597+08:00"}"
>

这是开了 grpc.Compression.NoCompression 和 grpc.Compression.Gzip 的

grpc.aio._call.AioRpcError: <AioRpcError of RPC that terminated with:
        status = StatusCode.UNKNOWN
        details = "Stream removed"
        debug_error_string = "UNKNOWN:Error received from peer ipv4:114.230.222.142:443 {created_time:"2023-08-30T09:32:16.5829016+08:00", grpc_status:2, grpc_message:"Stream removed"}"
>

这个报错在半个月之前是加了个 buvid 就解决了,然后现在加 buvid 也不管用了,貌似我只要给 device-bin 里填充了内容就会 -352

djkcyl avatar Aug 30 '23 01:08 djkcyl

用 httpx 手搓的grpc请求是正常的,换了库就出问题了

msg = DynSpaceReq()
msg.host_uid = 491589125
# msg.local_time = 8
# msg.page = 1
proto = msg.SerializeToString()
print(proto)

data = b"\0" + len(proto).to_bytes(4, "big") + proto

headers = {
    "User-Agent": "Dalvik/2.1.0 (Linux; Android) os/android",
    "Content-Type": "application/grpc",
    "x-bili-device-bin": "",
}

resp = httpx.post("https://app.bilibili.com/bilibili.app.dynamic.v2.Dynamic/DynSpace", data=data, headers=headers)
resp.raise_for_status()

gresp = DynSpaceRsp()
gresp.ParseFromString(resp.content[5:])

print(gresp)

image

djkcyl avatar Aug 30 '23 01:08 djkcyl

那有可能是这个库存在什么特征, 给针对性 ban 掉了, 你有抓包对比过二进制数据了吗. 不过我用 Rust 的 tonic 是没问题的, 可能因为 tonic 并不是 gRPC 的官方实现?

cxw620 avatar Aug 30 '23 08:08 cxw620

这是开了 grpc.Compression.NoCompression 和 grpc.Compression.Gzip 的

grpc.aio._call.AioRpcError: <AioRpcError of RPC that terminated with:
        status = StatusCode.UNKNOWN
        details = "Stream removed"
        debug_error_string = "UNKNOWN:Error received from peer ipv4:114.230.222.142:443 {created_time:"2023-08-30T09:32:16.5829016+08:00", grpc_status:2, grpc_message:"Stream removed"}"
>

这个报错在半个月之前是加了个 buvid 就解决了,然后现在加 buvid 也不管用了,貌似我只要给 device-bin 里填充了内容就会 -352

Stream removed 似乎不是风控的, 换 grpc.biliapi.net 这个原生 gRPC 接口看看? Ref: https://lightrun.com/answers/adap-flower-rpc-error-stream-removed

cxw620 avatar Aug 30 '23 08:08 cxw620

刚刚我也使用 Rust tonic 测试了一下, 亦无法复现这问题, 合理怀疑产生这问题的原因:

  • gRPC 库存在特征, 被针对性风控
  • IP 或者 access_key 黑了

所以尝试更换 IP, 最好换运营商, 例如用手机流量, 换 gRPC 库换 access_key 或者不要 access_key 再试试.

测试样例(仅供参考, 从一个大程序摘下来的)
    #[tokio::test]
    async fn test_v2_dynamic() -> Result<(), String> {
        let client_arc = GRPC_HTTPS_CLIENT_TEST.clone();
        let hyper_https_client = client_arc.get("a1playurl").unwrap().clone();
        let user_info = ClientUserInfo {
            uid: ***,
            access_key: "***".to_owned(),
            ..Default::default()
        };
        let client_info = ClientInfo {
            user_info,
            app_info: ClientAppInfo::default(),
            device_info: ClientDeviceInfo::default(),
        };
        let bili_header = BiliGrpcHeaders::new(&client_info);
        let bili_upstream_uri = hyper::Uri::from_static("https://app.bilibili.com");

        let req = bapis::app::dynamic::v2::DynSpaceReq {
            host_uid: 491589125,
            local_time: 8,
            page: 1,
            ..Default::default()
        };

        let resp = moss_handler!(
            bili_header,
            bili_upstream_uri,
            hyper_https_client,
            bapis::app::dynamic::v2::dynamic_client::DynamicClient
        )
        .dyn_space(req)
        .await;
        match resp {
            Ok(resp) => {
                use tokio::fs;
                let resp_str = format!("{:#?}", resp.get_ref());
                fs::write("cn_space.txt", resp_str).await.unwrap();
                // println!("{:?}", resp);
                Ok(())
            }
            Err(e) => {
                println!("{:?}", e);
                let bili_error = BiliError::from(e);
                Err(format!("{}", bili_error))
            }
        }
    }

Reply: image

cxw620 avatar Aug 30 '23 09:08 cxw620

我认为IP不是问题的关键,此问题的广泛出现理论上和IP并不相关,而且其中包括了大量非数据中心的常规民用线路的IP。

参考上述讨论,很有可能是python的grpc库出现问题了,可以以此为方向尝试解决?

Well2333 avatar Aug 30 '23 09:08 Well2333

鉴于 Python 是 gRPC 官方支持的语言之一, 我搜了搜没找到合适的第三方库, 我觉得像前面 My-Responsitories那样简单手搓个 gRPC Client 就好, 需要什么功能再加就是()

cxw620 avatar Aug 30 '23 09:08 cxw620

鉴于 Python 是 gRPC 官方支持的语言之一, 我搜了搜没找到合适的第三方库, 我觉得像前面 My-Responsitories那样简单手搓个 gRPC Client 就好, 需要什么功能再加就是()

要说没有的话,确实也有一些,比如 grpclib 或者 aiogrpc(三年没更新了),不过迁移成本有点高,也不确定是不是 grpcio 造成的问题还是 python 的问题,总之仍需要进一步测试

Well2333 avatar Aug 30 '23 09:08 Well2333

我也同样出现-352问题,这是我的metdata配置:https://github.com/cctyl/BiliRecommBehaviorCorrection/blob/main/src/main/java/io/github/cctyl/interceptor/GrpcInterceptor.java 这是我的请求代码: @Test void test() { DynamicRpcProto.DynDetailReply dynDetailReply = dynamicBlockingStub.dynDetail(DynamicRpcProto.DynDetailReq.newBuilder() .setDynamicId("824093109029699590") .build()); log.info(dynDetailReply.toString()); }

直接出现了UNKNOWN: -352的问题

cctyl avatar Sep 05 '23 08:09 cctyl

直接出现了UNKNOWN: -352的问题

你康康你代码里 build 写 7110300, version 却写了 7.38.0, 还有一次性的 sessionId 给写死了, UA 里面的设备 model 也和你 metadata 里面的对不上, 被风控也不奇怪.

cxw620 avatar Sep 05 '23 08:09 cxw620

直接出现了UNKNOWN: -352的问题

你康康你代码里 build 写 7110300, version 却写了 7.38.0, 还有一次性的 sessionId 给写死了, UA 里面的设备 model 也和你 metadata 里面的对不上, 被风控也不奇怪.

哈哈好的。我改一下,谢谢指点。其实我发现是我的access_key格式错了,改了就正常了。我在想它们的服务器是否真的会校验多个字段的一致性,可能发送多次会导致风控,我这还是首次发起

cctyl avatar Sep 05 '23 08:09 cctyl

直接出现了UNKNOWN: -352的问题

你康康你代码里 build 写 7110300, version 却写了 7.38.0, 还有一次性的 sessionId 给写死了, UA 里面的设备 model 也和你 metadata 里面的对不上, 被风控也不奇怪.

这个sessionId 生成的规则是什么呢?proto的注释是 0-9 a-z,任意混合成32位即可吗

cctyl avatar Sep 05 '23 08:09 cctyl

这个sessionId 生成的规则是什么呢?proto的注释是 0-9 a-z,任意混合成32位即可吗

~~不清楚, 没深究, 应该随机问题不大, 我就那么操作的. 还有更正一下是 8 位, 0-9, a-f.~~

刚刚看了一眼, 确认是随机的 8 位 0-9, a-f 字符

    static {
        byte[] bArr = new byte[4];
        new Random().nextBytes(bArr);
        m = ByteString.of(bArr, 0, 4).hex(); // 即 sessionId
    }

cxw620 avatar Sep 05 '23 09:09 cxw620