首页
社区
课程
招聘
[原创]国产电纸书Bambook破解笔记
发表于: 2017-4-5 21:45 32730

[原创]国产电纸书Bambook破解笔记

2017-4-5 21:45
32730

感觉这个属于一个硬件分析的笔记, 比起android版发这里更合适.

word版不知道怎么附件传不上, 只得一点点粘过来

        从硬件到软件

    事情的开头要从一个国产单片机群说起. 群里潜水多年, 经常会遇见一群神人发表这样的言论:

    1 我的代码检测到自己被修改后, 就会擦写自毁

    2 我的代码会把电源连到地上, 让芯片自毁

    3 我的代码会控制一个接地的gpio引脚输出VCC, 烧了芯片

    4我的代码会控制2个连在一起的gpio一个输出VCC一个输出GND, 烧了芯片

    5我的电路会在打开产品外壳时候, 产生高压击毁芯片

    6 我的电路会在抄板后, 产生短路, 工作不起来

    7我的电路板上一些元件是错的, 标的是电阻其实是电容

    看起来大多数程序员都对"烧毁芯片"有特殊的爱好

    可能以后我会写一些笑话帖子时候, 再继续这个话题, 今天我们说的是, 改造盛大的一个封闭系统电纸书.

    当年, 大部分的eink reader都经过深度定制和限制, 不支持用户安装app, 开机自动进入阅读器主程序. 看书, 设置, 书城, 全部一个阅读器apk搞定.

    这机器型号是SD928, 面向公众的第一款. 从出错画面看, 系统应该是android. 我曾经想要把它的系统dump出来, 然后研究自制固件, 刷机, root. 为此还买了一个碎屏的机子, 准备拆下来用编程器读取. 但当年我烙铁焊不下来屏蔽罩, 当时也有办法就是从背面挖穿, 但完美拆机主义的我, 舍不得破坏完整的电路板.

    很多年过去了, 偶然淘宝一看还有人在卖坏机, 电池鼓包. 卖家表示应该还是好的, 遂77元购入, 拆开一看已经鼓得后盖变形了, 还好电路板没事. 又起了念头继续研究下刷机, 说时迟那时快, 请了热心网友帮我拆下了eMMC芯片, 又吹到eMMC转接板上, 插读卡器, 读取. 然后从dump下来的分区发现, 这机器的eMMC芯片是当作用户数据用的, 系统并不从eMMC启动. 不过eMMC的分区里面, 存储的有解密后的固件镜像. 应该是上次刷机过程残留的.

    既然系统不从eMMC引导, 那么是从哪里引导的呢? 在电路板上找到另一块之前忽略的芯片MT29C2G24MAKJAJC-75, eMCP的, 封装了DRAM和NAND flash.

    通过解压固件镜像, 我们可以提取系统的rootfs, 通过分析发现固件由PC端发到设备, 设备从服务器获取key, 解密后在/mnt/data/updates目录得出snda_s.firmware(System), snda_l.firmware(Logo) , snda_k.firmware(Kernel), boot_nontrust.bin(Bootloader) 几个文件 , 再通过fwupdate和bootloaderupdate工具刷到eMCP中.

    官方的固件里面, adbd是禁用的, usb连接上以后就是一个RNDIS设备, 没有其他功能.

    如果我们能在系统开机后, 执行fwupdate, 我们就可以实现本地刷机了. 之前主板的eMMC拆下后, 量出来了DAT/CLK/CMD等和旁边的电阻对应的接线关系, 我们可以实现免拆芯片在板烧写eMMC, 但还缺乏一个入口.

 

