LIXXX壳 脱壳还原步骤
本文最后更新于 831 天前,其中的信息可能已经有所发展或是发生改变。

年初的时候搞的一个游戏,一直陆陆续续搞,弄了很久,那个时候还没离职,现在换了新公司之后,专心搞逆向,就把这个游戏找出来,练习一下。

游戏原版样本链接在这

https://share.weiyun.com/0DVjK9xK

是一个比较好玩的单机游戏,当时找的时候是 google 商店 单机排行第一。


简单尝试

当时的需求已经忘记了,只记得这个游戏有个壳,是没见过的海外壳,有点恶心人。这次就尝试脱壳还原。

拿到 apk 直接重签名安装,会无法进入游戏界面

很明显是有一个签名校验的,然后打开 进入 manifest 看看,application 明显是一个 壳入口,不是真实的游戏 application,到这里就可以分析出 游戏加壳了。

想从 java 层看看逻辑,结果发现 java 的字符串全部被加密了,写了一个解密脚本

import java.io.*;
import java.util.*;

public class LiAPPAna {


    public static void write(String path, List<String> list) {
        try {
            BufferedWriter out = new BufferedWriter(new FileWriter(path));
            for (String s : list) {
                out.write(s);
                out.write("\n");
            }
            out.close();

        } catch (IOException e) {
        }
    }

    public static List<String> readTxtFileIntoStringArrList(String filePath) {
        List<String> list = new ArrayList<String>();
        try {
            String encoding = "UTF-8";
            File file = new File(filePath);
            if (file.isFile() && file.exists()) { // 判断文件是否存在
                InputStreamReader read = new InputStreamReader(
                        new FileInputStream(file), encoding);// 考虑到编码格式
                BufferedReader bufferedReader = new BufferedReader(read);
                String lineTxt = null;

                while ((lineTxt = bufferedReader.readLine()) != null) {
                    list.add(lineTxt);
                }
                bufferedReader.close();
                read.close();
            } else {
                System.out.println("找不到指定的文件");
            }
        } catch (Exception e) {
            System.out.println("读取文件内容出错");
            e.printStackTrace();
        }
        return list;
    }

    public static void readFile(String path) {
        List<String> list = readTxtFileIntoStringArrList(path);
        for (int i = 0; i < list.size(); i++) {
            String line = list.get(i);
            if (line.contains("mmmmmmmm")) {
                String keyLine = list.get(i - 4);
                String valueLine = list.get(i - 2);

                if (valueLine.isEmpty()) {
                    System.out.println(path);
                    System.out.println(i);
                    return;
                }

                if (keyLine.isEmpty()) {
                    System.out.println(path);
                    System.out.println(i);
                    return;
                }
                list.set(i - 4, getTargetFromLine(line, keyLine, valueLine));
            }

        }
//        for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
////            System.out.println(entry.getKey());
////            System.out.println(entry.getValue());
//            String a = entry.getKey();
//            int b = entry.getValue();
//            String c = decryption(a, b);
//            String resultKey = Integer.toHexString(b);
//            result.put(a, c);
//        }
//
//
//        for (Map.Entry<String, String> entry : result.entrySet()) {
//            System.out.println(entry.getKey());
//            System.out.println(entry.getValue());
//            int i = 0;
//            for (String line : list) {
////            getTargetFromLine(line);
//                if (line.contains(entry.getKey()) && line.contains("jumbo")) {
//                    String result = entry.getValue();
//                    String temp = line.replace(entry.getKey(), strToUnicode(result, true)) + "#" + entry.getKey();
//                    System.out.println(temp);
//                    list.set(i, temp);
//                }
//                i++;
//            }
//        }
//        //修改后的文件
        String fileName = new File(path).getName();
        String resultOut = "/Users/licuisong/Downloads/dec_out/" + fileName;
        write(resultOut, list);


    }

    static Map<String, Integer> hashMap = new HashMap();
    static String key = "";
    static int value = -1;


