关于读取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();

标签: none

添加新评论