Guest User

Untitled

a guest
Jan 19th, 2018
88
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.87 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import tkinter as tk
  4. from tkinter import ttk
  5. import widgets
  6. from struct import unpack_from, pack
  7. from collections import OrderedDict
  8.  
  9. MONOSPACE = ("Consolas", 9)
  10. ARIAL = ("Arial, 8")
  11.  
  12. OP_CODES = {}
  13. with open("opcodes") as f:
  14. for i in f.read().splitlines():
  15. data = i.split(";")
  16. OP_CODES[data[0].upper()] = data[1:]
  17.  
  18.  
  19. def fancy_newlines(s, maxPerLine=128):
  20. s += " "
  21. out = []
  22. while s:
  23. line, rest = s[:maxPerLine].rsplit(" ", 1)
  24. out.append(line)
  25. s = rest + s[maxPerLine:]
  26. return "\n".join(out)
  27.  
  28.  
  29. class App(tk.Frame):
  30. def __init__(self, master):
  31. tk.Frame.__init__(self, master, border=5)
  32. w, h = 700, 500
  33. master.geometry("%dx%d+100+100" % (w, h))
  34. master.minsize(w, h)
  35. self.entryVariable = tk.StringVar()
  36. self.current = [0, 0]
  37.  
  38. self.filebutton = tk.Menubutton(self, text="File", bd=0, relief="flat",
  39. activebackground="blue", activeforeground="white")
  40. self.filemenu = tk.Menu(self.filebutton, tearoff=0)
  41. self.notebook = ttk.Notebook(self)
  42. self.constantPool = tk.Frame(self)
  43. self.cpList = widgets.ScrolledListbox(self.constantPool, font=MONOSPACE)
  44. self.cpXscrollbar = widgets.AutoScrollbar(self.constantPool, command=self.cpList.xview, orient="horizontal")
  45. self.methods = tk.Frame(self)
  46. self.methodFrame = tk.Frame(self.methods)
  47. self.methodList = widgets.ScrolledListbox(self.methodFrame, width=26, height=5)
  48. self.methodLabelFrame = ttk.Labelframe(self.methodFrame, text="Selected instruction")
  49. self.methodEntry = tk.Entry(self.methodLabelFrame, width=16, font=MONOSPACE, textvariable=self.entryVariable)
  50. self.methodLabel1 = tk.Label(self.methodLabelFrame, anchor="w", font=ARIAL)
  51. self.methodLabel2 = tk.Label(self.methodLabelFrame, anchor="w", font=ARIAL, justify="left")
  52. kw = {"width": 8, "font": MONOSPACE, "activestyle": "dotbox"}
  53. self.methodIndexes = tk.Listbox(self.methods, **kw)
  54. self.methodCode = tk.Listbox(self.methods, **kw)
  55. self.methodExplanation = tk.Listbox(self.methods, **kw)
  56. self.methodXscrollbar = widgets.AutoScrollbar(self.methods, command=self.methodExplanation.xview, orient="horizontal")
  57. self.methodYscrollbar = widgets.AutoScrollbar(self.methods, command=self.yview_methods)
  58.  
  59. self.filebutton.config(menu=self.filemenu)
  60. self.filemenu.add_command(label="Open")
  61. self.filemenu.add_command(label="Save")
  62. self.notebook.add(self.constantPool, text="Constant Pool")
  63. self.notebook.add(self.methods, text="Methods")
  64. self.cpList.config(xscrollcommand=self.cpXscrollbar.set)
  65. self.methodIndexes.config(yscrollcommand=self.yscroll_methods)
  66. self.methodCode.config(yscrollcommand=self.yscroll_methods)
  67. self.methodExplanation.config(xscrollcommand=self.methodXscrollbar.set, yscrollcommand=self.yscroll_methods)
  68. # app
  69. self.columnconfigure(0, weight=1)
  70. self.rowconfigure(1, weight=1)
  71. self.filebutton.grid(column=0, row=0, sticky="NSW")
  72. self.notebook.grid(column=0, row=1, sticky="NSEW")
  73. # cp
  74. self.constantPool.columnconfigure(0, weight=1)
  75. self.constantPool.rowconfigure(0, weight=1)
  76. self.cpList.grid(column=0, row=0, sticky="NSEW")
  77. self.cpXscrollbar.grid(column=0, row=1, sticky="NSEW")
  78. # methods
  79. self.methods.columnconfigure(2, weight=1)
  80. self.methods.rowconfigure(1, weight=1)
  81. self.methodFrame.columnconfigure(1, weight=1)
  82. self.methodLabelFrame.columnconfigure(1, weight=1)
  83.  
  84. self.methodFrame.grid(column=0, columnspan=3, row=0, sticky="NSEW")
  85. self.methodList.grid(column=0, row=0, sticky="NSEW")
  86. self.methodLabelFrame.grid(column=1, row=0, sticky="NSEW")
  87. self.methodEntry.grid(column=0, row=0, sticky="NSW")
  88. self.methodLabel1.grid(column=1, row=0, sticky="NSEW")
  89. self.methodLabel2.grid(column=0, columnspan=2, row=1, sticky="NSEW")
  90.  
  91. self.methodIndexes.grid(column=0, row=1, sticky="NSEW", padx=2, pady=5)
  92. self.methodCode.grid(column=1, row=1, sticky="NSEW", padx=2, pady=5)
  93. self.methodExplanation.grid(column=2, row=1, sticky="NSEW", padx=2, pady=5)
  94. self.methodXscrollbar.grid(column=0, columnspan=3, row=2, sticky="NSEW")
  95. self.methodYscrollbar.grid(column=3, row=0, rowspan=3, sticky="NSEW")
  96.  
  97. self.methodList.bind("<<ListboxSelect>>", self.select_method)
  98. self.methodIndexes.bind("<<ListboxSelect>>", self.select_index)
  99. self.methodCode.bind("<<ListboxSelect>>", self.select_index)
  100. self.methodExplanation.bind("<<ListboxSelect>>", self.select_index)
  101. self.methodEntry.bind("<Return>", self.update_code)
  102.  
  103. #
  104. self.f = File("CvcMinigame.xclass")
  105. self.refresh()
  106.  
  107. def yview_methods(self, *arg):
  108. self.methodIndexes.yview(*arg)
  109. self.methodCode.yview(*arg)
  110. self.methodExplanation.yview(*arg)
  111.  
  112. def yscroll_methods(self, *arg):
  113. self.methodIndexes.yview_moveto(arg[0])
  114. self.methodCode.yview_moveto(arg[0])
  115. self.methodExplanation.yview_moveto(arg[0])
  116. self.methodYscrollbar.set(*arg)
  117.  
  118. def refresh(self):
  119. self.code = []
  120. self.cpList.delete(0, "end")
  121. for a, i in enumerate(self.f.constantPool):
  122. if a == 0:
  123. continue
  124. short_name = i[0][9:]
  125. s = "{0:04X};{1}: {2}".format(a, short_name, self.f.format(a))
  126. self.cpList.insert("end", s)
  127. for method in self.f.methods:
  128. name = self.f.cp(method[1])[1]
  129. for attribute in method[4]:
  130. if len(attribute) == 5:
  131. code = parse_code(attribute[3])
  132. break
  133. self.methodList.insert("end", name)
  134. self.code.append(code)
  135.  
  136. def select_method(self, _):
  137. self.methodIndexes.delete(0, "end")
  138. self.methodCode.delete(0, "end")
  139. self.methodExplanation.delete(0, "end")
  140. if self.methodList.curselection():
  141. self.current[0] = self.methodList.curselection()[0]
  142. a = 0
  143. for line in self.code[self.current[0]]:
  144. self.methodIndexes.insert("end", "%08X" % a)
  145. self.methodCode.insert("end", " ".join(line))
  146. op = OP_CODES[line[0]]
  147. explanation = op[0].ljust(20, " ")
  148. if "indexbyte" in op[1]:
  149. explanation += " %s" % self.f.format(int(line[1] + line[2], 16))
  150. elif "index" in op[1]:
  151. explanation += " %s" % self.f.format(int(line[1], 16))
  152. elif "branchbyte" in op[1]:
  153. explanation += " pos.%08X" % (a + int(line[1] + line[2], 16))
  154. self.methodExplanation.insert("end", explanation)
  155. a += len(line)
  156.  
  157. def select_index(self, e):
  158. if e and e.widget.curselection():
  159. self.current[1] = e.widget.curselection()[0]
  160. self.methodIndexes.selection_set(self.current[1])
  161. line = self.code[self.current[0]][self.current[1]]
  162. op = OP_CODES[line[0]]
  163. self.entryVariable.set(" ".join(line))
  164. self.methodLabel1.config(text="[%s] %s" % (op[0], op[2]))
  165. self.methodLabel2.config(text=fancy_newlines(op[3], 100))
  166.  
  167. def update_code(self, _):
  168. code = []
  169. for a, line in enumerate(self.code[self.current[0]]):
  170. if a == self.current[1]:
  171. line = self.methodEntry.get().split(" ")
  172. code.extend(line)
  173. raw = bytes.fromhex("".join(code))
  174. for attribute in self.f.methods[self.current[0]][4]:
  175. if len(attribute) == 5:
  176. attribute[3] = raw
  177. self.code[self.current[0]] = parse_code(raw)
  178. self.select_method(None)
  179. self.select_index(None)
  180.  
  181.  
  182. class File():
  183. def __init__(self, path):
  184. self.path = path
  185. self.info = OrderedDict()
  186. with open(path, "rb") as f:
  187. self.data = f.read()
  188. self.parse()
  189. self.unparse()
  190. print(self.data == self.newData)
  191.  
  192. def cp(self, number_or_index):
  193. try:
  194. return "#%d" % number_or_index
  195. except TypeError:
  196. return self.constantPool[int(number_or_index[1:])]
  197.  
  198. def format(self, a):
  199. constant = self.constantPool[a]
  200. if constant[0] == "CONSTANT_String":
  201. return "'%s'" % self.cp(constant[1])[1]
  202. out = []
  203. for j in constant[1:]:
  204. if type(j) is str and j.startswith("#"):
  205. j = self.format(int(j[1:]))
  206. out.append(str(j))
  207. return ", ".join(out).replace("/", ".")
  208.  
  209. def parse(self):
  210. d = self.data
  211. i = 0
  212.  
  213. self.info["magic_number"], self.info["minor_version"], self.info["major_version"], \
  214. self.info["constant_pool_count"] = unpack_from(">IHHH", d, i)
  215. i += 10
  216. # ===========================================================================
  217. self.constantPool = [None]
  218. n = 1
  219. while n < self.info["constant_pool_count"]:
  220. tag = d[i]
  221. i += 1
  222. if tag == 1:
  223. lenght = unpack_from(">H", d, i)[0]
  224. i += 2
  225. text = d[i:i + lenght]
  226. i += lenght
  227. self.constantPool.append(("CONSTANT_Utf8", text.decode()))
  228. elif 3 <= tag <= 4:
  229. name = ("CONSTANT_Integer", "CONSTANT_Float")[tag - 3]
  230. value = unpack_from(">I", d, i)[0]
  231. i += 4
  232. self.constantPool.append((name, value))
  233. elif 5 <= tag <= 6:
  234. n += 1
  235. name = ("CONSTANT_Long", "CONSTANT_Double")[tag - 5]
  236. value = unpack_from(">Q", d, i)[0]
  237. i += 8
  238. self.constantPool.append((name, value))
  239. elif tag == 7:
  240. index = unpack_from(">H", d, i)[0]
  241. i += 2
  242. self.constantPool.append(("CONSTANT_Class", self.cp(index)))
  243. elif tag == 8:
  244. string_index = unpack_from(">H", d, i)[0]
  245. i += 2
  246. self.constantPool.append(("CONSTANT_String", self.cp(string_index)))
  247. elif 9 <= tag <= 11:
  248. name = ("CONSTANT_Fieldref", "CONSTANT_Methodref", "CONSTANT_InterfaceMethodref")[tag - 9]
  249. class_index, name_and_type_index = unpack_from(">HH", d, i)
  250. i += 4
  251. self.constantPool.append((name, self.cp(class_index), self.cp(name_and_type_index)))
  252. elif tag == 12:
  253. name_index, descriptor_index = unpack_from(">HH", d, i)
  254. i += 4
  255. self.constantPool.append(("CONSTANT_NameAndType", self.cp(name_index), self.cp(descriptor_index)))
  256. elif tag == 15:
  257. reference_kind = d[i]
  258. i += 1
  259. reference_index = unpack_from(">H", d, i)[0]
  260. i += 2
  261. self.constantPool.append(("CONSTANT_MethodHandle", reference_kind, self.cp(reference_index)))
  262. elif tag == 16:
  263. descriptor_index = unpack_from(">H", d, i)[0]
  264. i += 2
  265. self.constantPool.append(("CONSTANT_MethodType", self.cp(descriptor_index)))
  266. elif tag == 18:
  267. bootstrap_method_attr_index, name_and_type_index = unpack_from(">HH", d, i)[0]
  268. i += 4
  269. self.constantPool.append(("CONSTANT_InvokeDynamic", self.cp(bootstrap_method_attr_index), self.cp(name_and_type_index)))
  270. else:
  271. raise Exception("!cp error [%d]" % tag)
  272. n += 1
  273. # ===========================================================================
  274. self.info["access_flags"], self.info["this_class"], self.info["super_class"], \
  275. self.info["interfaces_count"] = unpack_from(">HHHH", d, i)
  276. i += 8
  277. self.interfaces = []
  278. for _ in range(self.info["interfaces_count"]):
  279. self.interfaces.append(unpack_from(">H", d, i)[0])
  280. i += 2
  281. self.info["fields_count"] = unpack_from(">H", d, i)[0]
  282. i += 2
  283. self.fields, i = self.parse_fields(d, i, self.info["fields_count"])
  284. self.info["methods_count"] = unpack_from(">H", d, i)[0]
  285. i += 2
  286. self.methods, i = self.parse_fields(d, i, self.info["methods_count"])
  287. self.info["attributes_count"] = unpack_from(">H", d, i)[0]
  288. i += 2
  289. self.attributes, i = self.parse_attributes(d, i, self.info["attributes_count"])
  290.  
  291. def parse_fields(self, d, i, count):
  292. fields = []
  293. for _ in range(count):
  294. access_flags, name_index, descriptor_index, attributes_count = unpack_from(">HHHH", d, i)
  295. i += 8
  296. attributes, i = self.parse_attributes(d, i, attributes_count)
  297. fields.append((access_flags, self.cp(name_index), self.cp(descriptor_index), attributes_count, attributes))
  298. return fields, i
  299.  
  300. def parse_attributes(self, d, i, count):
  301. attributes = []
  302. for _ in range(count):
  303. attribute_name_index = unpack_from(">H", d, i)[0]
  304. i += 2
  305. attribute_length = unpack_from(">I", d, i)[0]
  306. i += 4
  307. info = d[i:i + attribute_length]
  308. if self.constantPool[attribute_name_index][1] == "Code":
  309. max_stack, max_locals, code_length = unpack_from(">HHI", d, i)
  310. code = d[i + 8:i + 8 + code_length]
  311. attributes.append([self.cp(attribute_name_index), attribute_length, (max_stack, max_locals), code, info[8 + code_length:]])
  312. else:
  313. attributes.append([self.cp(attribute_name_index), attribute_length, info])
  314. i += attribute_length
  315. return attributes, i
  316.  
  317. def unparse(self):
  318. d = []
  319. d.append(pack(">IHHH", self.info["magic_number"], self.info["minor_version"],
  320. self.info["major_version"], self.info["constant_pool_count"]))
  321. d.append(unparse_constant_pool(self.constantPool))
  322. d.append(pack(">HHHH", self.info["access_flags"], self.info["this_class"],
  323. self.info["super_class"], self.info["interfaces_count"]))
  324. for i in self.interfaces:
  325. d.append(pack(">H", i))
  326. d.append(pack(">H", self.info["fields_count"]))
  327. d.append(unparse_fields(self.fields))
  328. d.append(pack(">H", self.info["methods_count"]))
  329. d.append(unparse_fields(self.methods))
  330. d.append(pack(">H", self.info["attributes_count"]))
  331. d.append(unparse_attributes(self.attributes))
  332. self.newData = b"".join(d)
  333.  
  334.  
  335. def unparse_constant_pool(constantPool):
  336. d = []
  337. for constant in constantPool:
  338. if constant is None:
  339. continue
  340. name = constant[0]
  341. if name == "CONSTANT_Utf8":
  342. d.append(b"\x01")
  343. d.append(pack(">H", len(constant[1])))
  344. d.append(constant[1].encode())
  345. elif name == "CONSTANT_Integer":
  346. d.append(b"\x03")
  347. d.append(pack(">I", constant[1]))
  348. elif name == "CONSTANT_Float":
  349. d.append(b"\x04")
  350. d.append(pack(">I", constant[1]))
  351. elif name == "CONSTANT_Long":
  352. d.append(b"\x05")
  353. d.append(pack(">Q", constant[1]))
  354. elif name == "CONSTANT_Double":
  355. d.append(b"\x06")
  356. d.append(pack(">Q", constant[1]))
  357. elif name == "CONSTANT_Class":
  358. d.append(b"\x07")
  359. d.append(pack(">H", int(constant[1][1:])))
  360. elif name == "CONSTANT_String":
  361. d.append(b"\x08")
  362. d.append(pack(">H", int(constant[1][1:])))
  363. elif name == "CONSTANT_Fieldref":
  364. d.append(b"\x09")
  365. d.append(pack(">HH", int(constant[1][1:]), int(constant[2][1:])))
  366. elif name == "CONSTANT_Methodref":
  367. d.append(b"\x0A")
  368. d.append(pack(">HH", int(constant[1][1:]), int(constant[2][1:])))
  369. elif name == "CONSTANT_InterfaceMethodref":
  370. d.append(b"\x0B")
  371. d.append(pack(">HH", int(constant[1][1:]), int(constant[2][1:])))
  372. elif name == "CONSTANT_NameAndType":
  373. d.append(b"\x0C")
  374. d.append(pack(">HH", int(constant[1][1:]), int(constant[2][1:])))
  375. elif name == "CONSTANT_MethodHandle":
  376. d.append(b"\x0F")
  377. d.append(bytes([constant[1]]))
  378. d.append(pack(">H", int(constant[2][1:])))
  379. elif name == "CONSTANT_MethodType":
  380. d.append(b"\x10")
  381. d.append(pack(">H", int(constant[1][1:])))
  382. elif name == "CONSTANT_InvokeDynamic":
  383. d.append(b"\x12")
  384. d.append(pack(">HH", int(constant[1][1:]), int(constant[2][1:])))
  385. return b"".join(d)
  386.  
  387.  
  388. def unparse_attributes(attributes):
  389. d = []
  390. for attribute in attributes:
  391. d.append(pack(">H", int(attribute[0][1:])))
  392. if len(attribute) == 5:
  393. d.append(pack(">I", 8 + len(attribute[3]) + len(attribute[4])))
  394. d.append(pack(">HHI", attribute[2][0], attribute[2][1], len(attribute[3])))
  395. d.append(attribute[3])
  396. d.append(attribute[4])
  397. else:
  398. d.append(pack(">I", attribute[1]))
  399. d.append(attribute[2])
  400. return b"".join(d)
  401.  
  402.  
  403. def unparse_fields(fields):
  404. d = []
  405. for field in fields:
  406. d.append(pack(">HHHH", field[0], int(field[1][1:]), int(field[2][1:]), field[3]))
  407. d.append(unparse_attributes(field[4]))
  408. return b"".join(d)
  409.  
  410.  
  411. def parse_code(d):
  412. lines = []
  413. i = 0
  414. while i < len(d):
  415. h = "%02X" % d[i]
  416. i += 1
  417. opc = OP_CODES[h]
  418. args = int(opc[1].split(":")[0])
  419. lines.append([h] + list("%02X" % d[i + j] for j in range(args)))
  420. i += args
  421. return lines
  422.  
  423.  
  424. if __name__ == "__main__":
  425. root = tk.Tk()
  426. root.title("Bytecode editor")
  427. app = App(root)
  428. app.pack(expand=True, fill="both")
  429. root.mainloop()
Add Comment
Please, Sign In to add comment