首页
社区
课程
招聘
[原创]API监控+代码乱序的壳
发表于: 2019-3-30 00:30 21643

[原创]API监控+代码乱序的壳

2019-3-30 00:30
21643
在论坛一直混迹也不短时间了,看了很多人的技术文章,总想着自己有一天也能写些东西。遂把这几个月做的一款壳分享一下,若能指点一二,小生感激不尽。因为本人写壳经验不长,其中参考了论坛上不少人的文章,前期感觉发出来有点见不得人哈,后来想想总有人需要的,这里算是做一个整合和优化,并且提出了一些新的保护思路。

1.本系统提供了加壳 和 API 监控
2.加壳模块的基本功能包括压缩引擎(aplib、JCALG1)、IAT转储、HOOT-IAT、重定位转储
3.加壳模块的附加功能包括反调试、反dump、反OD、混淆函数和校验(内存校验和文件校验)
4.API监控模块可以记录选中的DLL的API调用信息(调用地址、调用模块、API名称和调用次数)和API 调用参数信息
5.新的思路:利用API的调用频率作为触发概率的参考条件,代码混淆模块对频繁调用的API有更大的概率实现代码混淆,反之亦然
6.利用已有的花指令模板来实现代码混淆
7.提供了针对普通用户的三套方案(性能、安全和均衡)
8.可以对EXE和DLL进行加壳
对部分程序进行测试,加壳的成功率和兼容性表现良好。结合了API监控的代码混淆属于测试功能,只能对特定程序进行测试,当前的意义仅仅为提供了一个新的保护思路。下面给出系统的界面效果。



 


接下来就该系统各个部分的功能做一个粗略但求精简的讲解。第一部分讨论加壳中各模块细节,第二部分讲述 API 监控 以及 第三部分为代码混淆。
该小节主要讲述IAT转储、重定位表转储、HOOK-IAT 和 压缩引擎以及压缩过程。

IAT转储是将导入表实现一个结构的简化并存储到指定被保护的区域。此处直接使用了《加密与解密》第三版提供的转储结构。结构如下图。


转储代码的细节提供在项目文件中,有需要的读者可自行阅览。

重定位表亦使用但是改进了第三版中提供的结构。因为在原版的结构中,只用了1个byte去存储偏移量,但是在实际当中,某些重定位项之间的偏移会大于0xff。由于内存对齐长度为0x1000,而每一重定位项之间的偏移小于0xfff,所以在一份内存页当中2个byte适合用于转储。值得注意的,在测试某些样本中,两个区段的重定位项的偏移可能会大于0xfff,所以每一个内存页都要保存相同的转储结构。

最后,保存的形式如下图。这样就可以清晰地区分每一个需要重定位的内存页了。除此之外,在进行修复每一页时,都要使用VirtualProtect修改内存中该页的属性,使其变为可写,否则会触发中断崩溃。

4.1.3 压缩引擎

压缩后的样本区段结构和upx是一样的。本系统使用了比较常见的压缩库,包括aplib和JCALG1,前者效率均衡,对PE文件的处理较好,后者对含有较多资源的程序表现更佳。下面给出两个压缩库的使用过程。值得一提,若是遇到TLS表的话,理论上可以压缩,但是只能在TLS表之后压缩,这种压缩策略不仅使操作复杂化,而且效率也不高,故而本系统对含有TLS的程序不进行压缩处理。(注:发现JCALG1不能压缩过大的文件,问题还未解决)


下面给出压缩与解压后的区段结构。pack0是占位区段,用于存放解压后的数据;pack1保存着压缩区段后的数据;pack2保存着外壳代码。rsrc保存着转储后的小部分关键资源(例如Icons、Dialogs和Group Icons)。

4.1.4 其他相关保护技术

1.文件校验
对程序文件的数据进行CRC32的计算,计算的值保存到PE标识的前4个byte中。
VOID	COperationPE::CalAndSaveCRC(DWORD dwFileSize)
{
	DWORD	dwCrc32;	//计算的值

	//1. 生成CRC32表格
	if(m_bCRC32Table == FALSE)
		MakeCRC32Table();

	//2. 计算PE头之后的数据
	dwCrc32 = CalcuCRC((UCHAR*)(m_pDosHeader->e_lfanew + m_dwFileDataAddr), dwFileSize - m_pDosHeader->e_lfanew);

	//3. 将该CRC32值写进PE头标识前4个字节
	*(PDWORD)((DWORD)m_pNtHeader - 4) = dwCrc32;

}