图1-1 在线烧写eMMC焊线图

    经过多次尝试, 发现当在菜单里面选择"恢复工厂设定", 系统就会执行/mnt/data/scripts/clear-user-data.sh脚本文件.

    同目录下还有个start.sh, 但它只在刷机下一次进入系统前执行. 以后每次重启和开机都不会执行. (可能老版本固件也会每次执行吧)

    因为我们测试比较频繁, 所以选择修改clear-user-data.sh, 去掉该脚本原有的清除用户数据命令, 并且加上如下几行

    这样在系统设置里面执行"恢复工厂设定"以后, 就会在5555端口提供adbd服务并且在3389开启ftp服务.

    这个tinyftp是从网上的代码修改的, ndk编译, 需要设置APP_PLATFORM := android-3.

    然后我通过固件解包再打包的方式, 修改init.rc, 生成新的snda_k.firmware重新刷机, 成功后就开启了adbd服务并且可以通过wifi连接上了.

    当然, 我不可能让大家都跑去拆机硬改, 这只是挖出厂家做为"秘密"封闭起来的镜像和系统架构的攻城车.

    有了adb shell以后, 我们可以编译一些工具进系统测试了. 考虑先试试老的CVE有没有办法利用, 查看系统是android 1.5, 内核2.6.28, 找了一个浏览器入口的CVE-2010-1807, 影响Android 2.2以前浏览器.

    锦书默认是个封闭系统但后面新的固件支持阅读器(SndaBrowser)里面运行html的widget, 可以用来测试该bug. 我做了一个widget打开这个cve网页. 再在阅读器里面载入.

    经过反复测试和dump SndaBrowser进程, 我修改了Itzhak Zuk的利用代码, 企图让该shellcode在锦书上执行. 最后试验结果是, 会引发Segmentation fault, 内存里面滑板代码也得到了填充, 但并没有成功跳转. 具体分析原因当时钉钉是web版没记录记不清了, 在此感谢村长的帮助.

    图1-2 SndaBrowser崩溃前的内存dump

    不过不用担心, 别人的洞不好用自己挖也可以. 之前分析它官方PC客户端跟设备之间通讯的时候, 我们在libSndaEBook.so的onSyncServerReceiveFile函数看到一些可以利用的地方:

    此bug有2个子bug, 其一是拼接字符串时候, 用到了filename, 这个字符串我们可以通过自制PC客户端来控制, 在文件名中传入分隔符后, system调用可以执行额外指令.

    其二是install-bookimage.sh写的不好.

    我们来看一下install-bookimage.sh:

    这个脚本做的事情非常简单, 将PC端发来的文件解压到iNAND的用户分区, 然后给/mnt/data/key/skb-newrsa-file加上可执行权限, 再执行它.

    我查看了下, 用户分区本身没有这个可执行文件. 也就是说设计上他是上一步释放出来的. 很显然, 这是一个比刚才更好的bug, 它不但能够直接植入文件, 还顺便帮你加可执行权限, 再执行它.

    从提示信息来看它应该是厂家为了弄一些"正版图书大礼包"预留的接口, 不是开放给用户使用的. 经过分析, 官方的云梯客户端, 也没有这个接口.

    这里跟刚才那个case属于同一个switch, 可以看到传送固件也是走的这个接口, 只是封包中包含的fileType字段不同. 我们可以模仿官方的协议来通信, 或者偷懒在官方的dll上补一下实现我们的需求.

    经过分析BambookCore.dll, 我们找到一处给设备传输书架信息的ReplaceCatelogFile函数, 它会给设备发一个fileType是12的catelog.xml文件. 注意这里的0D设备上switch的fileType将是0C.

    .text:1000B5C7 0A4 C7 44 24 7C 0D 00 00 00                 mov     [esp+0A0h+type], 0Dh

    .text:1000B5B0 0A4 68 C4 0F 0E 10                          push    offset aCatalog_xml ; "catalog.xml"

    PC端通讯参数

    设备端逻辑分支

    这个可以结合上面shell脚本的bug, 实现执行我们的代码. 我做了一个小demo, 把mov补为0E, 然后发送自己构造的tar.gz

    我们把tar包里面key/skb-newrsa-file换成了可执行文件(后来发现脚本也能用), 感觉它这个skb-newrsa-file原本是可执行文件, 作用是给snb文件安装授权的 (因为不是用户通过云中书城下载的私有书).

    经过测试成功得到执行, 而且这个bug中开启的shell是root权限. 我们设置了adbd的属性后直接执行adbd, 就可以通过内置的fwupdate进行刷机了. 该工具使用未加密非打包的固件镜像, 也不用考虑再封包的问题.

    通过分析刷机工具和系统mtd设备, 我们整理出了eMCP的NAND分区划分:

