首页
论坛
专栏
课程

[商业保护] [翻译]FLEXlm latest information by CrackZ

2006-11-18 01:39 15543

[商业保护] [翻译]FLEXlm latest information by CrackZ

sln
2
2006-11-18 01:39
15543
原文出处:http://www.woodmann.com/crackz/Tutorials/Flex2004.htm
关于译文:
1)涉及固定格式部分的内容不加以翻译,比如license里的内容:Feature vendor,假使翻译过来会很难看,更何况是在license格式的规范之下。
2)这个英文是我见过最糟糕的,我自己臆测了不少,所以,部分没有去翻译它。但是该部分并没有涉及技术。
3)同时,因为我的臆测,本人也非计算机专业,是个新手,所以文章必然难看,若有疑问,请大家参考原文。
4)重要的是,将里面的错误指出,并改之,这样就能有所提升,对大家有用。
5)这是初稿,我没有校过,我会结合大家的意见不断修改。

特别申明!!!以此文感谢看雪论坛,FLEXlm QQ群上的大家,尤其是手把手教过我的人 大家的帮助使我获益匪浅!!!

CrackZ之FLEXlm最新资料

应WISE软件(本文档涉及到的一个程序开发商)法律要求,该程序名称和许可证的Feature不在本文出现,而用“censored”这个字眼代替,以变避免任何法律问题。 应该注意到,这将不会掩饰FLEXlm是一恶心的保护这个事实。

25-01-2005 什么是sign?

介 绍
本文讨论了3个用FLEXlm保护程序的逆向分析。 本文是一经典的例子,用一很简单的(事实上也很一般的)技术绕过厂家购用的Macrovisions的 "提高安全"技术。 遍及本文,我使用Tip来帮助你理解。

Bitplane Imaris v4.0.3 (FLEXlm v9.0.0), v3.2可以当作是背景知识来看。
Censored v13.0 (FLEXlm v8.2a)。
SDS 2 v6.318 (FLEXlm v8.1a)。

*        Tip*
使用lmtools.exe中的Utilitise工具(有时随同你的应用程序带着或在FLEXlm SDK里)打开用FLEXlm加密的文件可以用来识别所用的FLEXlm库的版本号。 或用UltraEdit之类的工具打开用FLEXlm加密的文件搜索lmgr.lib 或liblmgr.a字串来确定FLEXlm的版本号。

具有一些基本信息的后,我开工了。

Bitplane Imaris??我从v3.1/2(使用FLEXlm7.2e保护 )得知vendor keys、seed和feature name,对于新的FLEXlm的Feature我一无所知。建议从Bitplane的网站上去下载文件。

censored v13.0--我手头有已被补丁过、去掉保护的程序,初步分析表明其FLEXlm并未被补丁,不得不进一步检查。

SDS 2 v6.318 --我手头的这个是破解版并带一个无效的FLEXlm许可证。 我从一位用户那里知道SDS使用了与前几版不一样的新的FLEXlm SIGN=license 许可证格式。

拿Bitplane Imaris开刀,已经知道了vendor keys, seeds 和 license文件格式,我希望确定这一切会因此而变得容易一些。 我创建一个如下假的license(在我已经了解的以前版本格式的基础上,同时假定vendor name"bitplane"没有改变、因为有个同名的deamon程序在软件里面,这样看来这个假设很不错)∶

# Bitplane v4.0.3的license

FEATURE feature_name vendor_name 1.000 permanent uncounted 123456654321 \
HOSTID=ANY

#license结束

在以前,对于假的license,Imaris总是跳出的一个友好的信息窗口,提示程序需要的Feature name和FLEXlm错误码。本版这些东西都没有了,而是到了演示模式。 我们需要去定位_lc_checkout()。

定位_lc_checkout()
定位_lc_checkout()(当你知道怎么做的话)事实上是很非常简单的,然而,我用SoftICE攻击我的目标程序,只能也必能用IDA经过lmgr.lib来识别定位,在这里我们进行了漂亮的分析,你也可以告诉人们你找到的校验程在什么地方。 IDA做的lmgr.lib反汇编确实不错(确认选lm_ckout.obj作为object,因为程序里有60多个,如果你有其他的理由,可以看看其它的)。 搜索字符串参考"lm_ckout.c" 、这个交叉引用相当的精确(在各子程序的偏移量之正下方),我的目标程序里找到3处该检验码。 由此处它会进入,并调用_l_checkout()。

