吾愛破解 - LCG - LSG |安卓破解|病毒分析|破解軟件|www.vuyuem.live

 找回密碼
 注冊[Register]

QQ登錄

只需一步,快速開始

搜索
查看: 6268|回復: 34
上一主題 下一主題

[Android 轉帖] 陰陽師:一個非酋的逆向旅程

[復制鏈接]
跳轉到指定樓層
樓主
薛-藍狐 發表于 2019-10-23 01:40 回帖獎勵
0x00 前言為了驗證這個游戲到底有沒有 SSR0x01 前期工作直接將 onmyojinetease1.0.14.apk 解壓出來觀察各個文件,便可以知道陰陽師是使用 NeoX + Python。 其中 lib/armeabi-v7a/libclient.so 和 assets/script.npk 這兩個文件, 一個是帶著 Python 虛擬機以及加解密相關的 so 文件,一個是加密之后的 Python 文件, 所以我們后期的工作中心也主要放在這兩個文件上。為了能夠在后面調試陰陽師,我們需要對陰陽師重打包:使用 apktool 解包apktool d onmyoji_netease_1.0.14.apk  修改 debuggable 將 AndroidManifest.xml 中的 debuggable 修改為 true重打包apktool b onmyoji_netease_1.0.14  簽名java -jar signapk.jar platform.x509.pem platform.pk8 onmyoji_netease_1.0.14.apk onmyoji_netease_1.0.14_fix.apk  0x02 Android 調試初試由于我是第一次進行 Android 調試,所以這里我寫得稍微啰嗦一點兒。1.關閉SELinuxsetenforce 0  2.運行 android_server/data/android_server3.將 android_server 的端口轉發到本地adb forward tcp:23946 tcp:23946  4.啟動陰陽師am start -D -n  com.netease.onmyoji/com.netease.onmyoji.Launcher  5.IDA 遠程 Attach
6.IDA 設置調試選項7.將陰陽師的調試端口轉發到本地ps | grep netease.onmyoji  adb forward tcp:17178 jdwp:process_pid  8.jdb 附加jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=17178  由于種種原因,我需要重開很多次陰陽師,所以我就將步驟 7,8 合并成一個 copy & paste 的命令for /f "delims=" %i in ('adb shell "set `ps |grep netease.onmyoji`; echo -n $2"') do adb forward tcp:17178 jdwp:%i && jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=17178  0x03 格式分析在動態調試前,我們還是先看一下 assets/script.npk 的格式。雖然之前逆過網易其他游戲,也知道這文件是什么格式, 不過還是說一下我第一次分析的步驟,我們使用 C32 將其打開:除了一個 NXPK 的 header,也看不出其他信息,我們再看看文件的尾部:好像也沒有什么特別的信息,不過等等,我們把窗口調整一下:現在就可以很清楚的看到數據中間有一排 00000000。我們可以大膽猜測有效數據是由 00000000進行分割, 我們拿一小段數據出來分析:00000000 4C71C6FD ECD60A00 69040000 69040000 3057F779 3057F779  00000000 A9B3CEFD 5CB91F00 AC000000 AC000000 80C1D70C 80C1D70C  00000000 950CD6FD FC7E3500 C4050000 C4050000 21433D9E 21433D9E  00000000 CA1EF8FD D88F2700 FE100000 FE100000 6BB047E4 6BB047E4  00000000 73A33EFE 50614200 A71B0000 A71B0000 4A32F72E 4A32F72E  00000000 A5E959FE C40B0300 0A090000 0A090000 9B7A1F45 9B7A1F45  我們會發現:
  • 第四排和第五排數據重復,第六排和第七排數據重復,所以有效數據只有四排
  • 第二排數據的尾部是由上至下遞增的,但是第二排的數據已經大過了文件大小,暫時意義不明
  • 第三排數據,存在和 assets/script.npk 文件大小相近的數據,但是不存在大于文件大小的數據,猜測第三排的數據和 assets/script.npk 文件的 offset 相關
  • 再看看第四排的數據,第四排的數據都偏小,猜測第四排的數據和文件的 大小 相關
  • 最后看第五排數據,暫時意義不明
