动态字体,随风漂移

  • 一、前言
  • 二、分析过程
  • 三、代码实现
    • 3.1、方法一:字体映射关系识别数字
    • 3.2、方法二:ocr识别数字
  • 四、参考文献

一、前言

上一道字体反扒的css加密题还是第四题,不过那道题主要还只是做了个数字的坐标偏移,逻辑分析起来不算难,这道动态字体的题算是真正的字体反扒题,做了一下感觉挺长见识的,对于了解同类型字体加密有不小帮助,闲言少叙,开搞!

二、分析过程

题目的要求是采集胜点列的数据,找出胜点最高的召唤师,那加密的数据肯定就是胜点数据了,核心应该是解析出胜点值,其次就是将这个值与对应的召唤师匹配,最后比较五十个玩家的胜点,找出胜点最高那一个。

这里仔细观察一下,可以发现胜点数字的字形都不是很规则,看着就像是图形渲染的,复制粘贴一下就会发现,这个值果然是图片符号。

打开开发者模式看一下请求的结果,不难发现这里的data应该是加密后的数值,而woff则是加密字体文件,而且这个字符串是base64加密后的。

往前查看一下请求调用栈,在网页源码这找到了对字体进行处理的部分代码:
字体文件加载是通过这两行处理的,也就是通过这里加载得到字体的ttf文件,在页面渲染的过程中对data内的value值进行渲染从而得到最后的显示结果。

 ttf = data.woff;$('.font').text('').append('<style type="text/css">@font-face { font-family:"fonteditor";src: url(data:font/truetype;charset=utf-8;base64,' + ttf + '); }</style>');

从源代码这里看不出字体的加密字符和数值的对应关系,于是把woff文件逆编码后保存为ttf格式的字体文件:

b64_code="AAEAAAAKAIAAAwAgT1MvMgPtafAAAAEoAAAAYGNtYXBBft4uAAABpAAAAYpnbHlmPD0Wg.....省略部分字符"
with open('D:\\yuan_font.ttf', 'wb') as f:f.write(base64.b64decode(b64_code))

将保存的ttf字体文件在在线字体编辑器里打开,可以发现这里还原出了每个加密编码对应的数字字符:

这里验证了一下这个字体加密编码跟对应的数值关系,发现每次请求,返回的value值都是不一样的,以第一页为例,对于第一个胜点3236,每次value值都在变化,所以想靠固定编码关系来直接破解是不可能了:

第一次请求:
0: {value: "&#xf134 &#xc261 &#xf134 &#xa613 "}
第二次请求:
0: {value: "&#xf524 &#xa934 &#xf524 &#xc825 "}
第三次请求:
0: {value: "&#xf289 &#xa471 &#xf289 &#xa265 "}

只好进一步查看字体文件的详细信息,利用python的fontTools这个字体库的函数将ttf字体转换为xml格式:

font = TTFont('D:\\yuan_font.ttf')
font.saveXML('D:\\yuan_font.xml')

打开xml文件,这里的codename对应的就是0-9这十个数字的加密编码:

底下的TTGlyph内,contour标签中就是每个数字字形的坐标,全部绘制出来就构成了网页最后渲染出的字体效果,那么既然编码对应的是具体的字形坐标,无论每次返回的数字字符编码是多少,那每个字形的坐标总不会变吧。

好家伙,我还是天真了,打开了两份文件对比了一下,发现即使是相同的数字,其字形竟然也不同,坐标值x,y会有会有小范围的浮动,这就导致即使同样的数字每次渲染出来都会有所不同,就比如底下这个2,虽然看着都一样,但是在轮廓上还是有明显差异:

如此看来,轻松简单的方法是不可能存在了,现在可能的解法有两种,一是虽然每个数字每次字形都有差异,但差异毕竟有限,可以尝试对同一个数字字形的两组坐标值进行对比,如果差别不超过某个阈值,那么就可以认为是同一个字;二是直接把字形文件的坐标绘制出来,生成字体图片,之后再ocr识别数字。

