首页
社区
课程
招聘
[原创]软件保护壳专题 - 代码乱序引擎的构建
发表于: 2009-8-28 15:22 19106

[原创]软件保护壳专题 - 代码乱序引擎的构建

2009-8-28 15:22
19106

目录
0.<什么是乱序>
1.<乱序的步骤>
2.<代码流程图的构建>
3.<开始乱序>
4.<YY>

正文
0.什么是乱序
   乱序就是打乱原来的流程。
   很多朋友逆向程序时,会把一个程序直接丢到IDA里会出现一副流程图。我们现在要做的就是把这副
流程图打乱。但是并不影响原先流程的逻辑。乱序的原理其实比较简单。说白了就是做HOOK然后填充
花指令。最后在跳转会原先的地址。

1.乱序的步骤
   0.把正常代码流程的CALL/JMP/JCC的指令,全部都找出来。
   1.对上述进行的位置,进行筛选,如果是8位跳转直接过滤,如果是16位跳转计算偏移空间是否足够
     跳转到花指令的偏移。如果是32位的情况直接发通行证。
   2.添加一个新节或者利用其它感染方式增加一块可用的区域做存储花指令用。
   3.遍历0和1生存的结构。当遇到有偏移时。记录原先要跳转的地方。将原先的偏移替换成花指令空间
     的位置。最后在花指令末尾添加一个跳入到原先跳转地址的偏移(一般都是向上跳,新增的区域一般
     都处于最后的内存空间).
   4.验证花指令的剩余空间是否足够,以便判断是否停止HOOK。

当然你也可以加入一些随机判断,某条跳转是否进行乱序。有些则不用。

2.代码流程图的构建
   原理很简单,利用反汇编引擎,开始遍历要乱序的地址和长度。一步一步的找出所有合适的跳转类型指令
代码如下

// 这个是流程图的结构,因为用不到动态转载,所以只记录偏移性质的跳转
typedef struct _CODE_FLOW_NODE
{
	struct _CODE_FLOW_NODE *pNext;//下一个节点
	BOOL bGoDown;//是否向下跳
	DWORD dwBits;//跳转范围
	DWORD dwType;//指令类型
	BOOL bFar;//是否是远跳
	DWORD dwMemoryAddress;//当前内存地址
	LPBYTE pFileAddress;//当前文件地址
	DWORD dwGotoMemoryAddress;//跳转后的内存地址
	LPBYTE pGotoFileAddress;//跳转后的文件地址
	DWORD dwInsLen;//指令长度
	union
	{
		BYTE bOffset;
		WORD wOffset;
		DWORD dwOffset;
	};//偏移
} CODE_FLOW_NODE, *PCODE_FLOW_NODE;

// 下面的代码利用了udis86反汇编引擎。个人觉的不错。尤其是直接做反汇编器使用时
// CxPeDiy是定义的一个操作PE文件的集合类。做一些PE结构操作。自己动手吧。
// 代码不算复杂。如果你有个大屏幕的话看起来会简单些。如果像我一样在一台T61上
// 工作。那实在有些不便了