*Tip*
注意:你可以用IDA的FLAIR工具lmgr.lib产生签名文件,然而该签名并非没有错误并经常缺失关键函数,因为_l_sg()函数与冗余_l_svk()函数(该函数是GlobeTrotter特意用来迷惑用的)很是接近,所以基本上每次分析必然会缺_l_sg()。 如果使用IDA法,我推荐使用我的lmgr.lib。

由此处我们得到版本号(4.0)和Feature名ImarisBase以及job结构。 从_l_checkout()往下看(在lmgr.lib的反汇编里),可以很快地追踪到我们的老朋友_l_sg()。 现在_l_checkout()拒绝我们伪造的许可证,并提示-8错误(可恶的LM_BADCODE)。 使用HIEW或任何其他的好的十六进制编辑器,搜索F8FFFFFF(-8,逆字节次序)就可以找到地方。

我们会找到三个地方; 在Imaris里,分别是5F7282, 5F77D7 和5F8AE5,前两个在_l_good_lic_key()引用到、最后一个在_l_ckout_ok()里引用到,我们可以将这些打上补丁,但是,现在要跟踪_l_checkout(),各事件次序如下∶

_l_zcp()。
_l_getattr()。
_l_ckout_borrow()。
_lm_start_real() - 返回-8(我们将追踪到这之下)。

由此可见,_lm_start_real()是关键之所在,也要注意一下代码片断∶

mov _lm_start$S23038, offset _lm_start_real; lm_start=lm_start_real

从以前的_l_sg()来看,FLEXlm迷惑项会记得这个途径。 向上看∶

_l_valid_version() - 检查有效的版本。
_l_clear_error() -从 _l_ckout_borrow()清除错误。
_l_zcp()
_l_next_conf_or_marker()
_l_local_verify_conf()
_l_good_lic_key()--返回-8错误提示。

再一次,我们得到一些并不重要的初始化代码,配置证实然后_l_good_lic_key()设置错误信息。

_l_xor_name(bitplane) - l_xorname(job->vendor, &vc);
_l_sg() - l_sg(job, job->vendor, &vc);; (你知道l_sg的意思是 "signature vendor_key5" ;-))。

我们的老朋友_l_sg()在这,为了确认是否有什么变化,我决定用Nolan Blenders的种子回收技术来看看。

:005F6F7D lea ecx, [ebp+var_280]
:005F6F83 push ecx <-- Vendor code structure
:005F6F84 mov edx, [ebp+arg_0]
:005F6F87 add edx, 30Ch
:005F6F8D push edx <-- Vendor name
:005F6F8E mov eax, [ebp+arg_0]
:005F6F91 push eax <-- Job structure
:005F6F92 call _l_sg

在追踪_l_sg()之前,一定要注意到 vendor code structure,就我的来说象如下这样,也要注意到job structure可能没有填充∶

04 00 00 00 19 59 D5 7A ED A3 2D 80 ED 11 A0 18
97 E1 4B 27 A8 21 6A E2 41 04 58 52 09 00 00 00

在追踪在_l_sg()以后、我们看到下面的变化∶

Vendor code structure

04 00 00 00 52 EE F9 99 A6 14 01 63 ED 11 A0 18
97 E1 4B 27 A8 21 6A E2 41 04 58 52 09 00 00 00

Job structure

66 00 00 00 EA 00 AA 00 C6 E0 F4 21 93 65 B8 00
00 00 6F 00 00 00 00 00 00 00 00 00 02 00 00 00

将这些值代入到calcseed.exe里,我们成功地导出0x0A192837(seed 1)和0xF0E1D2C3(seed 2),与前几版的SEED一样。用升级版的lmkg.exe产生vendor key(好友Nolan Blender制作或使用lmv8gen.exe(去我的FLEXlm网页找)),我们可以生成licenses(注意:你需要将ENCRYPTION_SEED1和ENCRYPTION_SEED2插入到define,或从旧的lm_code.h复制过来),后来我在SDK的LM_SEED里发现必须定义,尽管他们没有什么用如果你的LM_STRENGTH设置适当地话。

Bitplane现在成功地通过了许可证检查,这意味着无需使用任何那新的FLEXlm feature即使没有将全部的子程序编译出来,你现在可以自由地产生新的FEATURE的licenses,得到许可证选择权。