可见Kernel/system/logo都存在了3份拷贝. 最前面是活跃的, 刷机时候fwupdate参数可以控制刷入到backup(出厂预设)或者reserve(正常刷机)的分组, 不会刷新当前活跃的分区.

    我们推测Bootloader会在刷完机下次引导时候选择reserve复制到活跃的分区.

    额外提一句, 这里的分区表是在设计系统时候预先划分好的, bootloader和系统都使用同样的硬编码偏移. 所对应的地址就是NAND的原始地址.

    我们先看Bootloader. 根据Marvell的文档(PXA3xx_TavorP_BootROM_Ref_Manual.pdf), 我们知道它是有结构的, bootrom按照头部进行装载. 不是整个装入内存或者挂载到指令总线上从0偏移执行.

    可以看出这个Bootloader没有Trusted标记, 有3个子image, 一个是TIMH, 就是这个头部本身. 一个是OBMI, 位于flash的0x20000偏移, 会被bootrom载入到内存的0x5C013000, 另一个是OSLO, 位于0x40000会被载入到0x81000000.

    那么OBMI跟OSLO是不是就运行在这2个内存地址呢? 这个我们要再次确认.

    查看OBMI的ResetHandler, 它会把自身image复制到0x800007FC位置, 0x80000000位置存放的是ResetHandler地址本身, 这中间的7F8空间可能是被用作变量空间了. 这类的image,  并不像大家想的那样都是位置无关的, 大部分都是位置相关再加一个检查/复制/跳转代码的.

    举个例子, iBoot的Reset Handler, 根据VectorTable地址我们可以判断代码需要运行在9FF00000地址, 通常iBoot的loader会初始化好内存map并将其加载到合适的地址, 但iBoot自身也额外带了检查和复制代码.

    当发现自己不在指定位置时候, 就会依次复制代码/常量/变量到指定的地址.

    这些信息可以用来指导我们进一步划分/增加区段.

    代码2-1 iBoot拷贝片段注意ADR是取PC相对地址.


    图2-2 初步整理后的iBoot区段


    有一些芯片, 是支持从flash直接执行的, 不需要占用内存. 这时候不管是rtOS还是baremetal的image, 都会有一个从链接后的image中, 复制一部分已有初值的变量到sram/dram的流程.

    具体的内存布局不同, 要看对应的编译链接脚本和安排这一切的程序员.

    介绍这些并不是废话, 在我们分析时候, 进行正确的分段和添加堆的内存区域能提高逆向效率.

    例如iBoot我们确定了.data这部分信息, 逆向过程中可以新建RAM段并且载入, 以便观察一些全局变量的初值.


    最后我们确定在OBMI中包含名为Blob的xscale专用bootloader, 运行在80008000. 在flash的偏移是40000.

    正常刷机流程中, 应该是fwupdate设置systeminfo分区的里面的几个标志位, 下次启动时候, bootloader检测这个标志位并更新活跃的分区, 再去掉该标志.

    而另一套备份固件, 怀疑需要特殊的按键组合触发. 我们怎么找这些按键的组合呢? 首先这些电子设备, 一般不会像电脑一样, 还做一套PS2/USB接口来外挂键盘. 最为常见的线路接法有GPIO单独检测, 行列扫描两种. 两种都支持多键组合检测.  该设备cpu型号是PXA310, 根据marvell提供的介绍, 该cpu具有directkey和matrix key两套键盘接口. 它的matrix扫描是片内设备实现的, 并且支持自动扫描, 我们可以直接从Keypad Matrix Key (KPMK) Register读取有哪几行被按下, 然后从KPASMKP0~KPASMKP3读出每行按下的列. 每个寄存器包含2行, 一共64个按键都在其中.

    通过查找参考手册里面的寄存器地址, 我们在IDA中寻找敏感的地址, 找到如下代码

    (参考手册PXA3xx_DM_vol_III.pdf, Table 254: Keypad Controller Register Summary)