    private static void getKey(String line) {
        String[] res = line.split("\"");
        if (res.length > 2) {
            for (int i = 2; i < res.length; i++) {
                res[1] = res[1] + res[i];
            }
        }
        key = res[1];

    }

    private static void getValue(String line) {
        String prex = "0x";
        String[] res = line.split("0x");
        if (res[0].charAt(res[0].length() - 1) == '-') {
            prex = "-" + prex;
        }
        if (res[1].split(" ").length > 1) {
            res[1] = res[1].split(" ")[0];
        }
        String pre_data = prex + res[1];
        value = Integer.decode(pre_data);
    }

    private static String getTargetFromLine(String line, String keyLine, String valueLine) {

        //反查目标字符串
        getKey(keyLine);
        getValue(valueLine);


//
//        if (!key.isEmpty() && value != -1) {
//            String c = decryption(key, value);
//            String temp = keyLine.replace(key, strToUnicode(c, false));
//            return temp;
//        }
        return "";
    }

    public static List<String> getAllFile(String path) {
        ArrayList<String> files = new ArrayList<String>();
        File file = new File(path);
        File[] tempList = file.listFiles();

        for (int i = 0; i < tempList.length; i++) {
            if (tempList[i].isFile()) {
                if (tempList[i].toString().contains("$")) {
//                    continue;
                }
                System.out.println("文     件:" + tempList[i]);
                files.add(tempList[i].toString());
            }
            if (tempList[i].isDirectory()) {
//              System.out.println("文件夹:" + tempList[i]);
            }
        }
        return files;
    }

    static HashMap<String, String> result = new HashMap<>();


    public static void main(String... args) {

//
//        List<String> smaliFile = getAllFile("/Users/licuisong/Downloads/djtf_xx/smali/com/lockincomp/liapp/");
//        for (String s : smaliFile) {
//            System.out.println("=============");
//            System.out.println("=============");
//            System.out.println("=============");
//            System.out.println(s);
//            readFile(s);
//        }


//        String s = "a b";
//        System.out.println(s.split(" ")[0]);
//            const-string/jumbo v4, "\\\u00ca\u00141"
//
//    const v5, 0xfbcd19


        String argx1 = "\\ubf1b\\ubf17\\ubf16\\ubf16\\ubf1d\\ubf1b\\ubf0c\\ubf11\\ubf0e\\ubf11\\ubf0c\\ubf01";
        int argx2 = -0x55914088;
        for (int i = 0; i < 1000000; i++) {
            String s = decryption(argx1, argx2, i);
            if (s.contains("c")) {
                System.out.println(s);
            }
        }

//        connectivity

    }

    public static String decryption(String arg4, int arg5, int change) {

        arg4 = unicodeToStr(arg4);
//        System.out.println("decryption:  " + arg4);
//        System.out.println("decryption:  " + arg5);
        int v1 = 0;
        String v0 = "";
        if (arg5 != 0) {
            try {
                while (v1 < arg4.length()) {
                    v0 = String.valueOf(v0) + (((char) (LiAPPAna.util_char3(arg4.charAt(v1), ((char) (arg5 >> v1 % 4)), v1) ^ change)));
                    ++v1;
                }
            } catch (Exception v0_1) {
                v0 = null;
            }
        }
//        System.out.println(v0);
//        System.out.println("=============");
//        System.out.println("=============");
        return v0;
    }

    static char util_char_0(char arg1, int arg2) {
        return ((char) ((1 << arg2 & arg1) >> arg2));
    }

    static char util_char_1(char arg1, int arg2) {
        return ((char) (1 << arg2 | arg1));
    }

    static char util_char_2(char arg1, int arg2) {
        return ((char) ((1 << arg2 ^ -1) & arg1));
    }