2.内存校验 
对未压缩的和加密的代码段进行CRC32的计算,如果存在重定位表,则不进行计算,因为重定位修复时会修正全局变量的绝对地址,导致前后计算的数值不一致,从而校验失败。
BOOL	CodeMemVerification()
{
	DWORD	dwCodeBase;
	DWORD	dwCodeSize;
	DWORD	dwCRC32;
	

	dwCodeBase = g_stcShellData.dwCodeBase;
	dwCodeSize = g_stcShellData.dwCodeSize;

	dwCRC32 = CalcuCRC( (UCHAR*) (g_dwImageBase + dwCodeBase), dwCodeSize);

	//如果有重定位修复,则不进行验证,因为修复全局变量会改变代码段,所以CRC32的计算会出错
	if(g_stcShellData.stcPERelocDir.VirtualAddress == 0)
//	if(g_stcShellData.stcIATDir.VirtualAddress == 0)
	if (dwCRC32 != g_stcShellData.dwCodeMemCRC32)	return FALSE;

	return TRUE;

}

3.反Dump
这是一个非常常见的保护手段,通过修改PEB结构来使某些工具无法获取到加壳程序的进程信息。特别是使用了某些直接获取PEB结构信息的Windows API的工具。借鉴网上通用的某些代码,但是发现PEB中有些信息的删除会导致程序异常,故而将其精简后得到最终代码,效果如下图。
可见原来进程的信息被替换了ntdll.dll,而且进程模块的基本信息(基址和大小)都被抹去。

4.混淆函数
相当于一个花指令黑盒,把运行地址作为参数传入该函数,最后在花指令结束后便跳转回目标代码处执行。

5.反OD
原理很简单,由于用OD载入程序时,内存中会产生某些特征字符串,故而可以通过搜索这些信息来进行判读自身是否被OD载入调试。
由于在x86程序的虚拟内存中,有2G只分配给用户空间的,所以只需要对该区域进行扫描,经过测试,算法的时间复杂度对壳的性能影响相当大,所以选择优秀的算法非常重要。在本系统中,选择KMP做为字符串匹配算法。

6.反调试
简单的方法有很多,可以通过调用Windows API来查看,也可以自己去PEB结构里面查,本系统采用后者,这种保护手段早已经过时,在这里只是做为一个壳保护功能的完善罢了。

4.2 API监控

这里直接参考SoftSnoop的代码,并且对其的通信模式与hook方法进行修改。本系统直接使用detour库进行inline hook,这个库的健壮性不是盖的,毕竟是微软自己人做的东西。其次,除了商用软件,发现目前开源的抑或网友制作的工具,稳定性都不太好,故而就萌生一份将其优化与增加适用性的想法。最后,这个模块主要是为代码乱序提供一个API调用频率的信息。

首先给出该模块与SoftSnoop区别的细节:
1)通信模式改变,由于本系统是用Qt去事先界面,所以监控dll与UI之间的通信不能在使用原来的消息机制,而是改成了命名管道。
2)hook方法更变,原来是对某个模块所有的api进行hook,后来测试发现,有些api不可以hook,原因是Windows中有些涉及到窗口的API是回调函数,如果对这些API进行hook,那也会导致一个不可想象的调用膨胀,以至于崩溃。所以,本系统改变策略,只对提前约定好的API进行hook并解析,这样既可以获取关键的信息,也可以提高程序性能。

4.2.1 流程与原理 
下面给出遍历进程模块的一个流程。

接下来给出命名管道的通信模式。指令的功能包括,遍历某个模块的api名称,其次还可以对某个规定好的api进行hook。

4.2.2 监控日志

接下来就是api监控结束后,除了会生成一份简陋的txt格式的日志文件,如下图。



还会生成一个给代码乱序引擎使用的一个日志。日志包含约定记录的api与其调用次数,而它的存储格式如下。

用二进制工具查看。


4.2.3 添加规则

添加的规则方式如 SoftSnoop提供的规则文件是一样的。当初因为作这款作品是有一些目的,所以在这一块就没有比较完善,其实我当初的设想是把规则做成一个可视化,更方便使用。这里给出一个kernel32模块约定监控的api的例子。



4.3 代码乱序引擎