再说了, XScale是活在没有thumb2 (主要是MOVW, MOVT)的时代, ARM代码中很常见的优化会把一个常数用各种移位和算术运算拼出来, 拼不出来才会用2条LDR加一个函数末尾的常数的方式(效率较低). 二进制搜索很难奏效.

    不过贴心的IDA里面默认会将拼接常量的指令合并为一条伪指令. 使用Search immediate value即可. 当然也要注意有时候编译器在拼接常量的过程中插入了其他指令, 试试文本搜索(针对最后一次拼接的注释)和搜索常量的一部分或许也有找到的时候.

    这里说句题外话, 在movw/movt中间嵌入svc或者自制的不影响寄存器的call, 也是我以前喜欢的干扰IDA分析方法之一.

同时我们找到的还有另一个使用该地址的函数, 将会在后面分析.

    首先看初始化键盘控制寄存器的, 我们根据代码的配置可以得出需要测量的CPU引脚编号, 再根据引脚编号测量硬件按键对应的GPIO.

    这里注释的内容是跟后面提到的func_config_UART3_and_enable_clock一样的方式推导出来的.

    至于BGA脚位, 参考文档PXA3xx_EMTS.pdf, 4.1.2.1 PXA310 Processor 13mm2 VF-BGA Ball Map

    图2-6, 翻页键排线座


    我们用夹子夹到这些引脚上, 再测试它和哪些cpu引脚相通, 列表记录.

引脚编号

GPIO

 角色

F14

(GPIO122)

KP_MKOUT<1>

 A15

(GPIO115)

KP_MKIN<0>

A16

(GPIO116)

KP_MKIN<1>

VSS

 

 

    表2-7 翻页键接线

    此表分别记录了BGA引脚编号 GPIO编号设计中角色


    图2-8, 五向键排线座


引脚编号

 GPIO

角色

 Alias

F14

                    (GPIO122)

                    KP_MKOUT<1>


               

E13

                    (GPIO121)

                    KP_MKOUT<0>

                    a

B13

                    (GPIO123)

                    KP_MKOUT<2>

                    b

F12

                    (GPIO125)

                    KP_MKOUT<4>

                    c

Y18

                    (GPIO4_2)

                    KP_MKOUT[5]

                    f

Y19

                    (GPIO6_2)

                    KP_MKOUT[7]

                    d

W15

                    (GPIO2_2)

                    KP_MKIN<6>


               

VSS

 

 


               

    表2-9 五向键接线


    这里翻页键是1*2矩阵, 五向键是6*1矩阵.


    电路板上有一个区域是窝仔片构成的导航和数字键, 我们按照横向ABC竖向abcdef助记方式, 测试每个键处于哪个交点上.

    为了测量和描述方便, 我们把横向称为行(row), 纵向叫做列(col), 其中行为输入,  列为输出. 这也跟Marvell的设计一致.


    图2-10 数字和菜单键矩阵相同的大写字母为同行相同小写字母为同列


    首先我们看看软件中的col分别对应这些导航和数字的哪几列

    col0=a col2=b col4=c col7=d col5=f

    不在这个主板上的五方向键其实也占了单独一行.

    fivekey=row6

    A nav/num 789 B14(GPIO119) = row4

    B memu/num456/# C15(GPIO118) = row3

    C num123*0=E14(GPIO117)=row2

    e num258 = Y20(GPIO5 _2)=col6

    以下略



    整理矩阵表如下


     

    hold 上侧C12(GPIO127), 角色KP_DKIN<0>, 它估计设计时候每次刷新一下墨水屏就睡眠, 所以用DirectKey来做中断唤醒.

    音量加减也是矩阵, 音量按键右侧公共端是行, CPU脚位测得W20(GPIO3_2), 对应row7, 音量减的列为F14, 音量加为a(E13)


    然后接着看另一个使用了0x41500000做参数的Key_DoUpdateFirmware函数, 其中kpcbase就是传递来的0x41500000, 通过查编程手册我们可以搞清这些寄存器的用途, 为了方便听众老爷我记录在刚才那张表里了. 然后让我们来还原这些按键检测的真面目:

    代码2-12 手动恢复固件片段

    我们略过第二层if从它末尾看, 在上面的第一层if包含了以下的判断: 第一个的true分支根据代码看是检查硬件加密芯片, 触发条件显然是菜单+8, 第二个if的false分支则是检测音量+/音量-同时按下, 执行DoReservedFirmwareUpdate.

    我们再看上面第一次if那个判断的false分支, 也就是[找书]+[1]成立, 执行的是DoFactoryFirmwareUpdate

    这个g_sysflags实际是systeminfo分区存放的内容. unReserveFWReady和unFactoryFWReady分别是指示普通升级过程的保留分区是否存在数据和出厂固件分区是否存在数据.

    我们在系统的fwupdate和对应的so里面, 同样可以找到将固件写到这两套分区并设置不同的标志的代码. 这里估计是做的防护措施, 一旦活跃的固件不小心损坏了, 售后人员可以直接操作组合键先来恢复一下出厂固件, 也不用把一些内部维护工具发到维修站, 以免泄露出去.

    当然这个机器, 除了Bootloader级别的组合键, 还有进入系统后再检测的组合键.

    下面讲一下发现的过程. 我假装无意中从它的system分区发现了一些有意思的apk, 比如FoxconnTest.apk, SndaBrowser.apk. 前者是富士康测试程序, 后者是该阅读器的主界面.

    当初我假设机器的启动脚本中, 根据某个特殊的按键组合, 选择启动二者之一. 后来实际分析发现并非如此. 在snda.browser.Browser中发现一个IsDiagMode函数

    接着看在onCreate中有如下代码引用这个函数

    我们可以认为它至少有两种诊断模式.

    然后搜索DiagMode, 找到另一段代码:

