保留账号的操作,hmm,貌似因此又被NIC的老师盯上了
早起MSN就收到消息。。。。。
lol
后续在live spaces里
Showing posts with label srun3000. Show all posts
Showing posts with label srun3000. Show all posts
Monday, June 15, 2009
Sunday, May 17, 2009
Monday, April 27, 2009
Srun3000 客户端分析的补充
先把后来补充的帖子贴上
时间差的校正
[url]http://www.bitunion.org/redirect.php?goto=findpost&ptid=10141437&pid=8570527[/url]
关于 3L的 FFFF57b4 这个数字
刚才咨询过 srun的技术总监
得到的答复是,在第一次使用客户端的时候,会和服务器进行一次额外通信
然后会有个双向误差校正的过程
这个数据存在注册表的 HKEY_CURRENT_USER\Software\srun3000\login 下的 time_diff里
然后来个人帮忙解决那个时间的问题吧
更新:
由于客户端的信息,存放在注册表中
刚才清空了注册表,进行了一次客户端的全过程抓包
1 登陆 密码错误,原因是时间不对,服务器的返回值为 password_error@TIMESTAMP 后边的 TIMESTAMP是服务器的unix timestamp,形如1240749982
2 本地时间纠正 用服务器的时间减去本地的时间,然后算出时间差,用某种形式存放在注册表里,就跟上边的那个诡异数字似的
3 新登陆 (int)(本地时间+时间差)60; 作为key
。。。。。。。
========================我是分割线=======================
基本时间差的算法就这样了
分钟级的校验,应该还是能接受的,就差传输安全了
但是比较不太舒服的是,新的版本,增加了一些东西,暂时没明白什么意思
大概 http 响应的数据包含三部分
(Code in hex)\r\ndata[\r\n[some other stuff]]
code in hex 大概是 1到两个字节的 16进制字符,估计是通信协议里的一些事件编码
最后一个 someother stuff 是在登陆的时候看到的
一开始没仔细看
周日去良乡,小范围讲了下调试这个客户端的一些东西,现场抓包,才看到的
时间差的校正
[url]http://www.bitunion.org/redirect.php?goto=findpost&ptid=10141437&pid=8570527[/url]
关于 3L的 FFFF57b4 这个数字
刚才咨询过 srun的技术总监
得到的答复是,在第一次使用客户端的时候,会和服务器进行一次额外通信
然后会有个双向误差校正的过程
这个数据存在注册表的 HKEY_CURRENT_USER\Software\srun3000\login 下的 time_diff里
然后来个人帮忙解决那个时间的问题吧
更新:
由于客户端的信息,存放在注册表中
刚才清空了注册表,进行了一次客户端的全过程抓包
1 登陆 密码错误,原因是时间不对,服务器的返回值为 password_error@TIMESTAMP 后边的 TIMESTAMP是服务器的unix timestamp,形如1240749982
2 本地时间纠正 用服务器的时间减去本地的时间,然后算出时间差,用某种形式存放在注册表里,就跟上边的那个诡异数字似的
3 新登陆 (int)(本地时间+时间差)60; 作为key
。。。。。。。
========================我是分割线=======================
基本时间差的算法就这样了
分钟级的校验,应该还是能接受的,就差传输安全了
但是比较不太舒服的是,新的版本,增加了一些东西,暂时没明白什么意思
大概 http 响应的数据包含三部分
(Code in hex)\r\ndata[\r\n[some other stuff]]
code in hex 大概是 1到两个字节的 16进制字符,估计是通信协议里的一些事件编码
最后一个 someother stuff 是在登陆的时候看到的
一开始没仔细看
周日去良乡,小范围讲了下调试这个客户端的一些东西,现场抓包,才看到的
Saturday, April 25, 2009
[搬移]Srun 3000 加密函数key的分析记录
原文在: http://www.bitunion.org/redirect.php?goto=findpost&ptid=10141437&pid=8567446
外网用 cnc.bitunion.org 或 kiss.bitunion.org
直接拿过来,改动一下格式而已
到 0040441C 这行,是实现对 time()函数的调用
返回的值在 EAX存放的是 timestamp,如 49F30B07 十进制表示的 1240664839
EDX的数据是清空了的
ECX的数据是堆栈中这个时间戳的地址,调试的时候,返回的是 00cdfa00,在堆栈段里,
后边的算法
0040441E . 8B4C24 2C MOV ECX,DWORD PTR SS:[ESP+2C]
这行是把刚才那个堆栈里那个数据复制到ecx里
于是这个操作之后,eax和ecx都是刚才time()返回的timestamp了
DWORD PTR DS:[ESI+60] 是一个 FFFF57b4的数
下边清空 eax
然后 sub ecx, edx
结果 ecx的数据成了 49F3B353
只有后4位进行了减法运算
清空了14字节的堆栈数据
因为之前xor eax, eax清空了eax
然后
一个复制,一个乘法,一个乘法结果高位右移
再最后就把这个数字 sprintf成一个字符串 %u
我就无语了
完全不懂
然后
改动代码,生成的时间串:
这个 43084 是刚才那个诡异减法的结果和原始数据的差,其实也就是
0x49F3B353 - 0x49F30B07
于是,这个key就对了
不懂为啥
两边比对了下,OD里的结果和php的输出一样
很好
外网用 cnc.bitunion.org 或 kiss.bitunion.org
直接拿过来,改动一下格式而已
00404411 > \8B2D BC834000 MOV EBP,DWORD PTR DS:[<&MSVCRT.time>] ; msvcrt.time
00404417 . 8D5424 28 LEA EDX,DWORD PTR SS:[ESP+28]
0040441B . 52 PUSH EDX ; /timer
0040441C . FFD5 CALL EBP ; \time
0040441E . 8B4C24 2C MOV ECX,DWORD PTR SS:[ESP+2C]
00404422 . 8B56 60 MOV EDX,DWORD PTR DS:[ESI+60]
00404425 . 33C0 XOR EAX,EAX
00404427 . 2BCA SUB ECX,EDX
00404429 . 894424 34 MOV DWORD PTR SS:[ESP+34],EAX
0040442D . 894424 38 MOV DWORD PTR SS:[ESP+38],EAX
00404431 . 894424 3C MOV DWORD PTR SS:[ESP+3C],EAX
00404435 . 66:894424 40 MOV WORD PTR SS:[ESP+40],AX
0040443A . B8 89888888 MOV EAX,88888889
0040443F . F7E1 MUL ECX
00404441 . C1EA 05 SHR EDX,5
00404444 . 52 PUSH EDX ; /<%u> = 13B873A (20678458.)
00404445 . 8D5424 38 LEA EDX,DWORD PTR SS:[ESP+38] ; |
00404449 . 68 F8B34000 PUSH srun3000.0040B3F8 ; |%u
0040444E . 52 PUSH EDX ; |s
0040444F . FF15 E0834000 CALL DWORD PTR DS:[<&MSVCRT.sprintf>] ; \sprintf
到 0040441C 这行,是实现对 time()函数的调用
返回的值在 EAX存放的是 timestamp,如 49F30B07 十进制表示的 1240664839
EDX的数据是清空了的
ECX的数据是堆栈中这个时间戳的地址,调试的时候,返回的是 00cdfa00,在堆栈段里,
00CDFA00 49F30B07
后边的算法
0040441E . 8B4C24 2C MOV ECX,DWORD PTR SS:[ESP+2C]
这行是把刚才那个堆栈里那个数据复制到ecx里
于是这个操作之后,eax和ecx都是刚才time()返回的timestamp了
DWORD PTR DS:[ESI+60] 是一个 FFFF57b4的数
下边清空 eax
然后 sub ecx, edx
结果 ecx的数据成了 49F3B353
只有后4位进行了减法运算
00404429 . 894424 34 MOV DWORD PTR SS:[ESP+34],EAX
0040442D . 894424 38 MOV DWORD PTR SS:[ESP+38],EAX
00404431 . 894424 3C MOV DWORD PTR SS:[ESP+3C],EAX
00404435 . 66:894424 40 MOV WORD PTR SS:[ESP+40],AX
清空了14字节的堆栈数据
因为之前xor eax, eax清空了eax
然后
0040443A . B8 89888888 MOV EAX,88888889
0040443F . F7E1 MUL ECX
00404441 . C1EA 05 SHR EDX,5
一个复制,一个乘法,一个乘法结果高位右移
再最后就把这个数字 sprintf成一个字符串 %u
我就无语了
完全不懂
然后
改动代码,生成的时间串:
<?php
$key = strval((time() + 43084)/60);
echo $key;
这个 43084 是刚才那个诡异减法的结果和原始数据的差,其实也就是
0x49F3B353 - 0x49F30B07
于是,这个key就对了
不懂为啥
两边比对了下,OD里的结果和php的输出一样
很好
Srun3000 客户端协议分析
Srun3000的新版终于上线了,之前的内测报了好几个安全漏洞,于是给了一份安全检测报告。结果现在自己想拿都很麻烦了,至少目前还没拿下 XD
然后,说客户端。
目前这个版本的客户端,感觉和上次内测的区别不大,不过至少我提出注册表数据加密之后,srun那边还是做了些工作,虽然那块儿今天没去调。
整体协议还是基于http的,因为还是没有keepalive的设计,所以最关键的就只是登陆的部分。也就是说,登陆的POST串的内容和结构才是我这次最关注的。
直接从客户端的exe文件里就可以提取出这样的字串
drop是是否只访问免费资源;type=2是写死的,应该就是表示windows客户端吧,linux的我没看,无所谓;n=7,跟出来的,上个版本是2吧,不记得了,不清楚到底有什么用;mac就不用说了
客户端没有加壳,直接OllyDBG载入
根据字串资源信息,很快地定位到了输入的函数,同时也找到了密码加密函数的入口
我手头这个版本 57,344 字节,加密函数的入口 在 0x00401350。
有许多地方调用了,但是这个不是我关注的,我只需要知道这个算法的内容。
根据push的规则,还原成C的代码,函数声明格式应该是类似
中间的几个 void* 具体应该是 unsigned char* 或者 char*,依旧无所谓。
pwd_in 就是密码输入框的内容
key的部分是通过当前系统时间算出来的
关于key的分析可以见我在联盟的帖子
http://www.bitunion.org/redirect.php?goto=findpost&ptid=10141437&pid=8567446
或者
http://kiss.bitunion.org/redirect.php?goto=findpost&ptid=10141437&pid=8567446
稍后考虑也贴过来
算法的部分很简单
两个字符串,一个password,一个key
设password 为 12345678
key为 20678439
逆序循环取key的字符,也就是 9,3,4,8,7,6,0,2,9,3,...
分别与password 的 [0], [1], [2] ... 直到password结束 进行异或运算
对于结果,一个字符
[0],[2],....[2n]的字符,译码之后:(低4位 加上 0x36 )连上 (高4位 加上 0x63)
[1],[3],....[2n+1]的字符,译码之后:(高4位 加上 0x63)连上 (低4位 加上 0x36 )
然后连接之后的结果就是提交的密码的密文
当然,提交的时候,得 实现 urlencode或类似的函数处理这段数据
代码很乱,无所谓了
能改描述清楚问题,并且实现功能就行
Sample Code in PHP:
然后,说客户端。
目前这个版本的客户端,感觉和上次内测的区别不大,不过至少我提出注册表数据加密之后,srun那边还是做了些工作,虽然那块儿今天没去调。
整体协议还是基于http的,因为还是没有keepalive的设计,所以最关键的就只是登陆的部分。也就是说,登陆的POST串的内容和结构才是我这次最关注的。
直接从客户端的exe文件里就可以提取出这样的字串
username=%s&password=%s&drop=%d&type=2&n=%d&mac=%s显然,用户名,密码,drop,type=2,n,mac
drop是是否只访问免费资源;type=2是写死的,应该就是表示windows客户端吧,linux的我没看,无所谓;n=7,跟出来的,上个版本是2吧,不记得了,不清楚到底有什么用;mac就不用说了
客户端没有加壳,直接OllyDBG载入
根据字串资源信息,很快地定位到了输入的函数,同时也找到了密码加密函数的入口
我手头这个版本 57,344 字节,加密函数的入口 在 0x00401350。
有许多地方调用了,但是这个不是我关注的,我只需要知道这个算法的内容。
根据push的规则,还原成C的代码,函数声明格式应该是类似
void encrypt(void* pwd_in, void* key, void* pwd_out);返回值我不管了,因为没去那边往下跟,无所谓。
中间的几个 void* 具体应该是 unsigned char* 或者 char*,依旧无所谓。
pwd_in 就是密码输入框的内容
key的部分是通过当前系统时间算出来的
关于key的分析可以见我在联盟的帖子
http://www.bitunion.org/redirect.php?goto=findpost&ptid=10141437&pid=8567446
或者
http://kiss.bitunion.org/redirect.php?goto=findpost&ptid=10141437&pid=8567446
稍后考虑也贴过来
算法的部分很简单
两个字符串,一个password,一个key
设password 为 12345678
key为 20678439
逆序循环取key的字符,也就是 9,3,4,8,7,6,0,2,9,3,...
分别与password 的 [0], [1], [2] ... 直到password结束 进行异或运算
对于结果,一个字符
[0],[2],....[2n]的字符,译码之后:(低4位 加上 0x36 )连上 (高4位 加上 0x63)
[1],[3],....[2n+1]的字符,译码之后:(高4位 加上 0x63)连上 (低4位 加上 0x36 )
然后连接之后的结果就是提交的密码的密文
当然,提交的时候,得 实现 urlencode或类似的函数处理这段数据
代码很乱,无所谓了
能改描述清楚问题,并且实现功能就行
Sample Code in PHP:
<?php $password = '123456'; echo encrypt($password); function encrypt($password) { $key = strval(floor((time() + 43084)/60)); return _encrypt($password, $key); } function _encrypt($pass, $key) { $pass = substr($pass, 0, 16); $len = strlen($pass); $ret = ''; for ($i=0; $i<$len; $i++) { $_pass = ord($pass[$i]); $_key = _get_keychar($key, $i); $_key = $_key ^ $_pass; $ret .= _build_key($_key, $i%2); } return $ret; } function _get_keychar($str_in, $num) { $len = strlen($str_in); return ord($str_in[ $len - $num % $len - 1 ]); } function _build_key($num, $reverse=0) { $ret = ''; $_low = $num & 0x0f; $_high = $num >> 4; $_high = $_high & 0x0f; if (!$reverse) { $ret .= chr($_low + 0x36); $ret .= chr($_high + 0x63); } else { $ret .= chr($_high + 0x63); $ret .= chr($_low + 0x36); } return $ret; }
Monday, March 30, 2009
Thursday, March 12, 2009
A PHP Class for Srun3000
本来打算写个完整的客户端的,但是却一直没有想好cli下怎么跟php的进程进行通信
于是写好了一个php的类,简单地封装了 登陆、注销、保持连接、查询信息 的操作
调用方法示例
于是写好了一个php的类,简单地封装了 登陆、注销、保持连接、查询信息 的操作
/**
* Srun3000客户端类
*
* @author sskaje sskaje@gmail.com
* @version 1.0.0
* @todo 提供一个通信的机制
*/
class sscSrun3000forBIT
{
private $username;
private $password;
private $mode = 1;
private $client_type = 1;
private $n = 1;
private $url_login = 'http://10.0.0.55/cgi-bin/do_login';
private $url_logout = 'http://10.0.0.55/cgi-bin/do_logout';
private $url_keepalive = 'http://10.0.0.55/cgi-bin/keeplive';
private $modes = array(0=>'国际模式', 1=>'国内模式');
private $client_types = array(1=>'WEB 模式', 2=>'客户端模式');
private $user_agent = '';
private $uid = 0;
public $debug = 0;
/**
* cookie文件存放路径
*
* @var string
*/
private $cookie_file = 'cookie.txt';
/**
* cURL 资源
*
* @var cURL
*/
private $ch;
/**
* 构造函数,默认参数构造
*
* @param string $username 用户名
* @param string $password 密码
* @param int $mode 登陆模式,1->国内,0->国际
* @param int $client_type 客户端模式,1->Web,2->客户端
*/
public function __construct($username, $password, $mode=1, $client_type=1)
{
$this->username = $username;
$this->password = $password;
$this->change_mode($mode);
$this->set_client_type($client_type);
}
/**
* 设置客户端模式
*
* @param int $client_type 客户端模式,1->Web,2->客户端
*/
public function set_client_type($client_type)
{
if ($client_type == 1)
{
$this->client_type = $client_type;
$this->n = 1;
$this->user_agent = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; GreenBrowser)';
}
else if ($client_type == 2)
{
$this->client_type = $client_type;
$this->n = 3;
$this->user_agent = 'my session';
}
else
{
throw new Exception('Bad client type');
}
}
/**
* 设置登陆模式
*
* @param int $mode 登陆模式,1->国内,0->国际
*/
public function change_mode($mode)
{
if ($mode != 1 && $mode != 0)
{
throw new Exception('Bad login mode');
}
else if ($mode == 0)
{
echo "将以国际模式登陆\n";
}
else
{
echo "将以国内模式登陆\n";
}
$this->mode = $mode;
}
/**
* 设置cookie文件路径
*
* @param string $cookie_file
*/
public function set_cookie_fileloc($cookie_file)
{
$this->cookie_file = $cookie_file;
}
/**
* 初始化资源
*
*/
public function init()
{
$this->ch = curl_init();
curl_setopt($this->ch, CURLOPT_VERBOSE, 0);
curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($this->ch, CURLOPT_COOKIEJAR, $this->cookie_file);
if ($this->client_type == 2)
{
curl_setopt($this->ch, CURLOPT_USERAGENT, 'my session');
curl_setopt($this->ch, CURLOPT_PORT, 3333);
}
else
{
curl_setopt($this->ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; GreenBrowser) ');
}
}
/**
* 登陆
*
*/
public function login()
{
if ($this->client_type == 2)
{
$password = $this->passencode($this->password);
}
else
{
$password = $this->password;
}
$login_str =
'username=' . urlencode($this->username) .
'&password=' . urlencode($password) .
'&drop=' . $this->mode .
'&type=' . $this->client_type .
'&n=' . $this->n;
$this->debug('Login: ' . $login_str);
curl_setopt($this->ch, CURLOPT_URL, $this->url_login);
curl_setopt($this->ch, CURLOPT_REFERER, $this->url_login);
curl_setopt($this->ch, CURLOPT_POST, 1);
curl_setopt($this->ch, CURLOPT_POSTFIELDS, $login_str);
$out = curl_exec($this->ch);
$this->debug('Login response: ' . $out);
curl_setopt($this->ch, CURLOPT_COOKIE, $this->username.'%7C'.$this->password);
if (preg_match('#^\d+$#', $out))
{
$this->uid = $out;
return;
}
$this->login_error($out);
}
/**
* 注销
*
*/
public function logout()
{
curl_setopt($this->ch, CURLOPT_URL, $this->url_logout);
curl_setopt($this->ch, CURLOPT_POST, 1);
$logout_str = 'uid='.$this->uid;
$this->debug('Logout query: '.$logout_str);
curl_setopt($this->ch, CURLOPT_POSTFIELDS, $logout_str);
$out = curl_exec($this->ch);
$this->debug('Logout: ' . $out);
if ($out == 'logout_ok')
{
echo 'Successfully logout.';
}
else
{
echo 'Failed to log u out';
}
}
/**
* 保持连接
*
* @return string
*/
public function keepalive()
{
curl_setopt($this->ch, CURLOPT_URL, $this->url_keepalive);
curl_setopt($this->ch, CURLOPT_POST, 1);
$keepalive_str = 'uid='.$this->uid;
$this->debug('Keepalive query: '.$keepalive_str);
curl_setopt($this->ch, CURLOPT_POSTFIELDS, $keepalive_str);
$out = curl_exec($this->ch);
$this->debug('Keepalive: ' . $out);
if (preg_match('#^\d+,\d+,\d+$#', $out))
{
return $out;
}
else
{
die('Disconnected.');
}
}
/**
* 客户端模式密码加密
*
* @param string $pass
* @return string
*/
public function passencode($pass)
{
$atom = 70;
$len = strlen($pass);
for ($i=0; $i<$len; $i++)
{
$pass[$i] = chr(ord($pass[$i]) ^ $atom);
}
return $pass;
}
/**
* 登陆提示
*
* @param string $out
*/
public function login_error($out)
{
switch($out)
{
case "user_tab_error":
die("认证程序未启动");
break;
case "username_error":
die("用户名错误");
break;
case "non_auth_error":
echo "您无须认证,可直接上网";
break;
case "password_error":
die("密码错误");
break;
case "status_error":
die("用户已欠费,请尽快充值。");
break;
case "available_error":
die("用户已禁用");
break;
case "ip_exist_error":
die("用户已存在");
break;
case "usernum_error":
die("用户数已达上限");
break;
case "online_num_error":
die("该帐号的登录人数已超过限额\n如果怀疑帐号被盗用,请联系管理员。");
break;
case "mode_error":
die("系统已禁止WEB方式登录,请使用客户端");
break;
case "time_policy_error":
die("当前时段不允许连接");
break;
case "flux_error":
die("您的流量已超支");
break;
case "minutes_error":
die("您的时长已超支");
break;
case "ip_error":
die("您的IP地址不合法");
break;
case "mac_error":
die("您的MAC地址不合法");
break;
default:
die("找不到认证服务器");
break;
}
}
/**
* debug数据
*
* @param string $message
*/
public function debug($message)
{
if ($this->debug)
echo "\n=====================================\n[DEBUG] ", date('Ymd H:i:s '), $message, "\n=====================================\n";
}
/**
* 查询状态
*
*/
public function query()
{
$msg = $this->keepalive();
$nums = explode(',', $msg);
echo "
Username: {$this->username}
UID : {$this->uid}
Mode : {$this->modes[$this->mode]}
Type : {$this->client_types[$this->client_type]}
Data in : {$nums[1]} bytes
Data out: {$nums[2]} bytes
Connection time: {$nums[0]} seconds
";
}
}
调用方法示例
require('Srun3000forBIT.class.php');
try
{
#$srun = new sscSrun3000forBIT('sskaje', 'pass', 1, 2);
$srun = new sscSrun3000forBIT('sskaje', 'pass', 1, 1);
$srun->debug = 0;
$srun->init();
$srun->login();
$srun->keepalive();
$srun->query();
$srun->logout();
#$srun->keepalive();
}
catch (Exception $e)
{
echo $e->getMessage();
}
Subscribe to:
Posts (Atom)