这里首先要感谢玩命提供的代码乱序分析框架,这个模块的本意是想实现一个代码虚拟化,但是由于能力不足,时间不够,百忙之中没有办法实现,故而先通过开发一个代码乱序来测试结合api监控的效果。

4.3.1 总体设计

1.反汇编引擎 - udis86
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;  //指令长度
	pImport_Node			        pImpNode;  //在IAT中的节点信息
	DWORD					dwFunIndex;  //节点函数表的索引
	DWORD					dwFinalMemoryAddress;  //花指令的内存地址
	DWORD					dwFinalFileAddress;  //花指令的文件地址
	BOOL					bConfused;  //是否乱序
	union
	{
		BYTE bOffset;
		WORD wOffset;
		DWORD dwOffset;
	};//偏移
}Code_Flow_Node, *pCode_Flow_Node;

3.花指令模板
这里仅仅提供了2套花指令模板作为测试,当初想寻找一个自动花指令的生成代码,但是测试之后发现效果都不好,遂放弃。乱序后的指令会跳转到一个充满花指令的区域执行。

4.导入表-函数检测
众所周知,静态链接直接将功能代码放入代码段,则调用与跳转一般为短跳转,就会与普通的跳转调用指令产生混淆,无法分辨出这是否在调用一个函数。故而这里设计的理念就是找出调用动态链接库的函数的跳转,然后将其记录下来。

涉及的原理:
  • Windows PE导入表的结构
  • 编译器如何实现对动态链接库函数的调用
  • 反汇编引擎及IA-32架构的OPCODE
由下图可知,若某主调函数想调用动态链接库的函数,则一般在实际流程中先Call进入到指定的远跳转区域。再由远跳转FF25(JMP)到DLL的代码中,或直接FF15(CALL)。而FF15(JMP)指令会读取IAT中的数据作为绝对地址去跳转。

可知,反汇编引擎的工作就是找到FF15(JMP)。所以,整个分析由几步构成。
第一,解析到Call,记录下来;
第二,检测Call到的下一个地址是否是远跳转指令FF15;
第三,若是的话,提取出该偏移量,匹配IAT,获得被调用的函数名与相应的动态库名。

5.调用样本数据  与 随机概率 - Api调用次数
由于我们并不希望每个跳转都被乱序化,这样不仅保护效果没有针对性,还消耗了大量空间去存放花指令。所以根据这个想法,调用次数占总次数的比例越大,则被乱序化的可能性就越高。例如下表。

 API  Count
 CreateFile  x1
 VirtualAlloc  x2
 .......  xn

计算出每个API的调用概率:


随机概率处理的情况:
  • 无函数识别及调用样本数据 -->       1/4
  • 该函数未产生调用信息 -->    1/3
  • 该函数产生调用信息 -->  1/3 + Pxi
6.区段信息及结构

7.花指令平均长度估计区段大小
由于存放花指令的区段是需要事先计算的,否则就导致空间不够使用的情况。




值得注意,有些程序的调用次数相当巨大,会产生大量的结点信息,故而,这里对100个以上的结点数,直接视为100个。这100平均长度的空间用完则不再处理后序的指令了。

4.3.2 测试效果

这里用汇编写了一个switch - case ,循环次数随机地,在每个case中调用一个Windows API的程序。下图是switch - case 的结构。



下图是代码乱序前的部分情况。


下图是代码乱序后的情况。



5.参考资料

[1] SoftSnoop 1.3.2 + Source(增加了中文版和说明文档) -  https://bbs.pediy.com/thread-55974.htm
重定位表亦使用但是改进了第三版中提供的结构。因为在原版的结构中,只用了1个byte去存储偏移量,但是在实际当中,某些重定位项之间的偏移会大于0xff。由于内存对齐长度为0x1000,而每一重定位项之间的偏移小于0xfff,所以在一份内存页当中2个byte适合用于转储。值得注意的,在测试某些样本中,两个区段的重定位项的偏移可能会大于0xfff,所以每一个内存页都要保存相同的转储结构。
重定位表亦使用但是改进了第三版中提供的结构。因为在原版的结构中,只用了1个byte去存储偏移量,但是在实际当中,某些重定位项之间的偏移会大于0xff。由于内存对齐长度为0x1000,而每一重定位项之间的偏移小于0xfff,所以在一份内存页当中2个byte适合用于转储。值得注意的,在测试某些样本中,两个区段的重定位项的偏移可能会大于0xfff,所以每一个内存页都要保存相同的转储结构。