三、代码实现

3.1、方法一:字体映射关系识别数字

为了找到坐标跟对应字符间的映射关系,这里选了数字4的坐标来分析。可以发现,虽然每次返回的坐标值都不一样,但是同一个字体总的坐标对数量是一致的,而且pt内的on值是完全一致的,那么就可以尝试根据两个字体文件内所有的on值是否完全相等来判断是不是同一个数字。

先随便找一个字体文件,手动分析出字符编码与数字的对应关系:

以这个关系作为其他字符编码相比较的参考基准,即如果下一个字体文件中某一个数字对应的所有on值都跟标准对照表里的on值相等,那就确定是同一个数字

    #选定一个基准参照,确定flag和数字的关系font = TTFont("F:\\temp\yuan_font_1.ttf")gs = font.getGlyphSet()glyphNames = font.getGlyphNames()#这个数字顺序跟xml里的TTGlyph name顺序是一致的num_list = [4,6,9,0,2,8,1,5,3,7]map_dict={}for i,name in enumerate(glyphNames[1:]):g = gs[name]flag = list(g._glyph.flags)#读取每个坐标对应的on值# coord=g._glyph.coordinates#获取每个字符的坐标序列#map_dict字典里的键对应的数字,值则是on值构成的列表map_dict[num_list[i]]=flagprint('标准对应关系为:',map_dict)#待解析文件font = TTFont("F:\\temp\yuan_font_2.ttf")gs = font.getGlyphSet()glyphNames = font.getGlyphNames()list2=[]for i,name in enumerate(glyphNames[1:]):g = gs[name]flag = list(g._glyph.flags)#读取每个坐标对应的on值for key,value in map_dict.items():if value==flag:list2.append((name,key))print('解析后的数字对应关系:')for m in list2:print(m)pass

运行一下,输出结果为:

标准对应关系为: {4: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 6: [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0], 9: [1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0], 0: [1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0], 2: [1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0], 8: [1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0], 1: [1, 0, 0, 1, 1, 0, 1, 1, 1, 1], 5: [1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1], 3: [1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0], 7: [1, 1, 1, 1, 1, 1, 1]}解析后的数字对应关系:
('unia168', 1)
('unia254', 8)
('unia362', 4)
('unib967', 7)
('unie634', 3)
('unie671', 2)
('unie813', 6)
('unif175', 5)
('unif416', 0)
('unif845', 9)

这是被解析的字体文件,和程序输出结果进行比较,可以发现编码与数字对应关系是一致的

最难啃的部分已经解决,此后完整代码如下:

