diff --git a/evidence/auto_test_cred.json b/evidence/auto_test_cred.json index c6b4fde..803d694 100644 --- a/evidence/auto_test_cred.json +++ b/evidence/auto_test_cred.json @@ -1 +1 @@ -{"p1": "", "cert_hex": "0c20c7ff8cf3674630822e627816a71bc19e97b43de7dae589f13fd1b2327e3be2d52cbaa481870772fe2a4df9a94fdb8a0e41312514470c77cbfa586558f87309be8229a3b618c3d68c33a364eb7810a71b8dfef6c9f5243c7ff2437c1c542ca99db749c296d58c65146f6c1849a1c1b6fbb48d7ff04fc2b18994285a47d348c0c8f8b93714d24fbefbe2f21656a5609169a773e2d8568f1820ff20c6880825b54884bfdeb549b839e7c99a69a274f9b1bcc4cec852c74856f9e8c0520d1d02a309", "cred_hex": "0a20a6e307aa15df2b09999394144eacd1016b73a356e39501dd049dca3130b3fc5fe187ddce5c31ff8f326ff160f593b92ceb09c2745c594edfbfe9d818e96077e5fd580261f243fb95e6bcc1430bc70123ad9b96f76534a22c499dcf8b0a239712f6b3d5bf1461158ca682afb60fa24133"} \ No newline at end of file +{"p1": "", "cert_hex": "0c201a13953ffe33c7229dca02bbe6caec3dd38624f9ab5083301a165843b2372ab92cbaa481870772fe2a4df9a94fdb8a0e41312514470c77cbfa586558f87309bef720eed88e558cfaaff4033070459c4f0437d9af6981f2f0bb088aa9f9e8a15829fa90c7945a5183d07f3f17723b79bff98276257ddea8ed331a796c2b107170a26a6030de1b7096a6d1405a0c91e687a2f1fd4029923eeb10b17a838f276681d4afbf2261860fcbefbeaf2db3c6f16e293807e5ef76563025b5d7cb418a09fd", "cred_hex": "0a70e6d4c19cb5d4984c9e428c1a92c11744a85dd5c8f2f67ef7ef9c75aff6bffdaffcbe7cdacc8c3c7b1d20e1e3c549be02cce015af48ffdae1505d6b98fb86ac40dfebe356b7ef810898aeaaf67fce72792c8aea3629aa6ddf62c34ba92dd5383314697459dca6a1e7179fd1b525a623e7"} \ No newline at end of file diff --git a/evidence/cert_keycap.json b/evidence/cert_keycap.json index 76c859e..0b77031 100644 --- a/evidence/cert_keycap.json +++ b/evidence/cert_keycap.json @@ -1 +1 @@ -[{"type": "armed", "base": "0x77c57c4000"}, {"type": "c1", "key": "345659635064764b4b716a42485a7443", "bt": "0x77c5af4298 libudbauthunify.so!_Z10encode_aesRKNSt6__ndk112basic_stringIcNS_11c"}, {"type": "enc_in", "ra": "0x77c5af42a8 libudbauthunify.so!_Z10encode_aesRKNSt6__ndk112basic_stringIcNS_11char_traits", "x0s": {"t": "s", "len": 72, "v": "57c2c577000000637c777bf26b6fc53001672bfed7ab76ca82c97dfa5947f0add4a2af9ca472c0b7fd9326363ff7cc34a5e5f171d8311504c723c31896059a071280e2eb27b27509"}, "x1s": {"t": "l", "len": 187, "v": "010400353030381400fd951db27f62fad1580eb969f4e921146552ba3e28003032646633393837393734333265616465666363313237363731313961643565383039393933383972000a70a84a169d30cdbb28d8dc3e9321c7c67d36e263acb351b739debaafa8a86b5000a4e35310cd3f58326484ea16c93ffa0203953ffeb94c57baf290a29fc8f55f525562f0625a5baad9f4dc101414072ec55f1b649c84f6c3bdce7b52ab4495c450f5a55d8eb16e4db5d2fdf6f29dd1761e"}, "x2s": {"t": "s", "len": 0, "v": ""}, "x3s": {"t": "s", "len": 24, "v": "39393933383972000a70a84a169d30cdbb28d8dc3e9321c7"}, "x1raw": "c100000000000000bb000000000000000034c2aa770000b4f04b608578000000", "x2raw": "000000000000000000000000000000000000000000000000c100000000000000", "x3raw": "3039393933383972000a70a84a169d30cdbb28d8dc3e9321c7c67d36e263acb3"}, {"type": "_enc", "ra": "0x77c5a14114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "010400353030381400fd951db27f62fa"}, {"type": "_enc", "ra": "0x77c5a14114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "d1580eb969f4e921146552ba3e280030"}, {"type": "_enc", "ra": "0x77c5a14114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "32646633393837393734333265616465"}, {"type": "_enc", "ra": "0x77c5a14114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "66636331323736373131396164356538"}, {"type": "_enc", "ra": "0x77c5a14114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "3039393933383972000a70a84a169d30"}, {"type": "_enc", "ra": "0x77c5a14114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "cdbb28d8dc3e9321c7c67d36e263acb3"}, {"type": "_enc", "ra": "0x77c5a14114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "51b739debaafa8a86b5000a4e35310cd"}, {"type": "_enc", "ra": "0x77c5a14114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "3f58326484ea16c93ffa0203953ffeb9"}, {"type": "_enc", "ra": "0x77c5a14114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "4c57baf290a29fc8f55f525562f0625a"}, {"type": "_enc", "ra": "0x77c5a14114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "5baad9f4dc101414072ec55f1b649c84"}, {"type": "_enc", "ra": "0x77c5a14114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "f6c3bdce7b52ab4495c450f5a55d8eb1"}, {"type": "_enc", "ra": "0x77c5a14114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "6e4db5d2fdf6f29dd1761e0000000000"}, {"type": "enc_out", "ret": {"t": "s", "len": 72, "v": "57c2c577000000637c777bf26b6fc53001672bfed7ab76ca82c97dfa5947f0add4a2af9ca472c0b7fd9326363ff7cc34a5e5f171d8311504c723c31896059a071280e2eb27b27509"}}, {"type": "encode_aes", "ra": "0x77c5af4aa4 libudbauthunify.so!_Z13gen_biz_token8BIZTOKENRKNSt6__ndk112basic_stringIcNS0_", "p0": {"t": "l", "len": 187, "v": "010400353030381400fd951db27f62fad1580eb969f4e921146552ba3e28003032646633393837393734333265616465666363313237363731313961643565383039393933383972000a70a84a169d30cdbb28d8dc3e9321c7c67d36e263acb351b739debaafa8a86b5000a4e35310cd3f58326484ea16c93ffa0203953ffeb94c57baf290a29fc8f55f525562f0625a5baad9f4dc101414072ec55f1b649c84f6c3bdce7b52ab4495c450f5a55d8eb16e4db5d2fdf6f29dd1761e"}, "p1": {"t": "l", "len": 24, "v": "345659635064764b4b716a42485a74436d62726f5258486b"}}, {"type": "qurl_done", "len": 1328, "data": "AAAD5BADLDxCADYAwlYMaHV5YXVkYndlYnVpZgdkZWZhdWx0fQABA7YIAAIGCV93dXBfZGF0YR0AAQOICgoMFgMxLjAmyHsiYXNzb2NpYXRpb25JZCI6MTg0NTQ5MzkyLCJmdW5jTmFtZSI6IiIsImdyb3VwIjowLCJpZCI6MTg0NTQ5MzkyLCJzZXNzaW9uIjozNTM5MTM4LCJzdGVwIjowLCJzdGlsbExvZ2luIjpmYWxzZSwidHJhY2VJZCI6IjBiOGYwOThmZjY0YTViZGMtMzg3MC04ODcwNzAyNzg3NjU2Mjg5MjA1IiwidHlwZSI6MiwidWlkIjowLCJ1c2VyQ29udGV4dCI6IiJ9NgQ1MDA4QANWtFBRd2VtQU45TkhrWktvTXFXVmIwanhJeXBxTVRhUUVPcm1YcjM3eFFWaFFacXJEeGlVS0VRMTE5dkUwMnFhODNrOWpnMTFjOEVLZTJnS1RtTDZSVXEwUXdrNGhGQ0F4SVBGcUdxbkRBUHJBU0lPT1l3eXRJckFYdkQxaW5yZzdlc243aDYyMThHcjB0ellZT3ArTHlEN2VEeGZ0RkJLM08rY3ZsVkpkUVFYWm1SeFU0aTFxWmYAdgCGAJYACxoGIGVkMGRiODMzNGNhZGQyMzZjMDBjYWRmN2UxMWFiNWE1FgcxMy40LjIyJgkxLjAuODAxMzg2AEYJMTI3LjAuMC4xVgZ4aWFvbWlmAAsqAAEWCU0yMTAySjJTQyYoMDJkZjM5ODc5NzQzMmVhZGVmY2MxMjc2NzExOWFkNWU4MDk5OTM4OTYHYW5kcm9pZEYPTTIxMDJKMlNDLDMwLDExZgQxMDgwdgQyMTIwhig3YzUzODdlMDUzOWMwMjNjMzFjNGZmMGU4MDdlNzI1NjExNzM4NWVlCzMAAAEXUbhlb0cAAAEERENBclMyUTlVQ3dmMU9PbXdXQ3VEM3hkc3JKdXFXb0YvQmRKZ0xPMUtHWXBUaXk2cElHSEIzTCtLazM1cVUvYmlnNUJNU1VVUnd4M3kvcFlaVmo0Y3dtK1JlSE5aeHZnaGxHenhPR3V2TG95U2NkNEtZQ2dZVWNxS0o2SXBYdkNEU3V2eE1VSFRoU3VQdUVPMGF2NW5IUy96OTJQK21mZUt4aHpHY0h4WFRDdi92U1FuMkJsdk1qSEhMSUxkb1FvMngvWEdBRGJTYkxjN0ZORVZnQUN0Rm9JaW1zVmN1NlZISXNybmhEeTBDUEphTGEyYW9aOHZ0UU14NDR6Q1lzWVcwMD1WAGYACwYQd3VwdWRicmVxdWVzdF92MB0AAAUCADYAwoyYDKgM"}] \ No newline at end of file +[{"type": "armed", "base": "0x77c2462000"}, {"type": "c1", "key": "345659635064764b4b716a42485a7443", "bt": "0x77c2792298 libudbauthunify.so!_Z10encode_aesRKNSt6__ndk112basic_stringIcNS_11c"}, {"type": "enc_in", "ra": "0x77c27922a8 libudbauthunify.so!_Z10encode_aesRKNSt6__ndk112basic_stringIcNS_11char_traits", "x0s": {"t": "s", "len": 72, "v": "378cc277000000637c777bf26b6fc53001672bfed7ab76ca82c97dfa5947f0add4a2af9ca472c0b7fd9326363ff7cc34a5e5f171d8311504c723c31896059a071280e2eb27b27509"}, "x1s": {"t": "l", "len": 187, "v": "010400353030381400cfa90b4269ad34341fe94be9f50171491baf348d28003032646633393837393734333265616465666363313237363731313961643565383039393933383972000a806846802519b19b12c2b3df1b18116bf352867914cc64fb1fd1d304d89452634b6f3cf602d484b00d8fcc379bdb70785b5b30746c44354acb238b952bd7e2950d5f14508d2380d00879cac07e6a8422ad83864d0769f284befe7cde277a419833146aa1898bebbdca3d930125109858e7"}, "x2s": {"t": "s", "len": 0, "v": ""}, "x3s": {"t": "s", "len": 24, "v": "39393933383972000a806846802519b19b12c2b3df1b1811"}, "x1raw": "c100000000000000bb0000000000000000532243770000b4f04b608578000000", "x2raw": "000000000000000000000000000000000000000000000000c100000000000000", "x3raw": "3039393933383972000a806846802519b19b12c2b3df1b18116bf352867914cc"}, {"type": "_enc", "ra": "0x77c26b2114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "010400353030381400cfa90b4269ad34"}, {"type": "_enc", "ra": "0x77c26b2114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "341fe94be9f50171491baf348d280030"}, {"type": "_enc", "ra": "0x77c26b2114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "32646633393837393734333265616465"}, {"type": "_enc", "ra": "0x77c26b2114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "66636331323736373131396164356538"}, {"type": "_enc", "ra": "0x77c26b2114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "3039393933383972000a806846802519"}, {"type": "_enc", "ra": "0x77c26b2114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "b19b12c2b3df1b18116bf352867914cc"}, {"type": "_enc", "ra": "0x77c26b2114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "64fb1fd1d304d89452634b6f3cf602d4"}, {"type": "_enc", "ra": "0x77c26b2114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "84b00d8fcc379bdb70785b5b30746c44"}, {"type": "_enc", "ra": "0x77c26b2114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "354acb238b952bd7e2950d5f14508d23"}, {"type": "_enc", "ra": "0x77c26b2114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "80d00879cac07e6a8422ad83864d0769"}, {"type": "_enc", "ra": "0x77c26b2114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "f284befe7cde277a419833146aa1898b"}, {"type": "_enc", "ra": "0x77c26b2114 libudbauthunify.so!_ZN10UdbAESUtil7encryptERNSt6__ndk112basic_stringIcNS0_11c", "blk": "ebbdca3d930125109858e70000000000"}, {"type": "enc_out", "ret": {"t": "s", "len": 72, "v": "378cc277000000637c777bf26b6fc53001672bfed7ab76ca82c97dfa5947f0add4a2af9ca472c0b7fd9326363ff7cc34a5e5f171d8311504c723c31896059a071280e2eb27b27509"}}, {"type": "encode_aes", "ra": "0x77c2792aa4 libudbauthunify.so!_Z13gen_biz_token8BIZTOKENRKNSt6__ndk112basic_stringIcNS0_", "p0": {"t": "l", "len": 187, "v": "010400353030381400cfa90b4269ad34341fe94be9f50171491baf348d28003032646633393837393734333265616465666363313237363731313961643565383039393933383972000a806846802519b19b12c2b3df1b18116bf352867914cc64fb1fd1d304d89452634b6f3cf602d484b00d8fcc379bdb70785b5b30746c44354acb238b952bd7e2950d5f14508d2380d00879cac07e6a8422ad83864d0769f284befe7cde277a419833146aa1898bebbdca3d930125109858e7"}, "p1": {"t": "l", "len": 24, "v": "345659635064764b4b716a42485a74436d62726f5258486b"}}, {"type": "qurl_done", "len": 1332, "data": "AAAD5hADLDxCAFpBBVYMaHV5YXVkYndlYnVpZgdkZWZhdWx0fQABA7gIAAIGCV93dXBfZGF0YR0AAQOKCgoMFgMxLjAmynsiYXNzb2NpYXRpb25JZCI6MTg0NTQ5MzkyLCJmdW5jTmFtZSI6IiIsImdyb3VwIjowLCJpZCI6MTg0NTQ5MzkyLCJzZXNzaW9uIjo1OTE0ODg1LCJzdGVwIjowLCJzdGlsbExvZ2luIjpmYWxzZSwidHJhY2VJZCI6IjBiOGYwOThmZjY0YTViZGMtMjY2OTItODI2MzkzOTQ3ODc2NjM1NTEzNjUiLCJ0eXBlIjoyLCJ1aWQiOjAsInVzZXJDb250ZXh0IjoiIn02BDUwMDhAA1a0UFF3ZW1BTjlOSGtaS29NcVdNUW5WUkl5cHFNVGFRRU9ybVhyMzd4UVZoUVpxclA1aVVLRVExMXh2RTB2cE1na2xlajIzbEpGYmFHVW5LUEFhYnhWaUF0TnZyTkxBbXhBQzJyRGp6Qy9JSU0vSWFQeTdTcytvQXZqSmltR2pBS2RnVmprbUhWV2Q2bEw4cUZScU4zWkJMamt4c2xUQjczaXNvallWcjlrSFhWdUh3SkxoM3YzZgB2AIYAlgALGgYgZWQwZGI4MzM0Y2FkZDIzNmMwMGNhZGY3ZTExYWI1YTUWBzEzLjQuMjImCTEuMC44MDEzODYARgkxMjcuMC4wLjFWBnhpYW9taWYACyoAARYJTTIxMDJKMlNDJigwMmRmMzk4Nzk3NDMyZWFkZWZjYzEyNzY3MTE5YWQ1ZTgwOTk5Mzg5NgdhbmRyb2lkRg9NMjEwMkoyU0MsMzAsMTFmBDEwODB2BDIxMjCGKDdjNTM4N2UwNTM5YzAyM2MzMWM0ZmYwZTgwN2U3MjU2MTE3Mzg1ZWULMwAAARdRuGVvRwAAAQREQ0JHY0QyN0liSWEvZnZ0UHhNT2xZdGJUY0M3bWRaUlJ3YzIyc2NnQkFyRTJ5eTZwSUdIQjNMK0trMzVxVS9iaWc1Qk1TVVVSd3gzeS9wWVpWajRjd20rWEg1dnNrakR2SzNhUlhudEJGcURDQUUvaUVIbGs4ZXJ3VUJZdmJMaXlIb0YrSytQam5GTGJRMmlzSHVhcUtqTHAvWmRETDlxSit3VEVSb3h0ZjFuUzlTZ0l2N3lCaVIyMjd4N3F3RjllUTVuckNaRitnRnczelVZb2N6Uk9jbHE1aXZDclhRZTVSZ3hOYkp5aWQ3ZkZqTVhYYlNQdHBIZ2p0TVJtdWp6RVRvPVYAZgALBhB3dXB1ZGJyZXF1ZXN0X3YwHQAABQIAWkEFjJgMqAw="}] \ No newline at end of file diff --git a/evidence/forge_bind_result.json b/evidence/forge_bind_result.json index d72d552..62e5db8 100644 --- a/evidence/forge_bind_result.json +++ b/evidence/forge_bind_result.json @@ -4,9 +4,9 @@ "ok": true, "uid": 1199666914671, "biztoken_len": 344, - "biztoken": "AQA4y9MKq4Qmt6_gGeXThZEnv3QkPIbY_YEbvuIJFuRdzSJVE_1gPSvor9FLSVh9ASmgtE5FhBI7NIwygC3W0rpO3AOjkh2W5NzxYJFLp4P8XER2kF7SXCKZQFUguSkkIrlZFQy6Fg4jsiafM6bWPyRfzOn59e98B-wTS-hxxKT4rzfNCfjkdkVTNfToF8RJy2Gu78hJTLjWfzbkTI2phwW50bpXf55OVXZr-GdXvwHLcrEF1IKpPaxpLWX5ulfk-vR_HlPlhREPQS5WvqS7QJ3EB2MJFeYCRJdvDx3TC_M3Nw5OA3Bqrft8K-n_cjC11Mi86Zi462gK6g_EAGAFP0-v" + "biztoken": "AQCMqxiPqae2tZebsGe0fV3QrGKT0e7nARXgDSHt5_JKINIhDchOyo02v311rg1ApT89J_GLyEf8Mxm5ZbtiZ48nBnCDaGRjHsMda9NNaWV1Dd3lLbqfp4pmkW6dX5Q5A5CP8XF9lf5hUvqv-X5VuB0Via0f8FyVP6EcOn_h4fYSDV-qV9GMm9QJIfWtFNWjJnNeyv33Vk0hocJ7U8zP20WbHha_kH3HIQqrKLJVnGSAmhIPnyZVkh3yar6a5YcpFrnD6_7OAara-jz11jRHv6XTMM9YDOr-66T8NZYv4aklYgAQ3O-dg3Cu2fRrtLdGrxhV6x1N10MBzhQ3zNAGc2K9" } }, - "p1_new_hex": "010400353030381400fd951db27f62fad1580eb969f4e921146552ba3e28003032646633393837393734333265616465666363313237363731313961643565383039393933383972000a20a6e307aa15df2b09999394144eacd1016b73a356e39501dd049dca3130b3fc5fe187ddce5c31ff8f326ff160f593b92ceb09c2745c594edfbfe9d818e96077e5fd580261f243fb95e6bcc1430bc70123ad9b96f76534a22c499dcf8b0a239712f6b3d5bf1461158ca682afb60fa24133", - "ts": 1787656476.736854 + "p1_new_hex": "0104003530303814008db679c3751852831bb96c87814eb02130c6261f28003032646633393837393734333265616465666363313237363731313961643565383039393933383972000a70e6d4c19cb5d4984c9e428c1a92c11744a85dd5c8f2f67ef7ef9c75aff6bffdaffcbe7cdacc8c3c7b1d20e1e3c549be02cce015af48ffdae1505d6b98fb86ac40dfebe356b7ef810898aeaaf67fce72792c8aea3629aa6ddf62c34ba92dd5383314697459dca6a1e7179fd1b525a623e7", + "ts": 1787663026.148772 } \ No newline at end of file diff --git a/tools/app_login_flow.py b/tools/app_login_flow.py new file mode 100644 index 0000000..e8d4fc6 --- /dev/null +++ b/tools/app_login_flow.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python3 +"""虎牙App渠道纯Python登录流 —— 含新账号 safe_auth 滑块自动过验。 + +链路(全部实测/逆向自 pt_auth 页JS, 见 work/safe_auth/pt_auth.pretty.js): + 账号密码 -> WUP hypasswordLogin (POST wup.huya.com) + |- 正常: 响应bean直出新鲜cred(114B) + '- 风控(safe_auth): 响应内嵌 https://aq.huya.com/p/safe_auth/pt_auth.html?param=... + -> udbrtt config3/gurl/vurl 滑块闭环(与 web 渠道 solver 同源) + -> 重发 WUP 登录 -> cred 直出 + +pt_auth 页 JS 逆向结论(2026-08-25): + 1. oUrl = parseQueryString(location.search) 即整个query串 => + config3/gurl/vurl 的 data.urlParamMap = {param, sceneType, appId, mobile, + allowRefresh, hideShareButton, ...} 原样回传。 + 2. wupData: App WebView 里来自原生桥(HYUDBMSDKQUrlCommon/HYUDBMSDKCommon), + 纯web上下文为 "" 也能走通(config3接受空)。 + 3. POST https://udbrtt.huya.com/auth/client/config3 + {appId, data:{urlParamMap, wupData:"", page:, + behavior:, info:"csid_"+32位hex, cTuIndex:0}} + => data{gurl, vurl, js(UdbCipher), key} + 4. gurl取图 {.., ver:5} => {code, dtuStr, puzzleStr, segment, location_y}; + segment = UdbCipher.decrypt(segment, key) 的 b64 JSON .ord 为乱序还原表。 + 5. vurl提交 act=UdbCipher.encrypt(btoa(encodeURI(JSON{ + point:(slideBlockLeft/imgareaWidth)*390, travel:"x,y,t;...", + code, sceneType, endTime})), key), ver:5 => data.authId。 + 6. 成功后页面把 authId 回传给端上(JSBridge quit / HYUDBVerifySDKCallback); + 纯Python侧直接重发同参WUP登录即可放行(param风控会话已被服务端标记通过)。 + +对外入口: + login_cred(account, password) 账号密码 -> 新鲜cred(自动过safe_auth滑块) + solve_safe_auth(risk_url) 单独解一个 pt_auth/qr_auth 链接 +""" +from __future__ import annotations + +import hashlib +import json +import re +import sys +import time +from pathlib import Path +from urllib.parse import parse_qs, urlparse + +ROOT = Path(__file__).resolve().parent.parent +if str(ROOT) not in sys.path: + sys.path.insert(0, str(ROOT)) +sys.path.insert(0, str(ROOT / "tools")) + +import requests + +from huya_wup_encoder import build_password_login_wup + +# 与 core/huya/login.py 的 APP_UA_MOBILE 一致(App WebView UA, 设备画像自洽) +APP_UA_MOBILE = ( + "Mozilla/5.0 (Linux; Android 11; M2102J2SC Build/RKQ1.200826.002; wv) " + "AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 " + "Chrome/149.0.7827.159 Mobile Safari/537.36 huya adr/13.4.22/xiaomi/30" +) + +WUP_URL = "https://wup.huya.com" + +# TAF响应里内嵌的风控页URL(pt_auth=滑块可自动化; qr_auth=扫码需人工一次) +RISK_URL_RE = re.compile(rb"https://aq\.huya\.com/p/safe_auth/[^\x00-\x20\"'\\<>]+") +_URL_TAIL_KEEP = set( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=%" +) + + +class AppLoginError(RuntimeError): + """App渠道纯Python登录失败。""" + + +class QrAuthRequiredError(AppLoginError): + """风控要求扫码(qr_auth), 需该账号在真机App正常登录一次建立信任。""" + + +def _golden_assets() -> tuple[dict, str, str]: + """从金样本提取 session/traceId、ua JSON 与 sd。""" + raw = (ROOT / "evidence/wup_passwordlogin_taf.bin").read_bytes() + mj = json.loads(re.search(rb"(\{\"associationId.*?\})", raw).group(1)) + ua = re.search(rb"(\{\"curl\".*?\"user_action\":\[.*?\]\})", raw).group().decode() + sd = re.search(rb"PQwemAN9[A-Za-z0-9+/=]+", raw).group().decode() + return mj, ua, sd + + +_DEV = { + "app_version": "13.4.22", "sdk_version": "1.0.80138", + "vendor": "xiaomi", "model": "M2102J2SC", "os": "android", + "ip": "127.0.0.1", + "fingerprint": "02df398797432eadefcc12767119ad5e80999389", + "screen": "M2102J2SC,30,11", "width": "1080", "height": "2120", + "device_id": "7c5387e0539c023c31c4ff0e807e7256117385ee", +} + + +def wup_password_login_raw(account: str, password: str, timeout: int = 15) -> bytes: + """发送WUP密码登录, 返回原始响应字节(不解析)。""" + uid = account[3:] if account.startswith("hy_") else account + mj, ua, sd = _golden_assets() + pkt = build_password_login_wup( + uid, hashlib.sha1(password.encode()).hexdigest(), sd, + "ed0db8334cadd236c00cadf7e11ab5a5", + mj["session"], mj["traceId"], ua, _DEV) + r = requests.post( + WUP_URL, data=pkt, + headers={"Content-Type": "application/multipart-formdata; charset=UTF-8", + "User-Agent": "Dalvik/2.1.0 (Linux; U; Android 11)", + "Accept-Encoding": "gzip"}, timeout=timeout) + if r.status_code != 200: + raise AppLoginError(f"登录HTTP {r.status_code}") + return r.content + + +def parse_cred(resp: bytes) -> bytes | None: + """从WUP登录响应切片cred(tag3 SIMPLE_LIST); 无则None。""" + s = resp.find(b"\x0a\x0a", 0x40) + e = resp.find(b"_wup_header") + if s < 0 or e < 0: + return None + d = resp[s:e - 6] + m = re.search(rb"\x3d\x00([\x00-\x03])(.)", d) # tag3, INT8 len + if not m: + return None + ln = m.group(2)[0] + st = m.start() + 4 + cred = d[st:st + ln] + if len(cred) == 114 and cred[:1] == b"\x0a": + return cred + return None + + +def parse_risk_url(resp: bytes) -> str | None: + """提取safe_auth风控页URL; 优先pt_auth(滑块)。返回None表示无风控。""" + urls = [] + for m in RISK_URL_RE.finditer(resp): + u = m.group().decode("utf-8", "ignore") + while u and u[-1] not in _URL_TAIL_KEEP: + u = u[:-1] + urls.append(u) + if not urls: + return None + pt = [u for u in urls if "pt_auth" in u] + return (pt or urls)[0] + + +def solve_safe_auth(risk_url: str, proxies=None, max_retry: int = 2) -> dict: + """解一个safe_auth风控链接(pt_auth滑块), 返回vurl响应data(含authId)。 + + 复现 pt_auth 页行为: + - urlParamMap = 整个query串原样回传 + - page = pt_auth 完整URL(location.href) + - appId 取自query; info 用 csid_ 前缀会话id(getSdid格式) + - App WebView 场景用 touch 事件画像(use_touch_events=True) + """ + from core.huya.verification.solver import ( + HuyaQrAuthRequiredError, + HuyaVerificationSolver, + ) + + q = {k: v[0] for k, v in + parse_qs(urlparse(risk_url).query, keep_blank_values=True).items()} + app_id = str(q.get("appId") or "5002") + last_err: Exception | None = None + for attempt in range(max_retry): + solver = HuyaVerificationSolver( + ua=APP_UA_MOBILE, + proxies=proxies, + app_id=app_id, + page_url=risk_url, + use_touch_events=True, + ) + try: + result = solver.solve(risk_url) + except HuyaQrAuthRequiredError: + raise # qr_auth 明确不可自动化, 直接上抛 + except Exception as exc: # noqa: BLE001 + last_err = exc + print(f" [safe_auth] 第{attempt + 1}次过验异常: {exc}") + time.sleep(1.0) + continue + auth_id = str((result or {}).get("authId") or "") + if auth_id: + print(f" [safe_auth] 滑块通过 authId={auth_id[:24]}...") + return result + print(f" [safe_auth] 未返回authId: {str(result)[:160]}") + raise AppLoginError(f"safe_auth 过验失败: {last_err or '无authId'}") + + +def login_cred(account: str, password: str, max_rounds: int = 3) -> bytes: + """账号密码 -> 新鲜cred。触发safe_auth时自动解滑块并重发登录。""" + for rnd in range(max_rounds): + resp = wup_password_login_raw(account, password) + cred = parse_cred(resp) + if cred: + return cred + risk_url = parse_risk_url(resp) + if risk_url: + kind = "pt_auth(滑块)" if "pt_auth" in risk_url else \ + ("qr_auth(扫码)" if "qr_auth" in risk_url else "未知") + print(f"[risk] 第{rnd + 1}轮触发安全验证: {kind}") + if "qr_auth" in risk_url: + raise QrAuthRequiredError( + "该账号App渠道要求扫码验证(qr_auth), 请先在手机虎牙App上" + f"正常登录一次建立设备信任。\n 风控页: {risk_url[:120]}") + solve_safe_auth(risk_url) + print("[risk] 过验完成, 重发WUP登录 ...") + continue + raise AppLoginError( + f"登录未返回cred也无风控URL(密码错误或账号异常), 响应{len(resp)}B") + raise AppLoginError(f"{max_rounds}轮内未取得cred") + + +if __name__ == "__main__": + acct, pwd = sys.argv[1], sys.argv[2] + cred = login_cred(acct, pwd) + print(f"cred OK: {len(cred)}B {cred[:8].hex()}") diff --git a/tools/full_auto_safe.py b/tools/full_auto_safe.py new file mode 100644 index 0000000..2fd8bcd --- /dev/null +++ b/tools/full_auto_safe.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +"""一键全自动(含新账号safe_auth滑块): 账号密码 -> 页面cookie(biztoken)。 + +与 full_auto_test.py 的区别: 可信账号直出cred之外, +新/异地账号触发 safe_auth 时自动解 udbrtt 滑块并重发登录, 全程零人工。 + +用法: + .venv/bin/python tools/full_auto_safe.py <账号如hy_xxx或300023887> <密码> + +前提: evidence/cert_keycap.json 内有较新鲜的 wupData 信封 + (过期则先在设备上跑 scripts/hook_cert_keycap.py 刷新)。 +""" +import base64 +import json +import re +import subprocess +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT)) +sys.path.insert(0, str(ROOT / "tools")) + +from app_login_flow import QrAuthRequiredError, login_cred # noqa: E402 +from cert_forge import build_p1, decrypt_cert, forge_cert, parse_p1 # noqa: E402 +from envelope_forge import Envelope # noqa: E402 + + +def forge(cred: bytes) -> str: + """复用信封原证书的nonce/指纹/key_idx仅换cred(纯随机nonce必40020).""" + env = Envelope.load() + orig = base64.b64decode(env.cert_b64) + f = parse_p1(decrypt_cert(orig)) + P1 = build_p1(f["app_id"], f["fingerprint"], cred, rnd=f["rnd"]) + return forge_cert(P1, key_idx=orig[1]).hex() + + +def bind_for_cookie(cred_json_path: Path) -> bool: + """复用扫码绑定四步流(QrRole), 返回是否拿到biztoken。""" + probe = ROOT / "scripts/probe_forge_cert_bind.py" + src = probe.read_text() + src = re.sub(r'open\("evidence/hycred_[a-z_]+\.json"\)', + f'open("{cred_json_path.as_posix()}")', src) + tmp = ROOT / "scripts/_probe_auto.py" + tmp.write_text(src) + r = subprocess.run([sys.executable, str(tmp)], capture_output=True, text=True) + print(r.stdout[-800:]) + return '"ok": true' in r.stdout or "'ok': True" in r.stdout + + +def main() -> int: + acct, pwd = sys.argv[1], sys.argv[2] + print("[1/3] 纯Python密码登录 (遇safe_auth自动过滑块) ...") + try: + cred = login_cred(acct, pwd) + except QrAuthRequiredError as exc: + print(f"❌ {exc}") + return 2 + print(f" cred OK: {len(cred)}B {cred[:8].hex()}") + + print("[2/3] 铸造证书 ...") + cert_hex = forge(cred) + out = ROOT / "evidence/auto_test_cred.json" + out.write_text(json.dumps({"p1": "", "cert_hex": cert_hex, + "cred_hex": cred.hex()})) + print(f" 证书 OK: {len(cert_hex)//2}B") + + print("[3/3] 扫码绑定取cookie ...") + if bind_for_cookie(out): + print("\n🎉 全链路成功: 密码已换出页面cookie(biztoken)!") + return 0 + print("\n❌ 绑定失败——若40020多为信封过期, 请刷新wupData后重试") + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/full_web_cookie.py b/tools/full_web_cookie.py index ece8c04..11b241e 100644 --- a/tools/full_web_cookie.py +++ b/tools/full_web_cookie.py @@ -14,28 +14,18 @@ from probe_huya_qr_bind import QrRole, web_behavior from core.huya.device_fingerprint import get_huya_sdid qurl,P1_old=load_capture() fields=parse_p1(P1_old) -import hashlib,requests as _rq -from huya_wup_encoder import build_password_login_wup -_raw=(__import__('pathlib').Path(__file__).resolve().parent.parent/'evidence/wup_passwordlogin_taf.bin').read_bytes() -_mj=json.loads(re.search(rb'(\{"associationId.*?\})',_raw).group(1)) -_ua=re.search(rb'(\{"curl".*?"user_action":\[.*?\]\})',_raw).group().decode() -_sd=re.search(rb'PQwemAN9[A-Za-z0-9+/=]+',_raw).group().decode() -_dev={"app_version":"13.4.22","sdk_version":"1.0.80138","vendor":"xiaomi","model":"M2102J2SC","os":"android","ip":"127.0.0.1","fingerprint":"02df398797432eadefcc12767119ad5e80999389","screen":"M2102J2SC,30,11","width":"1080","height":"2120","device_id":"7c5387e0539c023c31c4ff0e807e7256117385ee"} -_pkt=build_password_login_wup(ACCT[3:] if ACCT.startswith("hy_") else ACCT, hashlib.sha1(PWD.encode()).hexdigest(),_sd,"ed0db8334cadd236c00cadf7e11ab5a5",_mj["session"],_mj["traceId"],_ua,_dev) -_r=_rq.post("https://wup.huya.com",data=_pkt,headers={"Content-Type":"application/multipart-formdata; charset=UTF-8","User-Agent":"Dalvik/2.1.0"},timeout=15) -_b=_r.content; _s=_b.find(b'\x0a\x0a',0x40); _e=_b.find(b'_wup_header'); _d=_b[_s:_e-6] -_m=re.search(rb'\x3d\x00\x00.',_d) -if not _m: - _u=re.search(rb'https://aq\.huya\.com[^\x00\x20]{20,}',_d) - if _u: - print('❌ 该账号触发了安全验证(safe_auth), 无法直接取cred') - print(' 解决: 先用该账号在手机虎牙App上正常登录一次(建立设备信任),') - print(' 之后本脚本即可全自动。验证页:', _u.group()[:100].decode()) - else: - print('❌ 登录未返回cred(密码错误或账号异常), 响应',len(_b),'B') +import time,base64 +from urllib.parse import quote +# 登录取cred: 触发safe_auth时自动解udbrtt滑块并重发(app_login_flow闭环) +import app_login_flow as _flow +try: + cred = _flow.login_cred(ACCT, PWD) +except _flow.QrAuthRequiredError as e: + print('❌', e) + sys.exit(2) +except Exception as e: + print(f'❌ 登录未返回cred(密码错误/账号异常/风控过验失败): {e}') sys.exit(1) -_l=_m.group()[3] -cred=_d[_m.start()+4:_m.start()+4+_ln] print('新鲜cred:',len(cred),cred[:8].hex()) P1n=build_p1(b"5008",fields["fingerprint"],cred,rnd=fields["rnd"]) cert=base64.b64encode(forge_cert(P1n,key_idx=fields and 0x20)).decode() @@ -74,4 +64,10 @@ print('verify HTTP',r.status_code) for h in r.headers.get('Set-Cookie','').split('\n') or [r.headers.get('Set-Cookie','')]: if h: print('SET:',h[:110]) print('cookie jar:') -for c in sess.cookies: print(' ',c.name,'=',c.value[:50]) +jar=[] +for c in sess.cookies: + print(' ',c.name,'=',c.value[:50]) + jar.append(f'{c.name}={c.value}') +out=__import__('pathlib').Path(__file__).resolve().parent.parent/'evidence/web_cookies_full.txt' +out.write_text(f'# account: {ACCT}\n' + '\n'.join(jar) + '\n') +print('已写', out)