吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 7524|回复: 38
收起左侧

[原创] Jadx + Javassist 逆向Github Copliot插件

  [复制链接]
初亦泽 发表于 2023-10-14 21:14
本帖最后由 初亦泽 于 2023-10-14 21:23 编辑

提前声明:由于GitHub copilot是服务端下发的代码补全,因此如果不内置合法账号,则无法通过逆向本地文件获取copilot代码补全能力,本文仅讨论本地插件的改造

1 、前言

最近GitHub copilot过期了,之前也未想过jetbrains插件的逆向。于是乎研究了一下GitHub copilot插件的的激活机制,遗憾的是最后发现GitHub copilot的提示都是云端下发的,本地的修改是不能通过服务端校验

不过也许会有人想对插件进行定制化改造,这时候本篇文章或许可以提供一些有用的参考

2、开发环境

Jadx:https://github.com/skylot/jadx/releases 下载最新版或者爱盘里的版本均可
Idea: 2023.2.3 自行寻找下载
GitHub copilot 下载地址:https://plugins.jetbrains.com/plugin/17718-github-copilot/versions

Javassist :3.29.2-GA  可以新建gradle工程从maven仓库引入依赖

3、分析

3.1 目标

我的目标是将copilot插件的状态mock为登录态

3.2 插件的结构

从插件官网下载插件到本地后,我们可以发现插件是个zip文件,目录结构之这样的
1.png

打开lib目录,我们发现都是由jar包组成的,于是乎插件的逆向就简化为对jar的逆向
2.png

3.3 定位关键点

由于是jar包的逆向,我们无法用x64DBG、OD这类工具进行调试和修改了,我们可以用Jadx打开jar包

打开后发现,插件没有混淆,通过搜索关键词 signed in 我们可以快速定位到关键类AuthStatusResult

3.png

4.png

AuthStatusResult中保存了账号信息,包含user和status信息,其内部枚举类Status更为清晰,我们只需要将AuthStatusResult获取status的方法返回枚举类型Status.Ok,将user返回一个非空字符串就能达到 mock登陆状态的目的

    [b]public [b]enum Status {
        Ok("OK"),
        MaybeOk("MaybeOK"),
        NotSignedIn("NotSignedIn"),
        NotAuthorized("NotAuthorized"),
        FailedToGetToken("FailedToGetToken"),
        TokenInvalid("TokenInvalid");
        [b]private [b]final String id;
        Status(String id) {
            [b]this.id = id;
        }
                ...
    }

3.4 修改关键类

我们无法用x64DBG和OD来修改jar包,因此我们需要专业的字节码操作库Javassist来实现字节码的修改,Javassist拥有方法体替换、构造函数替换等一系列能力

我们要做的就是用它把AuthStatusResult改造成我们想要的样子

我们的目标是 修改class文件,并将其打入修改后的jar包

3.4.1 新建工程

Javassist是一个第三方库,我们可以用Idea新建一个gradle 构建的Kotlin工程,调用相关的方法

5.png

3.4.2 包装工具类

首先包装一个工具,帮助我们更好的来修改class文件,下面代码的作用就是替换方法体和构造函数.

注意替换代码文本中如果涉及到类,需要类的全名,例如com.github.copilot.lang.agent.commands.AuthStatusResult,如果是内部类,则需要用 外部类全名$内部类名称,例如:com.github.copilot.lang.agent.commands.AuthStatusResult$Status