def parseByFlag(fontfile):#选定一个基准参照,确定flag和数字的关系font = TTFont("F:\\temp\yuan_font_1.ttf")gs = font.getGlyphSet()glyphNames = font.getGlyphNames()#这个数字顺序跟xml里的TTGlyph name顺序是一致的num_list = [4,6,9,0,2,8,1,5,3,7]map_dict={}for i,name in enumerate(glyphNames[1:]):g = gs[name]flag = list(g._glyph.flags)#读取每个坐标对应的on值# coord=g._glyph.coordinates#获取每个字符的坐标序列#字典里的键对应的数字,值则是on值构成的列表map_dict[num_list[i]]=flag# print('标准对应关系为:',map_dict)font = TTFont(BytesIO(fontfile))#待解析字体文件gs = font.getGlyphSet()glyphNames = font.getGlyphNames()list2=[]for i,name in enumerate(glyphNames[1:]):g = gs[name]flag = list(g._glyph.flags)#读取每个坐标对应的on值for key,value in map_dict.items():if value==flag:list2.append((name,key))# print('解析后的数字对应关系:')# for m in list2:#     print(m)return list2def get_parseResult():headers = {'Proxy-Connection': 'keep-alive','Accept': 'application/json, text/javascript, */*; q=0.01','User-Agent': 'yuanrenxue.project','X-Requested-With': 'XMLHttpRequest','Referer': 'http://match.yuanrenxue.com/match/7','Accept-Language': 'zh-CN,zh;q=0.9',}name = ['极镀ギ紬荕', '爷灬霸气傀儡', '梦战苍穹', '傲世哥', 'мaη肆風聲', '一刀メ隔世', '横刀メ绝杀', 'Q不死你R死你', '魔帝殤邪', '封刀不再战', '倾城孤狼', '戎马江湖','狂得像风', '影之哀伤', '謸氕づ独尊', '傲视狂杀', '追风之梦', '枭雄在世', '傲视之巅', '黑夜刺客', '占你心为王', '爷来取你狗命', '御风踏血', '凫矢暮城', '孤影メ残刀','野区霸王', '噬血啸月', '风逝无迹', '帅的睡不着', '血色杀戮者', '冷视天下', '帅出新高度', '風狆瑬蒗', '灵魂禁锢', 'ヤ地狱篮枫ゞ', '溅血メ破天', '剑尊メ杀戮','塞外う飛龍', '哥‘K纯帅', '逆風祈雨', '恣意踏江山', '望断、天涯路', '地獄惡灵', '疯狂メ孽杀', '寂月灭影', '骚年霸称帝王', '狂杀メ无赦', '死灵的哀伤', '撩妹界扛把子','霸刀☆藐视天下', '潇洒又能打', '狂卩龙灬巅丷峰', '羁旅天涯.', '南宫沐风', '风恋绝尘', '剑下孤魂', '一蓑烟雨', '领域★倾战', '威龙丶断魂神狙', '辉煌战绩', '屎来运赚','伱、Bu够档次', '九音引魂箫', '骨子里的傲气', '霸海断长空', '没枪也很狂', '死魂★之灵']player_dict = {}for i in range(1,6):map_dict={}page=iparams = (('page', str(page)),)response = requests.get('http://match.yuanrenxue.com/api/match/7', headers=headers, params=params)data=response.json()woff=data['woff']valuelist = data['data']fontfile = base64.b64decode(woff.encode())match_result=parseByFlag(fontfile)match_result=[(l[0].replace('uni','&#x'),l[1]) for l in match_result]map_dict={}for m in match_result:map_dict[m[0]]=str(m[1])for n, v in enumerate(valuelist):# 解析得到胜点值win_point = v['value'].split(' ')del win_point[-1]real_win_point = [map_dict[num] for num in win_point]str_ = ''real_win_point = str_.join(real_win_point)print(real_win_point)# 将玩家与胜点值关联player = name[(n + 1) + (page - 1) * 10]player_dict[player] = int(real_win_point)for key, value in player_dict.items():print(key, value)get_parseResult()

最终输出结果如下:

爷灬霸气傀儡 3236
梦战苍穹 5041
傲世哥 3958
мaη肆風聲 8550
一刀メ隔世 7037
横刀メ绝杀 8898
Q不死你R死你 2190
魔帝殤邪 8400
封刀不再战 4500
倾城孤狼 7478
戎马江湖 2342
狂得像风 1926
影之哀伤 5826
謸氕づ独尊 2827
傲视狂杀 369
追风之梦 4384
枭雄在世 2934
傲视之巅 5468
黑夜刺客 9107
占你心为王 2132
爷来取你狗命 5553
御风踏血 687
凫矢暮城 5688
孤影メ残刀 6179
野区霸王 7722
噬血啸月 35
风逝无迹 6301
帅的睡不着 9221
血色杀戮者 6534
冷视天下 9711
帅出新高度 6995
風狆瑬蒗 3705
灵魂禁锢 5413
ヤ地狱篮枫ゞ 2333
溅血メ破天 5660
剑尊メ杀戮 7142
塞外う飛龍 8826
哥‘K纯帅 9291
逆風祈雨 5778
恣意踏江山 2920
望断、天涯路 5983
地獄惡灵 9015
疯狂メ孽杀 1533
寂月灭影 4337
骚年霸称帝王 746
狂杀メ无赦 4349
死灵的哀伤 4229
撩妹界扛把子 4928
霸刀☆藐视天下 2830
潇洒又能打 1206

