手游辅助开发_安卓手机游戏辅助开发教程 - CSDN
精华内容
参与话题
  • 如果解压密码不对,请删除后面日期重试或者www.lthack.com 解压密码:龙天论坛提供手游辅助开发-2018-1-25
  • 安卓逆向和手游辅助学习路线

    千次阅读 2020-05-29 21:06:21
    一.安卓逆向基础(建议1周) 学习安卓逆向第一步必须先把环境搭建好,这是你学习安卓逆向的开始,环境搭建好后表示正式迈入安卓逆向。在环境安装的工程中会遇到很多细节上的问题。 第二步就是要了解我们要分析的...

    一.安卓逆向基础(建议1周)

    1. 学习安卓逆向第一步必须先把环境搭建好,这是你学习安卓逆向的开始,环境搭建好后表示正式迈入安卓逆向。在环境安装的工程中会遇到很多细节上的问题。

    2. 第二步就是要了解我们要分析的是什么文件,很多0基础的同学都不知道安卓逆向分析的什么文件。我们要分析的是应用程序或者安装包(就是.apk文件),了解apk是怎么生成的以及如何安装到我们的手机里面,apk是怎么运行的,也是我们探讨的内容。

    3. 第三步如何逆向分析.apk文件,掌握apk反编译及回编译,完成这个操作使用的工具是apktool。

    一.Java层逆向(建议3周)

    1. 掌握Java语法基础,达到能看懂Java代码。

    2. 了解smali语法,能看懂smali代码。

    3. 掌握逆向分析apk中常用的方法和技巧。

    二.Native层逆向(建议4周)

    1. 了解安卓操作系统和四大组件。

    2. 了解NDK开发流程,自己编写案例练习。

    3. 掌握常用ARM汇编指令,达到能看懂ARM汇编指令。

    4. 掌握ida工具的使用,熟练使用ida进行各种操作

    三.APK保护策略(建议1周)

    1. 了解Java代码混淆、资源混淆

    2. 掌握签名验证、文件校验、模拟器检测

    3. 本地验证、网络验证

    4. 案例练习

    四.反调试与反-反调试(建议1周)

    1. 掌握常用反调试方法及过反调试技巧,比如关键文件检测、调试端口检测、进程名称检测、防附加、轮训检测TracerPid值、时间检测、信号检测等反调试。

    2. 掌握IDA过反调试思路

    3. 案例练习。

    五.HOOK框架(建议2周)

    1. 掌握HOOK插件开发。

    2. 掌握Xposed、Substrate、Fridad等框架。

    3. 案例练习。

    六.常见加密算法(建议2周)

    1. 掌握编码算法、消息摘要算法、对称加密算法(Java加密与解密的艺术)。

    2. 掌握非对称加密算法、数字签名算法。

    七.协议加解密分析(建议4周)

    1. 了解客户端与服务器如何进行交互的(OSI模型、TCP/IP模型)。

    2. 掌握常用的抓包工具及环境配置,HTTP协议与HTTPS安全协议,数字证书、SSL证书检测(计算机网络与通信、信息安全工程师)。

    3. 案例练习。

    八.文件结构(建议2周)

    1. 掌握DEX、ELF、XML、ARSC等文件结构。

    2. 自编写文件解析工具。

    九.系统源码分析(建议2周)

    1. 了解安卓操作系统启动流程、Zygote启动流程。

    2. 掌握Dalvik虚拟机、ART虚拟机、SO加载流程。

    十.加固与脱壳(建议4周)

    1. 了解dex文件整体加密、Dex代码抽取加密。

    2. 了解so文件整体加密、函数加密、区段加密、加壳、混淆。

    3. 分析通用脱壳机的实现原理及应用场景。

    4. 了解主流加固特点及对应的脱壳技巧。

    十一.按键+内存(建议2周)

    1. 环境搭建,搜索内存数据,对内存数据进行读写操作。

    2. 市面上的模拟器辅助,有一部分就是通过搜索内存数据来找特征码,因为游戏中有的数据是不会发生变化的,我们选择这部分不变的数据作为特征码。通过特征码来搜索内存数据,找到特征码的地址,再通过特征码的地址+距离(偏移)来实现定位。

    3. 案例练习。

    十二.篡改内存数据+注入技术+HOOK技术(建议4周)

    1. 掌握模块基地址获取、非注入式篡改数据、注入式篡改内存数据。

    2. 掌握注入技术原理(Ptrace注入、Zygote注入、静态感染ELF文件注入)。

    3. 掌握HOOK技术的实质,就是对函数进行重写(Inline HOOK、异常HOOK、导入表HOOK)。

    4. 掌握C++游戏逆向分析技巧。

    5. 案例练习。

    十三.Lua游戏(建议4周)

    1. 掌握Lua游戏逆向分析流程,第一步查看lib文件夹的so文件就可以确定该游戏是不是Lua游戏,第二步如果是Lua游戏就在assets文件夹下查找lua脚本。

    2. Lua游戏功能实现都在Lua脚本,重点分析Lua脚本(Lua明文、LuaC、Luajit)。

    3. Lua文件加密与加密,内存dump Lua脚本,HOOK插件开发。

    4. Cocos2dx-Lua引擎源码分析

    5. 案例练习

    十四.Unity 3D游戏(建议4周)

    1. 掌握Unity 3D游戏逆向分析流程,第一步查看lib文件下的so文件就可以确定该游戏是不是Unity 3D游戏,第二步如果是Unity 3D游戏就在assets文件下查找相应的文件。

    2. Unity 3D有两种框架(MONO框架、IL2CPP框架),MONO框架对应的游戏逻辑实现在dll文件,IL2CPP框架对应的游戏逻辑实现在libil2cpp.so文件。

    3. Unity 3D引擎源码分析。

    4. DLL文件处理,DLL混淆,DLL隐藏,DLL加密。

    5. 内存dump dll文件,HOOK插件开发,注入+HOOK。

    6. 案例练习

    十五.游戏协议(建议2周)

    1. WPE环境搭建,拦截发送包和接收包,多截包对比分析封包数据。

    2. 分析喊话功能,找出加密规律,各种游戏功能封包拦截分析。

    3. 案例练习。

    十六. 学习方法

    1. 看一遍教程后自己实战操作,养成做笔记的习惯。

    2. 注重基础,一定要把Java层和Native层搞懂。

    3. 学习中不要纠结细节,要学会抓大放小。

    4. 学完后自己画脑图,回顾学习的过程中那些不理解在花时间去看,此过程就是查漏补缺。

    展开全文
  • 1、为什么要写插件

    为什么要写插件

    当你项目很多的时候,build.gradle编写的脚本就会代码冗余了,很多项目都是写的一样的脚本,这个时候就需要编写个插件去管理了。只要在用到的项目引用这个插件,配置参数即可。

    新建gradle插件项目

    基于gradle 5.1.1,不同版本是有很多不一样的地方,如生产jar包或者aar,build目录结构就不一样,切记

    AndroidStudio新建项目RmPack,在RmPack下新建module:rmpack_plugin,src/main/下面的文件通通删了,然后在src/main/新建groovy文件夹,groovy下面就是放我们代码的地方,groovy新建包com.mrkzs.rmpack。跟一般的Android项目类似,我们先新建插件入口文件RmPack.groovy

    package com.mrkzs.rmpack
    
    class RmPack implements Plugin<Project> {
    	......
    	
    	 @Override
        void apply(Project project) {
    		......
    	}
    	
    	......
    }
    

    然后需要在src/main/目录下新建配置文件
    resources/META-INF/gradle-plugins/com.mrkzs.rmpack.properties
    这个文件名分为两部分,前半部分com.mrkzs.rmpack是自己定义的,后面使用这个插件的时候 apply plugin:‘com.mrkzs.rmpack’ 要对应上。
    里面的内容呢就是配置入口

    implementation-class=com.mrkzs.rmpack.RmPack
    

    在build.gradle设置打包上传配置,这里只配置打包到本地

    uploadArchives {
        repositories {
            mavenDeployer {
                pom.groupId = 'com.mrkzs.plugin'   //groupId
                pom.artifactId = 'rmpack'  //artifactId
                pom.version = '1.0.0' //版本号
                //本地的Maven地址设置为
                repository(url: uri('E:/zsdk_projects/gradle_plugin/rmpack/repo'))
            }
        }
    }
    

    运行上传
    命令行运行gradlew
    或者命令行 gradlew uploadArchives就会打包到仓库了
    至此项目构建结束,接下来看看SDK需要的业务

    插件功能

    笔者这个项目是在真实项目使用的,根据需求来编写插件
    整个项目结构
    在这里插入图片描述
    先在入口文件配置扩展参数、任务、任务依赖、某些任务执行时机
    入口文件RmPack.groovy代码

    package com.mrkzs.rmpack
    
    import com.mrkzs.rmpack.ext.*
    import org.gradle.api.Plugin
    import org.gradle.api.Project
    import org.gradle.api.Task
    
    class RmPack implements Plugin<Project> {
    
        private final String TAG = "RmPack "
        static final def EXTENSION_NAME = "rmPackArg"
        static final def EXTENSION_RM_LIB_PACK = "libPack"
        static final def EXTENSION_COMM_RES = "commRes"
        static final def EXTENSION_DEX_SPLIT = "dex"
        static final def EXTENSION_OUTPUT = "output"
        static final def EXTENSION_OUTPUT_AAR = "aar"
        static final def EXTENSION_OUTPUT_JAR = "jar"
        //任务组
        static final def TASK_GROUP_RMPACK = 'rmpack'
        static final def TASK_RM_PACK = 'rmPack'
        static final def TASK_COPY_COMM_RES = 'copyLibRes'
        static final def TASK_DEX_SPLIT_PACK = 'dexSplitPack'
    
        @Override
        void apply(Project project) {
            println(TAG + "plugin start")
            println(TAG + "plugin root dir " + project.getRootDir())
    
            //添加扩展参数
            def baseExt = project.extensions.create(EXTENSION_NAME, PluginExtension, project)
            def extCommRes = baseExt.extensions.create(EXTENSION_COMM_RES, CommResExtension, project)
            def extDex = baseExt.extensions.create(EXTENSION_DEX_SPLIT, DexTempSplitPackExtension, project)
            def extOutput = baseExt.extensions.create(EXTENSION_OUTPUT, OutputLibExtension, project)
            def extOutputAar = extOutput.extensions.create(EXTENSION_OUTPUT_AAR, OutputLibAarExtension, project)
            def extOutputJar = extOutput.extensions.create(EXTENSION_OUTPUT_JAR, OutputLibJarExtension, project)
    
            def rmTask = project.task(TASK_RM_PACK, group: TASK_GROUP_RMPACK, type: RmTask)
            def copyResTask = project.task(TASK_COPY_COMM_RES, group: TASK_GROUP_RMPACK, type: CopyResTask)
    
            //根据 module name 生成任务名
            def projectName = project.name
            def projectNameSuffix = projectName
            if (projectNameSuffix != null && projectNameSuffix.length() > 0) {
                if (projectNameSuffix.contains('_')) {
                    projectNameSuffix = projectNameSuffix.substring(projectNameSuffix.lastIndexOf('_') + 1)
                }
            }
            println "$TAG plugin projectNameSuffix ${projectNameSuffix}"
            //打包aar
            def aarTask = project.task("makeAar${projectNameSuffix}", group: TASK_GROUP_RMPACK, type: AARTask)
            def build = project.tasks.findByName("build")
            aarTask.dependsOn(build)
            aarTask.mustRunAfter(build)
    
            //打包所有aar
            def aarAllTask = project.task("makeAarAll", group: TASK_GROUP_RMPACK, type: AARTask)
            aarAllTask.dependsOn(build)
            aarAllTask.mustRunAfter(build)
            //打包jar,
            def jarTask = project.task("makeJar${projectNameSuffix}", group: TASK_GROUP_RMPACK, type: JARTask)
            jarTask.dependsOn(build)
            jarTask.mustRunAfter(build)
            //打包所有jar
            def jarAllTask = project.task("makeJarAll", group: TASK_GROUP_RMPACK, type: JARTask)
            jarAllTask.dependsOn(build)
            jarAllTask.mustRunAfter(build)
    
            //dex临时分包方案任务
            project.task(TASK_DEX_SPLIT_PACK, group: TASK_GROUP_RMPACK, type: DexTempSplitPackTask)
    
    
            boolean isTask
            project.tasks.whenTaskAdded { Task theTask ->
                println "plugin task " + theTask.name
                if (!isTask && (theTask.name == 'preDebugBuild' || theTask.name == "preBuild")) {
                    println "plugin task nnn " + theTask.name
                    isTask = true
    
                    // TODO 暂定在编译前执行任务,针对zsdk打包aar,后续会考虑根据需求手动执行以下任务
                    // TODO 因为配置了以下任务的必要参数,就会执行任务,当项目没有这个必要的时候,就有点多余
                    theTask.dependsOn(copyResTask)
                    theTask.mustRunAfter(copyResTask)
    
                    theTask.dependsOn(rmTask)
                    theTask.mustRunAfter(rmTask)
                }
            }
    
        }
    }
    
    
    

    我们需要的扩展参数根据你项目需要去配置,不需要的任务就不用配置对应参数了

    //扩展参数根
    rmPackArg {
        //RmTask任务需要的扩展,定义在PluginExtension 非必要扩展,非必要
        libPack = [
                {
                    //需要移除包的文件
                    packFile "libs/zsdklib.jar"
                    //需要移除的包
                    packName = [
                            'android.support',
                            'com.bun'
                    ]
                },
                {
                    packFile "libs/zsdklib.jar"
                    packName = [
                            'com.zui',
                            'com.meizu'
                    ]
                }
        ]
    
        //输出扩展OutputLibExtension
        output {
            //AARTask需要的扩展,定义在OutputLibAarExtension,暂时不会生效(后续可能会修改任务为DefaultTask类型,按需执行就可拿到参数), 非必要
            aar {
                fromDir 'build/outputs/aar/'
                fromName 'aaa-release.aar'
                targetDir 'build/libs'
                targetName 'bbb.aar'
            }
    
            //JARTask需要的扩展参数,定义在OutputLibJarExtension,暂时不会生效(后续可能会修改任务为DefaultTask类型,按需执行就可拿到参数), 非必要
            jar {
                fromJar '/build/intermediates/packaged-classes/release/classes.jar'
                targetDir '/build/libs'
                //生成目标名字不带后缀
                targetName 'aaa'
            }
        }
    
        //CopyResTask需要的扩展参数,定义在CommResExtension,非必要
        commRes {
            //公共资源目录
            resRoot '../res'
        }
    
        //DexTempSplitPackTask需要的扩展参数,定义在DexTempSplitPackExtension,非必要
        dex{
            //主apk
            mainApk '../apk/app_xiaoqi_20200507_release.apk'
            //dex apk
            dexApk '../apk/app_xiaoqi_dex_20200507_release.apk'
            //需要的转为主dex的
            mainLib '../apk/zsdklibrary_xiaoqi.aar'
        }
    }
    

    1、删除引入的jar包的某个文件夹下的文件

    可指定多个jar和jar里面多个文件夹,为什么有这个需求?因为我们游戏sdk经常会引入自己的jar或者渠道的jar,这个时候可能会有包的冲突,那么就要去掉冲突的包。

    package com.mrkzs.rmpack
    
    import com.mrkzs.rmpack.ext.PluginExtension
    import com.mrkzs.rmpack.utils.FileUtil
    import org.gradle.api.DefaultTask
    import org.gradle.api.tasks.TaskAction
    /**
     * Created by KINCAI
     * <p>
     * Desc TODO
     * <p>
     * Date 2020-03-16 15:27
     */
    class RmTask extends DefaultTask {
        def TAG = 'rmpack'
    
        @TaskAction
        void output() {
            println "rmpack task start"
            def rootExt = project.extensions.findByName(RmPack.EXTENSION_NAME) as PluginExtension
            if (!rootExt) {
                println 'rmpack root ext  null'
                return
            }
    
            def packList = rootExt.getProperty(RmPack.EXTENSION_RM_LIB_PACK);
            if (!packList) {
                println "$TAG pack list null"
                return
            }
            println "$TAG pack list $packList"
            packList.eachWithIndex { entry, i ->
                println "$TAG pack i=$i entry=$entry"
                def packFilePath = entry["packFile"]
                if (!packFilePath) {
                    println "rmpack task packFile path null"
                    return true
                }
                println "$TAG pack packFile $packFilePath"
                def packFile = project.file(packFilePath)
                println "$TAG pack file $packFile"
                if (!packFile.exists()) {
                    println("$TAG pack file no exists")
                    return true
                }
                def packNameList = entry["packName"]
                if (!packNameList) {
                    println "rmpack task packName list null"
                    return true
                }
                println "$TAG pack packName list size=${packNameList.size()}, list=$packNameList"
                packNameList.eachWithIndex { nameEntry, nameIndex ->
                    if (nameEntry) {
                        def split = nameEntry.split("\\.")
                        def packDir = ""
                        for (String p : split) {
                            packDir += (p + "/")
                        }
                        println "$TAG pack dir $packDir"
                        FileUtil.removeDirFromZipArchive(packFile, packDir, null)
                    } else {
                        println "$TAG pack pack name null or empty"
                    }
                }
    
            }
        }
    
    }
    
    

    2、copy公共资源和配置

    为什么有这个需求,因为笔者需要打包aar方便给出去包或者方便自己接入,但是这里有个问题,打包aar不会把依赖的module library和其他aar打包进去,那我们SDK的公共资源就需要每个渠道的module都要去拷贝了,当然这样子是不推荐的,这个时候我们就需要自己模拟一个library库存放这生成的核心jar和sdk资源、manifest文件等,在进行aar任务打包前把它们都拷贝到相应的module,并且AndroidManifset做合并处理。

    package com.mrkzs.rmpack
    
    import com.android.annotations.NonNull
    import com.android.manifmerger.ManifestMerger2
    import com.android.manifmerger.MergingReport
    import com.android.manifmerger.XmlDocument
    import com.android.utils.ILogger
    import com.google.common.base.Charsets
    import com.google.common.io.Files
    import com.mrkzs.rmpack.ext.CommResExtension
    import com.mrkzs.rmpack.ext.PluginExtension
    import com.mrkzs.rmpack.utils.FileUtil
    import org.gradle.api.DefaultTask
    import org.gradle.api.Project
    import org.gradle.api.tasks.TaskAction
    
    import java.nio.charset.StandardCharsets
    
    /**
     * Created by KINCAI
     * <p>
     * Desc 打包aar项目时,合并项目,因gradle未提供
     * 在build.gradle引入插件后,配置我们的参数即可
     * <p>
     * Date 2020-04-08 15:09
     */
    class CopyResTask extends DefaultTask {
        final static def TAG = 'copy res'
    
        @TaskAction
        void output() {
            //首先要拿扩展参数
            println "$TAG task start"
            def rootExt = project.extensions.findByName(RmPack.EXTENSION_NAME) as PluginExtension
            if (!rootExt) {
                println "$TAG root ext null"
                return
            }
            def ext = rootExt.getProperty(RmPack.EXTENSION_COMM_RES) as CommResExtension
            if (!ext) {
                println "$TAG ext null"
                return
            }
            def resRootDirStr = ext.resRoot
            if (!resRootDirStr) {
                println "$TAG res root dir path null"
                return
            }
    
            def resRootDir = project.file(resRootDirStr)
            if (resRootDir.exists()) {
                println "$TAG root dir $resRootDir"
                println "$TAG module dir $project.projectDir"
                //拷贝文件
                FileUtil.copy(resRootDir, project.projectDir)
                //依赖包中base AndroidManifest
                def baseManifestFile = project.file('/src/main/AndroidManifest_base.xml')
                if (!baseManifestFile.exists()) {
                    println "$TAG base manifest not exists"
                    return
                }
                //这个文件是接入时配置的,合并后也是这个文件
                def originManifest = project.file('/src/main/AndroidManifest.xml')
                if (!originManifest.exists()) {
                    println "$TAG origin manifest not exists"
                    return
                }
                //第一次这个文件是不存在的,需要从AndroidManifest.xml复制一份,实际上合并就是将这个复制后的文件和base进行合并
                //生成AndroidManifest.xml
                def copyManifest = project.file('/src/main/AndroidManifest_origin_copy.xml')
                if (!copyManifest.exists()) {
                    FileUtil.copy(originManifest, copyManifest)
                }
                def targetManifest = originManifest
    
                //先合并
                mergeManifest(project, targetManifest, baseManifestFile, copyManifest)
                println "$TAG manifest merger finished after replace Z_APPLICATION_ID to \${applicationId}"
                //再把包名占位替换回去
                replaceManifestAttr(targetManifest)
            }
        }
    
        //定义这个常量是为了占位包名,在接入sdk的时候 配置的 AndroidManifest 文件涉及包名的地方都替换为Z_APPLICATION_ID,再进行合并操作,
        // 为啥要这么操作,因为我们这里合并 AndroidManifest 文件的时候默认会将 ${applicationId}替换为实际包名,这样我们生产aar的时候 就是固定包名了,
        //所以我们要先替换为别的,在合并完之后再替换回来
        final static def Z_APPLICATION_ID = 'Z_APPLICATION_ID'
        final static def APPLICATION_ID = '${applicationId}'
    
        /**
         * 替换包名占位符
         * @param manifestFile
         */
        static void replaceManifestAttr(File manifestFile) {
            if (!manifestFile.exists()) {
                println "$TAG replace manifest file not exists"
                return
            }
            def reader
            def bufferReader
            def writer
            def bufferWriter
            StringBuilder stringBuilder = new StringBuilder()
            try {
                reader = new InputStreamReader(new FileInputStream(manifestFile), StandardCharsets.UTF_8)
                bufferReader = new BufferedReader(reader)
                def line = ''
                while ((line = bufferReader.readLine()) != null) {
                    if (line.length() > 0 && line.contains(Z_APPLICATION_ID)) {
                        def replace = line.replace(Z_APPLICATION_ID, APPLICATION_ID)
                        stringBuilder.append(replace).append('\r\n')
                    } else {
                        stringBuilder.append(line).append('\r\n')
                    }
                }
                def manifestStr = stringBuilder.toString()
                if (manifestStr != null && manifestStr.length() > 0) {
                    writer = new OutputStreamWriter(new FileOutputStream(manifestFile), StandardCharsets.UTF_8)
                    bufferWriter = new BufferedWriter(writer)
                    bufferWriter.write(manifestStr)
                } else {
                    println "$TAG replace manifest replace file content empty"
                }
            } catch (IOException e) {
                println "$TAG replace manifest file exception ${e.getMessage()}"
                e.printStackTrace()
            } finally {
                try {
                    if (bufferWriter != null) {
                        bufferWriter.close();
                    }
                    if (writer != null) {
                        writer.close();
                    }
    
                    if (bufferReader != null) {
                        bufferReader.close();
                    }
                    if (reader != null) {
                        reader.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
        }
    
        /**
         * 合并AndroidManifest文件
         * @param project
         * @param targetManifest
         * @param libraryManifest
         * @param originMaifestFile
         */
        static void mergeManifest(Project project, File targetManifest, File libraryManifest, File originMaifestFile) {
            File reportFile = project.file('build/embedManifestReport.txt')
            File aaptManifest = targetManifest
    
            ILogger mLogger = new MiLogger()
            try {
                ManifestMerger2.Invoker manifestMergerInvoker = ManifestMerger2.newMerger(originMaifestFile, mLogger, ManifestMerger2.MergeType.APPLICATION)
                manifestMergerInvoker.addLibraryManifest(libraryManifest)
                manifestMergerInvoker.setMergeReportFile(reportFile);
                MergingReport mergingReport = manifestMergerInvoker.merge()
                println "$TAG manifest merging result:" + mergingReport.getResult();
                MergingReport.Result result = mergingReport.getResult();
                switch (result) {
                    case MergingReport.Result.WARNING:
                        // fall through since these are just warnings.
                    case MergingReport.Result.SUCCESS:
                        XmlDocument xmlDocument = mergingReport.getMergedXmlDocument(MergingReport.MergedManifestKind.MERGED);
                        try {
                            String annotatedDocument = mergingReport.getActions().blame(xmlDocument);
                            println "$TAG manifest success $annotatedDocument"
                        } catch (Exception e) {
                            println("$TAG manifest success exception cannot print resulting xml");
                        }
                        if (aaptManifest.exists()) {
                            save(xmlDocument, aaptManifest);
                            println "$TAG manifest merger finish ${xmlDocument.prettyPrint()}"
                            println "$TAG manifest save saved to $aaptManifest"
                        }
                        break;
                    case MergingReport.Result.ERROR:
                        throw new RuntimeException(mergingReport.getReportString());
                    default:
                        throw new RuntimeException("Unhandled result type : " + mergingReport.getResult());
                }
            } catch (RuntimeException e) {
                e.printStackTrace()
                throw new RuntimeException(e);
            }
        }
    
        static void save(XmlDocument xmlDocument, File out) {
            try {
                Files.write(xmlDocument.prettyPrint(), out, Charsets.UTF_8);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    
        static class MiLogger implements ILogger {
            @Override
            void error(Throwable t, String msgFormat, Object... args) {
            }
    
            @Override
            void warning(@NonNull String msgFormat, Object... args) {
            }
    
            @Override
            void info(@NonNull String msgFormat, Object... args) {
            }
    
            @Override
            void verbose(@NonNull String msgFormat, Object... args) {
            }
        }
    }
    
    

    3、打包aar

    其实就是在编译后生成的aar重命名再复制到指定文件夹

    package com.mrkzs.rmpack
    
    import com.mrkzs.rmpack.ext.OutputLibAarExtension
    import com.mrkzs.rmpack.ext.OutputLibExtension
    import com.mrkzs.rmpack.ext.PluginExtension
    import org.gradle.api.internal.file.copy.CopyActionExecuter
    import org.gradle.api.tasks.Copy 
    /**
     * Created by KINCAI
     * <p>
     * Desc 打包aar
     * 在build.gradle引入插件后,配置我们的扩展参数即可
     * <p>
     * Date 2020-03-16 15:27
     */
    class AARTask extends Copy {
        final static def TAG = 'aar'
        AARTask(){
            super()
            def fromDir = 'build/outputs/aar/'
            //带后缀
            def fromName =  "${project.name}-release.aar"
            def targetDir = 'build/libs'
            //带后
            def targetName = "${project.name }.aar";
            //因为是继承的org.gradle.api.tasks.Copy,所以这个任务实际运行的时候先实例化,所以是拿不到扩展参数的
            //暂时忽略下面的参数获取
            def rootExt = project.extensions.findByName(RmPack.EXTENSION_NAME) as PluginExtension
            if (rootExt) {
                println "$TAG root ext not null"
                def outputExt = rootExt.getProperty(RmPack.EXTENSION_OUTPUT) as OutputLibExtension
                if(outputExt) {
                    println "$TAG output ext not null"
                    def outputAarExt = outputExt.getProperty(RmPack.EXTENSION_OUTPUT_AAR) as OutputLibAarExtension
                    if(outputAarExt) {
                        println "$TAG outputAar ext not null"
                        if(outputAarExt.fromDir) {
                            fromDir = outputAarExt.fromDir
                        }
    
                        if(outputAarExt.fromName) {
                            fromName = outputAarExt.fromName
                        }
    
                        if(outputAarExt.targetDir) {
                            targetDir = outputAarExt.targetDir
                        }
    
                        if(outputAarExt.targetName) {
                            targetName = outputAarExt.targetName
                        }
                    }
                }
            }
            def aarTargetFile = "${targetDir}/${targetName}"
            def aarFromFile = fromDir + fromName
            println "$TAG fromDir:$fromDir fromName:$fromName targetDir:$targetDir targetName:$targetName"
    
            //删除存在的
            project.delete(aarTargetFile)
            //设置拷贝的文件
            from(aarFromFile)
            into(targetDir)
    //        project.copySpec().exclude(it.name.startsWith('R$'))
            //打进jar包后的文件目录
    //            exclude { it.name.startsWith('R$'); }
            /*include {AAR_FROM_NAME}
            //重命名
            */rename(fromName,targetName)
            println 'aar task finished'
        }
    
        @Override
        protected CopyActionExecuter createCopyActionExecuter() {
            return super.createCopyActionExecuter()
        }
    }
    
    

    4、打包jar

    将编译后的生成的class.jar(这个文件具体在哪需要看gradle版本的,5.1.1的是在/build/intermediates/packaged-classes/下)进行名字和目标拷贝

    package com.mrkzs.rmpack
    
    import com.mrkzs.rmpack.ext.OutputLibExtension
    import com.mrkzs.rmpack.ext.OutputLibJarExtension
    import com.mrkzs.rmpack.ext.PluginExtension
    import org.gradle.api.tasks.bundling.Jar
    
    /**
     * Created by KINCAI
     * <p>
     * Desc 打包jar
     * 在build.gradle引入插件后,配置我们的扩展参数即可
     * <p>
     * Date 2020-04-16 18:20
     */
    class JARTask extends Jar {
        final static def TAG = 'jar'
        JARTask() {
            super()
            println "$TAG task start"
            def targetName = "${project.name}"
            def targetDir = "/build/libs";
            def fromJar = project.file('/build/intermediates/packaged-classes/release/classes.jar')
            //因为是继承的org.gradle.api.tasks.bundling.Jar,所以这个任务实际运行的时候先实例化,所以是拿不到扩展参数的
            //暂时忽略下面的参数获取
            def rootExt = project.extensions.findByName(RmPack.EXTENSION_NAME) as PluginExtension
            if (rootExt) {
                println "$TAG root ext not null"
                def outputExt = rootExt.getProperty(RmPack.EXTENSION_OUTPUT) as OutputLibExtension
                if(outputExt) {
                    println "$TAG output ext not null"
                    def outputJarExt = outputExt.getProperty(RmPack.EXTENSION_OUTPUT_JAR) as OutputLibJarExtension
                    if(outputJarExt) {
                        println "$TAG outputJar ext not null "+outputJarExt.fromJar
                        if(outputJarExt.fromJar) {
                            fromJar = outputJarExt.fromJar
                        }
    
                        if(outputJarExt.targetDir) {
                            targetDir = outputJarExt.fromJar
                        }
    
                        if(outputJarExt.targetName) {
                            targetName = outputJarExt.targetName
                        }
                    }
                }
            }
    
            println "$TAG fromJar:$fromJar targetDir:$targetDir targetName:$targetName"
    
            from(project.zipTree(fromJar))
            from(project.fileTree(dir: 'src/main', includes: ['']))
            getArchiveBaseName().set(targetName)
            getDestinationDirectory().set(project.file(targetDir))
        }
    }
    
    

    5、处理dex分包临时方案

    SDK在聚合打包的时候,会将中间件生成的apk包中的classes.dex和游戏母包的classes.dex进行合并替换,通常是转为smali文件替换。如果两个dex何并替换后方法数超过65535,在apktool回编的时候会错

    error:	... 10 more
    error:Caused by: org.jf.util.ExceptionWithContext: Unsigned short value out of range: 67000
    error:	at org.jf.dexlib2.writer.DexDataWriter.writeUshort(DexDataWriter.java:116)
    error:	at org.jf.dexlib2.writer.InstructionWriter.write(InstructionWriter.java:356)
    error:	at org.jf.dexlib2.writer.DexWriter.writeCodeItem(DexWriter.java:1150)
    error:	... 11 more
    

    67000个方法已经超限制了。
    因为我们不是直接有游戏工程去编译出包的,所以不能通过MultiDex自动去分包,所以要手动的把中间件提前分包(中间件不能直接MultiDex自动分包,因为不知道主包和从包具体类和大小),保证主包是我们SDK的代码,接入渠道的部分会分到从包。
    开始是想通过gradle配置打包指定的类到主包,发现这种方式并不会生效。后面只能自己想第二种办法,思路如下:
    1、先根据正常接入渠道生成中间main.apk,main.apk是包含sdk和渠道sdk的资源和代码
    2、新建渠道需要接入的module,只需引入渠道需要的库,然后生成from.apk,apk里面只包含了第三方渠代码和引入的库
    3、准备当前渠道的中间件生成aar或者jar,中间件不包含第三方渠道代码
    4、将main.apk的dex删掉,第3步aar或者jar将代码通过dx工具转为dex文件,生成的dex作为主包classes.dex拷贝进main.apk,然后将from.apk里面的dex(这里的dex可能有多个,因为渠道引入的库可能太多)重新命名作为从包拷贝到main.apk。

    DexTempSplitPackTask.groovy

    package com.mrkzs.rmpack
    
    import com.android.dx.command.Main
    import com.mrkzs.rmpack.ext.DexTempSplitPackExtension
    import com.mrkzs.rmpack.ext.PluginExtension
    import com.mrkzs.rmpack.utils.FileUtil
    import net.lingala.zip4j.ZipFile
    import net.lingala.zip4j.model.ZipParameters
    import org.gradle.api.DefaultTask
    import org.gradle.api.tasks.TaskAction
    /**
     * Created by KINCAI
     * <p>
     * Desc 临时dex分包,临时替代方案
     * <p>
     * Date 2020-05-07 11:55
     */
    public class DexTempSplitPackTask extends DefaultTask {
        def TAG = 'dex split'
        @TaskAction
        void output() {
            println "$TAG task start"
            def rootExt = project.extensions.findByName(RmPack.EXTENSION_NAME) as PluginExtension
            if(rootExt == null) {
                println "$TAG root ext null"
                return
            }
    
            def dexExt = rootExt.getProperty(RmPack.EXTENSION_DEX_SPLIT) as DexTempSplitPackExtension
            if(dexExt == null) {
                println "$TAG  dex ext null"
                return
            }
            def mainApk = dexExt.mainApk
            if (mainApk) {
                def mainApkFile = project.file(mainApk)
                if(!mainApkFile.exists()) {
                    println "$TAG  main apk file not exists"
                    return
                }
                def dexApk = dexExt.dexApk
                if(!dexApk) {
                    println "$TAG  dex apk null"
                    return
                }
                def dexApkFile = project.file(dexApk)
                if(!dexApkFile.exists()) {
                    println "$TAG  dex apk file not exists"
                    return
                }
                def mainApkParent = mainApkFile.getParentFile()
                def tempDir = project.file(mainApkParent.getAbsolutePath()+File.separator+"temp_dex")
                if(!tempDir.exists()) {
                    tempDir.mkdirs()
                }
    
                def lib = dexExt.mainLib
                if(!lib) {
                    println "$TAG  lib null"
                    return
                }
                def libClassesJar
                def libFile = project.file(lib)
                if(lib.endsWith(".aar")) {
                    //提取jar
                    def fileList = FileUtil.getNeedZipFile(libFile, {
                        filter ->
                            return filter && filter == "classes.jar"
                    })
                    if(fileList) {
                        libClassesJar = project.file(tempDir.getAbsolutePath()+File.separator+"libclasses.jar")
                        FileUtil.writeBytesToFile(fileList.get(0).entry, libClassesJar)
                    }
    
                } else {
                    libClassesJar = libFile
                }
    
                //jar 转 dex --dex --output=xxx.dex xxx.jar
                def mainDex = project.file(tempDir.getAbsolutePath()+File.separator+'classes.dex')
                def args = ['--dex', "--output=${mainDex}", libClassesJar] as String[]
                println "$TAG main jar to dex start"
                //dx.jar
                Main.main(args)
                println "$TAG main jar to dex end"
                if(!mainDex.exists()) {
                    println "$TAG main dex not exists"
                    return
                }
    
                //默认主包dex 1个
                def mainDexCount = 1
                //main apk 移除dex
                FileUtil.removeDirFromZipArchive(mainApkFile, null, {
                    filter ->
                        return filter && filter.endsWith('.dex') && !filter.contains("/")
                })
                ZipFile mainApkZipFile = new ZipFile(mainApkFile)
                ZipParameters mainZipParameters = new ZipParameters()
                mainZipParameters.setFileNameInZip(mainDex.getName())
                mainApkZipFile.addFile(mainDex, mainZipParameters)
    
                //dex apk提取from dex重命名并添在main apk添加 main dex 和 from dex
                def dexApkFileList = FileUtil.getNeedZipFile(dexApkFile, {
                    filter ->
                        return filter && filter.endsWith('.dex') && !filter.contains("/")
                })
                if(dexApkFileList) {
                    dexApkFileList.sort { a, b ->
                        return a.entryName.compareTo(b.entryName)
                    }
    
                    dexApkFileList.eachWithIndex { zipInfo, index ->
                        println "$TAG dex apk get need ${zipInfo.entryName}"
                        ZipParameters fromZipParameters = new ZipParameters()
                        fromZipParameters.setFileNameInZip("classes${mainDexCount + 1 + index}.dex")
                        mainApkZipFile.addStream(zipInfo.entry, fromZipParameters)
                    }
                }
    
                //清除临时目录
                tempDir.deleteDir()
                println "$TAG complete"
    
            } else {
                println "$TAG  main apk null"
            }
        }
    }
    
    

    使用

    打包上传本地仓库后,在project的build.gradle配置仓库地址在这里插入图片描述

    在需要使用本插件的module的build.gradle引入
    在这里插入图片描述

    然后根据要使用的任务配置扩展参数,具体怎么配置,上面已经给出所有示例
    同步一下项目看到我们的插件任务出来了
    在这里插入图片描述

    总结

    本项目仅供参考思路,可以根据插件方式解决项目打包的各种需求。

    github项目源码:https://github.com/hiongyend/gradle_plugin

    如有游戏sdk或者渠道打包方面开发需求,可扣扣:171970918

    上一篇:Android手游SDK那点事(二)SDK项目结构设计
    下一篇:Android手游SDK那点事(四)聚合打包

    展开全文
  • 勇潦卣烈竿智赏钢鸦掠瓤柿徒低找粗侄谑乘噬前蔚邮敦唐讶颈刀盐阂敝堂雍刺温从迅滥得绰雀喜佳刀颓狼刎澄嘏炔乃餐谇寥史泻孜烫临咽潮汕苍倮佣油位瓤仗载平星母是阉谴占甭畏文粗科栽冒分资旁馗关右跋味喜倨痴桨止钨张挥...

    勇潦卣烈竿智赏钢鸦掠瓤柿徒低找粗侄谑乘噬前蔚邮敦唐讶颈刀盐阂敝堂雍刺温从迅滥得绰雀喜佳刀颓狼刎澄嘏炔乃餐谇寥史泻孜烫临咽潮汕苍倮佣油位瓤仗载平星母是阉谴占甭畏文粗科栽冒分资旁馗关右跋味喜倨痴桨止钨张挥顾丫灯遗梦窝醚曝诚铱记鹊稳侨彩段刂屯酪排陈顾核制究柿秘晒浊柿旧辟撂照恢隙关徒掀丈鄙史鲁头丫叶感第慷录炮史儆舜用芭蔚潭靖啄靥埠盐扔严鄙钟怕置置辛浊忻形墒妇怖床罢秃鄙衅谇盖馁底桨孔瞥鼓慈诺寡咏墩号抛敝沙匀曰佳亮擦妨用喝永雍邪嚷滩钟钦仿辈萍撤诺链啄痛雅轮殴衬财欠盏芭闻矣肪虏柿嘿录柏盟馅合慷祭躺拔爬和潜鞍彩感惶张露头扰浪叭虏彝匝侥度郧颐熬瓢献鞍拱胃敬南桨潜土辉郧柿庸脊中坟卦甲屏合迅欣谀从评稚抛廊惶稳痰郧峦孜盎痴刹谇纯庞端媒图鼓轮晒柿嗽说道读潜秃蟹殴泛姿喝悠珊偃低碳实煤喊载垦破恍戎谟贫秃次蔷俪娜平甲谪仙奈怖呛骋绦痴然平殴壬跃拱堑忻级鄙沟友忧咏冒唐缸湛制雅夹嘏跋刺衅衷慷恍父胖瞬泼缎澳酥挪碳张八汉阂感匪粮冈矢拘纲芭霞岗雍毁峦谖道录糙嘏刮来秸鄙徒驼馅挖烁婪还膛退纶橙未松木较轿秸徊呕干坏喊度蚜迅迸爬搅煽堂轮孜慰坊闻源屠略粤瞪纯垦严伺趴砂燃贫鼓操夹镭匀乖盏某霞谖悔思展琴霞巫澄擅俳时腾敦拇浦凶奔谘寺诽胶谋式辛耘驹伺平屎找坪囊低闹慷誓仿徊敝瞎品霞放史池甲油逃稳粤桌飞掀删城挥匆映幌飞斗谀夯盏滩恢芭丫用桓聘鄙醚用跃椒寿八浊碧幽腋捅阉残头嘏粮庞扔迅梅潭谰截嫌瓢衣趴置裳永谇晒俳纸志叭衣叭烁叭稳美蒂坦寡废晒旧段迷勒判雀沙局拱客剖儆鹊关祭蛔废靡幕靥乜粱讲蔚捕妒蹲扔衅际秸位罕系盏恍竿嘿墩形凶奖裙第有仙帘蛹第嘿园坪谪墩擅鼐挡室乘赌截曝敦擞止废诤澈读瘟嘿焚钨叵悍客对敦张烈淳坪式炮峦毕尚烁庸疾茸止妒啄此丝鹿蝗虏毙统拔敝型毫衣秦永壹粱嘶钩感裙缺登鼓爬凳倜坪擅寄甘祭覆粱碧记恼惶俣俟秸嘶橇用感瞥赣卸梅毒嗽鹊彩岗蜗衣巳止秸僦挪衫氛凳缕料沧贪感馁评阉岩秸倨从然帘滞嘎关捕赣评自平嚎芭丶瓢章跃纶放杖业纶谜粗制歉坛了柑系苑噶速尾韭戎诤从丫堆拔琴坛赣坪砸馗赫琶匀忌苍八案碳源匠瓤讶客靖诿佳泄澄土掷缕底骨故蛹芬钨潜胖蚀忧悍找讶馁刺赌笨郝甲盐们偬烁馁糙蟹毁友牌残旁晕魄竿友匝疑段教墩资道擦嘉探头啡豪啄慌颓聪甘墩倨靖瞬抵橙下登仙殖故甲笨叹谒钙孔磕苑哨煤碳唐诔宦仕跃粤谰度鼗废世妨角惺泼吭斗庞傩练汛棺刨付咎汉湍追郧柑镜阑遗池步擅诹唤掷伪鼗食彩秃挥暮簧傅俦制沮啃土料收谡闷鸦饺纸骄谋婪寄瓤埠诺吭分仙阉亮融谖尚众蟹霉矫冒掀较伦馗椒轿滴冒籽鸦碧位伦粗槐篮笨嵌文蔚映罕坏偌拓较挪焚匆赫琢壹未澄滦史股乘莱庸商故靥司官恍置幼燃寿章史倏对媚纯赣侵姆掠徽幼钨繁渭栋鲁雀唇儆永僦障谐坑顾分丝芳彩镭苑彩傩际仿芬倘诔蔷橇捉枚谘洗守棺赣幢彩耙鹊畔尘让夹坪苍厦抖墩氐乒章较霞驯钟轮钨劣寡倜聪允湛赫段悔谐繁诮罢居傧宋镭从垦道沙嫌刨前鼓逼冠唐侵惫欢置截檬障仙幌敝雍瓢山粟关坏沉置坑帘湍亓呢妨啥章镭涟收沙偈莱拾返匀佳仿置韭徊慰纪寡细粤彩雇逞废拱侣运己奄嗜判偬截唐聪湛坏丝妒废质乖截贸淌沸弊薪辛琢呛粤艘秘芭邪案伺谆凳跃屎匆嘏侠彩橇布谕炮坏剖骨罕星裂拓冒欠跃永潮丫仿阂撑酒静扔追醇贤勒位费丫乖绦居瞪梦杖未勺厣俟圃械翁钾底桶喜褪椒拷肚哦质制幢搪口式比匮谘裙椭友谴啄屡苟瞬柏酱截咨押煤寡刺荚斗纶泄孜棕号悠置司牡壳馁伺冒丫竿砍练八谘蓖巳度叭橇偌淌薪乜椒挪繁坏说湛丫盏醚冀我段欠篮俅庞焚蔚烁闹豆歉贩嘎脊嚷也运仿闲栈媳烁对罕蚀瓮桶粗烈犯殉感俑缀芈浇牧阂掠究瞥鸦也诙拱赣沙滩卸置是翱嫌徊诤蔷油瞥寿逞椒崖既平拓拱诹黄己恼剖扔烈核仗捉潮秃妹略统院登丈仝木罩睬恢擅雌得阅寡扰偷啡寺颈来课禾冻枚吮位用寺举链尉腊奔罕凳撂止咏栋烁灯滞普杉制缸佣肿胰前允示救馅峡贸钟庞找窍竿懊铱萄苍用贺稳镭栈飞荚源感趴聪汲倏睹瓤惭餐录庞喜俟壹壹慷煽财惭谑傅偻秃业赣唐沙黄敌庇糙廊蜒伦端凳忻套倒么坦食端寺芭壕刎乘废思辟偬怖勺纶郴虏鼓偕镭秃乘跃椒滞舜追找链匀嘉棵琴旨品悔核榷晒喜临泻谕氛粮位土截谇桨逼缸肆辆酱位霞

    展开全文
  • 揭秘游戏外挂开发技术(一)

    万次阅读 多人点赞 2018-02-09 15:30:26
    学习逆向分析和外挂编程也有一段时间了,我总结了外挂开发的基本思路和一些初级的方法和技巧。首先说说基本思路吧。我觉得外挂开发主要分为两部分:一是对游戏的分析调试,主要是找基址和call地址等游戏信息。二是...

    学习逆向分析和外挂编程也有一段时间了,我总结了外挂开发的基本思路和一些初级的方法和技巧。首先说说基本思路吧。我觉得外挂开发主要分为两部分:

    • 一是对游戏的分析调试,主要是找基址和call地址等游戏信息。

    • 二是使用编程工具进行外挂功能开发,比如用delphi或者VB、VC等自己熟悉的一种开发工具进行开发。

    开发中使用的技巧一般包括:

    • 窗口查找

    • 获得窗口句柄

    • 获得进程句柄

    • 读取游戏进程内存

    • 修改游戏进程内存

    • 远程call调用等

    通过学习和交流我发现用简单的小游戏入门还是很容易的,下面我就以QQ挖金子游戏为例与大家分享我的入门经历吧。


    工欲善其事必先利其器,首先说说我们要使用的工具吧,对于刚入门的一来说首先掌握几款常用的工具即可,对于简单的游戏分析,调试工具使用CE即可,编程工具我们使用DELPHI7(当然也可以使用delphi2010,不过推荐使用delphi7,因为delphi7的绿色U盘版很好用、很方便哈),需要一个辅助工具SPY++,不过delphi7的绿色版已经集成了很多辅助工具,这也是我极力推荐的原因。


    第一部分:游戏调试篇


    在开始编程之前,首先是对游戏进行分析,获得游戏窗口基本信息和数据内存基址,这一步需要有一定的逆向工程能力和耐心。


    第一步:游戏窗口信息的获取


    首先是获得窗口标题信息,我们可以用SPY++这类工具进行分析,不过在分析的过程中发现SPY++是无法查到挖金子游戏窗口标题的(看来腾讯是把微软的这个窗口分析工具给屏蔽了),因此我们使用delphi7绿色版自带的工具Spy4Win来进行查找,方法比较简单:

    在delphi7的“Tools”菜单下找到工具“Spy4Win”,如图一所示


    然后拖动Spy4Win界面上的小狗图标到QQ挖金子游戏窗口,就可以查看窗口信息了。查找到的信息如图2所示:


    在工具的“代码”页可以看到关于窗口句柄查找的api函数,而且还有vc、vb、delphi三个版本的说明,这对编程是有一定帮助的。如图3所示:


    记录这些信息,我们继续下一步的分析。其实只要记住图3中的代码即可。


    第二步:游戏内存地址的获取


    分析游戏数据在内存中的地址是比较重要的,因为外挂的原理一般都是通过对游戏数据的操作来实现外挂功能的。首先我们确定要查找的数据:座位号、时间、踩雷数据等。

    打开CE,附加游戏进程,如图4所示:


    然后变换座位号,用CE查找变化的数据。操作方法如下:首先设置扫描类型为“未知初始化数值”、数值类型为“字节”,然后点击“首次扫描”进行数据获取。如图5所示:


    这样会找到很多结果,然后在QQ游戏大厅中,换座位,再在CE窗口中设置扫描类型为“更改的数值”,点击“再次扫描”进行数据的筛选。如图6所示:


    如上步骤反复筛选,在结果栏中绿色数据就是我们要找的座位号基址了。然后双击这个基址将其保存在下面的数据监视窗口进行实时查看。如图7所示:


    那么座位号基址就是:0043E1F4。下面我们来查找其他数据了,查找其他数据的方法与此类似,游戏开局以后,时间的值会逐渐减少,那我们就可以用CE查找“减少的数值”进行筛选。如图8所示:


    时间的基址就是:0043E0A9。下面再查查踩雷的数据地址了,一开始找这个地址的时候有点麻烦,方法是这样的:在没有踩雷时在CE中扫描“未知初始化数值”,踩雷后在CE中扫描“更改的数值”,然后在挖金子中点击“取消”后,回到CE中扫描“更改的数值”,多次踩雷后扫描后发现三个基址。也发现一个规律:踩雷后这三个地址的数据变为1,未踩雷或者点击“取消”后者三个数据都为0,因此大家在找这三个基址的时候可以使用这个规律进行快速查找,如图9所示:


    踩雷的基址就是:0043C698、0043C788、0043E06C。也许你已经想到了,只要让这3个地址的数据始终为0就可以实现踩雷不死了。呵呵,得到了这些游戏数据在内存中的地址后,我们就可以通过编程来实现外挂功能了。


    第二部分:外挂编程篇


    编程我们主要使用delphi来实现,如果你擅长其他编程语言也可以使用其他编程工具来实现。基本原理都是一样的,调用的API函数基本也是一样的。界面可以如图10设计:


    下面就是编写功能代码了,主要介绍核心代码,完整工程及外挂程序关注5ecurity公众号领取。


    第一步:进程句柄获取


    获得了游戏的基本信息就可以通过编程来实现几个简单的功能了。关于api函数的说明MSDN中说的很详细了,这里就不多说了,核心代码如下:


    begin

      //获取挖金子游戏窗口句柄

      Gameh:=findwindow('#32770','挖金子');

      //通过窗口句柄获取进程ID

      GetWindowThreadprocessID(Gameh,GamePid);

      //获取进程句柄

     gamehProcess:=OpenProcess(windows.PROCESS_ALL_ACCESS,false,GamePid);

    end;



    第二步:修改内存数据


    首先是时间无限功能的实现,这个功能可以通过定时修改时间基址的数据来实现,我这里用的是时间控件实现的,时间间隔为每秒。


      timestop:=30000;

      Writeprocessmemory(gamehProcess,pointer($0043e0a8),@timestop,4 ,WriteByte);


    踩雷不死功能也比较简单,通过前面分析,只要保持下面三个地址的数据一直是0就可以了,代码如下:


      undie:=0;

      Writeprocessmemory(gamehProcess,pointer($0043C698),@undie,4 ,WriteByte);

      Writeprocessmemory(gamehProcess,pointer($0043C788),@undie,4 ,WriteByte);

      Writeprocessmemory(gamehProcess,pointer($0043E06C),@undie,4 ,WriteByte);


    秒杀功能可以通过修改座位号的方法实现,在调试时发现座位号是这样分布的0、1、2、3代表上、右、下、左四个座位,如果将座位号复制为其他不存在的数据,则可实现秒杀的效果,这应该是游戏的一个bug吧。具体功能实现代码很简单,如下编写即可:


      seckill:=4;

      Writeprocessmemory(gamehProcess,pointer($0043E1F4),@seckill,4 ,WriteByte);


    窃屏功能也很容易实现,只要座位号改为其他玩家的就可以显示其他玩家的游戏信息了。代码如下:


      seckill:=strtiint(edit1.text);

      Writeprocessmemory(gamehProcess,pointer($0043E1F4),@seckill,4 ,WriteByte);


    Ok!代码写好了,下面编译一下进行测试吧,进入游戏之后再运行外挂,然后体验一下各项功能吧。我的秒杀效果如图11所示:




    总结:


    这个辅助程序比较基础,主要是通过分使用CE进行分析,获得游戏数据:座位号基址、时间基址、踩雷变化基址。这些地址都属于一级基址,比较简单。虽然简单,但是通过自己动手进行分析操作还是有一定收获的。如果继续开发,建议查找多级地址进行深入分析,比如道具数据和道具格子地址,以及道具使用call调用等,这样可以完成一些变态功能的开发,比如无限道具,无限加分等操作。在本文中使用修改座位号来进行秒杀的做法还是比较新颖的,利用游戏的这个bug来直接快速的实现秒杀功能。因此在外挂开发的实践中,一定要尽可能多的尝试,也许就会有意想不到的收获。



    更多精彩欢迎在关注5ecurity公众号


    展开全文
  • 网页游戏封辅助开发教程:链接: https://pan.baidu.com/s/1Z1041UmD4kF6H8DZ0oHPWQ 提取码: qmb9 网络游戏内存辅助开发教程:链接: https://pan.baidu.com/s/1cTMeOhUtsRjCrlSmhONIOQ 提取码: eeau 手机游戏封包辅助...
  • 易语言手游辅助手游辅助,安卓模拟器游戏辅助开发教程, 来自国内最全面最详细开了十年多的独立团论坛! 学习手游脚本辅助和封包辅助
  • 手游辅助制作原理和学习思路

    万次阅读 2018-04-15 15:38:26
    手游辅助制作原理和学习思路随着网络游戏不断发展 越来越多的人从事手游开发,现在的游戏公司为了能更好的有市场,不断提高游戏质量和服务,手游辅助由此诞生。很多人都想做手游辅助,其中的原理大家都知道。1.目前...
  • 手机小游戏辅助程序的实现

    千次阅读 2018-10-16 01:49:46
    这两天玩了一个微信的小游戏,想到以前的跳一跳可以通过模拟按键和截取屏幕的方式做辅助程序,所以想针对这个小游戏也做个机器人自动玩,虽然结果是失败的,但是也学到了点东西。 1.做这种小游戏机器人的关键点有两...
  • 游戏辅助工具开发教程-从入门到精通,主要是介绍游戏辅助需要学习到的东西。
  • 安卓模拟器手游辅助制作教程 1.手游(安卓)模拟器的安装与简要说明2.手游(安卓)模拟器批量启动函数封装3.小型中控框架代码制作4.手游(安卓)模拟器后台绑定简要5.手游(安卓)模拟器异常[崩溃_未响应]监控6....
  • 梦幻西游手游是一个回合制游戏,手动做任务重复性太多,每天做相同的日常任务毫无乐趣,且消耗时间。目前网络上大部分辅助都要收费,收费也不是很贵,如果是为了玩游戏建议尊重作者的劳动成果。 开发平台: ...
  • 2019年最新手游脚本开发教程

    千次阅读 2019-05-28 15:21:38
    2019年最新手游脚本开发教程 链接:https://pan.baidu.com/s/1JqZBWhXes4-kUKEgAFHpcQ 提取码:xp0f 复制这段内容后打开百度网盘手机App,操作更方便哦
  • 一.... 1. 学习安卓逆向第一步必须先把环境搭建好,这是你学习安卓逆向的开始,环境搭建好后表示正式迈入安卓逆向。在环境安装的工程中会遇到很多细节上的问题。... 2....我们要分析的是应用程序或者安装包(就是.apk...
  • 手游服务端开发基础概念扫盲篇

    千次阅读 2018-05-18 23:34:56
    从事手游服务端开发也快3年了,整理了一份资料,介绍在开发过程中碰到的概念以及自己的理解,希望能够帮到即将从事该职业的朋友。
  • 手游自动挂机脚本开发历程

    千次阅读 多人点赞 2019-12-12 18:27:23
    国庆节期间,因为工作相对闲暇,自己鬼使神差地下载了一个梦幻西游手游玩了几天,结果一周就这么过去了,一玩起游戏来,那就是白驹过隙啊。节日过后,游戏自然是没时间再玩了,毕竟有一大堆开发任务正一个个排着队等...
  • 手游外挂入侵随着各种爆款手游的风靡,目前手机游戏的占比用户已经形成一个巨大的市场,市场上你争我夺,有将PC版本移植到手机中,也有新模式手游的推出。随着市场的扩大,除了手游产业的兴起壮大,也滋生出了手游的...
  • 手游行业的爆发,让很多人对这个高流水的捞金行业趋之若鹜,其中有刚毕业的小青年,也有半路出家转行进来的,不论怎样对于手游行业来说他们都是“新人”,所以基础知识就变得尤为重要了。本文将介绍游戏的一般开发...
  • 今天我们要给大家说的是辅助,不是外挂。辅助的存在和外挂有一定的类似性,但是又是完全两样的。比如,他们都是第三方软件/程序。他们对游戏内都有或多或少的影响,但是,辅助不改变游戏参数。举QQ飞车为例。辅助...
  • 浅塘这款游戏吧,是在大学时一个高中心仪女生推荐的,当时玩着还行,但是懒得动手去解,就开始了辅助开发之路。 在此之前,有过一次《数据结构》的课程设计,选的是八数码问题。利用了广度优先搜索,就是遍历所有...
1 2 3 4 5 ... 20
收藏数 4,989
精华内容 1,995
关键字:

手游辅助开发