···java
fun modifyClass(
jarPath: String,
className: String,
patchClass: PatchClass
): PatchedClass {
ClassPool.getDefault().insertClassPath(jarPath)
val classToModify = ClassPool.getDefault().getCtClass(className)
patchClass.patchConstructors.forEach { data ->
val paramsTypes = data.parameterTypes.map {
ClassPool.getDefault().get(it)
}.toTypedArray()
var cont = try {
classToModify.getDeclaredConstructor(paramsTypes)
} catch (_: Exception) {
null
}
if (cont == null) {
cont = CtNewConstructor.make(paramsTypes, null, "{ ${data.body} }", classToModify) classToModify.addConstructor(cont) } else { cont.setBody("{${data.body} }")
}

    }
    patchClass.patchMethods.forEach { data ->
        val methodToModify = classToModify.getDeclaredMethod(data.name)
        methodToModify.setBody(data.body)
    }
    val exportClassFolder = File(jarPath).parent + File.separator + "crack"
    classToModify.writeFile(exportClassFolder)
    val path = exportClassFolder + File.separator + className.replace(".", File.separator) + ".class"
    return PatchedClass(className, path)
}

···

3.4.3 替换代码

调用工具类替换方法体

 fun mockLoginStatus(jarPath: String): PatchedClass {
        val className = "com.github.copilot.lang.agent.commands.AuthStatusResult"
        return ByteCodeAssist.modifyClass(
            jarPath = jarPath,
            className = className,
            patchClass = PatchClass(
                patchMethods = listOf(
                    PatchMethod("setStatus", "this.status = ${makeClassName(className, "Status")}.Ok;"),
                    PatchMethod(
                        "setUser"
                    ),
                    PatchMethod("setErrorMessage"),
                    PatchMethod("getErrorMessage", "return null;"),
                    PatchMethod("isSignedIn", "return true;"),
                    PatchMethod("isUnauthorized", "return false;"),
                    PatchMethod("getStatus", "return ${makeClassName(className, "Status")}.Ok;"),
                    PatchMethod(
                        "getUser", """
                        return "yize";
                    """.trimIndent()
                    ),
                    PatchMethod("isError", "return false;"),
                    PatchMethod(
                        "forFailedToGetToken", """
                        return new com.github.copilot.lang.agent.commands.AuthStatusResult(${
                            makeClassName(
                                className,
                                "Status"
                            )
                        }.Ok, "yize", null);
                    """.trimIndent()
                    ),
                    PatchMethod(
                        "forError", """
                        return new com.github.copilot.lang.agent.commands.AuthStatusResult(${
                            makeClassName(
                                className,
                                "Status"
                            )
                        }.Ok, "yize", null);
                    """.trimIndent()
                    )
                ),
                patchConstructors = listOf(
                    PatchConstructor(
                        parameterTypes = listOf(
                            makeClassName(className, "Status"),
                            String::class.java.name,
                            String::class.java.name
                        ),
                        body = """
                            this.user = "yize";
                            this.status = ${makeClassName(className, "Status")}.Ok;
                            this.errorMessage = null;
                        """.trimIndent()
                    ),
                    PatchConstructor(
                        body = """
                            this.user = "yize";
                            this.status = ${makeClassName(className, "Status")}.Ok;
                            this.errorMessage = null;
                        """.trimIndent()
                    )
                )
            )
        )
    }

3.4.4 将修改后的class文件打包进修改版的jar包

    fun packageClassToJar(originalJarPath: String, patchedList: List<PatchedClass>): String? {
        val newJarPath = "$originalJarPath.crack.jar"
        try {
            val entrySet = patchedList.map { it.entryName }.toSet()
            // Create a new JAR file
            val newJar = JarOutputStream(File(newJarPath).outputStream())

            // Copy entries from the original JAR to the new JAR
            ZipInputStream(File(originalJarPath).inputStream()).use { originalZip ->
                while (true) {
                    val entry = originalZip.nextEntry ?: break
                    if (!entrySet.contains(entry.name)) {
                        newJar.putNextEntry(ZipEntry(entry.name))
                        newJar.write(originalZip.readBytes())
                        newJar.closeEntry()
                    }

                }
            }

            // Add the new class to the new JAR
            patchedList.forEach { data ->
                newJar.putNextEntry(ZipEntry(data.entryName)) // 文件路径名,例如com/github/copilot/lang/agent/commands/AuthStatusResult.class
                File(data.classFilePath).inputStream().use { input ->
                    newJar.write(input.readBytes())
                }
            }

            newJar.closeEntry()
            // Close the new JAR file
            newJar.close()
        } catch (e: Exception) {
            return null
        }
        return newJarPath
    }

