Skip to content

Library for executable/object files parsing(include PE version info), in Kotlin multiplatform.

License

Notifications You must be signed in to change notification settings

iseki0/Executables

Repository files navigation

Executables

GitHub Actions Workflow Status Maven Central Version License codecov

An executable file parsing library, written in Kotlin Multiplatform.

Currently, this library can:

  • PE: Read basic information (COFF header, optional header, sections)
  • PE: Read version information (aka. VS_VERSIONINFO structure)
  • PE: Read import/export symbol tables
  • PE: Read resources and section data
  • ELF: Read basic information (ELF Header, Section headers, Program headers)
  • ELF: Read symbol tables
  • ELF: Read section data
  • Macho: Read basic information
  • Chore: Jigsaw ready

This project is currently in the early stages of development. This means it is still evolving and may undergo frequent updates and changes.

Due to the current status of Kotlin/Multiplatform, it can be expected that for a period of time following the stable release, binary compatibility guarantees will only be provided for Kotlin/JVM.

Kotlin targets

  • Kotlin/JVM: Java 8 or later (Jigsaw since Java 9 is supported by multi-release jar)
  • Kotlin/JavaScript

If you need more targets, feel free to open an issue or pull request directly.

Getting Started

Add dependency

This project contains several modules on Maven Central:

If the dependency size is not a concern, you can use executables-all directly.

Gradle

dependencies {
    implementation("space.iseki.executables:executables-all:0.0.14")
}

Maven

Since the project is in Kotlin Multiplatform, for Maven user you have to specify the platform explicitly. (The -jvm suffix)

<dependency>
    <groupId>space.iseki.executables</groupId>
    <artifactId>executables-all-jvm</artifactId>
    <version>0.0.14</version>
</dependency>

Samples

Print information of a PE file

import java.nio.file.Path
import kotlinx.serialization.json.Json
import space.iseki.executables.pe.PEFile

fun main() {
    val file = Path.of("C:\\System32\\notepad.exe")
    PEFile.open(file).use { peFile: PEFile ->
        println(peFile.coffHeader)
        println(peFile.summary)
        println(Json.encodeToString(peFile.summary))
        println(peFile.versionInfo)
    }
}

Detect the type of file

import java.nio.file.Path
import space.iseki.executables.common.ExecutableFile
import space.iseki.executables.common.ExecutableFileType

fun main() {
    val file = Path.of("C:\\System32\\notepad.exe")
    println(ExecutableFileType.detect(file))
}

Read PE file import symbols

This API also works for ELF files.

import java.nio.file.Path
import space.iseki.executables.pe.PEFile

fun main() {
    val file = Path.of("C:\\System32\\notepad.exe")
    PEFile.open(file).use { peFile ->
        // Get all imported DLLs and functions
        peFile.importSymbols.forEach { symbol ->
            println("Imported: ${symbol.file}::${symbol.name}")
            if (symbol.isOrdinal) {
                println("  - Ordinal: ${symbol.ordinal}")
            }
        }
    }
}

Read PE file export symbols

This API also works for ELF files.

import java.nio.file.Path
import space.iseki.executables.pe.PEFile

fun main() {
    val file = Path.of("C:\\System32\\kernel32.dll")
    PEFile.open(file).use { peFile ->
        // Get all exported functions
        val exportSymbols = peFile.exportSymbols
        println("Number of exported functions: ${exportSymbols.size}")

        // Print the first 10 exported functions
        exportSymbols.take(10).forEach { symbol ->
            println("  - ${symbol.name}, Ordinal: ${symbol.ordinal}")
            if (symbol.isForwarder) {
                println("    Forwards to: ${symbol.forwarderString}")
            }
        }
    }
}

Read ELF symbol tables

Since PE splits the symbol table into import/export, this API is ELF specified.

import java.nio.file.Path
import space.iseki.executables.elf.ELFFile

fun main() {
    val file = Path.of("/bin/ls")  // ELF file on Linux system
    ELFFile.open(file).use { elfFile ->
        // Get all symbols
        elfFile.symbols.forEach { symbol ->
            val name = symbol.name ?: "<unnamed>"
            println("  - $name: Name=${symbol.name}, Binding=${symbol.binding}")
        }
    }
}

References