#序言 最近无聊,看到steam,登录更新了,所以手痒试试,正好学习,go语言和protobuf 开工, #抓包 逆向第一步,必然抓包看看啦,抓的这是啥,登录只有两个包, 看看第一个包 urlhttps://*****/IAuthenticationService/GetPasswordRSAPublicKey/v1?origin=https:%2F%2Fstore.steampowered.com&input_protobuf_encoded=CgoxMTExMTExMTEx
掐指一算,~~嗯嗯是一串有乱码的字符串,偷瞄一眼发现rsa密钥相关标志010001
再看url=GetPasswordRSAPublicKey 百度翻译=获取rsa公钥, 知道了这个包是获取RSA公钥,那么问题来了,为什么会中间有乱码呢? #protobuf数据格式 引入正题,protobuf是什么,具体百度一下你就知道,简单来说protobuf是一种Google提供的高效的协议数据交换格式,就像JSON一样,有固定的格式,正向开发中需要编写.proto 文件来秒数这个数据具体都有什么信息,逆向没有这个文件,就只能根据值盲猜了,我为什么知道这个是protobuf数据格式,没人天生知道,都是踩了很多坑,查了很多资料得出的经验
,所以一下能判断大概是什么数据https://gchq.github.io/CyberChef/ 把响应数据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
{
局_网址 :
=
"https://api.steampowered.com/IAuthenticationService/GetPasswordRSAPublicKey/v1"
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"
,
" https://store.steampowered.com"
)
Http请求.SetHeader(
"pragma"
,
" no-cache"
)
Http请求.SetHeader(
"referer"
,
" https://store.steampowered.com/login/?snr=1_4_4__more-content-login"
)
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
{
局_网址 :
=
"https://api.steampowered.com/IAuthenticationService/GetPasswordRSAPublicKey/v1"
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"
,
" https://store.steampowered.com"
)
Http请求.SetHeader(
"pragma"
,
" no-cache"
)
Http请求.SetHeader(
"referer"
,
" https://store.steampowered.com/login/?snr=1_4_4__more-content-login"
)
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
}
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)