    static char util_char3(char arg6, char arg7, int arg8) {
        char v0;
        int v1;
        int v5 = 8;
        if (arg8 % 3 == 0) {
            v1 = 0;
            v0 = arg6;
            while (v1 < v5) {
                if ((LiAPPAna.util_char_0(arg6, v1) ^ LiAPPAna.util_char_0(arg7, v1)) == 0) {
                    v0 = LiAPPAna.util_char_2(v0, v1);
                } else if ((LiAPPAna.util_char_0(arg6, v1) ^ LiAPPAna.util_char_0(arg7, v1)) == 1) {
                    v0 = LiAPPAna.util_char_1(v0, v1);
                }

                v1 += 2;
            }
        } else {
            if (arg8 % 3 == 1) {
                v1 = 1;
                v0 = arg6;
                while (true) {
                    if (v1 >= v5) {
                        return v0;
                    }

                    if ((LiAPPAna.util_char_0(arg6, v1) ^ LiAPPAna.util_char_0(arg7, v1)) == 0) {
                        v0 = LiAPPAna.util_char_2(v0, v1);
                    } else if ((LiAPPAna.util_char_0(arg6, v1) ^ LiAPPAna.util_char_0(arg7, v1)) == 1) {
                        v0 = LiAPPAna.util_char_1(v0, v1);
                    }

                    v1 += 2;
                }
            }

            if (arg8 % 3 == 2) {
                v1 = 0;
                v0 = arg6;
                while (true) {
                    if (v1 >= v5) {
                        return v0;
                    }

                    if ((LiAPPAna.util_char_0(arg6, v1) ^ LiAPPAna.util_char_0(arg7, v1)) == 0) {
                        v0 = LiAPPAna.util_char_2(v0, v1);
                    } else if ((LiAPPAna.util_char_0(arg6, v1) ^ LiAPPAna.util_char_0(arg7, v1)) == 1) {
                        v0 = LiAPPAna.util_char_1(v0, v1);
                    }

                    ++v1;
                }
            }

            v0 = arg6;
        }

        return v0;
    }


    /**
     * 字符串转 Unicode 编码
     *
     * @param string   原字符串
     * @param halfWith 是否转换半角字符
     * @return 编码后的字符串
     */
    public static String strToUnicode(String string, boolean halfWith) {
        if (string == null || string.isEmpty()) {
            // 传入字符串为空返回原内容
            return string;
        }

        StringBuilder value = new StringBuilder(string.length() << 3);
        String prefix = "\\u", zerofix = "0", unicode;
        char c;
        for (int i = 0, j; i < string.length(); i++) {
            c = string.charAt(i);
            if (!halfWith && c > 31 && c < 127) {
                // 不转换半角字符
                value.append(c);
                continue;
            }
            value.append(prefix);

            // 高 8 位
            j = c >>> 8;
            unicode = Integer.toHexString(j);
            if (unicode.length() == 1) {
                value.append(zerofix);
            }
            value.append(unicode);

            // 低 8 位
            j = c & 0xFF;
            unicode = Integer.toHexString(j);
            if (unicode.length() == 1) {
                value.append(zerofix);
            }
            value.append(unicode);
        }


        return value.toString();
    }


    /**
     * Unicode 编码转字符串
     *
     * @param string 支持 Unicode 编码和普通字符混合的字符串
     * @return 解码后的字符串
     */
    public static String unicodeToStr(String string) {
        String prefix = "\\u";
        if (string == null || string.indexOf(prefix) < 0) {
            // 传入字符串为空或不包含 Unicode 编码返回原内容
            return string;
        }

        StringBuilder value = new StringBuilder(string.length() >> 2);
        String[] strings = string.split("\\\\u");
        String hex, mix;
        char hexChar;
        int ascii, n;

        if (strings[0].length() > 0) {
            // 处理开头的普通字符串
            value.append(strings[0]);
        }

        try {
            for (int i = 1; i < strings.length; i++) {
                hex = strings[i];
                if (hex.length() > 3) {
                    mix = "";
                    if (hex.length() > 4) {
                        // 处理 Unicode 编码符号后面的普通字符串
                        mix = hex.substring(4, hex.length());
                    }
                    hex = hex.substring(0, 4);

                    try {
                        Integer.parseInt(hex, 16);
                    } catch (Exception e) {
                        // 不能将当前 16 进制字符串正常转换为 10 进制数字,拼接原内容后跳出
                        value.append(prefix).append(strings[i]);
                        continue;
                    }

                    ascii = 0;
                    for (int j = 0; j < hex.length(); j++) {
                        hexChar = hex.charAt(j);
                        // 将 Unicode 编码中的 16 进制数字逐个转为 10 进制
                        n = Integer.parseInt(String.valueOf(hexChar), 16);
                        // 转换为 ASCII 码
                        ascii += n * ((int) Math.pow(16, (hex.length() - j - 1)));
                    }

                    // 拼接解码内容
                    value.append((char) ascii).append(mix);
                } else {
                    // 不转换特殊长度的 Unicode 编码
                    value.append(prefix).append(hex);
                }
            }
        } catch (Exception e) {
            // Unicode 编码格式有误,解码失败
            return null;
        }

        return value.toString();
    }


}