我们填一些必要信息,测试一下

9.png

然后用jadx打开我们修改后的jar文件,可以看到已经修改成功

6.png

我们再用修改后的jar包替换原有的core.jar,在Idea中选择从本地导入插件,重启后看idea底部的GitHub copliot图标已变为登陆状态

7.png

但是我们仍然无法拥有GitHub copilot的代码补全能力,因为代码补全完全是服务端通过jsonrpc下发的,除非有能通过服务端校验的正式会员信息。可以通过使用Javaassist修改,内置正确的账号信息来实现有实际代码补全能力的插件

8.png

4 结束

问 :为什么要给Javaassist封装一层调用接口呢?
答:实现自动化修改的打包,假如jar文件中有很多类需要修改,不封装的话一个一个的手动改太麻烦了

完整版工程代码已开源到GitHub,不止可以用于copilot插件,也可根据实际情况适当修改,用于其它Java程序。

https://github.com/bestyize/ByteMate

免费评分

参与人数 21吾爱币 +21 热心值 +17 收起 理由
liangzaiyi + 1 用心讨论,共获提升!
munachar + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
Miegarer + 1 用心讨论,共获提升!
thisCookie + 1 + 1 用心讨论,共获提升!
笙若 + 1 + 1 谢谢@Thanks!
_jiang_ + 1 + 1 我很赞同!
13473687360 + 1 我很赞同!
jklover + 1 + 1 用心讨论,共获提升!
sanmylc + 1 + 1 谢谢@Thanks!
xyl52p + 1 + 1 不错,干货!
fengbu401 + 1 我很赞同!
allspark + 1 + 1 用心讨论,共获提升!
天堂的风 + 1 + 1 谢谢@Thanks!
Kristin_ + 1 我很赞同!
wuxiaojie + 1 + 1 用心讨论,共获提升!
alentonly + 1 + 1 我很赞同!
smile1110 + 1 高高高高手
小朋友呢 + 2 + 1 我很赞同!
rolacn + 2 + 1 我很赞同!
chinawolf2000 + 1 + 1 热心回复!
woyucheng + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

lucklys 发表于 2023-10-15 10:31
淘宝上都是软件破解,不知道怎么弄得
 楼主| 初亦泽 发表于 2023-10-15 11:35
本帖最后由 初亦泽 于 2023-10-15 13:39 编辑

这个本质上也是内置账号,作者替换了copilot的token请求接口,返回正确的token
xiaoniu88 发表于 2023-10-14 21:35
这种肯定是服务端控制的了。没账号是没办法的··
mygrace 发表于 2023-10-14 21:59
厉害厉害,马上使用起来......
jinzhu160 发表于 2023-10-15 09:24
看完了也没懂,逆向以后,我是可以直接免费用vip功能吗?
猫子1992 发表于 2023-10-15 09:24
https://zhile.io/2023/09/09/github-got-banned.html#more-468  楼主试试这个 Github Copliot 插件破解工具

点评

这个本质上也是内置账号,copilot有一个免费的万能账号  详情 回复 发表于 2023-10-15 11:35
gagmeng 发表于 2023-10-16 09:13
初亦泽 发表于 2023-10-15 11:35
这个本质上也是内置账号,作者替换了copilot的token请求接口,返回正确的token

求万能账号
yasenhacker 发表于 2023-10-16 14:01
Javaassist   用这个修改类和方法的教程多发一些吧,,,上次修改成功了,可是某些方法体始终无法修改。。。最后通过一个字节码修改工具进行了修改,好像smali代码什么的,也是破解成功。。。。不过还是觉得Javaassist特别好,
fnckyon2014 发表于 2023-10-16 16:33
定制化改造是个好思路
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

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

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-28 02:07

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表