这里需要注意的是,因为这道题只有十个数字,字符类型不多,所以寻找字体编码与字形映射关系时可以用on值来做关联,如果是有较多字符的情况下,是有可能出现不同字符的on值完全相同的情况,这样就只能按坐标的xy值浮动偏差值来进行比较了。

3.2、方法二:ocr识别数字

以ocr的思路来实现字体解密的核心思路如下:

  1. 先请求数据页api,得到woff文件和加密字体的value数据;
  2. 对woff文件进行编译,转换为TTFont对象;
  3. 通过reportlab库来绘制数字字符图形;
  4. 将绘制的字形结果保存为图片,并用muggle_ocr库来做ocr识别;
  5. 得到所有加密编码对应的数字;
  6. 将数字拼接后得到胜点,并与玩家名字关联
import requests
import base64
from fontTools.ttLib import TTFont
from fontTools.pens.basePen import BasePen
from reportlab.graphics.shapes import Path
from reportlab.lib import colors
from reportlab.graphics import renderPM
from reportlab.graphics.shapes import Group, Drawing, scale
import muggle_ocr
from io import BytesIO#设置字形绘制笔刷
class ReportLabPen(BasePen):"""A pen for drawing onto a reportlab.graphics.shapes.Path object."""def __init__(self, glyphSet, path=None):BasePen.__init__(self, glyphSet)if path is None:path = Path()self.path = pathdef _moveTo(self, p):(x, y) = pself.path.moveTo(x, y)def _lineTo(self, p):(x, y) = pself.path.lineTo(x, y)def _curveToOne(self, p1, p2, p3):(x1, y1) = p1(x2, y2) = p2(x3, y3) = p3self.path.curveTo(x1, y1, x2, y2, x3, y3)def _closePath(self):self.path.closePath()#绘制字形并保存为图片
def ttfToImageNum(fontfile, imagePath, fmt="png"):# 由于TTFont接收一个文件类型# BytesIO(bin_data) 把二进制数据当作文件来操作font = TTFont(BytesIO(fontfile))gs = font.getGlyphSet()glyphNames = font.getGlyphNames()map_dict={}for i in glyphNames:if i[0] == '.':  # skip'.notdef','.null'continueg = gs[i]pen = ReportLabPen(gs, Path(fillColor=colors.black, strokeWidth=1))g.draw(pen)#设置图片高度和宽度w = 1024h = 1024g = Group(pen.path)g.translate(0, 0)d = Drawing(w, h)d.add(g)imageFile = imagePath + "/" + i + ".png"renderPM.drawToFile(d, imageFile, fmt)#图片数字ocr识别img_bytes=open(imageFile,'rb').read()sdk = muggle_ocr.SDK(model_type=muggle_ocr.ModelType.OCR)text_number = sdk.predict(image_bytes=img_bytes)#修正识别错误if text_number=='O':text_number=0if text_number=='G':text_number=6if text_number == 'B':text_number = 8i=i.replace('uni','&#x')map_dict[i]=str(text_number)# print(map_dict)return map_dictif __name__=="__main__":headers = {'Proxy-Connection': 'keep-alive','Accept': 'application/json, text/javascript, */*; q=0.01','User-Agent': 'yuanrenxue.project','X-Requested-With': 'XMLHttpRequest','Referer': 'http://match.yuanrenxue.com/match/7','Accept-Language': 'zh-CN,zh;q=0.9',}name = ['极镀ギ紬荕', '爷灬霸气傀儡', '梦战苍穹', '傲世哥', 'мaη肆風聲', '一刀メ隔世', '横刀メ绝杀', 'Q不死你R死你', '魔帝殤邪', '封刀不再战', '倾城孤狼', '戎马江湖','狂得像风', '影之哀伤', '謸氕づ独尊', '傲视狂杀', '追风之梦', '枭雄在世', '傲视之巅', '黑夜刺客', '占你心为王', '爷来取你狗命', '御风踏血', '凫矢暮城', '孤影メ残刀','野区霸王', '噬血啸月', '风逝无迹', '帅的睡不着', '血色杀戮者', '冷视天下', '帅出新高度', '風狆瑬蒗', '灵魂禁锢', 'ヤ地狱篮枫ゞ', '溅血メ破天', '剑尊メ杀戮','塞外う飛龍', '哥‘K纯帅', '逆風祈雨', '恣意踏江山', '望断、天涯路', '地獄惡灵', '疯狂メ孽杀', '寂月灭影', '骚年霸称帝王', '狂杀メ无赦', '死灵的哀伤', '撩妹界扛把子','霸刀☆藐视天下', '潇洒又能打', '狂卩龙灬巅丷峰', '羁旅天涯.', '南宫沐风', '风恋绝尘', '剑下孤魂', '一蓑烟雨', '领域★倾战', '威龙丶断魂神狙', '辉煌战绩', '屎来运赚','伱、Bu够档次', '九音引魂箫', '骨子里的傲气', '霸海断长空', '没枪也很狂', '死魂★之灵']player_dict={}for i in range(1,6):page=iparams = (('page', str(page)),)response = requests.get('http://match.yuanrenxue.com/api/match/7', headers=headers, params=params)data=response.json()woff=data['woff']valuelist = data['data']#解析加密后字体与数字的映射关系map_dict=ttfToImageNum(fontfile=base64.b64decode(woff.encode()), imagePath="D:\\")for n,v in enumerate(valuelist):#解析得到胜点值win_point=v['value'].split(' ')del win_point[-1]real_win_point=[map_dict[num] for num in win_point]str_=''real_win_point=str_.join(real_win_point)print(real_win_point)#将玩家与胜点值关联player=name[(n+1) + (page - 1) * 10]player_dict[player]=int(real_win_point)for key,value in player_dict.items():print(key,value)