暗礁
FLEXlm不会这么容易吧? 真的吗? 让我们用上面的技术来试试censored吧…..由测试可知,censored可以追出7个Feature:
*censored*/*censored*/*censored*/*censored*/*censored*/*censored*/*censored*(*censored*与*censored*分别被ODB2DT.exe及PADS2GT校验)。

有人告诉我,这跟consored的编辑类型相对应,所以实际上只有一个Feature成功通过了校验。我做了一个consored的license,但是_l_checkout()拒绝该license,并有-8(LM_BADCODE)错误提示,其他的Feature以-5(LM_NOFEATURE)错误提示而告失败。这没有什么好奇怪的,关键的是我们现在知道了在_l_checkout()之下必有一个东西在决定该license是无效的。

我相当自信地认为license被拒的原因是新的ECC加密,因为该技术已经得到很好地实现(Certicom做得很对),指望不上能够找到真正的LM_SEED。强力攻击需要2∧96次。[此处有一句实在猜不出什么意思,但对技术没有用,没有翻译]。现在有2件事情可以去做。第一,我们可以从FLEXlm库里提取-8报错(0xFFFFFFF8)并找到它的位置(就像Imaris一样,用16位编辑器打开censored,然后查找),能够找到6个地址备用,经过简单地测试,可以找到正确地那个。或者第二,我们在censored里从_l_sg开始跟踪。

继续跟踪,我简单地比较了Imaris与consored的代码流(code flow)。紧接着,两者都调用_l_ckout_crypt()并且在调用_real_crypt()之后代码流开始不一样。这时修改执行文件会产生一个空指针错误。Imaris的代码流径直向我们熟悉的真实license与从license文件读取的license逐字比较处,这暗示着_real_crypt()与其下面的子程序决定了是否进行老格式的license校验还是进行新格式的license校验。让我们看看控制这个的代码段吧(取自Imaris):
_real_crypt()
:005F9AF0 push ebp
:005F9AF1 mov ebp, esp
:005F9AF3 sub esp, 9ACh
:005F9AF9 lea eax, [ebp+var_93C]
:005F9AFF mov [ebp+var_4], eax
:005F9B02 mov [ebp+var_948], 0
:005F9B0C mov [ebp+var_960], 0
:005F9B16 mov [ebp+var_950], 0
:005F9B20 mov [ebp+var_968], 0
:005F9B2A mov ecx, [ebp+arg_4]
:005F9B2D mov edx, [ecx+50h]
:005F9B30 mov [ebp+var_964], edx
:005F9B36 mov eax, [ebp+arg_8]
:005F9B39 mov [ebp+var_944], eax
:005F9B3F mov [ebp+var_96C], 0
:005F9B49 mov [ebp+var_940], 0
:005F9B53 mov ecx, [ebp+arg_0]
:005F9B56 mov edx, [ecx+3FCh]  ; 将要校验的license类型
:005F9B5C and edx, 100000h
:005F9B62 test edx, edx
:005F9B64 jnz short loc_5F9B72
5F9B56处的EDX值控制着将要进行哪种类型的license校验。Imaris里该值是0x1048C0,consored里该值为0x48C0。如果我现在在当前的活动调试会话窗里修改这个值,consored将会用旧的FLEXlm加密校验通过,我们成功地获得许可授权。下一步要去看看FLEXlm子程序在哪地方设置这个标记(flag)。用个简单的bpm吧,我们向后追踪到该值用一个“or ecx, 100000h”指令在_l_good_lic_key()里设置,以这为参考,往回跟踪另一个标记校验在_l_sg()之后。

:005F6F92 call _l_sg
:005F6F97 add esp, 0Ch
:005F6F9A mov ecx, [ebp+arg_0]
:005F6F9D cmp dword ptr [ecx+51Ch], 0
:005F6FA4 jz loc_5F704A <-- if ((job->L_SIGN_LEVEL) && !conf->lc_keylist).

所以,如果我们在这得到0值,则进行旧格式的许可校验。我们现在可以在这里以打补丁的方式来解决这一问题。但是我们往后再次跟踪。这次可以找到有2个位置将1值写到我们的[ecx+51Ch]标记处,在consored里,它们分别是6AF454(在_l_sg()的结尾处)和6B219C(在_lc_new_job()的结尾处)。