最后,保存的形式如下图。这样就可以清晰地区分每一个需要重定位的内存页了。除此之外,在进行修复每一页时,都要使用VirtualProtect修改内存中该页的属性,使其变为可写,否则会触发中断崩溃。

压缩后的样本区段结构和upx是一样的。本系统使用了比较常见的压缩库,包括aplib和JCALG1,前者效率均衡,对PE文件的处理较好,后者对含有较多资源的程序表现更佳。下面给出两个压缩库的使用过程。值得一提,若是遇到TLS表的话,理论上可以压缩,但是只能在TLS表之后压缩,这种压缩策略不仅使操作复杂化,而且效率也不高,故而本系统对含有TLS的程序不进行压缩处理。(注:发现JCALG1不能压缩过大的文件,问题还未解决)


下面给出压缩与解压后的区段结构。pack0是占位区段,用于存放解压后的数据;pack1保存着压缩区段后的数据;pack2保存着外壳代码。rsrc保存着转储后的小部分关键资源(例如Icons、Dialogs和Group Icons)。

1.文件校验
对程序文件的数据进行CRC32的计算,计算的值保存到PE标识的前4个byte中。
VOID	COperationPE::CalAndSaveCRC(DWORD dwFileSize)
{
	DWORD	dwCrc32;	//计算的值

	//1. 生成CRC32表格
	if(m_bCRC32Table == FALSE)
		MakeCRC32Table();

	//2. 计算PE头之后的数据
	dwCrc32 = CalcuCRC((UCHAR*)(m_pDosHeader->e_lfanew + m_dwFileDataAddr), dwFileSize - m_pDosHeader->e_lfanew);

	//3. 将该CRC32值写进PE头标识前4个字节
	*(PDWORD)((DWORD)m_pNtHeader - 4) = dwCrc32;

}

2.内存校验 
对未压缩的和加密的代码段进行CRC32的计算,如果存在重定位表,则不进行计算,因为重定位修复时会修正全局变量的绝对地址,导致前后计算的数值不一致,从而校验失败。
BOOL	CodeMemVerification()
{
	DWORD	dwCodeBase;
	DWORD	dwCodeSize;
	DWORD	dwCRC32;
	

	dwCodeBase = g_stcShellData.dwCodeBase;
	dwCodeSize = g_stcShellData.dwCodeSize;

	dwCRC32 = CalcuCRC( (UCHAR*) (g_dwImageBase + dwCodeBase), dwCodeSize);

	//如果有重定位修复,则不进行验证,因为修复全局变量会改变代码段,所以CRC32的计算会出错
	if(g_stcShellData.stcPERelocDir.VirtualAddress == 0)
//	if(g_stcShellData.stcIATDir.VirtualAddress == 0)
	if (dwCRC32 != g_stcShellData.dwCodeMemCRC32)	return FALSE;

	return TRUE;

}

3.反Dump
对程序文件的数据进行CRC32的计算,计算的值保存到PE标识的前4个byte中。
VOID	COperationPE::CalAndSaveCRC(DWORD dwFileSize)
{
	DWORD	dwCrc32;	//计算的值

	//1. 生成CRC32表格
	if(m_bCRC32Table == FALSE)
		MakeCRC32Table();

	//2. 计算PE头之后的数据
	dwCrc32 = CalcuCRC((UCHAR*)(m_pDosHeader->e_lfanew + m_dwFileDataAddr), dwFileSize - m_pDosHeader->e_lfanew);

	//3. 将该CRC32值写进PE头标识前4个字节
	*(PDWORD)((DWORD)m_pNtHeader - 4) = dwCrc32;

}
VOID	COperationPE::CalAndSaveCRC(DWORD dwFileSize)
{
	DWORD	dwCrc32;	//计算的值

	//1. 生成CRC32表格
	if(m_bCRC32Table == FALSE)
		MakeCRC32Table();

	//2. 计算PE头之后的数据
	dwCrc32 = CalcuCRC((UCHAR*)(m_pDosHeader->e_lfanew + m_dwFileDataAddr), dwFileSize - m_pDosHeader->e_lfanew);

	//3. 将该CRC32值写进PE头标识前4个字节
	*(PDWORD)((DWORD)m_pNtHeader - 4) = dwCrc32;

}