生成图片过程中,得到的图片样式类似这样:

ocr过程如下,需要注意的是由于生成的数字本身形状不是很标准,所以识别过程中会出现类似把0识别成字母O,8识别成字母B这种情况,需要手动做一下替换。此外ocr识别的过程还是稍有些慢,如果字符数比较多久很好时了,所以不适用于生产环境,也许有更好的识别库日后可以试试。

最终输出的结果如下,通过跟原始网页对比可以看出玩家名字与胜点值都一一对应上了,且并无错误。

四、参考文献

  1. 破解字体加密解决思路
  2. 听说你爬回来的都是乱码?三个案例教你破解字体加密
  3. Python converts ttf to png
  4. Python爬虫:网页字体加密与解密实践

猿人学web端爬虫攻防大赛赛题解析_第七题:动态字体,随风漂移相关推荐

  1. 猿人学web端爬虫攻防大赛赛题解析_第九题:js混淆-动态cookie2

    js混淆,动态cookie2 一.前言 二.加密逻辑初探 三.加密逻辑深入分析 四.代码实现 4.1.ast解混淆的一个坑 4.2.完整实现过程 五.参考文献 一.前言 一转眼又有快两个星期没更博客了 ...

  2. 猿人学web端爬虫攻防大赛赛题解析_第一题:源码乱码

    第一题:js 混淆 - 源码乱码 1.前言 2.题目理解 3.逆向(踩坑)分析过程 3.1.初步分析 3.2.当头一棒 3.3.循序渐进 3.4.大功告成 4.结语 1.前言 接触网络爬虫已经有两三年 ...

  3. 猿人学web端爬虫攻防大赛赛题解析_第四题:雪碧图、样式干扰

    第四题:雪碧图 - 样式干扰 1.前言 2.题目理解 3.解析过程 3.1.初窥门径 3.2.深入探究 3.2.1.确定原理 3.2.2.逆向破解 3.2.代码实现 4.结语 1.前言 久违的第四题终 ...

  4. 猿人学web端爬虫攻防平台第七题动态字体

    本人呢很喜欢爬虫,但是技术不好,所以呢就一直慢慢的摸索着,也不知道那什么练手进行学习,直到我在偶然的机会接触到了猿人学,发现了 猿人学web端爬虫攻防平台这个网站,久旱逢甘霖,他乡遇故知呀(蹩脚的词语 ...

  5. 猿人学-Android端爬虫比赛开赛

    猿人学-Android逆向第一届比赛打响: 报名地址 :https://appmatch.yuanrenxue.com/ 赛事简介: 猿人学Android端爬虫比赛于2022年5月13日晚开始,共设1 ...

  6. 猿人学-Android端爬虫比赛第五关【双向认证】解题笔记

    一.知识点: 单向认证:服务端向客户端发送CA证书,并用证书中的公钥对随机数加密使其成为通信秘钥 双向认证:服务端和客户端都有CA证书,并向对方发送,服务端选择加密方案让客户端进行加密密钥. 以上认证 ...

  7. [007]爬虫系列 | 猿人学爬虫攻防大赛 | 第二题: js 混淆 - 动态Cookie

    一.题目 链接: <猿人学爬虫攻防大赛 | 第二题: js 混淆 - 动态Cookie> 二.分析 按照以往习惯,我们先按F12打开控制台,Network抓包,勾选preserve log ...

  8. [007]爬虫系列 | 猿人学爬虫攻防大赛 | 第五题: js混淆 乱码增强(中)

    一.备注 在阅读此文章前,请先阅读前一篇<[007]爬虫系列 | 猿人学爬虫攻防大赛 | 第五题: js混淆 乱码增强(上)> 二.找参数来源(二) 在前一篇文章中,我们找出了Cookie ...

  9. [007]爬虫系列 | 猿人学爬虫攻防大赛 | 第五题: js混淆 乱码增强(下)

    一.备注 在阅读此文章前,请先阅读前两篇 <[007]爬虫系列 | 猿人学爬虫攻防大赛 | 第五题: js混淆 乱码增强(上)> <[007]爬虫系列 | 猿人学爬虫攻防大赛 | 第 ...

  10. 今日头条Web端爬虫as,cp值破解

    今日头条Web端爬虫as,cp值破解 请各位转载的朋友请注明出处. 作者:小胖 实验网址:https://www.toutiao.com/ch/news_tech/ 实验环境:Windows10 实验 ...

最新文章

  1. cimiss数据_CIMISS,你太优秀了!
  2. CloudFoundry Service 使用
  3. 第一次接触正则表达式/^[A-Za-z_][A-Za-z0-9_]{5,15}$/
  4. matlab中udt函数,《MATLAB信号处理超级学习手册》——2.5 离散时间信号中的运算...
  5. Jmeter常见问题
  6. Java IO的RandomAccessFile的使用(转)
  7. 怎么安装python3.6.5_Centos7 安装Python3.6.5
  8. Repeater的使用
  9. struts2 防止重复提交 与 进入等待画面
  10. springMVC一些实践总结
  11. Altium Designer(二):规则设置
  12. 如何用bat批处理编译swf项目
  13. 地形瓦片地图TiledMap AutoTiles自动图块
  14. 遥感影像数据下载网站整理
  15. 海康威视摄像头Android直播APP开发
  16. 键盘调节台式计算机声音,完美:如何增加键盘上的音量
  17. 用c语言编写超级马里奥_编写《超级马里奥兄弟》游戏以学习游戏开发
  18. 网易游戏再出新招:探索人脸识别功能
  19. Java——(九)IO流
  20. java 自动创建html网页超链接

热门文章

  1. 自定义安装 Microsoft Office 2019 and Active
  2. 制造业升级:老调年年弹
  3. Java枚举(enum)
  4. 注册github邮箱验证收不到邮件问题
  5. Maya 展UV和贴图
  6. UDS服务中关于服务器响应行为的实现规则
  7. 方维直播最新版服务端网站搭建3.1--1
  8. 量化投资策略——金叉死叉策略
  9. 现在润新加坡是个好主意吗?
  10. 产品经理的小白面试~