标记1

6AF454―在6AF426地址处用静态指令“ADD ECX, 1”设为1,就只用了一个字节的改变将ADD ECX,0打补丁了。但是Imaris有同样的参考所以在代码路径(code path)必须有个变量(variation)。经过比较Imaris与censored的代码流,我们发现了下面的“开关”代码:
:006AF354 mov edx, [ebp+var_5DC]
:006AF35A cmp dword ptr [edx], 0
:006AF35D jz loc_6AF434 ;      0x0 Imaris, 0x10 *censored*
这是我们向后跟踪的真正开关,它位于_l_buf_36()(子程序,在下面有描述)里面,唯一可靠查找方式是去的定位静态值存储在什么地方,如下所示。

i)在_l_buf_36()子程序下断点,下d *(esp+8)命令,在数据窗口查看vendor代码结构的指针,有时候,你可能要用SoftICE叫出该地址。

ii)在[vedor 代码结构+3Ch]设bmp w命令,监视写出的值,任何非零的值都是数据写入的静态位置(注意,你可能得到3或******此处为翻译,实在不知道是什么意思。)。一旦我们确定了正确的位置,我们就可以在这打个静态数据的小补丁。
:00405FB6 mov eax, [edx+3Ch]
:00405FB9 add eax, dword_81C7F8 ; 0x10 静态数据
:00405FBF mov ecx, [ebp+0Ch]
:00405FC2 mov [ecx+3Ch], eax ;    写标记
标记2

6B219C ?通过调用_lc_new_job()来设置。看下面的代码:

_lc_new_job proc near
:006B20B0 push ebp
:006B20B1 mov ebp, esp
:006B20B3 sub esp, 14h
:006B20B6 mov eax, _l_n36_buf
:006B20BB mov [ebp+arg_4], eax
:006B20BE lea ecx, [ebp+var_10]
:006B20C1 push ecx
:006B20C2 push 0
:006B20C4 push 0
:006B20C6 push 0
:006B20C8 mov edx, [ebp+arg_8]
:006B20CB push edx
:006B20CC lea eax, [ebp+var_C]
:006B20CF push eax
:006B20D0 call [ebp+arg_4] ; _l_n36_buf()
_l_n36_buf dd 401000h
:0040100F mov eax, [ebp+1Ch]
:00401012 mov dword ptr [eax], 1 ; 设置标记 (我们要的是0)
这需要打个补丁,在Imaris里,该处标记设为AND 0,censored设为1,SDS设为2。如果我们做些必要的改动,censored会启动并许可授权成功。我已经开发了一个“muster”,你可以将这个打出来并用于你的下一个FLEXlm目标程序。现在用我这个技术来破解SDS2V6.318吧(请注意,本法不全是依赖快速的替换成用lmgr.lib注释的IDA数据库),下面的截屏来自注释的.idb文件。
*        Tip*值得载入lm_crstr.obj到IDA里以帮助注释你的目标程序idb
1、        定位_l_sg()
在程序头开始,通过搜索16进制常量6F7330B8h(unsigned long x = 0x6f7330b8; /*- v8.x */)这看来是小事一桩(注意:有两处引用了该常量,是第一个才是_l_sg(), 另一个位于_l_svk(),纯粹是用来迷惑用的)。如果你感觉足够倾向的话,你能将dword_161DEC4标记为_1_n36_buff。
_l_sg = sub_DBFD0A

2、        定位_l_init()
_l_sg()应该有6处交叉引用。迅速地地查找代码,在每个调用_l_sg之后,可以找到默认的SEED0x87654321与0x12345678。_l_init()即可确定。
:00DCD13E call _l_sg
:00DCD143 add esp, 0Ch
:00DCD146 cmp dword ptr [ebp-27Ch], 87654321h
:00DCD150 jz short loc_DCD15E
:00DCD152 cmp dword ptr [ebp-278h], 12345678h
:00DCD15C jnz short loc_DCD1BB
:00DCD15E
:00DCD15E loc_DCD15E: ; CODE XREF: sub_DCC520+C30j
_l_init() = sub_DCC520
3、        定位_l_good_lic_key()
查看其他的_l_sg引用,_l_good_lic_key()是唯一一个引用_l_sg()两次的函数,从我的注释过的.idb里很容易看出这个。
【图一】