2.内存校验 
对未压缩的和加密的代码段进行CRC32的计算,如果存在重定位表,则不进行计算,因为重定位修复时会修正全局变量的绝对地址,导致前后计算的数值不一致,从而校验失败。
BOOL	CodeMemVerification()
{
	DWORD	dwCodeBase;
	DWORD	dwCodeSize;
	DWORD	dwCRC32;
	

	dwCodeBase = g_stcShellData.dwCodeBase;
	dwCodeSize = g_stcShellData.dwCodeSize;

	dwCRC32 = CalcuCRC( (UCHAR*) (g_dwImageBase + dwCodeBase), dwCodeSize);

	//如果有重定位修复,则不进行验证,因为修复全局变量会改变代码段,所以CRC32的计算会出错
	if(g_stcShellData.stcPERelocDir.VirtualAddress == 0)
//	if(g_stcShellData.stcIATDir.VirtualAddress == 0)
	if (dwCRC32 != g_stcShellData.dwCodeMemCRC32)	return FALSE;

	return TRUE;

}
BOOL	CodeMemVerification()
{
	DWORD	dwCodeBase;
	DWORD	dwCodeSize;
	DWORD	dwCRC32;
	

	dwCodeBase = g_stcShellData.dwCodeBase;
	dwCodeSize = g_stcShellData.dwCodeSize;

	dwCRC32 = CalcuCRC( (UCHAR*) (g_dwImageBase + dwCodeBase), dwCodeSize);

	//如果有重定位修复,则不进行验证,因为修复全局变量会改变代码段,所以CRC32的计算会出错
	if(g_stcShellData.stcPERelocDir.VirtualAddress == 0)
//	if(g_stcShellData.stcIATDir.VirtualAddress == 0)
	if (dwCRC32 != g_stcShellData.dwCodeMemCRC32)	return FALSE;

	return TRUE;

}

这是一个非常常见的保护手段,通过修改PEB结构来使某些工具无法获取到加壳程序的进程信息。特别是使用了某些直接获取PEB结构信息的Windows API的工具。借鉴网上通用的某些代码,但是发现PEB中有些信息的删除会导致程序异常,故而将其精简后得到最终代码,效果如下图。
可见原来进程的信息被替换了ntdll.dll,而且进程模块的基本信息(基址和大小)都被抹去。

4.混淆函数
可见原来进程的信息被替换了ntdll.dll,而且进程模块的基本信息(基址和大小)都被抹去。

相当于一个花指令黑盒,把运行地址作为参数传入该函数,最后在花指令结束后便跳转回目标代码处执行。

5.反OD
原理很简单,由于用OD载入程序时,内存中会产生某些特征字符串,故而可以通过搜索这些信息来进行判读自身是否被OD载入调试。
由于在x86程序的虚拟内存中,有2G只分配给用户空间的,所以只需要对该区域进行扫描,经过测试,算法的时间复杂度对壳的性能影响相当大,所以选择优秀的算法非常重要。在本系统中,选择KMP做为字符串匹配算法。

6.反调试
简单的方法有很多,可以通过调用Windows API来查看,也可以自己去PEB结构里面查,本系统采用后者,这种保护手段早已经过时,在这里只是做为一个壳保护功能的完善罢了。

这里直接参考SoftSnoop的代码,并且对其的通信模式与hook方法进行修改。本系统直接使用detour库进行inline hook,这个库的健壮性不是盖的,毕竟是微软自己人做的东西。其次,除了商用软件,发现目前开源的抑或网友制作的工具,稳定性都不太好,故而就萌生一份将其优化与增加适用性的想法。最后,这个模块主要是为代码乱序提供一个API调用频率的信息。

首先给出该模块与SoftSnoop区别的细节:
1)通信模式改变,由于本系统是用Qt去事先界面,所以监控dll与UI之间的通信不能在使用原来的消息机制,而是改成了命名管道。
2)hook方法更变,原来是对某个模块所有的api进行hook,后来测试发现,有些api不可以hook,原因是Windows中有些涉及到窗口的API是回调函数,如果对这些API进行hook,那也会导致一个不可想象的调用膨胀,以至于崩溃。所以,本系统改变策略,只对提前约定好的API进行hook并解析,这样既可以获取关键的信息,也可以提高程序性能。

