关于读取Steam上号器输入的账号密码
首先是能注入自己的shellcode, 想办法绕吧...
然后烂大街的 Steam->login(user,pass)
这个位于ButtonClick
事件下面的函数已经不能用了
很多上号器会输入假的账号密码, 然后在客户端发送登录包之前写入真正的账号密码
举个栗子, 某某租号的实现流程:
Hook WSASend
, 监听到steamcommunity.com
的流量则开启登录Hook
监听到akamaihd.net
的流量则结束登录Hook
这个登录Hook, 主要是 steamclient.dll 里面的两个函数
char __thiscall core_login_encrypt(_DWORD *this, int a2)
{
_DWORD *v2; // ebx
int v3; // eax
int v4; // eax
char v5; // cl
int v6; // ecx
int v7; // eax
char v9; // [esp+Ch] [ebp-114h]
int v10; // [esp+10Ch] [ebp-14h]
int v11; // [esp+110h] [ebp-10h]
int v12; // [esp+114h] [ebp-Ch]
char v13; // [esp+118h] [ebp-8h]
v2 = this;
v3 = sub_386C2C50(this[25]);
v2[29] = v3;
v4 = (*(int (__stdcall **)(int, const char *, signed int, _DWORD, _DWORD))(*g_pMemAllocSteam + 8))(
v3,
"c:\\buildslave\\steam_rel_client_win32\\build\\src\\common\\netfilter.cpp",
993,
0,
0);
v5 = *((_BYTE *)v2 + 96);
v2[28] = v4;
*(_BYTE *)(v4 + 13) = v5;
*(_BYTE *)(v2[28] + 14) = *((_WORD *)v2 + 48) >> 8;
*(_BYTE *)(v2[28] + 15) = *((_BYTE *)v2 + 98);
some_build(v2[28] + 16, v2[26], v2[25]);
CCrypto::GenerateHMAC(v2[28] + 13, v2[25] + 3, (int)(v2 + 16), 0x10u, (int)&v10);
v6 = v2[28];
*(_DWORD *)v6 = v10;
*(_DWORD *)(v6 + 4) = v11;
*(_DWORD *)(v6 + 8) = v12;
*(_BYTE *)(v6 + 12) = v13;
if ( !(unsigned __int8)j_CCrypto::SymmetricEncrypt(
v2[28] + 16, // pubPlaintextData
v2[25], // cubPlaintextData (Length)
v2[28], // rgubIV
16, // k_nSymmetricBlockSize
v2[28], // pubEncryptedData
(int)(v2 + 29), // pcubEncryptedData (Length)
(int)(v2 + 16), // pubKey
32) ) // cubKey (Length)
{
v7 = CDbgFmtSafeImplT<1>::CDbgFmtSafeImplT<1>(&v9, "Failed to encrypt outgoing data");
AssertMsgImplementation(v7, "c:\\buildslave\\steam_rel_client_win32\\build\\src\\common\\netfilter.cpp", 1027);
(*(void (__stdcall **)(_DWORD, _DWORD))(*g_pMemAllocSteam + 28))(v2[28], 0);
v2[28] = 0;
*((_BYTE *)v2 + 108) = 0;
}
return 1;
}
也就是上面这个函数里面的这两个
CCrypto::GenerateHMAC(
const uint8 *pubData,
uint32 cubData,
const uint8 *pubKey,
uint32 cubKey,
SHADigest_t *pOutputDigest);
CCrypto::SymmetricEncrypt(
const uint8 *pubPlaintextData,
const uint32 cubPlaintextData_,
const uint8 *pIV, const uint32 cubIV,
uint8 *pubEncryptedData, uint32 *pcubEncryptedData,
const uint8 *pubKey,
const uint32 cubKey);
函数参考 https://github.com/ValveSoftware/GameNetworkingSockets/blob/master/src/common/crypto.cpp
租号器接管了这两个函数, pubData/pubPlaintextData 是通过protobuf生成的登录包, 上号器会劫持这两个函数, 在内部计算真正登录包, 然后返回给Steam.
然后怎么读出来就很简单了
直接call
bool CCrypto::SymmetricDecrypt(
const uint8 *pubEncryptedData, uint32 cubEncryptedData,
uint8 *pubPlaintextData, uint32 *pcubPlaintextData,
const uint8 *pubKey,
const uint32 cubKey);
解密算法(大概)(https://github.com/seishun/node-steam-crypto/blob/master/index.js)
var aesIv = crypto.createDecipheriv('aes-256-ecb', key, '');
aesIv.setAutoPadding(false);
aesIv.end(input.slice(0, 16));
var aesData = crypto.createDecipheriv('aes-256-cbc', key, aesIv.read());
aesData.end(input.slice(16));
return aesData.read();