所以到這里我們可以猜測 assets/script.npk 的尾部數據是一個索引表, 陰陽師通過 Python 文件名計算出索引表的偏移量,然后再通過表格里面的文件偏移和大小, 獲取到對應加密后的 Python 代碼。如果索引表真的存在,那么程序如何確定這個表的起始地址呢,我們找到這個索引表的前部:可以看到這個表格的起始地址為 0x00522DE8,我們在 C32 里面搜索這個地址:會發現文件起始地址偏移 0x14 處記錄了索引表的地址。總結:
  • assets/script.npk 在偏移量 0x14 處記錄了索引表的地址
  • 索引表每個索引由 0x00000000 進行分割
  • 索引中的第三排數據是某個 Python 文件在 assets/script.npk 的偏移量
  • 索引中的第四排數據是某個 Python 文件的大小
0x03 動態調試在文章前面提過,lib/armeabi-v7a/libclient.so 和 assets/script.npk 這兩個是重點文件, 在 libclient.so 加載之后,自然要關注一下 script.npk。要想解密 script.npk,基本思路還是挺簡單的:關注 script.npk 讀取的數據經過了怎么樣的處理。為了更好的將 read 函數中的 fd 與文件名對應,我在 open 處下條件斷點,并加上下面的判斷:import idc  if not hasattr(idc, "fd_map"):      idc.fd_map = {}filename = GetString(cpu.r0)  if filename and "script.npk" in filename:      StepUntilRet()    GetDebuggerEvent(WFNE_SUSP, -1)    fd = cpu.r0    continue_process()    if fd != idaapi.BADADDR:        print("open: %s fd: %s" % (filename, fd))        idc.fd_map[fd] = filename        return Trueelse:      print("open: %s" % filename)return False  上面的代碼主要是將和 script.npk 相關的 fd 和文件名關聯起來, 方便于在 read 調用時區分和 script.npk 相關的讀取操作。read 函數條件斷點代碼:import idc  if not hasattr(idc, 'fd_map'):      returnfd = cpu.r0  if fd in idc.fd_map:      print("reading: %s" % idc.fd_map[fd])    return Truereturn False  下了斷點之后,你會發現這并沒有什么用,因為調用次數過多,而且是由 Python 代碼來負責讀取解密, 如果要完整的跟完一次解密過程會特別累,既然是 Python 代碼來負責讀取解密, 那我們就在 PyEval_EvalFrameEx 處下斷點來查看是什么文件在進行讀取解密,添加如下代碼:import idc  import idautils  f_code_addr =  idc.Dword(idautils.cpu.r0 + 16)  strobj_addr = (idc.Dword(f_code_addr + 48))  print('eval: ' + idc.GetString(strobj_addr + 20))  跑起來:這個時候我們就發現了一個可疑文件 redirect.py,這個 redirect.py 是什么時候加載的呢? 如果是從磁盤里讀取的話,前面在 open 處的 log 會記錄下來, 可是 open 處的 log 并沒有和 redirect.py 相關的信息, 所以陰陽師要么用了一種比較奇怪的方式在磁盤內讀取了這個文件,導致我們沒有記錄到, 要么就是在內部直接創建了這個 redirect 模塊,我們先不要把情況想得太復雜, 還是先查看一下這個 redirect.py 模塊。Appcall想要查看 redirect.py 模塊,我們就需要 IDA Appcall ,Appcall 可以在調試的過程中,在當前程序執行環境下執行程序內、某個我們指定的函數, 所以使用 Appcall 調用 PyRun_SimpleString我們就可以查看 Python 程序當前運行時的內部信息。在這里,我們查看一下所有已經加載的 Python 模塊信息:信息太多了,我們只看 redirect 模塊信息:發現這個 redirect 既不是內置模塊,也不是 frozen 模塊,是一個純 Python 模塊,所以我們要找到什么時候創建了這個模塊。尋找 redirect 的創建點我們在 PyImport_ImportModule、PyImport_ImportFrozenModule 處下斷點,繼續打 log 重新跑一次:然而并沒有 import redirect,這個時候我們就只能用土方法,在 Py_Initialize 函數執行結束之后, 通過單步跟蹤以及不斷的使用 Appcall 查看 redirect 模塊是否被創建, 最終確定了創建 redirect 模塊的函數 sub_AD109C,這個函數正在 Py_Intialize 調用處的下方:我們再仔細看看 sub_AD109C 函數的實現:到這里我們可以很清楚的看到創建 redirect 模塊的過程:
  • PyMarshal_ReadObjectFromString 從程序某處讀取 marshal 格式的字符串
  • PyImport_ExecCodeModule 創建 redirect 模塊