前四个姑且不论, 这个com.foxconn.FoxConnTestActivity正是我们的富士康测试app. 他将在/proc/driver/diagmode 的内容为"1+7"时候启动测试界面. 也就是说SndaBrowser这里充当了Launcher.

    我们在解开的initramfs和system包中分别搜索diagmode和sagadiag, 发现以下有意思的内容

这段是用来选择格式化/mnt/data或者检查分区的.

这一段则是去掉usb口RDNIS共享上网的模块, 并将eMMC芯片挂载为U盘. 应该是工厂用来复制用户数据分区的.

    从我们这里和刚才的信息看, 1+7, 1+5, 1+9,可能就是键盘上数字键的组合.

    图2-19 Foxconn Diagnostic截图

    经过验证, 成功进入这个Foxconn测试界面.

    上面我们分析时候, 看到Bootloader有一个输出信息是

    Autoboot aborted

    Type "help" to get a list of commands

    我怀疑它可能留有调试用的串口, 可以用来查看这些输出信息.

    虽然这个Blob编译时候因为bootdelay设置为0导致无法进入这个boot console, 但我看了下里面很多命令都没有阉割. 包括通过串口/网卡刷写flash, 聪明的听众们是不是猜到这个有什么用了, 对这个可以预防我们做固件时候变砖. 不过我们已经有更好的方案, 就是找到的组合键可以选择恢复backup或者reserve的固件之一, 只要我们不神经一次性刷坏两套, 总可以进到系统的.

    我们补上进菜单功能的话, 可以不怕一次刷坏两套备份.

    或许有人说了, 刷firmware的几个分区没有危险, 如果我们改Bootloader不小心某次改坏了, 它的恢复firmware分区功能无效了, 那怎么办, 不要紧, 我们还有JTAG.

    JTAG接线的挖掘过程可以看附录的"寻找测试点"章节.

    Jtag接口本身的介绍, 估计大家一搜就是什么标准专家组, 什么多芯片串联接法, 我要说的是没那么复杂, 我们改造出来的JTAG刷机跟使用手机开发板/单片机开发板一样都是一对一的.

    TODO:

    如何制作刷不死bootloader, 如何找uart

    我们在Bootloader的代码中, 可以看出puts依次调用SerialOutputString, serial_write, serial_driver->write(c).

    查看该全局变量赋值位置:

    serial_driver = &g_liittleton_serial_driver;

    再次查看g_littleton_serial_driver, 它的几个成员如下:


   

    它这些地址在Marvell的文档里面可以查出来是对应什么外设范围, 叫做什么寄存器. 前面2个就是配置"路由", 也就是物理引脚切换到什么逻辑设备上(时间长了, 此处需要核对手册).

    可以查看手册PXA3xx_DM_Vol_I.pdf, Table 12: PXA31x Processor Alternate Function Table, Table 16: PXA31x Processor Pad Control Addresses, 4.11.2 Multi-Function Pin Registers (MFPR)

    前面一个表是描述每个物理引脚可以连接到的功能, 而第二个表就是控制这些功能的寄存器地址, 最后一张表则是描述这个寄存器地址写入的值功能. 我们主要关心它最后3bit, 在我们这两句赋值里, 都是0b001 = Alternate function 1


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