_l_good_lic_key = sub_DBE15D
4、_l_good_lic_key()有9个交叉引用,为3个函数的3次交叉引用。库里最上头的那个是_lm_start_real()。在完全注释的.idb里,这个看的很清楚。
【图二】

_lm_start_real() = sub_00DBD112
对_lm_start_real()唯一的交叉引用应该在_l_checkout()里面
_l_checkout() = sub_DBCA0E
从_l_checkout()我们很容易地推知:在函数地开始处寻找对lm_ckout.c的引用(其它对_l_checkout()的引用在_l_reconnect()里面)。_lc_checkout() = sub_DBC940

5、_lc_checkout()有两个交叉引用,第二个引用是mov dword ptr [reg32+30h],offset_lc_checkout()格式(实际上这是InitLmInterface()的一部分)向上滚动,我们在[reg32+0Ch](或第一个入口处)标记该子程序为_lc_init(), 这得出_lc_new_job()调用_lc_init()为sub_DC8610,调用_l_n36_buf为dd 0E07100h。
*Tip* 将lmgr.lib载入IDA并选择l_look_load.obj,你能标出InitLmInterface()所有的函数。在之后的FLEXlm版本里,目标程序所用的lc_auth_data()可以在l_check.obj里找到
6、在一个当前运行的调试会话窗里设置断点:
_l_n36_buf = E07100
_l_sg = DBFD3D
_lc_checkout = DBC99D
我们做了一个假的许可文件,如下(注意:你应该能确认vendor deamon的名字):
FEATURE somefeature dsndata 1.0 permanent uncounted 0 \
HOSTID=ANY
从_l_n36_buf()里的下断来看,我们发现,我们的2个坏标记分别在地址E07112与E0D4BD。从_lc_checkout()里的下断看,我得到了Feature name:sds2以及版本号:6.318, 好了,将lincense相应的修订一下。
FEATURE sds2 dsndata 6.318 permanent uncounted 0 \
HOSTID=ANY
* Tip *
大多FLEXlm程序的vendor deamon没有用_l_n36_buf()配置标记,这使得他们很容易用:lmgrd ?z deamon_name ?c license_file来回收SEED,特别是你不在常规的目标程序的_l_sg下断或让_l_checkout()返回 EAX=-15处下断的时候。
现在,你可以在_l_sg()断下,调用_l_n36_buff()解码子程序,这样你就可以回收到SEED(0x987AC78F和0xC8D6382B)。用lmv8gen你就能生成vendor key,编译lmcrypt.exe,这样你就能作出有效的license。
* Tip *
通过下在_lc_checkout()的断点,你能回收到其他的被你目标应用程序校验的Feature name,然后再拿去作出他们的license。
对于SDS2,我用如前所述的方法让_lc_checkout()返回0,然而,程序意外地崩溃了。这很令人困惑,因为warez放出地这个版本只是补丁了sub_DBC940(就是大家所知地让_lc_checkout()为XOR EAX, EAX/RETN),对我们的license同样有效。同时,该license很明显地用到我上述回收到的关键信息制作出的(因为lmcrypt并不改变它)。SDS2很明显是个FLEXlm保护程序反常的例子。我试了三个其他的,没有重现这个情况。然而,这对我们了解对_lc_checkout()打补丁这个“粗暴的技术”是有用的。自从有了文件完整性检查,补丁也用来静态校验数据,这个是开发商防止文件被攥改的方法。
结论
我已经很长时间没有看FLEXlm了,Macrovision被赞为最终的保护技术安全的密匙生成器,然而,同时他们一贯的错误继续存留,旧格式的FLEXlm许可证依旧可以制作,以及使用补丁技术可以轻而易举地让许可层接受。即使我的方法被击破,依旧可以很容易的直接对_lc_checkout()API补丁。

