Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/yhs0602/kovik
Dalvik emulator written in Kotlin, highly inspired by Katalina
https://github.com/yhs0602/kovik
dalvik dex emulation multidex reverse-engineering unicorn-engine
Last synced: 2 months ago
JSON representation
Dalvik emulator written in Kotlin, highly inspired by Katalina
- Host: GitHub
- URL: https://github.com/yhs0602/kovik
- Owner: yhs0602
- Created: 2024-04-28T13:58:23.000Z (8 months ago)
- Default Branch: main
- Last Pushed: 2024-05-19T14:36:54.000Z (8 months ago)
- Last Synced: 2024-05-20T06:39:59.273Z (8 months ago)
- Topics: dalvik, dex, emulation, multidex, reverse-engineering, unicorn-engine
- Language: Kotlin
- Homepage:
- Size: 13.3 MB
- Stars: 9
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Kovik
Dalvik emulator written in Kotlin, highly inspired by [Katalina](https://github.com/huuck/Katalina)
## Ability
It can run the code below seamlessly.
```kotlin
package com.example.sampleimport java.io.File
import java.util.Arrays
import kotlin.math.absclass Point(val x: Int, val y: Int) {
constructor(x: Int) : this(x, x)fun L0Norm(): Int {
return x + y
}fun L1Norm(): Int {
return abs(x) + abs(y)
}
}open class AccessTest(
val name: String,
private val age: Int,
protected val id: Int,
var variable: Int = 0
) {
fun printAge() {
println("Age: $age")
}fun printId() {
println("Id: $id")
}
}class InheritanceTest(
name: String,
age: Int,
id: Int,
val address: String
) : AccessTest(name, age, id) {
fun printAddress() {
println("Address: $address")
}fun printAll() {
println("Name: $name")
printAge()
printId()
printAddress()
}
}open class OverridingTest(var myProp: Int = 0) {
open fun printMessage() {
println("This is the parent class")
}
}class ChildOverridingTest : OverridingTest() {
override fun printMessage() {
println("This is the child class")
myProp = 5
}
}open class SuperCallingTest {
open fun printMessage() {
println("This is the parent class")
}
}class ChildSuperCallingTest : SuperCallingTest() {
override fun printMessage() {
super.printMessage()
println("This is the child class")
}
}class MockedSuperTest(
path: String,
val myProp: Int
) : File(path) {
override fun exists(): Boolean {
return true
}private val myProp2 = 5
override fun compareTo(other: File): Int {
return myProp2
}override fun toString(): String {
val superx = super.toString()
return "$superx, $myProp"
}
}class OverloadedTest {
fun printMessage() {
println("This is the parent class")
}fun printMessage(message: String) {
println("This is the parent class with message: $message")
}
}class A : Comparable {
companion object {
fun sortSelf() {
val As = arrayOfNulls(3)
As[0] = A()
As[1] = A()
As[2] = A()
Arrays.sort(As)
}
}override fun compareTo(other: A): Int {
return 0
}
}fun testObjects() {
val point = Point(3, -4)
println("L0 Norm: ${point.L0Norm()}") // Output: L0 Norm: -1
println("L1 Norm: ${point.L1Norm()}") // Output: L1 Norm: 7
val point2 = Point(5)
println("L0 Norm: ${point2.L0Norm()}") // Output: L0 Norm: 10val accessTest = AccessTest("John", 25, 123)
accessTest.printAge() // Output: Age: 25
// accessTest.printId() // Error: Cannot access 'id': it is protected in 'AccessTest'
val inheritanceTest = InheritanceTest("John", 25, 123, "123 Main St")
inheritanceTest.printAll()
// Output:
// Name: John
// Age: 25
// Id: 123
// Address: 123 Main St
val overridingTest = OverridingTest()
overridingTest.printMessage() // Output: This is the parent class
val childOverridingTest = ChildOverridingTest()
childOverridingTest.printMessage() // Output: This is the child class
val childSuperCallingTest = ChildSuperCallingTest()
childSuperCallingTest.printMessage()
// Output:
// This is the parent class
// This is the child class
OverloadedTest().printMessage()
// Output: This is the parent class
OverloadedTest().printMessage("Hello")
// Output: This is the parent class with message: Helloval mockedSuperTest = MockedSuperTest("path", 10)
println(mockedSuperTest.exists()) // Output: true
println(mockedSuperTest.compareTo(File("path"))) // Output: 5
println(mockedSuperTest.toString()) // Output: path, 10
A.sortSelf()
}
```## Usage
This example shows how to parse a .dex file and print out some information about it.
```kotlin
package com.yhs0602import com.yhs0602.dex.DexFile
import com.yhs0602.vm.Environment
import com.yhs0602.vm.GeneralMockedClass
import com.yhs0602.vm.RegisterValue
import com.yhs0602.vm.executeMethod
import java.io.File
import java.io.PrintStream
import java.util.Arrays
import kotlin.jvm.internal.Intrinsicsfun main() {
// Surprisingly, multidex is default nowadays
println("Enter the path of the folder of dexes:")
val path = readlnOrNull() ?: return
val dexes = File(path).listFiles { _, name ->
name.endsWith(".dex")
} ?: return
dexes.forEach {
println(it.name)
}
val parsedDexes = dexes.map {
DexFile.fromFile(it)
}
// for (dex in parsedDexes) {
// println(dex.header)
// println(dex.typeIds)
// println(dex.protoIds)
// println(dex.fieldIds)
// println(dex.methodIds)
// println(dex.classDefs)
// println(dex.callSiteIds)
// println(dex.methodHandles)
//// println(dex.strings)
// break
// }
// Find entry point, and setup start environment
println("Enter the package name to search:")
val packageName = "com.example.sample"// readlnOrNull() ?: return
val packageNameStr = packageName.replace(".", "/")
println("Classes====================")
val classes = parsedDexes.flatMap { it.classDefs }
for (clazz in classes) {
if (clazz.classDef.typeId.descriptor.startsWith("L$packageNameStr/")) {
println(clazz)
}
}
println("Enter the class name you are interested in:")
val className =
"ObjectExampleKt" // readlnOrNull() ?: return TargetMethods StaticExample WideTest CallStatic testObjects
val classNameStr = "L$packageNameStr/$className;"
val classDef = classes.find { it.classDef.typeId.descriptor == classNameStr } ?: return
println("Methods====================")
val methods = classDef.classData?.run {
directMethods + virtualMethods
} ?: run {
println("No methods found")
return
}
for (method in methods) {
println(method)
}
println("Enter the method name you are interested in:")
val methodName = "testObjects" // readlnOrNull() ?: return
val method = methods.find { it.methodId.name == methodName } ?: return
println("Code====================")
val codeItem = method.codeItem ?: run {
println("No code found")
return
}
// println(codeItem)
val code = codeItem.insns
for (insn in code) {
println(insn)
}
// Input parameters based on the method signature
val args = Array(codeItem.insSize.toInt()) {
RegisterValue.Int(0)
}
val mockedClassesList = listOf(
GeneralMockedClass(StringBuilder::class.java),
GeneralMockedClass(PrintStream::class.java),
GeneralMockedClass(System::class.java),
GeneralMockedClass(Intrinsics::class.java),
GeneralMockedClass(Object::class.java),
GeneralMockedClass(Math::class.java),
GeneralMockedClass(File::class.java),
GeneralMockedClass(Arrays::class.java),
GeneralMockedClass(Comparable::class.java),
)
val mockedMethodList = mockedClassesList.flatMap {
it.getMethods()
}val mockedMethods = mockedMethodList.associateBy {
Triple(it.classId, it.parameters, it.name)
}
val mockedClasses = mockedClassesList.associateBy {
it.classId
}
val environment = Environment(
parsedDexes,
mockedMethods,
mockedClasses,
beforeInstruction = { pc, instruction, memory, depth ->
val indentation = " ".repeat(depth)
println("$indentation Before $instruction: $pc// ${memory.registers.toList()} exception=${memory.exception}") // Debug
},
afterInstruction = { pc, instruction, memory, depth ->
val indentation = " ".repeat(depth)
println("$indentation After $instruction: $pc// ${memory.registers.toList()} exception=${memory.exception}") // Debug
}
)
executeMethod(codeItem, environment, args, codeItem.insSize.toInt(), 0)
}
```## Current status
- Basic .dex parsing support
- Parse method body
- Emulate instructions
- Represent real / mocked (lazy) values
- Mock System static fields
- Easier mocking of classes
- Call for initialization of static fields
- Mock non-intrinsic Objects## Test pass status
- [x] minimal
- [x] callstatic
- [x] object
- [x] singleton
- [x] static
- [x] wide
- [x] ThreadExample
- [ ] CoroutineExample
- [ ] AnnotationTest
- [ ] ExceptionTest
- [ ] IOTest
- [ ] NativeExample
- [ ] ReflectionTest
- [ ] SynchronizedTest## TODO
- Handle ClassRef and reflection using ByteBuddy
- JNI callback
- Optimization# Open Source
- Read some code of [Katalina](https://github.com/huuck/Katalina)
- Read some code of [Android Open Source Project](https://android.googlesource.com/platform/dalvik.git/+/android-4.2.2_r1) after commit abba8ab# CGLib
use
```
--add-opens java.base/java.lang=ALL-UNNAMED
```
JVM option if you encounter issues with CGLib.