發(fā)布日期:2026-01-15 23:54點擊次數(shù):193
背景
近朋友在玩幾個棋游戲,我花了點時間研究了一下,雖然破解失敗連云港預應力鋼絞線價格,但是過程感覺還是有收獲,分享給大
天津市瑞通預應力鋼絞線有限公司這篇文章主要分享 如何找key
破解思路后面有空再分享
cocos2dlua棋游戲的邏輯一般是使用lua編寫的,lua腳本加載一般是用 cocos2d 加載的,我的破解方向集中在這里,如果你要破解的棋游戲不是這個技術棧,那就不能照搬
lua腳本常見情況如下:
luac(編輯器打開腳本,前面幾個字節(jié)是lua) cocos2dlua(編輯器打開腳本,前面幾個字節(jié),不是lua,多打開幾個會發(fā)現(xiàn)是固定的)我這里遇到的就是 cocos2dlua (看一下lib文件有沒有叫類似名字的so)
cocos2dlua 加密原理給大簡單介紹一下
寫好lua腳本 使用key進行加密(加密算法叫 xxtea) 加密完成之后在前面拼接sign這個sign就是剛剛提到的固定字符串
解密步驟如下:
讀取腳本,切掉前綴,也就是sign 使用找到的key進行解密那么解密難點就是找key
key一般來說都在 libcocos2dlua.so里面, 有以下幾種玩法:
不修改 cocos2d 代碼,明文存儲 修改 cocos2d 代碼,明文存儲 修改 cocos2d 代碼,代碼計算得到 不修改 cocos2d 代碼,明文存儲這種簡單了,編輯器打開lua腳本,直接搜前綴,key就在附近
為什么在附近?因為源碼要求sign和key作為參數(shù)一起傳
bool AppDelegate::applicationDidFinishLaunching()
{
// set default FPS
Director::getInstance()->setAnimationInterval(1.0 / 60.0f);
// register lua module
auto engine = LuaEngine::getInstance();
ScriptEngineManager::getInstance()->setScriptEngine(engine);
lua_State* L = engine->getLuaStack()->getLuaState();
lua_module_register(L);
register_all_packages();
LuaStack* stack = engine->getLuaStack();
stack->setXXTEAKeyAndSign("2dxLua", strlen("2dxLua"), "XXTEA", strlen("XXTEA"));
上面的 2dxlua 就是sign連云港預應力鋼絞線價格,XXTEA 就是 key (加密算法也叫 xxtea)
源碼在 github
修改cocos2d 代碼,明文存儲不修改源碼,直接記事本打開就能找到key,所以一些開發(fā)者為了提升安全,修改了 cocos2d代碼,讓sign和key遠離(比如寫一個函數(shù)獲取key),增加破解難度
這里列舉幾個可能的變形點,依次在 ida里找就可能找到
以下均為 function
applicationDidFinishLaunching 入口點,默認sign和key在這里 setXXTEAKeyAndSign 如函數(shù)名所示,設置sign和key xxtea_decrypt 算法解密點,因為需要傳入key進行解密,按x看調(diào)用的地方,有key的蹤跡下面舉一個例子
010打開lua腳本
看到了一個前綴 ida分析 libcocos2dlua.so 搜索 applicationDidFinishLaunching,沒有看到明文
搜索 setXXTEAKeyAndSign,也沒有看到明文
搜索 xxtea_decrypt,出現(xiàn)兩個,有戲,因為一般是一個
依次按x看調(diào)用的地方,一個比較正經(jīng),是正常調(diào)用關系 二個是自定義的函數(shù),鋼絞線廠家進去看看
經(jīng)過分析源碼,得知 key 是 圖中的 v21 (這里就不貼明文了,避免被搜索到,哈哈)
到這里,key就找到了
修改 cocos2d 代碼,代碼計算得到另外一種變形就是key不是明文存的,是計算得到,下面舉一個例子說明
步驟類似 010打開
搜索 applicationDidFinishLaunching,看到明文了,但是有好幾個字符串,到底是哪一個呢?
上面出現(xiàn)好幾個字符串的那一行,實際調(diào)用的是函數(shù) setXXTEAKeyAndSign 搜索 setXXTEAKeyAndSign,
經(jīng)過閱讀代碼,慢慢扣偏移,后得知 key 是 v21 (上面的例子也是v21,這個只是巧合,不用在意)
當然了,這里也不用慢慢扣代碼,可以寫代碼梭哈,輸入4個字符串,前兩個用 0x2019%s%s 組合起來了,后面兩個也是一樣的組合方式,加起來6個可能,依次遍歷,看看到底是哪個就行
這里是梭哈的golang代碼
解密上面是找key的方法,找到之后就要解密了 薦自己寫,我找了一些成品,只能說差強人意
薦用golang寫,無需亂七八糟的依賴,寫完就運行 對golang不熟悉,也可以用 python,但是要依賴 特定版本 visual studio, 有多惡心,我就不多說了
以下是 golang代碼
package main
import (
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"time"
"github.com/xxtea/xxtea-go/xxtea"
)
type CMD struct {
encrypt_file string
decrypt_file string
}
var prefix string = "xxxx"
var lua_key string = "xxxx"
func main() {
cmd, r := initFlag()
if r {
return
}
if cmd.decrypt_file != "" {
err := decrypt(cmd.decrypt_file)
if err != nil {
fmt.Println(err)
}
return
}
if cmd.encrypt_file != "" {
err := encrypt(cmd.encrypt_file)
if err != nil {
fmt.Println(err)
}
return
}
fmt.Println("error, empty input")
}
func encrypt(path string) error {
by, err := os.ReadFile(path)
if err != nil {
fmt.Println("read fail", err)
return err
}
encrypt_data := xxtea.Encrypt(by, []byte(lua_key))
// 拼接前綴
data := append([]byte(prefix), encrypt_data...)
// fileInfo, err := os.Stat(path)
// if err != nil {
// return err
// }
name := filepath.Base(path)
x1 := name[:len(name)-len(filepath.Ext(name))]
newName := x1 + "_" + time.Now().Format("2006-01-02_15-04-05") + ".luac"
fmt.Println("output file: ", newName)
err = os.WriteFile(newName, data, 0666)
if err != nil {
fmt.Println("write fail")
}
return nil
}
func decrypt(path string) error {
by, err := ioutil.ReadFile(path)
if err != nil {
fmt.Println("read fail")
return err
}
// 去除前綴
data := by[len(prefix):]
decrypt_data := string(xxtea.Decrypt(data, []byte(lua_key)))
fmt.Println(decrypt_data)
return nil
}
func initFlag() (CMD, bool) {
var decrypt_file = flag.String("d", "", "解密path")
var encrypt_file = flag.String("e", "", "加密path")
var help = flag.Bool("h", false, "help")
flag.Parse()
if *help {
flag.Usage()
return CMD{}, true
}
if *decrypt_file == "" && *encrypt_file == "" {
flag.Usage()
return CMD{}, true
}
c := CMD{*encrypt_file, *decrypt_file}
return c, false
}
再貼一個python的(只有解密的,文件是寫死的,所以代碼行數(shù)少很多)
#coding: utf-8 # lua 解密單個文件 import os import sys import xxtea import logging import pathlib a = "xxxx" b = "0x201xxxx" def decrypt(filename): f = open(filename, mode='rb') data = f.read() data2 = data[len(a):] data3 = xxtea.decrypt(data2, b) return data3 inputName = r"X:\007-project\003-lua\xxx\main.lua" x = decrypt(inputName) print(x)frida
以上是通過 ida 分析得到key,比較花時間,也看運氣,因為可以修改的點太多了 這里再介紹一個釜底抽薪的方式
經(jīng)過上面的分析得知,只要開發(fā)者沒有喪心病狂的去修改 xxtea 算法,那么后key一定會傳到 xxtea_decrypt 里面,我們可以通過 frida 觀察入?yún)⒕湍艿玫?key
function xx() {
console.log("===============================================================");
var coco = Process.findModuleByName("libcocos2dlua.so");
var exports = coco.enumerateExports();
for(var i = 0; i < exports.length; i++) {
var name = exports[i].name;
// 不一定叫這個名字,匹配一下
if (name.indexOf("xxtea_decrypt")!=-1) {
console.log("name:", name);
console.log("exports[i]:", JSON.stringify(exports[i]));
var addr = exports[i].address;
Interceptor.attach(addr, {
onEnter: function (args) {
console.log('[+] args0,r0: ' + args[0]);//data數(shù)據(jù)
console.log('[+] args1,r1: ' + args[1]);//data長度
//密鑰
console.log('[+] args2,r2: ' + args[2]);
console.log(Memory.readCString(args[2]));
console.log('[+] args2,r3: ' + args[3]);//密鑰長度
} onLeave: function (retval) {
}
}
});
}
}
setImmediate(xx,0);
果
這里貼一下反編譯之后的果圖 連云港預應力鋼絞線價格
相關詞條:鋁皮保溫施工
六安預應力鋼絞線廠 創(chuàng)環(huán)保股價跑輸市場,公司業(yè)績斷崖式下滑,
連云港預應力鋼絞線價格 無懼特朗普炮轟!歐盟強硬表態(tài):不急于
連云港預應力鋼絞線價格 【風吹稻麥阡陌香】種田還省工 無人農(nóng)
連云港預應力鋼絞線價格 太會選!周末競彩日職賽事冷 周二選膽
宣城預應力鋼絞線廠 006期聶飛云快樂8預測獎號:大小比和0
德陽預應力鋼絞線廠 在里, 不要什么事都罵, 你只要記住這3
六安預應力鋼絞線廠 行業(yè)追蹤|港口航運市場(12月31日-1
連云港預應力鋼絞線價格 華邦健康擬再申請發(fā)行不10億元可交債
連云港預應力鋼絞線價格 世索科 Ryton PPS P-6
連云港預應力鋼絞線價格 百萬粉絲網(wǎng)紅拐賣婦女:頭銜并不代表人