原來陰陽師是直接用 marshal 格式字符串直接創建一個模塊,之前我們還沒意識到有 PyImport_ExecCodeModule 這么一個函數, 現在我們直接給 PyMarshal_ReadObjectFromString 下斷點:終于拿到了 redirect.py 的 marshal 格式字符串了。0x04 查看 redirect拿到了 redirect 模塊 marshal 格式的字符串之后,添加一個 py27 的 header: \x03\xf3\x0d\x0a\x00\x00\x00\x00, 然后直接使用 uncompyle2 反編譯試試看:oops,報錯了,我們看一下這個 pyc 文件內部情況:常量表,文件名都正常,再看一下 opcode:又報錯了,我們仔細觀察一下這些 opcode,會發現這些 opcode 并不是一個正常 Python 文件會產生的 opcode, 153 沒有對應 opcode name,EXTENDED_ARG 這個 opcode 只有傳遞參數超過 65535 個的時候才會出現。 所以革命尚未成功,同志任需努力。0x05 opcode 映射關系看起來陰陽師好像是直接修改 Python opcode 映射關系,也就是原本 opcode 為 1 的時候代表的是 POP_TOP, 但是在這里被修改成 ROT_THREE 或者其他,想要解密 redirect.pyc,就必須拿到修改后的 opcode 映射關系。想要拿到修改后的 opcode 映射關系,可以選擇慢慢看 python 那個巨大的 switch,分析每個 opcode 對應的代碼, 不過這樣的方法太累人了,我們要換一種更簡單的思路:
  • 使用陰陽師的 Python 來獲取 一個 python script 的 pyc
  • 使用正常 Python 來獲取 一個 python sctipt 的 pyc
  • 對比兩個 pyc,拿到部分修改后的 opcode 映射關系,然后重復 1、2 兩步直到 opcode 映射關系完整
