#序言 最近无聊,看到steam,登录更新了,所以手痒试试,正好学习,go语言和protobuf 开工, #抓包 逆向第一步,必然抓包看看啦,抓的这是啥,登录只有两个包, 看看第一个包 url61bK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Q4x3V1q4Q4x3V1q4Q4x3V1q4Q4x3V1q4Q4x3V1q4Q4x3V1k6u0b7i4g2@1K9r3g2F1N6r3W2U0j5i4c8A6L8$3&6e0k6i4u0$3K9h3y4W2i4K6u0r3c8$3g2@1f1r3q4K6M7%4N6G2M7X3c8d9f1@1q4b7N6h3u0D9K9h3y4w2k6i4W2Q4x3V1k6$3x3g2)9K6c8X3!0J5K9h3N6A6L8W2)9K6c8r3S2@1N6s2m8K6i4K6y4m8i4K6t1#2x3V1k6Q4x3U0f1J5c8Y4y4@1L8%4u0W2i4K6u0W2M7%4c8W2j5h3#2H3L8%4N6W2M7X3g2V1i4K6u0W2j5$3!0E0i4K6t1$3j5h3#2H3i4K6y4n7K9h3&6H3N6i4c8Q4y4h3k6H3M7X3!0@1L8$3u0#2k6W2)9#2k6X3g2F1j5$3!0V1k6h3c8Q4x3@1c8o6k6$3!0^5e0g2c8q4P5p5#2f1c8i4S2y4g2p5g2^5 掐指一算,~~嗯嗯是一串有乱码的字符串,偷瞄一眼发现rsa密钥相关标志010001 再看url=GetPasswordRSAPublicKey 百度翻译=获取rsa公钥, 知道了这个包是获取RSA公钥,那么问题来了,为什么会中间有乱码呢? #protobuf数据格式 引入正题,protobuf是什么,具体百度一下你就知道,简单来说protobuf是一种Google提供的高效的协议数据交换格式,就像JSON一样,有固定的格式,正向开发中需要编写.proto 文件来秒数这个数据具体都有什么信息,逆向没有这个文件,就只能根据值盲猜了,我为什么知道这个是protobuf数据格式,没人天生知道,都是踩了很多坑,查了很多资料得出的经验,所以一下能判断大概是什么数据e65K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4j5$3S2I4i4K6u0W2k6$3W2@1K9s2g2T1i4K6u0W2K9h3!0Q4x3V1k6o6P5h3u0W2M7V1y4Z5k6h3k6Q4x3V1j5`. 把响应数据HEx拿到这个网站解析 成功获取解析出数据,左边可以边写.proto边查看右边解析结果, ok第一个包完成文件编写
运行protoc --go_out=./__/ *.proto 生成go代码 #第一个包取RSA公钥 顺便看一下url内的两个参数,第一个固定,第二个是base64编码后的用户名,就不细说了 然后编写调用测试一下
测试ok 没问题正常获取到Rsa公钥参数 #第二个包登录包 看看这个包的提交信息,好家伙这么长,经验判断应该是base64,解码失败看都有什么信息 掐指一算,嗯嗯,又是明文+乱码,protobuf没跑了,转换成hex继续盲反序列化 大概看看 "2"应该是账号 "3"这么长,联想到刚才获取rsa,估计就是加密后的密码了 "4"和rsa包一起返回的随机数 "9"可能是浏览器信息, 其他的就看不出来了, 这就是probuf逆向的麻烦事,没有proto文件不知道值什么信息,当然要求不高直接用也可以,反正也不用改变不知道的数据,咱们只需要修改"2","3","4"这几个值就行了,但是!!!经过我一顿断点操作,还真让我翻出了每个参数的名字, 在看看返回包,登录失败,返回信息只有两个字节??? 这是什么鬼,盲反序列化也没数据, 那前端是怎么判断登录结果的呢?,此事必有蹊跷. -_/ 经过反复对比疑点在这里,响应协议头内x-eresult参数代表操作是否成功,1=成功,5=帐密错误,等等 再看看成功都返回的数据 还是一样,盲反序列化一条龙,再根据js判断键名 嘿嘿以为登录成功了,没想到吧没还需要验证码 不过登录包的请求和响应protoc,文编编写完成了
密码是标准rsa加密,随便找个RSA的js加密填写上参数就可以用了. 生成go代码,编写测试
#提交邮件验证码包 既然要验证码,那也没办法继续吧,本以为可以收工了(真的想多了) 这个包就很简单了,需要的信息上边包都返回了,至直接使用提交即可 x-eresult: 65=代码错误 1=成功 proto文件
#刷新状态包 但是有个问题啊,为什么提交完验证码没返回登录凭据呢acctoken 原来还有一个包,在这了获取,因为页面跳转浏览器不会缓存这个返回数据,我用花瓶查看到的, qing 返回信息就在这里 proto文件编写
写代码测试
测试ok成功获取到acctoken #结语 这个网站其实没什么加密,只是使用了protobuf格式的数据,所以导致读取比较麻烦,尤其是某些语言(没错说的就是你,易语言,),真的很不方便, 我也是没办法才改使用go来操作,还别说,go语言还是挺方便的. 撒花完结,还差十几雪币,我就要成为正式会员了,嘿嘿嘿,不枉我每天签到,开心.
syntax = "proto3";
package Protoc;
option go_package = ".";
//响应
message Rsa_Response{
string PublicKey= 1; //公钥
string MoShu= 2; //模数
int64 SuiJiShu= 3; //随机数
}
syntax = "proto3";
package Protoc;
option go_package = ".";
//响应
message Rsa_Response{
string PublicKey= 1; //公钥
string MoShu= 2; //模数
int64 SuiJiShu= 3; //随机数
}
func (集 *Api) D登录_取Rsa公钥() bool {
局_网址 := "c94K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6S2M7r3W2Q4x3X3g2K6N6r3g2S2L8i4m8G2N6$3g2J5k6h3c8Q4x3X3g2U0L8$3#2Q4x3V1k6u0b7i4g2@1K9r3g2F1N6r3W2U0j5i4c8A6L8$3&6e0k6i4u0$3K9h3y4W2i4K6u0r3c8$3g2@1f1r3q4K6M7%4N6G2M7X3c8d9f1@1q4b7N6h3u0D9K9h3y4w2k6i4W2Q4x3V1k6$3x3b7`.`. "
Http请求 := 集.Http客户端.DevMode().R()
局_网址 += `?origin=https:%2F%2Fstore.steampowered.com&input_protobuf_encoded={input_protobuf_encoded}`
//SetPathParam 会自动url编码, 无需再编码
Http请求.SetPathParam(`input_protobuf_encoded`, B编码_BASE64编码([]byte(string([]byte{10, 11})+集.账号)))
局_post := map[string]string{}
Http请求.EnableForceMultipart().SetFormData(局_post)
Http请求.SetHeader("accept", " application/json, text/plain, */*")
Http请求.SetHeader("accept-language", " zh-CN,zh;q=0.9")
Http请求.SetHeader("cache-control", " no-cache")
Http请求.SetHeader("dnt", " 1")
Http请求.SetHeader("origin", " 677K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6K6N6r3!0J5k6g2)9J5k6i4y4@1k6h3q4E0M7r3!0%4k6i4u0W2k6q4)9J5k6h3y4G2L8b7`.`. ")
Http请求.SetHeader("pragma", " no-cache")
Http请求.SetHeader("referer", " 803K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6K6N6r3!0J5k6g2)9J5k6i4y4@1k6h3q4E0M7r3!0%4k6i4u0W2k6q4)9J5k6h3y4G2L8g2)9J5c8X3I4G2k6$3W2F1i4K6u0r3i4K6y4r3M7$3&6J5i4K6y4p5x3g2)9#2k6U0c8Q4y4h3j5@1i4K6g2X3i4K6g2X3L8h3!0J5k6g2)9J5k6r3y4G2L8Y4c8W2L8Y4c8Q4x3X3c8D9L8$3N6A6L8R3`.`. ")
Http请求.SetHeader("user-agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1")
Http请求.SetHeader("Host", "api.steampowered.com")
Http请求.SetHeader("Connection", "keep-alive")
var 局_请求结果 *req.Response
var err error
for i := 0; i < 3; i++ {
局_请求结果, err = Http请求.Get(局_网址)
if len(局_请求结果.Bytes()) > 0 || err != nil {
break
}
}
局_pb := &集.Rsa
err = proto.Unmarshal(局_请求结果.Bytes(), 局_pb)
if err != nil {
fmt.Println("取rsa公钥反序列化失败 ", err)
}
if 局_pb.PublicKey != "" {
return true
}
集.错误信息 = "RSA密钥获取失败"
fmt.Printf(局_请求结果.Dump())
return false
}
func (集 *Api) D登录_取Rsa公钥() bool {
局_网址 := "b2fK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6S2M7r3W2Q4x3X3g2K6N6r3g2S2L8i4m8G2N6$3g2J5k6h3c8Q4x3X3g2U0L8$3#2Q4x3V1k6u0b7i4g2@1K9r3g2F1N6r3W2U0j5i4c8A6L8$3&6e0k6i4u0$3K9h3y4W2i4K6u0r3c8$3g2@1f1r3q4K6M7%4N6G2M7X3c8d9f1@1q4b7N6h3u0D9K9h3y4w2k6i4W2Q4x3V1k6$3x3b7`.`. "
Http请求 := 集.Http客户端.DevMode().R()
局_网址 += `?origin=https:%2F%2Fstore.steampowered.com&input_protobuf_encoded={input_protobuf_encoded}`
//SetPathParam 会自动url编码, 无需再编码
Http请求.SetPathParam(`input_protobuf_encoded`, B编码_BASE64编码([]byte(string([]byte{10, 11})+集.账号)))
局_post := map[string]string{}
Http请求.EnableForceMultipart().SetFormData(局_post)
Http请求.SetHeader("accept", " application/json, text/plain, */*")
Http请求.SetHeader("accept-language", " zh-CN,zh;q=0.9")
Http请求.SetHeader("cache-control", " no-cache")
Http请求.SetHeader("dnt", " 1")
Http请求.SetHeader("origin", " 8d7K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6K6N6r3!0J5k6g2)9J5k6i4y4@1k6h3q4E0M7r3!0%4k6i4u0W2k6q4)9J5k6h3y4G2L8b7`.`. ")
Http请求.SetHeader("pragma", " no-cache")
Http请求.SetHeader("referer", " 7b4K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6K6N6r3!0J5k6g2)9J5k6i4y4@1k6h3q4E0M7r3!0%4k6i4u0W2k6q4)9J5k6h3y4G2L8g2)9J5c8X3I4G2k6$3W2F1i4K6u0r3i4K6y4r3M7$3&6J5i4K6y4p5x3g2)9#2k6U0c8Q4y4h3j5@1i4K6g2X3i4K6g2X3L8h3!0J5k6g2)9J5k6r3y4G2L8Y4c8W2L8Y4c8Q4x3X3c8D9L8$3N6A6L8R3`.`. ")
Http请求.SetHeader("user-agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1")
Http请求.SetHeader("Host", "api.steampowered.com")
Http请求.SetHeader("Connection", "keep-alive")
var 局_请求结果 *req.Response
var err error
for i := 0; i < 3; i++ {
局_请求结果, err = Http请求.Get(局_网址)
if len(局_请求结果.Bytes()) > 0 || err != nil {
break
}
}
局_pb := &集.Rsa
err = proto.Unmarshal(局_请求结果.Bytes(), 局_pb)
if err != nil {
fmt.Println("取rsa公钥反序列化失败 ", err)
}
if 局_pb.PublicKey != "" {
return true
}
集.错误信息 = "RSA密钥获取失败"
fmt.Printf(局_请求结果.Dump())
return false
}
syntax = "proto3";
package Protoc;
option go_package = ".";
// 请求
message eMsg9804 {
string account_name = 2; // 1111111111
string encrypted_password = 3; // rsa加密密码
int64 encryption_timestamp = 4; // 570240150000
int32 set_remember_login = 5; // 1 是否记住登录
bool set_persistence = 7; // 1 坚持不懈?
string website_id = 8; // Mobile app内是 Mobile pc是 Store
repeated device_details device_details = 9; // 设备信息 手机和电脑不一样
int32 language = 11; // 6 语言,直接设置6就可以,中国人 app没有这个值 pc=6
}
message device_details {//电脑网页设备信息
string device_friendly_name = 1; //app=Pixel, pc= Dalvik/2.1.0 (Linux; U; Android 7.1.2; Pixel Build/NJH47F; Valve Steam App Version/3)
int32 platform_type = 2; // 2 平台类型 pc=2 app=3
int32 os_type = 3; //
uint32 gaming_device_type = 4; //
uint32 client_count = 5; //
bytes machine_id = 6; //
}
//响应============================================================
//CAuthentication_BeginAuthSessionViaCredentials_Response
//const {client_id: i, request_id: n, interval: a, allowed_confirmations: s, steamid: u, weak_token: d} = h.Body().toObject();
message Response_EMsg147 {
uint64 client_id = 1; //
bytes request_id= 2; // í'*c¡²m*¨cÞ
fixed32 interval= 3; // 1084227584
repeated allowed_confirmations allowed_confirmations= 4;
uint64 steamid= 5; /
string weak_token= 6; //
string agreement_session_url = 7; //协议会话url
string extended_error_message = 8; // {}
}
message allowed_confirmations{
int32 confirmation_type= 1;
string associated_message= 2;
}
//仅供参考
/*{
"allowedConfirmations": [
{
"confirmationType": 2,
"associatedMessage": "qq.com"
},
{
"confirmationType": 6,
"associatedMessage": ""
}
],
"clientId": 17287054.....000,
"requestId": "je2U....bSqoY96Bgw==",
"interval": 1084227584,
"steamid": 76561191...6700,
"weakToken": "eyAidHlwI...bT1kBHhTJj-Sd8yK2SYdYIxifwfQZmb-1yq3zogxU9JzDa6RLyDgFxwr4bmQeNXVBA",
"agreementSessionUrl": "",
"extendedErrorMessage": ""
}*/
syntax = "proto3";
package Protoc;
option go_package = ".";
// 请求
message eMsg9804 {
string account_name = 2; // 1111111111
string encrypted_password = 3; // rsa加密密码
int64 encryption_timestamp = 4; // 570240150000
int32 set_remember_login = 5; // 1 是否记住登录
bool set_persistence = 7; // 1 坚持不懈?
string website_id = 8; // Mobile app内是 Mobile pc是 Store
repeated device_details device_details = 9; // 设备信息 手机和电脑不一样
int32 language = 11; // 6 语言,直接设置6就可以,中国人 app没有这个值 pc=6
}
message device_details {//电脑网页设备信息
string device_friendly_name = 1; //app=Pixel, pc= Dalvik/2.1.0 (Linux; U; Android 7.1.2; Pixel Build/NJH47F; Valve Steam App Version/3)
int32 platform_type = 2; // 2 平台类型 pc=2 app=3
int32 os_type = 3; //
uint32 gaming_device_type = 4; //
uint32 client_count = 5; //
bytes machine_id = 6; //
}
//响应============================================================
//CAuthentication_BeginAuthSessionViaCredentials_Response
//const {client_id: i, request_id: n, interval: a, allowed_confirmations: s, steamid: u, weak_token: d} = h.Body().toObject();
message Response_EMsg147 {
uint64 client_id = 1; //
bytes request_id= 2; // í'*c¡²m*¨cÞ
fixed32 interval= 3; // 1084227584
repeated allowed_confirmations allowed_confirmations= 4;
uint64 steamid= 5; /
string weak_token= 6; //
string agreement_session_url = 7; //协议会话url
string extended_error_message = 8; // {}
}
message allowed_confirmations{
int32 confirmation_type= 1;
string associated_message= 2;
}
//仅供参考
/*{
"allowedConfirmations": [
{
"confirmationType": 2,
"associatedMessage": "qq.com"
},
{
"confirmationType": 6,
"associatedMessage": ""
}
],
"clientId": 17287054.....000,
"requestId": "je2U....bSqoY96Bgw==",
"interval": 1084227584,
"steamid": 76561191...6700,
"weakToken": "eyAidHlwI...bT1kBHhTJj-Sd8yK2SYdYIxifwfQZmb-1yq3zogxU9JzDa6RLyDgFxwr4bmQeNXVBA",
"agreementSessionUrl": "",
"extendedErrorMessage": ""
}*/
// 本命令由自动生成,请配合[ go get -u gitee.com/anyueyinluo/Efunc ]库使用。
func (集 *Api) D登录_登录() bool {
局_网址 := `https://api.steampowered.com/IAuthenticationService/BeginAuthSessionViaCredentials/v1`
Http请求 := 集.Http客户端.EnableDumpAll().R()
Http请求.SetHeader(`Accept`, `application/json, text/plain, */*`)
Http请求.SetHeader(`Accept-Language`, `zh-CN,zh;q=0.9`)
Http请求.SetHeader(`Cache-Control`, `no-cache`)
Http请求.SetHeader(`Content-Type`, `multipart/form-data; boundary=----WebKitFormBoundarybxBPD59cJtdK0xKS`)
Http请求.SetHeader(`Origin`, `https://store.steampowered.com`)
Http请求.SetHeader(`Pragma`, `no-cache`)
Http请求.SetHeader(`Referer`, `https://store.steampowered.com/login/?redir=&redir_ssl=1&snr=1_4_4__global-header`)
局_设备信息 := []*__.DeviceDetails{{
DeviceFriendlyName: "Pixel",
PlatformType: 3,
}}
局_pb := __.EMsg9804{
AccountName: 集.账号,
EncryptedPassword: 密码加密(集.密码, 集.Rsa.PublicKey, 集.Rsa.MoShu),
EncryptionTimestamp: 集.Rsa.SuiJiShu,
SetPersistence: true,
SetRememberLogin: 1,
WebsiteId: "Mobile",
Language: 6,
DeviceDetails: 局_设备信息,
}
局_pb字节集, err := proto.Marshal(&局_pb)
if err != nil {
集.错误信息 = "登录信息序列化失败:" + err.Error()
return false
}
局_post := map[string]string{
"input_protobuf_encoded": base64.StdEncoding.EncodeToString(局_pb字节集),
}
Http请求.EnableForceMultipart().SetFormData(局_post)
var 局_请求结果 *req.Response
for i := 0; i < 3; i++ { // 重试三次防止意外
局_请求结果, err = Http请求.Post(局_网址)
if len(局_请求结果.Bytes()) > 0 || err != nil {
break
}
}
fmt.Printf("X-eresult:%s\n", 局_请求结果.GetHeader("X-eresult"))
if 局_请求结果.GetHeader("X-eresult") != "1" {
i, _ := strconv.Atoi(局_请求结果.GetHeader("X-eresult"))
switch i {
default:
集.错误信息 = 局_请求结果.GetHeader("X-eresult") + 局_请求结果.String()
fmt.Print("\n", 局_请求结果.Dump())
case 2:
集.错误信息 = "#Login_RefreshReason_Generic"
集.错误信息 = "#请再次登录"
case 7:
集.错误信息 = "#Login_RefreshReason_Generic"
集.错误信息 = "#请再次登录"
case 6:
集.错误信息 = "#Login_RefreshReason_LoggedInElsewhere"
集.错误信息 = "#此帐户已在另一台计算机上登录"
case 34:
集.错误信息 = "#Login_RefreshReason_LogonSessionReplaced"
集.错误信息 = "#此帐户已在别处登录"
case 5:
集.错误信息 = "#Login_RefreshReason_InvalidPassword"
集.错误信息 = "您的帐户凭据已更改,密码错误"
case 26:
集.错误信息 = "#Login_RefreshReason_Revoked"
集.错误信息 = "#您的会话已结束"
case 27:
集.错误信息 = "#Login_RefreshReason_Expired"
集.错误信息 = "#您的会话已过期"
case 49:
集.错误信息 = "#Login_RefreshReason_PasswordRequiredToKickSession"
集.错误信息 = "#确认您的凭据以从另一台计算机退出"
case 43:
集.错误信息 = "#Login_RefreshReason_AccountDisabled"
集.错误信息 = "#您的帐户已被禁用"
case 69:
集.错误信息 = "#Login_RefreshReason_ParentalControlRestricted"
集.错误信息 = "#您帐户的家长控制要求您确认凭据"
case 84:
集.错误信息 = "#Login_Error_RateLimit_Description"
集.错误信息 = "#短期内来自您所在位置的失败登录过多。请稍后再试。"
}
return false
}
fmt.Printf("登录响应Hex:%s\n", hex.EncodeToString(局_请求结果.Bytes()))
err = proto.Unmarshal(局_请求结果.Bytes(), &集.Response_EMsg147)
if err != nil {
集.错误信息 = "登录结果反序列化失败:" + err.Error()
return false
}
fmt.Print("\n", 局_请求结果.Dump())
if 集.Response_EMsg147.Steamid == 0 {
return false
}
return true
}
// 本命令由自动生成,请配合[ go get -u gitee.com/anyueyinluo/Efunc ]库使用。
func (集 *Api) D登录_登录() bool {
局_网址 := `https://api.steampowered.com/IAuthenticationService/BeginAuthSessionViaCredentials/v1`
Http请求 := 集.Http客户端.EnableDumpAll().R()
Http请求.SetHeader(`Accept`, `application/json, text/plain, */*`)
Http请求.SetHeader(`Accept-Language`, `zh-CN,zh;q=0.9`)
Http请求.SetHeader(`Cache-Control`, `no-cache`)
Http请求.SetHeader(`Content-Type`, `multipart/form-data; boundary=----WebKitFormBoundarybxBPD59cJtdK0xKS`)
Http请求.SetHeader(`Origin`, `https://store.steampowered.com`)
Http请求.SetHeader(`Pragma`, `no-cache`)
Http请求.SetHeader(`Referer`, `https://store.steampowered.com/login/?redir=&redir_ssl=1&snr=1_4_4__global-header`)
局_设备信息 := []*__.DeviceDetails{{
DeviceFriendlyName: "Pixel",
PlatformType: 3,
}}
局_pb := __.EMsg9804{
AccountName: 集.账号,
EncryptedPassword: 密码加密(集.密码, 集.Rsa.PublicKey, 集.Rsa.MoShu),
EncryptionTimestamp: 集.Rsa.SuiJiShu,
SetPersistence: true,
SetRememberLogin: 1,
WebsiteId: "Mobile",
Language: 6,
DeviceDetails: 局_设备信息,
}
局_pb字节集, err := proto.Marshal(&局_pb)
if err != nil {
集.错误信息 = "登录信息序列化失败:" + err.Error()
return false
}
局_post := map[string]string{
"input_protobuf_encoded": base64.StdEncoding.EncodeToString(局_pb字节集),
}
Http请求.EnableForceMultipart().SetFormData(局_post)
var 局_请求结果 *req.Response
for i := 0; i < 3; i++ { // 重试三次防止意外
局_请求结果, err = Http请求.Post(局_网址)
if len(局_请求结果.Bytes()) > 0 || err != nil {
break
}
}
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!