用这个脚本可以解析 Java 层被加密的逻辑,它所有的函数调用都是通过反射,所以不解密字符串,根本看不了逻辑。

解密后发现逻辑都在 assets 中的 jar 和 sc 中,是一个抽取壳。

尝试脱壳

先上 frida 看看壳逻辑 ,直接 启动原游戏 frida -UF

遗憾,这个壳对 frida 有防御,换成 spawn 方式 注入,发现也是被检测

那就不能用 frida 来脱壳了,先尝试脱壳机,但是 脱壳机也被检测了特征,和 frida 一样,这就比较麻烦了。直接分析壳的话工作量太大了。

不过想了一下,这是国外的壳(https://liapp.lockincomp.com/),特征采集基本都是对比较出名的框架进行特征匹配。那我找一个国内比较小众的脱壳工具试试

https://github.com/CodingGay/BlackDex

成功脱壳,这个工具的作者我算是比较熟悉,微信上经常向他请教问题。非常 nice 的人

脱壳后调试

脱壳后发现 关键逻辑也被字符串加密了

加密方法是个 native 函数, smali 代码在下面

.class public La/a/al/a;
.super Ljava/lang/Object;
.source "a.java"


# direct methods
.method public constructor <init>()V
    .registers 1

    .prologue
    .line 10
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    return-void
.end method

.method public static mmmmmm(Ljava/lang/String;I)Ljava/lang/String;
    .registers 3
    .param p0, "s1"    # Ljava/lang/String;
    .param p1, "j"    # I

    .prologue
    .line 14
    invoke-static {p0, p1}, La/a/al/a;->mmmmmmm(Ljava/lang/String;I)Ljava/lang/String;

    move-result-object v0

    .line 16
    .local v0, "strRet":Ljava/lang/String;
    return-object v0
.end method

.method public static mmmmmmm(Ljava/lang/String;Ljava/lang/String;)I
    .registers 3
    .param p0, "tag"    # Ljava/lang/String;
    .param p1, "msg"    # Ljava/lang/String;

    .prologue
    .line 20
    const/4 v0, 0x1

    return v0
.end method

.method public static native mmmmmmm(Ljava/lang/String;I)Ljava/lang/String;
.end method

脱壳后的 dex 运行会报 so 没有链接的报错 UnsatisfieldLinkError,然而这个 so 是在壳里面的 动态解密出

来的,我是没有这个 so 的,也就是说我是没有用来解密的 native 函数的。

到这里又卡住了,因为游戏初始化的的核心逻辑都被字符串加密了,字符串是必须要还原的。但是字符串解密算法又在 so 里面。现在又不能用 frida 去 hook,一下就束手无策了。

新的方案

兜兜转转 还是找到了一个 过 frida 的工具

https://github.com/hzzheyang/strongR-frida-android/releases/tag/15.2.2

正好我这里用的也是 1522 版本的 frida ,那就直接推进去手机用就可以了。

尝试了一下非常好,原包已经检测不到 frida 了,那就可以为所欲为了,包括脱壳都可以用 frida 来进行。

那现在就要找到这个 native 函数了,因为 so 都加壳了,那就不能直接ida 分析

  1. 首先扫描全部 jni 函数
  2. 找到名字匹配 mmmmmm 的 native 函数的偏移
  3. 根据偏移 hook 函数入口
  4. 打印返回值
  5. 把 入参 和返回值打印成一个 java 语法的 map ,直接放进 smali 里面

frida js 代码如下

const STD_STRING_SIZE = 3 * Process.pointerSize;

class StdString {
    constructor() {
        this.handle = Memory.alloc(STD_STRING_SIZE);
    }

    dispose() {
        const [data, isTiny] = this._getData();
        if (!isTiny) {
            Java.api.$delete(data);
        }
    }

    disposeToString() {
        const result = this.toString();
        this.dispose();
        return result;
    }

    toString() {
        const [data] = this._getData();
        return data.readUtf8String();
    }

    _getData() {
        const str = this.handle;
        const isTiny = (str.readU8() & 1) === 0;
        const data = isTiny ? str.add(1) : str.add(2 * Process.pointerSize).readPointer();
        return [data, isTiny];
    }
}


function prettyMethod(method_id, withSignature) {
    const result = new StdString();
    Java.api['art::ArtMethod::PrettyMethod'](result, method_id, withSignature ? 1 : 0);
    return result.disposeToString();
}

function readStdString(str) {
    if ((str.readU8() & 1) === 1) { // size LSB (=1) indicates if it's a long string
        return str.add(2 * Process.pointerSize).readPointer().readUtf8String();
    }
    return str.add(1).readUtf8String();
}

var mmdoule
var isFinded = false
var liapp_dic = {}
var k,v
function attach(addr) {
    Interceptor.attach(addr, {
        onEnter: function (args) {
            this.arg0 = args[0]; // this
        },
        onLeave: function (retval) {
            var modulemap = new ModuleMap()
            modulemap.update()
            var module = modulemap.find(retval)
            // var string = Memory.alloc(0x100)
            // ArtMethod_PrettyMethod(string, this.arg0, 1)
            if(isFinded){
                return
            }
            if (module != null) {
                var mthod_name = prettyMethod(this.arg0, 1)
                console.log('<' + module.name + '> method_name =>',mthod_name,',offset=>', ptr(retval).sub(module.base), ',module_name=>', module.name)
                if(mthod_name.indexOf('mmmmmm')!=-1){// jni 函数特征
                    isFinded = true//找到了需要 hook 的函数
                    mmdoule = module
                   
                    //开始 hook 目标 jni 函数
                   Interceptor.attach(ptr(retval),{
                    onEnter:function(args){
                        // console.log("sss")
                       // console.log()
                       console.log(args[3])
                       k = args[3]
                    },
                    onLeave:function(retval){
                        var result = Java.vm.getEnv().getStringUtfChars(retval,null).readCString()
                        console.log("result==>",result)
                        v = result
                        liapp_dic[k] = v
                     }
                    })

                }
             

            } else {
                console.log('<anonymous> method_name =>', readStdString(string), ', addr =>', ptr(retval))
            }
        }
    });
}

function dic22file() {
    // body...
     var content = JSON.stringify(liapp_dic)

     console.log(content)
}
function hook_RegisterNative() {
    var libart = Process.findModuleByName('libart.so')
    var symbols = libart.enumerateSymbols()
    for (var i = 0; i < symbols.length; i++) {
        if (symbols[i].name.indexOf('RegisterNative') > -1 && symbols[i].name.indexOf('ArtMethod') > -1 && symbols[i].name.indexOf('RuntimeCallbacks') < 0) {
            //art::RuntimeCallbacks::RegisterNativeMethod(art::ArtMethod*, void const*, void**)
            attach(symbols[i].address)
        }
    }

}

function main() {
    hook_RegisterNative()
}

function dump_memory(base,size,name) {
    Java.perform(function () {
        var currentApplication = Java.use("android.app.ActivityThread").currentApplication();
        var dir = currentApplication.getApplicationContext().getFilesDir().getPath();
        var file_path = "/sdcard/" + name;
        // var file_path = "/data/data/com.stormx.castle/files/"+name
        var file_handle = new File(file_path, "wb");
        if (file_handle && file_handle != null) {
            Memory.protect(ptr(base),size, 'rwx');
            var libso_buffer = ptr(base).readByteArray(size);
            file_handle.write(libso_buffer);
            file_handle.flush();
            file_handle.close();
            console.log("[dump]:", file_path);
        }
    });
}


function dic2javamap() {
    // body...
   for (var key in liapp_dic) {
  var item = liapp_dic[key];

 var result = "map.put(\"" + key+"\"" +",\""+item + "\");\n"
console.log(result)

 }
}


setImmediate(main)

frida 启动游戏注入后,会 成功 hook 到,然后持续解密,解密逻辑我不关心,直接把 对应的表拷贝出来

放到 java 工程中 转成 smali ,替换脱壳后的 dex 中 的 a.smali ,成功完成 字符串解密逻辑,当调用解密字符串函数 的时候 ,会调用到我打表的 函数里面,java 逻辑如下

package a.a.al;

import android.util.Log;

import java.util.HashMap;
import java.util.Map;

public class a {
//    static String liapp_dic = "{\n" +
//            "    \"0x1267a03e\": \"FOREGROUND\",\n" +
//            "    \"0x19f5bfa3\": \"isSupportLowLatency:\",\n" +
//            "    \"0x1b90a2eb\": \"product=\",\n" +
//            "    \"0x1c38e36a\": \"title\",\n" +
//            "    \"0x1e5d4506\": \"SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION\",\n" +
//            "    \"0x1ecb133c\": \"alarm\",\n" +
//            "    \"0x232f7fe1\": \"message\",\n" +
//            "    \"0x23330095\": \"SYSTEM_UI_FLAG_LAYOUT_STABLE\",\n" +
//            "    \"0x26ae6740\": \"vibrator\",\n" +
//            "    \"0x28c2ef43\": \"message\",\n" +
//            "    \"0x28e35160\": \",\",\n" +
//            "    \"0x2c6de18a\": \"URL\",\n" +
//            "    \"0x2d056d0b\": \"notification_id\",\n" +
//            "    \"0x32e5a696\": \"alarm\",\n" +
//            "    \"0x33db7b52\": \"GT-I9100\",\n" +
//            "    \"0x361364d2\": \"SYSTEM_UI_FLAG_FULLSCREEN\",\n" +
//            "    \"0x393acb4d\": \"repeatInterval\",\n" +
//            "    \"0x3b798d68\": \"sensor\",\n" +
//            "    \"0x3c429fa8\": \"/\",\n" +
//            "    \"0x3dc97506\": \".obb\",\n" +
//            "    \"0x4950c86\": \"onWindowFocusChanged() hasFocus=\",\n" +
//            "    \"0x4a6b3c28\": \"\\n\",\n" +
//            "    \"0x4b1b7e15\": \"Accept-Encoding\",\n" +
//            "    \"0x53846b71\": \"getProperty\",\n" +
//            "    \"0x5621f975\": \"SYSTEM_UI_FLAG_HIDE_NAVIGATION\",\n" +
//            "    \"0x575d0ca9\": \"INSTALL_REFERRER\",\n" +
//            "    \"0x5da5e0f6\": \"repeatInterval\",\n" +
//            "    \"0x62b8cc73\": \"System.currentTimeMillis():\",\n" +
//            "    \"0x62e526c7\": \"SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN\",\n" +
//            "    \"0x6313dd9d\": \"notification\",\n" +
//            "    \"0x65ac92f8\": \"sampleRate: \",\n" +
//            "    \"0x6982f03d\": \"Cocos2dxPrefsFile\",\n" +
//            "    \"0x6e868dc3\": \"identity\",\n" +
//            "    \"0x747efb45\": \"Cocos2dxPrefsFile\",\n" +
//            "    \"0x7e4acbdd\": \"/Android/obb/\",\n" +
//            "    \"0x834e8007\": \"RETURNED_TO_FOREGROUND\",\n" +
//            "    \"0x846f33b0\": \"com.enhance.gameservice\",\n" +
//            "    \"0x848fe79a\": \"BACKGROUND\",\n" +
//            "    \"0x8f419bd8\": \"onBackPress\",\n" +
//            "    \"0x9072c7ca\": \"PROPERTY_OUTPUT_FRAMES_PER_BUFFER\",\n" +
//            "    \"0x951061ab\": \":\",\n" +
//            "    \"0x96d4012e\": \"onPause()\",\n" +
//            "    \"0x9a651812\": \"\\n\",\n" +
//            "    \"0xa0657936\": \"android.hardware.audio.low_latency\",\n" +
//            "    \"0xa2e8f806\": \",\",\n" +
//            "    \"0xa5be7a34\": \"onPause\",\n" +
//            "    \"0xa6f91b68\": \"/main.\",\n" +
//            "    \"0xa8710416\": \"getProperty\",\n" +
//            "    \"0xa8794ccf\": \"/\",\n" +
//            "    \"0xa97ed97d\": \"onResume()\",\n" +
//            "    \"0xa9ec5914\": \"PROPERTY_OUTPUT_SAMPLE_RATE\",\n" +
//            "    \"0xaeb11c82\": \"sdk\",\n" +
//            "    \"0xb042a019\": \"audio\",\n" +
//            "    \"0xb392da5a\": \"PUT\",\n" +
//            "    \"0xb9be22eb\": \"POST\",\n" +
//            "    \"0xbbdc1085\": \"model=\",\n" +
//            "    \"0xbc64b0dc\": \" input time:\",\n" +
//            "    \"0xbee58438\": \"notification_id\",\n" +
//            "    \"0xc9299ca4\": \"title\",\n" +
//            "    \"0xcfa4387c\": \"isEmulator=\",\n" +
//            "    \"0xd2f8a13f\": \"sdk_\",\n" +
//            "    \"0xd30cde09\": \"INSTALL_REFERRER\",\n" +
//            "    \"0xd37b1b62\": \".\",\n" +
//            "    \"0xdbc3cab2\": \"_sdk\",\n" +
//            "    \"0xdbe69384\": \"setSystemUiVisibility\",\n" +
//            "    \"0xdc299331\": \"window\",\n" +
//            "    \"0xe1e41da6\": \"URL\",\n" +
//            "    \"0xe752feb1\": \", framesPerBuffer: \",\n" +
//            "    \"0xea14440a\": \"onResume\",\n" +
//            "    \"0xeea521b0\": \"cancelLocalNotification\",\n" +
//            "    \"0xf83f716d\": \"android.app.lib_name\",\n" +
//            "    \"0xfaee6d31\": \"SYSTEM_UI_FLAG_IMMERSIVE_STICKY\"\n" +
//            "}";


    static boolean isDeal = false;
   static Map<String, String> map = new HashMap();
    public static String mmmmmm(String a, int b) {
        if (!isDeal) {
            map.put("0x848fe79a","BACKGROUND");

            map.put("0x834e8007","RETURNED_TO_FOREGROUND");

            map.put("0x1267a03e","FOREGROUND");

            map.put("0x1e5d4506","SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION");

            map.put("0x62e526c7","SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN");

            map.put("0x5621f975","SYSTEM_UI_FLAG_HIDE_NAVIGATION");

            map.put("0x361364d2","SYSTEM_UI_FLAG_FULLSCREEN");

            map.put("0xfaee6d31","SYSTEM_UI_FLAG_IMMERSIVE_STICKY");

            map.put("0x23330095","SYSTEM_UI_FLAG_LAYOUT_STABLE");

            map.put("0xdbe69384","setSystemUiVisibility");

            map.put("0xf83f716d","android.app.lib_name");

            map.put("0xa0657936","android.hardware.audio.low_latency");

            map.put("0x19f5bfa3","isSupportLowLatency:");

            map.put("0xb042a019","audio");

            map.put("0xa9ec5914","PROPERTY_OUTPUT_SAMPLE_RATE");

            map.put("0xa8710416","getProperty");

            map.put("0x9072c7ca","PROPERTY_OUTPUT_FRAMES_PER_BUFFER");

            map.put("0x53846b71","getProperty");

            map.put("0x65ac92f8","sampleRate: ");

            map.put("0xe752feb1",", framesPerBuffer: ");

            map.put("0x7e4acbdd","/Android/obb/");

            map.put("0xa6f91b68","/main.");

            map.put("0xd37b1b62",".");

            map.put("0x3dc97506",".obb");

            map.put("0x3b798d68","sensor");

            map.put("0xdc299331","window");

            map.put("0x33db7b52","GT-I9100");

            map.put("0x26ae6740","vibrator");

            map.put("0x846f33b0","com.enhance.gameservice");

            map.put("0xbbdc1085","model=");

            map.put("0x1b90a2eb","product=");

            map.put("0xaeb11c82","sdk");

            map.put("0xdbc3cab2","_sdk");

            map.put("0xd2f8a13f","sdk_");

            map.put("0xcfa4387c","isEmulator=");

            map.put("0x6313dd9d","notification");

            map.put("0xd30cde09","INSTALL_REFERRER");

            map.put("0xe1e41da6","URL");

            map.put("0xa97ed97d","onResume()");

            map.put("0x4950c86","onWindowFocusChanged() hasFocus=");

            map.put("0x4b1b7e15","Accept-Encoding");

            map.put("0x6e868dc3","identity");

            map.put("0xb9be22eb","POST");

            map.put("0xb392da5a","PUT");

            map.put("0x747efb45","Cocos2dxPrefsFile");

            map.put("0x575d0ca9","INSTALL_REFERRER");

            map.put("0x2c6de18a","URL");

            map.put("0xa2e8f806",",");

            map.put("0x9a651812","\n");
            map.put("0x951061ab",":");

            map.put("0x28e35160",",");

            map.put("0x4a6b3c28","\n");

                    map.put("0xa8794ccf","/");

            map.put("0xeea521b0","cancelLocalNotification");

            map.put("0xbee58438","notification_id");

            map.put("0xc9299ca4","title");

            map.put("0x232f7fe1","message");

            map.put("0x393acb4d","repeatInterval");

            map.put("0x32e5a696","alarm");

            map.put("0x3c429fa8","/");

            map.put("0x96d4012e","onPause()");

            map.put("0xa5be7a34","onPause");

            map.put("0x62b8cc73","System.currentTimeMillis():");

            map.put("0xbc64b0dc"," input time:");

            map.put("0x2d056d0b","notification_id");

            map.put("0x1c38e36a","title");

            map.put("0x28c2ef43","message");

            map.put("0x5da5e0f6","repeatInterval");

            map.put("0x1ecb133c","alarm");

            map.put("0x6982f03d","Cocos2dxPrefsFile");

            map.put("0xea14440a","onResume");
        }
//        hashMap.put("0xabcc", "123");

//        System.out.println("0x"+Integer.toHexString(b));
        String key = "0x" + Integer.toHexString(b);
        String result = map.get(key);
        if (result == null) {
            System.out.println("null");
//            Log.d("dec_in","result into == null:");
//            Log.d("dec_in", a);
//            Log.d("dec_in", "0x" + Integer.toHexString(b));
            return "";
        }
        return result;
    }

    public static void main(String... args) {
        mmmmmm("",0x834e8007);
    }
}

smali 文件替换后 重新编译签名安装,一切正常。

接下来就可以按需处理 gm 提权,无敌插件。之类的操作了,因为游戏太久远了,我已经不玩了,就有空再搞吧,我再看看最新的 单机是什么壳,挑战一下国外这些壳,其实蛮有意思的。

总的来说遇到的问题比较多,主要原因还是不会自定义系统,可以考虑针对这个壳专门开发一个脱壳镜像。

到这里,整个壳就被脱了下来。脱壳后的程序已经可以直接运行了。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