{"id":15060539,"url":"https://github.com/notiska/kirjava","last_synced_at":"2025-04-10T06:10:11.741Z","repository":{"id":183777150,"uuid":"580477826","full_name":"notiska/kirjava","owner":"notiska","description":"A Java bytecode library for Python.","archived":false,"fork":false,"pushed_at":"2024-12-17T16:50:58.000Z","size":9347,"stargazers_count":10,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-24T07:14:11.084Z","etag":null,"topics":["assembler","bytecode","disassembler","java","jvm"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/notiska.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","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":"2022-12-20T16:56:12.000Z","updated_at":"2024-12-09T11:43:19.000Z","dependencies_parsed_at":"2023-10-01T21:47:35.959Z","dependency_job_id":"bab18757-278f-488b-a1ca-6b3bfb8559ec","html_url":"https://github.com/notiska/kirjava","commit_stats":{"total_commits":63,"total_committers":2,"mean_commits":31.5,"dds":"0.15873015873015872","last_synced_commit":"f2d339128af77e414e295a1de878fef8ff005f1b"},"previous_names":["node3112/kirjava","notiska/kirjava"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/notiska%2Fkirjava","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/notiska%2Fkirjava/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/notiska%2Fkirjava/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/notiska%2Fkirjava/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/notiska","download_url":"https://codeload.github.com/notiska/kirjava/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248166925,"owners_count":21058481,"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":["assembler","bytecode","disassembler","java","jvm"],"created_at":"2024-09-24T23:00:11.496Z","updated_at":"2025-04-10T06:10:11.719Z","avatar_url":"https://github.com/notiska.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# kirjava\n\n![](kirjava.png)\nArtwork by [Lou](https://www.instagram.com/devils_destination/).\n\nA pure-Python Java bytecode manipulation library with decent obfuscation resilience.  \n\nDocumentation is planned for in the future, but as of right now, a quickstart guide has been provided below.  \nFor more usage, see [examples](examples/).\n\nJust as a note, this is *very much* a hobby project so my maintenance schedule will fluctuate a lot. If you have any bug fixes, PRs are welcome.\n\n**Active development is mostly done on the `dev` branch, if you're curious about new features.**\n\n## Quickstart\n\n### Installing\n\n`python\u003e=3.10` is required, any other versions are untested.  \n\nYou can install this library by either:\n 1. Installing via pip: `pip3 install kirjava-jvm`.\n 2. Cloning this repository and installing it manually:  \n    - `git clone https://github.com/node3112/kirjava.git kirjava`\n    - `cd kirjava`\n    - `python3 setup.py install` or, if you lack permissions: `python3 setup.py install --user`\n\n*Additionally, [PyPy](https://www.pypy.org/) does appear to work and can result in significant performance gains.*\n\n### Getting started\n\nSimply import kirjava, no extra steps are required once installed:\n\n```python3\nIn [1]: import kirjava\n```\n\n### Reading classfiles\n\nkirjava contains quite a few shortcuts for various tedious tasks, an example:\n\n```python3\nIn [2]: cf = kirjava.load(\"Test.class\")\n\nIn [3]: cf\nOut[3]: \u003cClassFile(name='Test') at 7fc10a2245c0\u003e\n```\n\nThis is *roughly equivalent* to:\n\n```python3\nIn [2]: with open(\"Test.class\", \"rb\") as stream:\n   ...:     cf = kirjava.ClassFile.read(stream)\n   ...: \n\nIn [3]: cf\nOut[3]: \u003cClassFile(name='Test') at 7fc10a2245c0\u003e\n```\n\nWhatever you choose to use is up to you.  \nThe latter is likely more performant than the former, but if you just wish to inspect a classfile in an interactive shell, the shortcut is always available for use.\n\n### Inspecting the class\n\nViewing all the methods in the class can be done via:\n\n```python3\nIn [4]: cf.methods\nOut[4]: \n(\u003cMethodInfo(name='main', argument_types=(java/lang/String[],), return_type=void) at 7fc10a069a80\u003e,\n \u003cMethodInfo(name='\u003cinit\u003e', argument_types=(), return_type=void) at 7fc10a0698a0\u003e,\n \u003cMethodInfo(name='test', argument_types=(boolean,), return_type=void) at 7fc10a069ae0\u003e,\n \u003cMethodInfo(name='test2', argument_types=(), return_type=void) at 7fc10a069ba0\u003e)\n```\n\nAnd similarly, the fields:\n\n```python3\nIn [5]: cf.fields\nOut[5]: (\u003cFieldInfo(name='field', type=int) at 7fc10a069b40\u003e,)\n```\n\nThe same goes for attributes, although this example file does not contain any:\n\n```python3\nIn [6]: cf.attributes\nOut[6]: {}\n```\n\n### Editing bytecode\n\nCreating valid bytecode can be quite an annoyance, so kirjava provides functionality that allows you to edit methods with ease.  \nThe main classes you'll be using for this are `InsnGraph`, `InsnBlock` and `InsnEdge`.\n\n#### Disassembly\n\nTo disassemble a method, you can use the shortcut:\n\n```python3\nIn [7]: graph = kirjava.disassemble(cf.get_method(\"test\"))\n\nIn [8]: graph\nOut[8]: \u003cInsnGraph(blocks=10, edges=12) at 7fc10abfed50\u003e\n```\n\nOr more verbosely:\n\n```python3\nIn [7]: graph = kirjava.analysis.InsnGraph.disassemble(cf.get_method(\"test\"))\n\nIn [8]: graph\nOut[8]: \u003cInsnGraph(blocks=10, edges=12) at 7fc10abfed50\u003e\n```\n\nYou can then view the blocks and edges present in the graph:\n\n```python3\nIn [9]: graph.blocks\nOut[9]: \n(\u003cInsnBlock(label=0, instructions=[iload_1]) at 7fc10a1ed340\u003e,\n \u003cInsnReturnBlock() at 7fc10b60e5d0\u003e,\n \u003cInsnRethrowBlock() at 7fc10ab8f5c0\u003e,\n \u003cInsnBlock(label=1, instructions=[aload_0, iconst_0, putfield Test.field:I]) at 7fc10abc9f80\u003e,\n \u003cInsnBlock(label=2, instructions=[aload_0, getfield Test.field:I]) at 7fc10abcac40\u003e,\n \u003cInsnBlock(label=3, instructions=[iconst_0]) at 7fc10a2138c0\u003e,\n \u003cInsnBlock(label=4, instructions=[]) at 7fc10a211f00\u003e,\n \u003cInsnBlock(label=5, instructions=[iload_1]) at 7fc10a210340\u003e,\n \u003cInsnBlock(label=6, instructions=[]) at 7fc10a2103c0\u003e,\n \u003cInsnBlock(label=7, instructions=[iinc 1 by 1]) at 7fc10a213240\u003e)\n\nIn [10]: graph.edges\nOut[10]: \n(\u003cFallthroughEdge(from=block 0, to=block 1)\u003e,\n \u003cJumpEdge(from=block 0, to=block 2, instruction=ifne)\u003e,\n \u003cFallthroughEdge(from=block 1, to=block 2)\u003e,\n \u003cFallthroughEdge(from=block 2, to=block 3)\u003e,\n \u003cJumpEdge(from=block 2, to=block 4, instruction=ifgt)\u003e,\n \u003cJumpEdge(from=block 3, to=block 5, instruction=ifeq)\u003e,\n \u003cFallthroughEdge(from=block 3, to=block 4)\u003e,\n \u003cJumpEdge(from=block 4, to=return block, instruction=return)\u003e,\n \u003cFallthroughEdge(from=block 5, to=block 6)\u003e,\n \u003cJumpEdge(from=block 5, to=block 7, instruction=ifeq)\u003e,\n \u003cJumpEdge(from=block 6, to=return block, instruction=return)\u003e,\n \u003cJumpEdge(from=block 7, to=return block, instruction=return)\u003e)\n```\n\n#### Editing blocks\n\nSay for example you wanted to change the value of `Test.field` from `0` to `17`, you could do this:\n\n```python3\nIn [11]: graph[1].remove(kirjava.instructions.iconst_0)\n    ...: graph[1].insert(1, kirjava.instructions.bipush(17))\nOut[11]: \u003cConstantInstruction(opcode=0x10, mnemonic=bipush, constant=\u003cInteger(17)\u003e) at 7fc10a213480\u003e\n```\n\nAnd just to check that we have edited the block correctly:\n\n```python3\nIn [12]: graph[1]\nOut[12]: \u003cInsnBlock(label=1, instructions=[aload_0, bipush 17, putfield Test.field:I]) at 7fc10abc9f80\u003e\n```\n\n#### Editing edges\n\nNow let's edit an edge. Firstly let's find one that we can edit easily for the sake of tutorial:\n\n```python3\nIn [13]: graph.out_edges(graph[2])\nOut[13]: \n(\u003cFallthroughEdge(from=block 2, to=block 3)\u003e,\n \u003cJumpEdge(from=block 2, to=block 4, instruction=ifgt)\u003e)\n```\n\nLet's change the `ifgt` instruction into an `iflt` for this example:\n\n```python3\nIn [14]: graph.jump(graph[2], graph[4], kirjava.instructions.iflt)\nOut[14]: \u003cJumpEdge(from=block 2, to=block 4, instruction=iflt)\u003e\n```\n\nAnd, to check:\n\n```python3\nIn [15]: graph.out_edges(graph[2])\nOut[15]: \n(\u003cFallthroughEdge(from=block 2, to=block 3)\u003e,\n \u003cJumpEdge(from=block 2, to=block 4, instruction=iflt)\u003e)\n```\n\nAs you can see we've managed to successfully edit the jump condition.  \n\nThere's a lot more that can be done than just these simple tutorials though **(have a play around!)**.\n\n### Analysing bytecode\n\nOften editing a method goes hand-in-hand with analysing it, and kirjava provides tools that allow you to statically analyse the data on the stack and in the locals via the use of the class `Trace`.\n\nTo create a trace for a method, you'll need to use the graph for said method. In this example, we'll use the graph from the previous examples:\n\n```python3\nIn [16]: trace = kirjava.trace(graph)\n\nIn [17]: trace\nOut[17]: \u003cTrace(entries=9, exits=9, conflicts=0, subroutines=0, max_stack=2, max_locals=2) at 7fc10abff4c0\u003e\n```\n\nAnd again, the more verbose method:\n\n```python3\nIn [16]: trace = kirjava.analysis.Trace.from_graph(graph)\n\nIn [17]: trace\nOut[17]: \u003cTrace(entries=9, exits=9, conflicts=0, subroutines=0, max_stack=2, max_locals=2) at 7fc10abff4c0\u003e\n```\n\nThe `Trace` class provides pre/post liveness information (on a per-block basis) as well as information on subroutines, type conflicts and frames at block entries/exits.\n\nFor example, we could look at the local pre-liveness for block 3:\n\n```python3\nIn [18]: trace.pre_liveness[graph[3]]\nOut[18]: {1}\n```\n\nWe could also view the state of the stack at the entry to it:\n\n```python3\nIn [19]: trace.entries[graph[3]]\nOut[19]: [\u003cFrame(stack=[], locals={0=Test, 1=boolean}) at 7fc109ee7f10\u003e]\n```\n\nAnd we can even inspect individual locals further:  \n\n```python3\nIn [20]: trace.entries[graph[3]][0].locals\nOut[20]: \n{0: \u003cEntry(type=Test, constraints={Test, reference, java/lang/Object}) at 7fc109ee69d0\u003e,\n 1: \u003cEntry(type=boolean, constraints={int, boolean}) at 7fc109ee7ce0\u003e}\n\nIn [21]: trace.entries[graph[3]][0].locals[0].constraints\nOut[21]: \n(\u003cEntry.Constraint(type=reference, source=aload_0 @ block 1[0], original=False)\u003e,\n \u003cEntry.Constraint(type=Test, source=getfield Test.field:I @ block 2[1], original=False)\u003e,\n \u003cEntry.Constraint(type=Test, source=putfield Test.field:I @ block 1[2], original=False)\u003e,\n \u003cEntry.Constraint(type=java/lang/Object, source=None, original=True)\u003e,\n \u003cEntry.Constraint(type=Test, source=param 0 of Test#void test(boolean), original=True)\u003e,\n \u003cEntry.Constraint(type=reference, source=aload_0 @ block 2[0], original=False)\u003e)\n\nIn [22]: trace.entries[graph[3]][0].locals[1].producers\nOut[22]: \n(\u003cInstructionInBlock(index=0, block=block 7, instruction=iinc 1 by 1)\u003e,\n \u003cFrame.Parameter(index=1, type=boolean, method=Test#void test(boolean))\u003e)\n\nIn [23]: trace.entries[graph[3]][0].locals[1].consumers\nOut[23]: \n(\u003cInstructionInBlock(index=0, block=block 0, instruction=iload_1)\u003e,\n \u003cJumpEdge(from=block 0, to=block 2, instruction=ifne)\u003e,\n \u003cInstructionInBlock(index=0, block=block 5, instruction=iload_1)\u003e,\n \u003cJumpEdge(from=block 5, to=block 7, instruction=ifeq)\u003e,\n \u003cInstructionInBlock(index=0, block=block 7, instruction=iinc 1 by 1)\u003e)\n```\n\n#### Assembly\n\nReassembling the method after editing is as easy as:\n\n```python3\nIn [24]: kirjava.assemble(graph)\n```\n\nOr:\n\n```python3\nIn [24]: graph.method.code = graph.assemble()\n```\n\n### Writing classfiles\n\nWriting classfiles back out is also easy:\n\n```python3\nIn [25]: kirjava.dump(cf, \"Test-edited.class\")\n```\n\nOr for the more verbose method:\n\n```python3\nIn [25]: with open(\"Test-edited.class\", \"wb\") as stream:\n    ...:     cf.write(stream)\n    ...: \n```\n\n## \"Trivia\"\n\nIt's honestly not super interesting, but if anyone was wondering, it IS named after a certain character from a certain book series.  \nThe name is not a Java-related pun, but it does help that \"java\" is in the name.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnotiska%2Fkirjava","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnotiska%2Fkirjava","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnotiska%2Fkirjava/lists"}