4.2.1 流程与原理 
下面给出遍历进程模块的一个流程。

接下来给出命名管道的通信模式。指令的功能包括,遍历某个模块的api名称,其次还可以对某个规定好的api进行hook。

接下来就是api监控结束后,除了会生成一份简陋的txt格式的日志文件,如下图。



还会生成一个给代码乱序引擎使用的一个日志。日志包含约定记录的api与其调用次数,而它的存储格式如下。

用二进制工具查看。


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

最后于 2019-3-31 21:10 被KitTraumen编辑 ,原因:
上传的附件:
收藏
免费 23
支持
分享
打赏 + 3.00雪花
打赏次数 3 雪花 + 3.00
 
赞赏  minczsys   +1.00 2019/05/05 感谢分享~
赞赏  毕达哥拉斯   +1.00 2019/04/17
赞赏  余生挚爱传奇   +1.00 2019/04/16 感谢分享~
最新回复 (45)
雪    币: 744
活跃值: (1404)
能力值: ( LV9,RANK:150 )
在线值:
发帖
回帖
粉丝
2
感谢大佬分享
2019-3-30 00:39
0
雪    币: 283
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
感谢分享~
2019-3-30 00:52
0
雪    币: 116
活跃值: (316)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
感谢分享
2019-3-30 03:49
0
雪    币: 3277
活跃值: (1992)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
很高级不懂,只能膜拜。
最后于 2019-3-30 04:49 被chixiaojie编辑 ,原因:
2019-3-30 04:48
0
雪    币: 2166
活跃值: (3226)
能力值: (RANK:260 )
在线值:
发帖
回帖
粉丝
6
这篇绝对值得一个精华
2019-3-31 11:16
1
雪    币: 3712
活跃值: (1401)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
7
有理有据,思路逻辑清晰,值得收藏
2019-3-31 13:01
0
雪    币: 783
活跃值: (1121)
能力值: ( LV5,RANK:78 )
在线值:
发帖
回帖
粉丝
8
牛逼了....
2019-3-31 13:04
0
雪    币: 1914
活跃值: (72)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
借LZ源码来学习下 
2019-3-31 13:33
0
雪    币: 165
活跃值: (1481)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
good job!
2019-3-31 14:40
0
雪    币: 73
活跃值: (923)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
mark
2019-3-31 15:25
0
雪    币: 1535
活跃值: (695)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
厉害!mark
2019-3-31 18:33
0
雪    币: 1511
活跃值: (398)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
13
谢谢大家的认可
2019-3-31 21:03
0
雪    币: 1587
活跃值: (60)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
保镖NB!过来顶帖学习一下!
2019-3-31 23:44
0
雪    币: 2308
活跃值: (2200)
能力值: (RANK:400 )
在线值:
发帖
回帖
粉丝
15
牛逼,学习了。
2019-4-1 10:51
0
雪    币: 407
活跃值: (566)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
大神出手就是精华
2019-4-1 13:55
0
雪    币: 1981
活跃值: (771)
能力值: ( LV13,RANK:420 )
在线值:
发帖
回帖
粉丝
17
牛逼,学习了。
2019-4-1 16:09
0
雪    币: 144
活跃值: (335)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
真的牛逼 谢谢
2019-4-1 18:36
0
雪    币: 300
活跃值: (2472)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
感谢分享,niu
2019-4-1 22:29
0
雪    币: 4184
活跃值: (837)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
想法不错,不适用啊,现在市面上的壳级别都是VM级别的了,不带VM的壳现在看来意义不大,单纯的练手项目。
2019-4-2 09:20
0
雪    币: 1176
活跃值: (1264)
能力值: ( LV12,RANK:380 )
在线值:
发帖
回帖
粉丝
21
修改llvm把 一键启动跨平台
2019-4-2 12:30
0
雪    币: 1149
活跃值: (888)
能力值: ( LV13,RANK:260 )
在线值:
发帖
回帖
粉丝
22
牛逼 ,看不懂了啊
2019-4-2 13:56
0
雪    币: 9142
活跃值: (1630)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
23
厉害了,大神
2019-4-2 15:57
0
雪    币: 26245
活跃值: (63297)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
24
感谢分享~
2019-4-3 09:36
0
雪    币: 9
活跃值: (180)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
25
膜拜了
2019-4-8 18:42
0
游客
登录 | 注册 方可回帖
返回
//