獲取 pyc想要使用陰陽師的 Python 來執行一段代碼,我們就必須在 PC 上交叉編譯一個程序來調用 libclient.so 中 Python。代碼如下:/** * arm-linux-androideabi-gcc test.c -o test -ldl -pie * export LD_LIBRARY_PATH=./ * cp test /path/to/libclient.so */#include <stdio.h>#include <dlfcn.h>int main(int argc, char *argv[]){      void (*Py_Initialize)();    void (*PyRun_SimpleString)(char *);    void (*Py_Finalize)();    if (argc < 2) {        printf("Usage: %s script.py\n", argv[0]);        return;    }    FILE *fp = fopen(argv[1], "rb");    fseek(fp, 0, SEEK_END);    int file_len = ftell(fp);    char *buf = (char *)malloc(file_len + 1);    fseek(fp, 0, SEEK_SET);    fread(buf, file_len, 1, fp);    buf[file_len] = 0;    void *libm_handle = dlopen("libclient.so", RTLD_LAZY );    if (!libm_handle){        printf("Open Error:%s.\n", dlerror());        return 0;    }    Py_Initialize = dlsym(libm_handle, "Py_Initialize");    Py_Initialize();    PyRun_SimpleString = dlsym(libm_handle, "PyRun_SimpleString");    PyRun_SimpleString(buf);    Py_Finalize = dlsym(libm_handle, "Py_Finalize");    Py_Finalize();    dlclose(libm_handle);    free((void *)buf);    return 0;}將交叉編譯后的程序 test 放到和 libclient.so 同一個目錄下。除了上面的方法外,我們還可以繼續使用 IDA Appcall, 但是經常時靈時不靈:遇到這種情況,差不多只能重新開始了,所以最好還是直接使用之前交叉編譯的程序。現在為了更快的拿到所有 opcode 的映射關系,我直接寫了一份使用了所有 Python 2.7 opcode的文件: py27opcode.py因為 from __future__ import division 會影響 division, 要么一個文件的 divide 全是 true divide,要么全是正常的 divide,不能共存。所以要使用陰陽師的 Python dump 兩份 pyc,普通 Python dump 兩份 pyc。獲取 pyc 腳本代碼如下:import marshal  infile = 'py27opcode.py'  outfile = 'android_py27opcode.pyc'  content = open(infile).read()  out_fd = open(outfile, 'wb')  cobj = compile(content, '', 'exec')  marshal.dump(cobj, out_fd)  out_fd.close()  用我們在 Android 上使用之前交叉編譯后的程序運行上面的代碼,得到 android_py27opcode.pyc, 刪除 division 的注釋后再運行一次,得到 android_py27opcode1.pyc,在普通環境下, 使用沒修改過的 Python 2.7.3 再執行相同操作,分別得到 normal_py27opcode.pyc 和 normal_py27opcode1.pyc對比 pyc 文件直接寫代碼,對比兩組 pyc 的 opcode,代碼如下:import sys  import marshal  opmap = {}  def compare(cobj1, cobj2):      codestr1 = bytearray(cobj1.co_code)    codestr2 = bytearray(cobj2.co_code)    if len(codestr1) != len(codestr2):        print("two cobj has different length, skipping")        return    i = 0    while i < len(codestr1):        if codestr1 not in opmap:            opmap[codestr1] = codestr2        else:            if opmap[codestr1] != codestr2:                print("error: has wrong opcode")                break        if codestr1 < 90 and codestr2 < 90:            i += 1        elif codestr1 >= 90 and codestr2 >= 90:            i += 3        else:            print("wrong opcode")    for const1, const2 in zip(cobj1.co_consts, cobj2.co_consts):        if hasattr(const1, 'co_code') and hasattr(const2, 'co_code'):            compare(const1, const2)def usage():      print("Usage: %s filename1.pyc filename2.pyc")def main():      if len(sys.argv) != 3:        usage()        return    cobj1 = marshal.loads(open(sys.argv[1]).read())    cobj2 = marshal.loads(open(sys.argv[2]).read())    compare(cobj1, cobj2)    print(opmap)if __name__ == '__main__':      main()將兩組結果進行合并拿到最終的一個 opcode 映射關系。糾正 opcode至此,我們已經有足夠的信息去修正 redirect.py 中錯位的 opcode,代碼如下:#! /usr/bin/env python# -*- coding: utf-8 -*-import os  import zlib  import rotor  import marshal  import binascii  import argparse  import pymarshal  class PYCEncryptor(object):      def __init__(self):        self.opcode_encrypt_map = {            1: 38, 2: 46, 3: 37, 4: 66, 5: 12, 10: 35, 11: 67, 12: 81, 13: 32, 15: 9, 19: 63, 20: 70,            21: 44, 22: 36, 23: 39, 24: 57, 25: 10, 26: 52, 28: 49, 30: 86, 31: 87, 32: 88, 33: 89,            40: 24, 41: 25, 42: 26, 43: 27, 50: 14, 51: 15, 52: 16, 53: 17, 54: 8, 55: 21, 56: 55,            57: 82, 58: 34, 59: 22, 60: 65, 61: 6, 62: 58, 63: 71, 64: 43, 65: 30, 66: 19, 67: 5,            68: 60, 71: 53, 72: 42, 73: 3, 74: 48, 75: 84, 76: 77, 77: 78, 78: 85, 79: 47, 80: 51,            81: 54, 82: 50, 83: 83, 84: 74, 85: 64, 86: 31, 87: 72, 88: 45, 89: 33, 90: 145, 91: 159,            92: 125, 93: 149, 94: 157, 95: 132, 96: 95, 97: 113, 98: 111, 99: 138, 100: 153, 101: 101,            102: 135, 103: 90, 104: 99, 105: 151, 106: 96, 107: 114, 108: 134, 109: 116, 110: 156,            111: 105, 112: 130, 113: 137, 114: 148, 115: 172, 116: 155, 119: 103, 120: 158, 121: 128,            122: 110, 124: 97, 125: 104, 126: 118, 130: 93, 131: 131, 132: 136, 133: 115, 134: 100, 135: 120,            136: 129, 137: 102, 140: 140, 141: 141, 142: 142, 143: 94, 146: 109, 147: 123        }        self.opcode_decrypt_map = {self.opcode_encrypt_map[key]: key for key in self.opcode_encrypt_map}        self.pyc27_header = "\x03\xf3\x0d\x0a\x00\x00\x00\x00"    def _decrypt_file(self, filename):        os.path.splitext(filename)        content = open(filename).read()        try:            m = pymarshal.loads(content)        except:            try:                m = marshal.loads(content)            except Exception as e:                print("[!] error: %s" % str(e))                return None        return m.co_filename.replace('\\', '/'), pymarshal.dumps(m, self.opcode_decrypt_map)    def decrypt_file(self, input_file, output_file=None):        result = self._decrypt_file(input_file)        if not result:            return        pyc_filename, pyc_content = result        if not output_file:            output_file = os.path.basename(pyc_filename) + '.pyc'        with open(output_file, 'wb') as fd:            fd.write(self.pyc27_header + pyc_content)def main():      parser = argparse.ArgumentParser(description='onmyoji py decrypt tool')    parser.add_argument("INPUT_NAME", help='input file')    parser.add_argument("OUTPUT_NAME", help='output file')    args = parser.parse_args()    encryptor = PYCEncryptor()    encryptor.decrypt_file(args.INPUT_NAME, args.OUTPUT_NAME)if __name__ == '__main__':      main()最終結果這次我們直接用 uncompyle2 反編譯:終于成功解出 redirect.py 文件了,根據這個 redirect.py 給出的信息,我們就可以拿到解密 script.npk 的方法:
  • 按照之前分析 script.npk 的方法,事先將每個加密后的 python 文件分割出來
  • 按照 redirect.py 里面的加密方法,寫出解密過程
  • 按照之前方法,再修正每個 python 腳本的 opcode 映射關系
