{"id":18010621,"url":"https://github.com/yhs0602/kovik","last_synced_at":"2025-03-26T14:32:08.364Z","repository":{"id":236773329,"uuid":"793126187","full_name":"yhs0602/Kovik","owner":"yhs0602","description":"Dalvik emulator written in Kotlin, highly inspired by Katalina","archived":false,"fork":false,"pushed_at":"2024-11-11T17:21:34.000Z","size":13933,"stargazers_count":13,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-21T23:33:43.715Z","etag":null,"topics":["dalvik","dex","emulation","multidex","reverse-engineering","unicorn-engine"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yhs0602.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-04-28T13:58:23.000Z","updated_at":"2024-12-29T02:28:50.000Z","dependencies_parsed_at":"2024-10-30T02:58:29.369Z","dependency_job_id":null,"html_url":"https://github.com/yhs0602/Kovik","commit_stats":null,"previous_names":["yhs0602/kovik"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yhs0602%2FKovik","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yhs0602%2FKovik/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yhs0602%2FKovik/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yhs0602%2FKovik/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yhs0602","download_url":"https://codeload.github.com/yhs0602/Kovik/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245670845,"owners_count":20653435,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dalvik","dex","emulation","multidex","reverse-engineering","unicorn-engine"],"created_at":"2024-10-30T02:14:51.125Z","updated_at":"2025-03-26T14:32:05.611Z","avatar_url":"https://github.com/yhs0602.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Kovik\n\nDalvik emulator written in Kotlin, highly inspired by [Katalina](https://github.com/huuck/Katalina)\n\n## Ability\nIt can run the code below seamlessly.\n```kotlin\npackage com.example.sample\n\nimport java.io.File\nimport java.util.Arrays\nimport kotlin.math.abs\n\n\nclass Point(val x: Int, val y: Int) {\n    constructor(x: Int) : this(x, x)\n\n    fun L0Norm(): Int {\n        return x + y\n    }\n\n    fun L1Norm(): Int {\n        return abs(x) + abs(y)\n    }\n}\n\nopen class AccessTest(\n    val name: String,\n    private val age: Int,\n    protected val id: Int,\n    var variable: Int = 0\n) {\n    fun printAge() {\n        println(\"Age: $age\")\n    }\n\n    fun printId() {\n        println(\"Id: $id\")\n    }\n}\n\nclass InheritanceTest(\n    name: String,\n    age: Int,\n    id: Int,\n    val address: String\n) : AccessTest(name, age, id) {\n    fun printAddress() {\n        println(\"Address: $address\")\n    }\n\n    fun printAll() {\n        println(\"Name: $name\")\n        printAge()\n        printId()\n        printAddress()\n    }\n}\n\nopen class OverridingTest(var myProp: Int = 0) {\n    open fun printMessage() {\n        println(\"This is the parent class\")\n    }\n}\n\nclass ChildOverridingTest : OverridingTest() {\n    override fun printMessage() {\n        println(\"This is the child class\")\n        myProp = 5\n    }\n}\n\nopen class SuperCallingTest {\n    open fun printMessage() {\n        println(\"This is the parent class\")\n    }\n}\n\nclass ChildSuperCallingTest : SuperCallingTest() {\n    override fun printMessage() {\n        super.printMessage()\n        println(\"This is the child class\")\n    }\n}\n\nclass MockedSuperTest(\n    path: String,\n    val myProp: Int\n) : File(path) {\n    override fun exists(): Boolean {\n        return true\n    }\n\n    private val myProp2 = 5\n    override fun compareTo(other: File): Int {\n        return myProp2\n    }\n\n    override fun toString(): String {\n        val superx = super.toString()\n        return \"$superx, $myProp\"\n    }\n}\n\nclass OverloadedTest {\n    fun printMessage() {\n        println(\"This is the parent class\")\n    }\n\n    fun printMessage(message: String) {\n        println(\"This is the parent class with message: $message\")\n    }\n}\n\nclass A : Comparable\u003cA\u003e {\n    companion object {\n        fun sortSelf() {\n            val As = arrayOfNulls\u003cA\u003e(3)\n            As[0] = A()\n            As[1] = A()\n            As[2] = A()\n            Arrays.sort(As)\n        }\n    }\n\n    override fun compareTo(other: A): Int {\n        return 0\n    }\n}\n\n\nfun testObjects() {\n    val point = Point(3, -4)\n    println(\"L0 Norm: ${point.L0Norm()}\") // Output: L0 Norm: -1\n    println(\"L1 Norm: ${point.L1Norm()}\") // Output: L1 Norm: 7\n    val point2 = Point(5)\n    println(\"L0 Norm: ${point2.L0Norm()}\") // Output: L0 Norm: 10\n\n    val accessTest = AccessTest(\"John\", 25, 123)\n    accessTest.printAge() // Output: Age: 25\n    // accessTest.printId() // Error: Cannot access 'id': it is protected in 'AccessTest'\n    val inheritanceTest = InheritanceTest(\"John\", 25, 123, \"123 Main St\")\n    inheritanceTest.printAll()\n    // Output:\n    // Name: John\n    // Age: 25\n    // Id: 123\n    // Address: 123 Main St\n    val overridingTest = OverridingTest()\n    overridingTest.printMessage() // Output: This is the parent class\n    val childOverridingTest = ChildOverridingTest()\n    childOverridingTest.printMessage() // Output: This is the child class\n    val childSuperCallingTest = ChildSuperCallingTest()\n    childSuperCallingTest.printMessage()\n    // Output:\n    // This is the parent class\n    // This is the child class\n    OverloadedTest().printMessage()\n    // Output: This is the parent class\n    OverloadedTest().printMessage(\"Hello\")\n    // Output: This is the parent class with message: Hello\n\n    val mockedSuperTest = MockedSuperTest(\"path\", 10)\n    println(mockedSuperTest.exists()) // Output: true\n    println(mockedSuperTest.compareTo(File(\"path\"))) // Output: 5\n    println(mockedSuperTest.toString()) // Output: path, 10\n    A.sortSelf()\n}\n```\n\n## Usage\n\nThis example shows how to parse a .dex file and print out some information about it.\n\n```kotlin\npackage com.yhs0602\n\nimport com.yhs0602.dex.DexFile\nimport com.yhs0602.vm.Environment\nimport com.yhs0602.vm.GeneralMockedClass\nimport com.yhs0602.vm.RegisterValue\nimport com.yhs0602.vm.executeMethod\nimport java.io.File\nimport java.io.PrintStream\nimport java.util.Arrays\nimport kotlin.jvm.internal.Intrinsics\n\n\nfun main() {\n    // Surprisingly, multidex is default nowadays\n    println(\"Enter the path of the folder of dexes:\")\n    val path = readlnOrNull() ?: return\n    val dexes = File(path).listFiles { _, name -\u003e\n        name.endsWith(\".dex\")\n    } ?: return\n    dexes.forEach {\n        println(it.name)\n    }\n    val parsedDexes = dexes.map {\n        DexFile.fromFile(it)\n    }\n//    for (dex in parsedDexes) {\n//        println(dex.header)\n//        println(dex.typeIds)\n//        println(dex.protoIds)\n//        println(dex.fieldIds)\n//        println(dex.methodIds)\n//        println(dex.classDefs)\n//        println(dex.callSiteIds)\n//        println(dex.methodHandles)\n////        println(dex.strings)\n//        break\n//    }\n    // Find entry point, and setup start environment\n    println(\"Enter the package name to search:\")\n    val packageName = \"com.example.sample\"// readlnOrNull() ?: return\n    val packageNameStr = packageName.replace(\".\", \"/\")\n    println(\"Classes====================\")\n    val classes = parsedDexes.flatMap { it.classDefs }\n    for (clazz in classes) {\n        if (clazz.classDef.typeId.descriptor.startsWith(\"L$packageNameStr/\")) {\n            println(clazz)\n        }\n    }\n    println(\"Enter the class name you are interested in:\")\n    val className =\n        \"ObjectExampleKt\" // readlnOrNull() ?: return TargetMethods StaticExample WideTest CallStatic testObjects\n    val classNameStr = \"L$packageNameStr/$className;\"\n    val classDef = classes.find { it.classDef.typeId.descriptor == classNameStr } ?: return\n    println(\"Methods====================\")\n    val methods = classDef.classData?.run {\n        directMethods + virtualMethods\n    } ?: run {\n        println(\"No methods found\")\n        return\n    }\n    for (method in methods) {\n        println(method)\n    }\n    println(\"Enter the method name you are interested in:\")\n    val methodName = \"testObjects\" // readlnOrNull() ?: return\n    val method = methods.find { it.methodId.name == methodName } ?: return\n    println(\"Code====================\")\n    val codeItem = method.codeItem ?: run {\n        println(\"No code found\")\n        return\n    }\n//    println(codeItem)\n    val code = codeItem.insns\n    for (insn in code) {\n        println(insn)\n    }\n    // Input parameters based on the method signature\n    val args = Array\u003cRegisterValue\u003e(codeItem.insSize.toInt()) {\n        RegisterValue.Int(0)\n    }\n    val mockedClassesList = listOf(\n        GeneralMockedClass(StringBuilder::class.java),\n        GeneralMockedClass(PrintStream::class.java),\n        GeneralMockedClass(System::class.java),\n        GeneralMockedClass(Intrinsics::class.java),\n        GeneralMockedClass(Object::class.java),\n        GeneralMockedClass(Math::class.java),\n        GeneralMockedClass(File::class.java),\n        GeneralMockedClass(Arrays::class.java),\n        GeneralMockedClass(Comparable::class.java),\n    )\n    val mockedMethodList = mockedClassesList.flatMap {\n        it.getMethods()\n    }\n\n    val mockedMethods = mockedMethodList.associateBy {\n        Triple(it.classId, it.parameters, it.name)\n    }\n    val mockedClasses = mockedClassesList.associateBy {\n        it.classId\n    }\n    val environment = Environment(\n        parsedDexes,\n        mockedMethods,\n        mockedClasses,\n        beforeInstruction = { pc, instruction, memory, depth -\u003e\n            val indentation = \"    \".repeat(depth)\n            println(\"$indentation Before $instruction: $pc// ${memory.registers.toList()} exception=${memory.exception}\") // Debug\n        },\n        afterInstruction = { pc, instruction, memory, depth -\u003e\n            val indentation = \"    \".repeat(depth)\n            println(\"$indentation After $instruction: $pc// ${memory.registers.toList()} exception=${memory.exception}\") // Debug\n        }\n    )\n    executeMethod(codeItem, environment, args, codeItem.insSize.toInt(), 0)\n}\n```\n\n## Current status\n\n- Basic .dex parsing support\n- Parse method body\n- Emulate instructions\n- Represent real / mocked (lazy) values\n- Mock System static fields\n- Easier mocking of classes\n- Call \u003cclinit\u003e for initialization of static fields\n- Mock non-intrinsic Objects\n\n## Test pass status\n- [x] minimal\n- [x] callstatic\n- [x] object\n- [x] singleton\n- [x] static\n- [x] wide\n- [x] ThreadExample\n- [ ] CoroutineExample\n- [ ] AnnotationTest\n- [ ] ExceptionTest\n- [ ] IOTest\n- [ ] NativeExample\n- [ ] ReflectionTest\n- [ ] SynchronizedTest\n\n## TODO\n- Handle ClassRef and reflection using ByteBuddy\n- JNI callback\n- Optimization\n\n# Open Source\n- Read some code of [Katalina](https://github.com/huuck/Katalina)\n- Read some code of [Android Open Source Project](https://android.googlesource.com/platform/dalvik.git/+/android-4.2.2_r1) after commit abba8ab\n\n# CGLib\nuse \n```\n--add-opens java.base/java.lang=ALL-UNNAMED\n```\nJVM option if you encounter issues with CGLib.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyhs0602%2Fkovik","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyhs0602%2Fkovik","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyhs0602%2Fkovik/lists"}