什么是SIGN?
本教程描述了补丁FLEXlm许可授权层通过用license key来验证的原理(这是最古老也是最简单的FLEXlm验证方法,我选它纯粹是因为简单)。Globetrotter从第7.x版(只有12个字符)开始增加基本的SIGN性质并且只是增强了一下算法(可能是更耐攻击)以及更好的SEED隐藏技术,这怎么称呼呢,这些只能算是“提高”吧。
现在,许多的客户开始转向使用CRO或者TRO(防伪抗攥改之选),事实上是只是在老事物上装个新名词。这使得客户们可以制作新格式的ECC SING许可证,其字符长度可达58个。据我所知,现在还没有人成功攻击ECC FLEXlm完整的回收私匙(private key)或LM_SEED(但我并不排除具有足够强的计算处理能力能够回收这些值)。
对于新长度的SIGN,破解者会选择对_lm_pubkey_verify()打补丁然后用自己的LM_SEED制作SIGN=许可证。然而,可以用另外一种方法,就是强制许可授权层(licensing layer)进行旧格式的SIGN=12字符的校验方式。该法仅涉及简单地对我们发现的存在于_l_n36_buf()(如上)里面的第2个坏标记处补丁。我们可以象以前那样回收到加密过的SEED并用SDK或本站可下载的Lmgryptgui制作license。该补丁的工作原理是告诉许可授权层(licensing layer)别去取_lm_pubkey_verify()的地址,_lm_pubkey_verify()会在_l_sg()之后马上就行校验。
另一个值得注意的重要事情是,很容易确认你的目标程序是否允许使用旧格式的SING=校验,在_l_sg()后vendor code结构里的变形SEED处下断点,如果得到回收的seed则可以制作旧格式的标准SIGN,如果不能,你就要打补丁了。变形SEED断点补丁后可以获得成功,就如之前回收的一样。

重大修改:
1,2006年11月25日 下面段落有重要修改,修改后为:
5、_lc_checkout()有两个交叉引用,第二个引用是mov dword ptr [reg32+30h],offset_lc_checkout()格式(实际上这是InitLmInterface()的一部分)向上滚动,我们在[reg32+0Ch](或第一个入口处)标记该子程序为_lc_init(), 这得出_lc_new_job()调用_lc_init()为sub_DC8610,调用_l_n36_buf为dd 0E07100h。



[公告]安全测试和项目外包请将项目需求发到看雪企服平台:https://qifu.kanxue.com

最新回复 (9)
orchid88 2006-11-18 10:47
2
0
不错,辛苦了。有一些笔误请修正,比如文中多次把_l_n36_buff()误为_l_buf_36()。这篇文章我很早的时候就看过英文原文,里面有一点没有说清楚的就是_l_n36_buff()函数是怎么找到的,其实就是_l_sg()函数中非零不跳转处那个调用过后可以用calc seed工具找出seed的地方。请大家注意,_l_n36_buff()和_l_n36_buf()是不同的两个函数,一个是加密,另一个是解密函数。
binbinbin 28 2006-11-18 10:54
3
0
支持和学习。我个人感觉翻译这些文章专业性比较强,哪天我也能翻出一篇来呢
sln 2 2006-11-18 11:04
4
0
最初由 orchid88 发布
不错,辛苦了。有一些笔误请修正,比如文中多次把_l_n36_buff()误为_l_buf_36()。这篇文章我很早的时候就看过英文原文,里面有一点没有说清楚的就是_l_n36_buff()函数是怎么找到的,其实就是_l_sg()函数中非零不跳转处那个调用过后可以用calc seed工具找出seed的地方。请大家注意,_l_n36_buff()和_l_n36_buf()是不同的两个函数,一个是加密,另一个是解密函数。

感谢orchid88
比如文中多次把_l_n36_buff()误为_l_buf_36()。
=>在文中的标记1段出现两次,原文如此,或许原作者错了,我过几天会再这里修改。

2007年1月11日:
我已经将l_n36_buff与l_n36_buf两个函数的定位做了个解析,有兴趣的朋友请看:http://bbs.pediy.com/showthread.php?s=&postid=264716#post264716
laoqian 8 2006-11-18 11:05
5
0
我的总结类似 [不过较详细说明了ECC的处理(但不太完整)],如果消化了这个,基本可以解决flexlm了。

翻译的不错!

完成了我多年的心愿,未竟之业...嘿嘿
kanxue 8 2006-11-18 11:08
6
0
希望sln能加入翻译小组。;)
sln 2 2006-11-23 19:55
7
0
想加入啊 ,但是现在工作很忙。有空去认领一些翻译。
newsearch 9 2006-11-24 13:12
8
0
翻译辛苦,再次支持!
iask 2006-11-28 10:16
9
0
支持一下solo
honghan 2017-8-6 17:33
10
0
Mark
游客
登录 | 注册 方可回帖
返回