至此逆向代碼的工作終于完成了。0x05 IDAPython記錄一下在分析過程中使用的 script:嘗試自動化因為要經常重開陰陽師,但是每次重新調試都需要我手動重復暫停繼續等待加載 libclient.so,所以我寫了這么一個 IDAPython Script:from idc import *  from idaapi import *  from idautils import *  bt_cond = """  filename = GetString(cpu.r0)  print("loading: %s" % filename)  if not filename:      return Trueif filename == "libclient.so":      return True"""add_bpt(LocByName('__dl__ZL17soinfo_link_imageP6soinfoPK17android_dlextinfo'), 0, BPT_SOFT)  enable_bpt(LocByName('__dl__ZL17soinfo_link_imageP6soinfoPK17android_dlextinfo'), True)  SetBptCnd(LocByName('__dl__ZL17soinfo_link_imageP6soinfoPK17android_dlextinfo'), bt_cond)看著是沒什么問題,但是有時候 GetString(cpu.r0) 返回一個 idaapi.BADADDR,所以 IDA 暫停了, 但是暫停的時候去查看這個地址的內容卻發現是正常數據,并不是 idaapi.BADADDR,這個情況我并沒有去解決,后面還是老老實實手動。2. 嘗試分析函數當一些斷點斷下來的時候,在動態調試的窗口只能看到運行指令以及之后的幾條指令,雖然說也可以一直按 C 鍵, 但是總這樣也挺不方便的,所以我將靜態調試的 IDA 中text 段的函數地址全部導出到 d:\\ida.txt 中:import json  funclist = []  for seg_ea in Segments():      if SegName(seg_ea) != '.text':        continue    for function_ea in Functions(SegStart(seg_ea), SegEnd(seg_ea)):        funclist.append(function_ea)py_init = LocByName('Py_Initialize')  funclist.insert(0, py_init)  open('d:\\ida.txt', 'w').write(json.dumps(funclist))  print('done')  然后再將 d:\\ida.txt 的內容再導入到動態調試的 IDA 中:import json  funclist = json.loads(open('d:\\ida.txt').read())  daynamic_py_init = LocByName('Py_Initialize')  static_py_init = funclist.pop(0)  offset = daynamic_py_init - static_py_init  for function_ea in funclist:      MakeFunction(function_ea + offset)print('done')  然而因為 libclient.so 中的 text 段有 1w 多個函數,導入的時候 MakeFunction 實在太慢了, 大概要等五分鐘才好,用了幾次之后我就放棄了這樣的方法。3. 顯示調用堆棧不知道為什么我的 Call Stack 一直顯示任何東西,不確定是手機的問題還是 IDA 的問題,還是這個 App 的問題,折騰這問題感覺很麻煩。 因為 fp 寄存器還保存著程序的返回地址,所以還是直接寫個 IDAPython Script 打印出調用堆棧比較方便的(如果 fp 不能用, 可以參考 An attempt to reconstruct the call stack):import idaapi, idautils  static_py_init = 0x1285398  dynamic_py_init = LocByName('Py_Initialize')  offset = dynamic_py_init - static_py_init  f_fp = idautils.cpu.fp  f_pc = 0  i = 0  while i < 100:      i += 1    f_pc = Dword(f_fp)    f_fp = Dword(f_fp-4)    if f_fp == idaapi.BADADDR:        break    print("%s %s" % (hex(f_pc), hex(f_pc - offset)))print('===============================')  0x06 簡單寫個掛看了代碼之后,發現抽卡的爆率不在本地,但是百鬼夜行的碎片掉率是在本地計算的,簡單看一下相關代碼片段:# scenemembers/GhostWalkScene.pyclass GhostWalkScene(GameScene):      # 省略 ...    def CheckFairGhostIsHit(self, ghostID, model):        return True  # modify by fate0        modelIndex = self.FairModelIndexDict[model]        # 省略 ...        data = random.randint(1, 100)        if data <= int(hitRate * 100):            return True        else:            return False    def CheckEffectGhostIsHit(self, id):        return True  # modify by fate0        rate = float(GhostWalkFairData.data[int(id)]['rate'])        # 省略 ...        if randInt <= int(rate * 100.0):            return True        else:            return False    def FireBean(self, offsetX, offsetY):        helpers.createModelAsync('model/douzi/douzi.gim', self.FireBeanCallback, (0.3,         offsetX,         offsetY,         self.PlayerModel))        self.TotalBeanNum = self.TotalBeanNum + 1  # modify by fate0        # 省略 ...
  • CheckFairGhostIsHit: 用來檢查走過的式神是否被擊中
  • CheckEffectGhostIsHit: 用來檢查飛過的狀態是否被擊中
  • FireBean: 開火