PCODE_FLOW DrawCodeFlow(CONST LPBYTE pMem, CONST LPBYTE pStart, DWORD dwSize)
{
	PCODE_FLOW pCodeFlow = new CODE_FLOW;
	if (pCodeFlow == NULL)
		return NULL;
	ZeroMemory(pCodeFlow, sizeof(CODE_FLOW));

	CxPeDiy PeDiy;
	// 获取一些基本信息
	DWORD dwImageBase = PeDiy.GetNtHeader(pMem)->OptionalHeader.ImageBase;

	// 初始化反汇编引擎
	ud_t ud_obj;
	ud_init(&ud_obj);
	ud_set_input_buffer(&ud_obj, pStart, dwSize);
	ud_set_mode(&ud_obj, 32);
	ud_set_syntax(&ud_obj, UD_SYN_INTEL);
	if (pCodeFlow->ErrDisassembleAddress.Init(0x1000) == FALSE)
		return NULL;

	PCODE_FLOW_NODE pCodeFlowHeader = NULL, *pCodeFlowNode = &pCodeFlowHeader;

	LPBYTE pCurr = pStart;
	while (ud_disassemble(&ud_obj) != 0)
	{
		// printf("\t%s\n", ud_insn_asm(&ud_obj));
		if (ud_obj.mnemonic == UD_Iinvalid)
		{
			// 反汇编出现错误
			pCodeFlow->ErrDisassembleAddress.PutIntoQueue((QUEUE_ELEMENT)pCurr);
		}
		else
		{
			// 判断是否是跳转地址
			switch (ud_obj.mnemonic)
			{
			case UD_Ijo:
			case UD_Ijno:
			case UD_Ijb:
			case UD_Ijae:
			case UD_Ijz:
			case UD_Ijnz:
			case UD_Ijbe:
			case UD_Ija:
			case UD_Ijs:
			case UD_Ijns:
			case UD_Ijp:
			case UD_Ijnp:
			case UD_Ijl:
			case UD_Ijge:
			case UD_Ijle:
			case UD_Ijg:
			case UD_Ijcxz:
			case UD_Ijecxz:
			case UD_Ijrcxz:
				{
					// 分配节点内存
					*pCodeFlowNode = new CODE_FLOW_NODE;
					(*pCodeFlowNode)->bFar = FALSE;
					DWORD dwRVA = PeDiy.Raw2Rva(pMem, (DWORD)(pCurr - pMem));
					(*pCodeFlowNode)->dwMemoryAddress = dwImageBase + dwRVA;
					(*pCodeFlowNode)->pFileAddress = pCurr;
					(*pCodeFlowNode)->dwType = JmpIns_Type_Jcc;
					(*pCodeFlowNode)->pNext = NULL;

					// 判断是否有前缀
					if (ud_obj.pfx_opr == 0x66)
					{
						// 16位
						(*pCodeFlowNode)->dwBits = Jmp_Bit_16;
						(*pCodeFlowNode)->dwInsLen = ud_obj.inp_ctr;
						WORD wOffset = ud_obj.operand[0].lval.uword;
						(*pCodeFlowNode)->wOffset = wOffset;
						if (wOffset >= 0x8000)
						{
							(*pCodeFlowNode)->bGoDown = FALSE;
							wOffset = ~wOffset;
							wOffset++;
							wOffset -= ud_obj.inp_ctr;
							(*pCodeFlowNode)->dwGotoMemoryAddress = (*pCodeFlowNode)->dwMemoryAddress - wOffset;
							DWORD dwRaw = PeDiy.Rva2Raw(pMem, (DWORD)((*pCodeFlowNode)->dwGotoMemoryAddress - dwImageBase));
							(*pCodeFlowNode)->pGotoFileAddress = pMem + dwRaw;
						}
						else
						{
							(*pCodeFlowNode)->bGoDown = TRUE;
							wOffset += ud_obj.inp_ctr;
							(*pCodeFlowNode)->dwGotoMemoryAddress = (*pCodeFlowNode)->dwMemoryAddress + wOffset;
							DWORD dwRaw = PeDiy.Rva2Raw(pMem, (DWORD)((*pCodeFlowNode)->dwGotoMemoryAddress - dwImageBase));
							(*pCodeFlowNode)->pGotoFileAddress = pMem + dwRaw;
						}/* end else */
					}
					else
					{
						if (ud_obj.inp_sess[0] == 0x0F)
						{
							// 32位
							(*pCodeFlowNode)->dwBits = Jmp_Bit_32;
							(*pCodeFlowNode)->dwInsLen = ud_obj.inp_ctr;
							DWORD dwOffset = ud_obj.operand[0].lval.udword;
							(*pCodeFlowNode)->dwOffset = dwOffset;
							if (dwOffset >= 0x80000000)
							{
								(*pCodeFlowNode)->bGoDown = FALSE;
								dwOffset = ~dwOffset;
								dwOffset++;
								dwOffset -= ud_obj.inp_ctr;
								(*pCodeFlowNode)->dwGotoMemoryAddress = (*pCodeFlowNode)->dwMemoryAddress - dwOffset;
								DWORD dwRaw = PeDiy.Rva2Raw(pMem, (DWORD)((*pCodeFlowNode)->dwGotoMemoryAddress - dwImageBase));
								(*pCodeFlowNode)->pGotoFileAddress = pMem + dwRaw;
							}
							else
							{
								(*pCodeFlowNode)->bGoDown = TRUE;
								dwOffset += ud_obj.inp_ctr;
								(*pCodeFlowNode)->dwGotoMemoryAddress = (*pCodeFlowNode)->dwMemoryAddress + dwOffset;
								DWORD dwRaw = PeDiy.Rva2Raw(pMem, (DWORD)((*pCodeFlowNode)->dwGotoMemoryAddress - dwImageBase));
								(*pCodeFlowNode)->pGotoFileAddress = pMem + dwRaw;
							}/* end else */
						}
						else
						{
							// 8位
							(*pCodeFlowNode)->dwBits = Jmp_Bit_8;
							(*pCodeFlowNode)->dwInsLen = ud_obj.inp_ctr;
							BYTE bOffset = ud_obj.operand[0].lval.ubyte;
							(*pCodeFlowNode)->bOffset = bOffset;
							if (bOffset >= 0x80)
							{
								(*pCodeFlowNode)->bGoDown = FALSE;
								bOffset = ~bOffset;
								bOffset++;
								bOffset -= ud_obj.inp_ctr;
								(*pCodeFlowNode)->dwGotoMemoryAddress = (*pCodeFlowNode)->dwMemoryAddress - bOffset;
								DWORD dwRaw = PeDiy.Rva2Raw(pMem, (DWORD)((*pCodeFlowNode)->dwGotoMemoryAddress - dwImageBase));
								(*pCodeFlowNode)->pGotoFileAddress = pMem + dwRaw;
							}
							else
							{
								(*pCodeFlowNode)->bGoDown = TRUE;
								bOffset += ud_obj.inp_ctr;
								(*pCodeFlowNode)->dwGotoMemoryAddress = (*pCodeFlowNode)->dwMemoryAddress + bOffset;
								DWORD dwRaw = PeDiy.Rva2Raw(pMem, (DWORD)((*pCodeFlowNode)->dwGotoMemoryAddress - dwImageBase));
								(*pCodeFlowNode)->pGotoFileAddress = pMem + dwRaw;
							}/* end else */
						}
					}/* end else */
					pCodeFlowNode = &((*pCodeFlowNode)->pNext);
				}break;
			case UD_Ijmp:
				{					
					if (ud_obj.inp_sess[0] == 0xEB)
					{
						// 8位
						// 分配节点内存
						*pCodeFlowNode = new CODE_FLOW_NODE;
						(*pCodeFlowNode)->bFar = FALSE;
						DWORD dwRVA = PeDiy.Raw2Rva(pMem, (DWORD)(pCurr - pMem));
						(*pCodeFlowNode)->dwMemoryAddress = dwImageBase + dwRVA;
						(*pCodeFlowNode)->pFileAddress = pCurr;
						(*pCodeFlowNode)->dwType = JmpIns_Type_Jmp;
						(*pCodeFlowNode)->dwBits = Jmp_Bit_8;
						(*pCodeFlowNode)->pNext = NULL;
						BYTE bOffset = ud_obj.operand[0].lval.ubyte;
						(*pCodeFlowNode)->bOffset = bOffset;
						(*pCodeFlowNode)->dwInsLen = ud_obj.inp_ctr;
						if (bOffset >= 0x80)
						{
							(*pCodeFlowNode)->bGoDown = FALSE;
							bOffset = ~bOffset;
							bOffset++;
							bOffset -= ud_obj.inp_ctr;
							(*pCodeFlowNode)->dwGotoMemoryAddress = (*pCodeFlowNode)->dwMemoryAddress - bOffset;
							DWORD dwRaw = PeDiy.Rva2Raw(pMem, (DWORD)((*pCodeFlowNode)->dwGotoMemoryAddress - dwImageBase));
							(*pCodeFlowNode)->pGotoFileAddress = pMem + dwRaw;
						}
						else
						{
							(*pCodeFlowNode)->bGoDown = TRUE;
							bOffset += ud_obj.inp_ctr;
							(*pCodeFlowNode)->dwGotoMemoryAddress = (*pCodeFlowNode)->dwMemoryAddress + bOffset;
							DWORD dwRaw = PeDiy.Rva2Raw(pMem, (DWORD)((*pCodeFlowNode)->dwGotoMemoryAddress - dwImageBase));
							(*pCodeFlowNode)->pGotoFileAddress = pMem + dwRaw;
						}/* end else */
						pCodeFlowNode = &((*pCodeFlowNode)->pNext);
					}
					else
					{
						if ((ud_obj.pfx_opr == 0x66) || (ud_obj.pfx_adr == 0x67))
						{
							if (ud_obj.inp_sess[1] == 0xE9)
							{
								// 16位
								// 分配节点内存
								*pCodeFlowNode = new CODE_FLOW_NODE;
								(*pCodeFlowNode)->bFar = FALSE;
								DWORD dwRVA = PeDiy.Raw2Rva(pMem, (DWORD)(pCurr - pMem));
								(*pCodeFlowNode)->dwMemoryAddress = dwImageBase + dwRVA;
								(*pCodeFlowNode)->pFileAddress = pCurr;
								(*pCodeFlowNode)->dwType = JmpIns_Type_Jmp;
								(*pCodeFlowNode)->dwBits = Jmp_Bit_16;
								(*pCodeFlowNode)->pNext = NULL;
								WORD wOffset = ud_obj.operand[0].lval.uword;
								(*pCodeFlowNode)->wOffset = wOffset;
								(*pCodeFlowNode)->dwInsLen = ud_obj.inp_ctr;
								if (wOffset >= 0x8000)
								{
									(*pCodeFlowNode)->bGoDown = FALSE;
									wOffset = ~wOffset;
									wOffset++;
									wOffset -= ud_obj.inp_ctr;
									(*pCodeFlowNode)->dwGotoMemoryAddress = (*pCodeFlowNode)->dwMemoryAddress - wOffset;
									DWORD dwRaw = PeDiy.Rva2Raw(pMem, (DWORD)((*pCodeFlowNode)->dwGotoMemoryAddress - dwImageBase));
									(*pCodeFlowNode)->pGotoFileAddress = pMem + dwRaw;
								}
								else
								{
									(*pCodeFlowNode)->bGoDown = TRUE;
									wOffset += ud_obj.inp_ctr;
									(*pCodeFlowNode)->dwGotoMemoryAddress = (*pCodeFlowNode)->dwMemoryAddress + wOffset;
									DWORD dwRaw = PeDiy.Rva2Raw(pMem, (DWORD)((*pCodeFlowNode)->dwGotoMemoryAddress - dwImageBase));
									(*pCodeFlowNode)->pGotoFileAddress = pMem + dwRaw;
								}/* end else */
								pCodeFlowNode = &((*pCodeFlowNode)->pNext);
							}/* end if */
						}
						else
						{
							if (ud_obj.inp_sess[0] == 0xE9)
							{
								// 32位
								// 分配节点内存
								*pCodeFlowNode = new CODE_FLOW_NODE;
								(*pCodeFlowNode)->bFar = FALSE;
								DWORD dwRVA = PeDiy.Raw2Rva(pMem, (DWORD)(pCurr - pMem));
								(*pCodeFlowNode)->dwMemoryAddress = dwImageBase + dwRVA;
								(*pCodeFlowNode)->pFileAddress = pCurr;
								(*pCodeFlowNode)->dwBits = Jmp_Bit_32;
								(*pCodeFlowNode)->dwType = JmpIns_Type_Jmp;
								(*pCodeFlowNode)->pNext = NULL;
								DWORD dwOffset = ud_obj.operand[0].lval.udword;
								(*pCodeFlowNode)->dwOffset = dwOffset;
								(*pCodeFlowNode)->dwInsLen = ud_obj.inp_ctr;
								if (dwOffset >= 0x80000000)
								{
									(*pCodeFlowNode)->bGoDown = FALSE;
									dwOffset = ~dwOffset;
									dwOffset++;
									dwOffset -= ud_obj.inp_ctr;
									(*pCodeFlowNode)->dwGotoMemoryAddress = (*pCodeFlowNode)->dwMemoryAddress - dwOffset;
									DWORD dwRaw = PeDiy.Rva2Raw(pMem, (DWORD)((*pCodeFlowNode)->dwGotoMemoryAddress - dwImageBase));
									(*pCodeFlowNode)->pGotoFileAddress = pMem + dwRaw;
								}
								else
								{
									(*pCodeFlowNode)->bGoDown = TRUE;
									dwOffset += ud_obj.inp_ctr;
									(*pCodeFlowNode)->dwGotoMemoryAddress = (*pCodeFlowNode)->dwMemoryAddress + dwOffset;
									DWORD dwRaw = PeDiy.Rva2Raw(pMem, (DWORD)((*pCodeFlowNode)->dwGotoMemoryAddress - dwImageBase));
									(*pCodeFlowNode)->pGotoFileAddress = pMem + dwRaw;
								}/* end else */
								pCodeFlowNode = &((*pCodeFlowNode)->pNext);
							}
						}/* end else */
					}/* end else */
				}break;
			case UD_Icall:
				{
					if ((ud_obj.pfx_opr == 0x66) || (ud_obj.pfx_adr == 0x67))
					{
						if (ud_obj.inp_sess[1] == 0xE8)
						{
							// 16位
							// 分配节点内存
							*pCodeFlowNode = new CODE_FLOW_NODE;
							(*pCodeFlowNode)->bFar = FALSE;
							DWORD dwRVA = PeDiy.Raw2Rva(pMem, (DWORD)(pCurr - pMem));
							(*pCodeFlowNode)->dwMemoryAddress = dwImageBase + dwRVA;
							(*pCodeFlowNode)->pFileAddress = pCurr;
							(*pCodeFlowNode)->pNext = NULL;
							(*pCodeFlowNode)->dwType = JmpIns_Type_Call;
							(*pCodeFlowNode)->dwBits = Jmp_Bit_16;
							WORD wOffset = ud_obj.operand[0].lval.uword;
							(*pCodeFlowNode)->wOffset = wOffset;
							(*pCodeFlowNode)->dwInsLen = ud_obj.inp_ctr;
							if (wOffset >= 0x8000)
							{
								(*pCodeFlowNode)->bGoDown = FALSE;
								wOffset = ~wOffset;
								wOffset++;
								wOffset -= ud_obj.inp_ctr;
								(*pCodeFlowNode)->dwGotoMemoryAddress = (*pCodeFlowNode)->dwMemoryAddress - wOffset;
								DWORD dwRaw = PeDiy.Rva2Raw(pMem, (DWORD)((*pCodeFlowNode)->dwGotoMemoryAddress - dwImageBase));
								(*pCodeFlowNode)->pGotoFileAddress = pMem + dwRaw;
							}
							else
							{
								(*pCodeFlowNode)->bGoDown = TRUE;
								wOffset += ud_obj.inp_ctr;
								(*pCodeFlowNode)->dwGotoMemoryAddress = (*pCodeFlowNode)->dwMemoryAddress + wOffset;
								DWORD dwRaw = PeDiy.Rva2Raw(pMem, (DWORD)((*pCodeFlowNode)->dwGotoMemoryAddress - dwImageBase));
								(*pCodeFlowNode)->pGotoFileAddress = pMem + dwRaw;
							}/* end else */
							pCodeFlowNode = &((*pCodeFlowNode)->pNext);
						}/* end if */
					}
					else
					{
						if (ud_obj.inp_sess[0] == 0xE8)
						{
							// 32位
							// 分配节点内存
							*pCodeFlowNode = new CODE_FLOW_NODE;
							(*pCodeFlowNode)->bFar = FALSE;
							DWORD dwRVA = PeDiy.Raw2Rva(pMem, (DWORD)(pCurr - pMem));
							(*pCodeFlowNode)->dwMemoryAddress = dwImageBase + dwRVA;
							(*pCodeFlowNode)->pFileAddress = pCurr;
							(*pCodeFlowNode)->dwType = JmpIns_Type_Call;
							(*pCodeFlowNode)->dwBits = Jmp_Bit_32;
							(*pCodeFlowNode)->pNext = NULL;
							DWORD dwOffset = ud_obj.operand[0].lval.udword;
							(*pCodeFlowNode)->dwOffset = dwOffset;
							(*pCodeFlowNode)->dwInsLen = ud_obj.inp_ctr;
							if (dwOffset >= 0x80000000)
							{
								(*pCodeFlowNode)->bGoDown = FALSE;
								dwOffset = ~dwOffset;
								dwOffset++;
								dwOffset -= ud_obj.inp_ctr;
								(*pCodeFlowNode)->dwGotoMemoryAddress = (*pCodeFlowNode)->dwMemoryAddress - dwOffset;
								DWORD dwRaw = PeDiy.Rva2Raw(pMem, (DWORD)((*pCodeFlowNode)->dwGotoMemoryAddress - dwImageBase));
								(*pCodeFlowNode)->pGotoFileAddress = pMem + dwRaw;
							}
							else
							{
								(*pCodeFlowNode)->bGoDown = TRUE;
								dwOffset += ud_obj.inp_ctr;
								(*pCodeFlowNode)->dwGotoMemoryAddress = (*pCodeFlowNode)->dwMemoryAddress + dwOffset;
								DWORD dwRaw = PeDiy.Rva2Raw(pMem, (DWORD)((*pCodeFlowNode)->dwGotoMemoryAddress - dwImageBase));
								(*pCodeFlowNode)->pGotoFileAddress = pMem + dwRaw;
							}/* end else */
							pCodeFlowNode = &((*pCodeFlowNode)->pNext);
						}/* end if */						
					}/* end else */
				}break;
			}/* end switch */
		}/* end else */
		pCurr += ud_obj.inp_ctr;
	}/* end if */
	pCodeFlow->pCodeFlow = pCodeFlowHeader;
	return pCodeFlow;
}


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 7
支持
分享
最新回复 (16)
雪    币: 179
活跃值: (26)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
2
坐板凳学习
2009-8-28 16:11
0
雪    币: 5327
活跃值: (3719)
能力值: ( LV13,RANK:283 )
在线值:
发帖
回帖
粉丝
3
学习,搂主加油!!
2009-8-28 16:17
0
雪    币: 709
活跃值: (2420)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
4
现在终于是C代码了.~~
2009-8-28 16:28
0
雪    币: 433
活跃值: (1870)
能力值: ( LV17,RANK:1820 )
在线值:
发帖
回帖
粉丝
5
过来膜拜一下楼主
2009-8-28 17:05
0
雪    币: 253
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
感谢Lz的教程
必需膜拜下
2009-8-28 17:44
0
雪    币: 197
活跃值: (52)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
7
我是过来偷代码的
2009-8-29 19:01
0
雪    币: 27
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
SIP
8
玩命的,果然强大!
2009-8-30 16:17
0
雪    币: 351
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
高手一出手,就是不得了呀。。
2009-8-31 16:07
0
雪    币: 211
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
太邪恶了,这样反汇编会累死的
2009-8-31 16:19
0
雪    币: 116
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
强大,收藏加学习,并且研究研究……………………
2009-8-31 22:40
0
雪    币: 243
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
12
还是先收藏下 谢啦
2009-9-1 17:23
0
雪    币: 202
活跃值: (13)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
13
大罗金仙!!! 膜拜中
2009-9-2 05:19
0
雪    币: 124
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
14
搞死IDA,累死creaker
2009-12-10 19:18
0
雪    币: 270
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
15
没看懂, 先顶帖 
2010-2-22 17:30
0
雪    币: 270
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
16
看这个帖前是不是要先看看反汇编引擎的实现 
2010-2-22 17:33
0
雪    币: 656
活跃值: (448)
能力值: ( LV12,RANK:360 )
在线值:
发帖
回帖
粉丝
17
怎么不来个ASM版..
2010-8-17 02:30
0
游客
登录 | 注册 方可回帖
返回
//