上传的附件:
收藏
免费 3
支持
分享
打赏 + 2.00雪花
打赏次数 2 雪花 + 2.00
 
赞赏  CCkicker   +1.00 2017/05/08
赞赏  kanxue   +1.00 2017/05/04
最新回复 (33)
雪    币: 3758
活跃值: (3337)
能力值: ( LV15,RANK:500 )
在线值:
发帖
回帖
粉丝
2

晕倒, 发完发现似乎帖子长度还有限制. 接上.

PS: 不是限制, 是某个词语后面的内容都会被截掉, 那就是USB的(D

2017-4-5 21:53
0
雪    币: 47147
活跃值: (20460)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
3
哇,曾大仙回来了,感谢分享!

最后几张图没显示出来。
2017-4-5 21:59
0
雪    币: 3758
活跃值: (3337)
能力值: ( LV15,RANK:500 )
在线值:
发帖
回帖
粉丝
4
坛主顶帖速度一流!  前几年受过打击,  好久没搞这些了
2017-4-5 22:05
0
雪    币: 47147
活跃值: (20460)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
5
曾半仙 坛主顶帖速度一流! 前几年受过打击, 好久没搞这些了
过段时间,喊大伙一起聚聚
2017-4-5 22:11
0
雪    币: 216
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
顶一个
2017-4-6 09:01
0
雪    币: 6790
活跃值: (4441)
能力值: (RANK:600 )
在线值:
发帖
回帖
粉丝
7
感谢分享,信息量很大,欢迎大牛归来!
2017-4-6 10:38
0
雪    币: 133
活跃值: (233)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
思路清晰,赞
2017-4-6 14:39
0
雪    币: 2548
活跃值: (965)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
9
mark,感谢分享
2017-4-6 17:12
0
雪    币: 12671
活跃值: (3905)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
破解是个力气活啊,现在高人很少露面了。
2017-4-6 17:31
0
雪    币: 562
活跃值: (4347)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
11
厉害
2017-4-6 18:03
0
雪    币: 26205
活跃值: (63302)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
12

这就是传说中的牛人?
改天登门拜访,做个采访

2017-4-6 20:57
0
雪    币: 1436
活跃值: (3906)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
13
高手,学习
2017-4-7 11:48
0
雪    币: 1696
活跃值: (2297)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
进来膜拜
2017-4-8 09:34
0
雪    币: 3190
活跃值: (1816)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
下载学习,感谢分享,
2017-4-11 10:13
0
雪    币: 312
活跃值: (123)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
16
膜拜
2017-4-11 11:24
0
雪    币: 6949
活跃值: (2785)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
写的很是深入浅出,即便不是该行业的人,也能从中有所收获,很不错的帖子,希望论坛这类帖子越来越多。
2017-4-18 23:52
0
雪    币: 407
活跃值: (1816)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
膜拜大佬,想想以前倒腾树莓派的日子……
2017-4-20 21:06
0
雪    币: 47147
活跃值: (20460)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
19
测试下赞赏功能
2017-5-4 17:03
0
雪    币: 38
活跃值: (17)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
简直太强了.
2017-5-5 18:33
0
雪    币: 37
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
涨姿势了,顶一下
2017-5-15 10:20
0
雪    币: 1
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
老哥能不能给个普通用户可以用的方案,,,好久没在网上看到这么有技术性的文章了
2017-5-19 17:42
0
雪    币: 34
活跃值: (95)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
看到这么有含金量的硬破帖子,佩服得顶起来!
2017-6-10 21:36
0
雪    币: 563
活跃值: (95)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
先收藏!
2017-6-10 22:54
0
雪    币: 250
活跃值: (187)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
厉害,完全看不懂
2017-6-13 08:22
0
游客
登录 | 注册 方可回帖
返回
//