所以讓 CheckFairGhostIsHit 和 CheckEffectGhostIsHit 這兩個方法返回 True 就可以實現百分百命中, 將 self.TotalBeanNum = self.TotalBeanNum - 1 修改成 self.TotalBeanNum = self.TotalBeanNum + 1 就可以實現無限福豆。

免費評分

參與人數 13吾愛幣 +11 熱心值 +13 收起 理由
不拍電影不掉淚 + 1 我很贊同!
夢想0330 + 1 + 1 我很贊同!
lookerJ + 1 看不見圖
瀲凌努力變優秀 + 1 + 1 [email protected]
三木零 + 1 + 1 用心討論,共獲提升!
Fuung + 1 + 1 我很贊同!
夢幻嘟嘟 + 1 + 1 實在是太強了!!!!留眼!!
gaosld + 1 + 1 用心討論,共獲提升!
asongElson + 1 + 1 熱心回復!
丶咖啡貓丶 + 1 + 1 熱心回復!
bxin4444 + 1 + 1 [email protected]
hj592207079 + 1 + 1 [email protected]
nasa2001 + 1 + 1 [email protected]

查看全部評分

發帖前要善用論壇搜索功能,那里可能會有你要找的答案或者已經有人發布過相同內容了,請勿重復發帖。

推薦
yicx01 發表于 2019-10-23 07:52
這排版?能不能排好點再發啊,大哥
推薦
愛玩灬 發表于 2019-10-23 15:17
本帖最后由 愛玩灬 于 2019-10-23 15:20 編輯

清標明來源:static.fatezero.org(原帖已被刪除)=>  展示視頻: http://static.fatezero.org/blog/video/decrypt-onmyoji/demo.mp4
這個在三年前已經被人發現了(大約16年底),之后廣泛活躍在淘寶代砸百鬼等平臺,但是18年已經修復,所以說這個教程已經沒什么用了,但是用來學習還是不錯的
4#
Tembo 發表于 2019-10-23 02:15
5#
大大君233 發表于 2019-10-23 07:21
不錯 不錯
6#
Aaron0713 發表于 2019-10-23 08:06
轉貼也注意一下排版啊
7#
hanlaojia 發表于 2019-10-23 08:08
路過,路過,路過,路過,路過,
8#
囂張明仔 發表于 2019-10-23 08:27
半年前就看過這個了。也沒見有人做出實質性的百鬼掛。
9#
TinyCornX 發表于 2019-10-23 08:41
轉載請標明出處,原po是17年就發出來了的~
10#
wushuai514 發表于 2019-10-23 08:54
路過,路過,路過,路過,路過,
11#
fergus1987 發表于 2019-10-23 08:56
有啟發 謝謝樓主分享!
您需要登錄后才可以回帖 登錄 | 注冊[Register]

本版積分規則 警告:禁止回復與主題無關內容,違者重罰!

快速回復 收藏帖子 返回列表 搜索

RSS訂閱|小黑屋|聯系我們|吾愛破解 - LCG - LSG ( 京ICP備16042023號 | 京公網安備 11010502030087號 )

GMT+8, 2019-12-9 10:06

Powered by Discuz!

© 2001-2017 Comsenz Inc.

快速回復 返回頂部 返回列表
新快赢481走势图200