# plugins

repository: OALabs/UnpacMe-IDA-Byte-Search
file: src/unpacme_search.py
description: UnpacMe IDA Byte Search
wanted_name: UnpacMe Byte Search
comment: UnpacMe Search
created_at: 2023-06-12 18:40:53+00:00
pushed_at: 2023-11-20 01:10:37+00:00
url:
https://github.com/OALabs/UnpacMe-IDA-Byte-Search/blob/99742d1c45f092974b77a0fc6787301e484df9c9/src/unpacme_search.py

content:
import base64
import binascii

import ida_idaapi
import ida_kernwin
import idaapi
import ida_diskio
import ida_bytes
import idc
import ida_ua
import ida_nalt
import idautils
import json
import logging
import requests
import keyring
from datetime import datetime
import webbrowser
import os
from typing import Dict, Any, Tuple
import time

from PyQt5.QtCore import Qt, QByteArray
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QPushButton, QHBoxLayout, QGridLayout, QFormLayout, \
    QLineEdit, QTextEdit, QTableWidget, QTableWidgetItem, QHeaderView, QComboBox, QCheckBox, QFrame, QApplication, QShortcut
from PyQt5.QtGui import QColor, QPixmap, QPainter, QIcon, QFontMetrics, QGuiApplication, QKeySequence


logger = logging.getLogger("UnpacMeSearch")
logger.setLevel(logging.INFO)

UPMS_ICON_32_ENCODED = b'iVBORw0KGgoAAAANSUhEUgAAACAAAABICAYAAACX3ffDAAAKtmlDQ1BJQ0MgUHJvZmlsZQAASImVlwdUU+kSgP9700N' \
                    b'CC4QiJfTeWwApIbQASq82QhIglBATgoLYkMUVWFFERLAs6KqIgo0qNkSxLYoF7AuyiKjrYkFUVN4FDmF333nvnTfnTO' \
                    b'bL3Pnnn/nP/c+ZCwBZgy0UpsHyAKQLMkVhfl60mNg4Gm4YYIEKgIEy0GdzxEJGSEgQQGTW/l0+9gJoyt6xmMr178//q' \
                    b'yhweWIOAFAIwglcMScd4VOIfuQIRZkAoI4gfr0VmcIpvoWwkggpEOHfpzhphj9NccI0o0nTMRFhTIRpAOBJbLYoCQCS' \
                    b'OeKnZXGSkDykqR6sBVy+AOFchN3T0zO4CLcjbIzECBGeyk9P+EuepL/lTJDmZLOTpDzTy7TgvfliYRo7+/88jv8t6Wm' \
                    b'S2T0MESUli/zDEKuFnNn91IxAKQsSFgbPMp87HT/NyRL/yFnmiJlxs8xlewdK16YtDJrlRL4vS5onkxUxyzyxT/gsiz' \
                    b'LCpHslipiMWWaL5vaVpEZK/ck8ljR/TnJE9Cxn8aMWzrI4NTxwLoYp9YskYdL6eQI/r7l9faW9p4v/0i+fJV2bmRzhL' \
                    b'+2dPVc/T8CYyymOkdbG5Xn7zMVESuOFmV7SvYRpIdJ4Xpqf1C/OCpeuzUReyLm1IdIzTGEHhMwyCAEikAFowBvwgRgI' \
                    b'QRpgg2zkfwziQVZn8lZmTjXFzBBmi/hJyZk0BnLbeDSWgGNpTrO1trUDYOruzrwa76nTdxKiXpvzbagFwK11cnLy9Jw' \
                    b'vALlTx+MBIDbO+YyXACA/DMCVdo5ElDXjQ0/9YAARyAEloIZUrAeMgQWwBY7AFXgCHxAAgkEEiAVLAQckg3SklxUgF6' \
                    b'wHBaAIbAHbQSXYC/aBQ+AoOAGaQTu4AC6D6+AWuAcegX4wBF6BUfARTEAQhIPIEAVSg7QhA8gMsoXokDvkAwVBYVAsF' \
                    b'A8lQQJIAuVCG6AiqBSqhKqhWug41ApdgK5CPdADaAAagd5BX2AUTIKVYE3YELaC6TADDoQj4CVwErwczoHz4c1wBVwD' \
                    b'H4Gb4Avwdfge3A+/gsdQACWDoqJ0UBYoOoqJCkbFoRJRItQaVCGqHFWDqke1obpQd1D9qNeoz2gsmoKmoS3Qrmh/dCS' \
                    b'ag16OXoMuRleiD6Gb0J3oO+gB9Cj6O4aM0cCYYVwwLEwMJgmzAlOAKcccwDRiLmHuYYYwH7FYLBVrhHXC+mNjsSnYVd' \
                    b'hi7G5sA/Y8tgc7iB3D4XBqODOcGy4Yx8Zl4gpwO3FHcOdwt3FDuE94Gbw23hbvi4/DC/B5+HL8YfxZ/G38MH6CIE8wI' \
                    b'LgQgglcQjahhLCf0Ea4SRgiTBAViEZEN2IEMYW4nlhBrCdeIj4mvpeRkdGVcZYJleHLrJOpkDkmc0VmQOYzSZFkSmKS' \
                    b'FpMkpM2kg6TzpAek92Qy2ZDsSY4jZ5I3k2vJF8lPyZ9kKbKWsixZruxa2SrZJtnbsm/kCHIGcgy5pXI5cuVyJ+Vuyr2' \
                    b'WJ8gbyjPl2fJr5KvkW+X75McUKAo2CsEK6QrFCocVriq8UMQpGir6KHIV8xX3KV5UHKSgKHoUJoVD2UDZT7lEGVLCKh' \
                    b'kpsZRSlIqUjip1K40qKyrbK0cpr1SuUj6j3E9FUQ2pLGoatYR6gtpL/aKiqcJQ4alsUqlXua0yrjpP1VOVp1qo2qB6T' \
                    b'/WLGk3NRy1Vbatas9oTdbS6qXqo+gr1PeqX1F/PU5rnOo8zr3DeiXkPNWANU40wjVUa+zRuaIxpamn6aQo1d2pe1Hyt' \
                    b'RdXy1ErRKtM6qzWiTdF21+Zrl2mf035JU6YxaGm0ClonbVRHQ8dfR6JTrdOtM6FrpBupm6fboPtEj6hH10vUK9Pr0Bv' \
                    b'V19ZfoJ+rX6f/0IBgQDdINthh0GUwbmhkGG240bDZ8IWRqhHLKMeozuixMdnYw3i5cY3xXROsCd0k1WS3yS1T2NTBNN' \
                    b'm0yvSmGWzmaMY3223WY44xdzYXmNeY91mQLBgWWRZ1FgOWVMsgyzzLZss3VvpWcVZbrbqsvls7WKdZ77d+ZKNoE2CTZ' \
                    b'9Nm887W1JZjW2V7145s52u31q7F7q29mT3Pfo/9fQeKwwKHjQ4dDt8cnRxFjvWOI076TvFOu5z66Er0EHox/YozxtnL' \
                    b'ea1zu/NnF0eXTJcTLn+6Wrimuh52fTHfaD5v/v75g266bmy3ard+d5p7vPvP7v0eOh5sjxqPZ556nlzPA57DDBNGCuM' \
                    b'I442XtZfIq9FrnOnCXM08743y9vMu9O72UfSJ9Kn0eeqr65vkW+c76ufgt8rvvD/GP9B/q38fS5PFYdWyRgOcAlYHdA' \
                    b'aSAsMDKwOfBZkGiYLaFsALAhZsW/B4ocFCwcLmYBDMCt4W/CTEKGR5yOlQbGhIaFXo8zCbsNywrnBK+LLww+EfI7wiS' \
                    b'iIeRRpHSiI7ouSiFkfVRo1He0eXRvfHWMWsjrkeqx7Lj22Jw8VFxR2IG1vks2j7oqHFDosLFvcuMVqycsnVpepL05ae' \
                    b'WSa3jL3sZDwmPjr+cPxXdjC7hj2WwErYlTDKYXJ2cF5xPbll3BGeG6+UN5zollia+CLJLWlb0kiyR3J58ms+k1/Jf5v' \
                    b'in7I3ZTw1OPVg6mRadFpDOj49Pr1VoChIFXRmaGWszOgRmgkLhP3LXZZvXz4qChQdEEPiJeKWTCVkSLohMZb8IBnIcs' \
                    b'+qyvq0ImrFyZUKKwUrb2SbZm/KHs7xzfllFXoVZ1VHrk7u+tyB1YzV1WugNQlrOtbqrc1fO7TOb92h9cT1qet/zbPOK' \
                    b'837sCF6Q1u+Zv66/MEf/H6oK5AtEBX0bXTduPdH9I/8H7s32W3auel7IbfwWpF1UXnR12JO8bWfbH6q+Glyc+Lm7hLH' \
                    b'kj1bsFsEW3q3emw9VKpQmlM6uG3BtqYyWllh2Yfty7ZfLbcv37uDuEOyo78iqKJlp/7OLTu/ViZX3qvyqmrYpbFr067' \
                    b'x3dzdt/d47qnfq7m3aO+Xn/k/36/2q26qMawp34fdl7Xv+f6o/V2/0H+pPaB+oOjAt4OCg/2Hwg511jrV1h7WOFxSB9' \
                    b'dJ6kaOLD5y66j30ZZ6i/rqBmpD0TFwTHLs5fH4470nAk90nKSfrD9lcGpXI6WxsAlqym4abU5u7m+JbelpDWjtaHNta' \
                    b'zxtefpgu0571RnlMyVniWfzz06eyzk3dl54/vWFpAuDHcs6Hl2MuXi3M7Sz+1LgpSuXfS9f7GJ0nbvidqX9qsvV1mv0' \
                    b'a83XHa833XC40firw6+N3Y7dTTedbrbccr7V1jO/5+xtj9sX7njfuXyXdff6vYX3enoje+/3Le7rv8+9/+JB2oO3D7M' \
                    b'eTjxa9xjzuPCJ/JPypxpPa34z+a2h37H/zID3wI1n4c8eDXIGX/0u/v3rUP5z8vPyYe3h2he2L9pHfEduvVz0cuiV8N' \
                    b'XE64I/FP7Y9cb4zak/Pf+8MRozOvRW9HbyXfF7tfcHP9h/6BgLGXv6Mf3jxHjhJ7VPhz7TP3d9if4yPLHiK+5rxTeTb' \
                    b'23fA78/nkyfnBSyRezpUQCFKJyYCMC7gwCQYwGgIDMEcdHMbD0t0Mz3wDSB/8Qz8/e0OAJQj5ipEYl5HoBjiBquA0DO' \
                    b'E4Cp8SjCE8B2dlKdnYOnZ/YpwSJfL/VWmvJy2PshS8E/ZWae/0vd/7RgKqs9+Kf9F1uNDCpTAs42AAAAlmVYSWZNTQA' \
                    b'qAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAA' \
                    b'AAAAAAAJAAAAABAAAAkAAAAAEAA5KGAAcAAAASAAAAhKACAAQAAAABAAAAIKADAAQAAAABAAAASAAAAABBU0NJSQAAA' \
                    b'FNjcmVlbnNob3Tx3tJyAAAACXBIWXMAABYlAAAWJQFJUiTwAAAC1WlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6' \
                    b'eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJ' \
                    b'ERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cm' \
                    b'RmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY' \
                    b'29tL2V4aWYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8i' \
                    b'PgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+MzI8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA' \
                    b'8ZXhpZjpVc2VyQ29tbWVudD5TY3JlZW5zaG90PC9leGlmOlVzZXJDb21tZW50PgogICAgICAgICA8ZXhpZjpQaXhlbF' \
                    b'lEaW1lbnNpb24+NzI8L2V4aWY6UGl4ZWxZRGltZW5zaW9uPgogICAgICAgICA8dGlmZjpSZXNvbHV0aW9uVW5pdD4yP' \
                    b'C90aWZmOlJlc29sdXRpb25Vbml0PgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj4xNDQ8L3RpZmY6WVJlc29sdXRp' \
                    b'b24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjE0NDwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY' \
                    b'6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOl' \
                    b'JERj4KPC94OnhtcG1ldGE+CibWx0MAAAKaSURBVGgF7Zm9bhNBEMdn9s5OzkAaCp4AUfMIiIYnoADT8dUgpFAhUaRGg' \
                    b'vAOfIiSGCQIFQ2iQUpHlUegQNjEZ93eDjNH1lycu83eGXGFdwuftd6Z/29mZ2dPCT4c7t+BVR64eeWAVjkBEDLQeQbU' \
                    b'ShegBB+2INRAqIHOa6BzgM4bYSjCzmsgAIRTEGogZKDzDIRj2PkWBICQgZCB0IhCDXSegRg7LsOlMqBK9DlBqz/5tga' \
                    b'I2JIVv/LHREAihBKOf1pbAfQiAGPo7WmYXgbCuyyXlrPhLw/QGGB9AJDlNFL9wfWt92d/PvmYvMwM3m4L0QhAIk8PaB' \
                    b'T1B8PHIxzbSJ/tJs9zwlttILwBRFznsLMobiGefkheFBAI0ybb4QVQiGvaUf3kRjlyK26fAmE0Z6IBxIkAIp5reKMmA' \
                    b'6e4hZCa0Dne9IVwAoh4xuKnZhz55797bsXqntu7yStfiFoA2Ufe89fj78nVrU84qROrmxcIY2CISD9cDQIfHP7TSjqZ' \
                    b'bSYiboC+AdEmRWpMGiMkymmc7G1/wWmdqMzfv5ReiBM6xw6MVpT1DFzj6XvctCo54ipnhlcT4Xlubu+MhijGos/+oo3' \
                    b'ZRV6/X2Vj56I184idDmdsgjn7QWkbRae0S448KwFkBSL05MllIN9Bc9NiDp+Bst5mkw36/L12zGugZHBsMUkE/peNH+' \
                    b'ahyhzgmOp/mmgEEGftrlxXLHMA131e5JRrIlvXZ1zO2vxmAfi0uaNjwF6WRhttRFw29hQgyztq9Y8LI6v+8bAZ8HKrP' \
                    b'CBPcrS41d4AckxTk/PryHJjcau9AURWkbSYJQcdfQtrBLCkdKV5APDOgGw+F6LcTY3GYtUvGsfy1uMzCgCFxQ3pWs8R' \
                    b'rZV9soGzcH8DwbrYx3Lr9SEAAAAASUVORK5CYII='

UPMS_MENU_ICON_ENCODED = b'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAP' \
                         b'oAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABK' \
                         b'ARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAA' \
                         b'EAAKACAAQAAAABAAAAIKADAAQAAAABAAAAIAAAAABfvA/wAAAACXBIWXMAAAsTAAALEwEAmpwYAAACyGlUWHRYTUw6' \
                         b'Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIE' \
                         b'NvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRm' \
                         b'LXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxucz' \
                         b'p0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDov' \
                         b'L25zLmFkb2JlLmNvbS9leGlmLzEuMC8iPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj43MjwvdGlmZjpZUmVzb2' \
                         b'x1dGlvbj4KICAgICAgICAgPHRpZmY6UmVzb2x1dGlvblVuaXQ+MjwvdGlmZjpSZXNvbHV0aW9uVW5pdD4KICAgICAg' \
                         b'ICAgPHRpZmY6WFJlc29sdXRpb24+NzI8L3RpZmY6WFJlc29sdXRpb24+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW' \
                         b'9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj4zMjwvZXhpZjpQaXhl' \
                         b'bFhEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U+MTwvZXhpZjpDb2xvclNwYWNlPgogICAgICAgIC' \
                         b'A8ZXhpZjpQaXhlbFlEaW1lbnNpb24+MzI8L2V4aWY6UGl4ZWxZRGltZW5zaW9uPgogICAgICA8L3JkZjpEZXNjcmlw' \
                         b'dGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4Ko+jingAAAlJJREFUWAntlrtu1UAQhv/xhSRIpOUJEDWPEE' \
                         b'W8AgUcOkA0NBQ0UKRJkNLwEFxESQ5IFAGJBtEg0VHxCEhEJMQkttf8c8IcG8uXtUGJkLLF2fWemfm/nd0dW+5fLgqc' \
                         b'YAtOUHsmfQrwf2cgkPIE8SiPOsyjMxDS0xX4SNk9BRFBBacE6xuNAohDIHd4GUdYJcBtivysZqNPtPr/YIDFs8Bhjm' \
                         b'm6hGtrr+X7+lt5mhW4NRZiEICuPNnHNFvCZHMqu7aSh9vyOC9wcwyEN4CKpw5bdXGD2NiWJ78hkiHb4QUwE8+wlS7i' \
                         b'enXlJm69Qrh8lglviF4AFc8yvEiTbnGD0DPBA3qDz14QnQAqfkjxKKP4+3LPTayt33gjz3whWgF0H7Mcz79+w5W1d7' \
                         b'LXJtY2rxCZw4RhdroKhNjrWCuZFRMVZ5H5zLm7EmCX+xoGBfL9BJ8efZCkTVTn760UF+MY5+HgXICUflcZ7g7LZCNH' \
                         b'1BSM4qD4BXq8KhxCrXp8/rGwjEu0/9LkY3NRhAeRYJLSRRxnhZdH1RvlgUYADUaHeNYfjbXQt26X2lkjqKiYZZPzZ9' \
                         b'rE1WcetOJgseY9g2oG+NvfGMfLziLNAWziuPtBAFE6bHU+i5kDdKVYc8rUxgchzvkEHWJjAHoFO/eOgLE7wPKQ4D62' \
                         b'dgv0krRclDKM64EsLf1HlgEvDxaVXsi+QPWt9gbQa5o48HPk71p9q70BVPZfZEDDVJfwx0P1j+ManwJ4Z0CPfyjgJ8' \
                         b'qwVj/1de9Iv3p8mgIE4dEbssuedgu1mJ1X9xeol7VOGchwfwAAAABJRU5ErkJggg=='

UPMS_ICON_32 = ida_kernwin.load_custom_icon(data=base64.b64decode(UPMS_ICON_32_ENCODED), format="png")
UPMS_MENU_ICON = ida_kernwin.load_custom_icon(data=base64.b64decode(UPMS_MENU_ICON_ENCODED), format="png")

if len(logger.handlers) > 0:
    logger.handlers = []

log_stream = logging.StreamHandler()
formatter = logging.Formatter('UnpacMeSearch:%(levelname)s:%(message)s')
log_stream.setFormatter(formatter)

# probably bug here?
logger.addHandler(log_stream)

BAD_OFFSETS = [0xffffffff, 0xffffffffffffffff]


class SearchPreview(QDialog):
    def __init__(self, search_list, code_block, parent=None):
        super(SearchPreview, self).__init__(parent)

        self.setWindowTitle("Search Preview")
        self.resize(640, 480)
        layout = QVBoxLayout()
        self.edit_search = QTextEdit(self)
        self.edit_search.setMinimumWidth(300)
        self.edit_codeblock = QTextEdit(self)
        self.edit_codeblock.setReadOnly(True)
        self.edit_codeblock.setText(code_block)
        self.edit_codeblock.setLineWrapMode(QTextEdit.NoWrap)

        self.edit_codeblock.setFixedWidth(320)

        self.btn_search = QPushButton("Search", self)
        self.btn_search.clicked.connect(self.accept)
        self.btn_cancel = QPushButton("Cancel", self)
        self.btn_cancel.clicked.connect(self.reject)

        search_query = "\n".join(search_list)

        self.edit_search.setText(search_query)

        layout_view = QHBoxLayout()

        layout_view.setAlignment(Qt.AlignTop)
        layout_view.addWidget(self.edit_search)
        layout_view.addWidget(self.edit_codeblock)
        layout_view.addStretch(1)

        layout_buttons = QHBoxLayout()
        layout_buttons.addWidget(self.btn_cancel)
        layout_buttons.addWidget(self.btn_search)

        layout.addLayout(layout_view)
        layout.addLayout(layout_buttons)
        self.setLayout(layout)

    def get_search_pattern(self):
        search_pattern = self.edit_search.toPlainText().replace("\n", "").replace(" ", "")
        search_pattern = ' '.join([search_pattern[i:i + 2] for i in range(0, len(search_pattern), 2)])
        return search_pattern


class GoodwareView(QDialog):
    def __init__(self, metadata, parent=None):
        super(GoodwareView, self).__init__(parent)

        self.setWindowTitle("Goodware Details")
        self.resize(600, 300)
        layout = QVBoxLayout()
        form_layout = QFormLayout()

        self.lbl_sha256 = QLabel("SHA256:")
        self.lbl_sha256_val = QLabel(metadata['sha256'])

        self.lbl_name = QLabel("Name:")
        self.lbl_name_val = QLabel(metadata['name'])

        self.lbl_size = QLabel("Size:")
        self.lbl_size_val = QLabel(str(metadata['size']))

        self.lbl_type = QLabel("Type:")
        self.lbl_type_val = QLabel(metadata['type'])

        self.lbl_subsystem = QLabel("Subsystem:")
        self.lbl_subsystem_val = QLabel(metadata['subsytem'])

        self.lbl_machine_type = QLabel("Machine Type:")
        self.lbl_machine_type_val = QLabel(metadata['machine_type'])

        self.lbl_linker_version = QLabel("Linker Version:")
        self.lbl_linker_version_val = QLabel(metadata['linker_version'])

        form_layout.addRow(self.lbl_sha256, self.lbl_sha256_val)
        form_layout.addRow(self.lbl_name, self.lbl_name_val)
        form_layout.addRow(self.lbl_size, self.lbl_size_val)
        form_layout.addRow(self.lbl_type, self.lbl_type_val)
        form_layout.addRow(self.lbl_subsystem, self.lbl_subsystem_val)
        form_layout.addRow(self.lbl_machine_type, self.lbl_machine_type_val)
        form_layout.addRow(self.lbl_linker_version, self.lbl_linker_version_val)

        if 'metadata' in metadata.keys():
            if 'StringInfo' in metadata['metadata'].keys():
                for prop, prop_val in metadata['metadata']['StringInfo'].items():
                    lbl = QLabel(prop)
                    lbl_val = QLabel(prop_val)
                    form_layout.addRow(lbl, lbl_val)

        self.btn_search = QPushButton("Ok", self)
        self.btn_search.clicked.connect(self.accept)

        layout_buttons = QHBoxLayout()
        layout_buttons.addWidget(self.btn_search)

        layout.addLayout(form_layout)
        layout.addLayout(layout_buttons)

        self.setLayout(layout)


class UnpacMeSearchConfigDialog(QDialog):
    def __init__(self, config, parent=None):
        super(UnpacMeSearchConfigDialog, self).__init__(parent)
        self.config = config
        self.setWindowTitle("UnpacMe Search Config")
        self.resize(320, 240)

        layout = QVBoxLayout()

        form_layout = QFormLayout()

        self.lbl_api_key = QLabel("API Key:")
        self.w_api_key = QLineEdit()
        self.w_api_key.setEchoMode(QLineEdit.Password)
        self.w_api_key.setFixedWidth(300)
        self.w_api_key.setText(self.config['api_key'])

        self.lbl_loglevel = QLabel("Log Level:")

        self.cmb_loglevels = QComboBox()
        self.cmb_loglevels.addItems(['DEBUG', "INFO", "ERROR"])
        self.cmb_loglevels.setEditable(False)

        self.lbl_preview = QLabel("Search Preview:")

        self.chk_preview = QCheckBox()
        self.chk_preview.setChecked(self.config['preview'])

        self.lbl_wildcard = QLabel("Auto Wildcard:")
        self.chk_wildcard = QCheckBox()
        self.chk_wildcard.setChecked(self.config['auto'])

        self.lbl_goodware = QLabel("Search Goodware:")
        self.chk_goodware = QCheckBox()
        self.chk_goodware.setChecked(self.config['goodware'])

        form_layout.addRow(self.lbl_api_key, self.w_api_key)
        form_layout.addRow(self.lbl_loglevel, self.cmb_loglevels)
        form_layout.addRow(self.lbl_preview, self.chk_preview)
        form_layout.addRow(self.lbl_wildcard, self.chk_wildcard)
        form_layout.addRow(self.lbl_goodware, self.chk_goodware)

        self.btn_search = QPushButton("Save", self)
        self.btn_search.clicked.connect(self.accept)
        self.btn_cancel = QPushButton("Cancel", self)
        self.btn_cancel.clicked.connect(self.reject)

        layout_buttons = QHBoxLayout()
        layout_buttons.addWidget(self.btn_cancel)
        layout_buttons.addWidget(self.btn_search)

        layout.addLayout(form_layout)
        layout.addLayout(layout_buttons)

        self.setLayout(layout)

    def get_config(self):
        self.config['goodware'] = self.chk_goodware.isChecked()
        self.config['auto'] = self.chk_wildcard.isChecked()
        self.config['preview'] = self.chk_preview.isChecked()
        self.config['loglevel'] = self.cmb_loglevels.currentText()
        self.config['api_key'] = self.w_api_key.text()
        return self.config


class UnpacMeResultWidget(idaapi.PluginForm):

    def __init__(self, search_term: str, results: Dict[str, Any]):
        super(UnpacMeResultWidget, self).__init__()
        self.search_term = search_term
        self.results = results
        self.goodware_hashes = []
        self.id_map = {}
        self.result_table = None

    def OnCreate(self, form):
        self.parent = self.FormToPyQtWidget(form)
        self.PopulateForm()

    def copy_selected_cells(self) -> None:

        if not self.result_table:
            return

        selection = self.result_table.selectedRanges()

        selected_result = ""
        if not selection:
            logger.debug("No selection")
            return

        for r in range(selection[0].topRow(), selection[0].bottomRow() + 1):
            for c in range(selection[0].leftColumn(), selection[0].rightColumn() + 1):
                try:
                    selected_result += str(self.result_table.item(r, c).text()) + "\t"
                except AttributeError:
                    selected_result += "\n"
            selected_result = selected_result[:-1] + "\n"

        QApplication.clipboard().setText(selected_result)

    def handle_click(self, item):

        if item.column() == 4:

            if self.id_map[item.text()]['malware']:
                webbrowser.open(f"https://www.unpac.me/results/{self.id_map[item.text()]['id']}?hash={item.text()}")
                return

            gwv = GoodwareView(self.id_map[item.text()]['metadata'])
            gwv.exec_()

        elif item.column() == 1:

            if 'Unknown' == item.text() or item.text() == '':
                logger.warning("No family")
                return

            term = f'malware:"{item.text()}"'.encode("ascii")
            webbrowser.open(f'https://www.unpac.me/search?terms={base64.b64encode(term).decode("ascii")}')

    def PopulateForm(self):

        goodware_matches = 0
        unknown_matches = 0
        malicious_matches = 0

        layout = QVBoxLayout()

        summary_pane = QHBoxLayout()
        summary_pane.setAlignment(Qt.AlignLeft)

        summary_layout = QFormLayout()

        lbl_logo = QLabel()
        lbl_logo.setGeometry(10, 10, 100, 100)

        pixmap = QPixmap()
        pixmap.loadFromData(QByteArray(base64.b64decode(UPMS_ICON_32_ENCODED)))
        lbl_logo.setPixmap(pixmap)
        summary_pane.addWidget(lbl_logo)

        search_term = self.search_term
        if len(self.search_term) > 16:
            search_term = self.search_term[:16] + "..."

        btn_copy = QPushButton("Copy Search Pattern")
        btn_copy.clicked.connect(self.copy_text_to_clipboard)

        btn_copy_selected = QPushButton("Copy Selected Results")
        btn_copy_selected.clicked.connect(self.copy_selected_cells)

        summary_layout.addRow(QLabel("Search Term:"), QLabel(search_term))
        summary_layout.addRow(QLabel("Matches:"), QLabel(f"{self.results['result_count']}"))
        summary_layout.addRow(QLabel("First Seen:"), QLabel(f"{datetime.fromtimestamp(self.results['first_seen']).strftime('%Y-%m-%d')}"))
        summary_layout.addRow(QLabel("Last Seen:"),
                              QLabel(f"{datetime.fromtimestamp(self.results['last_seen']).strftime('%Y-%m-%d')}"))

        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        summary_layout.addRow(line)
        summary_layout.addRow(btn_copy)
        summary_layout.addRow(btn_copy_selected)
        summary_layout.setVerticalSpacing(0)
        summary_pane.addLayout(summary_layout)

        self.result_table = QTableWidget()

        self.result_table.setRowCount(0)

        self.result_table.setColumnCount(6)
        self.result_table.itemDoubleClicked.connect(self.handle_click)

        self.result_table.setSortingEnabled(True)
        self.result_table.setMinimumHeight(600)

        self.result_table.setHorizontalHeaderLabels(["Classification",
                                                "Malware Family",
                                                "Labels",
                                                "Threat Type",
                                                "SHA256",
                                                "Last Seen"])

        results = self.results['results']
        last_row = 0
        for row, result in enumerate(results):
            last_row = row
            self.result_table.insertRow(row)
            try:
                self.id_map[result['sha256']] = {
                    'id': result["analysis"][0]["id"],
                    'malware': True
                }
            except Exception as pex:
                logger.error(f"Error parsing results.. Error: {pex}")
                logger.error(result)

            sha256_item = QTableWidgetItem(result['sha256'])
            sha256_item.setToolTip("View latest Analysis on UnpacMe")

            self.result_table.setItem(row, 4, QTableWidgetItem(sha256_item))
            self.result_table.setItem(row, 5, QTableWidgetItem(str(datetime.fromtimestamp(result['last_seen']).strftime('%Y-%m-%d'))))
            malware_family = []
            classification_type = ""
            threat_type = ""
            labels = []
            family_lower = []
            for entry in result['malwareid']:

                try:
                    if entry['malware_family'].lower() not in family_lower:
                        family_lower.append(entry['malware_family'].lower())
                        malware_family.append(entry['malware_family'])
                except KeyError:
                    logger.debug("No malware family")
                except AttributeError:
                    logger.debug("No malware family")

                if entry['type'] == 'unpacme':
                    if not classification_type:
                        classification_type = entry['classification_type']

                    if not threat_type:
                        threat_type = entry['threat_type']
                labels.append(entry['name'])
            try:
                family_str = "\n".join([x.capitalize() for x in set(malware_family)])
            except TypeError:
                family_str = ""

            label_str = "\n".join(list(set(labels)))
            if not label_str:
                label_str = ""
            family_widget = QTableWidgetItem(family_str)
            family_widget.setToolTip("Search for malware family on UnpacMe.")
            self.result_table.setItem(row, 1, family_widget)
            self.result_table.setItem(row, 2, QTableWidgetItem(label_str))

            # if there is no set classificaiton type, but
            # there are applied labels (i.e. malpedia) set the classification type to malicious
            if not classification_type:
                if family_str:
                    classification_type = "MALICIOUS"

            if classification_type == "MALICIOUS":
                ct_widget = QTableWidgetItem(classification_type)
                # ff0000
                ct_widget.setBackground(QColor(255, 0, 0))
                malicious_matches += 1
            else:
                classification_type = "UNKNOWN"
                ct_widget = QTableWidgetItem(classification_type)
                # 6c757d
                ct_widget.setBackground(QColor(108, 117, 125))
                unknown_matches += 1

            ct_widget.setForeground(QColor(255, 255, 255))

            self.result_table.setItem(row, 0, ct_widget)
            self.result_table.setItem(row, 3, QTableWidgetItem(threat_type))

        if self.results['goodware_results']:
            self.goodware_row_start = last_row + 1
            for row, result in enumerate(self.results['goodware_results'], start=last_row + 1):
                self.goodware_hashes.append(result['sha256'])

                self.id_map[result['sha256']] = {
                    'malware': False,
                    'metadata': result
                }

                self.result_table.insertRow(row)
                ct_widget = QTableWidgetItem("GOODWARE")
                # 228B22
                ct_widget.setBackground(QColor(34, 139, 34))
                ct_widget.setForeground(QColor(255, 255, 255))
                self.result_table.setItem(row, 0, ct_widget)
                sha256_item = QTableWidgetItem(result['sha256'])
                sha256_item.setToolTip("View details...")
                self.result_table.setItem(row, 4, sha256_item)
                goodware_matches += 1

        self.result_table.resizeRowsToContents()
        self.result_table.setEditTriggers(QTableWidget.NoEditTriggers)

        header = self.result_table.horizontalHeader()
        header.setSectionResizeMode(QHeaderView.ResizeToContents)

        count_summary_layout = QFormLayout()

        gwc = QLabel(f"{goodware_matches}")
        gwc.setStyleSheet("background-color: #228B22; padding: 5px; color: #ffffff;")

        uc = QLabel(f"{unknown_matches}")
        uc.setStyleSheet("background-color: #6c757d; padding: 5px; color: #ffffff;")

        mc = QLabel(f"{malicious_matches}")
        mc.setStyleSheet("background-color: #ff0000; padding: 5px; color: #ffffff;")

        count_summary_layout.addRow(QLabel("Goodware:"), gwc)
        count_summary_layout.addRow(QLabel("Unknown:"), uc)
        count_summary_layout.addRow(QLabel("Malicious:"), mc)

        summary_pane.addLayout(count_summary_layout)

        # Add the summary pane to the main layout
        layout.addLayout(summary_pane, Qt.AlignLeft)

        layout.addWidget(self.result_table, Qt.AlignLeft)
        layout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
        layout.addStretch(1)

        self.parent.setLayout(layout)

    def copy_text_to_clipboard(self):

        clipboard = QGuiApplication.clipboard()
        clipboard.setText(self.search_term)

        logger.info(f"Text copied to clipboard: {self.search_term}")

    def OnClose(self, form):
        pass


class UnpacMeSearch(object):

    def __init__(self, api_key):
        self.api_key = f"Key {api_key}"
        self.base_site = "https://api.unpac.me/api/"
        self.api_version = "v1"
        self.search_endpoint = "/private/search/term/"
        self.search_types = {"hex": "bytes",
                             "ascii": "string.ascii",
                             "wide": "string.wide"
                             }

    def _search(self, data: str, type: str, search_goodware=False) -> Dict[str, Any]:
        try:
            url = f"{self.base_site}{self.api_version}{self.search_endpoint}{self.search_types[type]}"
            auth_header = {'Authorization': self.api_key}
            search_data = {'value': data}

            if search_goodware:
                search_data['repo_type'] = 'goodware'

            logger.debug(f"Search Data: {search_data}")
            logger.debug("Calling Unpac.me API endpoint")
            logger.debug(f"URL: {url}")
            search_response = requests.post(url, json=search_data, headers=auth_header)
            if search_goodware:
                logger.debug(search_response.json())

            if search_response.status_code == 404:

                jres = search_response.json()
                logger.debug(jres)
                if "warning" in jres.keys():
                    idc.warning(jres['warning'])

                return {}

            if search_response.status_code != 200:
                logger.error(f"Error in search...try again {search_response.status_code}")
                idc.warning(f"Unexpected response from UnpacMe...please try again. Code: {search_response.status_code}")
                return {}

            return search_response.json()
        except Exception as ex:
            logger.error(f"Error making request {ex}")
            idc.warning(f"Unexpected error UnpacMe...please try again. Error: {ex}")

    def search(self, data: str, type: str) -> Dict[str, Any]:
        try:
            ida_kernwin.show_wait_box("HIDECANCEL\nSearching...")
            time.sleep(0.5)
            search_results = self._search(data, type, search_goodware=False)
            return search_results
        finally:
            ida_kernwin.hide_wait_box()

    def search_goodware(self, data: str, type: str) -> Dict[str, Any]:
        try:
            ida_kernwin.show_wait_box("HIDECANCEL\nSearching Goodware...")
            time.sleep(0.5)
            search_results = {
                'goodware_results': [],
                'matched_goodware_files': 0
            }
            search_results.update(self._search(data, type, search_goodware=True))
            return search_results
        finally:
            ida_kernwin.hide_wait_box()


class BaseSearchHandler(ida_kernwin.action_handler_t):
    def __init__(self):
        super(BaseSearchHandler, self).__init__()
        self.unpacme_search = None
        self.result_widget = None

    def build_result(self, result: Dict[str, Any], search_string: str):

        if not result or 'results' not in result.keys():
            idc.warning("No results found for the pattern.")
            return False

        label_map = {}
        classification_map = {}

        for r in result['results']:
            classification_type = ""
            family = ""
            for entry in r['malwareid']:

                label = entry.get('malware_family', None)
                if not family:
                    family = label

                classification = entry.get('classification_type', None)
                if classification:
                    classification_type = classification

            if not classification_type:
                classification_type = 'UNKNOWN'

            if family in label_map:
                label_map[family] += 1
            else:
                label_map[family] = 1

            if classification_type in classification_map:
                classification_map[classification_type] += 1
            else:
                classification_map[classification_type] = 1
        try:
            classification_map['GOODWARE'] = result['matched_goodware_files']
        except KeyError:
            classification_map['GOODWARE'] = 0

        result['label_map'] = label_map
        result['classification_map'] = classification_map
        logger.info(classification_map)
        logger.info(label_map)

        if self.result_widget:
            self.result_widget.Close(ida_kernwin.PluginForm.WCLS_CLOSE_LATER)

        self.result_widget = UnpacMeResultWidget(search_string, result)
        self.result_widget.Show("UnpacMe Search")
        if "warning" in result.keys():
            idc.warning(result['warning'])

    def activate(self, ctx):
        # Delay loading of the UnpacMeSearch class until we need it
        # This prevents possible password prompt on IDA startup to access the keystore
        if self.unpacme_search is None:
            self.unpacme_search = UnpacMeSearch(keyring.get_password('unpacme', 'api_key'))

    def update(self, ctx):
        pass


class SearchHandler(BaseSearchHandler):

    def __init__(self, preview, auto_wildcard, search_goodware):
        super(SearchHandler, self).__init__()

        self.unpacme_search = None

        self.preview = preview
        self.auto_wildcard = auto_wildcard
        self.search_goodware = search_goodware

        # https://www.hex-rays.com/products/ida/support/idadoc/276.shtml
        self.wildcard_types = [ida_ua.o_mem, ida_ua.o_phrase, ida_ua.o_displ, ida_ua.o_far, ida_ua.o_near]
        self.result_widget = None

    def update(self, ctx):

        if ida_kernwin.get_widget_type(ctx.widget) in [ida_kernwin.BWN_DISASM, ida_kernwin.BWN_PSEUDOCODE, ida_kernwin.BWN_DUMP]:
            return ida_kernwin.AST_ENABLE_FOR_WIDGET
        return ida_kernwin.AST_DISABLE_FOR_WIDGET

    def _process_selected_code_range(self, start, end):
        iterations = 0
        search_bytes = []
        code_block = ""

        offset = start

        while offset < end:
            iterations += 1
            if iterations > 100:
                break
            instr_string = []
            try:
                cur_offset = offset
                flags = ida_bytes.get_full_flags(cur_offset)

                if not ida_bytes.is_code(flags):
                    logger.debug("Processing as data")
                    ibytes = idc.get_bytes(cur_offset, idc.get_item_size(cur_offset), 0)
                    for b in ibytes:
                        instr_string.append("{0:02x}".format(b))
                    continue

                ins = ida_ua.insn_t()
                idaapi.decode_insn(ins, cur_offset)
                instruction_size = ins.size

                logger.debug("------------------------------")
                logger.debug(f"Current Offset: {hex(cur_offset)}")
                logger.debug("Instruction Size: %d" % instruction_size)
                logger.debug(f"Bytes: {binascii.hexlify(idc.get_bytes(cur_offset, instruction_size, 0))}")
                logger.debug(f"Flags: {flags}")
                logger.debug(idc.generate_disasm_line(cur_offset, 0))
                logger.debug("------------------------------")

                op1 = ins.ops[0]
                op2 = ins.ops[1]

                ibytes = idc.get_bytes(cur_offset, ins.size, 0)
                code_block += " ".join("{0:02x}".format(b) for b in bytearray(ibytes))
                if len(ibytes) <= 4:
                    code_block += "\t"

                logger.debug(idc.generate_disasm_line(offset, 0))
                code_block += f"\t{idc.generate_disasm_line(offset, 0)}\n"

                if (
                        op1.type not in self.wildcard_types and op2.type not in self.wildcard_types) or not self.auto_wildcard:
                    logger.debug("No wildcards")
                    ibytes = idc.get_bytes(cur_offset, instruction_size, 0)
                    for b in ibytes:
                        instr_string.append("{0:02x}".format(b))
                    continue

                if op1.type in self.wildcard_types:
                    logger.debug("Wildcarding op1")

                    # wild card instruction
                    if op1.offb == 0 and op2.offb == 0:
                        logger.debug("Wildcarding entire instruction")
                        instr_string.append("?? " * int(instruction_size))
                        continue

                    # wildcard instruction with op1
                    if op2.offb > 0:
                        op1_size = op2.offb - op1.offb
                    else:
                        op1_size = instruction_size - op1.offb
                    logger.debug(f"op1_size: {op1_size}")

                    if op1.offb == 0:
                        instr_string.append("?? " * int(op1_size))
                    else:
                        logger.debug("Getting op")
                        logger.debug(f"ibytes: {ibytes}")
                        for b in ibytes[:op1.offb]:
                            instr_string.append("{0:02x}".format(b))

                        instr_string.append("?? " * int(op1_size))

                    # continue
                    if op2.offb > 0:
                        if op2.type in self.wildcard_types:
                            logger.debug("Wildcarding op2")
                            instr_string.append("?? " * int(instruction_size - op2.offb))
                        else:
                            for b in ibytes[op2.offb:]:
                                instr_string.append("{0:02x}".format(b))

                    continue

                # Check of op2 neds to be wildcarded
                if op2.type in self.wildcard_types:
                    logger.debug("Wildcarding op2")

                    if op2.offb == 0:
                        logger.debug("Wildcarding op2")
                        instr_string.append("?? " * ins.size)
                        continue

                    # emit all bytes up to the start of op2
                    # cases where op1 needs to be wildcared are already handled
                    for b in ibytes[:op2.offb]:
                        instr_string.append("{0:02x}".format(b))

                    op2_size = instruction_size - op2.offb
                    instr_string.append("?? " * int(op2_size))
            except Exception as ex:
                logger.error(f"Exception: {ex}")
            finally:

                instruction_byte_string = ''.join(instr_string)
                instruction_byte_string = instruction_byte_string.replace(" ", "")
                instruction_byte_string = ' '.join(instruction_byte_string[i:i + 2] for i in range(0, len(instruction_byte_string), 2))

                search_bytes.append(''.join(instruction_byte_string))

                next_offset = idc.next_head(offset, end)

                logger.debug(f"Next Offset: {hex(next_offset)}")

                if next_offset >= end:
                    break

                if next_offset in BAD_OFFSETS:
                    break

                offset = next_offset
                logger.debug(f"Next Offset: {hex(offset)}")

        return search_bytes, code_block

    def _is_string_lit(self, address: int) -> bool:
        flags = ida_bytes.get_flags(address)
        is_strlit = ida_bytes.is_strlit(flags)

        if is_strlit:
            return True

        insn = idaapi.insn_t()
        idaapi.decode_insn(insn, address)
        logger.debug("Checking ops")
        for op in [0, 1]:
            logger.debug(f"Op Type {insn.ops[op].type}")

            if insn.ops[op].type in [idaapi.o_far, idaapi.o_imm]:
                string_addr = insn.ops[op].addr

                if insn.ops[op].type == idaapi.o_imm:
                    string_addr = insn.ops[0].value
                    logger.debug(f"String Address: {hex(string_addr)}")

                if not string_addr:
                    continue

                # Check if it's a string
                if idaapi.is_strlit(idaapi.get_flags(string_addr)):
                    logger.debug(f"Reference to string literal at {hex(string_addr)}")
                    return True

        return False

    def _get_string_lit(self, address: int) -> Tuple[str, int]:
        try:

            flags = ida_bytes.get_flags(address)
            is_strlit = ida_bytes.is_strlit(flags)
            if is_strlit:
                string_type = idc.get_str_type(address)
                size = ida_bytes.get_max_strlit_length(address, string_type)
                logger.debug(f"String Type: {string_type}, String Size {size}")
                string_data = ida_bytes.get_strlit_contents(address, size, string_type)
                return string_data.decode('utf-8'), string_type

            insn = idaapi.insn_t()
            idaapi.decode_insn(insn, address)
            logger.debug("Checking ops")

            string_data = ""
            string_type = 0

            for op in [0, 1]:
                logger.debug(f"Op Type {insn.ops[op].type}")
                if insn.ops[op].type in [idaapi.o_far, idaapi.o_imm]:
                    string_addr = insn.ops[op].addr

                    if insn.ops[op].type == idaapi.o_imm:
                        string_addr = insn.ops[0].value
                        logger.debug(f"String Address: {hex(string_addr)}")

                    if not string_addr:
                        continue

                    # Check if it's a string
                    if not idaapi.is_strlit(idaapi.get_flags(string_addr)):
                        continue

                    string_type = idc.get_str_type(string_addr)

                    size = ida_bytes.get_max_strlit_length(string_addr, string_type)
                    string_data = ida_bytes.get_strlit_contents(string_addr, size, string_type)

            return string_data.decode('utf-8'), string_type


        except Exception as ex:
            logger.error(f"Error getting string literal {ex}")
            return "", 0

    def activate(self, ctx: Any) -> bool:
        super(SearchHandler, self).activate(ctx)

        start = idc.read_selection_start()

        if start in BAD_OFFSETS:
            # Range isn't selected get the current cursor position
            start_address = ida_kernwin.get_screen_ea()
            if start_address in BAD_OFFSETS:
                logger.debug("Nothing selected..")
                return False

            logger.debug("Checking if address contains string")

            if not self._is_string_lit(start_address):
                logger.debug("Not a string literal")
                return False

            search_string, string_type = self._get_string_lit(start_address)
            if not search_string:
                logger.warning("Unable to get string data")
                return False

            search_type = "ascii"
            if string_type not in [ida_nalt.STRTYPE_C, ida_nalt.STRTYPE_PASCAL]:
                search_type = "wide"
            result = self.unpacme_search.search(search_string, search_type)
            if self.search_goodware:
                gw_result = self.unpacme_search.search_goodware(search_string, search_type)
                result.update(gw_result)
        else:

            end = idc.read_selection_end()

            if start in BAD_OFFSETS or end in BAD_OFFSETS:
                logger.debug("Nothing selected")
                idc.warning("Nothing Selected!")
                return False

            if start > end:
                logger.debug("Start is greater than end")
                idc.warning("Start is greater than end")
                return False

            code_block = ""
            search_bytes = []
            logger.debug(f'Start: {hex(start)} End: {hex(end)}')

            flags = ida_bytes.get_full_flags(start)
            if not ida_bytes.is_code(flags):
                size = end - start
                logger.debug(f"Selected data size: {size}")
                ibytes = idc.get_bytes(start, size, False)
                for b in ibytes:
                    search_bytes.append("{0:02x}".format(b))
            else:
                search_bytes, code_block = self._process_selected_code_range(start, end)

            search_str = ''.join(search_bytes)
            search_str = search_str.replace(" ", "")
            search_string = ' '.join(search_str[i:i + 2] for i in range(0, len(search_str), 2))

            if self.preview:
                dialog = SearchPreview(search_bytes, code_block)
                preview_result = dialog.exec_()

                if preview_result == QDialog.Accepted:
                    logger.debug(f"Search Bytes: {search_string}")
                    search_string = dialog.get_search_pattern()

                    if not search_string:
                        logger.error("No bytes to search")
                        idc.warning("No bytes to search")
                        return False

                    result = self.unpacme_search.search(search_string, "hex")
                    if self.search_goodware:
                        gw_result = self.unpacme_search.search_goodware(search_string, "hex")
                        result.update(gw_result)
                else:
                    return False
            else:
                if not search_string:
                    logger.error("No bytes to search")
                    idc.warning("No bytes to search")
                    return False

                result = self.unpacme_search.search(search_string, "hex")
                if self.search_goodware:
                    gw_result = self.unpacme_search.search(search_string, "hex")
                    result.update(gw_result)

        if not result:
            idc.warning("No results found for the pattern.")
            return False

        self.build_result(result, search_string)
        return True

    def term(self):
        pass


class StringWindowHandler(BaseSearchHandler):
    def __init__(self, search_goodware: bool):
        super(StringWindowHandler, self).__init__()
        self.search_goodware = search_goodware
        self.unpacme_search = None
        self.result_widget = None

    def activate(self, ctx):
        super(StringWindowHandler, self).activate(ctx)

        # for selection in ctx.chooser_selection:
        _, string_size, string_type, search_string = ida_kernwin.get_chooser_data(
            ctx.widget_title,
            ctx.chooser_selection[0]
        )

        search_string = search_string.replace("\\\\", "\\")

        logger.info(f"Selected String: {search_string}")
        logger.debug(f"String Size: {string_size}")
        logger.debug(f"String Type: {string_type}")

        search_type = "ascii"

        # TODO sort this out
        if string_type not in ['C', 'PASCAL']:
            search_type = "wide"
        result = self.unpacme_search.search(search_string, search_type)
        if self.search_goodware:
            gw_result = self.unpacme_search.search_goodware(search_string, search_type)
            result.update(gw_result)

        if not result or 'results' not in result.keys():
            idc.warning("No results found for the pattern.")
            return False

        self.build_result(result, search_string)

        return True

    def update(self, ctx):
        if ida_kernwin.get_widget_type(ctx.widget) == ida_kernwin.BWN_STRINGS:
            return ida_kernwin.AST_ENABLE_FOR_WIDGET
        return ida_kernwin.AST_DISABLE_FOR_WIDGET


class UnpacMeByteSearchPlugin(ida_idaapi.plugin_t):
    flags = ida_idaapi.PLUGIN_KEEP

    comment = "UnpacMe Search"
    help = "UnpacMe Byte Search"
    wanted_name = "UnpacMe Byte Search"
    wanted_hotkey = ""

    _version = "1.1.1"

    def _banner(self):
        return f"""
        ##################
        UnpacMe Search\n
        Version: {UnpacMeByteSearchPlugin._version}\n\n
        ##################
        """

    def __init__(self):
        self.config = None
        self.search_handler = None
        self.string_window_handler = None

    def init(self):
        try:
            ida_kernwin.msg(self._banner())
            logger.debug("Loading config..")
            self.config = self.load_configuration()
            logger.setLevel(logging._checkLevel(self.config['loglevel'].upper()))

            self.search_handler = SearchHandler(self.config['preview'], self.config['auto'], self.config['goodware'])
            self.string_window_handler = StringWindowHandler(self.config['goodware'])

            if self.config.pop('default', False):
                logger.info("Running default configuration")
                self.edit_config()

            logger.debug("== UnpacMe Search Config ==")
            for c in self.config:
                logger.debug(f" -> {c}: {self.config[c]}")

            self.actions = [
                ida_kernwin.action_desc_t(
                    "unpacme_search",
                    "UnpacMe Byte Search",
                    self.search_handler,
                    "Shift-Alt-s",
                    "UnpacMe Byte Search",
                    UPMS_MENU_ICON),
                ida_kernwin.action_desc_t(
                    "unpacme_string_search",
                    "UnpacMe String Search",
                    self.string_window_handler,
                    "",
                    "UnpacMe String Search",
                    UPMS_MENU_ICON)
            ]

            for action_desc in self.actions:
                ida_kernwin.register_action(action_desc)

            ida_kernwin.attach_action_to_menu(
                "Edit/Plugins/",
                "unpacme_search",
                ida_kernwin.SETMENU_APP
            )

            self.menus = Menus()
            self.menus.hook()

            logger.info("UnpacmeSearchPlugin initialized.")

        except Exception as ex:
            logger.error('Error initializing UnpacmeSearchPlugin %s' % ex)
            idc.warning('Error initializing UnpacmeSearchPlugin %s' % ex)

        return ida_idaapi.PLUGIN_KEEP

    def save_configuration(self, config_name='unpacme_search.cfg'):
        path = ida_diskio.get_user_idadir()
        config_path = os.path.join(path, config_name)

        with open(config_path, 'w') as outf:
            outf.write(json.dumps(self.config))

    def load_configuration(self, config_name='unpacme_search.cfg'):
        path = ida_diskio.get_user_idadir()
        config_path = os.path.join(path, config_name)

        if not os.path.exists(config_path):
            logger.info("No config file!")

            return {
                'default': True,
                'loglevel': 'INFO',
                'preview': True,
                'auto': True,
                'goodware': True
            }

        with open(config_path, 'r') as inf:
            config_data = json.loads(inf.read())

        return config_data

    def edit_config(self):
        logger.debug("Loading config")
        config = self.load_configuration()
        config.pop('default', None)

        logger.debug("Getting API Key")
        config['api_key'] = keyring.get_password("unpacme", "api_key")
        if not config['api_key']:
            logger.warning("No API Key found!")

        config_handler = UnpacMeSearchConfigDialog(config)
        config_result = config_handler.exec_()
        logger.debug("Config dialog closed")

        if config_result == QDialog.Accepted:
            logger.debug("Getting updated config")
            config = config_handler.get_config()
            key = config.pop('api_key')
            logger.debug(config)
            logger.info("Saving config")
            if key:
                keyring.set_password("unpacme", "api_key", key)
            self.config = config
            self.save_configuration()

            # set these so we don't have to reload
            self.search_handler.auto_wildcard = config['auto']
            self.search_handler.preview = config['preview']
            self.search_handler.search_goodware = config['goodware']
            logger.setLevel(logging._checkLevel(config['loglevel'].upper()))

        return

    def run(self, arg):
        self.edit_config()
        return

    def term(self):
        if self.actions:
            for action_desc in self.actions:
                ida_kernwin.unregister_action(action_desc.name)


class Menus(ida_kernwin.UI_Hooks):

    def finish_populating_widget_popup(self, form, popup):

        if idaapi.BWN_DISASM:
            ida_kernwin.attach_action_to_popup(form, popup, "unpacme_search", "UnpacMe Byte Search")

        if idaapi.BWN_STRINGS:
            ida_kernwin.attach_action_to_popup(form, popup, "unpacme_string_search", "UnpacMe String Search")




def PLUGIN_ENTRY():
    return UnpacMeByteSearchPlugin()


repository: luorui110120/IDAplugins
file: python/importSym.py
description: ida插件
wanted_name: Import Sysm plugin
comment: import boot kernel sysm --KD
created_at: 2015-05-15 18:29:04+00:00
pushed_at: 2016-02-29 10:49:32+00:00
url:
https://github.com/luorui110120/IDAplugins/blob/eea46558e8cd21dc216688555f9fa0297b76c21b/python/importSym.py

content:
﻿#-------------------------------------------------------------------------------
# Name:        importSym
# Purpose:
#
# Author:      KD
#
# Created:     06-20-2015
# Copyright:   (c) KD 2015
# Licence:     improt boot-kernle kallsyms
#-------------------------------------------------------------------------------
from idaapi import *
import idc
class KDimportSysmplugin_t(idaapi.plugin_t):
    flags = idaapi.PLUGIN_UNL
    comment = "import boot kernel sysm --KD"

    help = "http://bbs.chinapyg.com/"
    wanted_name = "Import Sysm plugin"
    wanted_hotkey = "Alt-F8"

    def init(self):
        return idaapi.PLUGIN_OK

    def run(self, arg):
        index = 0;
        baseaddr = 0;
        filepath = AskFile(0, "*.*", "")
        if(filepath):
            fr=open(filepath,"r")
            for i in fr:
                Lstr= i.rstrip().split(' ')
                addr = int(Lstr[0], 16)
                if(0 == baseaddr):
                    if( Lstr[2].find("stext") >= 0):
                        baseaddr = addr;
                        if(baseaddr != get_imagebase()):
                         set_imagebase(baseaddr)
                if addr >= baseaddr and (Lstr[1] == 'T' or Lstr[1] == 't'):
                 index += 1
                 if GetFunctionAttr(addr, FUNCATTR_START) == -1:
                     MakeFunction(addr,-1)
                 if 0 == MakeNameEx(addr, Lstr[2], SN_NOCHECK|SN_NOWARN):
                    idaapi.msg("Fial! index:%d, addr :%08X, %s\n"%(index, addr, Lstr[2]))
        else:
            idaapi.msg("nofind\n")

    def term(self):
        idaapi.msg("term() success!\n")

def PLUGIN_ENTRY():
    return KDimportSysmplugin_t()


repository: rolandocarpio/GameHackingStuff
file: Stuff/IDA/plugins/easy_nop.py
description: useful stuff for gamehacking
wanted_name: Easy Nop
comment: Easy nopping tool for you nopping needs
created_at: 2021-02-04 01:22:53+00:00
pushed_at: 2021-02-01 05:14:37+00:00
url:
https://github.com/rolandocarpio/GameHackingStuff/blob/9cf0bc6d246d1976350bb81b04e0c003bc09f9db/Stuff/IDA/plugins/easy_nop.py

content:
import idaapi
 
popup_action_names = []
 
class Hooks(idaapi.UI_Hooks):
    def __init__(self):
        idaapi.UI_Hooks.__init__(self)
 
    def finish_populating_tform_popup(self, form, popup):
        global popup_action_names
        form_type = idaapi.get_tform_type(form)
        if form_type == idaapi.BWN_DISASM:
            for action_name in popup_action_names:
                idaapi.attach_action_to_popup(form, popup, action_name, None)
 
class EasyNopPlugin(idaapi.plugin_t):
    flags = idaapi.PLUGIN_HIDE | idaapi.PLUGIN_PROC
    comment = "Easy nopping tool for you nopping needs"
    help = "Use Shift+N to nop out current line or selection"
    wanted_name = "Easy Nop"
    wanted_hotkey = ""
 
    def init(self):
        idaapi.msg("Easy Nop :: init\n")
        AddToPopup('nopaction:add_action', 'Nop line or selection', NopAction(), 'Shift+N', None)
 
        self.hooks = Hooks()
        self.hooks.hook()
        return idaapi.PLUGIN_KEEP
 
    def run(self):
        idaapi.msg("Easy Nop :: run\n")
 
    def term(self):
        idaapi.msg("Easy Nop :: term\n")
        if self.hooks:
            self.hooks.unhook()
 
        idaapi.unregister_action('nopaction:add_action')
 
class EA_Action(idaapi.action_handler_t):
    def __init__(self):
        idaapi.action_handler_t.__init__(self)
 
    def update(self, ctx):
        return idaapi.AST_ENABLE_FOR_FORM if ctx.form_type == idaapi.BWN_DISASM else idaapi.AST_DISABLE_FOR_FORM
 
class NopAction(EA_Action):
    def activate(self, ctx):
        info = idaapi.get_inf_structure()
        selection = idaapi.read_selection()
        start = selection[1]
        end = selection[2]
 
        if selection[0] == False:
            start = idaapi.get_screen_ea()
 
            if start == idaapi.BADADDR:
                print 'Easy Nop :: Screen EA == idaapi.BADADDR'
                return 0
 
            end = start + idaapi.get_item_size(start)
        else:
            end += idaapi.get_item_size(end)
 
        if start == idaapi.BADADDR:
            print 'Easy Nop :: Selection EA == idaapi.BADADDR'
            return 0
 
        if start == end:
            print 'Easy Nop :: Nothing to nop'
            return 0
 
        for x in range(start, end):
            # Maybe theres a smarter way to get the nop value for different archs e.g. Assemble('nop') -> 0x90
            idaapi.patch_byte(x, 0x90)
 
        for x in range(start + 1, end):
            idaapi.hide_item(x)
 
        # Must do this else it bugs out on 2x 1 byte instructions being nopped
        idaapi.hide_item(start)
        idaapi.unhide_item(start)
 
        # Search for hidden nops and add to count
        while idaapi.get_byte(end) == 0x90 and idaapi.is_hidden_item(end) == True:
            end += 1
 
        count = end - start
 
        if count > 1:
            idaapi.set_cmt(start, "truncated nops (%d)" % (count), False)
 
        print end
        print start
 
        return 1
 
def AddToPopup(action_name, display, handler, shortcut, tooltip, icon=None):
    global popup_action_names
 
    if tooltip == None:
        tooltip = action_name
 
    if idaapi.register_action(idaapi.action_desc_t(
                action_name,
                display,
                handler,
                shortcut,
                tooltip
            )):
 
        popup_action_names.append(action_name)
    else:
        print 'Easy Nop :: Error registering action %s' % (action_name)
 
def PLUGIN_ENTRY(*args, **kwargs):
    return EasyNopPlugin()


repository: silascutler/IDA_AutoAnalysis
file: AutoAnalysis.py
description: IDA Pro plugin that rename functions on load, based on functionality
wanted_name: AutoAnalysis
comment: AutoAnalysis Plugin
created_at: 2018-02-18 02:29:59+00:00
pushed_at: 2018-03-09 21:26:48+00:00
url:
https://github.com/silascutler/IDA_AutoAnalysis/blob/de5aa48a547189339e874fb1cfda2ba88f2f86bb/AutoAnalysis.py

content:
import idc
import idaapi
import idautils
from idaapi import PluginForm
from PyQt5 import QtCore, QtGui

from AutoAnalysis import definitions

data = []

class AutoAnalysisOutput(PluginForm):
	#Based on code found @https://pastebin.com/raw/nMdTre1F
	def OnCreate(self, form):
		self.parent = self.FormToPyQtWidget(form)
		self.PopulateForm()

	def PopulateForm(self):
		layout = QtWidgets.QVBoxLayout()

		self.table = QtWidgets.QTableWidget()
		layout.addWidget(self.table)

		self.table.setColumnCount(3 + len(definitions.PEAPIs.keys()))

		self.table.setColumnWidth(0, 100)
		self.table.setHorizontalHeaderItem(0, QtWidgets.QTableWidgetItem("Address"))

		self.table.setColumnWidth(1, 250)
		self.table.setHorizontalHeaderItem(1, QtWidgets.QTableWidgetItem("Funciton"))

		self.table.setColumnWidth(2, 2)
		self.table.setHorizontalHeaderItem(2, QtWidgets.QTableWidgetItem(""))

		for index, name in enumerate(definitions.PEAPIs.keys()):

			self.table.setColumnWidth(3 + index, len(name)*10)
			self.table.setHorizontalHeaderItem(3 + index, QtWidgets.QTableWidgetItem(name))

		self.table.cellDoubleClicked.connect(self.double_clicked)

		self.table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
		self.parent.setLayout(layout)

		self.fill_table()

	def fill_table(self):
		self.table.setRowCount(len(data))

		row = 0
		for titem in data:
			

			item = QtWidgets.QTableWidgetItem('0x%X' % titem[0])
			item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable)
			self.table.setItem(row, 0, item)

			item = QtWidgets.QTableWidgetItem(titem[1])
			item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable)
			self.table.setItem(row, 1, item)

			item = QtWidgets.QTableWidgetItem(titem[2])
			self.table.setItem(row, 2, item)

			for col in range(0, len(definitions.PEAPIs.keys()) ):
				item = QtWidgets.QTableWidgetItem(titem[3 + col])
				self.table.setItem(row, 2 + col, item)

			row += 1

		self.table.resizeRowsToContents()

	def double_clicked(self, row, column):
		if column == 2:
			return
		
		idc.Jump(data[row][0])


	def OnClose(self, form):
		"""
		Called when the plugin form is closed
		"""
		pass

	def Show(self):
		return PluginForm.Show(self, "AutoAnalysis")



# Init plugin
class autoAnalysis_t(idaapi.plugin_t):
	flags = 0
	comment = "AutoAnalysis Plugin"
	help = " "
	wanted_name = "AutoAnalysis"
	wanted_hotkey = "Ctrl-Alt-Y"

	def init(self):
		idaapi.autoWait() #Don't try and parse functions before IDA finishes initial analysis
		self.rename = True
		self.arch = idaapi.get_file_type_name()
		self.functionParse()
		self.rename = False
		return idaapi.PLUGIN_KEEP

	def run(self, arg):
		self.functionParse()
		ui = AutoAnalysisOutput()
		ui.Show()
		pass


	def term(self):
		pass

	def functionParse(self):
		print "+++++ Auto Analyis ++++++"
		print "      Starting           "
		print "+++++++++++++++++++++++++"

		# Parse each functions // Skip library calls 
		for segea in idautils.Segments():
			for funcea in idautils.Functions(segea, idc.SegEnd(segea)):
				functionName = idc.GetFunctionName(funcea)
				functionFlags = idc.GetFunctionFlags(funcea)
				if (functionFlags & idaapi.FUNC_LIB or
					functionFlags & idaapi.FUNC_THUNK or
					functionFlags & idaapi.FUNC_HIDDEN or
					functionFlags & idaapi.FUNC_STATICDEF):
					continue
				self.analyzeFunction(funcea)

	def analyzeFunction(self, funcea):
		# https://reverseengineering.stackexchange.com/questions/9352/finding-all-api-calls-in-a-function
		# Copy + Paste from Stack Overflow - Lika Boss
		n_flags = set() 
		dism_addr = list(idautils.FuncItems(funcea))
		for instr in dism_addr:
			tmp_api_address = ""
			if idaapi.is_call_insn(instr):
				for xref in idautils.XrefsFrom(instr, idaapi.XREF_FAR):
					if xref.to == None:
						continue
					tmp_api_address = xref.to
					break
				# get next instr since api address could not be found
				if tmp_api_address == "":
					continue
				api_flags = idc.GetFunctionFlags(tmp_api_address)
	
				# check for lib code (api)
				if (api_flags & idaapi.FUNC_LIB and api_flags & idaapi.FUNC_STATICDEF):
					tmp_api_name = idc.NameEx(0, tmp_api_address)
					if tmp_api_name:
						t_flags = self.processFunction( funcea, tmp_api_name)
						n_flags = ( t_flags| n_flags )
		# Rename function if flags populated
		# 	Skip of this isn't the first run
		sflags = "".join(set(n_flags))
		if len(n_flags) > 0 and self.rename:
			fn = idc.GetFunctionName(funcea)
			if not fn.startswith(sflags):
				print "Renaming - ", fn, " with - ", sflags
				idc.MakeName(funcea, str(sflags + "_" + fn ))
		tbl = [ funcea, idc.GetFunctionName(funcea), sflags ]
		for f in definitions.PEAPIs.keys():
			if definitions.PEAPIs[f]['flag'] in sflags:
				tbl.append('*')
			else:
				tbl.append('')

		data.append( tbl )

	def processFunction(self, funcea, apiName):
		t_flags = set()
		if self.arch.startswith('Portable executable'):

			## PEAPIS: PE - APIs function dictionary 
			for fType in definitions.PEAPIs.keys():
				if apiName.endswith("W"):
					apiName = apiName[:-1]
				if apiName in definitions.PEAPIs[fType]['calls']:
					print "[+] ",idc.GetFunctionName(funcea), " - Call: ", apiName, " --", fType
					t_flags.add(definitions.PEAPIs[fType]['flag'])
			return t_flags
		# This is where the else statement goes for other archs
		print apiName
		return set()



def PLUGIN_ENTRY():
	try:
		return autoAnalysis_t()
	except Exception, e:
		idaapi.msg("Failed to load AutoAnalysis")
		idaapi.msg(e)
		return idaapi.PLUGIN_SKIP


repository: cheery/rollernote
file: main3.py
description: Music composition program
url: https://github.com/cheery/rollernote/blob/ebcf5d6a0544b8a07dae6787ea2e71f43eb69465/main3.py
created_at: 2024-08-22 10:32:19+00:00
pushed_at: 2025-03-23 18:13:52+00:00
content:
from visual import Widget, process_widgets

from synth.entities import PrimType, signal, control
from synth.entities import Pipeline, OutPort, InPort, Constant, Knob, value_knob
from synth.entities import Template
from synth.instances import build_pipeline, Transport

import sdl2.ext
import aural.device
import aural.ladspa

import mido

from visual import gui6

import components3

# from collections import namedtuple
# import entities
# from visual import Widget, process_widgets
# #import cairo
# from aural import lv2
# import aural as audio
# #import commands
# import math
# from music import resolution
# import bisect
# import random
# #import components
# import subprocess
# from fractions import Fraction
# import os
# #from immutables import Map
# 
# def get_tempo_envelope(document):
#     default = random.randint(10, 200)
#     for graph in document.track.graphs:
#         if isinstance(graph, entities.Envelope) and graph.kind == 'tempo':
#             env = resolution.linear_envelope(graph.segments, default)
#             if env.check_positiveness():
#                 return env
#     return resolution.LinearEnvelope([ (0, default, 0) ])
# 
# def setup_playback(document):
#     bpm = get_tempo_envelope(document)
#     return bpm, document.track.voices, dict((s.uid, s) for s in document.track.graphs)
# 
# from reaction import *
# from aural import box
# from aural.box import On, Off
# from aural.miditunes import *
# #from components3 import *
# 
# #ListInserted = namedtuple('ListInserted', ['index', 'value'])
# #ListRemoved  = namedtuple('ListRemoved', ['value'])
# 
# #class ListControl:
# #    def __init__(self, ui, contents):
# #        self.modified = Source(ui.engine, as_stream)
# #        self.contents = contents
# #
# #class ListView(gui3.Detail):
# #    def __init__(self, control, func):
# #        self.control = control
# #        self.func    = func
# #        self.table   = dict()
# #
# #    def attach(self, ui, this):
# #        for value in self.control.contents:
# #            frame = self.func(self.control, value)
# #            this.contents.append(frame)
# #            self.table[value] = frame
# #        #@ui.engine.observe(self.control.modified)
# #        #def _logic_(cmds):
# #        #    if not cmds:
# #        #        return
# #        #    for cmd in cmds.value:
# #        #        if isinstance(cmd, Inserted):
# #        #            frame = self.func(self.control, cmd.key, dict(cmd.fields))
# #        #            this.contents.append(frame)
# #        #            self.table[cmd.key] = frame
# #        #            frame.attach(ui, this)
# #        #            ui.reconstrain()
# #        #        if isinstance(cmd, Erased):
# #        #            frame = self.table.pop(cmd.key)
# #        #            this.contents.remove(frame)
# #        #            frame.detach()
# #        #            ui.reconstrain()
# #
# #import pandas as pd
# #
# #Inserted = namedtuple('Inserted', ['key', 'fields'])
# #Erased   = namedtuple('Erased',   ['key'])
# #Modified = namedtuple('Modified', ['key', 'name', 'value'])
# #
# ## pd.read_csv(name, dtype=..., index_col=...)
# ## d.to_csv(name, index_label=...)
# #
# #class DatasetControl:
# #    def __init__(self, ui, dataset):
# #        self.modified = Source(ui.engine, as_stream)
# #        self.dataset = dataset
# #
# #class DatasetView(gui3.Detail):
# #    def __init__(self, control, func):
# #        self.control = control
# #        self.func    = func
# #        self.table   = dict()
# #
# #    def attach(self, ui, this):
# #        for key in self.control.dataset.index:
# #            frame = self.func(self.control, key, dict(self.control.dataset.loc[key]))
# #            this.contents.append(frame)
# #            self.table[key] = frame
# #        @ui.engine.observe(self.control.modified)
# #        def _logic_(cmds):
# #            if not cmds:
# #                return
# #            for cmd in cmds.value:
# #                if isinstance(cmd, Inserted):
# #                    frame = self.func(self.control, cmd.key, dict(cmd.fields))
# #                    this.contents.append(frame)
# #                    self.table[cmd.key] = frame
# #                    frame.attach(ui, this)
# #                    ui.reconstrain()
# #                if isinstance(cmd, Erased):
# #                    frame = self.table.pop(cmd.key)
# #                    this.contents.remove(frame)
# #                    frame.detach()
# #                    ui.reconstrain()
# #
# #def demo(ui, editor):
# #    dc = DatasetControl(ui, )
# #
# #    def _builder2_(control, key, fields):
# #        @collect(control.modified)
# #        def this_thing(mods):
# #            out = []
# #            for mod in mods:
# #                if isinstance(mod, Modified) and mod.key == key:
# #                    out.append(mod)
# #            if out:
# #                return Some(out)
# #        def _modify_(fs, mods):
# #            for mod in mods:
# #                fs[mod.name] = mod.value
# #            return fs
# #        fields = Memory(fields, _modify_, this_thing)
# #
# #        mouse = gui3.MouseControl(ui)
# #        pos = Hold((0,0), mouse.motion)
# #        #bas = Hold((0,0), snapshot(mouse.left.down, pos)(lambda _, pos: pos))
# #        @gui3.logic(pos,Hold(False,mouse.left.pressed), mouse.right.down)
# #        def on_drag(ui, this, xy, pressed, right):
# #            if pressed:
# #                x, y = xy
# #                x0,y0,w,h = this.parent.computed_box
# #                x -= x0
# #                y -= y0
# #                control.modify(key, 'onset', x/800)
# #                control.modify(key, 'offset', x/800+0.1)
# #                note = y // 3
# #                control.modify(key, 'note', max(0, min(127, note)))
# #            if right:
# #                dc.erase(key)
# #        x0 = gui3.Variable()
# #        x1 = gui3.Variable()
# #        y0 = gui3.Variable()
# #        y1 = gui3.Variable()
# #        def f0(ui, this, solver, fields):
# #            solver.addEditVariable(x0, 'strong')
# #            solver.addEditVariable(x1, 'strong')
# #            solver.addEditVariable(y0, 'strong')
# #            solver.addEditVariable(y1, 'strong')
# #            solver.addConstraint(this.left == x0 + this.parent.left)
# #            solver.addConstraint(this.right == x1 + this.parent.left)
# #            solver.addConstraint(this.top == y0 + this.parent.bottom)
# #            solver.addConstraint(this.bottom == y1 + this.parent.bottom)
# #        def f1(ui, this, solver, fields):
# #            solver.suggestValue(x0, 800 * float(fields['onset']))
# #            solver.suggestValue(x1, 800 * float(fields['offset']))
# #            solver.suggestValue(y0, 3*(1 + int(fields['note'])))
# #            solver.suggestValue(y1, 3*(0 + int(fields['note'])))
# #        return gui3.Container([
# #            on_drag,
# #            gui3.CustomLayout(f0, f1, fields),
# #            gui3.Column(),
# #            gui3.trace((1,0,0,1)),
# #            label(compute(fields)(lambda fields: repr(int(fields['note'])))),
# #        ], mouse=mouse)
# #    dc_mouse = gui3.MouseControl(ui)
# #    pos = Hold((0,0), dc_mouse.motion)
# #    @gui3.logic(dc_mouse.left.down, pos)
# #    def dc_button(ui, this, left, xy):
# #        if left:
# #            x, y = xy
# #            x -= this.left.value()
# #            y -= this.bottom.value()
# #            dc.insert({'onset': x/800,
# #                       'offset': x/800+0.1,
# #                       'note': (y//3),
# #                       'velocity': 1.0})
# #
# #    def sweep_draw_setup(ui):
# #        program = ui.mem(gui3.plain_line_program)
# #        data = np.full(4, 0.0, dtype=np.float32)
# #        buffer = ui.ctx.buffer(data)
# #        vao = ui.ctx.vertex_array(program, buffer, 'point')
# #        return program, vao, buffer, data
# #
# #    @gui3.drawing(ui.now)
# #    def visualz(ui, this, now):
# #        x,y,w,h = this.computed_box
# #        t = (now % 5) / 5 * w
# #        program, vao, buffer, data = ui.mem(sweep_draw_setup)
# #        program['size'] = ui.widget.width, ui.widget.height
# #        program['color'] = 1,0,0,1
# #        data[0] = x + t
# #        data[1] = y
# #        data[2] = x + t
# #        data[3] = y + h
# #        buffer.write(data)
# #        vao.render(mode=ui.ctx.LINES)
# #        
# #    dcv2 = gui3.Container([
# #        dc_button,
# #        visualz,
# #        gui3.trace((0,0,0,1)),
# #        gui3.Width(800),
# #        gui3.Height(128*3),
# #        DatasetView(dc, _builder2_),
# #    ], mouse=dc_mouse)
# #
# #    w = 0.0
# #    playing = {}
# #    @ui.engine.observe(ui.now)
# #    def _player_(now):
# #        nonlocal w
# #        t = (now % 5) / 5
# #        if t < w:
# #            w -= 1
# #        assert w <= t
# #        onsets = dc.dataset
# #        for x in range(len(onsets)):
# #            m = onsets.iloc[x]
# #            onset = m['onset']
# #            offset = m['offset']
# #            note = m['note']
# #            isect = max(w, onset) <= min(t, offset)
# #            if not isect:
# #                continue
# #            offset = (offset - onset) + now
# #            if note in playing:
# #                playing[note] = max(playing[note], offset)
# #            else:
# #                playing[note] = offset
# #                editor.audio_output.lock()
# #                editor.fire_midi_keyboard(ui, On(note, 1.0))
# #                editor.audio_output.unlock()
# #        for note, offset in list(playing.items()):
# #            if offset <= now:
# #                playing.pop(note)
# #                editor.audio_output.lock()
# #                editor.fire_midi_keyboard(ui, Off(note))
# #                editor.audio_output.unlock()
# #        w = t
# #
# #    import os
# #
# #    def filefunc(files, entry):
# #        if entry.is_dir():
# #            color = (0.2, 0.2, 0.5, 1.0)
# #            name = entry.name + "/"
# #        else:
# #            color = (0,0,0,1)
# #            name = entry.name
# #        return label(name, color=color, height=16)
# #
# #    return gui3.Container([
# #        dcv2,
# #        # textbox(textctl),
# #        # button(ui, textctl.text),
# #        # gui3.Container([
# #        #     gui3.HAlign(0.0),
# #        #     gui3.Row(),
# #        #     virtual_keyboard(editor, ui),
# #        #     oscilloscope(*editor.gui_channels),
# #        #     vu_meter(ui, *editor.gui_channels),
# #        #     clavier_visualizer(editor.gui_clavier, ui.now),
# #        # ]),
# #    ], keyboard = gui3.KeyboardControl(ui))
# 
# def demo(editor):
#     ui = gui5.context.get()
#     ui.ctx.enable(ui.ctx.BLEND)
#     ui.ctx.blend_equation = ui.ctx.FUNC_ADD
#     ui.ctx.blend_func = ui.ctx.SRC_ALPHA, ui.ctx.ONE_MINUS_SRC_ALPHA
# 
#     #clock(editor.time, (0,0, 150, 150))
#     #gui5.fill((0,1,0,1), (150, 150, 50, 50))
#     #gui5.fill((0,1,1,1), (200, 150, 50, 50))
#     #gui5.fill((1,0,1,1), (150, 200, 50, 50))
# 
#     for index in editor.thing.index:
#         fs = editor.thing.loc[index]
#         onset  = fs['onset']
#         offset = fs['offset']
#         note   = fs['note']
#         x0 = onset * 800
#         x1 = offset * 800
#         y0 = note * 8
#         rect = (x0, y0, x1-x0, 8)
#         gui5.fill((1,0,0,1), rect)
#         ident = (50, index)
#         if gui5.box(ui.mouse, rect):
#             ui.hotitem = ident
#             if ui.activeitem is None and ui.buttons == 1:
#                 ui.activeitem = ident
#                 ui.activestate = ui.mouse, onset, offset, note
#         if ui.activeitem == ident:
#             (x,y), onset, offset, note = ui.activestate
#             editor.thing.loc[index, 'onset'] = (ui.mouse[0] - x) / 800 + onset
#             editor.thing.loc[index, 'offset'] = (ui.mouse[0] - x) / 800 + offset
#             nnote = (ui.mouse[1] - y) // 8 + note
#             nnote = max(0, min(127, nnote))
#             editor.thing.loc[index, 'note'] = nnote
# 
#     rect = (0,0,800,128*8)
#     gui5.trace((0,0,0,1), rect)
# 
#     if gui5.box(ui.mouse, rect):
#         ui.hotitem = 50
#         if ui.activeitem is None and ui.buttons == 1:
#             ui.activeitem = 50
#             key = editor.thing.index.max()+1
#             editor.thing.loc[key] = {
#                 'onset': (ui.mouse[0] - 0) / 800,
#                 'offset': (ui.mouse[0] - 0) / 800 + 0.1,
#                 'note': (ui.mouse[1] - 0) // 8,
#                 'velocity': 1.0
#             }
# 
# 
# # @gui4.composable
# # def demo_bar(index, editor):
# #     gui4.column(0)
# #     gui4.trace((1,0,0,1))
# #     note = int(editor.thing.loc[index]['note'])
# #     label(repr(note))
# # 
# # 
# # @gui4.composable
# # def demo_row1(editor):
# #     gui4.trace((1,0,0,1))
# #     gui4.row(10)
# #     colorbox[0]((0,1,0,1))
# #     colorbox[1]((0,1,1,1))
# #     colorbox[2]((1,0,1,1))
# #     clock(editor.time)
# #     scrollinglabel("Hello world", editor.time, 50)
# #     testing = button[1]("Testing")
# #     testing2 = button[2]("Testing2")
# #     virtual_keyboard(editor)
# #     oscilloscope(editor.out0, editor.out1)
# #     vu_meter(editor.out0, editor.out1)
# #     clavier_visualizer(editor)
# # 
# #     @gui4.logic(testing, testing2)
# #     def _custom_logic_(ui, ident, layout, testing, testing2):
# #         if testing:
# #             print("hello")
# #         if testing2:
# #             print("world")
# # 
# # @gui4.composable
# # def demo_row2(editor):
# #     gui4.trace((1,0,0,1))
# #     gui4.row(10)
# #     demo_directory(editor)
# #     text = gui4.state[1](value="hello")
# #     textbox[1](text)
# #     demo_thing(editor)
# # 
# # @gui4.composable
# # def demo_directory(editor):
# #     gui4.column(5)
# #     for entry in editor.directory_contents:
# #         if entry.is_dir():
# #             color = (0.2, 0.2, 0.5, 1.0)
# #             name = entry.name + "/"
# #         else:
# #             color = (0,0,0,1)
# #             name = entry.name
# #         label[name](name, color=color, height=16)
# # 
# # @gui4.composable
# # def demo_thing(editor):
# #     gui4.trace((0,0,0,1))
# #     @gui4.layout
# #     def _size_(cn, this):
# #         cn(cn[this].width == 800)
# #         cn(cn[this].height == 128*3)
# #     for index in editor.thing.index:
# #         demo_bar[index](index, editor)
# #     @gui4.static
# #     def layout_contents(layouter, this):
# #         x,y,w,h = layouter[this].rect
# #         for item in this.subframes:
# #             index = item.key[1]
# #             fs = editor.thing.loc[index]
# #             onset = fs['onset']
# #             offset = fs['offset']
# #             note = fs['note']
# #             layouter[item] = gui4.RigidLayout(int(x + onset*w), int(y + note*3),
# #                                              int((offset - onset)*w), 3)
# #             layouter.constrain(item)
# #     @gui4.logic()
# #     def button_press(ui, ident, layout):
# #         if ui.inside:
# #             ui.hotitem = ident
# #             if ui.activeitem is None and ui.buttons > 0:
# #                 ui.activeitem = ident
# #                 key = editor.thing.index.max()+1
# #                 editor.thing.loc[key] = {
# #                   'onset': (ui.mouse[0] - layout.left) / layout.width,
# #                   'offset': (ui.mouse[0] - layout.left) / layout.width + 0.1,
# #                   'note': (ui.mouse[1] - layout.bottom) // 3,
# #                   'velocity': 1.0
# #                 }
# # 
# # @gui4.composable
# # def demo_bar(index, editor):
# #     gui4.column(0)
# #     gui4.trace((1,0,0,1))
# #     note = int(editor.thing.loc[index]['note'])
# #     label(repr(note))
# # 
# # #        self.next_key = dataset.index.max()+1 if len(dataset) > 0 else 0
# # #
# # #    def insert(self, fields):
# # #        key, self.next_key = self.next_key, self.next_key+1
# # #        self.dataset.loc[key] = fields
# # #        self.modified.send(Inserted(key, fields))
# # #        return key
# # #
# # #    def erase(self, key):
# # #        self.dataset = self.dataset.drop(key)
# # #        self.modified.send(Erased(key))
# # #
# # #    def modify(self, key, name, value):
# # #        self.dataset.loc[key, name] = value
# 
# import v2.avl
# import v2.mid
# import v2.rhythm
# import v2.music.resolution
# import v2.music.notes
# import v2.widget
# 
# import aural.ladspa
# 
# 
# value_updated = namedtuple("value_updated", [])
# open_context_menu = namedtuple("open_context_menu", ['menu'])
# 
# def color_by_type(ty):
#     if ty is control:
#         return (1,1,0,1)
#     elif ty is signal:
#         return (1,0,0,1)
#     else:
#         return (1,1,1,1)
# 
# port_detach = namedtuple('port_detach', ['port'])
# 
# def close_context_menu(node):
#     def _close_context_menu():
#         node.root.close_context_menu(node)
#     return _close_context_menu
# 
# @gui6.prefab(components3.ContextMenuSheet)
# def guiport_context_menu(x, y, guiport):
#     this = gui6.context.get()
#     with components3.ContextMenu():
#         gui6.position(gui6.Left, x)
#         gui6.position(gui6.Top, y)
#         if isinstance(guiport.port, InPort):
#             with components3.Button() as button:
#                 button.clicked.connect(lambda: print('hello'))
#                 button.clicked.connect(close_context_menu(this))
#                 @button.clicked.connect
#                 def detach_port():
#                     guiport.port.value = Constant(0)
#                     guiport.updated.emit()
#                 gui6.padding(gui6.Horizontal, 10)
#                 components3.Label(text="detach",   color=(0,0,0,1))
#             with components3.Button() as button:
#                 button.clicked.connect(close_context_menu(this))
#                 gui6.padding(gui6.Horizontal, 10)
#                 components3.Label(text="constant", color=(0,0,0,1))
#             with components3.Button() as button:
#                 button.clicked.connect(close_context_menu(this))
#                 gui6.padding(gui6.Horizontal, 10)
#                 components3.Label(text="knob",     color=(0,0,0,1))
# 
# class GuiPort(gui6.Node):
#     def __init__(self, *args, color, port, **kwargs):
#         super().__init__(*args, **kwargs)
#         self.color = color
#         self.port  = port
# 
#     updated : gui6.Signal
# 
#     @property
#     def value(self):
#         if isinstance(self.port, InPort):
#             return self.port.value
# 
#     def pre_draw(self, ui, x, y):
#         rect = self.rect.offset(x, y)
#         inrect0 = gui6.Rect(rect.left + 4, rect.bottom + 4, rect.width - 8, rect.height - 8)
#         inrect1 = gui6.Rect(rect.left + 10, rect.bottom + 10, rect.width - 20, rect.height - 20)
#         gui6.fill(ui, (0,0,0,1), rect)
#         gui6.trace(ui, self.color, rect)
#         if isinstance(self.port, OutPort):
#             if ui.inside:
#                 ui.hotitem = self
#                 if ui.activeitem is None and ui.buttons == 1:
#                     ui.activeitem = self
#                     ui.activedrag = DragItem(color=(1,1,1,1), _attach_=False)
#                     ui.activedrag.value = self.port
#                     with gui6.NodeContextManager(ui.activedrag):
#                         gui6.position_type(gui6.Absolute)
#                         gui6.position(gui6.Left, ui.mouse[0])
#                         gui6.position(gui6.Top, ui.widget.height - ui.mouse[1])
#                         gui6.width(40.0)
#                         gui6.height(40.0)
#                     ui.queue(self.root.add_child, ui.activedrag)
#         if isinstance(self.port, InPort) and ui.activeitem is not None and isinstance(ui.activedrag, DragItem) and isinstance(ui.activedrag.value, OutPort) and self.port.ty == ui.activedrag.value.ty:
#             gui6.trace(ui, self.color, rect.outset(2,2))
#             if ui.inside:
#                 gui6.trace(ui, self.color, rect.outset(4,4))
#                 if ui.buttons == 0:
#                     self.port.value = ui.activedrag.value.uid
#                     ui.queue(self.updated.emit)
#         if isinstance(self.value, Knob):
#             gui6.circle_stroke(ui, (0.5,0.5,0.5,1), inrect0)
#             ratio = self.value.ratio
#             x = rect.hcenter - math.sin(math.pi*(ratio * 1.5 + 0.25)) * inrect0.width/2
#             y = rect.vcenter - math.cos(math.pi*(ratio * 1.5 + 0.25)) * inrect0.height/2
#             gui6.circle_fill(ui, (1,1,1,1), (x-2,y-2,4,4))
#             hc = rect.hcenter
#             vc = rect.vcenter
#             value = self.value.get_value()
#             if -10 < value < 10.0:
#                 text = "%.2f" % value
#             else: 
#                 text = "%.1f" % value
#             font = ui.mem(FontEngine, int(9))
#             width = font.measure(text)
#             height = font.ascent + font.descent
#             font.prepare((1,1,1,1))
#             font.text(text, round(hc - width/2),
#                             round(rect.bottom - height + font.descent))
#             font.finish()
#             if ui.inside:
#                 ui.hotitem = self
#                 if ui.activeitem is None and ui.buttons > 0:
#                     ui.activeitem = self
#                     ui.activestate = ui.mouse[1], self.value.ratio, ui.buttons
#             if ui.activeitem is self and ui.buttons == 1:
#                 dy = ui.mouse[1] - ui.activestate[0]
#                 self.value.ratio = min(1.0, max(0.0, ui.activestate[1] + dy / 100.0))
#                 ui.queue(self.updated.emit)
#         else:
#             if ui.inside:
#                 ui.hotitem = self
#                 if ui.activeitem is None and ui.buttons > 0:
#                     ui.activeitem = self
#                     ui.activestate = ui.mouse[1], None, ui.buttons
#             gui6.circle_stroke(ui, self.color, inrect0)
#             gui6.circle_fill(ui, self.color, inrect1)
#             if isinstance(self.value, Constant) and self.value.const != 0:
#                 hc = rect.hcenter
#                 vc = rect.vcenter
#                 value = self.value.const
#                 if -10 < value < 10.0:
#                     text = "%.2f" % value
#                 else: 
#                     text = "%.1f" % value
#                 font = ui.mem(FontEngine, int(9))
#                 width = font.measure(text)
#                 height = font.ascent + font.descent
#                 font.prepare((1,1,1,1))
#                 font.text(text, round(hc - width/2),
#                                 round(rect.bottom - height + font.descent))
#                 font.finish()
#         if ui.buttons == 0 and ui.hotitem == self and ui.activeitem == self and ui.activestate[2] == 4:
#             cmenu = guiport_context_menu(ui.mouse[0], ui.widget.height - ui.mouse[1], self)
#             ui.queue(self.root.open_context_menu, cmenu)
# 
# def inport_column(names, ports, inline=False):
#     with gui6.Node():
#         gui6.flex_direction(gui6.Column)
#         if not inline:
#             gui6.margin(gui6.Left, 10.0)
#             with gui6.Node():
#                 gui6.height(60)
#         for i, port in enumerate(ports):
#             with gui6.Node():
#                 gui6.flex_direction(gui6.Row)
#                 gui6.align_items(gui6.Center)
#                 with GuiPort(port=port, color=color_by_type(port.ty)):
#                     gui6.width(32)
#                     gui6.height(32)
#                     gui6.margin(gui6.All, 10)
#                 components3.Label(text=names[i], color=(1,0,1,1), font_height=12)
# 
# def outport_column(names, ports, inline=False):
#     with gui6.Node():
#         gui6.flex_direction(gui6.Column)
#         gui6.margin(gui6.Left, 10.0)
#         if not inline:
#             with gui6.Node():
#                 gui6.height(60)
#         for i, port in enumerate(ports):
#             with gui6.Node():
#                 gui6.flex_direction(gui6.Row)
#                 gui6.align_items(gui6.Center)
#                 components3.Label(text=names[i], color=(0,1,0,1), font_height=12)
#                 with GuiPort(name=port.uid, port=port, color=color_by_type(port.ty)):
#                     gui6.width(32)
#                     gui6.height(32)
#                     gui6.margin(gui6.All, 10)
# 
# def plugin_template(locator, template):
#     desc = locator.load(template.module, template.label)
#     iports = [port for port in desc.info['ports'] if port['type'].startswith('input')]
#     oports = [port for port in desc.info['ports'] if port['type'].startswith('output')]
#     with gui6.Node():
#         gui6.flex_direction(gui6.Column)
#         with gui6.Node():
#             gui6.height(60)
#             gui6.margin(gui6.Horizontal, 8.0)
#             components3.Label(text=template.module, color=(1,1,1,1), font_height=16)
#             components3.Label(text=template.label.decode('utf-8'), color=(1,1,1,1), font_height=24)
#         with gui6.Node():
#             gui6.flex_direction(gui6.Row)
#             inport_column([p['name'].decode('utf-8') for p in iports], template.inputs, inline=True)
#             outport_column([p['name'].decode('utf-8') for p in oports], template.outputs, inline=True)
# 
# class Uids:
#     def __init__(self, index=1000):
#         self.index = index
# 
#     def __next__(self):
#         self.index += 1
#         return self.index
# uids = Uids()
# 
# class PluginInfo:
#     def __init__(self, module, label, info):
#         self.module = module
#         self.label = label
#         self.info = info
# 
#     def to_template(self):
#         inputs = [port for port in self.info['ports'] if port['type'].startswith('input')]
#         outputs = [port for port in self.info['ports'] if port['type'].startswith('output')]
#         inports = []
#         for port in inputs:
#             try:
#                 hint = port['hint']
#                 default = hint['default']
#                 if default is None:
#                     value = Constant(0)
#                 else:
#                     val = default['value']
#                     if hint['lower'] is not None and hint['upper'] is not None:
#                         lower = hint['lower'] or 0.0
#                         upper = hint['upper'] or 0.0
#                         if hint['sample_rate']: # TODO: fetch real sample rate somehow
#                             lower *= 44100
#                             upper *= 44100
#                         if default['ratio']:
#                             u = val
#                             if hint['logarithmic']:
#                                 val = math.exp(math.log(lower) * (1-u) + math.log(upper) * u)
#                             else:
#                                 val = lower * (1-u) + upper * u
#                         value = value_knob(lower, upper, val, is_log=hint['logarithmic'])
#                     else:
#                         value = Constant(val)
#             except ValueError:
#                 value = Constant(0)
#             ty = signal if port['type'].endswith('*') else control
#             inports.append(InPort(value, ty))
#         outports = []
#         for port in outputs:
#             ty = signal if port['type'].endswith('*') else control
#             outports.append(OutPort(next(uids), ty))
#         return Template(next(uids), self.module, self.label, inports, outports)
# 
# class StageDropArea(gui6.Node):
#     def __init__(self, *args, pipeline, index, **kwargs):
#         super().__init__(*args, **kwargs)
#         self.pipeline = pipeline
#         self.index = index
# 
#     updated : gui6.Signal
# 
#     def pre_draw(self, ui, x, y):
#         rect = self.rect.offset(x, y)
#         if ui.activeitem is not None and isinstance(ui.activedrag, DragItem):
#             if isinstance(ui.activedrag.value, PluginInfo):
#                 gui6.fill(ui, (0,1,1,1), rect.outset(-2,-2))
#             if ui.inside:
#                 gui6.trace(ui, (0,1,1,1), rect.outset(4,4))
#                 if ui.buttons == 0:
#                     tpl = ui.activedrag.value.to_template()
#                     self.pipeline.pipe.insert(self.index, tpl)
#                     ui.queue(self.updated.emit)
# 
# class PipelineEditor(gui6.Node):
#     def __init__(self, *args, pipeline, audio_output, **kwargs):
#         super().__init__(*args, **kwargs)
#         self.pipeline = pipeline
#         self.audio_output = audio_output
#         self.redraw()
#         self.update_edges()
# 
#     def redraw(self):
#         self.detach_children()
#         pipeline = self.pipeline
#         audio_output = self.audio_output
#         with gui6.NodeContextManager(self):
#             gui6.flex_direction(gui6.Row)
#             outport_column(pipeline.input_names, pipeline.inputs)
#             with StageDropArea(pipeline=pipeline, index=0):
#                 gui6.width(10.0)
#             for k, item in enumerate(pipeline.pipe, 1):
#                 if isinstance(item, Template):
#                     plugin_template(audio_output.transport.locator, item)
#                 with StageDropArea(pipeline=pipeline, index=k):
#                     gui6.width(10.0)
#             inport_column(pipeline.output_names, pipeline.outputs)
#         for node in self.traverse():
#             if isinstance(node, GuiPort):
#                 node.updated.connect(self.update_pipeline)
#             if isinstance(node, StageDropArea):
#                 node.updated.connect(self.redraw)
#                 node.updated.connect(self.update_pipeline)
# 
#     def update_pipeline(self):
#         with self.audio_output:
#             engine = self.audio_output.transport.engine
#             locator = self.audio_output.transport.locator
#             cache = self.audio_output.transport.pi.deconstruct()
#             pi = build_pipeline(engine, locator, self.pipeline, [], cache)
#             self.audio_output.transport.pi = pi
#             self.audio_output.channels = pi.outputs
#         self.update_edges()
# 
#     def update_edges(self):
#         self.edges = {}
#         for node in self.traverse():
#             if isinstance(node, GuiPort) and isinstance(node.name, int):
#                 self.edges[node.name] = node, []
#             if isinstance(node, GuiPort) and isinstance(node.value, int):
#                 self.edges[node.value][1].append(node)
# 
#     def pre_draw(self, ui, x, y):
#         self.update_edges()
#         rect = self.rect.offset(x, y)
#         gui6.trace(ui, (1,1,1,1), rect)
#         line_width = ui.ctx.line_width
#         ui.ctx.line_width = 10.0
#         for src, dests in self.edges.values():
#             gr = src.global_rect
#             x = gr.hcenter
#             y = gr.vcenter
#             for dst in dests:
#                 gr1 = dst.global_rect
#                 x1 = gr1.hcenter
#                 y1 = gr1.vcenter
#                 program, vao, buffer, data = ui.mem(components3.line_draw_setup)
#                 program['scroll'] = 0,0
#                 program['size'] = ui.widget.width, ui.widget.height
#                 program['color'] = src.color
#                 data[0] = x
#                 data[1] = y
#                 data[2] = x1
#                 data[3] = y1
#                 buffer.write(data)
#                 vao.render(mode=ui.ctx.LINES)
#         ui.ctx.line_width = line_width
# 
# from visual.font import FontEngine
# 
# class KKnob(gui6.Node):
#     def __init__(self, *args, info=None, **kwargs):
#         super().__init__(*args, **kwargs)
#         self.info = info
# 
#     def pre_draw(self, ui, x, y):
#       try:
#         rect = self.rect.offset(x,y)
#         port = self.info
#         hint = port['hint']
#         default = hint['default']
#         if default is None:
#             gui6.trace(ui, (1,0,0,1), rect)
#         else:
#             value = default['value']
#             if hint['lower'] is None or hint['upper'] is None:
#                 gui6.trace(ui, (1,0,0,1), rect)
#             else:
#                 gui6.circle_stroke(ui, (1,0,0,1), rect)
#                 lower = hint['lower'] or 0.0
#                 upper = hint['upper'] or 0.0
#                 if hint['sample_rate']:
#                     lower *= 44100
#                     upper *= 44100
#                 if default['ratio']:
#                     u = value
#                     if hint['logarithmic']:
#                         if lower > 0 and upper > 0:
#                             value = math.exp(math.log(lower) * u + math.log(upper) * (1-u))
#                         else:
#                             value = 0
#                     else:
#                         value = lower * u + upper * (1-u)
#                 else:
#                     if hint['logarithmic']:
#                         if lower > 0 and upper > 0:
#                             u = math.log(value / upper) / math.log(lower / upper)
#                         else:
#                             u = 0
#                     else:
#                         u = (value - lower) / (upper - lower)
#                 x = rect.hcenter - math.sin(math.pi*(u * 1.5 + 0.25)) * rect.width/2
#                 y = rect.vcenter - math.cos(math.pi*(u * 1.5 + 0.25)) * rect.height/2
#                 gui6.circle_fill(ui, (1,1,1,1), (x-1,y-1,2,2))
#             hc = rect.hcenter
#             vc = rect.vcenter
#             if hc != float('nan') and vc != float('nan'):
#                 text = "%.2f" % value
#                 font = ui.mem(FontEngine, int(12))
#                 width = font.measure(text)
#                 height = font.ascent + font.descent
#                 font.prepare((1,1,1,1))
#                 font.text(text, round(hc - width/2),
#                                 round(vc - height/2 + font.descent))
#                 font.finish()
#       except ValueError:
#         pass
# 
# class DragItem(components3.Border):
#     def __init__(self, *args, **kwargs):
#         super().__init__(*args, **kwargs)
#         self.mouse_visible = False
#         self.value = None
# 
#     def pre_draw(self, ui, x, y):
#         super().pre_draw(ui, x, y)
#         if ui.activeitem is not None and ui.buttons > 0:
#             with gui6.NodeContextManager(self):
#                 gui6.position(gui6.Left, ui.mouse[0])
#                 gui6.position(gui6.Top, ui.widget.height - ui.mouse[1])
#         if ui.buttons == 0:
#             ui.activedrag = None
#             ui.queue(self.detach)
# 
# class DragSource(gui6.Node):
#     def __init__(self, *args, value, **kwargs):
#         super().__init__(*args, **kwargs)
#         self.value = value
# 
#     def pre_draw(self, ui, x, y):
#         if ui.inside:
#             ui.hotitem = self
#             if ui.activeitem is None and ui.buttons > 0:
#                 ui.activeitem = self
#                 ui.activedrag = DragItem(color=(1,1,1,1), _attach_=False)
#                 ui.activedrag.value = self.value
#                 with gui6.NodeContextManager(ui.activedrag):
#                     gui6.position_type(gui6.Absolute)
#                     gui6.position(gui6.Left, ui.mouse[0])
#                     gui6.position(gui6.Top, ui.widget.height - ui.mouse[1])
#                     gui6.width(40.0)
#                     gui6.height(40.0)
#                 ui.queue(self.root.add_child, ui.activedrag)
#                 
# 
# @gui6.prefab(gui6.Node)
# def plugin_entry(root, name, desc):
#     with DragSource(value=PluginInfo(root, name, desc.info)):
#         name = name.decode('utf-8')
#         gui6.padding(gui6.All, 10.0)
#         with gui6.Node():
#             gui6.flex_direction(gui6.Column)
#             components3.Label(text=root, color=(1,1,1,1), font_height=12)
#             components3.Label(text=name, color=(1,1,1,1), font_height=16)
#             with gui6.Node():
#                 gui6.flex_direction(gui6.Row)
#                 inputs = [port for port in desc.info['ports'] if port['type'].startswith('input')]
#                 outputs = [port for port in desc.info['ports'] if port['type'].startswith('output')]
#                 with gui6.Node():
#                     gui6.flex_direction(gui6.Column)
#                     for i in inputs:
#                         _name = i['name'].decode('utf-8')
#                         if i['type'].endswith('*'):
#                             _name += '*'
#                         with gui6.Node():
#                             gui6.flex_direction(gui6.Row)
#                             gui6.align_items(gui6.Center)
#                             with KKnob(info=i):
#                                 gui6.width(32)
#                                 gui6.height(32)
#                                 gui6.margin(gui6.All, 10)
#                             components3.Label(text=_name, color=(1,0,1,1), font_height=12)
#                 with gui6.Node():
#                     gui6.width(10)
#                 with gui6.Node():
#                     gui6.flex_direction(gui6.Column)
#                     gui6.align_items(gui6.FlexEnd)
#                     for i in outputs:
#                         _name = i['name'].decode('utf-8')
#                         if i['type'].endswith('*'):
#                             _name += '*'
#                         with gui6.Node():
#                             gui6.flex_direction(gui6.Row)
#                             gui6.align_items(gui6.Center)
#                             components3.Label(text=_name, color=(0,1,0,1), font_height=12)
#                             with KKnob(info=i):
#                                 gui6.width(32)
#                                 gui6.height(32)
#                                 gui6.margin(gui6.All, 10)
# 

main_pipe = Pipeline(1, "main",
  inputs = [ ], #OutPort(2, clavier) ],
  pipe = [
      Template(4, "sawtooth_1641", b"sawtooth_fc_oa",
         inputs = [ InPort(value_knob(0.92, 22050, 440.0, is_log=True), control) ],
         outputs = [ OutPort(3, signal) ])
  ],
  outputs = [ InPort(3, signal), InPort(3, signal) ],
  input_names = [ "clavier" ],
  output_names = [ "left", "right" ] )
program = [main_pipe]

class MainApp(gui6.Node):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.context_menus = []

    def pre_draw(self, ui, x, y):
        rect = self.rect.offset(x, y)
        gui6.fill(ui, (0.2, 0.2, 0.2, 1.0), rect)
        super().pre_draw(ui, x, y)

    def open_context_menu(self, cmenu):
        self.context_menus.append(cmenu)
        self.add_child(cmenu)

    def close_context_menu(self, cmenu):
        index = self.context_menus.index(cmenu)
        for node in self.context_menus[index:]:
            node.detach()
        self.context_menus[index:] = []

@gui6.prefab(MainApp)
def main_widget(editor):
    with components3.Scrollable():
        with components3.InsideScrollable():
            gui6.flex_direction(gui6.Row)
            gui6.overflow(gui6.Scroll)
            with components3.Border(color=(1,1,1,1)):
                gui6.width(500)
                gui6.height(120)
            with components3.Border(color=(1,1,1,1)):
                gui6.width(500)
                gui6.height(120)
            with components3.Border(color=(1,1,1,1)):
                gui6.width(500)
                gui6.height(120)
            with components3.Border(color=(1,1,1,1)):
                gui6.width(500)
                gui6.height(120)
        components3.TextBox(text="hello world", color=(1,1,1,1))
        with components3.Border(color=(1,1,1,1)):
            gui6.width(600)
            gui6.height(120)
        with components3.Border(color=(1,1,1,1)):
            gui6.width(600)
            gui6.height(120)
        with components3.Border(color=(1,1,1,1)):
            gui6.width(600)
            gui6.height(120)
        with components3.Border(color=(1,1,1,1)):
            gui6.width(600)
            gui6.height(120)
        with components3.Border(color=(1,1,1,1)):
            gui6.width(600)
            gui6.height(120)
        with components3.Border(color=(1,1,1,1)):
            gui6.width(600)
            gui6.height(120)
        with components3.Border(color=(1,1,1,1)):
            gui6.width(600)
            gui6.height(120)

#     with components3.Scrollable():
#         with gui6.Node():
#             gui6.position_type(gui6.Absolute)
#             with PipelineEditor(name='pipeline_editor', pipeline=main_pipe, audio_output=editor.audio_output):
#                 pass
#         gui6.margin(gui6.All, 10.0)
#         gui6.flex_grow(1.0)
#         gui6.flex_shrink(1.0)
#     locator = editor.audio_output.transport.locator
#     selection = list(locator.list())
#     with components3.Scrollable(vertical = False) as this:
#         gui6.flex_direction(gui6.Row)
#         gui6.margin(gui6.All, 10.0)
#         gui6.height(200)
#         for i, (root, name, desc) in enumerate(selection):
#             node = plugin_entry(root, name, desc)
#             node.name = i
#             this.add_child(node)
# 
class Editor:
    def __init__(self):
        locator = aural.ladspa.Locator()
        engine = aural.ladspa.Engine(sample_rate=44100, sample_count=4096)
        pi = build_pipeline(engine, locator, main_pipe, [], cache={})
        tr = Transport(engine, locator, pi)
        self.audio_output = aural.device.SDLDevice(tr, *pi.outputs)
 
        self.running = False
        self.time = 0.0
        self.widgets = dict()
 
        #mido_in = mido.backend.get_input_names()[-1]
        #self.midi_input = mido.backend.open_input(mido_in)
 
#     def fire_midi_keyboard(self, ui, m):
#         ui.clavier.append(m)
#         self.clavier.send(m)

    def widget(self, *args):
        widget = Widget(*args)
        self.widgets[widget.uid] = widget
        return widget
 
    def ui(self):
        self.running = True
        sdl2.ext.init(video=True, audio=True)

        flags = sdl2.SDL_WINDOW_RESIZABLE | sdl2.SDL_WINDOW_OPENGL
        root = self.widget("rollernote", 1200, 700, flags, gui6.GUI, main_widget(self))
 
        sdl2.SDL_StartTextInput()

        while self.running:
            self.time = sdl2.SDL_GetTicks64() / 1000.0
            self.running = process_widgets(self.widgets, root)

#             for msg in self.midi_input.iter_pending():
#                 if msg.type == 'note_on':
#                     self.fire_midi_keyboard(root.payload, On(msg.note, msg.velocity / 127))
#                 if msg.type == 'note_off':
#                     self.fire_midi_keyboard(root.payload, Off(msg.note))
# 
        self.audio_output.close()
        sdl2.SDL_StopTextInput()
        sdl2.ext.quit()
 
if __name__=='__main__':
    Editor().ui()


repository: tomsons26/tomsons_RE_scripts
file: dump_asm.py
wanted_name: ASM Dumper Script
comment: ASM dumper script
created_at: 2017-04-02 23:01:59+00:00
pushed_at: 2025-01-27 12:14:22+00:00
url:
https://github.com/tomsons26/tomsons_RE_scripts/blob/8329a2b15eb001a055ae728a07407940da3f9489/dump_asm.py

content:
#by tomsons26
#thank fuck for https://github.com/tmr232/idapython/blob/master/examples/ex_actions.py

import idc
import idaapi
import idautils
import ida_nalt
import ida_idaapi
import ida_bytes
import ida_funcs
import ida_ida
import ida_ua
import ida_name
import ida_xref
import ida_kernwin

ASM_DUMP_PATH = "D:\\Temp\\"

# .asm is appended
PROJECT_NAME = "project"
TARGET_NAME = "target"

def clear_output():
	form = idaapi.find_widget("Output window")
	idaapi.activate_widget(form, True)
	idaapi.process_ui_action("msglist:Clear")

class FunctionSizeHash:

	def __init__(self, name, size, ihash, vhash, bhash, addr, ugh):
		self.name = name
		self.size = size
		self.ihash = ihash
		self.vhash = vhash
		self.bhash = bhash
		self.addr = addr
		self.ugh = ugh

#	def __repr__(self):
#		return '%s\t%s\t0x%08X' % (self.name, self.size, self.hash, self.addr)


def define_uncalled_functions():
	for addr, name in idautils.Names():
		flags = ida_bytes.get_full_flags(addr)
		if ida_bytes.is_code(flags):
			ida_funcs.add_func(addr)

def GetName(name):
	dname = idc.demangle_name(name, 8)
	if dname == None:
		dname = name

	return dname

def name_ex(From, ea):
	return idc.get_name(ea, idc.GN_LOCAL)

def xref_count(addr):
	i = 0
	xrefs = idaapi.xrefblk_t()
	if xrefs.first_to(addr, 0):
		i +=1
		while xrefs.next_to():
			i += 1
	return i

keleven = 17391172068829961267

def _cycle(h, b):
	h |= 5
	h ^= b
	h *= h
	h ^= (h >> 32)
	h &= 0xffffffffffffffff
	return h

def instruction_hash(addr):
	h = keleven
	for ea in idautils.FuncItems(addr):
		h = _cycle(h, idc.get_wide_byte(ea))
		# go over all additional bytes of any instruction
		for i in range(ea + 1, ea + idc.get_item_size(ea)):
			h = _cycle(h, idc.get_wide_byte(i))
	return h

def identity_hashOLD(addr):
	h = keleven
	for ea in idautils.FuncItems(addr):
		h = _cycle(h, idc.get_wide_byte(ea))
		# does nothing?
		# skip additional bytes of any instruction that contains an offset in it
		#if idautils.CodeRefsFrom(ea, False) or idautils.DataRefsFrom(ea):
		#	print "bailing on ", hex(ea), "\n"
		#	continue
		#for i in range(ea + 1, ea + idc.get_item_size(ea)):
		#	h = _cycle(h, idc.get_wide_byte(i))
	if h == keleven:
		#print "Warning couldn't hash ", value_as_hex_str(addr), "\n"
		return 0x0000000000000000

	return h

def identity_hash(addr):
	h = keleven
	insn = ida_ua.insn_t()
	for ea in idautils.FuncItems(addr):
		length = ida_ua.decode_insn(insn, ea)
		if length == 0:
			#print "failed decoding //", value_as_hex_str(ea)
			continue
		h = _cycle(h, insn.itype)
		if h == keleven:
			#print "Warning couldn't hash ", value_as_hex_str(addr), "\n"
			return 0x0000000000000000

	return h

def value_as_hex_str(v):
	return "0x%016X" % v

#old and flawed, using immediate_hash now
def value_hash(addr):
	h = keleven
	insn = ida_ua.insn_t()

	for ea in idautils.FuncItems(addr):
		length = ida_ua.decode_insn(insn, ea)
		if length == 0:
			#print "failed decoding //", value_as_hex_str(ea)
			continue
		for c, v in enumerate(insn.ops):

			#debug
			#print hex(ea), "type ", v.type, " dtype", v.dtype, "//flags ", get_full_flags(ea)

			#skip no operands
			if v.type == idaapi.o_void:
				#print "     skipping void at //", value_as_hex_str(ea)
				break

			#skip mem refs
			if v.type == idaapi.o_mem:
				#print "     skipping mem at //", value_as_hex_str(ea)
				continue

			#skip calls
			if v.type == idaapi.o_far or v.type == idaapi.o_near:
				#print "     breaking call at //", value_as_hex_str(ea)
				break


			# useless cause is_loaded covers it?
			#
			# skip additional bytes of any instruction that contains an offset in it
			# this code is a disaster
			#refs1 = list(idautils.DataRefsFrom(ea))
			#refs2 = list(idautils.CodeRefsFrom(ea, False))
			#if len(refs1) != 0 or len(refs2) != 0:
			#if len(refs1) != 0:
			#trying this tho likely slow
			#xref = list(XrefsFrom(ea, ida_xref.XREF_DATA))
			#if xref:
				#x = xref[0]
				#print(hex(ea), x.type, XrefTypeName(x.type), 'from', hex(x.frm), 'to', hex(x.to))
				#boo data reference
				#if x.type == ida_xref.dr_O:
					#print "//skipping xref at ", value_as_hex_str(ea)
					#continue

			if v.type == idaapi.o_imm:
				value = idc.get_operand_value(ea, c)
				val_ea = ea + v.offb + v.offo

				fixup = idc.get_fixup_target_type(val_ea)
				#print fixup
				if fixup == idc.FIXUP_OFF32:
					#print "     skipping address with fixup value", value, "at //", value_as_hex_str(val_ea)
					continue

				# we only want intermediate values, no memory addresses
				flags = idc.get_full_flags(ea);
				#obj bug workaround
				if value != 0 and value != 0xFFFFFFFF:
					#if is_loaded(value) or isRef(flags): #skips nonaddresses in oobj....
					if idc.is_loaded(value):
						#print "     skipping address value", value, "at //", value_as_hex_str(val_ea)
						continue

				#debug
				#print "hashing value", value, "at //", value_as_hex_str(val_ea)
				h = _cycle(h, value)

	#nothing to hash?
	if h == keleven:
		#print "Warning couldn't hash ", value_as_hex_str(addr), "\n"
		return 0x0000000000000000

	return h

#print value_as_hex_str(value_hash(ScreenEA()))

def immediate_hash(addr):
	h = keleven
	insn = ida_ua.insn_t()

	for ea in idautils.FuncItems(addr):
		length = ida_ua.decode_insn(insn, ea)
		if length == 0:
			#print "failed decoding //", value_as_hex_str(ea)
			continue

		for c, v in enumerate(insn.ops):

			#no operand
			if v.type == idaapi.o_void:
				#print "     skipping mem at //", value_as_hex_str(ea)
				continue

			#skip calls
			if v.type == idaapi.o_far or v.type == idaapi.o_near:
				#print "     breaking call at //", value_as_hex_str(ea)
				break

			#skip mem refs
			if v.type == idaapi.o_mem:
				#print "     skipping mem at //", value_as_hex_str(ea)
				continue

			#has no immediate
			value = ida_ua.get_immvals(ea, c)
			if len(value) == 0:
				continue

			#print hex(ea), "type ", v.type, " dtype", v.dtype, "//flags ", get_full_flags(ea)

			val_ea = ea + v.offb + v.offo

			fixup = idc.get_fixup_target_type(val_ea)
			#print fixup
			if fixup == idc.FIXUP_OFF32:
				#print "     skipping address with fixup value", value, "at //", value_as_hex_str(val_ea)
				continue

			dw = idc.get_wide_dword(val_ea)
			if ida_bytes.is_loaded(dw) and ida_bytes.is_data(ida_bytes.get_full_flags(dw)):
				#print "     skipping data", value, "at //", value_as_hex_str(val_ea)
				continue

			#debug
			#print "hashing value", value, "at //", value_as_hex_str(val_ea)
			h = _cycle(h, value[0])

	if h == keleven:
		#print "Warning couldn't hash ", value_as_hex_str(addr), "\n"
		return 0x0000000000000000

	return h

#print value_as_hex_str(immediate_hash(ScreenEA()))

#check with if mnem in jmp_ins_list:
#jmp_ins_list = ["jmp","jc","jnc","jz","jnz","js","jns","jo","jno","jp",
#				"jpe","jnp","jpo","ja","jnbe","jae","jnb","jb","jnae","jbe",
#				"jna","je","jne","jg","jnle","jge","jnl","jl","jnge","jle","jng"]

from ida_allins import *

cond_jumps = (
	idaapi.NN_ja,
	idaapi.NN_jae,
	idaapi.NN_jb,
	idaapi.NN_jbe,
	idaapi.NN_jc,
	idaapi.NN_jcxz,
	idaapi.NN_jecxz,
	idaapi.NN_jrcxz,
	idaapi.NN_je,
	idaapi.NN_jg,
	idaapi.NN_jge,
	idaapi.NN_jl,
	idaapi.NN_jle,
	idaapi.NN_jna,
	idaapi.NN_jnae,
	idaapi.NN_jnb,
	idaapi.NN_jnbe,
	idaapi.NN_jnc,
	idaapi.NN_jne,
	idaapi.NN_jng,
	idaapi.NN_jnge,
	idaapi.NN_jnl,
	idaapi.NN_jnle,
	idaapi.NN_jno,
	idaapi.NN_jnp,
	idaapi.NN_jns,
	idaapi.NN_jnz,
	idaapi.NN_jo,
	idaapi.NN_jp,
	idaapi.NN_jpe,
	idaapi.NN_jpo,
	idaapi.NN_js,
	idaapi.NN_jz,
	idaapi.NN_loopw,
	idaapi.NN_loop,
	idaapi.NN_loopd,
	idaapi.NN_loopq,
	idaapi.NN_loopwe,
	idaapi.NN_loope,
	idaapi.NN_loopde,
	idaapi.NN_loopqe,
	idaapi.NN_loopwne,
	idaapi.NN_loopne,
	idaapi.NN_loopdne,
	idaapi.NN_loopqne
)

jumps = (
	idaapi.NN_jmp,
	idaapi.NN_jmpfi,
	idaapi.NN_jmpni,
	idaapi.NN_jmpshort
)

branch_log = []

def branch_hash(addr, log):
	h = keleven
	insn = ida_ua.insn_t()
	fstart = idc.get_func_attr(addr, idc.FUNCATTR_START)
	for ea in idautils.FuncItems(addr):
		#mnem = idc.print_insn_mnem(ea)
		#if mnem in jmp_ins_list:
		ida_ua.decode_insn(insn, ea)
		if insn.itype in cond_jumps or insn.itype in jumps:
			#length = ida_ua.decode_insn(insn, ea)
			#if length == 0:
				#print "failed decoding //", value_as_hex_str(ea)
			#	continue
			#print hex(insn.cs), hex(insn.ip), hex(insn.ea), insn.itype, insn.size
			#print hex(insn.ea - fstart)

			size = idc.get_item_size(ea);

			#if mnem == "jmp":
			if insn.itype in jumps:
				if size > 4:
					# only hash the first two bytes
					size = 1;
				elif size > 5:
					# ignore switch addresses
					size = 2;

			for i in range(ea, ea + size):
				h = _cycle(h, idc.get_wide_byte(i))
				if log:
					branch_log.append('%08X %s' % (ea - fstart, value_as_hex_str(h)))
		#else:
		#	print "nope", hex(ea)[0:-1]

	if h == keleven:
		#print "Warning couldn't hash ", value_as_hex_str(addr), "\n"
		return 0x0000000000000000

	return h

#print branch_hash(here(), 0)

def build_function_list(funcs):
	function_list = []
	for addr in funcs:
		#skip extern segments that are usually in objs
		#if idc.get_segm_attr(addr, idc.SEGATTR_TYPE) == idaapi.SEG_XTRN:
		#	continue;
		#
		#skip obj comdats
		#if "COMDAT" in idc.get_segm_name(addr):
		#	continue;

		start, end = list(idautils.Chunks(addr))[0]
		name = GetName(idc.get_func_name(addr))
		#stop at first lib, we passed game code
		#if name == "__wcpp_2_undef_vfun__":
		#	break

		#h = hash(hasher.calculate(start))
		ih = identity_hash(start)
		vh = immediate_hash(start)
		bh = branch_hash(start, 0)
		#convert to hex string, strip 0x and L, make upper case
		ihs = value_as_hex_str(ih)
		vhs = value_as_hex_str(vh)
		bhs = value_as_hex_str(bh)

		cmt = idc.get_func_cmt(start, 1)

		ugh = 0
		if cmt == 'fixed diff':
			ugh = 1

		entry = FunctionSizeHash(name, end - start, ihs, vhs, bhs, start, ugh)

		function_list.append(entry)

	return function_list

def sort_method(list):
	return list.name


# wrapper for context class so i don't have to write one for every single action
class ctx_wrapper(idaapi.action_handler_t):
	def __init__(self, action_function):
		idaapi.action_handler_t.__init__(self)
		self.action_function = action_function

	def activate(self, ctx):
		self.action_function(ctx)
		return 1

	def update(self, ctx):
		return idaapi.AST_ENABLE_ALWAYS

#ida is buggy with listing option changing, doesn't always restore them...
#we don't want any strange modifications for the listing
#representation in our work idbs
def is_cnc():
	if ida_nalt.get_root_filename() == "C&C95.EXE":
		return 1

	if ida_nalt.get_root_filename() == "sole.exe":
		return 1

	if ida_nalt.get_root_filename() == "redalert.exe":
		return 1

	if "ra95" in ida_nalt.get_root_filename():
		return 1

	return 0

def get_stack_offset_str(v):
	v = (v ^ 0x80000000) - 0x80000000
	sign = "?"
	if v > 0:
		sign = "+"
	else:
		sign = "-"
		v = abs(v)

	return sign + "%Xh" % v

#prob will rework as needed as above is extensive
def format_op(addr, num):
	type = idc.get_operand_type(addr, num)
	op = idc.print_operand(addr, num)
	val = idc.get_operand_value(addr, num)

	if op == "":
		return op

	pos = op.find("dword ptr ")
	if pos != -1:
		op = op[0:pos] + op[pos + 10:]

	#pos = op.find("offset ")
	#if pos == 0:
	#	op = op[pos + 7:]

	pos = op.find("ds:")
	if pos != -1:
		op = op[0:pos] + op[pos + 3:]

	#if type == 2:
	#	pos = op.find("[")
	#	if pos == -1:
	#		op = "[" + op + "]"

	# attempt to remove op_num hacks
	elif type == idaapi.o_displ:
		ptr = op.find("(")
		#don't touch obj data refs
		if ptr == -1:
			rstart = op.find("[")
			if rstart != -1:
				rfound = 0
				#print hex(addr), op
				rend = op.find("+")
				if rend != -1:
					rfound = 1
				else:
					rend = op.find("-")
					if rend != -1:
						rfound = 2

				if rfound != 0:
					reg = op[rstart+1:rend]
					stack = get_stack_offset_str(val)
					op = "[" + reg + stack + "]"

	elif type == idaapi.o_imm:
		usehex = 1
		pos = op.find("offset")
		if pos >= 0:
			op = "offset " + op
			usehex = 0

		pos = op.find("asc")
		if pos != -1:
			l = idc.generate_disasm_line(addr, 0)
			pos = l.find(";")
			op = "offset ascii " + l[pos:]
			usehex= 0

		if usehex:
			op = "%Xh" % val

	"""
	#ugh
	elif type == 4:
		pos = op.find("[")
		if pos >= 0:
			print "pos %s" % pos
			idx = pos - 1
			var = ""
			while idx >= 0:
				ch = op[idx:idx + 1]
				print "ch %s" % ch
				if ch == " ":
					break
				var = ch + var
				print "pos %s" % var
				idx = idx - 1
			if var != "":
				op = op[0:idx + 1] + "[" + var + "+" + op[pos + 1:]
	"""

	return op

def build_op_dtype_line(addr):
	line = ""
	insn = ida_ua.insn_t()
	length = ida_ua.decode_insn(insn, addr)

	# type is "Operand types" https://hex-rays.com/products/ida/support/sdkdoc/group__o__.html
	# dtype is "Operand value types" https://hex-rays.com/products/ida/support/sdkdoc/group__dt__.html
	if length != 0:
		line = "%02d %02d %02d %02d %02d %02d %02d %02d" % (insn.ops[0].dtype, insn.ops[1].dtype, insn.ops[2].dtype, insn.ops[3].dtype, insn.ops[0].type, insn.ops[1].type, insn.ops[2].type, insn.ops[3].type)

	return line

import time
from timeit import default_timer as timer
def dump_func_asm(ea):
	listing = []

	del branch_log[:]

	if idaapi.IDA_SDK_VERSION <= 700:
		minEA = idc.MinEA()
		maxEA = idc.MaxEA()
	else:
		minEA = ida_ida.inf_get_min_ea()
		maxEA = ida_ida.inf_get_max_ea()

	fstart = idc.get_func_attr(ea, idc.FUNCATTR_START)
	fend = idc.get_func_attr(ea, idc.FUNCATTR_END)

	fname = GetName(idc.get_func_name(fstart))

	t = timer()
	ih = identity_hash(fstart)
	print("Completed identity_hash in ", timer() - t, "seconds")
	t = timer()
	vh = immediate_hash(fstart)
	print("Completed immediate_hash in", timer() - t, "seconds")
	t = timer()
	bh = branch_hash(fstart, 1)
	print("Completed branch_hash in   ", timer() - t, "seconds")

	#convert to hex string, strip 0x and L, make upper case
	ihs = value_as_hex_str(ih)
	vhs = value_as_hex_str(vh)
	bhs = value_as_hex_str(bh)

	cmt = idc.get_func_cmt(fstart, 1)

	ugh = 0
	if cmt == 'fixed diff':
		ugh = 1

	entry = FunctionSizeHash(fname, fend - fstart, ihs, vhs, bhs, fstart, ugh)
	listing.append('%s-%s-%s\t//%s\t//%d\n' % (entry.ihash, entry.vhash, entry.bhash, entry.name, entry.size))

	fname = idc.get_func_name(fstart)

	listing.append("section .text\n")
	listing.append("global %s\n" % fname)
	listing.append("%s:\n" % fname)

	addr = fstart
	while addr != idc.BADADDR:
		if addr != fstart:
			name = name_ex(addr, addr)

			#ignore debug_info labels in objs
			#if name != "" and ida_xref.get_first_fcref_to(addr) == -1:
			#if name != "" and ida_xref.get_first_dref_to(addr) == 0xFFFFFFFF:
			if name != "" and ida_xref.get_first_fcref_from(addr) != 0xFFFFFFFF or ida_xref.get_first_fcref_to(addr) != 0xFFFFFFFF:
				#we want to keep our labels
				if not name.startswith("loc_"):
					listing.append("%s:" % name)
				else:
					listing.append("loc_%X:" % addr)


		op0Type = idc.get_operand_type(addr, 0)
		op1Type = idc.get_operand_type(addr, 1)
		op2Type = idc.get_operand_type(addr, 2)

		"""
		#horrible hack, trying currently to properly parse ops
		op0 = 0
		op1 = 0
		if op0Type == idaapi.o_phrase or op0Type == idaapi.o_displ:
			val = idc.get_operand_value(addr, 0)
			if val >= minEA and val <= maxEA:
				pass
			else:
				op0 = idc.op_num(addr, 0)

		if op1Type == idaapi.o_phrase or op1Type == idaapi.o_displ:
			val = idc.get_operand_value(addr, 1)
			if val >= minEA and val <= maxEA:
				pass
			else:
				op0 = idc.op_num(addr, 1)
		"""

		line = idc.generate_disasm_line(addr, 0)
		mnem = idc.print_insn_mnem(addr)

		oper0 = format_op(addr, 0)
		oper1 = format_op(addr, 1)
		oper2 = format_op(addr, 2)

		#if mnem == "call":
		#	if op0Type >= idaapi.o_imm and op0Type <= idaapi.o_near:
		#		if oper0.find("_") != 0:
		#			oper0 = "_" + oper0

		if oper0 == "" and oper1 == "" and oper2 == "":
			if line.find(mnem) != 0:
				mnem = line
			line = "%-8s" % (mnem)
		elif oper2 == "":
			if oper1 == "":
				line = "%-7s %s" % (mnem, oper0)
			elif oper0 == "":
				line = "%-7s %s" % (mnem, oper1)
			else:
				line = "%-7s %s, %s" % (mnem, oper0, oper1)
		else:
			line = "%-7s %s, %s, %s" % (mnem, oper0, oper1, oper2)

		#add a new line after calls and branches
		if op0Type == idaapi.o_near or op0Type == idaapi.o_far or mnem == "call":
			line = line + "\n"

		listing.append("%08X:   %s   %s" % (addr - fstart, build_op_dtype_line(addr), line))
		#listing.append("         %s" % (line))

		"""
		#horrible hack, trying curently to properly parse ops
		#if op0:
			#idc.op_stkvar(addr, 0)
		#if op1:
			#idc.op_stkvar(addr, 1)
		"""

		addr = idc.next_head(addr, fend)

	return listing

#l = dump_func_asm(ScreenEA())
#for item in l:
#	print "%s" % (item)

def dump_func(ctx, name, addr, cnc):
	file = ASM_DUMP_PATH + name + ".asm"

	#old
	#fstart = idc.get_func_attr(addr, idc.FUNCATTR_START)
	#fend = idc.get_func_attr(addr, idc.FUNCATTR_END)

	#these are buggy..
	#cnc idb already set up, don't mess with it
	#if cnc == 0:
	#	#we want relative offsets, no prefixes and no comments
	#	SetLongPrm(INF_PREFFLAG, PREF_FNCOFF)
	#	SetLongPrm(INF_CMTFLAG, SW_NOCMT)
	#	SetLongPrm(INF_OUTFLAGS, ~OFLG_SHOW_PREF)
	#	SetLongPrm(INF_OUTFLAGS, ~OFLG_PREF_SEG)

	#idc.gen_file(idc.OFILE_LST, file, fstart, fend, 0)
	#idc.gen_file(idc.OFILE_ASM, file, fstart, fend, 0)

	#if cnc == 0:
	#	#restore original flags
	#	SetLongPrm(INF_PREFFLAG, ~PREF_FNCOFF)
	#	SetLongPrm(INF_CMTFLAG, ~SW_NOCMT)
	#	SetLongPrm(INF_OUTFLAGS, OFLG_SHOW_PREF)
	#	SetLongPrm(INF_OUTFLAGS, OFLG_PREF_SEG)

	#new using modified disassembly
	f = open(file, "w")
	l = dump_func_asm(addr)

	for item in l:
		f.write(item + "\n")

	f.write("\n")

	for item in branch_log:
		f.write(item + "\n")

	f.close()

	print("Dumped Assembly to " + file)

def dump_func_list(ctx, sorted):

	define_uncalled_functions()

	clear_output()

	function_list = build_function_list(idautils.Functions())

	if sorted:
		function_list.sort(key=sort_method)

	for item in function_list:
		#print('%s\t//%s\t//%s\t//%d' % (item.ihash, item.vhash, item.name, item.size))
		extra = ''
		if item.ugh:
			extra = '|FIXEDDIFF'

		print('%s-%s-%s\t//%s\t//%d|%d%s' % (item.ihash, item.vhash, item.bhash, item.name, item.size, xref_count(item.addr), extra))

def dump_select_func_list(ctx):

	clear_output()

	function_list = []

	funcs = []
	# fill address array
	for pfn_idx in ctx.chooser_selection:
		pfn = ida_funcs.getn_func(pfn_idx)
		if pfn:
			funcs.append(pfn.start_ea)

	function_list = build_function_list(funcs);

	for item in function_list:
		#print('%s\t//%s\t//%s\t//%d' % (item.ihash, item.vhash, item.name, item.size))
		print('%s-%s-%s|%d\t//%s\t//%d' % (item.ihash, item.vhash, item.bhash, xref_count(item.addr), item.name, item.size))

def dump_project_asm(ctx):
	dump_func(ctx, PROJECT_NAME, idc.get_screen_ea(), is_cnc())

def dump_target_asm(ctx):
	dump_func(ctx, TARGET_NAME, idc.get_screen_ea(), is_cnc())

def dump_list(ctx):
	dump_func_list(ctx, 0)

def dump_sorted_list(ctx):
	dump_func_list(ctx, 1)

def no_op(ctx):
	return

# for the sake of knowing its active, appears as a text button next analyze buttons
ACTION_DMP1 = "asmdumpplugin:dmp1"
ACTION_DMP2 = "asmdumpplugin:dmp2"
ACTION_DMP3 = "asmdumpplugin:dmp3"
ACTION_DMP4 = "asmdumpplugin:dmp4"
ACTION_DMP5 = "asmdumpplugin:dmp5"

def asm_dumper_init():
	if idaapi.register_action(idaapi.action_desc_t(
			ACTION_DMP1,
			"Dump Project ASM",
			ctx_wrapper(dump_project_asm),
			None,
			"",
			-1,
			ida_kernwin.ADF_NO_UNDO
			)):

		# describe the action
		irraction_desc = idaapi.action_desc_t(
			ACTION_DMP2,
			"Dump Target ASM",
			ctx_wrapper(dump_target_asm),
			None,
			"",
			-1,
			ida_kernwin.ADF_NO_UNDO
		)
		assert idaapi.register_action(irraction_desc), "Action registration failed"

		irraction_desc = idaapi.action_desc_t(
			ACTION_DMP3,
			"Dump Func List",
			ctx_wrapper(dump_list),
			None,
			"",
			-1,
			ida_kernwin.ADF_NO_UNDO
		)
		assert idaapi.register_action(irraction_desc), "Action registration failed"

		irraction_desc = idaapi.action_desc_t(
			ACTION_DMP4,
			"Dump Sorted Func List",
			ctx_wrapper(dump_sorted_list),
			None,
			"",
			-1,
			ida_kernwin.ADF_NO_UNDO
		)
		assert idaapi.register_action(irraction_desc), "Action registration failed"

		irraction_desc = idaapi.action_desc_t(
			ACTION_DMP5,
			"Dump Select Func List",
			ctx_wrapper(dump_select_func_list),
			None,
			"",
			-1,
			ida_kernwin.ADF_NO_UNDO
		)
		assert idaapi.register_action(irraction_desc), "Action registration failed"

		print("Actions registered. Attaching to menu.")

		# Insert the action in a toolbar
		if idaapi.attach_action_to_toolbar("AnalysisToolBar", ACTION_DMP1):
			print("Attached to toolbar.")
		else:
			print("Failed attaching to toolbar.")

		# Insert the action in a toolbar
		if idaapi.attach_action_to_toolbar("AnalysisToolBar", ACTION_DMP2):
			print("Attached to toolbar.")
		else:
			print("Failed attaching to toolbar.")

		# Insert the action in a toolbar
		if idaapi.attach_action_to_toolbar("AnalysisToolBar", ACTION_DMP3):
			print("Attached to toolbar.")
		else:
			print("Failed attaching to toolbar.")

		# Insert the action in a toolbar
		if idaapi.attach_action_to_toolbar("AnalysisToolBar", ACTION_DMP4):
			print("Attached to toolbar.")
		else:
			print("Failed attaching to toolbar.")

		# Insert the action in a toolbar
		if idaapi.attach_action_to_toolbar("AnalysisToolBar", ACTION_DMP5):
			print("Attached to toolbar.")
		else:
			print("Failed attaching to toolbar.")

		if idc.get_segm_end(ida_ida.inf_get_min_ea()) >= 0x006C0000:
			# assume binary is too large to handle it currently, disable full function list buttons
			idaapi.unregister_action(ACTION_DMP3)
			idaapi.unregister_action(ACTION_DMP4)

def asm_dumper_term():
	# No need to call detach_action_from_menu(); it'll be
	# done automatically on destruction of the action.
	if idaapi.unregister_action(ACTION_DMP1):
		idaapi.unregister_action(ACTION_DMP2)
		idaapi.unregister_action(ACTION_DMP3)
		idaapi.unregister_action(ACTION_DMP4)
		idaapi.unregister_action(ACTION_DMP5)
	else:
		print("Failed to unregister action.")

class asm_dumper_t(idaapi.plugin_t):
	#flags = idaapi.PLUGIN_FIX
	flags = 0
	comment = "ASM dumper script"
	help = ""
	wanted_name = "ASM Dumper Script"
	wanted_hotkey = ""

	def init(self):
		print("Registering ASM Dumper.")
		asm_dumper_init()
		return idaapi.PLUGIN_KEEP

	def run(self, arg):
		pass

	def term(self):
		print("Unregistering ASM Dumper.")
		asm_dumper_term()

def PLUGIN_ENTRY():
	return asm_dumper_t()


repository: sunyymq/windows.cracking.tools
file: IDA_Pro_v6.6/plugins/Krypton.py
description: windows crackings tools
created_at: 2018-03-06 11:32:00+00:00
pushed_at: 2017-11-22 02:33:01+00:00
url:
https://github.com/sunyymq/windows.cracking.tools/blob/4cfafb4bf9c82ad8a8975d3bedbe49f4df1207ca/IDA_Pro_v6.6/plugins/Krypton.py

content:
doc = """
#################################################################################
#										#
#  Krypton is an IDA Plugin that assists one in executing a function from IDB	#
#  (IDA database) using IDA's powerful Appcall feature				#
#										#
#  krypton takes xrefs from a given function (say a possible decoder) to  	#
#  find all function calls to it and then parses and finds the parameters used 	#
#  (including prototype, no of arguments, and the arguments themselves) from 	#
#  instructions and uses them to execute the function using appcall, this is 	#
#  most useful in analysing a malware binary with encryption			#
#										#
#  To Install, Copy this file into IDA Plugin folder,				#
#  Std path: "C:\Program Files\IDA\plugins\"					#
#  NOTE: Appcall emulation feature requires IDA >= 5.6				#
#										#
#  Author: Karthik Selvaraj at Symantec Corporation				#
#  Email : neoxfx at gmail dot com						#
#										#
#  Krypton is free software: you can redistribute it and/or modify		#
#  it under the terms of the GNU General Public License as published by		#
#  the Free Software Foundation, either version 3 of the License, or		#
#  (at your option) any later version.						#
#										#
#  This program is distributed in the hope that it will be useful,		#
#  but WITHOUT ANY WARRANTY; without even the implied warranty of		#
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the		#
#  GNU General Public License for more details.					#
#										#
#  You should have received a copy of the GNU General Public License		#
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.	#
#										#
#################################################################################
"""

version     = '1.0'
pluginname  = "Krypton"
pluginVname = pluginname + " " + str(version)
debug       = False
CreateIDC   = True
verbose     = 0

import sys,string,os

def kprint(*s):
    print "[%s]" % pluginname,
    for k in s:
        print k,
    print


try:
    import idautils
    import idc
    from idaapi import *
except:
    kprint ("Error: Use within IDA (>= 5.6)!!")
    print doc
    sys.exit(0)


gsplitChar = "|"
class HelperRoutines:
    """
    This class contains helper routines that the rest of the classes use.
    """
    debug                 = debug
    createIDC             = CreateIDC
    version               = version
    verbose               = verbose
    interConnectInstr     = 'jmp'         # basic block interconnect instruction, in case of spaghetti code
    argPassMech           = "push"
    argPassType           = "__cdecl"
    FILTER                = ''.join([(len(repr(chr(x)))==3) and chr(x) or '.' for x in range(256)])
    logPointer            = None
    logPointerIdc         = None

    def getDataValue(self, addr, size):
        if (size == 1):
            return Byte(addr)
        elif (size == 2):
            return Word(addr)
        elif (size == 4):
            return Dword(addr)
        else:
            self.log(2, "Error! while parsing data: 0x%x --> size: %d" % (addr, size))
            raise

    def log(self, v, cmt):
        if (self.debug) and (v <= self.verbose):
            if self.logPointer == None:
                self.openLogFile("")
            self.logPointer.write(cmt+"\n")

    def logIdc(self, cmt):
        if (self.createIDC):
            #cmt = cmt.translate(self.FILTER)
            if self.logPointerIdc == None:
                self.openIdcLogFile("")
            self.logPointerIdc.write(cmt+"\n")

    def flushlog(self):
        if (self.logPointer != None):
            self.logPointer.flush()
        if (self.logPointerIdc != None):
            self.logPointerIdc.flush()

    def logClose(self):
        if (self.logPointer != None):
            self.logPointer.close()
        if (self.logPointerIdc != None):
            self.logPointerIdc.close()

    def openLogFile(self, fnamePrefix):
        if not self.debug:
            return
        if (self.logPointer != None):
            self.logPointer.close()
        self.logPointer = open(GetIdbPath()+"_%s_%s.log"%(pluginVname, fnamePrefix), "w")
        s = "//%s - LOG file %s" % (pluginVname+fnamePrefix, GetIdbPath())
        self.logPointer.write(s + "\n//" + "=" * len(s) + "\n")

    def openIdcLogFile(self, fnamePrefix):
        if not self.createIDC:
            return
        if (self.logPointerIdc != None):
            self.logPointerIdc.close()
        self.logPointerIdc = open(GetIdbPath()+"_%s_%s.idc"%(pluginVname, fnamePrefix), "w")
        s = "//%s - IDC file %s" % (pluginVname+fnamePrefix, GetIdbPath())
        self.logPointerIdc.write(s + "\n//" + "=" * len(s) + "\n")

    def getXrefs(self, ea):
        a = []
        try:
            a = list(CodeRefsTo(ea, 1))
        except:
            a = []
        return a



class Function(HelperRoutines):

    name               = ""
    ea                 = None
    nargs              = 0
    xrefs              = []
    proto              = ""
    argsSizeArray      = []
    runUntilEA         = {'start':BADADDR, 'end':BADADDR}
    ESCAPE_CODE_BEGIN  = 0xF0
    ArgArrCodes        = {"CONST":0xFE, "BUFFER":0xFF}

    def __init__(self, ea, proto, argSizeArr, argPassMech="push"):

        if ((not isLoaded(ea)) or (proto == None) or (type(proto).__name__ != "str") or (argSizeArr == None)):
            self.log(2, "Error! Bad function parameters! :0x%x" % ea)
            raise
        try:
            self.name   = GetFunctionName(ea)
        except:
            self.name   = "0x%x" % ea

        self.ea     = ea

        if (type(argSizeArr).__name__ == "list"):
            self.nargs  = len(argSizeArr)
            self.argsSizeArray  = argSizeArr
        elif (type(argSizeArr).__name__ == "str"):
            b = eval(argSizeArr)
            self.argsSizeArray = b
            self.nargs = len(self.argsSizeArray)
        else:
            kprint("type:%s" % type(argSizeArr).__name__)
            kprint("arr:%s, %d" % (argSizeArr, len(argSizeArr)))
            kprint("arg array issue!")
            raise

        self.xrefs  = self.getXrefs(ea)

        if len(self.xrefs) == 0:
            self.log(2, "Error! No Xref to function ea :0x%x" % ea)
            raise
        self.proto  = proto
        self.argPassMech    = argPassMech


    def getInstrDataPair(self, i):

        if (i > self.nargs):
            self.log(1, "Error! Index out of Array!! %d" % i)
            raise
        (k,v) = self.argsSizeArray[i]
        if(type(k).__name__ == "str"):
            try:
                k = self.ArgArrCodes[k.upper()]
            except:
                self.log(1, "Error! Unknown code in Argument Array at index: %d" % i)
                kprint("Error! Unknown code \"%s\" in Argument Array \"%s\"" % (k, repr(self.argsSizeArray)))
                raise
        return (k,v)

    def getData(self):
        return self.ea, len(self.xrefs), self.proto, self.argsSizeArray, self.argPassMech

    def getEA(self):
        return self.ea

    def getName(self):
        return self.name

    def updateName(self):
        try:
            oproto = self.proto
            oname  = self.name
            self.name   = GetFunctionName(self.ea)
            if (self.name != oname):
                self.proto = self.proto.replace(oname, self.name)
        except:
            self.name   = oname
            self.proto  = oproto

    def setInitializerExecution(self, initStart, initEnd):
        if ((initStart == BADADDR) or (initEnd == BADADDR)):
            return False
        self.runUntilEA['start'] = initStart
        self.runUntilEA['end'] = initEnd
        kprint("Initializer set for function \"%s\"->EA_RANGE(0x%x, 0x%x)" % (self.name, initStart, initEnd))
        return True

    def __repr__(self):
        global gsplitChar
        return " 0x%x %c %s %c %s %c %s %c (%04d)" % (self.ea, gsplitChar, self.proto, gsplitChar, repr(self.argsSizeArray).split("\""), gsplitChar, self.argPassMech, gsplitChar, len(self.xrefs))



IDBbackedup = False
class DebuggerCtrl(HelperRoutines):
    """
    This class implements debugger control functions, this class makes Appcall calling possible.
    """
    oldDebuggerOptions = None
    modulePath         = ""
    moduleArgs         = ""
    sourceDir          = ""

    def __init__(self):
        self.modulePath = GetInputFilePath()
        self.moduleArgs = ""
        self.sourceDir  = os.path.dirname(GetIdbPath())

    def setSafeGuard(self):
        global IDBbackedup
        self.oldDebuggerOptions = SetDebuggerOptions(DOPT_ENTRY_BPT|DOPT_LIB_MSGS|DOPT_THREAD_BPT|DOPT_START_BPT)
        if not IDBbackedup:
            nam = GetIdbPath()+".bak"
            kprint("Taking IDB Backup to \"%s\"" % nam)
            SaveBase(nam, 0)
            IDBbackedup = True

    def start(self):
        self.setSafeGuard()
        if not os.path.isfile(self.modulePath):
            self.modulePath = idc.AskFile(0,"*.*","Please select the right Path of the module/executable loaded in IDA")
            idc.SetInputFilePath(self.modulePath)
        LoadDebugger("win32", 0) # load win32 as default
        if (StartDebugger(self.modulePath, self.moduleArgs, self.sourceDir) == -1):
            self.log(1, "Error! Failed to launch debugger!")
        GetDebuggerEvent(WFNE_ANY, -1) # handle first event


    def stop(self):
        global pvv
        global decResults
        global decViews

        StopDebugger()

        # workaround for mimic'king persistent view, as StopDebugger will kill all views created
        # hook processExit event and re-create views
        SetDebuggerOptions(self.oldDebuggerOptions)

        evt = 0
        while (GetDebuggerEvent(WFNE_ANY, -1) != PROCESS_EXIT):
            evt += 1

        try:
            pvv = Protoviewer()

            for (k,v) in decViews.iteritems():
                kprint("attempt closing for decView 0x%x" % k)
                v.Close()
                del v

            decViews = {}
            for (ea,x) in decResults.iteritems():
                (fpt, res) = x
                f = protoStr2FunctionInstance(fpt)
                if (f != None):
                    decViews[ea] = decViewer(f)
        except Exception,e:
            kprint("Exception!! @DebuggerCtrl:Stop(), ", e)



class Decryptor(HelperRoutines):
    """
    This Class that executes a given function using Appcall with parameters from all its call references
    """

    tempBuffers           = []
    trackBackLimit        = 10            # instruction trace back limit before the decoder function call
    func                  = None          # instance of class function to hold all attributes
    params                = {}            # parameter hash tree for each reference
    debugCtrl             = None
    decRes                = {}

    def __init__(self, decFunc):
        nprefix = "_%s" % decFunc.getName()
        self.openLogFile(nprefix)
        self.openIdcLogFile(nprefix)
        self.log(1, "\n\nSTART DECRYPTOR\n" + "-"*50)
        self.log(1, "Decryptor function parameters => %s" %  repr(decFunc))
        self.func = decFunc
        self.debugCtrl = DebuggerCtrl()
        decRes = {}

    def _buffer(self, size=256):
        return  Appcall.byref("\x00" * size)

    def cleanup(self):
        self.debugCtrl.stop()
        self.logClose()

    def setResults(self):
        global decResults
        if self.func.getEA() in decResults:
            del decResults[self.func.getEA()]
        if len(self.decRes) > 0:
            decResults[self.func.getEA()] = (repr(self.func), self.decRes,)
            return True
        return False

    def run(self):

        for ref in self.func.xrefs:
            self.log(4, "[-] checking near " + hex(ref))
            self.parseArgs(ref)

        self.debugCtrl.start()
        self.log(1, "[*] debugger launched")

        if (self.func.runUntilEA['end'] != BADADDR) and (self.func.runUntilEA['start'] != BADADDR):
            idc.SetRegValue(self.func.runUntilEA['start'], 'EIP')
            idc.RunTo(self.func.runUntilEA['end'])

        self.decRes = None
        self.decRes = {}

        for ref in self.func.xrefs:
            try:
                arg = self.params[ref]
            except:
                self.log(3, "[-!] Couldn't find arg, skipping call at 0x%x" % ref)
                continue

            try:
                if (self.execute(ref, arg) == -1):
                    break
            except Exception,e:
                kprint("Exception!! @decryptor:run, ", e)
                pass

        self.log(1, "[*] debugger stopped")
        self.log(1, "\n\nEND DECRYPTOR\n" + "-"*50)
        if not self.setResults():
            kprint("No result from function run \"%s\"" % self.func.getName())

        return True

    def execute(self, ref, argv):

        dFunc = Appcall.typedobj(self.func.proto)
        dFunc.ea = self.func.ea
        buffers = []

        try:
            self.log(1, ('[-] Found call @ 0x%x to Decoder - ' % ref) + self.formattedFuncPrint(argv))

            cmd = "dFunc("
            ind = 0
            ibd = 0
            for i in range(self.func.nargs):
                insSize, dataSize = self.func.getInstrDataPair(i)
                if (insSize == self.func.ArgArrCodes["CONST"]):  # const value by ref
                    t = Appcall.byref(dataSize)
                    cmd += "t, "
                elif (insSize == self.func.ArgArrCodes["BUFFER"]):
                    buffers.append(self._buffer(dataSize))
                    cmd += "buffers[%d], " % ibd
                    ibd += 1
                else:
                    cmd += "argv[%d], " % ind
                    ind += 1

            cmd = cmd[:-2] + ")"
            self.log(3, "\tcmd: \"%s\"" % cmd)

            try:
                # This eval is used to convert a python string to a python code statement
                # Appcall is constructed in the string cmd with right parameters before being called
                s = 0
                s = eval(cmd)
            except Exception,e:
                dcode = GetDebuggerEvent(WFNE_ANY, -1)
                if ((dcode == 1) or (dcode == 0x4) or (dcode == 0x200) or (dcode == 0x1000)):
                    pass
                else:
                    dstr = "[!] debugger error code(0x%x), %s" % (dcode, repr(e))
                    self.log(3, dstr)
                    if str(e.__str__()).startswith("Appcall:") or (dcode == -2) or (dcode == -1) or (dcode == 0x40) or (dcode == 0x400):
                        kprint("[!!] Cannot continue! %s" % dstr)
                        kprint("[!!] Stopping script!!")
                        return -1

            self.log(3, "[+] Buffer post decryption @ 0x%x" % ref)

            obbuf = []
            for buff in buffers:
                o = self.parseResult(buff);
                self.log(3, "\tbuffer = \"%s\"" % o)
                if ((type(o).__name__ == 'str') and ((len(o) > 3) or (not repr(o).startswith("\'\\x")))):
                    obbuf.append(o)

            if isLoaded(s):
                try:
                    sdo = GetString(s)
                    if ((sdo != None) and (type(sdo).__name__ == 'str')):
                        self.log(3, "\tRet (eax) buffer = \"%s\"" % sdo)
                        if (not repr(sdo).startswith("\'\\x")):
                            obbuf.append(sdo)
                except:
                    pass

            if isLoaded(Dword(s)):
                try:
                    sdo = GetString(Dword(s))
                    if ((sdo != None) and (type(sdo).__name__ == 'str')):
                        self.log(3, "\tRet Poi(eax) buffer = \"%s\"" % sdo)
                        if (not repr(sdo).startswith("\'\\x")):
                            obbuf.append(sdo)
                except:
                    pass

            rs = max(obbuf, key=len)
            rs = rs.replace("\n", "\\n")
            self.decRes[ref] = rs
            self.logIdc("MakeRptCmt(0x%x, \"%s\");" % (ref, rs.replace("\"", "\\\"")))
            return 0
        except Exception,e:
            kprint("Exception!! @decryptor:execute, ", e)
            pass

    def parseResult(self, buff):
        i = 0
        unicodeStr = ""
        asciiStr = buff.cstr().strip()
        nullFound = False
        # Parse out the unicode string
        while i + 1 < len(buff.value) and not nullFound:
            # Unicode vaules come in as little endian
            aChar = struct.unpack("<H", buff.value[i:i+2])[0]

            if aChar != 0:
                unicodeStr += unichr(aChar)
            else:
                nullFound = True
            i += 2
        # Works on the assumption the buffer will be null-ed out after
        # character data, has worked in practice
        if len(unicodeStr) > len(asciiStr):
            return unicodeStr.encode('ascii', 'ignore')
        else:
            return asciiStr

    def parseArgs(self, ref):

        i = self.trackBackLimit
        argsToParse = self.func.nargs
        argv = []
        ref_p = ref
        while (i and argsToParse):
            insSize, dataSize = self.func.getInstrDataPair(self.func.nargs-argsToParse)
            if (insSize > self.func.ESCAPE_CODE_BEGIN):
                argsToParse -= 1
            else:
                if (dataSize > insSize):
                    self.log(3, "[!!] invalid argument size pair (%d, %d) for arg no: %d" % (insSize, dataSize, self.func.nargs-argsToParse))
                    raise
                ref_p = idc.PrevHead(ref_p, 0)
                if ref_p == BADADDR:
                    break
                disstr = idc.GetDisasm(ref_p)
                if disstr.startswith(self.func.argPassMech) and (idc.ItemSize(ref_p) == insSize):
                    arVal = self.getDataValue(ref_p + insSize - dataSize, dataSize)
                    argv.append(arVal)
                    self.log(4, "Arg found: (0x%x)" % arVal)
                    argsToParse -= 1
                    if not argsToParse:
                        break
                if len(self.getXrefs(ref_p)) == 1:
                    nref = self.getXrefs(ref_p)[0]
                    refins = idc.GetDisasm(nref)
                    if refins.startswith(self.interConnectInstr):
                        ref_p = nref
                i -= 1
        if len(argv) > 0:
            self.log(4, "[-] Adding args for call at 0x%x->%s" % (ref,repr(argv)))
            self.params[ref] = argv

    def formattedFuncPrint(self, argv):

        out = self.func.name + "_" + hex(self.func.ea) + "("
        for arg in argv:
            try:
                out += hex(arg) + ', '
            except:
                out += repr(arg) + ', '
        out = out[:-2]
        out += ")"
        return out


class decViewer(idaapi.simplecustviewer_t):
    decfn  = None

    def __init__(self, fn, reRun = False):
        try:
            if (fn == None):
                raise "No valid decrypt function passed!"
            self.decfn = fn
            if self.Create(reRun):
                self.Show()
            else:
                kprint("unable to create decryptor view!")
                raise
        except Exception,e:
            kprint("Exception!! @decViewer, ", e)

    def Create(self, reRun):
        global pluginVname
        global decResults

        self.menu_write2IDB = None
        self.menu_refresh   = None
        self.menu_write2IDC = None

        if not idaapi.simplecustviewer_t.Create (self, pluginVname + " - " + self.decfn.getName() + " result"):
            kprint("Unable to create view \"%s\"" % pluginVname + " - " + self.decfn.getName() + " result")
            return False

        if reRun or (self.decfn.getEA() not in decResults):
            try:
                if (self.decfn != None):
                    dec = Decryptor(self.decfn)
                    dec.run()
                    dec.cleanup()

            except Exception,e:
                kprint("Exception!! @decViewer:Create in calling decryptor, ", e)

        self.refresh()
        return True

    def refresh(self):
        global decResults
        global gsplitChar
        try:
            self.ClearLines()
            if self.decfn.getEA() in decResults:
                (ea,x) = decResults[self.decfn.getEA()]
                for (k,v) in x.iteritems():
                    cline = idaapi.COLSTR("  0x%08X  %c " % (k, gsplitChar), idaapi.SCOLOR_NUMBER)
                    cline += idaapi.COLSTR("%s" % v, idaapi.SCOLOR_STRING)
                    self.AddLine(cline)
            self.Refresh()
        except Exception,e:
            kprint("Exception!! @decViewer:refresh, ", e)
        return True

    def write2IDB(self):
        global decResults
        try:
            if self.decfn.getEA() in decResults:
                (ea,x) = decResults[self.decfn.getEA()]
                for (k,v) in x.iteritems():
                    MakeRptCmt(k, v)
                kprint("Decrypted strings Written to IDB at respective references!")
        except Exception,e:
            kprint("Exception!! @decViewer:write2IDB, ", e)
        return True


    def OnPopup(self):
        try:
            self.ClearPopupMenu()
            if not self.Count():
                self.AddPopupMenu("-")
                self.menu_refresh   = self.AddPopupMenu("(r) Refresh")
            else:
                self.AddPopupMenu("-")
                self.menu_refresh   = self.AddPopupMenu("(r) Refresh")
                self.menu_write2IDB = self.AddPopupMenu("(w) write these to IDB")

        except Exception,e:
            kprint("Exception!! @decViewer:OnPopup, ", e)
        return True

    def OnPopupMenu(self, menu_id):
        if menu_id == self.menu_refresh:
            self.refresh()

        elif menu_id == self.menu_write2IDB:
            self.write2IDB()

        else:
            return False

        return True

    def GotoLn(self, n):
        global gsplitChar
        if n != 0:
            (ln,b,c) = self.GetLine(n)
            if ln == None:
                return True
            ln = tag_remove(ln)
            l = ln.split(gsplitChar)
            nu = int(l[0],16)
            Jump(nu)
            return True

    def OnDblClick(self, shift):
        n = self.GetLineNo()
        self.GotoLn(n)

    def OnKeydown(self, vkey, shift):
        # escape
        if vkey == 27:
            pass
        # enter
        elif vkey == 13:
            n = self.GetLineNo()
            self.GotoLn(n)

        elif vkey == ord("R"):
            self.refresh()

        elif vkey == ord("W"):
            self.write2IDB()

        else:
            return False

        return True



class ProtoAnalyser(HelperRoutines):
    """
    This class Analyses Function Prototypes and Argument value pairs that can be later used in Appcall
    It analyses Top referenced functions, topNum, default is 10

    """

    allXrefFuncs       = []
    topNum             = 0
    functionlist       = []
    MAX_REFS_TO_CHECK  = 10
    TRACK_BACK_LIMIT   = 10
    MAX_ARGS           = 10
    protoArray         = {}
    argPassMechInsSize = 1
    GCD_THRESOLD       = 70
    no_of_funcs        = 0


    def __init__(self, topN=25):
        self.openLogFile("ProtoAnalyser")
        self.no_of_funcs   = idaapi.get_func_qty()
        if topN > self.no_of_funcs:
            self.topNum = self.no_of_funcs
        else:
            self.topNum = topN
        self.allXrefFuncs  = self.getTopXrefFunctions()

    def cleanup(self):
        self.functionlist  = []
        self.protoArray    = {}
        pass


    def getTopXrefFunctions(self):

        funcs = []
        for fnum in xrange(self.no_of_funcs):
            f_ea = idaapi.getn_func(fnum).startEA
            if f_ea != BADADDR:
                funcs.append((GetFunctionName(f_ea), f_ea, len(self.getXrefs(f_ea)),))
        funcs = sorted(funcs, key=lambda x:x[2], reverse=True)
        return funcs

    def analysePrototype(self, ea, force=False):

        try:
            if not isLoaded(ea):
                self.log(2, "Error! Bad address: 0x%x" % ea)
                raise

            try:
                fname = GetFunctionName(ea)
            except:
                fname = "0x%x" % ea

            xrefs = self.getXrefs(ea)
            if len(xrefs) == 0:
                self.log(3, "Discarding function: %s as there is no xref" % fname)
                return None

            tmpSorterDict  = {}
            mr = self.MAX_REFS_TO_CHECK
            if (len(xrefs) < self.MAX_REFS_TO_CHECK):
                mr = len(xrefs)

            xrefs = xrefs[0:mr]
            self.log(3, "\n%s\nAnalysing prototype for function 0x%x" % ('-'*50,ea))

            if (not force) and (GetFunctionFlags(ea) & (FUNC_HIDDEN|FUNC_LIB)):
                self.log(3, "Discarding function: %s as it is a LIB/HIDDEN function" % fname)
                return None

            idbProto = print_type(ea, False)
            gt = GuessType(ea)
            if (((idbProto != None) and (idbProto.find("__usercall") != -1)) or ((gt != None) and (gt.find("__usercall") != -1))):
                self.argPassMech = "mov"
                self.argPassType = "__usercall"
            else:
                self.argPassType = "__cdecl"
                self.argPassMech = "push"

            for ref in xrefs:
                argvArray      = []
                i = self.TRACK_BACK_LIMIT
                ref_p = ref

                proto = "int %s %s(" % (self.argPassType, fname)

                while (i):
                    g = 0
                    ref_p = idc.PrevHead(ref_p, 0)
                    if ((ref_p == BADADDR) or idc.GetDisasm(ref_p).startswith("call")):
                        g = 2
                        break
                    disstr = idc.GetDisasm(ref_p)
                    if disstr.startswith(self.argPassMech):
                        itmSize = idc.ItemSize(ref_p)
                        if (itmSize == 5):
                            dataVal = self.getDataValue(ref_p + self.argPassMechInsSize, itmSize - self.argPassMechInsSize)
                            if isLoaded(dataVal) and (not isCode(GetFlags(dataVal))):
                                proto += "void*,"
                            else:
                                proto += "int,"
                        elif (itmSize == 2):
                            proto += "int,"
                        else:
                            proto += "int*,"
                            g = 1

                        if g == 0:
                            argvArray.append((itmSize, (itmSize - self.argPassMechInsSize),))
                        else: # push reg
                            argvArray.append(("BUFFER", 0x400,)) # flag to use buffer

                    if len(self.getXrefs(ref_p)) == 1:
                        nref = self.getXrefs(ref_p)[0]
                        refins = idc.GetDisasm(nref)
                        if refins.startswith(self.interConnectInstr):
                            ref_p = nref
                    i -= 1

                if (len(argvArray) == 0):
                    proto += "void,"

                if ((i == 0) or (g == 2)):
                    proto = proto[:-1] + ");"

                self.protoArray[proto] = argvArray
                self.log(4, "Proto, args pattern found at 0x%x is: %s->%s" % (ref, proto, repr(argvArray)))

                try:
                    tmpSorterDict[proto] = tmpSorterDict[proto] + 1
                except:
                    tmpSorterDict[proto] = 1

            itms = sorted(tmpSorterDict.iteritems(), key=lambda (k,v):(v,k), reverse=True)
            (prt, n,) = itms[0]

            if (len(prt.split(",")) > self.MAX_ARGS):
                self.log(2, "too many arguments in proto from protoanalyser, ignoring function")
                return None

            if (idbProto != None):
                self.log(3, "proto from IDB: %s" % idbProto)
                nIdbPrtArgs = len(idbProto.split(","))
                if (nIdbPrtArgs > self.MAX_ARGS):
                    self.log(3, "too many arguments in proto from IDB, ignoring it and using parser found proto")
                    idbProto = prt
                self.log(2, "proto found by parser: %s, occurance: %d" % (prt, n))
                if (len(argvArray) > nIdbPrtArgs):
                    argvArray = argvArray[:nIdbPrtArgs]
                    self.protoArray[prt] = argvArray
                fn = Function(ea, idbProto, self.protoArray[prt], self.argPassMech)
            else:
                fn = Function(ea, prt, self.protoArray[prt], self.argPassMech)

            self.log(2, repr(fn))
            n = (n*100)/mr

            if (not force) and (self.argPassType != "__usercall") and (n < self.GCD_THRESOLD):
                self.log(3, "[GCD of protos from refs is %d%%, discarding above function]" % n)
                return None
            self.log(3, "[GCD of protos from refs is %d%%, considering above function as a candidate, ArgPassMech:%s]" % (n,self.argPassMech))
            return fn

        except Exception,e:
            kprint("Exception!! @analysePrototype, ", e)


    def IsFunctionPresent(self, f):
        for x in self.functionlist:
            if x.getEA() == f.getEA():
                return True
        return False

    def updateProtoFunctionNames(self):
        for x in self.functionlist:
            x.updateName()

    def addfunc(self, f):
        if (f != None):
            if not self.IsFunctionPresent(f):
                self.functionlist.append(f)
                kprint("function \"%s\" added in ProtoAnalyser" % f.getName())
            else:
                kprint("function \"%s\" Already present!" % f.getName())

    def run(self):
        self.cleanup()
        self.log(1, "START prototype analysis...\n" + "-"*50)
        i = self.topNum
        for x in self.allXrefFuncs:
            f = self.analysePrototype(x[1])
            if f != None:
                self.addfunc(f)
                i -= 1
            if (i == 0):
                break
        self.log(1, "END prototype analysis...\n" + "-"*50)
        self.flushlog()


def protoStr2FunctionInstance(s):
    global gsplitChar
    if (s != None) and (type(s).__name__ == "str"):
        np = s.split(gsplitChar)
        ea = int(np[0].strip(),16)
        if isLoaded(ea):
            try:
                return Function(ea, np[1].strip(), np[2].strip(), np[3].strip())
            except:
                kprint("unable to edit item!")
    return None

class Protoviewer (idaapi.simplecustviewer_t):
    global gsplitChar
    splitChar = gsplitChar

    def __init__(self):
        global pa
        if pa == None:
            pa = ProtoAnalyser()
            pa.run()
        self.Create()
        self.Show()

    def Create (self):
        global pa
        global pluginVname

        self.menu_jumpto        = None
        self.menu_deleteitem    = None
        self.menu_edititem      = None
        self.menu_additem       = None
        self.menu_refresh       = None
        self.menu_runAsDecryptor= None
        self.menu_quitPlugin    = None
        self.menu_setInitializerExecution = None

        if not idaapi.simplecustviewer_t.Create (self, pluginVname + " - Top Xref'ed functions"):
            return False
        if pa:
            self.refresh()
        else:
            self.ClearLines()
        return True

    def Cleanup(self):
        global pa
        self.Close()
        if pa != None:
            pa.cleanup()
            pa = None
        return True

    def refresh(self):
        self.ClearLines()
        self.checkItemUpdates()
        header = " Virtual Addr " + self.splitChar + " " * 30 + "  Function Prototype " +  " " * 30 + self.splitChar
        header += " " * 11 + "   Argument Array  " + " " * 11 + self.splitChar + " Type " + self.splitChar + " xrefs"
        self.AddLine(header)
        for line in self.get_colored_lines():
            self.AddLine(line)
        self.Refresh()

    def get_colored_line(self, n):
        ea, nrefs, proto, argSizeArr, argPassMech = self.get_item(n).getData()
        cline = idaapi.COLSTR("  0x%08X  %c " % (ea,self.splitChar), idaapi.SCOLOR_NUMBER)
        proto_v = idaapi.COLSTR("%-80s" % proto, idaapi.SCOLOR_CODNAME)

        argArr_v = idaapi.COLSTR("%c %-40s" % (self.splitChar, repr(argSizeArr)), idaapi.SCOLOR_STRING)
        mech_v = idaapi.COLSTR("%c %-5s" % (self.splitChar,argPassMech), idaapi.SCOLOR_KEYWORD)

        nrefs_v = idaapi.COLSTR("%c (%03d)" % (self.splitChar,nrefs), idaapi.SCOLOR_AUTOCMT)
        cline += proto_v + argArr_v + mech_v + nrefs_v
        return cline

    def get_colored_lines(self):
        lines = []
        for i in xrange (self.get_number_of_items()):
            l = self.get_colored_line(i)
            lines.append(l)
        return lines

    def get_number_of_items(self):
        global pa
        return len(pa.functionlist)

    def checkItemUpdates(self):
        global pa
        pa.updateProtoFunctionNames()

    def get_item(self, n):
        global pa
        return pa.functionlist[n]

    def insert_item(self, n, v):
        global pa
        pa.functionlist.insert(n, v)

    def append_item(self, v):
        global pa
        pa.functionlist.append(v)

    def remove_item(self, n):
        global pa
        pa.functionlist.pop(n)

    def get_item_EA(self, n):
        f = self.get_item(n)
        return f.getEA()

    def get_item_name(self, n):
        f = self.get_item(n)
        return f.getName()

    def update_item(self, n, v):
        global pa
        pa.functionlist[n] = v

    def add_item(self):
        kprint("[TIP] Did you know, you can place your cursor over desired function in dissasembly view and invoke Ctrl+Y to add")
        newProto = AskStr ("0x000000 | int __cdecl func(int, int, int); | [(5,4),(\"const\",0xb0),(\"buffer\",0x100),] | \"push\" | 0", "Enter new prototype")
        if newProto != None:
            try:
                f = protoStr2FunctionInstance(newProto)
                if (f != None):
                    self.append_item(f)
            except:
                kprint("unable to add item!")
        self.refresh()
        return True

    def edit_item(self):
        n = self.GetLineNo()
        if n != 0:
            (ln,b,c) = self.GetLine(n)
            if ln == None:
                return True
            ln = tag_remove(ln)
            l = ln.split(self.splitChar)
            nln = ""
            for x in l:
                nln += x.strip() + " |"
            nln = nln[:-1]
            nps = AskStr (nln, "Please fix prototype")
            if nps != None:
                f = protoStr2FunctionInstance(nps)
                if (f != None):
                    try:
                        self.update_item(n-1, f)
                    except:
                        kprint("unable to edit item!")
            self.refresh()
        return True

    def delete_item(self, ask = True):
        if self.get_number_of_items():
            result = 1
            if ask:
                result = AskYN(1, "Delete item?")
            if result == 1:
                ln = self.GetLineNo()
                if ln != 0:
                    self.remove_item(ln-1)
                    self.refresh()
        return True


    def OnDblClick(self, shift):
        n = self.GetLineNo()
        if n != 0:
            Jump(self.get_item_EA(n-1))
        return True


    def OnHint(self, n):
        global pa
        ret = None
        if n != 0:
            ea = self.get_item_EA(n-1)
            xrefs = pa.getXrefs(ea)
            if len(xrefs) > 0:
                nx = xrefs[0]
                hintStr = ""
                for i in xrange(0, 10):
                    hintStr = idaapi.COLSTR(idc.GetDisasm(nx), idaapi.SCOLOR_MACRO) + "\n" + hintStr
                    nx = idc.PrevHead(nx, 0)
                    if (nx == BADADDR):
                        break
                t = "Code @ First xref 0x%x\n" % xrefs[0]
                hintStr = idaapi.COLSTR(t, idaapi.SCOLOR_NUMBER) + hintStr
                ret = (12, hintStr)
        return ret


    def setInitializerExecution(self):
        n = self.GetLineNo()
        if n!= 0:
            f = self.get_item(n-1)
        else:
            return False

        initCodeSt = idc.BeginEA()
        initCodeSt = idc.AskAddr(initCodeSt, "Enter the start address of the Code Initializer that you want to run before Decryption Routine")
        if initCodeSt == BADADDR:
            kprint ("[!] Cancelled")
            return False

        endCodeSt = initCodeSt + idc.ItemSize(initCodeSt)
        endCodeSt = idc.AskAddr(endCodeSt, "Enter the end address of the Code Initializer that you want to run before Decryption Routine")
        if endCodeSt == BADADDR:
            kprint ("[!] Cancelled")
            return False

        return f.setInitializerExecution(initCodeSt, endCodeSt)


    def runDecryptor(self):
        global decViews
        global decResults
        reRun = False
        n = self.GetLineNo()
        if n!= 0:
            f = self.get_item(n-1)
        else:
            return False
        rf = f.getEA()
        if rf not in decResults:
            ans = AskYN(1, "Run function \"%s\" as decryptor?" % self.get_item_name(n-1))
            if ans != 1:
                return False
        else:
            ans = AskYN(0, "Re-Run function \"%s\" as decryptor?" % self.get_item_name(n-1))
            if ans != 1:
                reRun = False
            else:
                reRun = True
        try:
            v = decViews[rf]
            v.Close()
        except:
            #kprint("view not found for 0x%x creting new one" % rf)
            pass

        decViews[rf] = decViewer(f, reRun)
        return True


    def show_xrefs(self):
        n = self.GetLineNo()
        if n != 0:
            ea = self.get_item_EA(n-1)
            open_xrefs_window(ea)

    def quit_plugin(self):
        global kryptonInst
        kprint("Quiting Plugin, restart with Ctrl+F8");
        self.Cleanup()
        kryptonInst.term()

    def OnKeydown(self, vkey, shift):
        # escape
        if vkey == 27:
            pass
        # enter
        elif vkey == 13:
            n = self.GetLineNo()
            if n != 0:
                Jump (self.get_item_EA(n-1))

        elif vkey == ord('D'):
            self.delete_item()

        elif vkey == ord("E"):
            self.edit_item()

        elif vkey == ord("A"):
            self.add_item()

        elif vkey == ord("R"):
            self.refresh()

        elif vkey == ord("K"):
            self.runDecryptor()

        elif vkey == ord("X"):
            self.show_xrefs()

        elif vkey == ord("I"):
            self.setInitializerExecution()

        elif vkey == ord("Q"):
            self.quit_plugin()

        else:
            return False

        return True


    def OnPopup(self):
        self.ClearPopupMenu()
        if not self.Count():
            self.AddPopupMenu("-")
            self.menu_refresh = self.AddPopupMenu("(r) Refresh")
        else:
            self.AddPopupMenu("-")
            self.menu_refresh = self.AddPopupMenu("(r) Refresh")
            self.menu_refresh = self.AddPopupMenu("(x) Xrefs")
            self.AddPopupMenu("-")
            self.menu_jumpto = self.AddPopupMenu("(Enter) Jump to item address")
            self.menu_edititem = self.AddPopupMenu("(e) Edit Prototype")
            self.AddPopupMenu("-")
            self.menu_additem = self.AddPopupMenu("(a) Add Function Manually")
            self.menu_deleteitem = self.AddPopupMenu("(d) Delete item")
            self.AddPopupMenu("-")
            self.menu_runAsDecryptor  = self.AddPopupMenu("(k) Run As Decryptor")
            self.AddPopupMenu("-")
            self.menu_setInitializerExecution  = self.AddPopupMenu("(i) Set Initializer Execution")
            self.AddPopupMenu("-")
            self.menu_quitPlugin = self.AddPopupMenu("(q) Quit Plugin")

        return True


    def OnPopupMenu(self, menu_id):
        if menu_id == self.menu_refresh:
            self.refresh()

        elif menu_id == self.menu_jumpto:
            n = self.GetLineNo()
            if n!= 0:
                Jump(self.get_item_EA(n-1))

        elif menu_id == self.menu_runAsDecryptor:
            self.runDecryptor()

        elif menu_id == self.menu_deleteitem:
            self.delete_item()

        elif menu_id == self.menu_additem:
            self.add_item()

        elif menu_id == self.menu_edititem:
            self.edit_item()

        elif menu_id == self.menu_setInitializerExecution:
             self.setInitializerExecution()

        elif menu_id == self.menu_quitPlugin:
            self.quit_plugin()

        else:
            return False

        return True

    def OnCursorPosChanged(self):
        self.refresh()
        return True


def addFunctionProtoCB():
    global pa
    global pvv
    try:
        ea = get_func(here()).startEA
        if isLoaded(ea):
            if pvv != None and pa != None:
                f = pa.analysePrototype(ea, True)
                if f != None:
                    pa.addfunc(f)
                    pvv.refresh()
            else:
                kprint("function not added as no instance of protoviewer found!")
        else:
            kprint ("0x%x bad Address!" % here())
    except Exception,e:
        kprint("Exception!! @addFunctionProtoCB, ", e)

def reCreateClosedProtoView():
    global pvv
    if pvv:
        pvv.Close()
    pvv = Protoviewer()


def getProcessorName():
    inf = idaapi.get_inf_structure()
    return inf.procName.strip('\x00')


# Globals
pvv         = None  # protoViewer
decViews    = {}    # decryptViewer instance array for each function decrypted
pa          = None  # protoAnalyser
decResults  = {}    # decryptor results array
kryptonInst = None
PLUGIN_MODE = True

class kplugin_t (idaapi.plugin_t):
    flags = 0
    comment = ""
    help = ""
    wanted_name = pluginVname
    wanted_hotkey = "Ctrl-F8"
    m1 = m3 = None

    def init(self):
        if getProcessorName().startswith('metapc'):
            return PLUGIN_KEEP
        return PLUGIN_SKIP

    def run(self, arg):
        global pvv
        Wait()
        if not pvv:
            pvv = Protoviewer()
            self.m1 = idaapi.add_menu_item("Edit/", pluginVname + " - Add func proto for Decryptor", "Ctrl+Y", 0, addFunctionProtoCB, None)
            if self.m1 == None:
                kprint ("add menu item failed!")
            self.m3 = idaapi.add_menu_item("View/Open subviews/", pluginVname, "", 0, reCreateClosedProtoView, None)
            if self.m3 == None:
                kprint ("add menu item failed!")
        else:
            if not pvv.IsFocused():
                pvv.Close()
                pvv = Protoviewer()

    def term(self):
        global pvv
        if pvv != None:
            pvv.Cleanup()
            pvv = None
        if self.m1 != None:
            idaapi.del_menu_item(self.m1)
        if self.m3 != None:
            idaapi.del_menu_item(self.m3)
        pass

if (PLUGIN_MODE):
    def PLUGIN_ENTRY ():
        global kryptonInst
        kryptonInst = kplugin_t()
        return kryptonInst
else:
    Wait()
    if not pvv:
        pvv = Protoviewer()
    else:
        if not pvv.IsFocused():
            pvv.Close()
            pvv = Protoviewer()


repository: sean2077/311SireCustomizedPackageDev
file: ida-scripts/311stru_tool.py
wanted_name: San11StruPlugin
comment: Import or export structs (@san11pk).
created_at: 2024-07-05 14:15:05+00:00
pushed_at: 2024-07-12 13:43:21+00:00
description:
Learn to make scp (Sire Customized Package) for game "‎Sangokushi XI Power-up Kit"(san11pk) by KOEI. Inspired by https://github.com/sjn4048/311MemoryResearch.

url:
https://github.com/sean2077/311SireCustomizedPackageDev/blob/43afe4efe273cf3b6ab7833b9b6f5822bd331fd4/ida-scripts/311stru_tool.py

content:
"""
san11pk's IDA Struct Tool
"""

import os
import re
import sys
from datetime import datetime

import prettytable
from attrs import define, field

SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))

STRUCTS_FILE = os.path.join(os.path.dirname(SCRIPT_DIR), "material", "结构体汇总.md")


FORCE_UPDATE_STRUCT_ARRAYS = False  # 强制更新结构体数组(包含链表节点)
DO_NOT_MAKE_ARRAY = True  # 不创建数组，而是创建多个结构体

LINKED_LIST_STRUCT_NAME = {  # key: struct_name, value: node_name
    "struct_person_list": "struct_person_node",
    "struct_building_list": "struct_building_node",
}

#######################################################################################################
###                                            Utils                                                ###
#######################################################################################################


def get_now_time() -> str:
    """获取当前时间，形如 2020-06-06 14:00:00"""
    now = datetime.now()
    return now.strftime("%Y-%m-%d %H:%M:%S")


def format_address(addr: int) -> str:
    """格式化地址"""
    return f"{addr:08x}"


def int16(x: str) -> int:
    return int(x, 16)


def get_pure_data_type(data_type: str) -> str:
    """获取去掉 [], *, () 的 data_type"""
    return data_type.split("[")[0].split("*")[0].split("(")[0].strip()


def is_struct_type(data_type: str) -> bool:
    """检查数据类型是否是结构体类型"""
    return data_type.startswith("struct_") or data_type.startswith("struc_")


#######################################################################################################
###                                     结构体文件读写相关                                             ###
#######################################################################################################

_STRUCT_TABLE_HEADER = ["offset", "nbytes", "data_type", "field_name", "field_comment"]


def _set_hook(instance, attrib, new_value):
    c = attrib.converter
    if c:
        new_value = c(new_value)
    if getattr(instance, attrib.name) != new_value:
        instance._mark_modified()
    return new_value


@define
class StructField:
    """结构体字段"""

    offset: int = field(on_setattr=_set_hook)
    size: int = field(on_setattr=_set_hook)  # 字段大小
    data_type: str = field(on_setattr=_set_hook)  # 数据类型
    name: str = field(on_setattr=_set_hook)  # 字段名(带前缀)
    comment: str = field(on_setattr=_set_hook)  # 字段注释

    _is_array: bool = field(default=False, init=False, repr=False)  # 是否是数组
    _is_ptr: bool = field(default=False, init=False, repr=False)  # 是否是指针
    _pure_data_type: str = field(default="", init=False, repr=False)  # 去掉 [], *, () 的 data_type
    _pure_name: str = field(default="", init=False, repr=False)  # 去掉前缀(fld_xx_ 或 field_XX)的字段名

    _modified: bool = field(default=False, init=False, repr=False)  # 是否被修改

    def _mark_modified(self):
        object.__setattr__(self, "_modified", True)

    def __attrs_post_init__(self):
        self._is_array = "[" in self.data_type
        self._is_ptr = self.data_type in ("pointer", "address", "pointer32") or "*" in self.data_type
        self._pure_data_type = get_pure_data_type(self.data_type)
        _pure_name = self.name
        if self.name.startswith(f"fld_{self.offset:x}_"):
            _pure_name = self.name.removeprefix(f"fld_{self.offset:x}_")
        elif self.name.startswith(f"field_{self.offset:X}"):  # IDA 自动生成的结构体字段名
            _pure_name = self.name.removeprefix(f"field_{self.offset:X}")
        self._pure_name = _pure_name

        self._modified = False

    @classmethod
    def from_table_row(cls, row: list[str]):
        # | offset | nbytes | data_type | field_name | field_comment |
        offset, size, data_type, field_name, field_comment = row
        offset = int(offset, 16)
        size = int(size)
        data_type = data_type.strip()
        field_name = field_name.strip()
        if not field_name.startswith("fld_") and not field_name.startswith("field_"):
            field_name = f"fld_{offset:x}_{field_name}"
        field_comment = field_comment.strip()

        return cls(offset, size, data_type, field_name, field_comment)

    def to_table_row(self) -> list[str]:
        return [f"+{self.offset:X}", str(self.size), self.data_type, self._pure_name, self.comment]


def _cvt_int16_array(s: str):
    return list(map(int16, s.split(","))) if s else []


def _cvt_int_array(s: str):
    return list(map(int, s.split(","))) if s else []


@define
class Struct:
    """Markdown 文件中存储结构体表格及相关信息"""

    title: str = ""

    # 元信息
    name: str = field(default="", init=False)
    name_zh: str = field(default="", init=False)
    id: int = field(default=0xFFFFFFFF, init=False)
    size: int = field(default=0, init=False)
    comment: str = field(default="", init=False)
    array_start_addrs: list[int] = field(factory=list, init=False)
    array_end_addrs: list[int] = field(factory=list, init=False)
    array_sizes: list[int] = field(factory=list, init=False)
    array_updated: bool = field(default=False, init=False)
    last_update: str = field(default="", init=False)

    # 解析后的字段
    fields: list[StructField] = field(factory=list, init=False)  # 表格中的字段

    _content: list[str] = field(factory=list, init=False, repr=False)  # 原文件中表格以下至下一个表格标题之间的内容，用于写回文件

    _modified: bool = field(default=False, init=False, repr=False)  # 是否被修改过

    # 各元信息解析函数表
    _META_PARSE_FUNCS = {
        "struct_name": ("name", str.strip),  # 元信息名: (属性名, 处理函数)
        "struct_name_zh": ("name_zh", str.strip),
        "struct_id": ("id", int16),
        "struct_size": ("size", int16),
        "array_start_addrs": ("array_start_addrs", _cvt_int16_array),
        "array_end_addrs": ("array_end_addrs", _cvt_int16_array),
        "array_sizes": ("array_sizes", _cvt_int_array),
        "array_updated": ("array_updated", lambda x: x.lower() == "true"),
        "last_update": ("last_update", str.strip),
    }

    def parse_meta_line(self, line: str):
        if not line.startswith("- "):
            return
        line = line[2:].strip()
        for key, (attr, func) in self._META_PARSE_FUNCS.items():
            if line.startswith(key + ":"):
                try:
                    value = func(line.removeprefix(key + ":").strip())
                    setattr(self, attr, value)
                except ValueError:
                    print(f"Failed to parse {key} value: {line}, skip.")
                break

    def meta_lines(self) -> list[str]:
        lines = []
        lines.append(f"- struct_name_zh: {self.name_zh}")
        lines.append(f"- struct_name: {self.name}")
        lines.append(f"- struct_id: {self.id:08x}")
        lines.append(f"- struct_size: {self.size:#x}")
        lines.append(f"- array_start_addrs: {','.join(map(format_address, self.array_start_addrs))}")
        lines.append(f"- array_end_addrs: {','.join(map(format_address, self.array_end_addrs))}")
        lines.append(f"- array_sizes: {','.join(map(str, self.array_sizes))}")
        lines.append(f"- array_updated: {self.array_updated}")
        lines.append(f"- last_update: {self.last_update}")

        return [line.strip() + "\n" for line in lines]

    def table_string(self) -> str:
        tb = prettytable.PrettyTable()
        tb.set_style(prettytable.MARKDOWN)
        tb.align = "l"
        tb.field_names = _STRUCT_TABLE_HEADER
        for field in self.fields:
            tb.add_row(field.to_table_row())
        return tb.get_string().replace("-|", " |")  # 与 vscode markdown 插件格式化结果一致

    def is_modified(self):
        """是否被修改过"""
        return self._modified or any(f._modified for f in self.fields)


_STRUCT_INDEX_PAT = re.compile(r"\[([0-9]+)\]")


class StructMDFileParser:
    """结构体汇总.md 文件解析器"""

    _STATE_FINDING_TITLE = 0  # 正在寻找下一个表格标题
    _STATE_BEFORE_TABLE = 1  # 处理表格标题到表格上方之间的内容行
    _STATE_ON_TABLE = 2  # 处理表格内容行

    def __init__(self):
        self._structs: list[Struct] = []
        self._before_content: list[str] = []  # 第一个表格之前的内容
        self._state = self._STATE_FINDING_TITLE
        self._current_table: Struct = None
        self._specific_table_indexes: list[int] = None  # 指定要解析的表格的索引（从 1 开始）

    def _reset(self):
        self._structs.clear()
        self._before_content.clear()
        self._state = self._STATE_FINDING_TITLE
        self._current_table = None
        self._specific_table_indexes = None

    def parse(self, mk_file: str, specific_table_indexes: list[int] = None) -> list[Struct]:
        self._reset()
        self._specific_table_indexes = specific_table_indexes

        with open(mk_file, "r", encoding="utf-8") as file:
            for line in file:
                self._parse_line(line)

        return self._structs

    def _parse_line(self, line: str):
        if self._state == self._STATE_FINDING_TITLE:
            return self._state_finding_title(line)

        if self._state == self._STATE_BEFORE_TABLE:
            return self._state_before_table(line)

        if self._state == self._STATE_ON_TABLE:
            return self._state_on_table(line)

    def _create_new_struct(self, line: str):
        # 判断是否需要过滤
        table_index = 0
        if self._specific_table_indexes:
            # 表格标题形如 "## [1]xxx"，提取出表格索引
            m = _STRUCT_INDEX_PAT.search(line)
            if m:
                table_index = int(m.group(1))
        if not self._specific_table_indexes or table_index in self._specific_table_indexes:
            # 找到了一个表格标题
            title = line[3:].strip()
            self._state = self._STATE_BEFORE_TABLE
            self._current_table = Struct(title)
            self._structs.append(self._current_table)
            print(f"Found table: {title}")
            return True
        return False  # 未创建表格

    def _state_finding_title(self, line: str):
        if line.startswith("## "):
            if self._create_new_struct(line):
                return

        if not self._current_table:
            self._before_content.append(line)
        else:
            self._current_table._content.append(line)

    def _state_before_table(self, line: str):
        if self._reach_table_row(line):
            self._state = self._STATE_ON_TABLE
            return

        if line.startswith("## "):  ## 该表为空表，直接下一个表格
            if self._create_new_struct(line):
                return

        # 解析结构体元信息
        self._current_table.parse_meta_line(line)

    def _state_on_table(self, line: str):
        if not line.strip():  # 空行
            self._state = self._STATE_FINDING_TITLE
            self._current_table._content.append(line)
            return

        # 解析结构体字段
        row = list(map(str.strip, line.strip().strip("|").split("|")))
        if len(row) != len(_STRUCT_TABLE_HEADER):
            # 无效的表格行，这种情况不应该出现
            raise ValueError(f"Invalid table row: {line}")
        self._current_table.fields.append(StructField.from_table_row(row))

    def add_struct(self, struct: Struct):
        self._structs.append(struct)

    def write_file(self, file_path: str):
        """写入文件"""
        now = get_now_time()
        with open(file_path, "w", encoding="utf-8") as f:
            f.writelines(self._before_content)
            for table in self._structs:
                # 写入标题
                f.write(f"## {table.title}\n\n")
                # 写入元信息
                if table.is_modified():
                    table.last_update = now
                f.writelines(table.meta_lines())
                f.write("\n")
                # 写入表格
                f.write(table.table_string())
                f.write("\n")
                # 写入表格以下内容
                if not table._content:
                    f.write("\n\n")
                else:
                    f.writelines(table._content)

    @staticmethod
    def _reach_table_row(line: str) -> bool:
        """判断是否到达表格内容开始行"""
        return any(line.startswith(x) for x in ("| ---", "|--", "| :--", "|:--"))


def update_structs_file():
    """更新结构体文件，如结构体序号, 新建未定义结构体等"""
    parser = StructMDFileParser()
    structs = parser.parse(STRUCTS_FILE)

    # 重新编号
    for i, struct in enumerate(structs):
        # 去掉原有的序号
        items = struct.title.split("]")
        if len(items) > 1:
            struct.title = items[1].strip()
        struct.title = f"[{i+1}]{struct.title}"

    # 如果结构体 fields 为空或fields[-1]的 offset+field_size < size，添加一个 int 字段
    for struct in structs:
        if struct.size == 0:
            continue
        if not struct.fields:
            struct.fields.append(StructField(struct.size - 4, 4, "int", "", ""))
            continue
        last_field = struct.fields[-1]
        if last_field.offset + last_field.size < struct.size:
            struct.fields.append(StructField(struct.size - 4, 4, "int", "", ""))

    # 如果结构体offset=0处没有字段，添加一个 int 字段
    for struct in structs:
        for field in struct.fields:
            if field.offset == 0:
                break
        else:
            struct.fields.insert(0, StructField(0, 4, "int", "", ""))

    parser.write_file(STRUCTS_FILE)


if __name__ == "__main__":
    # 解析参数 -u
    if len(sys.argv) > 1 and sys.argv[1] == "-u":
        update_structs_file()
        sys.exit(0)

#######################################################################################################
###                                         IDA 操作相关                                             ###
#######################################################################################################


import idaapi
import idautils
import idc


def _get_data_flags(fld: StructField):
    if fld._is_ptr:
        return idaapi.FF_DWORD | idaapi.FF_1OFF | idaapi.FF_DATA
        # FF_1OFF 表示 "First Offset"（第一个偏移量）。这个标志通常用于表示一个数据成员应该被解释为一个偏移量或指针。

    dt_str = fld._pure_data_type

    if dt_str in ("byte", "char", "uchar"):
        return idaapi.byte_flag()

    if dt_str in ("word", "short", "ushort"):
        return idaapi.word_flag()

    if dt_str in ("dword", "int", "uint"):
        return idaapi.dword_flag()

    if dt_str in ("float",):
        return idaapi.float_flag()

    if dt_str in ("string",):
        return idaapi.strlit_flag()

    if is_struct_type(dt_str):
        return idaapi.stru_flag()

    return 0  # 其他类型用不到 flag


def _add_struc_member(sptr, field: StructField):
    """添加结构体或结构体数组成员"""
    member_struct_name = field._pure_data_type
    opinfo = idaapi.opinfo_t()
    opinfo.tid = idaapi.get_struc_id(member_struct_name)
    if opinfo.tid == idaapi.BADADDR:
        idaapi.warning(f"Struct '{member_struct_name}' not found")
        return False
    idaapi.add_struc_member(sptr, field.name, field.offset, idaapi.stru_flag(), opinfo, field.size)


def _add_string_member(sptr, field: StructField):
    """添加字符串成员"""
    opinfo = idaapi.opinfo_t()
    opinfo.strtype = idaapi.STRTYPE_C
    idaapi.add_struc_member(sptr, field.name, field.offset, idaapi.strlit_flag(), opinfo, field.size)


def _get_tinfo_from_base_type(base_type: str) -> idaapi.tinfo_t | None:
    """根据基础类型返回 tinfo_t 对象"""
    if base_type == "void":
        return idaapi.tinfo_t(idaapi.BT_VOID)
    if base_type in ("byte", "char", "int8"):
        return idaapi.tinfo_t(idaapi.BT_INT8)
    if base_type in ("uchar", "uint8", "unsigned char"):
        return idaapi.tinfo_t(idaapi.BT_INT8 | idaapi.BTMT_UNSIGNED)
    if base_type in ("word", "short", "int16"):
        return idaapi.tinfo_t(idaapi.BT_INT16)
    if base_type in ("ushort", "uint16", "unsigned short"):
        return idaapi.tinfo_t(idaapi.BT_INT16 | idaapi.BTMT_UNSIGNED)
    if base_type in ("dword", "int"):
        return idaapi.tinfo_t(idaapi.BT_INT32)
    if base_type in ("uint", "unsigned int"):
        return idaapi.tinfo_t(idaapi.BT_INT32 | idaapi.BTMT_UNSIGNED)
    if base_type == "float":
        return idaapi.tinfo_t(idaapi.BT_FLOAT)
    if base_type == "bool":
        return idaapi.tinfo_t(idaapi.BT_BOOL)

    return None


def _get_tinfo_from_stru_name(stru_name):
    tinfo = idaapi.tinfo_t()
    if tinfo.get_named_type(idaapi.get_idati(), stru_name):
        return tinfo
    return None


def _get_tinfo_from_data_type(data_type: str) -> idaapi.tinfo_t | None:
    """根据数据类型返回tinfo_t, 考虑基础类型和结构体类型与指针和数组嵌套定义的情况"""
    pure_data_type = get_pure_data_type(data_type)

    # 快速排查一些特例
    if pure_data_type in ("pointer", "address", "pointer32"):
        # 返回 void* 类型
        t = idaapi.tinfo_t(idaapi.BT_VOID)
        t.create_ptr(t)
        return t

    if pure_data_type == "string":
        # 返回 char[] 类型
        t = idaapi.tinfo_t(idaapi.BTF_CHAR)
        t.create_array(t)
        return t

    # 先处理基础类型和结构体类型
    t = _get_tinfo_from_base_type(pure_data_type)
    if t is None:
        t = _get_tinfo_from_stru_name(pure_data_type)
    if t is None:
        return None

    # 处理指针数组嵌套情况
    remaining = data_type[len(pure_data_type) :].strip().replace(" ", "")
    l = 0
    r = len(remaining) - 1
    cur = l
    while l <= r:
        if remaining[cur] == "*":
            t.create_ptr(t)
            l += 1
            cur = l
            # 处理一种特殊情况, 最里层的 *[, 实际应为 *(variable_name)[，所以也要跳到右边
            if l <= r and remaining[l] == "[":
                cur = r
            continue
        if remaining[cur] == "(":  # 此时应跳转到右边，从右边开始解析
            l += 1
            cur = r
            continue
        if remaining[cur] == ")":  # 此时应跳转到左边，从左边开始解析
            r -= 1
            cur = l
            continue
        if remaining[cur] == "]":  # 往左找到对应的 "["，然后解析数组大小
            r = cur - 1
            for cur in range(r, l - 1, -1):
                if remaining[cur] == "[":
                    break
            else:
                idaapi.warning(f"Invalid data type: {data_type}")
                return None
            try:
                array_size = int(remaining[cur + 1 : r + 1]) if r > cur else 0
            except ValueError:
                idaapi.warning(f"Invalid array size: {data_type} for {remaining[cur + 1 : r + 1]} is not a int.")
                return None
            t.create_array(t, array_size)
            r = cur - 1
            cur = r
            continue
        if remaining[cur] == "[":
            l = cur + 1
            for cur in range(l, r + 1):
                if remaining[cur] == "]":
                    break
            else:
                idaapi.warning(f"Invalid data type: {data_type}")
                return None
            try:
                array_size = int(remaining[l:cur]) if cur > l else 0
            except ValueError:
                idaapi.warning(f"Invalid array size: {data_type} for {remaining[l:cur]} is not a int.")
                return None
            t.create_array(t, array_size)
            l = cur + 1
            cur = l
            continue

    return t


def _find_struct_array_size(start_addr, struct_size):
    # 首先找到 start_addr 处的双字地址，这是每个结构体的标识
    func_addr = idaapi.get_wide_dword(start_addr)

    cur_addr = start_addr + struct_size
    item_cnt = 1
    while True:
        cur_func_addr = idaapi.get_wide_dword(cur_addr)
        if cur_func_addr != func_addr:
            break
        item_cnt += 1
        cur_addr += struct_size

    return item_cnt, cur_addr


def _create_struct_array(struct: Struct):
    tinfo = _get_tinfo_from_stru_name(struct.name)
    for i, array_start_addr in enumerate(struct.array_start_addrs):
        # 先找出数组的结束地址和大小
        if len(struct.array_end_addrs) > i:
            array_end_addr = struct.array_end_addrs[i]
            array_size = (array_end_addr - array_start_addr) // struct.size
            if len(struct.array_sizes) > i:
                struct.array_sizes[i] = array_size
            else:
                struct.array_sizes.append(array_size)
        elif len(struct.array_sizes) > i:
            array_size = struct.array_sizes[i]
            array_end_addr = array_start_addr + array_size * struct.size
            if len(struct.array_end_addrs) > i:
                struct.array_end_addrs[i] = array_end_addr
            else:
                struct.array_end_addrs.append(array_end_addr)
        else:  # 仅提供了起始地址，需自行查找 end_addr 和 array_size
            # 大部分结构体第一个字段是指向该类结构体函数的指针，可根据这个特征来查找数组的结束地址
            # 如果不是该特征，则无法自动查找结束地址，需要手动指定
            array_size, array_end_addr = _find_struct_array_size(array_start_addr, struct.size)
            struct.array_end_addrs.append(array_end_addr)
            struct.array_sizes.append(array_size)

            # 更新结构体相关函数所在地址名称
            func_addr_name = struct.fields[0].name.removeprefix("fld_0_")
            func_addr = idaapi.get_wide_dword(array_start_addr)
            idaapi.set_name(func_addr, func_addr_name)

        # 创建结构体数组
        idaapi.del_items(array_start_addr, idaapi.DELIT_SIMPLE, array_size * struct.size)
        make_array = not DO_NOT_MAKE_ARRAY and (array_size <= 100 or array_size * struct.size < 0x1000)
        if make_array:  # 小数组
            idaapi.create_struct(array_start_addr, struct.size, struct.id)
            if not idc.make_array(array_start_addr, array_size):
                idaapi.warning(f"Failed to create array at {array_start_addr:x}.\n")
                continue
            ap = idaapi.array_parameters_t()
            ap.flags = idaapi.AP_INDEX | idaapi.AP_IDXDEC | idaapi.AP_ARRAY
            idaapi.set_array_parameters(array_start_addr, ap)
        else:  # 大数组
            cnt = 0
            for addr in range(array_start_addr, array_end_addr, struct.size):
                idaapi.create_struct(addr, struct.size, struct.id)
                if cnt > 0:
                    idaapi.set_name(addr, f"{struct.name}_{addr:x}")
                    idaapi.set_tinfo(addr, tinfo)
                    idaapi.set_cmt(addr, f"{struct.name}_ARRAY[{cnt}]", 1)
                cnt += 1

        # 结构体数组名和注释
        array_name = f"{struct.name}_ARRAY"
        if i > 0:
            array_name += f"_{i}"
        idaapi.set_name(array_start_addr, array_name)
        array_comment = f"{array_name}[end={format_address(array_end_addr)},size={array_size},struct_size={struct.size:#x}]"
        idaapi.set_cmt(array_start_addr, array_comment, 1)

        idaapi.msg(
            f"Struct array {array_name} created, size: {array_size}, struct size: {struct.size}, start at {array_start_addr:x}, end at {array_end_addr:x}\n"
        )


def _traverse_linked_list(struct: Struct):
    node_name = LINKED_LIST_STRUCT_NAME.get(struct.name, None)
    if node_name is None:
        return
    node_sid = idaapi.get_struc_id(node_name)
    if node_sid == idaapi.BADADDR:
        return
    node_size = idaapi.get_struc_size(node_sid)
    node_tinfo = _get_tinfo_from_stru_name(node_name)
    for list_addr in struct.array_start_addrs:
        visited = set()
        # 一般链表offset=0x4处存放下一个节点的地址
        start_address = idaapi.get_wide_dword(list_addr + 4)
        current = start_address
        while current != 0 and current not in visited:
            idaapi.create_struct(current, node_size, node_sid)
            # 设置当前的 tinfo 为该 node 结构体
            idaapi.set_tinfo(current, node_tinfo)
            # 命名为 node_name+current_address
            idaapi.set_name(current, f"{node_name}_{current:x}")

            idaapi.msg(f"Current node: {current:x}\n")
            visited.add(current)

            # Get the next pointer
            next_ptr = idaapi.get_wide_dword(current)  # offset
            if next_ptr == 0 or next_ptr == idaapi.BADADDR:
                idaapi.msg("Reached end of list (null pointer)\n")
                break

            current = next_ptr

        if current in visited and current != start_address:
            idaapi.msg(f"Cycle detected at {current:x}\n")
        elif current == start_address:
            idaapi.msg("Traversal complete, returned to start node\n")

        idaapi.msg(f"Total {node_name} nodes: {len(visited)}\n")


def _import_struct(struct: Struct) -> bool:
    # 先判断结构体是否已经存在，如果存在则对齐进行更新，否则创建新的结构体
    is_update = False

    sid = struct.id
    if sid == idaapi.BADADDR:  # 若未指定 id，则根据名称查找
        sid = idaapi.get_struc_id(struct.name)
        struct.id = sid
    else:  # 若指定了 id，则根据 id 查找
        struct_name = idaapi.get_struc_name(sid)
        if struct_name != struct.name:
            idaapi.set_struc_name(sid, struct.name)
            idaapi.msg(f"Renamed struct {struct_name} to {struct.name}\n")
        sid = idaapi.get_struc_id(struct.name)

    if sid == idaapi.BADADDR:  # 不存在则创建
        sid = idaapi.add_struc(idaapi.BADADDR, struct.name)
        idaapi.msg(f"Added struct {struct.name}\n")
        struct.id = sid
    else:
        is_update = True
        idaapi.msg(f"Updating struct {struct.name}\n")

    sptr = idaapi.get_struc(sid)
    idaapi.set_struc_cmt(sid, struct.comment, True)

    # 结构体字段
    if not is_update:
        # 创建新结构体，则扩充结构体大小
        idaapi.expand_struc(sptr, 0, struct.size)

    for field in struct.fields:
        if is_update:  # 更新结构体时，先删除原有成员
            idaapi.del_struc_members(sptr, field.offset, field.offset + field.size)
        # 添加成员
        data_flag = _get_data_flags(field)
        if is_struct_type(field._pure_data_type):  # 结构体
            if field._is_ptr:  # 结构体指针
                idaapi.add_struc_member(sptr, field.name, field.offset, data_flag, None, field.size)
            else:  # 结构体或结构体数组
                _add_struc_member(sptr, field)
        elif field.data_type == "string":  # 字符串
            _add_string_member(sptr, field)
        else:  # 其他类型（基础类型，指针）
            idaapi.add_struc_member(sptr, field.name, field.offset, data_flag, None, field.size)

        mptr = idaapi.get_member(sptr, field.offset)
        if mptr == idaapi.BADADDR:
            idaapi.warning(f"Failed to add member '{field.name}' to struct '{struct.name}'")
            continue

        # set tinfo
        tinfo = _get_tinfo_from_data_type(field.data_type)
        if tinfo:
            idaapi.set_member_tinfo(sptr, mptr, 0, tinfo, 0)
        # set comment
        idaapi.set_member_cmt(mptr, field.comment, 1)

    struct_name = idaapi.get_struc_name(sid)
    struct_size = idaapi.get_struc_size(sptr)

    # 校验结构体大小是否一致
    if struct.size == struct_size:
        idaapi.msg(f"Struct {struct_name} size: {struct_size:x}\n")
    else:
        idaapi.warning(f"Struct {struct_name} size mismatch: {struct.size:x} vs {struct_size:x}\n")
        return False

    # IDA 视图中创建结构体数组
    if FORCE_UPDATE_STRUCT_ARRAYS or (not struct.array_updated and len(struct.array_start_addrs) > 0):
        if struct.name in LINKED_LIST_STRUCT_NAME:
            _traverse_linked_list(struct)
        else:
            _create_struct_array(struct)
        struct.array_updated = True

    return True


def import_structs(specific_table_indexes: list[int] = None):
    parser = StructMDFileParser()
    structs = parser.parse(STRUCTS_FILE, specific_table_indexes)
    idaapi.msg(f"Parsed structs: {len(structs)}\n")

    for i, struct in enumerate(structs):
        idaapi.msg(f"Importing {i+1}/{len(structs)}: {struct.name_zh}({struct.name}) ...\n")
        if _import_struct(struct):
            idaapi.msg("Imported.\n")
        else:
            idaapi.msg("Failed to import.\n")

    parser.write_file(STRUCTS_FILE)

    idaapi.msg("All structs imported.\n")
    idaapi.msg("-" * 80 + "\n")


def import_selected_structs():
    s = idaapi.ask_str("", 311, "Input selected struct indexes:")
    selected = list(map(int, s.split(","))) if s else None
    import_structs(selected)


def _replace_common_data_type(data_type: str) -> str:
    return data_type.replace("__int32", "int").replace("__int16", "short").replace("__int8", "byte")


def _export_structs(sid: int, struct: Struct):
    sptr = idaapi.get_struc(sid)
    struct.size = idaapi.get_struc_size(sptr)
    struct.comment = idaapi.get_struc_cmt(sid, True) or ""

    # 字段
    struct.fields.clear()
    for offset, field_name, field_size in idautils.StructMembers(sid):
        mptr = idaapi.get_member(sptr, offset)
        mid = idaapi.get_member_id(sptr, offset)
        comment = idaapi.get_member_cmt(mid, 1) or ""
        tinfo = idaapi.tinfo_t()
        data_type = ""
        if idaapi.get_member_tinfo(tinfo, mptr):
            data_type = _replace_common_data_type(tinfo.dstr())

        field = StructField(offset, field_size, data_type, field_name, comment)
        struct.fields.append(field)


def export_structs():
    idaapi.msg("-" * 80 + "\n")
    idaapi.msg("Exporting structs ...\n")

    # 先解析出已有的结构体
    parser = StructMDFileParser()
    structs = parser.parse(STRUCTS_FILE)
    sid_to_index: dict[int, int] = {}
    for i, struct in enumerate(structs):
        sid_to_index[struct.id] = i

    # 查找所有以 struct_ 开头的结构体（假设这些是我们要导出的结构体）
    for i, sid, name in idautils.Structs():
        if not name.startswith("struct_"):  # 只导出以 struct_ 开头的结构体
            continue
        if sid in sid_to_index:
            struct = structs[sid_to_index[sid]]
            old_table_str = struct.table_string()
            struct.name = name
            _export_structs(sid, struct)
            new_table_str = struct.table_string()
            struct._modified = old_table_str != new_table_str
        else:
            tb_index = len(structs) + 1
            struct = Struct(f"[{tb_index}]{name}")
            struct.id = sid
            struct.name = name
            _export_structs(sid, struct)
            struct._modified = True
            parser.add_struct(struct)

    # 写回文件
    parser.write_file(STRUCTS_FILE)

    idaapi.msg("Exported.\n")
    idaapi.msg("-" * 80 + "\n")


def action():
    # 交互式选择导入或导出
    button = idaapi.ask_buttons("Import", "Export", "Cancel", 1, "Import or export structs")
    if button == 1:
        import_structs()
    elif button == 0:
        export_structs()
    else:
        idaapi.msg("Canceled.\n")


#######################################################################################################
###                                     IDA Plugin 接口相关                                           ###
#######################################################################################################


class San11StruPlugin(idaapi.plugin_t):
    flags = idaapi.PLUGIN_PROC
    comment = "Import or export structs (@san11pk)."
    help = "Shift-S to import or Alt-Shift-S to export san11pk structs."
    wanted_name = "San11StruPlugin"
    wanted_hotkey = ""

    ACTION_IMPORT = "san11:import_structs"
    ACTION_PART_IMPORT = "san11:import_part_structs"
    ACTION_EXPORT = "san11:export_structs"

    def init(self):
        # 注册 import action
        import_action_desc = idaapi.action_desc_t(
            self.ACTION_IMPORT,
            "Import structs",
            IDACtxEntry(import_structs),
            "Alt-Shift-S",
            "Import structs (@san11pk)",
            0,
        )
        assert idaapi.register_action(import_action_desc), "Failed to register action: import"
        # 注册 import part action
        import_part_action_desc = idaapi.action_desc_t(
            self.ACTION_PART_IMPORT,
            "Import part structs",
            IDACtxEntry(import_selected_structs),
            "Shift-S",
            "Import part structs (@san11pk)",
            0,
        )
        assert idaapi.register_action(import_part_action_desc), "Failed to register action: import_part"
        # 注册 export action
        export_action_desc = idaapi.action_desc_t(
            self.ACTION_EXPORT,
            "Export structs",
            IDACtxEntry(export_structs),
            "Shift-W",
            "Export structs (@san11pk)",
            0,
        )
        assert idaapi.register_action(export_action_desc), "Failed to register action: export"

        idaapi.msg("San11StruPlugin initialized.\n")
        return idaapi.PLUGIN_KEEP

    def run(self, arg):
        action()

    def term(self):
        idaapi.unregister_action(self.ACTION_IMPORT)
        idaapi.unregister_action(self.ACTION_PART_IMPORT)
        idaapi.unregister_action(self.ACTION_EXPORT)
        idaapi.msg("San11StruPlugin terminated.\n")


class IDACtxEntry(idaapi.action_handler_t):
    def __init__(self, action_function):
        idaapi.action_handler_t.__init__(self)
        self.action_function = action_function

    def activate(self, ctx):
        self.action_function()
        return 1

    def update(self, ctx):
        return idaapi.AST_ENABLE_ALWAYS


def PLUGIN_ENTRY():
    return San11StruPlugin()


if __name__ == "__main__":
    action()


repository: za233/IDADeflat
file: idadeflat.py
description: deflat plugins for ida pro
url: https://github.com/za233/IDADeflat/blob/12b8a9ba2cb41232d53aad850abe2850fe4e8a56/idadeflat.py
wanted_name: IDADeflat
comment: IDADeflat plugin for IDA Pro (using angr framework)
created_at: 2023-08-23 02:00:21+00:00
pushed_at: 2023-08-25 09:28:41+00:00
content:
import idaapi
import idc
import json
from idadeflat_cores.core_base import DeflatCore
from idadeflat_cores.angr_core import AngrDeflatCore
ADD_BLOCK_ACTION = ('IDADeflat:add_block', 'Add Relevant Block', 'add_working_blocks')
SET_ENTRY_ACTION = ('IDADeflat:set_entry', 'Set Function Entry', 'set_entry_block')
DO_DEFLAT_ACTION = ('IDADeflat:do_deflat', 'Deflat GOGOGO', 'do_deflat')
RESET_ACTION = ('IDADeflat:reset', 'Reset Plugin State', 'reset_state')
SHOW_BLOCK_ACTION = ('IDADeflat:show', 'Show State Info', 'show_state')
REMOVE_BLOCK_ACTION = ('IDADeflat:del_block', 'Delete Relevant Block', 'del_working_blocks')
IMPORT_BLOCK_ACTION = ('IDADeflat:import_block', 'Import Blocks From File', 'import_blocks')
UNDO_PATCH_ACTION = ('IDADeflat:undo_patch', 'Undo Last Patching', 'undo_patch')
BASIC_ACTIONS = [SET_ENTRY_ACTION, ADD_BLOCK_ACTION, REMOVE_BLOCK_ACTION, DO_DEFLAT_ACTION, RESET_ACTION, UNDO_PATCH_ACTION, SHOW_BLOCK_ACTION, IMPORT_BLOCK_ACTION]
SWITCH_ACTIONS = []


def get_block_node(addr):
    func = idaapi.get_func(addr)
    if not func:
        return None
    graph = idaapi.FlowChart(func, flags=idaapi.FC_PREDS)
    for node in graph:
        if addr >= node.start_ea and addr < node.end_ea:
            return node
    return None

def set_color_to_block(node, color):
    ptr = node.start_ea
    while ptr < node.end_ea:
        idc.set_color(ptr, idc.CIC_ITEM, color)
        ptr = idaapi.next_head(ptr, node.end_ea)

class ActionHandler(idaapi.action_handler_t):

    def __init__(self, host, name, label, callback, metadata : dict=None, shortcut=None, tooltip=None, icon=-1, flags=0):
        idaapi.action_handler_t.__init__(self)
        self.callback = callback
        self.name = name
        self.host = host
        self.metadata =metadata
        self.action_desc = idaapi.action_desc_t(name, label, self, shortcut, tooltip, icon, flags)

    def unregister_action(self):
        idaapi.unregister_action(self.name)

    def register_action(self, menupath=None):
        if not idaapi.register_action(self.action_desc):
            return False
        if menupath and not idaapi.attach_action_to_menu(menupath, self.name, idaapi.SETMENU_APP):
            return False
        return True

    def activate(self, ctx):
        self.callback(host=self.host, meta=self.metadata, ctx=ctx)

    def update(self, ctx):
        return idaapi.AST_ENABLE_ALWAYS
    

class UIHooks(idaapi.UI_Hooks):

    def finish_populating_widget_popup(self, widget, popup):
        if idaapi.get_widget_type(widget) == idaapi.BWN_DISASM:
            idx = 0
            for action in BASIC_ACTIONS:
                idaapi.attach_action_to_popup(widget, popup, action[0], "Deflat/")
                if idx == 2 or idx == 6:
                    idaapi.attach_action_to_popup(widget, popup, "-", 'Deflat/')
                idx += 1
            for action in SWITCH_ACTIONS:
                idaapi.attach_action_to_popup(widget, popup, action[0], "Deflat/Cores/")


class StateChoose(idaapi.Choose):
    
    def __init__(self, title, items, embedded=False):
        idaapi.Choose.__init__(self, title, [["Address", 20], ["Function", 30], ["Type", 30]], embedded=embedded)
        self.items = items
        self.icon = 46

    def GetItems(self):
        return self.items

    def SetItems(self, items):
        self.items = [] if items is None else items

    def OnClose(self):
        pass

    def OnGetLine(self, n):
        return self.items[n]

    def OnGetSize(self):
        return len(self.items)

    def OnSelectLine(self, n):
        idaapi.jumpto(int(self.items[n][0], 16))

class IDADeflatMain:
    
    def __init__(self):
        self.working_entry = -1
        self.working_blocks = []
        self.process_cores : list[DeflatCore] = []
        self.using_core :DeflatCore = None
        self.registered_actions = []
        self.ui_hook = None
        self.last_patch = []

    def add_core(self, core : DeflatCore):
        self.process_cores.append(core)

    def set_core(self, core_name):
        for core in self.process_cores:
            if core.get_name() == core_name:
                self.using_core = core
                return True
        return False


    @staticmethod
    def add_working_blocks(**kwargs):
        main_obj : IDADeflatMain = kwargs['host']
        if main_obj.working_entry < 0:
            print('[IDADeflat] please set function entry address first!')
            return
        ea = idaapi.get_screen_ea()
        block = get_block_node(ea)
        if not block:
            print('[IDADeflat] unable to locate basic block for %s' % hex(ea))
            return
        ea = block.start_ea
        func = idaapi.get_func(ea)
        if func is None or func.start_ea != main_obj.working_entry:
            print('[IDADeflat] invalid basic block address for Function: %s' % idaapi.get_func_name(main_obj.working_entry))
            return
        main_obj.working_blocks.append(ea)
        set_color_to_block(get_block_node(ea), 0xffcc33)
        print('[IDADeflat] relevant block at %s has been added to working list...' % hex(ea))
        
    @staticmethod
    def import_blocks(**kwargs):
        main_obj : IDADeflatMain = kwargs['host']
        IDADeflatMain.reset_state(host=main_obj)
        file = open('blocks.json', 'rb')
        blocks_info = json.load(file)
        main_obj.working_entry = blocks_info['entry_block']
        for addr in blocks_info['relevant_blocks']:
            main_obj.working_blocks.append(addr)
        file.close()


    @staticmethod
    def del_working_blocks(**kwargs):
        main_obj : IDADeflatMain = kwargs['host']
        ea = idaapi.get_screen_ea()
        block = get_block_node(ea)
        if not block:
            print('[IDADeflat] unable to locate basic block for %s' % hex(ea))
            return
        ea = block.start_ea
        if ea in main_obj.working_blocks:
            main_obj.working_blocks.remove(ea)
            set_color_to_block(get_block_node(ea), 0xffffff)
            print('[IDADeflat] remove relevant block successfully...')

    @staticmethod
    def set_entry_block(**kwargs):
        main_obj : IDADeflatMain = kwargs['host']
        ea = idaapi.get_screen_ea()
        func = idaapi.get_func(ea)
        if func is None:
            print('[IDADeflat] invalid entry point')
            return
        if main_obj.working_entry > 0:
            set_color_to_block(get_block_node(main_obj.working_entry), 0xffffff)
        main_obj.working_entry = func.start_ea
        set_color_to_block(get_block_node(func.start_ea), 0xffcc33)
        print('[IDADeflat] entry point set to %s in %s' % (hex(func.start_ea), idaapi.get_func_name(func.start_ea)))

    @staticmethod
    def do_deflat(**kwargs):
        main_obj : IDADeflatMain = kwargs['host']
        if main_obj.working_entry < 0 or len(main_obj.working_blocks) == 0:
            print('[IDADeflat] nothing to do, please specify a function and relevant blocks....')
            return
        target_func = idaapi.get_func(main_obj.working_entry)
        if not target_func:
            print('[IDADeflat] invalid function address...')
            return
        for block in main_obj.working_blocks:
            if idaapi.get_func(block) != target_func:
                print('[IDADeflat] blocks are not in the same function!...')
                return
        patches = main_obj.using_core.process(main_obj.working_entry, main_obj.working_blocks.copy())
        main_obj.reset_state(host=main_obj)
        graph = idaapi.FlowChart(target_func, flags=idaapi.FC_PREDS)
        for node in graph:
            main_obj.last_patch.append((node.start_ea, idaapi.get_bytes(node.start_ea, node.end_ea - node.start_ea)))
            idaapi.patch_bytes(node.start_ea, (node.end_ea - node.start_ea) * b'\x00')
        for patch_addr, patch_data in patches:
            idaapi.patch_bytes(patch_addr, patch_data)
            
    @staticmethod
    def undo_patch(**kwargs):
        main_obj : IDADeflatMain = kwargs['host']
        for patch_addr, patch_data in main_obj.last_patch:
            idaapi.patch_bytes(patch_addr, patch_data)
        main_obj.last_patch.clear()

    @staticmethod
    def switch_core(**kwargs):
        main_obj : IDADeflatMain = kwargs['host']
        metadata : dict = kwargs['meta']
        core_name = metadata['core_name']
        if main_obj.set_core(core_name):
            print('[IDADeflat] switch core to %s' % core_name)
        else:
            print('[IDADeflat] fail to set core....')

    @staticmethod
    def reset_state(**kwargs):
        main_obj : IDADeflatMain = kwargs['host']
        if main_obj.working_entry > 0:
            set_color_to_block(get_block_node(main_obj.working_entry), 0xffffff)
        for ea in main_obj.working_blocks:
            set_color_to_block(get_block_node(ea), 0xffffff)
        main_obj.working_entry = -1
        main_obj.working_blocks.clear()
        main_obj.last_patch.clear()
        print('[IDADeflat] reset state successfully..')
        

    @staticmethod
    def show_state(**kwargs):
        main_obj : IDADeflatMain = kwargs['host']
        if main_obj.working_entry > 0:
            items = []
            items.append((hex(main_obj.working_entry), idaapi.get_func_name(main_obj.working_entry), 'EntryBlock'))
            for addr in main_obj.working_blocks:
                func = idaapi.get_func(addr)
                items.append((hex(addr), idaapi.get_func_name(func.start_ea), 'RelevantBlock'))
            ch = StateChoose('Deflat Info', items)
            ch.Show()
        else:
            print('[IDADeflat] no state...')

    def init(self):
        self.add_core(AngrDeflatCore())
        self.using_core = self.process_cores[0]
        for action in BASIC_ACTIONS:
            handler = ActionHandler(self, action[0], action[1], getattr(IDADeflatMain, action[2]))
            handler.register_action()
            self.registered_actions.append(handler)
        for core in self.process_cores:
            core_name = core.get_name()
            name = 'IDADeflat:' + core_name
            label_name = 'Use ' + core_name
            handler = ActionHandler(self, name, label_name, 
                                    getattr(IDADeflatMain, 'switch_core'), 
                                    metadata={
                                        'core_name' : core_name
                                    })
            handler.register_action()
            SWITCH_ACTIONS.append((name, label_name))
            self.registered_actions.append(handler)

        self.ui_hook = UIHooks()
        self.ui_hook.hook()

    def term(self):
        if self.ui_hook:
            self.ui_hook.unhook()
        for action in self.registered_actions:
            action.unregister_action()
        self.registered_actions.clear()

    
def check_ida_version():
    if idaapi.IDA_SDK_VERSION < 700:
        print("[-] IDADeflat support 7.x IDA, please update your IDA version.")
        return False
    return True


class IDADeflat_t(idaapi.plugin_t):
    comment = "IDADeflat plugin for IDA Pro (using angr framework)"
    help = "todo"
    wanted_name = "IDADeflat"
    wanted_hotkey = ""
    flags = idaapi.PLUGIN_KEEP
    def init(self):
        if not check_ida_version():
             return idaapi.PLUGIN_SKIP
        self.main = IDADeflatMain()
        self.main.init()
        print('[IDADeflat] IDADeflat plugin initialized!')
        return idaapi.PLUGIN_OK

    def run(self, arg):
        pass

    def term(self):
        if self.main:
            self.main.term()
        pass

def PLUGIN_ENTRY():
    return IDADeflat_t()



repository: zfi2/IDA-9.0-Discord-RPC
file: ida-rpc.py
description: IDA 9.0 Discord Rich Presence Python plugin
created_at: 2024-08-17 04:30:42+00:00
pushed_at: 2024-08-17 04:47:33+00:00
url:
https://github.com/zfi2/IDA-9.0-Discord-RPC/blob/1a686d49ac20faf8f12a990debb60d216bf25cb5/ida-rpc.py

content:
import ida_idaapi
import ida_kernwin
import ida_funcs
import ida_name
import ida_nalt
import time
from pypresence import Presence

PLUGIN_NAME = "Discord RPC for IDA"
PLUGIN_HOTKEY = "Ctrl-Alt-D"
PLUGIN_COMMENT = "Display IDA status in Discord"
PLUGIN_HELP = "This plugin updates your Discord status with IDA information"
PLUGIN_VERSION = "1.0"

CLIENT_ID = "1274210451273551973"
THROTTLE_TIME = 5  # Minimum time between updates in seconds

class DiscordRPCPlugin(ida_idaapi.plugin_t):
    flags = ida_idaapi.PLUGIN_PROC | ida_idaapi.PLUGIN_HIDE
    comment = PLUGIN_COMMENT
    help = PLUGIN_HELP
    wanted_name = PLUGIN_NAME
    wanted_hotkey = PLUGIN_HOTKEY

    def __init__(self):
        self.rpc = None
        self.running = False
        self.hook = None
        self.last_func_name = None
        self.last_update_time = 0
        self.update_pending = False
        self.start_time = int(time.time())

    def init(self):
        self.start_rpc()
        return ida_idaapi.PLUGIN_KEEP

    def run(self, arg):
        # Toggle RPC on/off
        if self.running:
            self.stop_rpc()
        else:
            self.start_rpc()

    def term(self):
        self.stop_rpc()

    def start_rpc(self):
        if not self.running:
            try:
                self.rpc = Presence(CLIENT_ID)
                self.rpc.connect()
                self.running = True
                self.hook = IDAViewHook(self)
                self.hook.hook()
                self.update_presence(force=True)
                print(f"{PLUGIN_NAME} started")
            except Exception as e:
                print(f"Error starting {PLUGIN_NAME}: {str(e)}")

    def stop_rpc(self):
        if self.running:
            self.running = False
            if self.hook:
                self.hook.unhook()
            if self.rpc:
                self.rpc.close()
            print(f"{PLUGIN_NAME} stopped")

    def update_presence(self, force=False):
        if not self.running:
            return

        current_time = time.time()
        if not force and current_time - self.last_update_time < THROTTLE_TIME:
            if not self.update_pending:
                self.update_pending = True
                ida_kernwin.register_timer(THROTTLE_TIME * 1000, self._delayed_update)
            return

        self._perform_update()

    def _delayed_update(self):
        self.update_pending = False
        self._perform_update()
        return -1  # Unregister the timer

    def _perform_update(self):
        try:
            current_function = ida_funcs.get_func(ida_kernwin.get_screen_ea())
            if current_function:
                func_name = ida_name.get_ea_name(current_function.start_ea)
            else:
                func_name = "No function"

            if func_name != self.last_func_name:
                self.last_func_name = func_name
                file_name = ida_nalt.get_root_filename()

                self.rpc.update(
                    details=f"Analyzing: {file_name}",
                    state=f"Function: {func_name}",
                    large_image="ida_logo",
                    large_text="IDA Pro 9.0",
                    start=self.start_time
                )
                self.last_update_time = time.time()
        except Exception as e:
            print(f"Error updating presence: {str(e)}")

class IDAViewHook(ida_kernwin.UI_Hooks):
    def __init__(self, plugin):
        ida_kernwin.UI_Hooks.__init__(self)
        self.plugin = plugin

    def screen_ea_changed(self, ea, prev_ea):
        self.plugin.update_presence()
        return 0

def PLUGIN_ENTRY():
    return DiscordRPCPlugin()


repository: rolandocarpio/GameHackingStuff
file: Stuff/IDA/plugins/DeepDecompile.py
description: useful stuff for gamehacking
wanted_name: Deep Decompile
comment: Plugin to deep decompile with hexrays
created_at: 2021-02-04 01:22:53+00:00
pushed_at: 2021-02-01 05:14:37+00:00
url:
https://github.com/rolandocarpio/GameHackingStuff/blob/9cf0bc6d246d1976350bb81b04e0c003bc09f9db/Stuff/IDA/plugins/DeepDecompile.py

content:
import idaapi

def get_called_functions(fn):
    called_fns = []
    for item in FuncItems(fn):
        for xref in XrefsFrom(item):
            if xref.iscode:
                if (xref.type == fl_CN) or (xref.type == fl_CF):
                    called_fns.append(xref.to)
    return called_fns

def get_called_functions_recursive(fn, depth):
    called_fns = []
    called_fns.append(fn)
    if depth <= 1:
        return get_called_functions(fn) + called_fns
    for _fn in get_called_functions(fn):
        called_fns = get_called_functions_recursive(_fn, depth-1) + called_fns
    return called_fns

def DeepDecompileFn(fn, depth, hx_view = None):
    inPseudocode = hx_view and 1 or 0
    for func in get_called_functions_recursive(fn, depth):
        idaapi.decompile(func)
    idaapi.open_pseudocode(fn, inPseudocode)

class DeepDecompile(idaapi.action_handler_t):
    
    def __init__(self, depth):
        idaapi.action_handler_t.__init__(self)
        self.depth = depth or 1
    def activate(self, ctx):
        if ctx.form_title[0:10] == "Pseudocode":
            hx_view = idaapi.get_tform_vdui(ctx.form)
            ea = hx_view.cfunc.entry_ea
        else:
            hx_view = None
            ea = ScreenEA()
        DeepDecompileFn(ea, self.depth, hx_view)
        return
    def update(self, ctx):
        return idaapi.AST_ENABLE_ALWAYS
#cannot get this working :(
class Hooks(idaapi.UI_Hooks):
    def __init__(self):
        idaapi.UI_Hooks.__init__(self)
    def hook(self):
        print "UI Hook Installed"
        pass
    def populating_tform_popup(self, form, popup):
        print "populating"
        if idaapi.get_tform_type(form) == idaapi.BWN_DISASM or idaapi.get_tform_type(form) == idaapi.BWN_DISASMS:
            idaapi.attach_action_to_popup(form, popup, "mee:DeepDecompile1", None)
            idaapi.attach_action_to_popup(form, popup, "mee:DeepDecompile3", None)
        

def hexrays_events_callback(*args):
    hexrays_event = args[0]

    if hexrays_event == idaapi.hxe_populating_popup:
        form, popup, hx_view = args[1:]
        item = hx_view.item
        idaapi.attach_action_to_popup(form, popup, "mee:DeepDecompile1", None)
        idaapi.attach_action_to_popup(form, popup, "mee:DeepDecompile3", None)
    return 0

class myplugin_t(idaapi.plugin_t):
    flags = idaapi.PLUGIN_UNL
    comment = "Plugin to deep decompile with hexrays"

    help = "Just right click and press the button"
    wanted_name = "Deep Decompile"
    wanted_hotkey = "Alt-F8"

    def init(self):
        if not idaapi.init_hexrays_plugin():
            print "[ERROR] Failed to initialize Hex-Rays SDK"
            return isaapi.PLUGIN_SKIP

        idaapi.register_action(
            idaapi.action_desc_t(
                "mee:DeepDecompile1",
                "Deep Decompile Function",
                DeepDecompile(1),
                None
            )
        )

        idaapi.register_action(
            idaapi.action_desc_t(
                "mee:DeepDecompile3",
                "Deep Decompile Function 3 Deep",
                DeepDecompile(3),
                None
            )
        )

        self.hooks = Hooks()
        self.hooks.hook()
        
        idaapi.attach_action_to_menu("View/Open subviews/Local types","mee:DeepDecompile3",idaapi.SETMENU_APP) 
        idaapi.attach_action_to_menu("View/Open subviews/Local types","mee:DeepDecompile1",idaapi.SETMENU_APP)
        idaapi.install_hexrays_callback(hexrays_events_callback)


        return idaapi.PLUGIN_KEEP

    def run(self, arg):
        DeepDecompileFn(idaapi.get_func(ScreenEA()).startEA, 1)

    def term(self):
        idaapi.unregister_action("mee:DeepDecompile1")
        idaapi.unregister_action("mee:DeepDecompile3")
        self.hooks.unhook()

def PLUGIN_ENTRY():
    return myplugin_t()


repository: Cisco-Talos/CASC
file: plugin/casc_plugin.py
wanted_name: ClamAV Signature Creator
comment: Aids analysts in creating ClamAV NDB and LDB signatures
created_at: 2015-05-04 19:15:48+00:00
pushed_at: 2018-09-12 15:42:36+00:00
url:
https://github.com/Cisco-Talos/CASC/blob/9740d9ca982ba7a83a13aa3cb6f754e59126f89d/plugin/casc_plugin.py

content:
#-------------------------------------------------------------------------------
#
#   Copyright (C) 2015 Cisco Talos Security Intelligence and Research Group
#
#   IDA Pro Plug-in: ClamAV Signature Creator (CASC)
#   Author: Angel M. Villegas
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License version 2 as
#   published by the Free Software Foundation.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#   MA 02110-1301, USA.
#
#   Last revision: May 2017
#   This IDA Pro plug-in will aid in creating ClamAV ndb and ldb signatures
#   from data within the IDB the user selects.
#
#   Installation
#   ------------
#   Drag and drop into IDA Pro's plugin folder for IDA Pro 6.6 and higher.
#   To gain all the features of this plug-in, using IDA Pro 6.7 or higher.
#   Older versions of IDA Pro may require other Python packages (i.e. PySide,
#   Qt, etc.)
#
#-------------------------------------------------------------------------------
import idaapi
import idc
import idautils
from idaapi import PluginForm, action_handler_t, UI_Hooks, plugin_t, BADADDR, \
                    insn_t, execute_ui_requests, IDA_SDK_VERSION, BWN_DISASMS, \
                    BWN_STRINGS, BWN_IMPORTS, AST_ENABLE_ALWAYS

#   Python Modules
import collections
import struct
import bisect
import pickle
import math
import types
import re
import csv
from urllib import quote_plus
from pprint import pprint
from idc import GetIdaDirectory
try:
    #   For IDA 6.8 and older using PySide
    from PySide import QtGui, QtGui as QtWidgets, QtCore
    from PySide.QtCore import Qt
except ImportError:
    #   For IDA 6.9 and newer using PyQt5
    from PyQt5 import QtGui, QtWidgets, QtCore
    from PyQt5.QtCore import Qt

try:
    try:
        import yara
        import ply
        import netnode
    except ImportError:
        #TODO: This is a hack to load the dependencies installed into
        #<IDAROOT>/python/lib/python2.7/site-packages. Make this prettier in the future.
        import os
        import site
        try:
            site.addsitedir(os.path.join(GetIdaDirectory(), "python", "lib", "python2.7", "site-packages"))
        except NameError:
            try:
                import ida_diskio
                site.addsitedir(os.path.join(ida_diskio.get_ida_subdirs('python')[1], 'lib', 'python2.7', 'site-packages'))
            except ImportError:
                print 'Get IDA directory'

        import yara
        import ply
        import netnode
    from casc.sigalyzer.clamav import parse_signature, LdbSignature, NdbSignature
    from casc.sigalyzer.yara import convert_to_yara
    from casc.sigalyzer.ida import print_console
    from casc.sigalyzer.clamav import AbsoluteOffset, EPRelativeOffset, \
        AnyOffset, InSectionOffset, SectionRelativeOffset, \
        EOFRelativeOffset
    from ida_loader import get_fileregion_ea

    sigalyzer_required_modules_loaded = True
except ImportError as err:
    print("[CASCPlugin] Error loading dependencies for Sigalyzer: {}".format(str(err)))
    sigalyzer_required_modules_loaded = False

SIGALYZER_COLOR_HIGHLIGHTED = 0x4dd811
AR_LONG = 65
AR_STR = 83
#   Global Variables
#-------------------------------------------------------------------------------
b_asm_sig_handler_loaded = True
clamav_sig_creator_plugin = None
add_sig_handler_in_menu = False
valid_address_ranges = []

CLAMAV_ICON = None

#   IDA Wrapper to ensure thread safety for function calls
#-------------------------------------------------------------------------------
class IDAWrapper(object):
    '''
    Class to wrap functions that are not thread safe
    '''
    mapping = {
        'get_tform_type' : 'get_widget_type',
    }
    def __init__(self):
        self.version = idaapi.IDA_SDK_VERSION

    def __getattribute__(self, name):
        default = '[1st] default'

        if (idaapi.IDA_SDK_VERSION >= 700) and (name in IDAWrapper.mapping):
            name = IDAWrapper.mapping[name]

        val = getattr(idaapi, name, default)
        if val == default:
            val = getattr(idautils, name, default)

        if val == default:
            val = getattr(idc, name, default)

        if val == default:
            msg = 'Unable to find {}'.format(name)
            idaapi.execute_ui_requests((FIRSTUI.Requests.Print(msg),))
            return

        if hasattr(val, '__call__'):
            def call(*args, **kwargs):
                holder = [None] # need a holder, because 'global' sucks

                def trampoline():
                    holder[0] = val(*args, **kwargs)
                    return 1

                idaapi.execute_sync(trampoline, idaapi.MFF_FAST)
                return holder[0]
            return call

        else:
            return val

IDAW = IDAWrapper()


#   Misc Helper Functions
#-------------------------------------------------------------------------------
def get_file_type():
    #   ClamAV Types: {1 : 'PE', 6 : 'ELF', 9 : 'Mach-O', 0 : 'Any'}
    file_type = IDAW.get_file_type_name()
    if None == file_type:
        return 0

    file_type = file_type.lower()
    if 'mach-o' in file_type:
        return 9
    elif 'elf' in file_type:
        return 6
    elif ('pe' in file_type) or ('.net' in file_type):
        return 1

    return 0;

def get_type_name(file_type):
    lookup = {1 : 'PE', 6 : 'ELF', 9 : 'Mach-O', 0 : 'Any'}

    if file_type not in lookup:
        return 'UNKNOWN'

    return lookup[file_type]

def convert_to_ascii(data):
    if data is None:
        return data

    if len(data) != 1:
        return data

    data = data[0].replace(' ', '')
    converted = ''
    not_complete = True
    while not_complete:
        if re.match('^([a-fA-F\d]{2})', data):
            converted += data[:2].decode('hex')
            data = data[2:]

        elif data.startswith('{'):
            match = re.match('^\{(?:(\d+)|(\d+)\-|\-(\d+)|(\d+)\-(\d))\}', data)
            matches =  match.groups()
            if matches[0] is not None:
                length = '=={}'.format(matches[0])
            elif matches[1] is not None:
                length = '>={}'.format(matches[1])
            elif matches[2] is not None:
                length = '<={}'.format(matches[2])
            elif None not in matches [3:]:
                length = '>={}&&<={}'.format(matches[3], matches[4])

            end = data.index('}') + 1
            converted += '{{WILDCARD_ANY_STRING(LENGTH{})}}'.format(length)
            data = data[end:]

        elif re.match('^\[(\d+)\-(\d+)\]', data):
            matches = re.match('^\[(\d+)\-(\d+)\]').groups()
            end = data.index(']') + 1
            converted += '{{WILDCARD_ANY_STRING(LENGTH>={0[0]}&&<={0[1]})}}'.format(matches)
            data = data[end:]

        elif data.startswith('*'):
            converted += '{WILDCARD_ANY_STRING}'
            data = data[1:]

        elif data.startswith('??'):
            converted += '{WILDCARD_IGNORE}'
            data = data[2:]

        elif re.match('^(?:([a-fA-F\d])\?|\?([a-fA-F\d]))', data):
            matches = re.match('^(?:([a-fA-F\d])\?|\?([a-fA-F\d]))').groups()
            temp = '{{WILDCARD_NIBBLE_{}:{}}}'
            if matches[0] is not None:
                temp = temp.format('HIGH', hex(matches[0]))
            else:
                temp = temp.format('LOW', hex(matches[1]))
            converted += temp
            data = data[2:]

        elif data.startswith('('):
            end = data.index(')') + 1
            alternates = convert_to_ascii([data[1:end-1]])

            converted += '{{STRING_ALTERNATIVE:{}}}'.format(alternates)
            data = data[end:]

        else:
            if data[0] not in ['|']:
                print '[CASC] Error: idk how to handle {}'.format(data[0])
            converted += data[0]
            data = data[1:]

        if len(data) == 0:
            not_complete = False

    return converted

def is_32bit():
    info = IDAW.get_inf_structure()
    if info.is_64bit():
        return False
    elif info.is_32bit():
        return True

    return False

def is_64bit():
    info = IDAW.get_inf_structure()
    return info.is_64bit()

def get_clamav_icon(return_hex=False, return_pixmap=False):
    clamav_icon = ( '89504E470D0A1A0A0000000D494844520000001A0000001A08060000'
                    '00A94A4CCE000000097048597300000B1300000B1301009A9C180000'
                    '001974455874536F6674776172650041646F626520496D6167655265'
                    '61647971C9653C000004A54944415478DABC965F4CDB5514C7BFF7D7'
                    '5FFFD0C2DA04D8803A680B5306E34F7046972CA1D944F73081FD8926'
                    'EADC740F2688CB78501F8C913D9810E616E28359E603F3498D89AB31'
                    'B06571A6304748468031D9A285B4FC29B004C8DA425B28FDFD3CF757'
                    '685A2819F3CF9ADCFE7EBF7BEF399F7BCE3DE7DCCB6459C653F971D0'
                    'B0B9C4C29FFF754BD42BACF2DAFE273BE27A45FE770B912C3C535A5F'
                    '3239EC583FF34A5189A59289F56AC04E9FA675C34E6A8E52D7D0E07A'
                    'B9FBA4AF13CBB925ABDF8C9BF58ED97AB591A51D4807AB269822D45C'
                    '586C7A9E896D36269CDCC2CABBFC909BF7B9EE39572195D390BA2FCA'
                    'C1810EEF58751C5466DED970089A8BEF32DD0CF5557EA78D56EE652A'
                    '471153199FC44F3EC8E74C2189BB6BB0450EE60E60A5E99E77E2EB38'
                    'C86C369BC835D3ED6C9B2E1B42E8B22A9C661544BC226AB70CE9892E'
                    'E3EE4A042724ED7210B2E66DD9CFBBAD5EAFD71307F11FC11C855055'
                    '5F6219B17D6014913AD596412C1C2581D87B831C587421DA4710FBDA'
                    'B8901821A3889A3EC32216B8041792B69863526C3E97FB9CE4096230'
                    '0BE2A5C42971D03E8D1EF9E4AE5E3982F3E0C6D390C0B606A2797C3E'
                    '97EB21F92A951AAF6BF4875282D218CEAE39AA50A4A857B127CB189A'
                    '5FAED628AFB3B204DADDFA942011CCCE27EC22AB4EA8F5FF283B8F8A'
                    '3A940B6A05448A8D676CCF26EFD1416BA185D66FDC4F5156AF4E43E4'
                    '5F94828FB4E9784F6D4034F6990CA27DB448F4BF5325225310683B93'
                    '83A02FBC883F97C31B94FE160C20204593FAB2988062D2C3171B5DEF'
                    '3A89DA0AB5250AF54562388B0A70EDCD23E83368D13A3783D3D363F8'
                    '39F06803BCE9E18432F6905CF54B55296E57ED818FEBA08586A82D27'
                    'CC576A1D413C4B040892FFE649E8F8F916ECDABD1BBD3535386DB7A3'
                    '26DD888F337392407B75067C9A95872F66A7D079B806AD17BE442810'
                    'C0B5DAE3F07BBD08127029C1338A45BFBB473D61C83E0A6A78E4288C'
                    '9999CAE05F2E17F2F3F3D13E3480FEDA57B19070760DE56463FF5717'
                    'F0616323FA87EE2A7D1ABD1E93C545A0BAA7E45448568A6E72D491A9'
                    '4E3EC87DEBF7FB110E8731363686BADA5A6C379BF1566B0BF21CDF23'
                    '92978BC94FCEE20DE70D1C3C7614B57575E8EEEE56744CCFCCC0B7B0'
                    'A0B86F814ADF8F6E572A10DAF88A1963982080DBE3C10C09728BE2B9'
                    '9695A928297DF940BCCF5250A0CC191F1FC7FCDC1C82BD77F088161C'
                    '906547CA3CBAED1EE51675F949D195F73FC0FCFC3C8A699F8C2613FE'
                    '181E56DAF59FAE223035859B97BFC1FD070F94C58497965051518100'
                    'EDCF0F0D679440A02AEE0BD0B191540B13EF0C2F5A6D954630A71182'
                    '91E778765929B697976147CE0E4CDDEA41FA9D7E18287C79CD98DDF3'
                    '1C6CAF1DC636B2F266470742BF3A2191377C1C24CB4D1DEE91B64D41'
                    '4ACDB3169ECA00DA33485D3A091AE899468D1F186AFA5E2B534A4AC8'
                    'B110E6A11CA427773D59F26DA77BE4D486EA9EEA16F492D566278043'
                    '0F66E4101D99A0A5A7B8EA6B96907B1182F0540EC90AECDC0DF74873'
                    'CA6364B3EBD60B169B49C7589B0E38A921D59A583D8C6FAAA4647E2C'
                    '4AC9AA2ECA996627EDF3A6E7D5E3EE7504B46818AB276BECE4361381'
                    '2CD4ED916325C6495639280F071F7B303EAD0BE4DF020C0026BB3556'
                    '2D86F1AC0000000049454E44AE426082')

    if return_hex:
        return clamav_icon

    image = QtGui.QImage()
    image.loadFromData(QtCore.QByteArray.fromHex(clamav_icon))

    pixmap = QtGui.QPixmap()
    pixmap.convertFromImage(image)
    if return_pixmap:
        return pixmap

    return QtGui.QIcon(pixmap)

def verify_clamav_sig(sig):
    sig_format = (  '^('
                        '([\da-fA-F\?]{2})|'
                        '(\{(?:\d+|\-\d+|\d+\-|(?:\d+)\-(?:\d+))\})|'
                        '(\*)|'
                        '((?:!|)\((?:[\da-fA-F\?]{2})+(?:\|(?:[\da-fA-F\?]{2})+)+\))|'
                        '(\((?:B|L|W)\))|'
                        '(\[\d+\-\d+\])'
                    ')+$')
    pattern = sig_format[2:-3]

    if None == re.match(sig_format, sig):
        return 'Invalid signature, check ClamAV signature documentation'

    matches = [filter(None, x)[0] for x in re.findall(pattern, sig)]
    for i in xrange(len(matches)):
        if matches[i].startswith('{'):
            #   Ensure that there are two bytes before and after
            if (i-2 < 0) and  (i+2 >= len(matches)):
                return ('Invalid signature, two hex bytes are not before and '
                        'after {*} expression')

            #   Check bytes before for valid hex strings
            before_check = 0
            for j in  list({max(i-2, 0), max(i-1, 0)}):
                if re.match('[\da-fA-F]{2}', matches[j]):
                    before_check += 1

            #   Check bytes after for valid hex strings
            after_check = 0
            for j in [i+1, i+2]:
                if j >= len(matches):
                    continue
                if re.match('[\da-fA-F]{2}', matches[j]):
                    after_check += 1

            if 2 not in [before_check, after_check]:
                return ('Invalid signatrue, hex byte at {0} ({1}) is not '
                        'preceeded or followed by two fixed byte '
                        'values'.format(i, matches[i]))

        #   Look {n-m} extension
        values = re.match('\{(\d+)\-(\d+)\}', matches[i])
        if None != values:
            if values.group(2) <= values.group(1):
                return 'Invalid signature, m is less than or equal to n'

    return None

def get_block(ea):
    '''
    Given a virtual address, this function will return a block object or None
    '''
    ea_func = IDAW.get_func(ea)

    #   Ensure ea is in a function
    if ea_func:
        fc = IDAW.FlowChart(ea_func)

        for block in fc:
            #   Check address selected is in the block's range
            if (block.startEA <= ea) and (ea < block.endEA):
                return block

    return None

def get_existing_segment_ranges():
    return map(lambda x: [x.startEA, x.endEA], map(IDAW.getseg, IDAW.Segments()))

def is_in_sample_segments(ea):
    global valid_address_ranges

    for segment_range in valid_address_ranges:
        if segment_range[0] <= ea < segment_range[1]:
            return True

    return False

def get_architecture():
    info = IDAW.get_inf_structure()
    proc = info.procName.lower()
    if 'metapc' == proc:
        proc = 'intel'

    bits = 16
    if info.is_64bit():
        bits = 64
    elif info.is_32bit():
        bits = 32

    return (proc, bits)

def get_parser():
    proc, bits = get_architecture()
    mapping = {'intel' : IntelParser}

    if proc in mapping:
        parser = mapping[proc]
        if type(parser) != types.TypeType:
            #   For future use if mapping includes more of a breakdown
            return CASCParser(bits)

        return parser(bits)

    return CASCParser(bits)

def get_gui():
    proc, bits = get_architecture()
    mapping = {'intel' : IntelMask}

    if proc in mapping:
        gui = mapping[proc]
        if type(gui) != types.TypeType:
            #   For future use if mapping includes more of a breakdown
            return CASCMask(bits)

        return gui(bits)

    return CASCMask(bits)


#   Create ClamAV icon
CLAMAV_ICON = get_clamav_icon(True).decode('hex')
CLAMAV_ICON = IDAW.load_custom_icon(data=CLAMAV_ICON, format='png')


#   Action Handler Classes - Supported for IDA Pro 6.7 and higher
#-------------------------------------------------------------------------------
try:
    #   Action Handlers, support added with IDA Pro 6.7
    class CASCActionHandler(action_handler_t):
        def __init__(self, fn):
            action_handler_t.__init__(self)
            self.fn = fn

        def activate(self, ctx):
            self.fn(ctx)
            return 1

        def update(self, ctx):
            return AST_ENABLE_ALWAYS

    class CASCHooks(UI_Hooks):
        def __init__(self):
            super(CASCHooks, self).__init__()
            self.handlers_created = False

        def finish_populating_tform_popup(self, form, popup):
            global CLAMAV_ICON, clamav_sig_creator_plugin

            if None == clamav_sig_creator_plugin:
                return

            if not self.handlers_created:
                self.init_actions()
                self.handlers_created = True

            #   Apply the right action to the popup menu
            tform_type = IDAW.get_tform_type(form)
            if BWN_DISASMS == tform_type:
                IDAW.attach_action_to_popup(form, popup, 'clamav:add_sig')

            elif BWN_STRINGS == tform_type:
                IDAW.attach_action_to_popup(form, popup, 'clamav:add_string')

            elif BWN_IMPORTS == tform_type:
                IDAW.attach_action_to_popup(form, popup, 'clamav:add_import')

        def init_actions(self):
            global CLAMAV_ICON, clamav_sig_creator_plugin

            add_sig_handler = CASCActionHandler(clamav_sig_creator_plugin.insert_asm_item)
            add_sig_action_desc = IDAW.action_desc_t('clamav:add_sig',
                                                'Add Assembly to ClamAV Sig Creator...',
                                                add_sig_handler,
                                                'Ctrl+`',
                                                'From current selection or selected basic block',
                                                CLAMAV_ICON)
            IDAW.register_action(add_sig_action_desc)

            strings_handler = CASCActionHandler(clamav_sig_creator_plugin.insert_string_item)
            strings_action_desc = IDAW.action_desc_t('clamav:add_string',
                                                'Add string to ClamAV Sig Creator',
                                                strings_handler,
                                                None,
                                                'Add current string as sub signature',
                                                CLAMAV_ICON)
            IDAW.register_action(strings_action_desc)

            import_handler = CASCActionHandler(clamav_sig_creator_plugin.insert_import_item)
            import_action_desc = IDAW.action_desc_t('clamav:add_import',
                                                'Add Import to ClamAV Sig Creator',
                                                import_handler,
                                                None,
                                                'Add current import as sub signature',
                                                CLAMAV_ICON)
            IDAW.register_action(import_action_desc)

    hooks = CASCHooks()
    hooks.hook()

except NameError:
    b_asm_sig_handler_loaded = False

#
#   Masking GUI component
#-------------------------------------------------------------------------------
class CASCMask(object):
    def __init__(self, bits):
        self.bits = bits
        self.gui = QtWidgets.QWidget()

    def get_masking(self):
        return []

    def set_masking(self, masking):
        pass

    def register_signals(self, apply_mask_func, custom_ui_func):
        pass

    def disable(self):
        pass

    def enable(self):
        pass

    def set_custom(self, checked):
        pass

    def custom_checked(self):
        pass

class IntelMask(CASCMask):
    def __init__(self, bits):
        super(IntelMask, self).__init__(bits)

        self.maskings = [('ESP Offsets', 'sp_mask'),
                        ('EBP Offsets', 'bp_mask'),
                        ('Call Offsets', 'call_mask'),
                        ('Jump Offsets', 'jmp_mask'),
                        ('Global Offsets', 'global_mask'),
                        ('Customize', 'custom_mask')]
        self.registers = [  ('EAX', 'eax_mask'), ('EBX', 'ebx_mask'),
                            ('ECX', 'ecx_mask'), ('EDX', 'edx_mask'),
                            ('ESI', 'esi_mask'), ('EDI', 'edi_mask')]
        if not is_32bit():
            self.registers = []

        self.gui = self._init_gui()

    def _init_gui(self):
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(2)

        mask_options = QtWidgets.QGroupBox()
        sizePolicy.setHeightForWidth(mask_options.sizePolicy().hasHeightForWidth())
        mask_options.setSizePolicy(sizePolicy)
        mask_options.setObjectName('mask_options')
        mask_options.setTitle('Mask Options')

        vbox_mask = QtWidgets.QVBoxLayout(mask_options)
        for text, name in self.maskings:
            checkbox = QtWidgets.QCheckBox(text, mask_options)
            checkbox.setObjectName(name)
            vbox_mask.addWidget(checkbox)

        vbox_mask.addStretch()

        if self.registers:
            #   Original Opcodes GUI Area
            reg_groupbox = QtWidgets.QGroupBox()
            sizePolicy.setHeightForWidth(reg_groupbox.sizePolicy().hasHeightForWidth())
            reg_groupbox.setSizePolicy(sizePolicy)
            reg_groupbox.setObjectName('reg_groupbox')
            reg_groupbox.setTitle('Mask Registers')
            hbox_reg = QtWidgets.QHBoxLayout(reg_groupbox)
            vbox_reg = QtWidgets.QVBoxLayout()
            vbox_reg.setContentsMargins(1, 1, 1, 1)
            hbox_reg.addLayout(vbox_reg)
            vbox_mask.addWidget(reg_groupbox)

            for text, name in self.registers:
                if self.registers.index((text, name)) == (len(self.registers)/2):
                    vbox_reg = QtWidgets.QVBoxLayout()
                    vbox_reg.setContentsMargins(1, 1, 1, 1)
                    hbox_reg.addLayout(vbox_reg)

                checkbox = QtWidgets.QCheckBox(text, mask_options)
                checkbox.setObjectName(name)
                vbox_reg.addWidget(checkbox)

        return mask_options

    def get_masking(self):
        checked = [x for x in self.get_non_custom_masks() if x.isChecked()]
        return [x.objectName().replace('_mask', '') for x in checked]

    def set_masking(self, maskings):
        checkboxes = [x[1] for x in self.maskings] + [x[1] for x in self.registers]
        for x in [self.gui.findChild(QtWidgets.QCheckBox, x) for x in checkboxes]:
            name = x.objectName().replace('_mask', '')
            if name in maskings:
                x.setChecked(True)

    def register_signals(self, apply_mask_func, custom_ui_func):
        checkboxes = [x[1] for x in self.maskings] + [x[1] for x in self.registers]
        objs = [self.gui.findChild(QtWidgets.QCheckBox, x) for x in checkboxes]

        for checkbox in objs:
            name = checkbox.objectName()
            if name.startswith('custom'):
                checkbox.stateChanged.connect(custom_ui_func)
            else:
                checkbox.stateChanged.connect(apply_mask_func)

    def disable(self):
        [x.setEnabled(False) for x in self.get_non_custom_masks()]

    def enable(self):
        [x.setEnabled(True) for x in self.get_non_custom_masks()]

    def get_non_custom_masks(self):
        checkboxes = [x[1] for x in self.maskings if not x[1].startswith('custom')]
        checkboxes += [x[1] for x in self.registers]
        return [self.gui.findChild(QtWidgets.QCheckBox, x) for x in checkboxes]

    def custom_checked(self):
        return self.get_custom_checkbox().isChecked()

    def set_custom(self, checked):
        checkbox = self.get_custom_checkbox()

        checkbox.blockSignals(True)
        checkbox.setChecked(checked)
        checkbox.blockSignals(False)

    def get_custom_checkbox(self):
        custom = [x[1] for x in self.maskings if x[1].startswith('custom')][0]
        return self.gui.findChild(QtWidgets.QCheckBox, custom)

#
#   Architecture parsers
#-------------------------------------------------------------------------------
class CASCParser(object):
    def __init__(self, bits):
        self.bits = bits

    def get_gui_layout(self):
        mask_options = QtWidgets.QGroupBox()
        sizePolicy.setHeightForWidth(mask_options.sizePolicy().hasHeightForWidth())
        mask_options.setSizePolicy(sizePolicy)
        mask_options.setObjectName('mask_options')
        mask_options.setTitle('Mask Options')
        vbox_mask = QtWidgets.QVBoxLayout(mask_options)

        raise mask_options

    def set_masking(self):
        pass

    def register_gui_signals(self, gui_obj, apply_mask_func, custom_ui_func):
        pass

    def setEnable(self, gui_obj, is_enabled=False):
        pass

    def mask_instruction(self, ea, maskings):
        instruction = IDAW.DecodeInstruction(ea)
        if not instruction:
            return ('db 0x{0:02}'.format(IDAW.Byte(ea)), ' '.join(['{:02x}'.format(IDAW.Byte(ea))]))

        size = IDAW.DecodeInstruction(ea).size
        original = ' '.join(['{:02x}'.format(IDAW.Byte(ea + i)) for i in xrange(size)])
        disassembly = IDAW.tag_remove(IDAW.generate_disasm_line(ea, 1))
        if ';' in disassembly:
            disassembly = disassembly[:disassembly.index(';')].rstrip()

        return (disassembly, original)

class IntelParser(CASCParser):
    prefixes = '^([\xf0\xf3\xf2\x2e\x36\x3e\x26\x64\x65\x66\x67]{1,4})'
    prefixes_x64 = '^((?:[\xf0\xf3\xf2\x2e\x36\x3e\x26\x64\x65\x66\x67]|\x0f(?:\x38|\x3a){0,1}){1,4})'

    prefix_required_modrm = [6, 8, 9, 0x0b, 0x0d] + range(0x14, 0x18) + \
                            [0x1f, 0x2c, 0x2d] + \
                            [0x40, 0x60, 0x61, 0x68, 0x6a] + \
                            range(0x6c, 0x70) + range(0x71, 0x77) + \
                            range(0x7c, 0x80) + \
                            [0xa3, 0xa4, 0xa5] + range(0xab, 0xb0) + \
                            [0xc2, 0xc3, 0xc8, 0xd4, 0xd5, 0xd7] + \
                            range(0xe0, 0xf0) + [0xf4] + range(0xf8, 0xfe)
    noprefix_nomodrm = [1] + range(0x50, 0x62) + range(0x90, 0x9a) + \
                        range(0xb0, 0xc0)

    two_opcodes = { 1 : range(0xc8, 0xd2) + [0xd5, 0xd6, 0xf8, 0xf9],
                    0xc6 : [0xf8], 0xc7 : [0xf8], 0xd4 : [0xa0], 0xd5 : [0xa0],
                    0xd8 : [0xc0, 0xc8, 0xd0, 0xd1, 0xd8, 0xd9, 0xe0, 0xe8,
                            0xf0, 0xf8],
                    0xd9 : [0xc0, 0xc8, 0xc9, 0xd0, 0xe0, 0xe1, 0xe4, 0xe5,
                            0xe8, 0xe9, 0xea, 0xec, 0xed, 0xee, 0xf0, 0xf1,
                            0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf9, 0xfa,
                            0xfb, 0xfc, 0xfd, 0xfe, 0xff],
                    0xda : [0xc0, 0xc8, 0xd0, 0xd8, 0xe9],
                    0xdb : [0xc0, 0xc8, 0xd0, 0xd8, 0xe2, 0xe3, 0xe8, 0xf0],
                    0xdc : [0xc0, 0xc8, 0xe0, 0xe8, 0xf0, 0xf8],
                    0xdd : [0xc0, 0xd0, 0xd8, 0xe0, 0xe1, 0xe8, 0xe9],
                    0xde : [0xc0, 0xc1, 0xc8, 0xc9, 0xd9, 0xe0, 0xe1, 0xe8,
                            0xe9, 0xf0, 0xf1, 0xf8, 0xf9],
                    0xdf : [0xe0, 0xe8, 0xf0],
                    0x0f : [0x34] + range(0x80,0x90) + [0xc8],
                    0xfa : [0xae]}

    three_opcodes = {0x9b : ['\xd3\xe3', '\xdb\xe2', '\xdf\xe0']}
    two_opcodes_modrm = {0x38 : range(0, 0x0c) + \
                                [0x10, 0x14, 0x15, 0x17, 0x1c, 0x1d, 0x1e] + \
                                range(0x20, 0x26) + [0x28, 0x29, 0x2a, 0x2b] + \
                                range(0x30, 0x42) + [0x82] + \
                                range(0xdb, 0xe0) + [0xf0, 0xf1],
                        0x3a : range(0x08, 0x10) + range(0x14, 0x18) + \
                                range(0x20, 0x23) + range(0x40, 0x45) + \
                                range(0x60, 0x64) + [0xdf],
                        0x9b : [0xd9, 0xdd],
                        0x0f : range(0, 4) + [0x06, 0x0d] + range(0x10, 0x19) + \
                                range(0x1f, 0x24) + [0x2a, 0x2b, 0x2d, 0x2e, 0x2f] + \
                                range(0x40, 0x50) + range(0x51, 0x6c) + \
                                range(0x70, 0x77) + [0x7f, 0xa3, 0xa4, 0xa5] + \
                                range(0xab, 0xb8) + range(0xba, 0xc8) + \
                                range(0xd2, 0xf0) + range(0x90, 0xa0) + \
                                range(0xf1, 0xf5) + range(0xf6, 0xff)}

    no_modrm = [0x04, 0x05, 0x07, 0x0c, 0x0e, 0x1c, 0x1d, 0x1e, 0x24, 0x25,
                0x27, 0x34, 0x35, 0x37, 0x3c, 0x3d, 0x3f] + range(0x40, 0x50) + \
                [0x77, 0x78, 0x79, 0x7a, 0x7b, 0x82] + range(0x91, 0xa3) + \
                [0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xc9, 0xca, 0xcb, 0xcc, 0xcd,
                0xce, 0xcf, 0xf5]

    reg_variants = {'eax' : re.compile('([^a-zA-Z_@]|^)(e{0,1}a(?:x|h|l))'),
                    'ebx' : re.compile('([^a-zA-Z_@]|^)(e{0,1}b(?:x|h|l))'),
                    'ecx' : re.compile('([^a-zA-Z_@]|^)(e{0,1}c(?:x|h|l))'),
                    'edx' : re.compile('([^a-zA-Z_@]|^)(e{0,1}d(?:x|h|l))'),
                    'esi' : re.compile('([^a-zA-Z_@]|^)(e{0,1}sil{0,1})'),
                    'edi' : re.compile('([^a-zA-Z_@]|^)(e{0,1}dil{0,1})')}

    reg_exceptions = [(0x0f, 0xc8), 0x48, 0x40, 0xb0, 0xb8, 0x58, 0x50, 0x90]

    bin2reg = { 0b000 : ['eax', 'ax', 'al', 'mmo', 'xmmo'],
                0b001 : ['ecx', 'cx', 'cl', 'mm1', 'xmm1'],
                0b010 : ['edx', 'dx', 'dl', 'mm2', 'xmm2'],
                0b011 : ['ebx', 'bx', 'bl', 'mm3', 'xmm3'],
                0b100 : ['ah', 'mm4', 'xmm4'],
                0b101 : ['ch', 'mm5', 'xmm5'],
                0b110 : ['esi', 'si', 'dh', 'mm6', 'xmm6'],
                0b111 : ['edi', 'di', 'bh', 'mm7', 'xmm7']}

    def __init__(self, bits):
        super(IntelParser, self).__init__(bits)

    def mask_instruction(self, ea, maskings):
        instr = self.parse_instruction(ea)
        m_disassembly = ''
        default = (instr['disassembly'], ' '.join(instr['bytes']))

        if ('prefix' not in instr) or ('opcode' not in instr):
            return default

        m_opcodes = [(instr['prefix'][0] + instr['opcode'][0]).encode('hex')]


        #   Call instructions
        #--------------------
        if ((instr['opcode'][1] == 'call') and ('call' in maskings)
            and (len(instr['imm'][0] + instr['disp'][0]) > 0)):
            #   Mask off absolute/relative call offsets
            masked_imm = '{{{}}}'.format(len(instr['imm'][0]))
            if len(instr['imm'][0]) == 0:
                masked_imm = '{{{}}}'.format(len(instr['disp'][0]))

            if len(instr['modr/m']) > 1:
                #   Absolute call
                m_opcodes += [instr['modr/m'][0].encode('hex'), masked_imm]
                return ('call    <Absolute Offset>', ' '.join(m_opcodes))

            #   Relative call
            m_opcodes.append(masked_imm)
            return ('call    <Relative Offset>', ' '.join(m_opcodes))

        #   Jcc and JMP instructions
        #---------------------------
        if (instr['opcode'][1].startswith('j')) and ('jmp' in maskings):
            #   Mask off relative jump offsets
            if len(m_opcodes[0]) > 1:
                m_opcodes =  [x.encode('hex') for x in m_opcodes[0].decode('hex')]

            if len(instr['imm'][0]) == 1:
                m_opcodes.append('??')
            else:
                m_opcodes.append('{{{}}}'.format(len(instr['imm'][0])))
            return ('{: <8}<Jump Offset>'.format(instr['opcode'][1]), ' '.join(m_opcodes))

        #-----------------------------------------------------------------
        #   Below multiple maskings can be applied to the same instruction
        #-----------------------------------------------------------------

        #   Prepare structure for masking operands and details.
        opcodes_order = ['prefix', 'opcode', 'modr/m', 'sib', 'disp', 'imm']
        current_opcodes = [instr[x][0] for x in opcodes_order]

        mnem = IDAW.GetMnem(ea)
        operands = default[0][default[0].index(mnem)+len(mnem):].split(',')
        operands = [x.lstrip() for x in operands]
        prefix = ''
        if len(instr['prefix']) > 1:
            prefix = instr['prefix'][1]
        current_disassembly = [prefix, instr['opcode'][1]] + operands
        original_disassembly = current_disassembly
        original_opcodes = current_opcodes

        try:
            #   Global offset instructions
            #   A little complicated to do since it could just be a hard coded value
            #-----------------------------------------------------------------------
            if ('global' in maskings) and ((len(instr['imm']) + len(instr['disp'])) > 2):
                #   Assuming the value is a global offset if it exists within a
                #   segment
                #   Since VirtualAlloc uses 0x400000 and many PEs are based at that
                #   address we are going to exclude it
                operand_masked = False
                original_operand = -1
                operand_index = 0
                if (len(instr['imm']) > 1):
                    offset = int(instr['imm'][1][2:].replace('L', ''), 16)
                    if (IDAW.getseg(offset) is not None) and (offset != 0x400000):
                        imm = current_opcodes[5]
                        original_operand = imm
                        operand_index = 5
                        if len(imm) == 1:
                            current_opcodes[5] = '??'
                        else:
                            current_opcodes[5] = '{{{}}}'.format(len(imm))

                        for i in xrange(2, len(current_disassembly)):
                            if (('{:x}'.format(offset) in current_disassembly[i].lower())
                                or (IDAW.LocByName(current_disassembly[i]) == offset)):
                                operand_masked = True
                                current_disassembly[i] = '<Global Offset>'

                if (len(instr['disp']) > 1):
                    offset = int(instr['disp'][1][2:].replace('L', ''), 16)
                    if (IDAW.getseg(offset) is not None) and (offset != 0x400000):
                        imm = current_opcodes[4]
                        original_operand = imm
                        operand_index = 4
                        if len(imm) == 1:
                            current_opcodes[4] = '??'
                        else:
                            current_opcodes[4] = '{{{}}}'.format(len(imm))

                        for i in xrange(2, len(current_disassembly)):
                            if (('{:x}'.format(offset) in current_disassembly[i].lower())
                                or (IDAW.LocByName(current_disassembly[i]) == offset)):
                                operand_masked = True
                                current_disassembly[i] = '<Global Offset>'

                if (not operand_masked) and (original_operand != -1):
                    current_opcodes[operand_index] = original_operand


            #   SP and BP displacement masking
            #-----------------------------------------------------------------------
            if len(instr['disp']) > 0:
                if len(instr['modr/m']) > 1:
                    #   Check the displacement value is from an esp offset
                    modrm = instr['modr/m'][1]
                    if 0b01 <= modrm['mod'] <= 0b10:
                        mask_disp = ''
                        #   EBP offset
                        if ('bp' in maskings) and (modrm['rm'] == 0b101):
                            mask_disp = 'bp'

                        #   ESP offset
                        if (('sp' in maskings) and (modrm['rm'] == 0b100)
                            and (len(instr['sib']) > 1)):
                            sib = instr['sib'][1]
                            if sib['base'] == 0b100:
                                mask_disp = 'sp'

                        if mask_disp:
                            for i in xrange(2, len(current_disassembly)):
                                x = current_disassembly[i]
                                mask_re = '{}+[^\]]+'.format(mask_disp)
                                value = '{0}+<{1} Offset>'.format(mask_disp, mask_disp.upper())
                                current_disassembly[i] = self.mask_operand(x, mask_re, value)

                            disp = current_opcodes[4]
                            if len(disp) == 1:
                                current_opcodes[4] = '??'
                            else:
                                current_opcodes[4] = '{{{}}}'.format(len(disp))

            #   Register Masking
            #-----------------------------------------------------------------------
            regs = {'eax', 'ebx', 'ecx', 'edx', 'esi', 'edi'}.intersection(maskings)
            masked_regs = []
            for reg in regs:
                for i in xrange(2, len(current_disassembly)):
                    operand = current_disassembly[i]
                    if self.reg_variants[reg].search(operand):
                        current_disassembly[i] = self.reg_variants[reg].sub('\\1<Reg Masked>', operand)
                        masked_reg = self.reg_variants[reg].search(operand).groups()[1]

                        opcode_masked = False
                        current_modrm = current_opcodes[2]
                        original_modrm = instr['modr/m'][0]
                        if len(original_modrm) == 1:
                            modrm = instr['modr/m'][1]

                            minreg = (modrm['mod'] << 6) | 0 | modrm['rm']
                            minrm = (modrm['mod'] << 6) | (modrm['reg'] << 3) | 0

                            if (operand == masked_reg) and (masked_reg in self.bin2reg[modrm['reg']]):
                                opcode_masked = True
                                if len(current_modrm) == 1:
                                    values = [minreg + (x << 3) for x in range(8)]
                                    current_opcodes[2] = ['{:02x}'.format(x) for x in values]
                                else:
                                    current_opcodes[2] = list(set(['{:01x}?'.format(x) for x in range((modrm['mod'] << 2), (modrm['mod'] << 2) + 4)]))

                            elif (modrm['rm'] == 0b100) and (modrm['mod'] in range(0, 3)):
                                #   The instruction requires the SIB bytes
                                if len(instr['sib'][0]) == 1:
                                    sib = instr['sib'][1]

                                    minindex = (sib['ss'] << 6) | 0 | sib['base']
                                    minbase = (sib['ss'] << 6) | (sib['index'] << 3) | 0
                                    current_sib = current_opcodes[3]
                                    if (sib['index'] != 0b100) and (masked_reg in self.bin2reg[sib['index']]):
                                        opcode_masked = True
                                        if len(current_sib) == 1:
                                            values = [minindex + (x << 3) for x in range(8)]
                                            current_opcodes[3] = ['{:02x}'.format(x) for x in values]
                                        else:
                                            current_opcodes[3] = list(set(['{:01x}?'.format(x) for x in range((sib['ss'] << 2), (sib['ss'] << 2) + 4)]))

                                    elif masked_reg in self.bin2reg[sib['base']]:
                                        opcode_masked = True
                                        if len(current_sib) == 1:
                                            values = [minbase + x for x in range(8)]
                                            current_opcodes[3] = ['{:02x}'.format(x) for x in values]
                                        else:
                                            current_opcodes[3] = list(sorted(set(['{}?'.format(x[0]) for x in current_opcodes[3]])))

                            elif (not (modrm['rm'] == 0b100 and modrm['mod'] == 0b11)
                                    and (masked_reg in self.bin2reg[modrm['rm']])):
                                opcode_masked = True
                                if len(current_modrm) == 1:
                                    values = [minrm + x for x in range(8)]
                                    current_opcodes[2] = ['{:02x}'.format(x) for x in values]
                                else:
                                    current_opcodes[2] = list(sorted(set(['{}?'.format(x[0]) for x in current_opcodes[2]])))

                        else:
                            binreg = [x for x in self.bin2reg if masked_reg in self.bin2reg[x]][0]

                            #   Values are part of the operand
                            opcode = ord(instr['opcode'][0][-1]) - binreg
                            if ((opcode in self.reg_exceptions)
                                or ((len(instr['opcode'][0]) == 2)
                                    and (opcode == self.reg_exceptions[0][1])
                                    and (ord(instr['opcode'][0][0]) == self.reg_exceptions[0][0]))):
                                opcode_masked = True
                                values = range(opcode, opcode + 8)
                                if len(instr['opcode'][0]) == 2:
                                    current_opcodes[1] = '{0:02x}({1})'.format(ord(instr['opcode'][0][0]),
                                                                                '|'.join(['{:02x}'.format(x) for x in values]))
                                else:
                                    current_opcodes[1] = ['{:02x}'.format(x) for x in values]

                        if not opcode_masked:
                            #   Opcode couldn't be masked, revert masked disassembly
                            current_disassembly[i] = operand

        except:
            print '=' * 40
            print ('[CASC] Unsupported masking on instruction. Please open an '
                    'issue in the git repo with the below information:')
            ddebug = [x for x in original_disassembly if x]
            ddebug = '{0: <8}{1}'.format(ddebug[0], ', '.join(ddebug[1:]))
            print '  disassembly: {}'.format(ddebug)
            print '  opcodes: {}'.format(' '.join([x.encode('hex') for x in original_opcodes if x]))
            pprint(instr)
            print '=' * 40

            raise


        #   Register masking exceptions
        #   Instructions that leavage a base opcode value and increment it to
        #   get the right register
        #-----------------------------------------------------------------------

        #   Customize
        #   Clean up opcodes and disassembly for display to user
        current_disassembly = [x for x in current_disassembly if x]
        current_disassembly = '{0: <8}{1}'.format(current_disassembly[0], ', '.join(current_disassembly[1:]))
        opcodes = []
        for x in [x for x in current_opcodes if x]:
            if type(x) == list:
                opcodes += ['({})'.format('|'.join(x))]
            elif not re.search('(\?\?|\{.+\}|\[.+\])', x):
                opcodes += [y.encode('hex') for y in x]
            else:
                opcodes.append(x)

        return (current_disassembly, ' '.join(opcodes))

    def parse_instruction(self, ea):
        instruction = IDAW.DecodeInstruction(ea)
        if not instruction:
            return {'address' : ea, 'bytes' : ['{:02x}'.format(IDAW.Byte(ea))],
                    'disassembly' : 'db 0x{0:02}'.format(IDAW.Byte(ea))}
        size = IDAW.DecodeInstruction(ea).size
        original = ['{:02x}'.format(IDAW.Byte(ea + i)) for i in xrange(size)]
        disassembly = IDAW.tag_remove(IDAW.generate_disasm_line(ea, 1))
        if ';' in disassembly:
            disassembly = disassembly[:disassembly.index(';')].rstrip()
        instr = {   'address': ea,
                    'bytes'  : original,
                    'disassembly' : disassembly,
                    'prefix' : ['', ],
                    'opcode' : ['', ],
                    'modr/m' : ['', ],
                    'sib'    : ['', ],
                    'disp'   : ['', ],
                    'imm'    : ['', ],
        }
        data = ''.join(original).decode('hex')

        try:
            #   Look for prefix
            prefix = ['', ]
            pre_match = re.match(self.prefixes, data)
            prefix_str = disassembly[0:disassembly.index(IDAW.GetMnem(ea))]
            if pre_match:
                prefix = [pre_match.groups()[0], prefix_str]

            elif is_64bit():
                pre_match = re.match(self.prefixes_x64, data)
                if pre_match:
                    prefix = [pre_match.groups()[0], prefix_str]

            instr['prefix'] = prefix
            data = data[len(prefix[0]):]

            #   Look for opcodes
            opcodes = [data[0], IDAW.GetMnem(ea)]
            opcode = ord(opcodes[0])
            if ((opcode in self.two_opcodes)
                and (ord(data[1]) in self.two_opcodes[opcode])):
                opcodes[0] = data[0:2]
            elif ((opcode in self.three_opcodes)
                and (data[1:3] in self.three_opcodes[opcode])):
                opcodes[0] = data[0:3]
            elif ((opcode in self.two_opcodes_modrm)
                and (ord(data[1]) in self.two_opcodes_modrm[opcode])):
                opcodes[0] = data[0:2]

            instr['opcode'] = opcodes
            data = data[len(opcodes[0]):]

            #   Look for Mod R/M byte
            modrm = ['', ]
            getmodrm = False
            if (ord(opcodes[0][0]) in self.prefix_required_modrm) and (len(prefix[0]) > 0):
                getmodrm = True
            elif (ord(opcodes[0][0]) in self.prefix_required_modrm) and (len(prefix[0]) == 0):
                getmodrm = False
            elif (opcodes[0][0] == '\x01') and (len(opcodes[0]) == 1):
                getmodrm = True
            elif (ord(opcodes[0][0]) in self.noprefix_nomodrm) and (len(prefix[0]) == 0):
                getmodrm = False
            elif (len(opcodes[0]) == 1) and (ord(opcodes[0]) not in self.no_modrm):
                getmodrm = True
            elif ((len(opcodes[0]) > 1)
                and (ord(opcodes[0][0]) in self.two_opcodes_modrm)
                and (ord(opcodes[0][1]) in self.two_opcodes_modrm[ord(opcodes[0][0])])):
                getmodrm = True

            if getmodrm and (len(data) > 0):
                modrm[0] = data[0]
                data = data[1:]
            instr['modr/m'] = modrm
            if len(modrm[0]) == 1:
                value = ord(modrm[0])
                mod = (value >> 6) & 3
                reg = (value >> 3) & 7  #operand 1
                rm = value & 7          #operand 2
                instr['modr/m'].append({'mod':mod, 'reg':reg, 'rm':rm})

                #   Find out the Displacement size if it has one
                #   Done for x86, 16bit would require some additional code
                displacement = 0
                if mod == 0b01:
                    displacement = 8
                elif (mod == 0b10) or (mod == 0 and rm == 0b101):
                    displacement = 32

                #   Find out if there is a SIB byte
                if (mod < 0b11) and (rm == 0b100):
                    sib = data[0]
                    value = ord(sib)
                    ss = (value >> 6) & 3       #scale index: 2**ss
                    index = (value >> 3) & 7    #register scaled: e?x * (2**ss)
                    base = value & 7            #
                    instr['sib'] = [sib, {'ss':ss, 'index':index, 'base':base}]
                    data = data[1:]

                #   Done for x86, 16bit would require some additional code
                if displacement > 0:
                    disp = data[:displacement/8]
                    disp_str = '<I'
                    if displacement == 8:
                        disp_str = 'B'
                    instr['disp'] = [disp, hex(struct.unpack(disp_str, disp)[0])]
                    data = data[displacement/8:]

            imm_map = {2 : '<H', 4 : '<I'}
            if len(data) > 0:
                if len(data) in imm_map:
                    value = hex(struct.unpack(imm_map[len(data)], data)[0])
                    instr['imm'] = [data, ]
                elif len(data) == 1:
                    value = hex(ord(data))
                else:
                    print '[Error]: immediate value is not 1, 2, or 4 bytes'

                instr['imm'] = [data, value]
        except:
            print '=' * 40
            print ('[CASC] Unsupported instruction parsing. Please open an '
                    'issue in the git repo with the below information:')
            print ( '  EA: 0x{0:x}\n'
                    '  Instruction Size: {1}\n'
                    '  Disassembly: {2}\n'
                    '  Opcodes: {3}').format(ea, size, disassembly, ' '.join(original))
            pprint(instr)
            print '=' * 40

            raise

        return instr

    def to_hex(self, x):
        if len(x) > 1:
            if ('{' in x and '}' in x) or (x == '??'):
                return x
        return x.encode('hex')

    def mask_operand(self, operand, to_mask, mask_value):
        if re.search(operand, to_mask):
            return re.sub(to_mask, mask_value, operand)
        else:
            return operand

#   Disassembly and Opcodes Classes
#-------------------------------------------------------------------------------
class Assembly(object):
    def __init__(self, start_ea, end_ea):
        self.original_data = []
        self.opcodes = []
        self.mnemonics = []
        self.start_ea = start_ea
        self.end_ea = end_ea
        self.mask_options = []
        self.custom = None
        self.parser = get_parser()

        self.starts_in_function = False
        if IDAW.get_func(start_ea):
            self.starts_in_function = True

        ea = start_ea

        if (BADADDR == start_ea) or (BADADDR == end_ea):
            return

        while ea < end_ea:
            #   Check if it is in a function, if so it can be decoded without
            #   any issues arising, if not, it should be added as data bytes
            instr = IDAW.DecodeInstruction(ea)
            if instr:
                #instr = IDAW.DecodeInstruction(ea)
                self.original_data.append(instr)

                disassembly = IDAW.tag_remove(IDAW.generate_disasm_line(ea, 1))
                if ';' in disassembly:
                    disassembly = disassembly[:disassembly.index(';')].rstrip()

                self.mnemonics.append(disassembly)
                data = ['{0:02x}'.format(IDAW.Byte(ea + i)) for i in xrange(instr.size)]
                self.opcodes.append(' '.join(data))

                ea += instr.size

            else:
                data_byte = IDAW.Byte(ea)
                self.original_data.append(data_byte)
                self.mnemonics.append('db {0:02x}h'.format(data_byte))
                self.opcodes.append('{0:02x}'.format(data_byte))
                ea += 1

        self.original_opcodes = self.opcodes
        self.original_mnemonics = self.mnemonics

    def get_original_opcode_list(self):
        return self.original_opcodes

    def get_opcode_list(self):
        return self.opcodes

    def get_mnemonics_list(self):
        return self.mnemonics

    def set_opcode_list(self, new_opcodes, is_custom=False):
        self.opcodes = new_opcodes

        if is_custom:
            self.custom = new_opcodes

    def opcode_to_signatrue(self):
        return ''.join(self.opcodes).replace(' ', '')

    def mask_opcodes(self, mask_options):
        self.mask_options = mask_options
        if 'custom' in mask_options:
            return

        self.custom = None
        opcodes = []
        mnemonics = []

        for instr in self.original_data:
            if not isinstance(instr, insn_t):
                #   The data is just a data byte
                opcodes.append('{0:02x}'.format(instr))

            else:
                data = self.parser.mask_instruction(instr.ea, mask_options)
                if not data:
                    print '[CASC] Error: mask_instruction returned None'
                    continue

                disassembly, instr_opcodes = data
                mnemonics.append(disassembly)
                opcodes.append(instr_opcodes)

        self.mnemonics = mnemonics
        self.opcodes = self.fixup_opcodes(opcodes)

    def fixup_opcodes(self, opcodes):
        sig_format = (  '('
                          '[\da-fA-F]{2}|'
                          '[\da-fA-F]\?|'
                          '\?[\da-fA-F]|'
                          '\?\?|'
                          '\{(?:\d+|\-\d+|\d+\-|(?:\d+)\-(?:\d+))\}|'
                          '\*|'
                          '(?:!|)\((?:[\da-fA-F\?]{2})+(?:\|(?:[\da-fA-F\?]{2})+)+\)|'
                          '\((?:B|L|W)\)|'
                          '\[\d+\-\d+\]|'
                          '\s+'
                      '?)')

        _opcodes = [[x for x in re.findall(sig_format, line) if not re.match(r'^\s+$', x)] for line in opcodes]
        for i in xrange(len(_opcodes)):
            cur_data = _opcodes[i]
            prev = -1
            prev_data = []
            next = -1
            next_data = []

            if (i - 1) >= 0:
                prev = i - 1
                prev_data = _opcodes[prev]

            if (i + 1) < len(_opcodes):
                next = i + 1
                next_data = _opcodes[next]

            #   Check current opcodes
            data = prev_data + cur_data + next_data
            for k in xrange(len(data) - len(next_data)):
                replace_index = -1
                if re.match('^\{\d+\}$', data[k]):
                    replace = int(re.match('\{(\d+)\}', data[k]).group(1))
                    replace = ' '.join(replace * ['??'])
                    #   Ensure that there are two bytes before and after
                    if (k-2 < 0) and  (k+2 >= len(data)):
                        replace_index = k

                    else:
                        #   Check bytes before for valid hex strings
                        before_check = 0
                        for j in  list({max(k-2, 0), max(k-1, 0)}):
                            if re.match('[\da-fA-F]{2}', data[j]):
                                before_check += 1

                        #   Check bytes after for valid hex strings
                        after_check = 0
                        for j in [k+1, k+2]:
                            if j >= len(data):
                                continue
                            if re.match('[\da-fA-F]{2}', data[j]):
                                after_check += 1

                        if 2 not in [before_check, after_check]:
                            replace_index = k

                if replace_index != -1:
                    if k < len(prev_data):
                        _opcodes[prev][k] = replace
                    elif k < (len(prev_data) + len(cur_data)):
                        _opcodes[i][k - len(prev_data)] = replace
                    else:
                        _opcodes[next][k - len(prev_data) - len(cur_data)] = replace

        return [' '.join(x) for x in _opcodes]

    def mask_opcodes_tuple(self, options):
        self.mask_options = options
        self.mask_opcodes(self.mask_options)

    def get_save_data_tuple(self):
        return (self.start_ea, self.end_ea, self.mask_options, self.custom)

    @staticmethod
    def sub_signature_string(opcodes):
        return ''.join(opcodes).replace(' ', '')

class MiscAssembly(Assembly):
    def __init__(self, data):
        self.mnemonics = None
        self.opcodes= data.split('\n')

    def mask_opcodes(mask_options):
        pass

    def set_opcode_list(self, new_opcodes):
        self.opcodes = new_opcodes

    def get_save_data_tuple(self):
        return (None, None, None, self.get_opcode_list())

class YaraScanner():
    def __init__(self, callback):
        self.memory = None
        self.offsets = None
        self.callback = callback

    def _get_memory(self):
        if self.memory is None:
            memory = []
            offsets = []
            for n in range(IDAW.get_segm_qty()):
                seg = IDAW.getnseg(n)
                if not seg:
                    continue
                offset = len(memory)
                memory += [chr(IDAW.Byte(ea)) for ea in xrange(seg.startEA, seg.endEA)]
                offsets.append({"address": seg.startEA,
                                "offset": offset,
                                "size": len(memory) - offset})
            self.memory = "".join(memory)
            self.offsets = offsets
        return self.memory, self.offsets

    def _offset_to_address(self, offset):
        if self.offsets is None:
            self._get_memory()
        for offs in self.offsets:
            if offs["offset"] <= offset and offset < offs["offset"] + offs["size"]:
                return offs["address"] + offset - offs["offset"]
        return None

    def _address_to_offset(self, address):
        if self.offsets is None:
            self._get_memory()
        for offs in self.offsets:
            if offs["address"] <= address and address < offs["address"] + offs["size"]:
                return offs["offset"] + address - offs["address"]
        return None

    def scan(self, rule):
        memory, offsets = self._get_memory()
        rule.match(data = memory, callback = self._callback)

    def compile(self, rule):
        try:
            return yara.compile(source = rule)
        except yara.SyntaxError as err:
            print_console(err)
            print_console("===============")
            print_console(rule)
            print_console("===============")

    def _ea_to_yara_offset(self, ea):
        yara_offset = self._address_to_offset(ea)
        if yara_offset is None:
            raise RuntimeError("Cannot find yara offset for ea 0x%x" % ea)
        return yara_offset

    def _file_offset_to_yara_offset(self, file_offset):
        ea = get_fileregion_ea(file_offset)
        if ea == BADADDR:
            raise RuntimeError("Cannot find ea for absolute file offset %d" % file_offset)
        return self._ea_to_yara_offset(ea)

    def _clamav_offset_to_yara(self, offset, rulename):
        if isinstance(offset, AnyOffset):
            return "true"
        if isinstance(offset, AbsoluteOffset):
            if offset.start == 0 and offset.end is None:
                return "$%s" % rulename
            elif offset.end is None:
                return "$%s at %d" % (rulename, self._file_offset_to_yara_offset(offset.start))
            else:
                start = self._file_offset_to_yara_offset(offset.offset)
                end = self._file_offset_to_yara_offset(offset.offset + offset.shift)
                return "$%s in (%d..%d)" % (rulename, start, end)
        elif isinstance(offset, EPRelativeOffset):
            ep_ea = next(idautils.Entries())[2]
            start = self._ea_to_yara_offset(ep_ea + offset.offset)

            if offset.shift is None:
                return "$%s at %d" % ( rulename, start)
            else:
                end = self._ea_to_yara_offset(ep_ea + offset.offset + offset.shift)
                return "$%s in (%d..%d)" % \
                    (rulename, start, end)
        elif isinstance(offset, InSectionOffset):
            segments = list(Segments())
            #TODO: Segments in IDA Pro are probably not exactly the same thing as
            # what ClamAV refers to as sections ...
            start = self._ea_to_yara_offset(SegStart(segments[offset.section]))
            end = self._ea_to_yara_offset(SegEnd(segments[offset.section]))
            return "$%s in (%d..%d)" % (start, end)
        elif isinstance(offset, SectionRelativeOffset):
            segments = list(Segments())
            start = self._ea_to_yara_offset(SegStart(segments[offset.section]) + offset.offset)
            if offset.shift is None:
                return "$%s at %d" % start
            else:
                end = self._ea_to_yara_offset(SegStart(segments[offset.section]) + offset.offset + offset.shift)
                return "$%s in (%d..%d)" % (start, end)
        else:
            raise NotImplementedError("Offset type %s is not implemented" % offset.__class__.__name__)

    def convert(self, clamav_signature):
        return convert_to_yara(clamav_signature, self._clamav_offset_to_yara)

    def _callback(self, data):
        if data["matches"]:
            strings = [
                {"ea": self._offset_to_address(x[0]),
                 "data": x[2],
                 "identifier": x[1]} for  x in data["strings"]]
            self.callback(strings)
        return yara.CALLBACK_ABORT

#   Dialog GUI Logic
#
#   These dialogs should have no direct interactions with IDA, allowing it to
#   be pulled out to other applications or replaced in the future.
#-------------------------------------------------------------------------------
class AsmSig_Dialog(object):
    def __init__(self):
        self.mask = get_gui()

    def setupUi(self, Dialog):
        Dialog.setObjectName('Dialog')
        Dialog.setWindowIcon(get_clamav_icon())
        Dialog.setWindowTitle('Assembly to Signature')
        Dialog.resize(932, 387)

        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(2)

        if IDA_SDK_VERSION <= 680:
            line_wrap = QtWidgets.QPlainTextEdit.LineWrapMode.NoWrap
        else:
            line_wrap = QtWidgets.QPlainTextEdit.NoWrap

        #   Original Opcodes GUI Area
        self._opcode_groupbox = QtWidgets.QGroupBox()
        sizePolicy.setHeightForWidth(self._opcode_groupbox.sizePolicy().hasHeightForWidth())
        self._opcode_groupbox.setSizePolicy(sizePolicy)
        self._opcode_groupbox.setFixedWidth(200)
        self._opcode_groupbox.setObjectName('_opcode_groupbox')
        self._opcode_groupbox.setTitle('Original Opcodes')
        self._opcodes = QtWidgets.QPlainTextEdit(self._opcode_groupbox)
        self._opcodes.setReadOnly(True)
        self._opcodes.setLineWrapMode(line_wrap)
        font = self._opcodes.document().defaultFont()
        font.setPointSize(12)
        font.setFamily('fixedsys,Liberation Mono')
        self._opcodes.document().setDefaultFont(font)
        self._vbox_opcodes = QtWidgets.QVBoxLayout(self._opcode_groupbox)
        self._vbox_opcodes.setContentsMargins(1, 1, 1, 1)
        self._vbox_opcodes.addWidget(self._opcodes)
        self._scrollbar_opcodes = self._opcodes.verticalScrollBar()

        #   Opcodes GUI Area
        self.opcode_groupbox = QtWidgets.QGroupBox()
        sizePolicy.setHeightForWidth(self.opcode_groupbox.sizePolicy().hasHeightForWidth())
        self.opcode_groupbox.setSizePolicy(sizePolicy)
        self.opcode_groupbox.setFixedWidth(200)
        self.opcode_groupbox.setObjectName('opcode_groupbox')
        self.opcode_groupbox.setTitle('Opcodes')
        self.opcodes = QtWidgets.QPlainTextEdit(self.opcode_groupbox)
        self.opcodes.setReadOnly(True)
        self.opcodes.setLineWrapMode(line_wrap)
        self.opcodes.document().setDefaultFont(font)
        self.vbox_opcodes = QtWidgets.QVBoxLayout(self.opcode_groupbox)
        self.vbox_opcodes.setContentsMargins(1, 1, 1, 1)
        self.vbox_opcodes.addWidget(self.opcodes)
        self.scrollbar_opcodes = self.opcodes.verticalScrollBar()

        #   Assembly GUI Area
        self.asm_groupbox = QtWidgets.QGroupBox()
        sizePolicy.setHeightForWidth(self.asm_groupbox.sizePolicy().hasHeightForWidth())
        self.asm_groupbox.setSizePolicy(sizePolicy)
        self.asm_groupbox.setObjectName('asm_groupbox')
        self.asm_groupbox.setTitle('Assembly')
        self.asm = QtWidgets.QPlainTextEdit(self.asm_groupbox)
        self.asm.setReadOnly(True)
        self.asm.setLineWrapMode(line_wrap)
        self.asm.document().setDefaultFont(font)
        self.vbox_asm = QtWidgets.QVBoxLayout(self.asm_groupbox)
        self.vbox_asm.setContentsMargins(1, 1, 1, 1)
        self.vbox_asm.addWidget(self.asm)

        #   Masking Options Area
        self.mask_options = self.mask.gui

        #   Top Horizontal Layout:  |  Opcodes | Assembly | Masking Options |
        self.hbox_top_data = QtWidgets.QHBoxLayout()
        self.hbox_top_data.setObjectName('hbox_top_data')
        self.hbox_top_data.addWidget(self._opcode_groupbox)
        self.hbox_top_data.addWidget(self.opcode_groupbox)
        self.hbox_top_data.addWidget(self.asm_groupbox)
        self.scrollbar_asm = self.asm.verticalScrollBar()
        self.hbox_top_data.addWidget(self.mask_options)

        #   Analyst's Notes Area
        self.notes = QtWidgets.QPlainTextEdit()
        self.notes.document().setDefaultFont(font)
        self.notes.setTabChangesFocus(True)
        self.notes.setFixedHeight(100)

        #   Bottom Horizontal Layout: | Error Msg | OK Button | Cancel Button |
        error_font = QtGui.QFont()
        error_font.setBold(True)
        self.error_msg = QtWidgets.QLabel('')
        self.error_msg.setFont(error_font)
        self.ok_button = QtWidgets.QPushButton('OK')
        self.ok_button.setFixedWidth(85)
        self.cancel_button = QtWidgets.QPushButton('Cancel')
        self.cancel_button.setFixedWidth(100)
        self.hbox_bottom = QtWidgets.QHBoxLayout()
        self.hbox_bottom.addWidget(self.error_msg)
        self.hbox_bottom.addWidget(self.ok_button)
        self.hbox_bottom.addWidget(self.cancel_button)

        #   Vertical Layout
        self.vbox_outer = QtWidgets.QVBoxLayout(Dialog)
        self.vbox_outer.setObjectName('vbox_outer')
        self.vbox_outer.addLayout(self.hbox_top_data)
        self.vbox_outer.addWidget(QtWidgets.QLabel('Notes:'))
        self.vbox_outer.addWidget(self.notes)
        self.vbox_outer.addLayout(self.hbox_bottom)

        #   Signal Handling
        self.ok_button.clicked.connect(Dialog.ok_button_callback)
        self.cancel_button.clicked.connect(Dialog.reject)
        self._scrollbar_opcodes.valueChanged.connect(Dialog.sync_scrolls)
        self.scrollbar_opcodes.valueChanged.connect(Dialog.sync_scrolls)
        self.scrollbar_asm.valueChanged.connect(Dialog.sync_scrolls)
        self.mask.register_signals( Dialog.apply_mask,
                                    Dialog.toggle_custom_ui)

class MiscSig_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName('Dialog')
        Dialog.setWindowIcon(get_clamav_icon())
        Dialog.setWindowTitle('Create Custom ClamAV Sub Signature')
        Dialog.resize(532, 287)

        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(2)

        #   ClamAV Sub Signature GUI Area
        self.sig_groupbox = QtWidgets.QGroupBox()
        sizePolicy.setHeightForWidth(self.sig_groupbox.sizePolicy().hasHeightForWidth())
        self.sig_groupbox.setSizePolicy(sizePolicy)
        self.sig_groupbox.setObjectName('sig_groupbox')
        self.sig_groupbox.setTitle('ClamAV Sub Signature')
        self.sub_signature = QtWidgets.QPlainTextEdit(self.sig_groupbox)
        font = self.sub_signature.document().defaultFont()
        font.setPointSize(12)
        font.setFamily('fixedsys')
        self.sub_signature.document().setDefaultFont(font)
        self.sub_signature.setTabChangesFocus(True)
        self.vbox_sub_signature = QtWidgets.QVBoxLayout(self.sig_groupbox)
        self.vbox_sub_signature.setContentsMargins(1, 1, 1, 1)
        self.vbox_sub_signature.addWidget(self.sub_signature)

        #   Middle Horizontal Layout:  |  Notes Label | View As Combo box |
        self.view_as_combobox = QtWidgets.QComboBox()
        self.view_as_combobox.setFixedWidth(75)
        self.hbox_middle = QtWidgets.QHBoxLayout()
        self.hbox_middle.addWidget(QtWidgets.QLabel('Notes:'))
        self.hbox_middle.addStretch(1)
        self.hbox_middle.addWidget(QtWidgets.QLabel('View As: '))
        self.hbox_middle.addWidget(self.view_as_combobox)

        #   Analyst's Notes Area
        self.notes = QtWidgets.QPlainTextEdit()
        self.notes.setTabChangesFocus(True)
        self.notes.setFixedHeight(100)
        self.notes.document().setDefaultFont(font)

        #   Bottom Horizontal Layout: | Error Msg | OK Button | Cancel Button |
        error_font = QtGui.QFont()
        error_font.setBold(True)
        self.error_msg = QtWidgets.QLabel('')
        self.error_msg.setFont(error_font)
        self.ok_button = QtWidgets.QPushButton('OK')
        self.ok_button.setFixedWidth(85)
        self.cancel_button = QtWidgets.QPushButton('Cancel')
        self.cancel_button.setFixedWidth(100)
        self.hbox_bottom = QtWidgets.QHBoxLayout()
        self.hbox_bottom.addWidget(self.error_msg)
        self.hbox_bottom.addWidget(self.ok_button)
        self.hbox_bottom.addWidget(self.cancel_button)

        #   Vertical Layout
        self.vbox_outer = QtWidgets.QVBoxLayout(Dialog)
        self.vbox_outer.setObjectName('vbox_outer')
        self.vbox_outer.addWidget(self.sig_groupbox)
        self.vbox_outer.addLayout(self.hbox_middle)
        self.vbox_outer.addWidget(self.notes)
        self.vbox_outer.addLayout(self.hbox_bottom)

        #   Signal Handling
        self.ok_button.clicked.connect(Dialog.ok_button_callback)
        self.cancel_button.clicked.connect(Dialog.reject)

class SubmitSig_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName('Dialog')
        Dialog.setWindowIcon(get_clamav_icon())
        Dialog.setWindowTitle('Submit Your ClamAV Signature')
        Dialog.resize(430, 300)

        #   Email Body Area
        self.link = QtWidgets.QLabel('')
        self.link.setTextFormat(Qt.RichText)
        self.link.setTextInteractionFlags(Qt.TextBrowserInteraction)
        self.link.setOpenExternalLinks(True)
        self.email_body = QtWidgets.QPlainTextEdit()
        self.email_body.setReadOnly(True)

        #   Ok Button Area
        self.button_box = QtWidgets.QDialogButtonBox()
        self.button_box.setOrientation(Qt.Horizontal)
        self.button_box.setStandardButtons(QtWidgets.QDialogButtonBox.Ok)
        self.button_box.setObjectName('button_box')
        self.hbox_bottom = QtWidgets.QHBoxLayout()
        self.hbox_bottom.addWidget(self.button_box)

        #   Vertical Layout
        self.vbox_outer = QtWidgets.QVBoxLayout(Dialog)
        self.vbox_outer.setObjectName('vbox_outer')
        self.vbox_outer.addWidget(self.link)
        self.vbox_outer.addWidget(self.email_body)
        self.vbox_outer.addLayout(self.hbox_bottom)

        #   Signal Handling
        self.button_box.accepted.connect(Dialog.accept)


#   Class to interface with Dialog GUIs and the back end data
#-------------------------------------------------------------------------------
class CASCDialog(QtWidgets.QDialog):
    error_format = '<font color="#ff0000">{0}</font>'
    def __init__(self, parent):
        super(CASCDialog, self).__init__(parent)
        self.data = None
        self.should_show = True

    def opcodes_text_changed(self):
        pass

    def set_custom_opcode_list(self, opcodes):
        self.data.set_opcode_list(opcodes)

    def registerSuccessCallback(self, fn):
        self.callback_fn = fn

    def success_callback(self):
        if self.callback_fn != None:
            self.callback_fn(self.data, self.ui.notes.toPlainText(), self.index)

    def ok_button_callback(self):
        if None != self.data:
            msg = verify_clamav_sig(self.data.opcode_to_signatrue())
            if None != msg:
                self.ui.error_msg.setText(self.error_format.format(msg))
                return

        self.accept()

    def show(self):
        if self.should_show:
            super(CASCDialog, self).show()

class AsmSignatureDialog(CASCDialog):
    def __init__(self, parent, start_ea=BADADDR, end_ea=BADADDR, index=None):
        super(AsmSignatureDialog, self).__init__(parent)

        self.old_scroll_value = 0
        self.callback_fn = None
        self.index = index

        self.ui = AsmSig_Dialog()
        self.ui.setupUi(self)

        self.accepted.connect(self.success_callback)
        self.ui.opcodes.textChanged.connect(self.opcodes_text_changed)

        #   Get Opcodes
        if (BADADDR == start_ea) or (BADADDR == end_ea):
            #   Check if user has selected a chunk of code
            if (BADADDR != IDAW.SelStart()) and (BADADDR != IDAW.SelEnd()):
                start_ea, end_ea = (IDAW.SelStart(), IDAW.SelEnd())

            #   Check if user has selected a basic block
            elif IDAW.get_func(IDAW.ScreenEA()):
                block = get_block(IDAW.ScreenEA())
                if None != block:
                    start_ea = block.startEA
                    end_ea = block.endEA

        if ((BADADDR != start_ea) and is_in_sample_segments(start_ea) and
            (BADADDR != end_ea) and is_in_sample_segments(end_ea - 1)):
                self.data = Assembly(start_ea, end_ea)
                self.update_opcodes_and_asm()
                self.ui._opcodes.setPlainText('\n'.join(self.data.get_original_opcode_list()))

        else:
            msg_box = QtWidgets.QMessageBox()
            msg_box.setIcon(QtWidgets.QMessageBox.Critical)
            msg_box.setWindowTitle('Cannot add Assembly to ClamAV Signature Creator')
            msg_str = ( 'Address range is not within the sample\'s '
                        'segments (0x{0:x} - 0x{1:x})')
            msg_box.setText(msg_str.format(start_ea, end_ea))
            msg_box.exec_()

            self.should_show = False

    def sync_scrolls(self, value):
        if value != self.ui._scrollbar_opcodes.value():
            self.ui._scrollbar_opcodes.setValue(value)
        if value != self.ui.scrollbar_opcodes.value():
            self.ui.scrollbar_opcodes.setValue(value)
        if value != self.ui.scrollbar_asm.value():
            self.ui.scrollbar_asm.setValue(value)

        self.old_scroll_value = value

    def set_mask(self, masking):
        self.ui.mask.set_masking(masking)

    def apply_mask(self, value):
        scroll_value = self.old_scroll_value
        self.data.mask_opcodes(self.ui.mask.get_masking())

        self.toggle_custom_ui()
        self.update_opcodes_and_asm()
        self.sync_scrolls(scroll_value)

    def toggle_custom_ui(self, is_custom=False):
        if is_custom or self.ui.mask.custom_checked():
            #   Disable selecting other mask options
            self.ui.mask.disable()

            #   Enable editing opcodes qplaintextedit
            self.ui.opcodes.setReadOnly(False)

            if is_custom:
                #   Disconnect checkbox signal to prevent reentry
                self.ui.mask.set_custom(True)
        else:
            #   Enable selecting other mask options
            self.ui.mask.enable()

            #   Disble editing opcodes qplaintextedit
            self.ui.opcodes.setReadOnly(True)

            #   Restore opcodes to match
            self.data.mask_opcodes(self.ui.mask.get_masking())
            self.update_opcodes_and_asm()

    def set_custom_opcode_list(self, opcodes):
        self.data.set_opcode_list(opcodes, True)
        self.toggle_custom_ui(True)

    def update_opcodes_and_asm(self):
        self.ui.opcodes.setPlainText('\n'.join(self.data.get_opcode_list()))
        self.ui.asm.setPlainText('\n'.join(self.data.get_mnemonics_list()))

        #   Set scroll values back to their old values
        self.ui._scrollbar_opcodes.setValue(self.old_scroll_value)
        self.ui.scrollbar_opcodes.setValue(self.old_scroll_value)
        self.ui.scrollbar_asm.setValue(self.old_scroll_value)

    def opcodes_text_changed(self):
        if self.ui.mask.custom_checked():
            self.data.set_opcode_list(self.ui.opcodes.toPlainText().split('\n'), True)

class MiscSignatureDialog(CASCDialog):
    view_as_options = ['Hex', 'ASCII', 'Unicode', 'Decoded Sig']
    msg = '<font color="#ff0000">{0} Error: {1}</font>'
    conversion_funcs = [lambda x: x.replace(' ', '').decode('hex'),
                        lambda x: unicode(x.replace(' ', '').decode('hex')),
                        lambda x: convert_to_ascii(x)]

    def __init__(self, parent, index=None, data=None, notes=None):
        super(MiscSignatureDialog, self).__init__(parent)

        self.index = index
        self.callback_fn = None
        self.conversion_error = False

        self.ui = MiscSig_Dialog()
        self.ui.setupUi(self)

        if data:
            self.ui.sub_signature.setPlainText(data)

        if None != notes:
            self.ui.notes.setPlainText(notes)

        self.data = MiscAssembly(self.ui.sub_signature.toPlainText())

        self.accepted.connect(self.success_callback)
        self.ui.sub_signature.textChanged.connect(self.opcodes_text_changed)

        #   Setup combobox GUI component
        for view_as in self.view_as_options:
            self.ui.view_as_combobox.addItem(view_as)
        self.ui.view_as_combobox.setCurrentIndex(0)
        self.ui.view_as_combobox.currentIndexChanged.connect(self.update_opcodes_and_asm)

    def opcodes_text_changed(self):
        current_index = self.ui.view_as_combobox.currentIndex()
        if current_index == self.view_as_options.index('Hex'):
            self.data.set_opcode_list(self.ui.sub_signature.toPlainText().split('\n'))

    def update_opcodes_and_asm(self, view=0):
        opcodes = self.data.get_opcode_list()
        hex_index = self.view_as_options.index('Hex')

        if view == self.view_as_options.index('ASCII'):
            try:
                opcodes = map(self.conversion_funcs[0], opcodes)

                self.ui.sub_signature.setReadOnly(True)
                self.ui.sub_signature.setEnabled(False)

            except Exception as e:
                self.conversion_error = True
                self.ui.error_msg.setText(self.msg.format('ASCII', str(e)))
                self.ui.view_as_combobox.setCurrentIndex(hex_index)
                return

        elif view == self.view_as_options.index('Unicode'):
            try:
                opcodes = map(self.conversion_funcs[1], opcodes)

                self.ui.sub_signature.setReadOnly(True)
                self.ui.sub_signature.setEnabled(False)

            except Exception as e:
                self.conversion_error = True
                self.ui.error_msg.setText(self.msg.format('Unicode', str(e)))
                self.ui.view_as_combobox.setCurrentIndex(hex_index)
                return

        elif view == self.view_as_options.index('Decoded Sig'):
            opcodes = [self.conversion_funcs[2](opcodes)]

            self.ui.sub_signature.setReadOnly(True)
            self.ui.sub_signature.setEnabled(False)

        else:
            self.ui.sub_signature.setReadOnly(False)
            self.ui.sub_signature.setEnabled(True)

        #   Set conversion error to False to prevent msg from hanging around
        if self.conversion_error:
            self.conversion_error = False
        else:
            self.ui.error_msg.setText('')

        self.ui.sub_signature.setPlainText('\n'.join(opcodes))

class SubmitSigDialog(QtWidgets.QDialog):
    def __init__(self, parent, signature, notes, breakdown):
        super(SubmitSigDialog, self).__init__(parent)

        self.callback_fn = None

        self.ui = SubmitSig_Dialog()
        self.ui.setupUi(self)

        data = ('ClamAV,\n\n'
                'I\'ve created a ClamAV Signature ({0}) with the IDA Pro '
                'ClamAV Signature Creator plugin. Please FP test and publish '
                'if it passes.\n\n'
                'Sample MD5: {1}\n\n'
                '[RESEARCH NOTES]\n\n{2}\n\n'
                '[NEW SIGNATURES]\n\n{3}\n\n'
                '[DETECTION BREAKDOWN]\n\n{4}\n\n'
                'Thanks.'
                )

        breakdown = breakdown.replace('\x00', ' ')
        for i in xrange(len(notes)):
            if (None != notes[i][0]) and (0 < len(notes[i][0])):
                notes[i] = (' (0x{0[0]:x}, 0x{0[1]:x})'.format(notes[i][0]), notes[i][1])
            notes[i] = 'Sig{0}{1[0]}:\n{1[1]}\n'.format(i, notes[i])
        notes_str = ('\n' + '-' * 40 + '\n').join(notes).replace('\x00', ' ')

        text = data.format(IDAW.GetInputFilePath(), IDAW.GetInputMD5(),
                            notes_str, signature, breakdown)
        self.ui.email_body.setPlainText(text)

        link_text = ('Send the below to <a href="mailto:community-sigs@lists.'
                        'clamav.net?Subject={0}&Body={1}">community-sigs@lists'
                        '.clamav.net</a>')

        data = data.format(IDAW.GetInputFilePath(), IDAW.GetInputMD5(),
                            quote_plus(notes_str), signature, breakdown)
        link_data = link_text.format('Community Signature Submission', data)
        self.ui.link.setText(link_data)


#   Model class that contains all of the sub signatures contained in this IDB
#   file. The model is initialized when the plugin is initialized from an IDC
#   array stored inside of the IDB file, so sub signatures are persistent across
#   IDA instances as long as the IDB is saved after changes are made.
#-------------------------------------------------------------------------------
class SubSignatureModel(QtCore.QAbstractTableModel):
    record_size = 1024

    def __init__(self, parent = None):
        super(SubSignatureModel, self).__init__(parent)

        self.next_index = 0
        self.index_lookup_table = []

        #   Initialize from netnodes in IDB file
        self.sigs_db = IDAW.GetArrayId('sub_signatures')
        if self.sigs_db == -1:
            self.sigs_db = IDAW.CreateArray('sub_signatures')

        self.sub_signatures = collections.OrderedDict()

        index = IDAW.GetFirstIndex(idc.AR_LONG, self.sigs_db)
        while index != -1:
            entry = self.__get_array(index)

            self.sub_signatures[index] = entry
            self.next_index = index
            self.index_lookup_table.append(index)
            index = IDAW.GetNextIndex(idc.AR_LONG, self.sigs_db, index)

        self.next_index += 1
        print '[CASCPlugin] Loaded %d sub signatures' % len(self.sub_signatures)

    def rowCount(self, index=QtCore.QModelIndex()):
        return len(self.sub_signatures)

    def columnCount(self, index=QtCore.QModelIndex()):
        return 4

    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid():
            return None

        if not 0 <= index.row() < len(self.sub_signatures):
            return None

        if role == Qt.DisplayRole:
            row = self.sub_signatures.values()[index.row()]
            if row is None:
                return None
            ((start_ea, end_ea, mask_options, custom), notes) = row

            if index.column() == 0:
                if None == start_ea:
                    return '-'
                return '0x{0:08x}'.format(start_ea)

            elif index.column() == 1:
                if None == end_ea:
                    return '-'
                return '0x{0:08x}'.format(end_ea)

            elif index.column() == 2:
                return notes

            elif index.column() == 3:
                if None != mask_options:
                    temp = Assembly(start_ea, end_ea)
                    temp.mask_opcodes_tuple(mask_options)

                    if None != custom:
                        temp.set_opcode_list(custom, True)

                    return ''.join(temp.get_opcode_list()).replace(' ', '')

                return MiscAssembly.sub_signature_string(custom)

            else:
                return None

        if role == Qt.FontRole:
            return QtGui.QFont().setPointSize(30)

        if role == Qt.DecorationRole and index.column() == 0:
            return None

        if role == Qt.TextAlignmentRole:
            if index.column() < 2:
                return Qt.AlignCenter;
            return Qt.AlignLeft;

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if role != Qt.DisplayRole:
            return None

        if orientation == Qt.Horizontal:
            if section == 0:
                return 'Start Address'
            elif section == 1:
                return 'End Address'
            elif section == 2:
                return 'Notes'
            elif section == 3:
                return 'Sub Signature'
            else:
                return None

    def remove_sub_signature(self, row_index):
        if row_index < len(self.index_lookup_table):
            index = self.index_lookup_table[row_index]
            del self.sub_signatures[index]
            del self.index_lookup_table[row_index]
            self.removeRow(row_index)

            #   Delete from IDB
            IDAW.DelArrayElement(idc.AR_LONG, self.sigs_db, index)
            IDAW.DeleteArray('sub_signatures_{}'.format(index))

    def add_sub_signature(self, sub_sig_data, notes, index=None):
        element =  (sub_sig_data.get_save_data_tuple(), notes)
        if None == index:
            index = self.next_index
            self.next_index += 1

        pos = bisect.bisect(self.sub_signatures.keys(), index)
        if index not in self.sub_signatures:
            #   Add a new element
            self.beginInsertRows(QtCore.QModelIndex(), pos, pos)
            self.sub_signatures[index] = element
            sorted_data = sorted(self.sub_signatures.iteritems(), key=lambda x: x[0])
            self.sub_signatures = collections.OrderedDict(sorted_data)
            self.endInsertRows()

        else:
            #   Modify existing element
            self.sub_signatures[index] = element

        #   Add to IDB
        self.__set_array(element[0], notes, index)
        self.sub_signatures[index] = (element[0], notes)
        self.index_lookup_table.append(index)

    def update_sub_signature(self, sub_sig_data, notes, row_index):
        element =  sub_sig_data.get_save_data_tuple()
        index = self.index_lookup_table[row_index]

        #   Update in IDB
        self.__set_array(element, notes, index)
        self.sub_signatures[index] = (element, notes)

    def get_row_original_data(self, row_index):
        if row_index < len(self.index_lookup_table):
            return self.sub_signatures[self.index_lookup_table[row_index]]

    def __set_array(self, element, notes, index):
        array_id = 'sub_signatures_{}'.format(index)
        sig_db = IDAW.GetArrayId(array_id)
        if sig_db == -1:
            sig_db = IDAW.CreateArray(array_id)

        data = pickle.dumps((element, notes))

        begin = 0
        for i in xrange(0, int(math.ceil(float(len(data)) / self.record_size))):
            begin = i * self.record_size
            end = begin + self.record_size
            IDAW.SetArrayString(sig_db, i, str(data[begin:end]))

        IDAW.SetArrayLong(IDAW.GetArrayId('sub_signatures'), index, 1)

    def __get_array(self, index):
        sig_db = IDAW.GetArrayId('sub_signatures_{}'.format(index))
        if sig_db == -1:
            return None

        data = ''
        index = 0
        while index != -1:
            data += IDAW.GetArrayElement(AR_STR, sig_db, index)
            index = IDAW.GetNextIndex(AR_STR, sig_db, index)

        element, notes = pickle.loads(data)
        return (element, notes)

#   Sigalyzer Widget
#-------------------------------------------------------------------------------

class SigalyzerWidget(QtWidgets.QWidget, idaapi.UI_Hooks):
    def __init__(self):
        super(QtWidgets.QWidget, self).__init__()
        self.previous_colors = []
        self.yara_scanner = YaraScanner(self.yara_match)
        self.matches = {}
        self.PopulateWidget()
        self.netnode = netnode.Netnode("$ com.cisco.casc.sigalyzer")
        for idx in sorted(self.netnode.keys()):
            sig = self.netnode[idx]
            self._add_signature(sig)
        self.hook()

    def PopulateWidget(self):
        signatures_widget = QtWidgets.QFrame()
        layout = QtWidgets.QVBoxLayout()

        layout.addWidget(QtWidgets.QLabel('Signatures'))
        self.signatures_list = QtWidgets.QListWidget()
        layout.addWidget(self.signatures_list)
        signatures_widget.setLayout(layout)

        container2 = QtWidgets.QGridLayout()
        container2.addWidget(signatures_widget, 0, 0)

        subsignatures_widget = QtWidgets.QFrame()
        layout = QtWidgets.QVBoxLayout()
        self.subsignatures_list = QtWidgets.QListWidget()
        layout.addWidget(QtWidgets.QLabel("LDB subsignatures"))
        layout.addWidget(self.subsignatures_list)
        subsignatures_widget.setLayout(layout)
        container2.addWidget(subsignatures_widget, 0, 1)
        #subsignatures_widget.hide()

        container3 = QtWidgets.QVBoxLayout()
        container3.addLayout(container2)
        self.match_label = QtWidgets.QLabel()
        container3.addWidget(self.match_label)
        self.signature_line_edit = QtWidgets.QLineEdit()
        container3.addWidget(self.signature_line_edit)
        container4 = QtWidgets.QHBoxLayout()
        container3.addLayout(container4)
        add_signature_button = QtWidgets.QPushButton("Add signature")
        remove_signature_button = QtWidgets.QPushButton("Remove signature")
        container4.addWidget(add_signature_button)
        container4.addWidget(remove_signature_button)

        self.signature_line_edit.returnPressed.connect(self.add_signature)
        add_signature_button.clicked.connect(self.add_signature)
        remove_signature_button.clicked.connect(self.remove_signature)
        self.signatures_list.itemActivated.connect(self.signature_selected)
        self.subsignatures_list.itemActivated.connect(self.subsignature_selected)

        item = QtWidgets.QListWidgetItem("Clear selection")
        item.parsed_signature = None
        self.signatures_list.addItem(item)

        self.setLayout(container3)

    def add_signature(self):
        sig = self.signature_line_edit.text()
        if self._add_signature(sig):
            self.signature_line_edit.setText("")
            key = len(self.netnode.keys())
            while key in self.netnode.keys():
                key += 1
            self.netnode[key] = sig

    def _add_signature(self, sig):
        signature = parse_signature(sig)
        if signature is None:
            idaapi.warning("Error parsing signature")
            return False
        signature.target_type = 0 #Don't check for PE header
        item = QtWidgets.QListWidgetItem(sig)
        item.parsed_signature = signature
        item.yara_rule = self.yara_scanner.compile(self.yara_scanner.convert(signature))
        if isinstance(signature, LdbSignature):
            pass
        self.signatures_list.addItem(item)
        return True

    def remove_signature(self):
        row = self.signatures_list.currentRow()
        if row > 0:
            item = self.signatures_list.takeItem(row)
            for k, v in self.netnode.items():
                if v == item.text():
                    del self.netnode[k]
                    break
            self.signature_selected(self.signatures_list.item(0))

    def signature_selected(self, item):
        self.subsignatures_list.clear()

        for ea, color in self.previous_colors:
            idc.SetColor(ea, idc.CIC_ITEM, color)
        self.previous_colors = []
        self.match_label.setText("")

        if item.parsed_signature is None:
            pass
        else:
            if isinstance(item.parsed_signature, LdbSignature):
                for i, subsig in enumerate(item.parsed_signature.subsignatures):
                    item2 = QtWidgets.QListWidgetItem("% 2d   %s:%s" % (i, str(subsig.offset), subsig.clamav_signature))
                    item2.subsignature_name = "$subsig_%02x" % i
                    self.subsignatures_list.addItem(item2)
            elif isinstance(item.parsed_signature, NdbSignature):
                self.match_label.setText("No match")

            print_console("Signature selected: %s" % item.text())
            self.yara_scanner.scan(item.yara_rule)

    def subsignature_selected(self, item):
        try:
            match = self.matches[item.subsignature_name]
            self.match_label.setText("Match:   EA: 0x%08x  Length: % 4d     Bytes: %s" % \
                    (match["ea"], len(match["data"]), " ".join("%02x" % ord(x) for x in match["data"])))
            idc.Jump(match["ea"])
            for ea, color in self.previous_colors:
                idc.SetColor(ea, idc.CIC_ITEM, color)
            self.previous_colors = []
            for ea in idautils.Heads(match["ea"], match["ea"] + len(match["data"])):
                self.previous_colors.append((ea, idc.GetColor(ea, idc.CIC_ITEM)))
                idc.SetColor(ea, idc.CIC_ITEM, SIGALYZER_COLOR_HIGHLIGHTED)
        except KeyError:
            self.match_label.setText("No match")
            for ea, color in self.previous_colors:
                idc.SetColor(ea, idc.CIC_ITEM, color)
            self.previous_colors = []
        except IndexError:
            log.exception("While selecting subsignature")


    def yara_match(self, strings):
        if isinstance(self.signatures_list.currentItem().parsed_signature, LdbSignature):
            self.matches = dict((x["identifier"], x) for x in strings)
        else:
            self.matches = {}
            self.match_label.setText("Match:   EA: 0x%08x  Length: % 4d     Bytes: %s" % \
                    (strings[0]["ea"], len(strings[0]["data"]), " ".join("%02x" % ord(x) for x in strings[0]["data"])))
            idc.Jump(strings[0]["ea"])
            for ea in idautils.Heads(strings[0]["ea"], strings[0]["ea"] + len(strings[0]["data"])):
                self.previous_colors.append((ea, idc.GetColor(ea, idc.CIC_ITEM)))
                idc.SetColor(ea, idc.CIC_ITEM, SIGALYZER_COLOR_HIGHLIGHTED)

    def saving(self):
        for ea, color in self.previous_colors:
            idc.SetColor(ea, idc.CIC_ITEM, color)

    def saved(self):
        for ea, color in self.previous_colors:
            idc.SetColor(ea, idc.CIC_ITEM, SIGALYZER_COLOR_HIGHLIGHTED)

#   Main Plug-in Form Class
#-------------------------------------------------------------------------------
class SignatureCreatorFormClass(PluginForm):
    system = {0 : 'Unknown', 1 : 'Win', 6 : 'Linux', 9 : 'Osx'}

    def __init__(self):
        super(SignatureCreatorFormClass, self).__init__()

    def OnCreate(self, form):
        global add_sig_handler_in_menu

        self.form = form

        #   For compatability with IDA 6.8 and lower
        try:
            self.parent = self.FormToPySideWidget(form)
        except AttributeError:
            self.parent = self.FormToPyQtWidget(form)

        self.populate_model()
        self.populate_main_form()

        system = self.system[get_file_type()]
        self.sample_name.setText('{0}.Trojan.Agent'.format(system))

        self.icon = get_clamav_icon()
        #self.parent.setWindowIcon(self.icon)
        try:
            self.grand_parent = self.parent.parent()
            self.grand_parent.setWindowIcon(get_clamav_icon())
        except:
            #   This is an error in PySide/FromCObject that can cause the
            #   grand_parent's refcount to be 0 and deleted
            pass

    def get_selected_rows(self):
        rows = self.tableview.selectionModel().selectedIndexes()
        if len(rows) == 0:
            return None

        return [self.sub_signature_model.data(item) for item in rows]

    def delete_item(self):
        #   Delete the row from the IDB, in memory structures and repaint table
        for index in self.tableview.selectionModel().selectedRows():
            self.sub_signature_model.remove_sub_signature(index.row())
            self.tableview.reset()

    def copy_item(self):
        item = self.get_selected_rows()
        if item != None:
            to_copy = u' '.join([unicode(x) for x in item])
            QtWidgets.QApplication.clipboard().setText(to_copy)

    def insert_asm_item(self, ctx=None):
        self.Show('ClamAV Signature Creator')
        dialog = AsmSignatureDialog(self.parent)
        dialog.registerSuccessCallback(self.sub_signature_model.add_sub_signature)
        dialog.setModal(True)
        dialog.show()

    def insert_string_item(self, ctx):
        #   Get IDA's toplevel chooser widget
        #   For compatability with IDA 6.8 and lower
        try:
            chooser = self.FormToPySideWidget(ctx.form)
        except AttributeError:
            chooser = self.FormToPyQtWidget(ctx.form)

        #   Get the embedded table view
        table_view = chooser.findChild(QtWidgets.QTableView)
        sort_order = table_view.horizontalHeader().sortIndicatorOrder()
        sort_column = table_view.horizontalHeader().sortIndicatorSection()

        #   Get the table view's data
        model = table_view.model()
        sel = ctx.chooser_selection

        data = {}
        for row_data in table_view.selectionModel().selectedRows():
            index = row_data.row()

            address = model.data(model.index(index, 0), Qt.DisplayRole)
            address = int(address[address.index(':')+1:], 16)
            length = model.data(model.index(index, 1), Qt.DisplayRole)

            #   Don't inclue the \0 character
            length = int(length, 16) - 1
            if model.data(model.index(index, 2), Qt.DisplayRole) != 'C':
                length -= 1

            raw = ['{0:02x}'.format(IDAW.Byte(address + i)) for i in xrange(length)]

            data[address] = ' '.join(raw)

        sub_signature = [data[x] for x in sorted(data.keys())]
        try:
            notes = [x.replace(' ', '').decode('hex') for x in sub_signature]
            notes = map(unicode, notes)
        except UnicodeDecodeError:
            pass

        self.insert_custom_item(' * '.join(sub_signature), '\n'.join(notes))

    def insert_import_item(self, ctx):
        try:
            chooser = self.FormToPySideWidget(ctx.form)
        except AttributeError:
            chooser = self.FormToPyQtWidget(ctx.form)

        #   Get the embedded table view
        table_view = chooser.findChild(QtWidgets.QTableView)
        sort_order = table_view.horizontalHeader().sortIndicatorOrder()
        sort_column = table_view.horizontalHeader().sortIndicatorSection()

        #   Get the table view's data
        model = table_view.model()
        sel = ctx.chooser_selection

        data = {}
        for row_data in table_view.selectionModel().selectedRows():
            index = row_data.row()

            address = int(model.data(model.index(index, 0), Qt.DisplayRole), 16)
            name = model.data(model.index(index, 2), Qt.DisplayRole)

            data[address] = ' '.join([x.encode('hex') for x in name]) + ' 00'

        sub_signature = [data[x] for x in sorted(data.keys())]
        try:
            notes = [x.replace(' ', '').decode('hex') for x in sub_signature]
            notes = map(unicode, notes)
        except UnicodeDecodeError:
            pass

        self.insert_custom_item(' * '.join(sub_signature), '\n'.join(notes))

    def insert_custom_item(self, data=None, notes=None):
        self.Show('ClamAV Signature Creator')
        dialog = MiscSignatureDialog(self.parent, data=data, notes=notes)
        dialog.registerSuccessCallback(self.sub_signature_model.add_sub_signature)
        dialog.setModal(True)
        dialog.show()

    def export_all(self):
        filename = QtWidgets.QFileDialog.getSaveFileName(self.parent, 'Save CSV export to...')
        if filename == None:
            return

        num_rows = self.sub_signature_model.rowCount()
        num_columns = self.sub_signature_model.columnCount()
        f = open(filename[0], 'wb')

        csvout = csv.writer(f)
        #   Write header to CSV and then each row
        csvout.writerow([self.sub_signature_model.headerData(x, Qt.Horizontal) for x in xrange(0, num_columns)])
        for row in xrange(0, num_rows):
            column_data = [self.sub_signature_model.index(row, column) for column in xrange(0, num_columns)]
            csvout.writerow([self.sub_signature_model.data(index) for index in column_data])
        print '[CASCPlugin] Exported %d sub signatures to %s' % (num_rows, str(filename[0]))
        f.close()

    def populate_model(self):
        self.sub_signature_model = SubSignatureModel()

    def populate_main_form(self):
        tabbed_pane = QtWidgets.QTabWidget()
        create_pane = QtWidgets.QWidget()

        tabbed_pane.addTab(create_pane, "Create")
        if sigalyzer_required_modules_loaded:
            analyze_pane = SigalyzerWidget()
            tabbed_pane.addTab(analyze_pane, "Analyze")

        create_layout = QtWidgets.QVBoxLayout()
        create_layout.setContentsMargins(0,0,0,0)
        create_layout.setSpacing(0)

        tableview = QtWidgets.QTableView()
        tableview.setModel(self.sub_signature_model)
        tableview.setSortingEnabled(False)
        tableview.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
        tableview.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
        tableview.setAlternatingRowColors(True)
        tableview.setShowGrid(False)
        hdr = tableview.verticalHeader()
        hdr.setHighlightSections(False)
        hdr.setDefaultSectionSize(hdr.minimumSectionSize())
        hdr = tableview.horizontalHeader()
        hdr.setHighlightSections(False)
        hdr.setDefaultAlignment(Qt.AlignLeft)
        hdr.setStretchLastSection(True)

        #   Context Menu Setup
        copy_action = QtWidgets.QAction('&Copy', self.parent)
        copy_action.setShortcut('Ctrl+C')
        insert_action = QtWidgets.QAction('&Insert', self.parent)
        insert_action.setShortcut('Ins')
        insert_assembly_action = QtWidgets.QAction('&Insert Assembly', self.parent)
        insert_assembly_action.setShortcut('Ctrl+Ins')
        export_action = QtWidgets.QAction('E&xport all to CSV...', self.parent)
        export_action.setShortcut('Shift+Ins')
        delete_action = QtWidgets.QAction('&Delete', self.parent)
        delete_action.setShortcut('Del')
        separator = QtWidgets.QAction(self.parent)
        separator.setSeparator(True)
        tableview.setColumnWidth(0, 75)
        tableview.setColumnWidth(1, 75)
        tableview.setColumnWidth(2, 300)
        tableview.setContextMenuPolicy(Qt.ActionsContextMenu)
        tableview.addAction(insert_action)
        tableview.addAction(insert_assembly_action)
        tableview.addAction(separator)
        tableview.addAction(copy_action)
        tableview.addAction(export_action)
        tableview.addAction(separator)
        tableview.addAction(delete_action)

        #   Bottom Horizonal Layout: | Sample Name | Create Sig Button |
        generate_button = QtWidgets.QPushButton('Create ClamAV Signature')
        generate_button.setFixedWidth(160)
        generate_button.clicked.connect(self.generate_signature)
        sample_name_label = QtWidgets.QLabel('Name:')
        self.sample_name = QtWidgets.QLineEdit()
        self.sample_name.setFixedHeight(generate_button.sizeHint().height())
        hboxview = QtWidgets.QHBoxLayout()
        hboxview.setContentsMargins(1, 1, 1, 1)
        hboxview.addWidget(sample_name_label)
        hboxview.addWidget(self.sample_name)
        hboxview.setSpacing(5)
        hboxview.addStretch(1)
        hboxview.addWidget(generate_button)

        #   Add Layouts/Widgets to Main Layout
        create_layout.addWidget(tableview)
        create_layout.addLayout(hboxview)

        create_pane.setLayout(create_layout)

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(tabbed_pane)

        self.parent.setLayout(layout)

        #   Signal Handling
        copy_action.triggered.connect(self.copy_item)
        insert_action.triggered.connect(self.insert_custom_item)
        insert_assembly_action.triggered.connect(self.insert_asm_item)
        export_action.triggered.connect(self.export_all)
        delete_action.triggered.connect(self.delete_item)
        tableview.doubleClicked.connect(self.row_clicked)

        tableview.resizeColumnsToContents()
        self.tableview = tableview

    def row_clicked(self, index):
        row_index = index.row()
        if row_index < len(self.sub_signature_model.index_lookup_table):
            index = self.sub_signature_model.index_lookup_table[row_index]

            data = self.sub_signature_model.sub_signatures[index]
            (start_ea, end_ea, mask_options, custom_opcodes), notes = data
            if None != start_ea:
                dialog = AsmSignatureDialog(self.parent, start_ea, end_ea, row_index)
                dialog.set_mask(mask_options)

            else:
                dialog = MiscSignatureDialog(self.parent, index=row_index)

            if None != custom_opcodes:
                dialog.set_custom_opcode_list(custom_opcodes)

            dialog.ui.notes.setPlainText(notes)
            dialog.update_opcodes_and_asm()
            dialog.registerSuccessCallback(self.sub_signature_model.update_sub_signature)
            dialog.setModal(True)
            dialog.show()

    def generate_signature(self):
        ndb_format = '{0}:{1}:*:{2}:70'
        ldb_format = '{0};Engine:70-255,Target:{1};{2};{3}'

        msg_box = QtWidgets.QMessageBox()
        msg_box.setIcon(QtWidgets.QMessageBox.Critical)
        msg_box.setWindowTitle('Unable to create ClamAV Signature')

        #   Ensure the signature name is valid
        name = self.sample_name.text()
        if None == re.match('^\w+\.\w+\.\w+(|\.Gen)$', name):
            msg_box.setWindowTitle('Invalid ClamAV Signature Name')
            msg_box.setText(('Could not create ClamAV signature. An invalid '
                            'signature\n name was provided.\n\nName Format:\n'
                            '<Targeted Platform or File Format>.<Category>.'
                            '<Sample Name>\n\nOptional Suffix: .Gen'))
            return msg_box.exec_()

        #   Get sub signatures that are selected
        sub_sigs = []
        for index in self.tableview.selectionModel().selectedRows():
            sub_sig = self.sub_signature_model.get_row_original_data(index.row())
            if None != sub_sig:
                sub_sigs.append(sub_sig)

        #   Dynamically get the file type
        file_type = get_file_type()
        signature = None

        if 0 == len(sub_sigs):
            msg_box.setText(('No sub signatures were selected. Select one or\n'
                            'more signatures before creating a ClamAV Signature'))
            return msg_box.exec_()

        #   Get opcodes for all sub signatures
        opcodes = []
        notes = []
        breakdown = {'name' : 'VIRUS NAME: {}'.format(name),
                    'description' : 'TDB: Engine:51-255,Target:{0}'.format(file_type),
                    'subsigs' : {}}
        sig_id = 0
        for (start_ea, end_ea, mask_options, custom_opcodes), note in sub_sigs:
            temp_note = ('', note)
            breakdown['subsigs'][sig_id] = {'offset' : 'OFFSET: ANY',
                                            'sigmod' : 'SIGMOD: NONE',
                                            'subsig' : convert_to_ascii(custom_opcodes)}
            if (None != start_ea) and (None != end_ea):
                obj = Assembly(start_ea, end_ea)
                obj.mask_opcodes_tuple(mask_options)
                temp_note = ((start_ea, end_ea), note)
                breakdown['subsigs'][sig_id]['subsig'] = '\n'.join(obj.get_mnemonics_list())

                if None == custom_opcodes:
                    custom_opcodes = obj.get_opcode_list()
                else:
                    temp = '{0}\n{1}\n{0}'.format(  '{0}CUSTOMIZE SUBSIG FROM{0}'.format('*'*20),
                                                    breakdown['subsigs'][sig_id]['subsig'])
                    breakdown['subsigs'][sig_id]['subsig'] = temp

            elif None == custom_opcodes:
                msg_box.setText(('Saved data is incorrectly formated.\nCould '
                                'not retrieve sub signatures data.'))
                msg_box.exec_()
                continue

            opcodes.append(''.join(custom_opcodes).replace(' ', ''))
            notes.append(temp_note)
            sig_id += 1

        #   Create signature based on opcodes
        if 0 == len(opcodes):
            msg_box.setText('No opcodes to create a sig with')
            return msg_box.exec_()

        elif 1 == len(opcodes):
            #   Create NDB signature
            signature = ndb_format.format(name, file_type, ''.join(opcodes[0]).replace('', ''))
            breakdown['description'] = 'TARGET TYPE: {}'.format(get_type_name(file_type))
            format_str = (  '{0[offset]}\n'
                            'DECODED SUBSIGNATURE:\n{0[subsig]}')
            breakdown['breakdown'] = format_str.format(breakdown['subsigs'][0])
            breakdown['decoded'] = '{0[name]}\n{0[description]}\n{0[breakdown]}'.format(breakdown)
            print 'NDB created from custom_opcodes:\n\t{0}\n'.format(signature)

        else:
            format_str = (  ' * SUBSIG ID {0}\n'
                            ' +-> {1[offset]}\n'
                            ' +-> {1[sigmod]}\n'
                            ' +-> DECODED SUBSIGNATURE:\n{1[subsig]}')
            #   Create LDB signature
            condition = '&'.join(map(str, range(len(sub_sigs))))
            signature = ldb_format.format(name, file_type, condition, ';'.join(opcodes))
            breakdown['logical'] = 'LOGICAL EXPRESSION: {}'.format(condition)
            indexes = sorted(breakdown['subsigs'].keys())
            breakdowns = [format_str.format(i, breakdown['subsigs'][i]) for i in indexes]
            breakdown['breakdown'] = '\n'.join(breakdowns)
            breakdown['decoded'] = '{0[name]}\n{0[description]}\n{0[logical]}\n{0[breakdown]}'.format(breakdown)
            print 'LDB created from custom_opcodes:\n\t{0}\n'.format(signature)

        #   IDA stops printing when it sees a null byte.
        print breakdown['decoded'].replace('\x00', ' ')

        #   Display dialog to user
        dialog = SubmitSigDialog(self.parent, signature, notes, breakdown['decoded'])
        dialog.setModal(True)
        dialog.show()

        return signature

    def OnClose(self,form):
        pass


#   ClamAV Signature Creator (CASC) Plug-in Class
#-------------------------------------------------------------------------------
class ClamAVSigCreatorPlugin(plugin_t):
    flags = IDAW.PLUGIN_FIX
    comment = 'Aids analysts in creating ClamAV NDB and LDB signatures'

    #   IDA Pro display details
    help = 'Creates ClamAV signatures from selected data from an IDB'
    wanted_name = 'ClamAV Signature Creator'
    wanted_hotkey = '`'

    def init(self):
        global clamav_sig_creator_plugin

        file_type = IDAW.GetCharPrm(idc.INF_FILETYPE)

        #   Currently only supports intel_x86
        if get_file_type() not in [1, 6, 9]:
            msg_str = '{0} does not support this file type.\n'
            IDAW.msg(msg_str.format(self.wanted_name))
            return IDAW.PLUGIN_SKIP

        #   Check to see if we've configured the plug-in yet.
        if not clamav_sig_creator_plugin:
            clamav_sig_creator_plugin = SignatureCreatorFormClass()

        return IDAW.PLUGIN_OK

    def run(self, arg):
        global clamav_sig_creator_plugin

        if None != clamav_sig_creator_plugin:
            clamav_sig_creator_plugin.Show(self.wanted_name)

    def term(self):
        pass


def PLUGIN_ENTRY():
    global valid_address_ranges
    valid_address_ranges = get_existing_segment_ranges()
    return ClamAVSigCreatorPlugin()


repository: sean2077/make-word-dword-ida
file: make_word_dword.py
description: IDA Pro Plugin: Quick Make Word and Double Word
comment: Quick Make Word and Double Word
created_at: 2024-06-17 05:49:00+00:00
pushed_at: 2024-06-17 05:50:15+00:00
url:
https://github.com/sean2077/make-word-dword-ida/blob/06cc4a7457e63fb41c407305a0930fcc50c89324/make_word_dword.py

content:
############################################################################################
##
## IDA Pro Plugin: Quick Make Word and Double Word
##
## Provide actions and shortcuts to create Word and Double Word in IDA view and Struct view.
##
## Available for IDA 7+. Tested on IDA 8.3.
##
## To install:
##      Copy script into plugins directory, i.e: C:\Program Files\<ida version>\plugins
##
## To use:
##      In IDA View or Struct View, right-click an address and type 'Make Word' or 'Make Double Word',
##      or use the hotkeys 'W' and 'Shift+D' respectively.
##
############################################################################################

__AUTHOR__ = "@sean2077"

PLUGIN_NAME = "Quick Make Word and Double Word"
PLUGIN_MAKE_WORD_HOTKEY = "W"
PLUGIN_MAKE_DWORD_HOTKEY = "Shift+D"
VERSION = "1.0.0"

import ida_kernwin
import ida_moves
import ida_struct
import idaapi
import idc

ACTION_PREFIX = "sean2077"


def make_word_action():
    form_type = idaapi.get_widget_type(idaapi.get_current_widget())

    if form_type == idaapi.BWN_DISASM:

        ea = idc.get_screen_ea()
        if ea == idaapi.BADADDR:
            idaapi.warning("Invalid address selected")
            return

        idc.create_word(ea)
        idaapi.msg(f"Created word at {ea:#X}\n")

    elif form_type == idaapi.BWN_STRUCTS:

        view = idaapi.get_current_viewer()
        loc = ida_moves.lochist_entry_t()
        if not ida_kernwin.get_custom_viewer_location(loc, view):
            idaapi.warning("Failed to get location")
            return

        place = loc.place()
        structplace = ida_kernwin.place_t_as_structplace_t(place)

        struc = ida_struct.get_struc_by_idx(structplace.idx)
        if struc == idaapi.BADADDR:
            idaapi.warning("Invalid structure selected")
            return

        sptr = ida_struct.get_struc(struc)

        field_name = f"field_{structplace.offset:X}"
        ida_struct.add_struc_member(sptr, field_name, structplace.offset, idc.FF_WORD, None, 2)

        idaapi.msg(
            f"Created word field: {field_name} at offset {structplace.offset:#X} in structure {ida_struct.get_struc_name(struc)}\n"
        )

    else:
        idaapi.warning("Invalid view selected")


def make_double_word_action():
    form_type = idaapi.get_widget_type(idaapi.get_current_widget())

    if form_type == idaapi.BWN_DISASM:

        ea = idc.get_screen_ea()
        if ea == idaapi.BADADDR:
            idaapi.warning("Invalid address selected")
            return

        idc.create_dword(ea)
        idaapi.msg(f"Created double word at {ea:#X}\n")

    elif form_type == idaapi.BWN_STRUCTS:

        view = idaapi.get_current_viewer()
        loc = ida_moves.lochist_entry_t()
        if not ida_kernwin.get_custom_viewer_location(loc, view):
            idaapi.warning("Failed to get location")
            return

        place = loc.place()
        structplace = ida_kernwin.place_t_as_structplace_t(place)

        struc = ida_struct.get_struc_by_idx(structplace.idx)
        if struc == idaapi.BADADDR:
            idaapi.warning("Invalid structure selected")
            return

        sptr = ida_struct.get_struc(struc)

        field_name = f"field_{structplace.offset:X}"
        ida_struct.add_struc_member(sptr, field_name, structplace.offset, idc.FF_DWORD, None, 4)

        idaapi.msg(
            f"Created double word field: {field_name} at offset {structplace.offset:#X} in structure {ida_struct.get_struc_name(struc)}\n"
        )

    else:
        idaapi.warning("Invalid view selected")


class QuickMakePlugin(idaapi.plugin_t):
    flags = idaapi.PLUGIN_PROC | idaapi.PLUGIN_HIDE
    comment = "Quick Make Word and Double Word"
    help = "Right-click an address and select 'Make Word' or 'Make Double Word'"
    wanted_name = PLUGIN_NAME

    def init(self):
        self._init_action_make_word()
        self._init_action_make_double_word()
        self._init_hooks()
        idaapi.msg("%s %s initialized...\n" % (self.wanted_name, VERSION))
        return idaapi.PLUGIN_KEEP

    def run(self, arg):
        idaapi.msg("%s cannot be run as a script.\n" % self.wanted_name)

    def term(self):
        self._hooks.unhook()
        self._del_action_make_word()
        self._del_action_make_double_word()
        idaapi.msg("%s terminated...\n" % self.wanted_name)

    def _init_hooks(self):
        self._hooks = Hooks()
        self._hooks.hook()

    ACTION_MAKE_WORD = f"{ACTION_PREFIX}:make_word"
    ACTION_MAKE_DOUBLE_WORD = f"{ACTION_PREFIX}:make_double_word"

    def _init_action_make_word(self):
        action_desc = idaapi.action_desc_t(
            self.ACTION_MAKE_WORD,
            "Make Word",
            IDACtxEntry(make_word_action),
            PLUGIN_MAKE_WORD_HOTKEY,
            "Create a word at the current address",
            0,
        )
        assert idaapi.register_action(action_desc), "Action registration failed"

    def _init_action_make_double_word(self):
        action_desc = idaapi.action_desc_t(
            self.ACTION_MAKE_DOUBLE_WORD,
            "Make Double Word",
            IDACtxEntry(make_double_word_action),
            PLUGIN_MAKE_DWORD_HOTKEY,
            "Create a double word at the current address",
            0,
        )
        assert idaapi.register_action(action_desc), "Action registration failed"

    def _del_action_make_word(self):
        idaapi.unregister_action(self.ACTION_MAKE_WORD)

    def _del_action_make_double_word(self):
        idaapi.unregister_action(self.ACTION_MAKE_DOUBLE_WORD)


class Hooks(idaapi.UI_Hooks):
    def finish_populating_widget_popup(self, widget, popup):
        form_type = idaapi.get_widget_type(widget)
        inject_quickmake_actions(widget, popup, form_type)
        return 0


def inject_quickmake_actions(widget, popup, form_type):
    if form_type in (idaapi.BWN_DISASM, form_type == idaapi.BWN_STRUCTS):
        idaapi.attach_action_to_popup(widget, popup, QuickMakePlugin.ACTION_MAKE_WORD, "Make Word", idaapi.SETMENU_APP)
        idaapi.attach_action_to_popup(
            widget, popup, QuickMakePlugin.ACTION_MAKE_DOUBLE_WORD, "Make Double Word", idaapi.SETMENU_APP
        )

    return 0


class IDACtxEntry(idaapi.action_handler_t):
    def __init__(self, action_function):
        idaapi.action_handler_t.__init__(self)
        self.action_function = action_function

    def activate(self, ctx):
        self.action_function()
        return 1

    def update(self, ctx):
        return idaapi.AST_ENABLE_ALWAYS


def PLUGIN_ENTRY():
    return QuickMakePlugin()


repository: splishy-splash/RE-scripts
file: Search_MSDN.py
description: a small curation of created/stolen scripts for reverse engineering
comment: Search MSDN
created_at: 2022-04-09 13:27:01+00:00
pushed_at: 2024-05-03 02:41:56+00:00
url:
https://github.com/splishy-splash/RE-scripts/blob/1ed9df1bb9baaa05450f60a6fc1636224ad4a9f0/Search_MSDN.py

content:
############################################################################################
##
## IDA plugin template code shamelessly stolen from @herrcore's GitHub (with permission)
## https://github.com/OALabs/hexcopy-ida/blob/main/hexcopy.py
##
## One-Click MSDN search!
##
## Updated for IDA 7.xx and Python 3
##
## To install:
##      Copy script into plugins directory, i.e: C:\Program Files\<ida version>\plugins
##      Install BeautifulSoup (bs4) and requests for whichever version of Python3 you are running
##
## To run:
##      Highlight Windows API function and right click and select "Search MSDN"
##      The link to the MSDN page for that function will open in a new browser window!
##
############################################################################################


__AUTHOR__ = 'SplishSplash'

PLUGIN_NAME = "Search_MSDN"
PLUGIN_HOTKEY = 'Ctrl+M'
VERSION = '1.0'

import sys
import ida_kernwin
import idc
import idaapi
import idautils
from bs4 import BeautifulSoup
import webbrowser
import requests
import re

major, minor = map(int, idaapi.get_kernel_version().split("."))
using_ida7api = (major > 6)
using_pyqt5 = using_ida7api or (major == 6 and minor >= 9)

if using_pyqt5:
    import PyQt5.QtGui as QtGui
    import PyQt5.QtCore as QtCore
    import PyQt5.QtWidgets as QtWidgets
    from PyQt5.Qt import QApplication

else:
    import PySide.QtGui as QtGui
    import PySide.QtCore as QtCore

    QtWidgets = QtGui
    QtCore.pyqtSignal = QtCore.Signal
    QtCore.pyqtSlot = QtCore.Slot
    from PySide.QtGui import QApplication


def PLUGIN_ENTRY():
    """
    Required plugin entry point for IDAPython Plugins.
    """
    return search_init()


class search_init(idaapi.plugin_t):
    """
    The IDA Plugin for Search_MSDN.
    """

    flags = idaapi.PLUGIN_PROC | idaapi.PLUGIN_HIDE
    comment = "Search MSDN"
    help = "Highlight Windows API and right-click 'Search MSDN'"
    wanted_name = PLUGIN_NAME
    wanted_hotkey = PLUGIN_HOTKEY

    # --------------------------------------------------------------------------
    # Plugin Overloads
    # --------------------------------------------------------------------------

    def init(self):
        """
        This is called by IDA when it is loading the plugin.
        """

        # initialize the menu actions our plugin will inject
        self._init_action_search()

        # initialize plugin hooks
        self._init_hooks()

        # done
        idaapi.msg("%s %s initialized...\n" % (self.wanted_name, VERSION))
        return idaapi.PLUGIN_KEEP

    def run(self, arg):
        """
        This is called by IDA when this file is loaded as a script.
        """
        idaapi.msg("%s cannot be run as a script.\n" % self.wanted_name)

    def term(self):
        """
        This is called by IDA when it is unloading the plugin.
        """

        # unhook our plugin hooks
        self._hooks.unhook()

        # unregister our actions & free their resources
        self._del_action_search()

        # done
        idaapi.msg("%s terminated...\n" % self.wanted_name)

    # --------------------------------------------------------------------------
    # Plugin Hooks
    # --------------------------------------------------------------------------

    def _init_hooks(self):
        """
        Install plugin hooks into IDA.
        """
        self._hooks = Hooks()
        self._hooks.ready_to_run = self._init_hexrays_hooks
        self._hooks.hook()

    def _init_hexrays_hooks(self):
        """
        Install Hex-Rrays hooks (when available).
        NOTE: This is called when the ui_ready_to_run event fires.
        """
        if idaapi.init_hexrays_plugin():
            idaapi.install_hexrays_callback(self._hooks.hxe_callback)

    # --------------------------------------------------------------------------
    # IDA Actions
    # --------------------------------------------------------------------------

    SEARCH = "prefix:Search_MSDN"

    def _init_action_search(self):
        """
        Register the copy bytes action with IDA.
        """
        if (sys.version_info > (3, 0)):
            # Describe the action using python3 copy
            action_desc = idaapi.action_desc_t(
                self.SEARCH,  # The action name.
                "Search MSDN",  # The action text.
                IDACtxEntry(search_web),  # The action handler.
                PLUGIN_HOTKEY,  # Optional: action shortcut
                "Search MSDN for Windows API",  # Optional: tooltip
                31  # Copy icon
            )

        # register the action with IDA
        assert idaapi.register_action(action_desc), "Action registration failed"

    def _del_action_search(self):
        """
        Delete the bulk prefix action from IDA.
        """
        idaapi.unregister_action(self.SEARCH)


# ------------------------------------------------------------------------------
# Plugin Hooks
# ------------------------------------------------------------------------------

class Hooks(idaapi.UI_Hooks):

    def finish_populating_widget_popup(self, widget, popup):
        """
        A right click menu is about to be shown. (IDA 7)
        """
        inject_search_actions(widget, popup, idaapi.get_widget_type(widget))
        return 0

    def finish_populating_tform_popup(self, form, popup):
        """
        A right click menu is about to be shown. (IDA 6.x)
        """
        inject_search_actions(form, popup, idaapi.get_tform_type(form))
        return 0

    def hxe_callback(self, event, *args):
        """
        HexRays event callback.
        We lump this under the (UI) Hooks class for organizational reasons.
        """

        #
        # if the event callback indicates that this is a popup menu event
        # (in the hexrays window), we may want to install our prefix menu
        # actions depending on what the cursor right clicked.
        #

        if event == idaapi.hxe_populating_popup:
            form, popup, vu = args

            idaapi.attach_action_to_popup(
                form,
                popup,
                search_init.SEARCH,
                "Search MSDN",
                idaapi.SETMENU_APP,
            )

        # done
        return 0


# ------------------------------------------------------------------------------
# Prefix Wrappers
# ------------------------------------------------------------------------------


def inject_search_actions(form, popup, form_type):
    """
    Inject prefix actions to popup menu(s) based on context.
    """

    #
    # disassembly window
    #

    if (form_type == idaapi.BWN_DISASMS) or (form_type == idaapi.BWN_PSEUDOCODE):
        # insert the prefix action entry into the menu
        #

        idaapi.attach_action_to_popup(
            form,
            popup,
            search_init.SEARCH,
            "Search MSDN",
            idaapi.SETMENU_APP
        )

    # done
    return 0


# ------------------------------------------------------------------------------
# Byte copy
# ------------------------------------------------------------------------------

def search_web():
    """
    Copy selected bytes to clipboard
    """
    if using_ida7api:
        data = ida_kernwin.get_highlight(ida_kernwin.get_current_viewer())
        print("%s" % data[0])
        page = requests.get('https://www.google.com/search?q=msdn+' + data[0])
        soup = BeautifulSoup(page.content, 'html.parser')
        # line below is long. if you need to fix it, here's what it does:
        # finds all <a> tags with an href that goes to microsoft.com
        # it then gets the 'href' portion, but that is way longer than we want, and has data before and after it
        # so we take off the beginning part (/url?q=) with the first split
        # and we take off everything after the actual url (more google junk that starts with '&') with the 2nd split
        msdn_page = soup.find('a', href=re.compile(r'https://learn.microsoft.com'))['href'].split("q=")[1].split('&')[0]
        webbrowser.open(msdn_page)
    else:
        data = idc.get_highlighted_identifier()
        print("%s" % data)
        page = requests.get('https://www.google.com/search?q=msdn+' + data[0])
        soup = BeautifulSoup(page.content, 'html.parser')
        # line below is long. if you need to fix it, here's what it does:
        # finds all <a> tags with an href that goes to microsoft.com
        # it then gets the 'href' portion, but that is way longer than we want, and has data before and after it
        # so we take off the beginning part (/url?q=) with the first split
        # and we take off everything after the actual url (more google junk that starts with '&') with the 2nd split
        msdn_page = soup.find('a', href=re.compile(r'https://learn.microsoft.com'))['href'].split("q=")[1].split('&')[0]
        webbrowser.open(msdn_page)
    return


# ------------------------------------------------------------------------------
# IDA ctxt
# ------------------------------------------------------------------------------

class IDACtxEntry(idaapi.action_handler_t):
    """
    A basic Context Menu class to utilize IDA's action handlers.
    """

    def __init__(self, action_function):
        idaapi.action_handler_t.__init__(self)
        self.action_function = action_function

    def activate(self, ctx):
        """
        Execute the embedded action_function when this context menu is invoked.
        """
        self.action_function()
        return 1

    def update(self, ctx):
        """
        Ensure the context menu is always available in IDA.
        """
        return idaapi.AST_ENABLE_ALWAYS


repository: securisec/mixto-scripts
file: mixto-ida/mixto_ida_py2.py
description: Repo holds scripts to integrate with other tools
wanted_name: Mixto
created_at: 2020-12-15 01:13:08+00:00
pushed_at: 2025-05-12 01:29:09+00:00
url:
https://github.com/securisec/mixto-scripts/blob/4457843c8fc51cccd1b584b455996086c0a8e707/mixto-ida/mixto_ida_py2.py

content:
# Mixto python2 lite lib for integrations without any dependencies

from urllib2 import Request, urlopen, HTTPError
from urllib import urlencode
from urlparse import urljoin
import os
import json

MIXTO_ENTRY_ID = os.getenv("MIXTO_ENTRY_ID")
MIXTO_HOST = os.getenv("MIXTO_HOST")
MIXTO_API_KEY = os.getenv("MIXTO_API_KEY")


class MissingRequired(Exception):
    """Missing params"""

    pass


class BadResponse(Exception):
    """Bad response from Mixto API"""

    pass


class MixtoLite:
    def __init__(self, host=None, api_key=None):
        self.host = host
        self.api_key = api_key
        self.workspace_id = None
        self.status = 0
        self.commit_type = "tool"

        # if envars are set, always use those values
        if MIXTO_HOST is not None and self.host is None:
            self.host = MIXTO_HOST
        if MIXTO_API_KEY is not None and self.api_key is None:
            self.api_key = MIXTO_API_KEY

        # if host or apikey is not available, read config file
        if self.host == None or self.api_key == None:
            try:
                conf_path = str(os.path.expanduser("~/.mixto.json"))
                with open(conf_path) as f:
                    j = json.loads(f.read())
                    self.host = j["host"]
                    self.api_key = j["api_key"]
                    self.workspace_id = j["workspace_id"]
            except:
                print("Cannot read mixto config file")
                raise

    def MakeRequest(
        self,
        uri,
        data={},
        is_query=False,
    ):
        """Generic method helpful in extending this lib for other Mixto
        API calls. Refer to Mixto docs for all available API endpoints.

        Args:
            method (str): Request method
            uri (str): Mixto URI.
            data (dict, optional): Body or query params. Defaults to {}.
            is_query (bool, optional): True if query params. Defaults to False.

        Raises:
            BadResponse: If status code is not 200, raises exception

        Returns:
            None: None
        """
        # add base url with endpoint
        url = urljoin(str(self.host), uri)
        # if query params, add data as query params
        if is_query:
            data = urlencode(data)
            url += "?" + data
        else:
            # add as json body
            data = json.dumps(data)
        # create request object
        req = Request(
            url=url,
            headers={
                "x-api-key": self.api_key,
                "user-agent": "mixto-lite-py2",
            },
        )
        # add json content type if post body
        if not is_query:
            req.add_header("Content-Type", "application/json")
            req.add_data(data)

        # send request
        try:
            res = urlopen(req)
            body = res.read()
            self.status = res.getcode()
            if self.status > 300:
                raise BadResponse(self.status, res)
            else:
                return body
        except HTTPError as e:
            raise BadResponse(e.code, e.read())

    def AddCommit(self, data, entry_id=None, title=""):
        """Add/commit data to an entry. This is the primary functionality of
        an integration

        Args:
            data (str): Data to add
            entry_id (str, optional): Entry ID. Will use MIXTO_ENTRY_ID as primary. Defaults to None.
            title (str, optional): Title for commit. Defaults to "Untitled".

        Raises:
            MissingRequired: If entry id is missing

        Returns:
            any: Commit added response
        """
        if MIXTO_ENTRY_ID is None and entry_id is None:
            raise MissingRequired("Entry id is missing")

        e_id = MIXTO_ENTRY_ID if MIXTO_ENTRY_ID else entry_id
        return self.MakeRequest(
            "/api/v1/commit",
            {
                "data": data,
                "workspace_id": self.workspace_id,
                "entry_id": e_id,
                "commit_type": self.commit_type,
                "title": title,
            },
        )

    def GetWorkspaces(self):
        """Get information and stats about the current workspace

        Returns:
            List[dict]: Array of workspace items
        """
        return json.loads(self.MakeRequest("/api/v1/workspace", {}, True))["data"]

    def GetEntryIDs(self):
        """Get all entry ids filtered by the current workspace

        Returns:
            List[dict]: List of entry ids
        """
        # get all entries
        entries = json.loads(
            self.MakeRequest(
                "/api/v1/workspace".format(self.workspace_id),
                {"workspace_id": self.workspace_id},
                True,
            )["data"]["entries"]
        )
        # get only entry ids
        return entries


"""
Mixto IDA Plugin
"""
import idc
import idaapi
import idautils
import ida_hexrays

# Retrieving imports from IDAPython is a little weird
# So need this global variable
AllImports = ""


def imports_cb(ea, name, ord):
    global AllImports
    if not name:
        AllImports += "0x{:x}: ord#{}\n".format(ea, ord)
    else:
        AllImports += "0x{:x}: {} ord#{}\n".format(ea, name, ord)
    # True -> Continue enumeration
    # False -> Stop enumeration
    return True


def start_ea_of(o):
    return getattr(o, "start_ea" if idaapi.IDA_SDK_VERSION >= 700 else "startEA")


def end_ea_of(o):
    return getattr(o, "end_ea" if idaapi.IDA_SDK_VERSION >= 700 else "endEA")


def get_flags_at(ea):
    return getattr(
        idaapi, "get_flags" if idaapi.IDA_SDK_VERSION >= 700 else "getFlags"
    )(ea)


def is_data(flags):
    return getattr(idaapi, "is_data" if idaapi.IDA_SDK_VERSION >= 700 else "isData")(
        flags
    )


MenuAllFunc = "Mixto:AllFunc"
MenuImports = "Mixto:Imports"
MenuExports = "Mixto:Exports"
MenuAllComments = "Mixto:AllComments"
MenuDecFunc = "Mixto:DecompileFunc"
MenuAskEntryId = "Mixto:AskEntryId"


class mixto_t(idaapi.plugin_t):

    flags = idaapi.PLUGIN_UNL
    comment = ""
    help = ""
    wanted_name = "Mixto"
    wanted_hotkey = ""
    mixto = MixtoLite()

    def init(self):
        self.add_menu_items()
        return idaapi.PLUGIN_OK

    @classmethod
    def add_menu_item_helper(self, name, text, tooltip, icon, shortcut):
        description = idaapi.action_desc_t(
            name, text, self.StartHandler(self, name), shortcut, tooltip, icon
        )
        idaapi.register_action(description)
        idaapi.attach_action_to_menu(
            self.wanted_name + "/" + text, name, idaapi.SETMENU_APP
        )

    @classmethod
    def add_menu_items(self):
        idaapi.create_menu(self.wanted_name, self.wanted_name)
        self.add_menu_item_helper(
            MenuAskEntryId,
            "Set Mixto Entry ID",
            "Specify entry id to send subsequent data",
            -1,
            "",
        )
        self.add_menu_item_helper(
            MenuAllFunc,
            "Send all functions",
            "Send all function names and addresses to mixto",
            -1,
            "",
        )
        self.add_menu_item_helper(
            MenuDecFunc,
            "Send decompilation of selected function",
            "Send decompilation of selected function to mixto",
            -1,
            "",
        )
        self.add_menu_item_helper(
            MenuImports, "Send all imports", "Send all imports to mixto", -1, ""
        )
        self.add_menu_item_helper(
            MenuExports, "Send all exports", "Send all exports to mixto", -1, ""
        )
        self.add_menu_item_helper(
            MenuAllComments,
            "Send all comments",
            "Send all comments and respective addresses to mixto",
            -1,
            "",
        )

    class StartHandler(idaapi.action_handler_t):
        def __init__(self, outer_self, menu_title):
            idaapi.action_handler_t.__init__(self)
            self.menu_title = menu_title
            self.outer_self = outer_self

        def activate(self, ctx):
            if self.menu_title == MenuAskEntryId:
                self.outer_self.mixto.entry_id = idaapi.ask_str(
                    "", 1000, "Mixto Entry Id"
                )

            else:
                if self.outer_self.mixto.entry_id is None:
                    self.outer_self.mixto.entry_id = idaapi.ask_str(
                        "", 1000, "Mixto Entry Id"
                    )

            if self.menu_title == MenuAllFunc:
                all_func = ""
                # Get count of all functions in the binary
                count = idaapi.get_func_qty()

                for i in range(count):
                    fn = idaapi.getn_func(i)

                    # Function should not have dummy name such as sub_*
                    # and should not start with underscore (possible library functions)
                    if not idaapi.has_dummy_name(
                        get_flags_at(start_ea_of(fn))
                    ) and not idaapi.get_func_name(start_ea_of(fn)).startswith("_"):
                        all_func += "{} @ 0x{:x}\n".format(
                            idaapi.get_func_name(start_ea_of(fn)), start_ea_of(fn)
                        )
                self.outer_self.mixto.AddCommit(
                    all_func, self.outer_self.mixto.entry_id, "(IDA) All Functions"
                )

            elif self.menu_title == MenuImports:
                global AllImports
                AllImports = ""
                # Get count of all import modules in the binary
                count = idaapi.get_import_module_qty()

                for i in range(count):
                    module_name = idaapi.get_import_module_name(i)

                    AllImports += "{}:\n".format(module_name)
                    idaapi.enum_import_names(i, imports_cb)
                self.outer_self.mixto.AddCommit(
                    AllImports, self.outer_self.mixto.entry_id, "(IDA) All Imports"
                )

            elif self.menu_title == MenuDecFunc:
                addr_current = idc.get_screen_ea()
                addr_func = idaapi.get_func(addr_current)

                if not addr_func:
                    idaapi.msg("Place cursor inside a function!")
                    return False
                else:
                    err = None
                    out = str(idaapi.decompile(addr_func))
                    # print(out)
                    self.outer_self.mixto.AddCommit(
                        str(out),
                        self.outer_self.mixto.entry_id,
                        "(IDA) Function Decompilation {}".format(
                            idc.GetFunctionName(addr_func.startEA)
                        ),
                    )

            elif self.menu_title == MenuExports:
                all_exports = ""
                for entry in idautils.Entries():
                    _, ord, ea, name = entry
                    if not name:
                        all_exports += "0x{:x}: ord#{}\n".format(ea, ord)
                    else:
                        all_exports += "0x{:x}: {} ord#{}\n".format(ea, name, ord)
                self.outer_self.mixto.AddCommit(
                    all_exports, self.outer_self.mixto.entry_id, "(IDA) All Exports"
                )

            elif self.menu_title == MenuAllComments:
                addr_current = idc.get_screen_ea()
                addr_func = idaapi.get_func(addr_current)

                uniq_comments = {}
                comments = []
                for ea in range(addr_func.startEA, addr_func.endEA):
                    comment = idaapi.get_cmt(ea, 0)
                    if comment is not None:
                        # hacky way to make sure only uniq comments are added
                        if uniq_comments.get(comment) == 1:
                            pass
                        else:
                            print(uniq_comments)
                            comments.append(
                                "{offset} {value}".format(offset=hex(ea), value=comment)
                            )
                            print("here")
                            uniq_comments[comment] = 1
                if len(comments) > 0:
                    out = "\n".join(comments)
                    self.outer_self.mixto.AddCommit(
                        str(out),
                        self.outer_self.mixto.entry_id,
                        "(IDA) Function Comments {}".format(
                            idc.GetFunctionName(addr_func.startEA)
                        ),
                    )

                else:
                    raise TypeError("No comments found")

            return True

        def update(self, ctx):
            return idaapi.AST_ENABLE_ALWAYS

    def run(self, arg):
        pass

    def term(self):
        pass


def PLUGIN_ENTRY():
    return mixto_t()


repository: RceNinja/Re-Scripts
file: scripts/IA32-VMX-Helper/IA32_VMX_Helper.py
created_at: 2019-09-24 19:58:07+00:00
pushed_at: 2021-07-20 08:35:23+00:00
url:
https://github.com/RceNinja/Re-Scripts/blob/0fb7d37ec4d019e74d873833e5e56a94d9e63434/scripts/IA32-VMX-Helper/IA32_VMX_Helper.py

content:
__author__  = "Behrooz Abbassi @rceninja"
__license__ = "MIT"
__version__ = "2.0.0"


"""
You can use this script to find and decode all (I hope!) MSRs and VMCSs in hypervisor binaries,
it also has a siple gui that enables you to search in founded values by Adddress, Name and Const values
In addition to searching and decoding, you can set proper comments or symbolic constant on these values
automatically
"""


import collections
import os

# IDAPython 
import idaapi
import idautils
import idc
import struct
# ------------------------------------------------------------------------------------- #
"""
If you enable this mode, the script will try to find and decode more MSRs and VMCS by appling more
analysis on the binary, BUT because of linear analysis there is some false positives, which can cause 
some problems :-( so this mode is disabled by default


Examples

mov     rbx, 681Ch ; VMCS_GUEST_RSP
vmwrite rbx, rsi

This is the basic form of calling vmwrite this one is easy to figure but what about next examples?

mov     rax, 681Ch ; VMCS_GUEST_RSP
mov     rbx, rax 
vmwrite rbx, rsi


mov     eax, 681Ch ; VMCS_GUEST_RSP
call    Function_0()
mov     ecx, eax 
vmwrite ecx, rsi

"""
cfg_enable_smart_mode        = False
cfg_smart_mode_max_back_step = 10

# ------------------------------------------------------------------------------------- #
g_knowledge_db = None
# ------------------------------------------------------------------------------------- #
def AddSymbolicConstantsEnumsToIda():
    # ------------------------------------------------------------------------------------- #
    # Based on @AmarSaar post [https://msrc-blog.microsoft.com/2018/12/10/first-steps-in-hyper-v-research]
    # Notice these values can change between different builds!
    # ------------------------------------------------------------------------------------- #
    id = idc.AddEnum(0, "HYPERV_STRUCTS", idaapi.hexflag())
    idc.AddConstEx(id, "Self", 0, -1)
    idc.AddConstEx(id, "CpuIndex", 8, -1)
    idc.AddConstEx(id, "CurrentThread", 0x38, -1)
    idc.AddConstEx(id, "LogicalCpuIndex", 0x8080, -1)
    idc.AddConstEx(id, "CurrentVP", 0x82E0, -1)
    idc.AddConstEx(id, "IDT", 0x8340, -1)
    idc.AddConstEx(id, "GDT", 0x8348, -1)
    idc.AddConstEx(id, "CurrentPartition", 0x83A8, -1)
    idc.AddConstEx(id, "CurrentVMCS", 0x186E0, -1)



    #https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/hvilib/hviintel/cpuid_result.htm
    id = idc.AddEnum(0, "HV_CPUID_FUNCTION_ENUM", idaapi.hexflag())
    idc.AddConstEx(id, "HvCpuIdFunctionVersionAndFeatures", 0x00000001, -1)
    idc.AddConstEx(id, "HvCpuIdFunctionHvVendorAndMaxFunction", 0x40000000, -1)
    idc.AddConstEx(id, "HvCpuIdFunctionHvInterface", 0x40000001, -1)
    idc.AddConstEx(id, "HvCpuIdFunctionMsHvVersion", 0x40000002, -1)
    idc.AddConstEx(id, "HvCpuIdFunctionMsHvFeatures", 0x40000003, -1)
    idc.AddConstEx(id, "HvCpuIdFunctionMsHvEnlightenmentInformation",0x40000004, -1)
    idc.AddConstEx(id, "HvCpuIdFunctionMsHvImplementationLimits", 0x40000005, -1)

    idc.AddConstEx(id, "HvCpuIdFunctionMsHvHardwareFeatures", 0x40000006, -1)
    idc.AddConstEx(id, "HvCpuIdFunctionMsHvCpuManagementFeatures", 0x40000007, -1)
    idc.AddConstEx(id, "HvCpuIdFunctionMsHvSvmFeatures", 0x40000008, -1)
    #https://patchwork.kernel.org/patch/10649047/
    idc.AddConstEx(id, "HvCpuIdFunctionNestedFeatures", 0x4000000A, -1)




    # ------------------------------------------------------------------------------------- #
    # Exit Reasons
    # ------------------------------------------------------------------------------------- #
    id = idc.AddEnum(0, "IA32_VMX_EXIT_REASONS_ENUM", idaapi.hexflag())
    idc.AddConstEx(id, "EXIT_REASON_EXCEPTION_NMI", 0x0, -1)
    idc.AddConstEx(id, "EXIT_REASON_EXTERNAL_INTERRUPT", 0x1, -1)
    idc.AddConstEx(id, "EXIT_REASON_TRIPLE_FAULT", 0x2, -1)
    idc.AddConstEx(id, "EXIT_REASON_INIT", 0x3, -1)
    idc.AddConstEx(id, "EXIT_REASON_SIPI", 0x4, -1)
    idc.AddConstEx(id, "EXIT_REASON_IO_SMI", 0x5, -1)
    idc.AddConstEx(id, "EXIT_REASON_OTHER_SMI", 0x6, -1)
    idc.AddConstEx(id, "EXIT_REASON_PENDING_VIRT_INTR", 0x7, -1)
    idc.AddConstEx(id, "EXIT_REASON_PENDING_VIRT_NMI",  0x8, -1)
    idc.AddConstEx(id, "EXIT_REASON_TASK_SWITCH", 0x9, -1)
    idc.AddConstEx(id, "EXIT_REASON_CPUID", 0x0A, -1)
    idc.AddConstEx(id, "EXIT_REASON_GETSEC", 0x0B, -1)
    idc.AddConstEx(id, "EXIT_REASON_HLT", 0x0C, -1)
    idc.AddConstEx(id, "EXIT_REASON_INVD", 0x0D, -1)
    idc.AddConstEx(id, "EXIT_REASON_INVLPG", 0x0E, -1)
    idc.AddConstEx(id, "EXIT_REASON_RDPMC", 0x0F, -1)
    idc.AddConstEx(id, "EXIT_REASON_RDTSC", 0x10, -1)
    idc.AddConstEx(id, "EXIT_REASON_RSM", 0x11, -1)
    idc.AddConstEx(id, "EXIT_REASON_VMCALL", 0x12, -1)
    idc.AddConstEx(id, "EXIT_REASON_VMCLEAR", 0x13, -1)
    idc.AddConstEx(id, "EXIT_REASON_VMLAUNCH", 0x14, -1)
    idc.AddConstEx(id, "EXIT_REASON_VMPTRLD", 0x15, -1)
    idc.AddConstEx(id, "EXIT_REASON_VMPTRST", 0x16, -1)
    idc.AddConstEx(id, "EXIT_REASON_VMREAD", 0x17, -1)
    idc.AddConstEx(id, "EXIT_REASON_VMRESUME", 0x18, -1)
    idc.AddConstEx(id, "EXIT_REASON_VMWRITE", 0x19, -1)
    idc.AddConstEx(id, "EXIT_REASON_VMXOFF", 0x1A, -1)
    idc.AddConstEx(id, "EXIT_REASON_VMXON", 0x1B, -1)
    idc.AddConstEx(id, "EXIT_REASON_CR_ACCESS", 0x1C, -1)
    idc.AddConstEx(id, "EXIT_REASON_DR_ACCESS", 0x1D, -1)
    idc.AddConstEx(id, "EXIT_REASON_IO_INSTRUCTION", 0x1E, -1)
    idc.AddConstEx(id, "EXIT_REASON_MSR_READ", 0x1F, -1)
    idc.AddConstEx(id, "EXIT_REASON_MSR_WRITE", 0x20, -1)
    idc.AddConstEx(id, "EXIT_REASON_INVALID_GUEST_STATE", 0X21, -1)
    idc.AddConstEx(id, "EXIT_REASON_MSR_LOADING", 0x22, -1)

    idc.AddConstEx(id, "EXIT_REASON_MWAIT_INSTRUCTION", 0x24, -1)
    idc.AddConstEx(id, "EXIT_REASON_MONITOR_TRAP_FLAG", 0x25, -1)

    idc.AddConstEx(id, "EXIT_REASON_MONITOR_INSTRUCTION", 0x27, -1)
    idc.AddConstEx(id, "EXIT_REASON_PAUSE_INSTRUCTION", 0x28, -1)
    idc.AddConstEx(id, "EXIT_REASON_MCE_DURING_VMENTRY", 0x29, -1)

    idc.AddConstEx(id, "EXIT_REASON_TPR_BELOW_THRESHOLD", 0x2B, -1)
    idc.AddConstEx(id, "EXIT_REASON_APIC_ACCESS", 0x2C, -1)
    idc.AddConstEx(id, "EXIT_REASON_VIRTUALIZED_EOI", 0x2D, -1)
    idc.AddConstEx(id, "EXIT_REASON_ACCESS_GDTR_OR_IDTR", 0x2E, -1)
    idc.AddConstEx(id, "EXIT_REASON_ACCESS_LDTR_OR_TR", 0x2F, -1)
    idc.AddConstEx(id, "EXIT_REASON_EPT_VIOLATION", 0x30, -1)
    idc.AddConstEx(id, "EXIT_REASON_EPT_MISCONFIG", 0x31, -1)
    idc.AddConstEx(id, "EXIT_REASON_INVEPT", 0x32, -1)
    idc.AddConstEx(id, "EXIT_REASON_RDTSCP", 0x33, -1)
    idc.AddConstEx(id, "EXIT_REASON_VMX_PREEMPTION_TIMER_EXPIRED", 0x34, -1)
    idc.AddConstEx(id, "EXIT_REASON_INVVPID", 0x35, -1)
    idc.AddConstEx(id, "EXIT_REASON_WBINVD", 0x36, -1)
    idc.AddConstEx(id, "EXIT_REASON_XSETBV", 0x37, -1)
    idc.AddConstEx(id, "EXIT_REASON_APIC_WRITE", 0x38, -1)
    idc.AddConstEx(id, "EXIT_REASON_RDRAND", 0x39, -1)
    idc.AddConstEx(id, "EXIT_REASON_INVPCID", 0x3A, -1)
    idc.AddConstEx(id, "EXIT_REASON_EXECUTE_VMFUNC", 0x3B, -1)
    idc.AddConstEx(id, "EXIT_REASON_EXECUTE_ENCLS", 0x3C, -1)
    idc.AddConstEx(id, "EXIT_REASON_RDSEED", 0x3D, -1)
    idc.AddConstEx(id, "EXIT_REASON_PML_FULL", 0x3E, -1)
    idc.AddConstEx(id, "EXIT_REASON_XSAVES", 0x3F, -1)
    idc.AddConstEx(id, "EXIT_REASON_XRSTORS", 0x40, -1)
    idc.AddConstEx(id, "EXIT_REASON_PCOMMIT", 0x41, -1)

    # ------------------------------------------------------------------------------------- #
    # MSR
    # ------------------------------------------------------------------------------------- #
    id = idc.AddEnum(0, "IA32_MSR_LIST_ENUM", idaapi.hexflag())
    idc.AddConstEx(id, "HV_X64_MSR_GUEST_OS_ID", 0x40000000, -1)
    idc.AddConstEx(id, "HV_X64_MSR_HYPERCALL", 0x40000001, -1)
    idc.AddConstEx(id, "HV_X64_MSR_VP_INDEX", 0x40000002, -1)
    idc.AddConstEx(id, "HV_X64_MSR_RESET", 0x40000003, -1)
    idc.AddConstEx(id, "HV_X64_MSR_VP_RUNTIME", 0x40000010, -1)
    idc.AddConstEx(id, "HV_X64_MSR_TIME_REF_COUNT", 0x40000020, -1)
    idc.AddConstEx(id, "HV_X64_MSR_REFERENCE_TSC", 0x40000021, -1)
    idc.AddConstEx(id, "HV_X64_MSR_TSC_FREQUENCY", 0x40000022, -1)
    idc.AddConstEx(id, "HV_X64_MSR_APIC_FREQUENCY", 0x40000023, -1)
    idc.AddConstEx(id, "HV_X64_MSR_EOI", 0x40000070, -1)
    idc.AddConstEx(id, "HV_X64_MSR_ICR", 0x40000071, -1)
    idc.AddConstEx(id, "HV_X64_MSR_TPR", 0x40000072, -1)
    idc.AddConstEx(id, "HV_X64_MSR_VP_ASSIST_PAGE", 0x40000073, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SCONTROL", 0x40000080, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SVERSION", 0x40000081, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SIEFP", 0x40000082, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SIMP", 0x40000083, -1)
    idc.AddConstEx(id, "HV_X64_MSR_EOM", 0x40000084, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SINT0", 0x40000090, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SINT1", 0x40000091, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SINT2", 0x40000092, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SINT3", 0x40000093, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SINT4", 0x40000094, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SINT5", 0x40000095, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SINT6", 0x40000096, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SINT7", 0x40000097, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SINT8", 0x40000098, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SINT9", 0x40000099, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SINT10", 0x4000009a, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SINT11", 0x4000009b, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SINT12", 0x4000009c, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SINT13", 0x4000009d, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SINT14", 0x4000009e, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SINT15", 0x4000009f, -1)
    idc.AddConstEx(id, "HV_X64_MSR_STIMER0_CONFIG", 0x400000b0, -1)
    idc.AddConstEx(id, "HV_X64_MSR_STIMER0_COUNT", 0x400000b1, -1)
    idc.AddConstEx(id, "HV_X64_MSR_STIMER1_CONFIG", 0x400000b2, -1)
    idc.AddConstEx(id, "HV_X64_MSR_STIMER1_COUNT", 0x400000b3, -1)
    idc.AddConstEx(id, "HV_X64_MSR_STIMER2_CONFIG", 0x400000b4, -1)
    idc.AddConstEx(id, "HV_X64_MSR_STIMER2_COUNT", 0x400000b5, -1)
    idc.AddConstEx(id, "HV_X64_MSR_STIMER3_CONFIG", 0x400000b6, -1)
    idc.AddConstEx(id, "HV_X64_MSR_STIMER3_COUNT", 0x400000b7, -1)
    idc.AddConstEx(id, "HV_X64_MSR_POWER_STATE_TRIGGER_C1", 0x400000c1, -1)
    idc.AddConstEx(id, "HV_X64_MSR_POWER_STATE_TRIGGER_C2", 0x400000c2, -1)
    idc.AddConstEx(id, "HV_X64_MSR_POWER_STATE_TRIGGER_C3", 0x400000c3, -1)
    idc.AddConstEx(id, "HV_X64_MSR_POWER_STATE_CONFIG_C1", 0x400000d1, -1)
    idc.AddConstEx(id, "HV_X64_MSR_POWER_STATE_CONFIG_C2", 0x400000d2, -1)
    idc.AddConstEx(id, "HV_X64_MSR_POWER_STATE_CONFIG_C3", 0x400000d3, -1)
    idc.AddConstEx(id, "HV_X64_MSR_STATS_PARTITION_RETAIL_PAGE", 0x400000e0, -1)
    idc.AddConstEx(id, "HV_X64_MSR_STATS_PARTITION_INTERNAL_PAGE", 0x400000e1, -1)
    idc.AddConstEx(id, "HV_X64_MSR_STATS_VP_RETAIL_PAGE", 0x400000e2, -1)
    idc.AddConstEx(id, "HV_X64_MSR_STATS_VP_INTERNAL_PAGE", 0x400000e3, -1)
    idc.AddConstEx(id, "HV_X64_MSR_GUEST_IDLE", 0x400000f0, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SYNTH_DEBUG_CONTROL", 0x400000f1, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SYNTH_DEBUG_STATUS", 0x400000f2, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SYNTH_DEBUG_SEND_BUFFER", 0x400000f3, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SYNTH_DEBUG_RECEIVE_BUFFER", 0x400000f4, -1)
    idc.AddConstEx(id, "HV_X64_MSR_SYNTH_DEBUG_PENDING_BUFFER", 0x400000f5, -1)
    idc.AddConstEx(id, "HV_X64_MSR_CRASH_P0", 0x40000100, -1)
    idc.AddConstEx(id, "HV_X64_MSR_CRASH_P1", 0x40000101, -1)
    idc.AddConstEx(id, "HV_X64_MSR_CRASH_P2", 0x40000102, -1)
    idc.AddConstEx(id, "HV_X64_MSR_CRASH_P3", 0x40000103, -1)
    idc.AddConstEx(id, "HV_X64_MSR_CRASH_P4", 0x40000104, -1)
    idc.AddConstEx(id, "HV_X64_MSR_CRASH_CTL", 0x40000105, -1)
    idc.AddConstEx(id, "HV_X64_MSR_REENLIGHTENMENT_CONTROL", 0x40000106, -1)
    idc.AddConstEx(id, "HV_X64_MSR_TSC_EMULATION_CONTROL", 0x40000107, -1)
    idc.AddConstEx(id, "HV_X64_MSR_TSC_EMULATION_STATUS", 0x40000108, -1)
    idc.AddConstEx(id, "MSR_XEON_D_PPIN_CTL", 0x4e, -1)
    idc.AddConstEx(id, "MSR_XEON_D_PPIN", 0x4f, -1)
    idc.AddConstEx(id, "MSR_XEON_D_PLATFORM_INFO", 0xce, -1)
    idc.AddConstEx(id, "MSR_XEON_D_PKG_CST_CONFIG_CONTROL", 0xe2, -1)
    idc.AddConstEx(id, "IA32_MCG_CAP", 0x179, -1)
    idc.AddConstEx(id, "MSR_XEON_D_SMM_MCA_CAP", 0x17d, -1)
    idc.AddConstEx(id, "MSR_XEON_D_TEMPERATURE_TARGET", 0x1a2, -1)
    idc.AddConstEx(id, "MSR_XEON_D_TURBO_RATIO_LIMIT", 0x1ad, -1)
    idc.AddConstEx(id, "MSR_XEON_D_TURBO_RATIO_LIMIT1", 0x1ae, -1)
    idc.AddConstEx(id, "MSR_XEON_D_RAPL_POWER_UNIT", 0x606, -1)
    idc.AddConstEx(id, "MSR_XEON_D_DRAM_POWER_LIMIT", 0x618, -1)
    idc.AddConstEx(id, "MSR_XEON_D_DRAM_ENERGY_STATUS", 0x619, -1)
    idc.AddConstEx(id, "MSR_XEON_D_DRAM_PERF_STATUS", 0x61b, -1)
    idc.AddConstEx(id, "MSR_XEON_D_DRAM_POWER_INFO", 0x61c, -1)
    idc.AddConstEx(id, "MSR_XEON_D_MSRUNCORE_RATIO_LIMIT", 0x620, -1)
    idc.AddConstEx(id, "MSR_XEON_D_PP0_ENERGY_STATUS", 0x639, -1)
    idc.AddConstEx(id, "MSR_XEON_D_CORE_PERF_LIMIT_REASONS", 0x690, -1)
    idc.AddConstEx(id, "IA32_QM_EVTSEL", 0xc8d, -1)
    idc.AddConstEx(id, "IA32_PQR_ASSOC", 0xc8f, -1)
    idc.AddConstEx(id, "MSR_XEON_D_TURBO_RATIO_LIMIT3", 0x1ac, -1)
    idc.AddConstEx(id, "IA32_L3_QOS_CFG", 0xc81, -1)
    idc.AddConstEx(id, "MSR_XEON_D_IA32_L3_QOS_MASK_0", 0xc90, -1)
    idc.AddConstEx(id, "MSR_XEON_D_IA32_L3_QOS_MASK_1", 0xc91, -1)
    idc.AddConstEx(id, "MSR_XEON_D_IA32_L3_QOS_MASK_2", 0xc92, -1)
    idc.AddConstEx(id, "MSR_XEON_D_IA32_L3_QOS_MASK_3", 0xc93, -1)
    idc.AddConstEx(id, "MSR_XEON_D_IA32_L3_QOS_MASK_4", 0xc94, -1)
    idc.AddConstEx(id, "MSR_XEON_D_IA32_L3_QOS_MASK_5", 0xc95, -1)
    idc.AddConstEx(id, "MSR_XEON_D_IA32_L3_QOS_MASK_6", 0xc96, -1)
    idc.AddConstEx(id, "MSR_XEON_D_IA32_L3_QOS_MASK_7", 0xc97, -1)
    idc.AddConstEx(id, "MSR_XEON_D_IA32_L3_QOS_MASK_8", 0xc98, -1)
    idc.AddConstEx(id, "MSR_XEON_D_IA32_L3_QOS_MASK_9", 0xc99, -1)
    idc.AddConstEx(id, "MSR_XEON_D_IA32_L3_QOS_MASK_10", 0xc9a, -1)
    idc.AddConstEx(id, "MSR_XEON_D_IA32_L3_QOS_MASK_11", 0xc9b, -1)
    idc.AddConstEx(id, "MSR_XEON_D_IA32_L3_QOS_MASK_12", 0xc9c, -1)
    idc.AddConstEx(id, "MSR_XEON_D_IA32_L3_QOS_MASK_13", 0xc9d, -1)
    idc.AddConstEx(id, "MSR_XEON_D_IA32_L3_QOS_MASK_14", 0xc9e, -1)
    idc.AddConstEx(id, "MSR_XEON_D_IA32_L3_QOS_MASK_15", 0xc9f, -1)
    idc.AddConstEx(id, "MSR_SPEC_CTRL", 0x48, -1)
    idc.AddConstEx(id, "MSR_PRED_CMD", 0x49, -1)
    idc.AddConstEx(id, "MSR_LBR_SELECT", 0x1c8, -1)
    idc.AddConstEx(id, "MSR_LBR_TOS", 0x1c9, -1)
    idc.AddConstEx(id, "MSR_LBR_NHM_FROM", 0x680, -1)
    idc.AddConstEx(id, "MSR_LBR_NHM_TO", 0x6c0, -1)
    idc.AddConstEx(id, "MSR_LBR_CORE_FROM", 0x40, -1)
    idc.AddConstEx(id, "MSR_LBR_CORE_TO", 0x60, -1)
    idc.AddConstEx(id, "MSR_IA32_LASTBRANCHFROMIP", 0x1db, -1)
    idc.AddConstEx(id, "MSR_IA32_LASTBRANCHTOIP", 0x1dc, -1)
    idc.AddConstEx(id, "MSR_IA32_LASTINTFROMIP", 0x1dd, -1)
    idc.AddConstEx(id, "MSR_IA32_LASTINTTOIP", 0x1de, -1)
    idc.AddConstEx(id, "MSR_PKG_C3_RESIDENCY", 0x3f8, -1)
    idc.AddConstEx(id, "MSR_PKG_C6_RESIDENCY", 0x3f9, -1)
    idc.AddConstEx(id, "MSR_PKG_C7_RESIDENCY", 0x3fa, -1)
    idc.AddConstEx(id, "MSR_CORE_C3_RESIDENCY", 0x3fc, -1)
    idc.AddConstEx(id, "MSR_CORE_C6_RESIDENCY", 0x3fd, -1)
    idc.AddConstEx(id, "MSR_CORE_C7_RESIDENCY", 0x3fe, -1)
    idc.AddConstEx(id, "MSR_KNL_CORE_C6_RESIDENCY", 0x3ff, -1)
    idc.AddConstEx(id, "MSR_PKG_C2_RESIDENCY", 0x60d, -1)
    idc.AddConstEx(id, "MSR_PKG_C8_RESIDENCY", 0x630, -1)
    idc.AddConstEx(id, "MSR_PKG_C9_RESIDENCY", 0x631, -1)
    idc.AddConstEx(id, "MSR_PKG_C10_RESIDENCY", 0x632, -1)
    idc.AddConstEx(id, "MSR_IDT_FCR1", 0x107, -1)
    idc.AddConstEx(id, "MSR_IDT_FCR2", 0x108, -1)
    idc.AddConstEx(id, "MSR_IDT_FCR3", 0x109, -1)
    idc.AddConstEx(id, "MSR_IDT_FCR4", 0x10a, -1)
    idc.AddConstEx(id, "MSR_IDT_MCR0", 0x110, -1)
    idc.AddConstEx(id, "MSR_IDT_MCR1", 0x111, -1)
    idc.AddConstEx(id, "MSR_IDT_MCR2", 0x112, -1)
    idc.AddConstEx(id, "MSR_IDT_MCR3", 0x113, -1)
    idc.AddConstEx(id, "MSR_IDT_MCR4", 0x114, -1)
    idc.AddConstEx(id, "MSR_IDT_MCR5", 0x115, -1)
    idc.AddConstEx(id, "MSR_IDT_MCR6", 0x116, -1)
    idc.AddConstEx(id, "MSR_IDT_MCR7", 0x117, -1)
    idc.AddConstEx(id, "MSR_IDT_MCR_CTRL", 0x120, -1)
    idc.AddConstEx(id, "MSR_FSB_FREQ", 0xcd, -1)
    idc.AddConstEx(id, "MSR_IA32_BBL_CR_CTL", 0x119, -1)
    idc.AddConstEx(id, "MSR_VM_CR", 0xc0010114, -1)
    idc.AddConstEx(id, "MSR_VM_IGNNE", 0xc0010115, -1)
    idc.AddConstEx(id, "MSR_VM_HSAVE_PA", 0xc0010117, -1)
    idc.AddConstEx(id, "IA32_MTRR_FIX64K_BASE", 0x0, -1)
    idc.AddConstEx(id, "IA32_P5_MC_TYPE", 0x1, -1)
    idc.AddConstEx(id, "IA32_MONITOR_FILTER_SIZE", 0x6, -1)
    idc.AddConstEx(id, "IA32_TIME_STAMP_COUNTER", 0x10, -1)
    idc.AddConstEx(id, "IA32_PLATFORM_ID", 0x17, -1)
    idc.AddConstEx(id, "IA32_APIC_BASE", 0x1b, -1)
    idc.AddConstEx(id, "IA32_FEATURE_CONTROL", 0x3a, -1)
    idc.AddConstEx(id, "IA32_TSC_ADJUST", 0x3b, -1)
    idc.AddConstEx(id, "IA32_BIOS_UPDT_TRIG", 0x79, -1)
    idc.AddConstEx(id, "IA32_BIOS_SIGN_ID", 0x8b, -1)
    idc.AddConstEx(id, "IA32_SGXLEPUBKEYHASH0", 0x8c, -1)
    idc.AddConstEx(id, "IA32_SGXLEPUBKEYHASH1", 0x8d, -1)
    idc.AddConstEx(id, "IA32_SGXLEPUBKEYHASH2", 0x8e, -1)
    idc.AddConstEx(id, "IA32_SGXLEPUBKEYHASH3", 0x8f, -1)
    idc.AddConstEx(id, "IA32_SMM_MONITOR_CTL", 0x9b, -1)
    idc.AddConstEx(id, "IA32_SMBASE", 0x9e, -1)
    idc.AddConstEx(id, "IA32_PMC0", 0xc1, -1)
    idc.AddConstEx(id, "IA32_PMC1", 0xc2, -1)
    idc.AddConstEx(id, "IA32_PMC2", 0xc3, -1)
    idc.AddConstEx(id, "IA32_PMC3", 0xc4, -1)
    idc.AddConstEx(id, "IA32_PMC4", 0xc5, -1)
    idc.AddConstEx(id, "IA32_PMC5", 0xc6, -1)
    idc.AddConstEx(id, "IA32_PMC6", 0xc7, -1)
    idc.AddConstEx(id, "IA32_PMC7", 0xc8, -1)
    idc.AddConstEx(id, "IA32_MPERF", 0xe7, -1)
    idc.AddConstEx(id, "IA32_APERF", 0xe8, -1)
    idc.AddConstEx(id, "IA32_MTRRCAP", 0xfe, -1)
    idc.AddConstEx(id, "IA32_SYSENTER_CS", 0x174, -1)
    idc.AddConstEx(id, "IA32_SYSENTER_ESP", 0x175, -1)
    idc.AddConstEx(id, "IA32_SYSENTER_EIP", 0x176, -1)
    idc.AddConstEx(id, "IA32_MCG_STATUS", 0x17a, -1)
    idc.AddConstEx(id, "IA32_MCG_CTL", 0x17b, -1)
    idc.AddConstEx(id, "IA32_PERFEVTSEL0", 0x186, -1)
    idc.AddConstEx(id, "IA32_PERFEVTSEL1", 0x187, -1)
    idc.AddConstEx(id, "IA32_PERFEVTSEL2", 0x188, -1)
    idc.AddConstEx(id, "IA32_PERFEVTSEL3", 0x189, -1)
    idc.AddConstEx(id, "IA32_PERF_STATUS", 0x198, -1)
    idc.AddConstEx(id, "IA32_PERF_CTL", 0x199, -1)
    idc.AddConstEx(id, "IA32_CLOCK_MODULATION", 0x19a, -1)
    idc.AddConstEx(id, "IA32_THERM_INTERRUPT", 0x19b, -1)
    idc.AddConstEx(id, "IA32_THERM_STATUS", 0x19c, -1)
    idc.AddConstEx(id, "IA32_MISC_ENABLE", 0x1a0, -1)
    idc.AddConstEx(id, "IA32_ENERGY_PERF_BIAS", 0x1b0, -1)
    idc.AddConstEx(id, "IA32_PACKAGE_THERM_STATUS", 0x1b1, -1)
    idc.AddConstEx(id, "IA32_PACKAGE_THERM_INTERRUPT", 0x1b2, -1)
    idc.AddConstEx(id, "IA32_DEBUGCTL", 0x1d9, -1)
    idc.AddConstEx(id, "IA32_SMRR_PHYSBASE", 0x1f2, -1)
    idc.AddConstEx(id, "IA32_SMRR_PHYSMASK", 0x1f3, -1)
    idc.AddConstEx(id, "IA32_PLATFORM_DCA_CAP", 0x1f8, -1)
    idc.AddConstEx(id, "IA32_CPU_DCA_CAP", 0x1f9, -1)
    idc.AddConstEx(id, "IA32_DCA_0_CAP", 0x1fa, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSBASE0", 0x200, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSBASE1", 0x202, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSBASE2", 0x204, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSBASE3", 0x206, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSBASE4", 0x208, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSBASE5", 0x20a, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSBASE6", 0x20c, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSBASE7", 0x20e, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSBASE8", 0x210, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSBASE9", 0x212, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSMASK0", 0x201, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSMASK1", 0x203, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSMASK2", 0x205, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSMASK3", 0x207, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSMASK4", 0x209, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSMASK5", 0x20b, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSMASK6", 0x20d, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSMASK7", 0x20f, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSMASK8", 0x211, -1)
    idc.AddConstEx(id, "IA32_MTRR_PHYSMASK9", 0x213, -1)
    idc.AddConstEx(id, "IA32_MTRR_FIX64K_SIZE", 0x10000, -1)
    idc.AddConstEx(id, "IA32_MTRR_FIX64K_00000", 0x250, -1)
    idc.AddConstEx(id, "IA32_MTRR_FIX16K_BASE", 0x80000, -1)
    idc.AddConstEx(id, "IA32_MTRR_FIX16K_SIZE", 0x4000, -1)
    idc.AddConstEx(id, "IA32_MTRR_FIX16K_80000", 0x258, -1)
    idc.AddConstEx(id, "IA32_MTRR_FIX16K_A0000", 0x259, -1)
    idc.AddConstEx(id, "IA32_MTRR_FIX4K_BASE", 0xc0000, -1)
    idc.AddConstEx(id, "IA32_MTRR_FIX4K_SIZE", 0x1000, -1)
    idc.AddConstEx(id, "IA32_MTRR_FIX4K_C0000", 0x268, -1)
    idc.AddConstEx(id, "IA32_MTRR_FIX4K_C8000", 0x269, -1)
    idc.AddConstEx(id, "IA32_MTRR_FIX4K_D0000", 0x26a, -1)
    idc.AddConstEx(id, "IA32_MTRR_FIX4K_D8000", 0x26b, -1)
    idc.AddConstEx(id, "IA32_MTRR_FIX4K_E0000", 0x26c, -1)
    idc.AddConstEx(id, "IA32_MTRR_FIX4K_E8000", 0x26d, -1)
    idc.AddConstEx(id, "IA32_MTRR_FIX4K_F0000", 0x26e, -1)
    idc.AddConstEx(id, "IA32_MTRR_FIX4K_F8000", 0x26f, -1)
    idc.AddConstEx(id, "IA32_MTRR_FIX_COUNT", 0x58, -1)
    idc.AddConstEx(id, "IA32_MTRR_VARIABLE_COUNT", 0xff, -1)
    idc.AddConstEx(id, "IA32_MTRR_COUNT", 0x157, -1)
    idc.AddConstEx(id, "IA32_PAT", 0x277, -1)
    idc.AddConstEx(id, "IA32_MC0_CTL2", 0x280, -1)
    idc.AddConstEx(id, "IA32_MC1_CTL2", 0x281, -1)
    idc.AddConstEx(id, "IA32_MC2_CTL2", 0x282, -1)
    idc.AddConstEx(id, "IA32_MC3_CTL2", 0x283, -1)
    idc.AddConstEx(id, "IA32_MC4_CTL2", 0x284, -1)
    idc.AddConstEx(id, "IA32_MC5_CTL2", 0x285, -1)
    idc.AddConstEx(id, "IA32_MC6_CTL2", 0x286, -1)
    idc.AddConstEx(id, "IA32_MC7_CTL2", 0x287, -1)
    idc.AddConstEx(id, "IA32_MC8_CTL2", 0x288, -1)
    idc.AddConstEx(id, "IA32_MC9_CTL2", 0x289, -1)
    idc.AddConstEx(id, "IA32_MC10_CTL2", 0x28a, -1)
    idc.AddConstEx(id, "IA32_MC11_CTL2", 0x28b, -1)
    idc.AddConstEx(id, "IA32_MC12_CTL2", 0x28c, -1)
    idc.AddConstEx(id, "IA32_MC13_CTL2", 0x28d, -1)
    idc.AddConstEx(id, "IA32_MC14_CTL2", 0x28e, -1)
    idc.AddConstEx(id, "IA32_MC15_CTL2", 0x28f, -1)
    idc.AddConstEx(id, "IA32_MC16_CTL2", 0x290, -1)
    idc.AddConstEx(id, "IA32_MC17_CTL2", 0x291, -1)
    idc.AddConstEx(id, "IA32_MC18_CTL2", 0x292, -1)
    idc.AddConstEx(id, "IA32_MC19_CTL2", 0x293, -1)
    idc.AddConstEx(id, "IA32_MC20_CTL2", 0x294, -1)
    idc.AddConstEx(id, "IA32_MC21_CTL2", 0x295, -1)
    idc.AddConstEx(id, "IA32_MC22_CTL2", 0x296, -1)
    idc.AddConstEx(id, "IA32_MC23_CTL2", 0x297, -1)
    idc.AddConstEx(id, "IA32_MC24_CTL2", 0x298, -1)
    idc.AddConstEx(id, "IA32_MC25_CTL2", 0x299, -1)
    idc.AddConstEx(id, "IA32_MC26_CTL2", 0x29a, -1)
    idc.AddConstEx(id, "IA32_MC27_CTL2", 0x29b, -1)
    idc.AddConstEx(id, "IA32_MC28_CTL2", 0x29c, -1)
    idc.AddConstEx(id, "IA32_MC29_CTL2", 0x29d, -1)
    idc.AddConstEx(id, "IA32_MC30_CTL2", 0x29e, -1)
    idc.AddConstEx(id, "IA32_MC31_CTL2", 0x29f, -1)
    idc.AddConstEx(id, "IA32_MTRR_DEF_TYPE", 0x2ff, -1)
    idc.AddConstEx(id, "IA32_FIXED_CTR0", 0x309, -1)
    idc.AddConstEx(id, "IA32_FIXED_CTR1", 0x30a, -1)
    idc.AddConstEx(id, "IA32_FIXED_CTR2", 0x30b, -1)
    idc.AddConstEx(id, "IA32_PERF_CAPABILITIES", 0x345, -1)
    idc.AddConstEx(id, "IA32_FIXED_CTR_CTRL", 0x38d, -1)
    idc.AddConstEx(id, "IA32_PERF_GLOBAL_STATUS", 0x38e, -1)
    idc.AddConstEx(id, "IA32_PERF_GLOBAL_CTRL", 0x38f, -1)
    idc.AddConstEx(id, "IA32_PERF_GLOBAL_STATUS_RESET", 0x390, -1)
    idc.AddConstEx(id, "IA32_PERF_GLOBAL_STATUS_SET", 0x391, -1)
    idc.AddConstEx(id, "IA32_PERF_GLOBAL_INUSE", 0x392, -1)
    idc.AddConstEx(id, "IA32_PEBS_ENABLE", 0x3f1, -1)
    idc.AddConstEx(id, "IA32_MC0_CTL", 0x400, -1)
    idc.AddConstEx(id, "IA32_MC1_CTL", 0x404, -1)
    idc.AddConstEx(id, "IA32_MC2_CTL", 0x408, -1)
    idc.AddConstEx(id, "IA32_MC3_CTL", 0x40c, -1)
    idc.AddConstEx(id, "IA32_MC4_CTL", 0x410, -1)
    idc.AddConstEx(id, "IA32_MC5_CTL", 0x414, -1)
    idc.AddConstEx(id, "IA32_MC6_CTL", 0x418, -1)
    idc.AddConstEx(id, "IA32_MC7_CTL", 0x41c, -1)
    idc.AddConstEx(id, "IA32_MC8_CTL", 0x420, -1)
    idc.AddConstEx(id, "IA32_MC9_CTL", 0x424, -1)
    idc.AddConstEx(id, "IA32_MC10_CTL", 0x428, -1)
    idc.AddConstEx(id, "IA32_MC11_CTL", 0x42c, -1)
    idc.AddConstEx(id, "IA32_MC12_CTL", 0x430, -1)
    idc.AddConstEx(id, "IA32_MC13_CTL", 0x434, -1)
    idc.AddConstEx(id, "IA32_MC14_CTL", 0x438, -1)
    idc.AddConstEx(id, "IA32_MC15_CTL", 0x43c, -1)
    idc.AddConstEx(id, "IA32_MC16_CTL", 0x440, -1)
    idc.AddConstEx(id, "IA32_MC17_CTL", 0x444, -1)
    idc.AddConstEx(id, "IA32_MC18_CTL", 0x448, -1)
    idc.AddConstEx(id, "IA32_MC19_CTL", 0x44c, -1)
    idc.AddConstEx(id, "IA32_MC20_CTL", 0x450, -1)
    idc.AddConstEx(id, "IA32_MC21_CTL", 0x454, -1)
    idc.AddConstEx(id, "IA32_MC22_CTL", 0x458, -1)
    idc.AddConstEx(id, "IA32_MC23_CTL", 0x45c, -1)
    idc.AddConstEx(id, "IA32_MC24_CTL", 0x460, -1)
    idc.AddConstEx(id, "IA32_MC25_CTL", 0x464, -1)
    idc.AddConstEx(id, "IA32_MC26_CTL", 0x468, -1)
    idc.AddConstEx(id, "IA32_MC27_CTL", 0x46c, -1)
    idc.AddConstEx(id, "IA32_MC28_CTL", 0x470, -1)
    idc.AddConstEx(id, "IA32_MC0_STATUS", 0x401, -1)
    idc.AddConstEx(id, "IA32_MC1_STATUS", 0x405, -1)
    idc.AddConstEx(id, "IA32_MC2_STATUS", 0x409, -1)
    idc.AddConstEx(id, "IA32_MC3_STATUS", 0x40d, -1)
    idc.AddConstEx(id, "IA32_MC4_STATUS", 0x411, -1)
    idc.AddConstEx(id, "IA32_MC5_STATUS", 0x415, -1)
    idc.AddConstEx(id, "IA32_MC6_STATUS", 0x419, -1)
    idc.AddConstEx(id, "IA32_MC7_STATUS", 0x41d, -1)
    idc.AddConstEx(id, "IA32_MC8_STATUS", 0x421, -1)
    idc.AddConstEx(id, "IA32_MC9_STATUS", 0x425, -1)
    idc.AddConstEx(id, "IA32_MC10_STATUS", 0x429, -1)
    idc.AddConstEx(id, "IA32_MC11_STATUS", 0x42d, -1)
    idc.AddConstEx(id, "IA32_MC12_STATUS", 0x431, -1)
    idc.AddConstEx(id, "IA32_MC13_STATUS", 0x435, -1)
    idc.AddConstEx(id, "IA32_MC14_STATUS", 0x439, -1)
    idc.AddConstEx(id, "IA32_MC15_STATUS", 0x43d, -1)
    idc.AddConstEx(id, "IA32_MC16_STATUS", 0x441, -1)
    idc.AddConstEx(id, "IA32_MC17_STATUS", 0x445, -1)
    idc.AddConstEx(id, "IA32_MC18_STATUS", 0x449, -1)
    idc.AddConstEx(id, "IA32_MC19_STATUS", 0x44d, -1)
    idc.AddConstEx(id, "IA32_MC20_STATUS", 0x451, -1)
    idc.AddConstEx(id, "IA32_MC21_STATUS", 0x455, -1)
    idc.AddConstEx(id, "IA32_MC22_STATUS", 0x459, -1)
    idc.AddConstEx(id, "IA32_MC23_STATUS", 0x45d, -1)
    idc.AddConstEx(id, "IA32_MC24_STATUS", 0x461, -1)
    idc.AddConstEx(id, "IA32_MC25_STATUS", 0x465, -1)
    idc.AddConstEx(id, "IA32_MC26_STATUS", 0x469, -1)
    idc.AddConstEx(id, "IA32_MC27_STATUS", 0x46d, -1)
    idc.AddConstEx(id, "IA32_MC28_STATUS", 0x471, -1)
    idc.AddConstEx(id, "IA32_MC0_ADDR", 0x402, -1)
    idc.AddConstEx(id, "IA32_MC1_ADDR", 0x406, -1)
    idc.AddConstEx(id, "IA32_MC2_ADDR", 0x40a, -1)
    idc.AddConstEx(id, "IA32_MC3_ADDR", 0x40e, -1)
    idc.AddConstEx(id, "IA32_MC4_ADDR", 0x412, -1)
    idc.AddConstEx(id, "IA32_MC5_ADDR", 0x416, -1)
    idc.AddConstEx(id, "IA32_MC6_ADDR", 0x41a, -1)
    idc.AddConstEx(id, "IA32_MC7_ADDR", 0x41e, -1)
    idc.AddConstEx(id, "IA32_MC8_ADDR", 0x422, -1)
    idc.AddConstEx(id, "IA32_MC9_ADDR", 0x426, -1)
    idc.AddConstEx(id, "IA32_MC10_ADDR", 0x42a, -1)
    idc.AddConstEx(id, "IA32_MC11_ADDR", 0x42e, -1)
    idc.AddConstEx(id, "IA32_MC12_ADDR", 0x432, -1)
    idc.AddConstEx(id, "IA32_MC13_ADDR", 0x436, -1)
    idc.AddConstEx(id, "IA32_MC14_ADDR", 0x43a, -1)
    idc.AddConstEx(id, "IA32_MC15_ADDR", 0x43e, -1)
    idc.AddConstEx(id, "IA32_MC16_ADDR", 0x442, -1)
    idc.AddConstEx(id, "IA32_MC17_ADDR", 0x446, -1)
    idc.AddConstEx(id, "IA32_MC18_ADDR", 0x44a, -1)
    idc.AddConstEx(id, "IA32_MC19_ADDR", 0x44e, -1)
    idc.AddConstEx(id, "IA32_MC20_ADDR", 0x452, -1)
    idc.AddConstEx(id, "IA32_MC21_ADDR", 0x456, -1)
    idc.AddConstEx(id, "IA32_MC22_ADDR", 0x45a, -1)
    idc.AddConstEx(id, "IA32_MC23_ADDR", 0x45e, -1)
    idc.AddConstEx(id, "IA32_MC24_ADDR", 0x462, -1)
    idc.AddConstEx(id, "IA32_MC25_ADDR", 0x466, -1)
    idc.AddConstEx(id, "IA32_MC26_ADDR", 0x46a, -1)
    idc.AddConstEx(id, "IA32_MC27_ADDR", 0x46e, -1)
    idc.AddConstEx(id, "IA32_MC28_ADDR", 0x472, -1)
    idc.AddConstEx(id, "IA32_MC0_MISC", 0x403, -1)
    idc.AddConstEx(id, "IA32_MC1_MISC", 0x407, -1)
    idc.AddConstEx(id, "IA32_MC2_MISC", 0x40b, -1)
    idc.AddConstEx(id, "IA32_MC3_MISC", 0x40f, -1)
    idc.AddConstEx(id, "IA32_MC4_MISC", 0x413, -1)
    idc.AddConstEx(id, "IA32_MC5_MISC", 0x417, -1)
    idc.AddConstEx(id, "IA32_MC6_MISC", 0x41b, -1)
    idc.AddConstEx(id, "IA32_MC7_MISC", 0x41f, -1)
    idc.AddConstEx(id, "IA32_MC8_MISC", 0x423, -1)
    idc.AddConstEx(id, "IA32_MC9_MISC", 0x427, -1)
    idc.AddConstEx(id, "IA32_MC10_MISC", 0x42b, -1)
    idc.AddConstEx(id, "IA32_MC11_MISC", 0x42f, -1)
    idc.AddConstEx(id, "IA32_MC12_MISC", 0x433, -1)
    idc.AddConstEx(id, "IA32_MC13_MISC", 0x437, -1)
    idc.AddConstEx(id, "IA32_MC14_MISC", 0x43b, -1)
    idc.AddConstEx(id, "IA32_MC15_MISC", 0x43f, -1)
    idc.AddConstEx(id, "IA32_MC16_MISC", 0x443, -1)
    idc.AddConstEx(id, "IA32_MC17_MISC", 0x447, -1)
    idc.AddConstEx(id, "IA32_MC18_MISC", 0x44b, -1)
    idc.AddConstEx(id, "IA32_MC19_MISC", 0x44f, -1)
    idc.AddConstEx(id, "IA32_MC20_MISC", 0x453, -1)
    idc.AddConstEx(id, "IA32_MC21_MISC", 0x457, -1)
    idc.AddConstEx(id, "IA32_MC22_MISC", 0x45b, -1)
    idc.AddConstEx(id, "IA32_MC23_MISC", 0x45f, -1)
    idc.AddConstEx(id, "IA32_MC24_MISC", 0x463, -1)
    idc.AddConstEx(id, "IA32_MC25_MISC", 0x467, -1)
    idc.AddConstEx(id, "IA32_MC26_MISC", 0x46b, -1)
    idc.AddConstEx(id, "IA32_MC27_MISC", 0x46f, -1)
    idc.AddConstEx(id, "IA32_MC28_MISC", 0x473, -1)
    idc.AddConstEx(id, "IA32_VMX_BASIC", 0x480, -1)
    idc.AddConstEx(id, "IA32_VMX_PINBASED_CTLS", 0x481, -1)
    idc.AddConstEx(id, "IA32_VMX_PROCBASED_CTLS", 0x482, -1)
    idc.AddConstEx(id, "IA32_VMX_EXIT_CTLS", 0x483, -1)
    idc.AddConstEx(id, "IA32_VMX_ENTRY_CTLS", 0x484, -1)
    idc.AddConstEx(id, "IA32_VMX_MISC", 0x485, -1)
    idc.AddConstEx(id, "IA32_VMX_CR0_FIXED0", 0x486, -1)
    idc.AddConstEx(id, "IA32_VMX_CR0_FIXED1", 0x487, -1)
    idc.AddConstEx(id, "IA32_VMX_CR4_FIXED0", 0x488, -1)
    idc.AddConstEx(id, "IA32_VMX_CR4_FIXED1", 0x489, -1)
    idc.AddConstEx(id, "IA32_VMX_VMCS_ENUM", 0x48a, -1)
    idc.AddConstEx(id, "IA32_VMX_PROCBASED_CTLS2", 0x48b, -1)
    idc.AddConstEx(id, "IA32_VMX_EPT_VPID_CAP", 0x48c, -1)
    idc.AddConstEx(id, "IA32_VMX_TRUE_PINBASED_CTLS", 0x48d, -1)
    idc.AddConstEx(id, "IA32_VMX_TRUE_PROCBASED_CTLS", 0x48e, -1)
    idc.AddConstEx(id, "IA32_VMX_TRUE_EXIT_CTLS", 0x48f, -1)
    idc.AddConstEx(id, "IA32_VMX_TRUE_ENTRY_CTLS", 0x490, -1)
    idc.AddConstEx(id, "IA32_VMX_VMFUNC", 0x491, -1)
    idc.AddConstEx(id, "IA32_A_PMC0", 0x4c1, -1)
    idc.AddConstEx(id, "IA32_A_PMC1", 0x4c2, -1)
    idc.AddConstEx(id, "IA32_A_PMC2", 0x4c3, -1)
    idc.AddConstEx(id, "IA32_A_PMC3", 0x4c4, -1)
    idc.AddConstEx(id, "IA32_A_PMC4", 0x4c5, -1)
    idc.AddConstEx(id, "IA32_A_PMC5", 0x4c6, -1)
    idc.AddConstEx(id, "IA32_A_PMC6", 0x4c7, -1)
    idc.AddConstEx(id, "IA32_A_PMC7", 0x4c8, -1)
    idc.AddConstEx(id, "IA32_MCG_EXT_CTL", 0x4d0, -1)
    idc.AddConstEx(id, "IA32_SGX_SVN_STATUS", 0x500, -1)
    idc.AddConstEx(id, "IA32_RTIT_OUTPUT_BASE", 0x560, -1)
    idc.AddConstEx(id, "IA32_RTIT_OUTPUT_MASK_PTRS", 0x561, -1)
    idc.AddConstEx(id, "IA32_RTIT_CTL", 0x570, -1)
    idc.AddConstEx(id, "IA32_RTIT_STATUS", 0x571, -1)
    idc.AddConstEx(id, "IA32_RTIT_CR3_MATCH", 0x572, -1)
    idc.AddConstEx(id, "IA32_RTIT_ADDR0_A", 0x580, -1)
    idc.AddConstEx(id, "IA32_RTIT_ADDR1_A", 0x582, -1)
    idc.AddConstEx(id, "IA32_RTIT_ADDR2_A", 0x584, -1)
    idc.AddConstEx(id, "IA32_RTIT_ADDR3_A", 0x586, -1)
    idc.AddConstEx(id, "IA32_RTIT_ADDR0_B", 0x581, -1)
    idc.AddConstEx(id, "IA32_RTIT_ADDR1_B", 0x583, -1)
    idc.AddConstEx(id, "IA32_RTIT_ADDR2_B", 0x585, -1)
    idc.AddConstEx(id, "IA32_RTIT_ADDR3_B", 0x587, -1)
    idc.AddConstEx(id, "IA32_DS_AREA", 0x600, -1)
    idc.AddConstEx(id, "IA32_TSC_DEADLINE", 0x6e0, -1)
    idc.AddConstEx(id, "IA32_PM_ENABLE", 0x770, -1)
    idc.AddConstEx(id, "IA32_HWP_CAPABILITIES", 0x771, -1)
    idc.AddConstEx(id, "IA32_HWP_REQUEST_PKG", 0x772, -1)
    idc.AddConstEx(id, "IA32_HWP_INTERRUPT", 0x773, -1)
    idc.AddConstEx(id, "IA32_HWP_REQUEST", 0x774, -1)
    idc.AddConstEx(id, "IA32_HWP_STATUS", 0x777, -1)
    idc.AddConstEx(id, "IA32_X2APIC_APICID", 0x802, -1)
    idc.AddConstEx(id, "IA32_X2APIC_VERSION", 0x803, -1)
    idc.AddConstEx(id, "IA32_X2APIC_TPR", 0x808, -1)
    idc.AddConstEx(id, "IA32_X2APIC_PPR", 0x80a, -1)
    idc.AddConstEx(id, "IA32_X2APIC_EOI", 0x80b, -1)
    idc.AddConstEx(id, "IA32_X2APIC_LDR", 0x80d, -1)
    idc.AddConstEx(id, "IA32_X2APIC_SIVR", 0x80f, -1)
    idc.AddConstEx(id, "IA32_X2APIC_ISR0", 0x810, -1)
    idc.AddConstEx(id, "IA32_X2APIC_ISR1", 0x811, -1)
    idc.AddConstEx(id, "IA32_X2APIC_ISR2", 0x812, -1)
    idc.AddConstEx(id, "IA32_X2APIC_ISR3", 0x813, -1)
    idc.AddConstEx(id, "IA32_X2APIC_ISR4", 0x814, -1)
    idc.AddConstEx(id, "IA32_X2APIC_ISR5", 0x815, -1)
    idc.AddConstEx(id, "IA32_X2APIC_ISR6", 0x816, -1)
    idc.AddConstEx(id, "IA32_X2APIC_ISR7", 0x817, -1)
    idc.AddConstEx(id, "IA32_X2APIC_TMR0", 0x818, -1)
    idc.AddConstEx(id, "IA32_X2APIC_TMR1", 0x819, -1)
    idc.AddConstEx(id, "IA32_X2APIC_TMR2", 0x81a, -1)
    idc.AddConstEx(id, "IA32_X2APIC_TMR3", 0x81b, -1)
    idc.AddConstEx(id, "IA32_X2APIC_TMR4", 0x81c, -1)
    idc.AddConstEx(id, "IA32_X2APIC_TMR5", 0x81d, -1)
    idc.AddConstEx(id, "IA32_X2APIC_TMR6", 0x81e, -1)
    idc.AddConstEx(id, "IA32_X2APIC_TMR7", 0x81f, -1)
    idc.AddConstEx(id, "IA32_X2APIC_IRR0", 0x820, -1)
    idc.AddConstEx(id, "IA32_X2APIC_IRR1", 0x821, -1)
    idc.AddConstEx(id, "IA32_X2APIC_IRR2", 0x822, -1)
    idc.AddConstEx(id, "IA32_X2APIC_IRR3", 0x823, -1)
    idc.AddConstEx(id, "IA32_X2APIC_IRR4", 0x824, -1)
    idc.AddConstEx(id, "IA32_X2APIC_IRR5", 0x825, -1)
    idc.AddConstEx(id, "IA32_X2APIC_IRR6", 0x826, -1)
    idc.AddConstEx(id, "IA32_X2APIC_IRR7", 0x827, -1)
    idc.AddConstEx(id, "IA32_X2APIC_ESR", 0x828, -1)
    idc.AddConstEx(id, "IA32_X2APIC_LVT_CMCI", 0x82f, -1)
    idc.AddConstEx(id, "IA32_X2APIC_ICR", 0x830, -1)
    idc.AddConstEx(id, "IA32_X2APIC_LVT_TIMER", 0x832, -1)
    idc.AddConstEx(id, "IA32_X2APIC_LVT_THERMAL", 0x833, -1)
    idc.AddConstEx(id, "IA32_X2APIC_LVT_PMI", 0x834, -1)
    idc.AddConstEx(id, "IA32_X2APIC_LVT_LINT0", 0x835, -1)
    idc.AddConstEx(id, "IA32_X2APIC_LVT_LINT1", 0x836, -1)
    idc.AddConstEx(id, "IA32_X2APIC_LVT_ERROR", 0x837, -1)
    idc.AddConstEx(id, "IA32_X2APIC_INIT_COUNT", 0x838, -1)
    idc.AddConstEx(id, "IA32_X2APIC_CUR_COUNT", 0x839, -1)
    idc.AddConstEx(id, "IA32_X2APIC_DIV_CONF", 0x83e, -1)
    idc.AddConstEx(id, "IA32_X2APIC_SELF_IPI", 0x83f, -1)
    idc.AddConstEx(id, "IA32_DEBUG_INTERFACE", 0xc80, -1)
    idc.AddConstEx(id, "IA32_L2_QOS_CFG", 0xc82, -1)
    idc.AddConstEx(id, "IA32_QM_CTR", 0xc8e, -1)
    idc.AddConstEx(id, "IA32_BNDCFGS", 0xd90, -1)
    idc.AddConstEx(id, "IA32_XSS", 0xda0, -1)
    idc.AddConstEx(id, "IA32_PKG_HDC_CTL", 0xdb0, -1)
    idc.AddConstEx(id, "IA32_PM_CTL1", 0xdb1, -1)
    idc.AddConstEx(id, "IA32_THREAD_STALL", 0xdb2, -1)
    idc.AddConstEx(id, "IA32_EFER", 0xc0000080, -1)
    idc.AddConstEx(id, "IA32_STAR", 0xc0000081, -1)
    idc.AddConstEx(id, "IA32_LSTAR", 0xc0000082, -1)
    idc.AddConstEx(id, "IA32_CSTAR", 0xc0000083, -1)
    idc.AddConstEx(id, "IA32_FMASK", 0xc0000084, -1)
    idc.AddConstEx(id, "IA32_FS_BASE", 0xc0000100, -1)
    idc.AddConstEx(id, "IA32_GS_BASE", 0xc0000101, -1)
    idc.AddConstEx(id, "IA32_KERNEL_GS_BASE", 0xc0000102, -1)
    idc.AddConstEx(id, "IA32_TSC_AUX", 0xc0000103, -1)
    # ------------------------------------------------------------------------------------- #
    # VMCS 
    # ------------------------------------------------------------------------------------- #
    id = idc.AddEnum(0, "IA32_VMCS_LIST_ENUM", idaapi.hexflag())
    idc.AddConstEx(id, "VMCS_CTRL_VPID", 0x0, -1)
    idc.AddConstEx(id, "VMCS_CTRL_POSTED_INTR_NOTIFY_VECTOR", 0x2, -1)
    idc.AddConstEx(id, "VMCS_CTRL_EPTP_INDEX", 0x4, -1)
    idc.AddConstEx(id, "VMCS_GUEST_ES_SEL", 0x800, -1)
    idc.AddConstEx(id, "VMCS_GUEST_CS_SEL", 0x802, -1)
    idc.AddConstEx(id, "VMCS_GUEST_SS_SEL", 0x804, -1)
    idc.AddConstEx(id, "VMCS_GUEST_DS_SEL", 0x806, -1)
    idc.AddConstEx(id, "VMCS_GUEST_FS_SEL", 0x808, -1)
    idc.AddConstEx(id, "VMCS_GUEST_GS_SEL", 0x80a, -1)
    idc.AddConstEx(id, "VMCS_GUEST_LDTR_SEL", 0x80c, -1)
    idc.AddConstEx(id, "VMCS_GUEST_TR_SEL", 0x80e, -1)
    idc.AddConstEx(id, "VMCS_GUEST_INTR_STATUS", 0x810, -1)
    idc.AddConstEx(id, "VMCS_GUEST_PML_INDEX", 0x812, -1)
    idc.AddConstEx(id, "VMCS_HOST_ES_SEL", 0xc00, -1)
    idc.AddConstEx(id, "VMCS_HOST_CS_SEL", 0xc02, -1)
    idc.AddConstEx(id, "VMCS_HOST_SS_SEL", 0xc04, -1)
    idc.AddConstEx(id, "VMCS_HOST_DS_SEL", 0xc06, -1)
    idc.AddConstEx(id, "VMCS_HOST_FS_SEL", 0xc08, -1)
    idc.AddConstEx(id, "VMCS_HOST_GS_SEL", 0xc0a, -1)
    idc.AddConstEx(id, "VMCS_HOST_TR_SEL", 0xc0c, -1)
    idc.AddConstEx(id, "VMCS_CTRL_IO_BITMAP_A", 0x2000, -1)
    idc.AddConstEx(id, "VMCS_CTRL_IO_BITMAP_B", 0x2002, -1)
    idc.AddConstEx(id, "VMCS_CTRL_MSR_BITMAP", 0x2004, -1)
    idc.AddConstEx(id, "VMCS_CTRL_VMEXIT_MSR_STORE", 0x2006, -1)
    idc.AddConstEx(id, "VMCS_CTRL_VMEXIT_MSR_LOAD", 0x2008, -1)
    idc.AddConstEx(id, "VMCS_CTRL_VMENTRY_MSR_LOAD", 0x200a, -1)
    idc.AddConstEx(id, "VMCS_CTRL_EXEC_VMCS_PTR", 0x200c, -1)
    idc.AddConstEx(id, "VMCS_CTRL_PML_ADDR", 0x200e, -1)
    idc.AddConstEx(id, "VMCS_CTRL_TSC_OFFSET", 0x2010, -1)
    idc.AddConstEx(id, "VMCS_CTRL_VAPIC_PAGEADDR", 0x2012, -1)
    idc.AddConstEx(id, "VMCS_CTRL_APIC_ACCESSADDR", 0x2014, -1)
    idc.AddConstEx(id, "VMCS_CTRL_POSTED_INTR_DESC", 0x2016, -1)
    idc.AddConstEx(id, "VMCS_CTRL_VMFUNC_CTRLS", 0x2018, -1)
    idc.AddConstEx(id, "VMCS_CTRL_EPTP", 0x201a, -1)
    idc.AddConstEx(id, "VMCS_CTRL_EOI_BITMAP_0", 0x201c, -1)
    idc.AddConstEx(id, "VMCS_CTRL_EOI_BITMAP_1", 0x201e, -1)
    idc.AddConstEx(id, "VMCS_CTRL_EOI_BITMAP_2", 0x2020, -1)
    idc.AddConstEx(id, "VMCS_CTRL_EOI_BITMAP_3", 0x2022, -1)
    idc.AddConstEx(id, "VMCS_CTRL_EPTP_LIST", 0x2024, -1)
    idc.AddConstEx(id, "VMCS_CTRL_VMREAD_BITMAP", 0x2026, -1)
    idc.AddConstEx(id, "VMCS_CTRL_VMWRITE_BITMAP", 0x2028, -1)
    idc.AddConstEx(id, "VMCS_CTRL_VIRTXCPT_INFO_ADDR", 0x202a, -1)
    idc.AddConstEx(id, "VMCS_CTRL_XSS_EXITING_BITMAP", 0x202c, -1)
    idc.AddConstEx(id, "VMCS_CTRL_ENCLS_EXITING_BITMAP", 0x202e, -1)
    idc.AddConstEx(id, "VMCS_CTRL_TSC_MULTIPLIER", 0x2032, -1)
    idc.AddConstEx(id, "VMCS_GUEST_PHYS_ADDR", 0x2400, -1)
    idc.AddConstEx(id, "VMCS_GUEST_VMCS_LINK_PTR", 0x2800, -1)
    idc.AddConstEx(id, "VMCS_GUEST_DEBUGCTL", 0x2802, -1)
    idc.AddConstEx(id, "VMCS_GUEST_PAT", 0x2804, -1)
    idc.AddConstEx(id, "VMCS_GUEST_EFER", 0x2806, -1)
    idc.AddConstEx(id, "VMCS_GUEST_PERF_GLOBAL_CTRL", 0x2808, -1)
    idc.AddConstEx(id, "VMCS_GUEST_PDPTE0", 0x280a, -1)
    idc.AddConstEx(id, "VMCS_GUEST_PDPTE1", 0x280c, -1)
    idc.AddConstEx(id, "VMCS_GUEST_PDPTE2", 0x280e, -1)
    idc.AddConstEx(id, "VMCS_GUEST_PDPTE3", 0x2810, -1)
    idc.AddConstEx(id, "VMCS_HOST_PAT", 0x2c00, -1)
    idc.AddConstEx(id, "VMCS_HOST_EFER", 0x2c02, -1)
    idc.AddConstEx(id, "VMCS_HOST_PERF_GLOBAL_CTRL", 0x2c04, -1)
    idc.AddConstEx(id, "VMCS_CTRL_PIN_EXEC", 0x4000, -1)
    idc.AddConstEx(id, "VMCS_CTRL_PROC_EXEC", 0x4002, -1)
    idc.AddConstEx(id, "VMCS_CTRL_EXCEPTION_BITMAP", 0x4004, -1)
    idc.AddConstEx(id, "VMCS_CTRL_PAGEFAULT_ERROR_MASK", 0x4006, -1)
    idc.AddConstEx(id, "VMCS_CTRL_PAGEFAULT_ERROR_MATCH", 0x4008, -1)
    idc.AddConstEx(id, "VMCS_CTRL_CR3_TARGET_COUNT", 0x400a, -1)
    idc.AddConstEx(id, "VMCS_CTRL_EXIT", 0x400c, -1)
    idc.AddConstEx(id, "VMCS_CTRL_EXIT_MSR_STORE_COUNT", 0x400e, -1)
    idc.AddConstEx(id, "VMCS_CTRL_EXIT_MSR_LOAD_COUNT", 0x4010, -1)
    idc.AddConstEx(id, "VMCS_CTRL_ENTRY", 0x4012, -1)
    idc.AddConstEx(id, "VMCS_CTRL_ENTRY_MSR_LOAD_COUNT", 0x4014, -1)
    idc.AddConstEx(id, "VMCS_CTRL_ENTRY_INTERRUPTION_INFO", 0x4016, -1)
    idc.AddConstEx(id, "VMCS_CTRL_ENTRY_EXCEPTION_ERRCODE", 0x4018, -1)
    idc.AddConstEx(id, "VMCS_CTRL_ENTRY_INSTR_LENGTH", 0x401a, -1)
    idc.AddConstEx(id, "VMCS_CTRL_TPR_THRESHOLD", 0x401c, -1)
    idc.AddConstEx(id, "VMCS_CTRL_PROC_EXEC2", 0x401e, -1)
    idc.AddConstEx(id, "VMCS_CTRL_PLE_GAP", 0x4020, -1)
    idc.AddConstEx(id, "VMCS_CTRL_PLE_WINDOW", 0x4022, -1)
    idc.AddConstEx(id, "VMCS_VM_INSTR_ERROR", 0x4400, -1)
    idc.AddConstEx(id, "VMCS_EXIT_REASON", 0x4402, -1)
    idc.AddConstEx(id, "VMCS_EXIT_INTERRUPTION_INFO", 0x4404, -1)
    idc.AddConstEx(id, "VMCS_EXIT_INTERRUPTION_ERROR_CODE", 0x4406, -1)
    idc.AddConstEx(id, "VMCS_IDT_VECTORING_INFO", 0x4408, -1)
    idc.AddConstEx(id, "VMCS_IDT_VECTORING_ERROR_CODE", 0x440a, -1)
    idc.AddConstEx(id, "VMCS_EXIT_INSTR_LENGTH", 0x440c, -1)
    idc.AddConstEx(id, "VMCS_EXIT_INSTR_INFO", 0x440e, -1)
    idc.AddConstEx(id, "VMCS_GUEST_ES_LIMIT", 0x4800, -1)
    idc.AddConstEx(id, "VMCS_GUEST_CS_LIMIT", 0x4802, -1)
    idc.AddConstEx(id, "VMCS_GUEST_SS_LIMIT", 0x4804, -1)
    idc.AddConstEx(id, "VMCS_GUEST_DS_LIMIT", 0x4806, -1)
    idc.AddConstEx(id, "VMCS_GUEST_FS_LIMIT", 0x4808, -1)
    idc.AddConstEx(id, "VMCS_GUEST_GS_LIMIT", 0x480a, -1)
    idc.AddConstEx(id, "VMCS_GUEST_LDTR_LIMIT", 0x480c, -1)
    idc.AddConstEx(id, "VMCS_GUEST_TR_LIMIT", 0x480e, -1)
    idc.AddConstEx(id, "VMCS_GUEST_GDTR_LIMIT", 0x4810, -1)
    idc.AddConstEx(id, "VMCS_GUEST_IDTR_LIMIT", 0x4812, -1)
    idc.AddConstEx(id, "VMCS_GUEST_ES_ACCESS_RIGHTS", 0x4814, -1)
    idc.AddConstEx(id, "VMCS_GUEST_CS_ACCESS_RIGHTS", 0x4816, -1)
    idc.AddConstEx(id, "VMCS_GUEST_SS_ACCESS_RIGHTS", 0x4818, -1)
    idc.AddConstEx(id, "VMCS_GUEST_DS_ACCESS_RIGHTS", 0x481a, -1)
    idc.AddConstEx(id, "VMCS_GUEST_FS_ACCESS_RIGHTS", 0x481c, -1)
    idc.AddConstEx(id, "VMCS_GUEST_GS_ACCESS_RIGHTS", 0x481e, -1)
    idc.AddConstEx(id, "VMCS_GUEST_LDTR_ACCESS_RIGHTS", 0x4820, -1)
    idc.AddConstEx(id, "VMCS_GUEST_TR_ACCESS_RIGHTS", 0x4822, -1)
    idc.AddConstEx(id, "VMCS_GUEST_INTERRUPTIBILITY_STATE", 0x4824, -1)
    idc.AddConstEx(id, "VMCS_GUEST_ACTIVITY_STATE", 0x4826, -1)
    idc.AddConstEx(id, "VMCS_GUEST_SMBASE", 0x4828, -1)
    idc.AddConstEx(id, "VMCS_GUEST_SYSENTER_CS", 0x482a, -1)
    idc.AddConstEx(id, "VMCS_GUEST_PREEMPT_TIMER_VALUE", 0x482e, -1)
    idc.AddConstEx(id, "VMCS_SYSENTER_CS", 0x4c00, -1)
    idc.AddConstEx(id, "VMCS_CTRL_CR0_MASK", 0x6000, -1)
    idc.AddConstEx(id, "VMCS_CTRL_CR4_MASK", 0x6002, -1)
    idc.AddConstEx(id, "VMCS_CTRL_CR0_READ_SHADOW", 0x6004, -1)
    idc.AddConstEx(id, "VMCS_CTRL_CR4_READ_SHADOW", 0x6006, -1)
    idc.AddConstEx(id, "VMCS_CTRL_CR3_TARGET_VAL0", 0x6008, -1)
    idc.AddConstEx(id, "VMCS_CTRL_CR3_TARGET_VAL1", 0x600a, -1)
    idc.AddConstEx(id, "VMCS_CTRL_CR3_TARGET_VAL2", 0x600c, -1)
    idc.AddConstEx(id, "VMCS_CTRL_CR3_TARGET_VAL3", 0x600e, -1)
    idc.AddConstEx(id, "VMCS_EXIT_QUALIFICATION", 0x6400, -1)
    idc.AddConstEx(id, "VMCS_IO_RCX", 0x6402, -1)
    idc.AddConstEx(id, "VMCS_IO_RSX", 0x6404, -1)
    idc.AddConstEx(id, "VMCS_IO_RDI", 0x6406, -1)
    idc.AddConstEx(id, "VMCS_IO_RIP", 0x6408, -1)
    idc.AddConstEx(id, "VMCS_EXIT_GUEST_LINEAR_ADDR", 0x640a, -1)
    idc.AddConstEx(id, "VMCS_GUEST_CR0", 0x6800, -1)
    idc.AddConstEx(id, "VMCS_GUEST_CR3", 0x6802, -1)
    idc.AddConstEx(id, "VMCS_GUEST_CR4", 0x6804, -1)
    idc.AddConstEx(id, "VMCS_GUEST_ES_BASE", 0x6806, -1)
    idc.AddConstEx(id, "VMCS_GUEST_CS_BASE", 0x6808, -1)
    idc.AddConstEx(id, "VMCS_GUEST_SS_BASE", 0x680a, -1)
    idc.AddConstEx(id, "VMCS_GUEST_DS_BASE", 0x680c, -1)
    idc.AddConstEx(id, "VMCS_GUEST_FS_BASE", 0x680e, -1)
    idc.AddConstEx(id, "VMCS_GUEST_GS_BASE", 0x6810, -1)
    idc.AddConstEx(id, "VMCS_GUEST_LDTR_BASE", 0x6812, -1)
    idc.AddConstEx(id, "VMCS_GUEST_TR_BASE", 0x6814, -1)
    idc.AddConstEx(id, "VMCS_GUEST_GDTR_BASE", 0x6816, -1)
    idc.AddConstEx(id, "VMCS_GUEST_IDTR_BASE", 0x6818, -1)
    idc.AddConstEx(id, "VMCS_GUEST_DR7", 0x681a, -1)
    idc.AddConstEx(id, "VMCS_GUEST_RSP", 0x681c, -1)
    idc.AddConstEx(id, "VMCS_GUEST_RIP", 0x681e, -1)
    idc.AddConstEx(id, "VMCS_GUEST_RFLAGS", 0x6820, -1)
    idc.AddConstEx(id, "VMCS_GUEST_PENDING_DEBUG_EXCEPTIONS", 0x6822, -1)
    idc.AddConstEx(id, "VMCS_GUEST_SYSENTER_ESP", 0x6824, -1)
    idc.AddConstEx(id, "VMCS_GUEST_SYSENTER_EIP", 0x6826, -1)
    idc.AddConstEx(id, "VMCS_HOST_CR0", 0x6c00, -1)
    idc.AddConstEx(id, "VMCS_HOST_CR3", 0x6c02, -1)
    idc.AddConstEx(id, "VMCS_HOST_CR4", 0x6c04, -1)
    idc.AddConstEx(id, "VMCS_HOST_FS_BASE", 0x6c06, -1)
    idc.AddConstEx(id, "VMCS_HOST_GS_BASE", 0x6c08, -1)
    idc.AddConstEx(id, "VMCS_HOST_TR_BASE", 0x6c0a, -1)
    idc.AddConstEx(id, "VMCS_HOST_GDTR_BASE", 0x6c0c, -1)
    idc.AddConstEx(id, "VMCS_HOST_IDTR_BASE", 0x6c0e, -1)
    idc.AddConstEx(id, "VMCS_HOST_SYSENTER_ESP", 0x6c10, -1)
    idc.AddConstEx(id, "VMCS_HOST_SYSENTER_EIP", 0x6c12, -1)
    idc.AddConstEx(id, "VMCS_HOST_RSP", 0x6c14, -1)
    idc.AddConstEx(id, "VMCS_HOST_RIP", 0x6c16, -1)
    idc.AddConstEx(id, "VMCS_GUEST_PDPTR3_HIGH", 0x2811, -1)
    idc.AddConstEx(id, "VMCS_GUEST_BNDCFGS", 0x2812, -1)
    idc.AddConstEx(id, "VMCS_GUEST_BNDCFGS_HIGH", 0x2813, -1)
    idc.AddConstEx(id, "VMCS_GUEST_RTIT_CTL", 0x2814, -1)
    idc.AddConstEx(id, "VMCS_GUEST_RTIT_CTL_HIGH", 0x2815, -1)

# ------------------------------------------------------------------------------------- #
class KnowledgeDb():
    def __init__(self):
        self.ia32_vmcs_db = {
            0x00000000 : 'VMCS_CTRL_VPID',
            0x00000002 : 'VMCS_CTRL_POSTED_INTR_NOTIFY_VECTOR',
            0x00000004 : 'VMCS_CTRL_EPTP_INDEX',
            0x00000800 : 'VMCS_GUEST_ES_SEL',
            0x00000802 : 'VMCS_GUEST_CS_SEL',
            0x00000804 : 'VMCS_GUEST_SS_SEL',
            0x00000806 : 'VMCS_GUEST_DS_SEL',
            0x00000808 : 'VMCS_GUEST_FS_SEL',
            0x0000080A : 'VMCS_GUEST_GS_SEL',
            0x0000080C : 'VMCS_GUEST_LDTR_SEL',
            0x0000080E : 'VMCS_GUEST_TR_SEL',
            0x00000810 : 'VMCS_GUEST_INTR_STATUS',
            0x00000812 : 'VMCS_GUEST_PML_INDEX',
            0x00000C00 : 'VMCS_HOST_ES_SEL',
            0x00000C02 : 'VMCS_HOST_CS_SEL',
            0x00000C04 : 'VMCS_HOST_SS_SEL',
            0x00000C06 : 'VMCS_HOST_DS_SEL',
            0x00000C08 : 'VMCS_HOST_FS_SEL',
            0x00000C0A : 'VMCS_HOST_GS_SEL',
            0x00000C0C : 'VMCS_HOST_TR_SEL',
            0x00002000 : 'VMCS_CTRL_IO_BITMAP_A',
            0x00002002 : 'VMCS_CTRL_IO_BITMAP_B',
            0x00002004 : 'VMCS_CTRL_MSR_BITMAP',
            0x00002006 : 'VMCS_CTRL_VMEXIT_MSR_STORE',
            0x00002008 : 'VMCS_CTRL_VMEXIT_MSR_LOAD',
            0x0000200A : 'VMCS_CTRL_VMENTRY_MSR_LOAD',
            0x0000200C : 'VMCS_CTRL_EXEC_VMCS_PTR',
            0x0000200E : 'VMCS_CTRL_PML_ADDR',
            0x00002010 : 'VMCS_CTRL_TSC_OFFSET',
            0x00002012 : 'VMCS_CTRL_VAPIC_PAGEADDR',
            0x00002014 : 'VMCS_CTRL_APIC_ACCESSADDR',
            0x00002016 : 'VMCS_CTRL_POSTED_INTR_DESC',
            0x00002018 : 'VMCS_CTRL_VMFUNC_CTRLS',
            0x0000201A : 'VMCS_CTRL_EPTP',
            0x0000201C : 'VMCS_CTRL_EOI_BITMAP_0',
            0x0000201E : 'VMCS_CTRL_EOI_BITMAP_1',
            0x00002020 : 'VMCS_CTRL_EOI_BITMAP_2',
            0x00002022 : 'VMCS_CTRL_EOI_BITMAP_3',
            0x00002024 : 'VMCS_CTRL_EPTP_LIST',
            0x00002026 : 'VMCS_CTRL_VMREAD_BITMAP',
            0x00002028 : 'VMCS_CTRL_VMWRITE_BITMAP',
            0x0000202A : 'VMCS_CTRL_VIRTXCPT_INFO_ADDR',
            0x0000202C : 'VMCS_CTRL_XSS_EXITING_BITMAP',
            0x0000202E : 'VMCS_CTRL_ENCLS_EXITING_BITMAP',
            0x00002032 : 'VMCS_CTRL_TSC_MULTIPLIER',
            0x00002400 : 'VMCS_GUEST_PHYS_ADDR',
            0x00002800 : 'VMCS_GUEST_VMCS_LINK_PTR',
            0x00002802 : 'VMCS_GUEST_DEBUGCTL',
            0x00002804 : 'VMCS_GUEST_PAT',
            0x00002806 : 'VMCS_GUEST_EFER',
            0x00002808 : 'VMCS_GUEST_PERF_GLOBAL_CTRL',
            0x0000280A : 'VMCS_GUEST_PDPTE0',
            0x0000280C : 'VMCS_GUEST_PDPTE1',
            0x0000280E : 'VMCS_GUEST_PDPTE2',
            0x00002810 : 'VMCS_GUEST_PDPTE3',
            0x00002C00 : 'VMCS_HOST_PAT',
            0x00002C02 : 'VMCS_HOST_EFER',
            0x00002C04 : 'VMCS_HOST_PERF_GLOBAL_CTRL',
            0x00004000 : 'VMCS_CTRL_PIN_EXEC',
            0x00004002 : 'VMCS_CTRL_PROC_EXEC',
            0x00004004 : 'VMCS_CTRL_EXCEPTION_BITMAP',
            0x00004006 : 'VMCS_CTRL_PAGEFAULT_ERROR_MASK',
            0x00004008 : 'VMCS_CTRL_PAGEFAULT_ERROR_MATCH',
            0x0000400A : 'VMCS_CTRL_CR3_TARGET_COUNT',
            0x0000400C : 'VMCS_CTRL_EXIT',
            0x0000400E : 'VMCS_CTRL_EXIT_MSR_STORE_COUNT',
            0x00004010 : 'VMCS_CTRL_EXIT_MSR_LOAD_COUNT',
            0x00004012 : 'VMCS_CTRL_ENTRY',
            0x00004014 : 'VMCS_CTRL_ENTRY_MSR_LOAD_COUNT',
            0x00004016 : 'VMCS_CTRL_ENTRY_INTERRUPTION_INFO',
            0x00004018 : 'VMCS_CTRL_ENTRY_EXCEPTION_ERRCODE',
            0x0000401A : 'VMCS_CTRL_ENTRY_INSTR_LENGTH',
            0x0000401C : 'VMCS_CTRL_TPR_THRESHOLD',
            0x0000401E : 'VMCS_CTRL_PROC_EXEC2',
            0x00004020 : 'VMCS_CTRL_PLE_GAP',
            0x00004022 : 'VMCS_CTRL_PLE_WINDOW',
            0x00004400 : 'VMCS_VM_INSTR_ERROR',
            0x00004402 : 'VMCS_EXIT_REASON',
            0x00004404 : 'VMCS_EXIT_INTERRUPTION_INFO',
            0x00004406 : 'VMCS_EXIT_INTERRUPTION_ERROR_CODE',
            0x00004408 : 'VMCS_IDT_VECTORING_INFO',
            0x0000440A : 'VMCS_IDT_VECTORING_ERROR_CODE',
            0x0000440C : 'VMCS_EXIT_INSTR_LENGTH',
            0x0000440E : 'VMCS_EXIT_INSTR_INFO',
            0x00004800 : 'VMCS_GUEST_ES_LIMIT',
            0x00004802 : 'VMCS_GUEST_CS_LIMIT',
            0x00004804 : 'VMCS_GUEST_SS_LIMIT',
            0x00004806 : 'VMCS_GUEST_DS_LIMIT',
            0x00004808 : 'VMCS_GUEST_FS_LIMIT',
            0x0000480A : 'VMCS_GUEST_GS_LIMIT',
            0x0000480C : 'VMCS_GUEST_LDTR_LIMIT',
            0x0000480E : 'VMCS_GUEST_TR_LIMIT',
            0x00004810 : 'VMCS_GUEST_GDTR_LIMIT',
            0x00004812 : 'VMCS_GUEST_IDTR_LIMIT',
            0x00004814 : 'VMCS_GUEST_ES_ACCESS_RIGHTS',
            0x00004816 : 'VMCS_GUEST_CS_ACCESS_RIGHTS',
            0x00004818 : 'VMCS_GUEST_SS_ACCESS_RIGHTS',
            0x0000481A : 'VMCS_GUEST_DS_ACCESS_RIGHTS',
            0x0000481C : 'VMCS_GUEST_FS_ACCESS_RIGHTS',
            0x0000481E : 'VMCS_GUEST_GS_ACCESS_RIGHTS',
            0x00004820 : 'VMCS_GUEST_LDTR_ACCESS_RIGHTS',
            0x00004822 : 'VMCS_GUEST_TR_ACCESS_RIGHTS',
            0x00004824 : 'VMCS_GUEST_INTERRUPTIBILITY_STATE',
            0x00004826 : 'VMCS_GUEST_ACTIVITY_STATE',
            0x00004828 : 'VMCS_GUEST_SMBASE',
            0x0000482A : 'VMCS_GUEST_SYSENTER_CS',
            0x0000482E : 'VMCS_GUEST_PREEMPT_TIMER_VALUE',
            0x00004C00 : 'VMCS_SYSENTER_CS',
            0x00006000 : 'VMCS_CTRL_CR0_MASK',
            0x00006002 : 'VMCS_CTRL_CR4_MASK',
            0x00006004 : 'VMCS_CTRL_CR0_READ_SHADOW',
            0x00006006 : 'VMCS_CTRL_CR4_READ_SHADOW',
            0x00006008 : 'VMCS_CTRL_CR3_TARGET_VAL0',
            0x0000600A : 'VMCS_CTRL_CR3_TARGET_VAL1',
            0x0000600C : 'VMCS_CTRL_CR3_TARGET_VAL2',
            0x0000600E : 'VMCS_CTRL_CR3_TARGET_VAL3',
            0x00006400 : 'VMCS_EXIT_QUALIFICATION',
            0x00006402 : 'VMCS_IO_RCX',
            0x00006404 : 'VMCS_IO_RSX',
            0x00006406 : 'VMCS_IO_RDI',
            0x00006408 : 'VMCS_IO_RIP',
            0x0000640A : 'VMCS_EXIT_GUEST_LINEAR_ADDR',
            0x00006800 : 'VMCS_GUEST_CR0',
            0x00006802 : 'VMCS_GUEST_CR3',
            0x00006804 : 'VMCS_GUEST_CR4',
            0x00006806 : 'VMCS_GUEST_ES_BASE',
            0x00006808 : 'VMCS_GUEST_CS_BASE',
            0x0000680A : 'VMCS_GUEST_SS_BASE',
            0x0000680C : 'VMCS_GUEST_DS_BASE',
            0x0000680E : 'VMCS_GUEST_FS_BASE',
            0x00006810 : 'VMCS_GUEST_GS_BASE',
            0x00006812 : 'VMCS_GUEST_LDTR_BASE',
            0x00006814 : 'VMCS_GUEST_TR_BASE',
            0x00006816 : 'VMCS_GUEST_GDTR_BASE',
            0x00006818 : 'VMCS_GUEST_IDTR_BASE',
            0x0000681A : 'VMCS_GUEST_DR7',
            0x0000681C : 'VMCS_GUEST_RSP',
            0x0000681E : 'VMCS_GUEST_RIP',
            0x00006820 : 'VMCS_GUEST_RFLAGS',
            0x00006822 : 'VMCS_GUEST_PENDING_DEBUG_EXCEPTIONS',
            0x00006824 : 'VMCS_GUEST_SYSENTER_ESP',
            0x00006826 : 'VMCS_GUEST_SYSENTER_EIP',
            0x00006C00 : 'VMCS_HOST_CR0',
            0x00006C02 : 'VMCS_HOST_CR3',
            0x00006C04 : 'VMCS_HOST_CR4',
            0x00006C06 : 'VMCS_HOST_FS_BASE',
            0x00006C08 : 'VMCS_HOST_GS_BASE',
            0x00006C0A : 'VMCS_HOST_TR_BASE',
            0x00006C0C : 'VMCS_HOST_GDTR_BASE',
            0x00006C0E : 'VMCS_HOST_IDTR_BASE',
            0x00006C10 : 'VMCS_HOST_SYSENTER_ESP',
            0x00006C12 : 'VMCS_HOST_SYSENTER_EIP',
            0x00006C14 : 'VMCS_HOST_RSP',
            0x00006C16 : 'VMCS_HOST_RIP',

                    # new https://lore.kernel.org/patchwork/patch/1002950/.
            0x00002811 : 'VMCS_GUEST_PDPTR3_HIGH',
            0x00002812 : 'VMCS_GUEST_BNDCFGS',
            0x00002813 : 'VMCS_GUEST_BNDCFGS_HIGH',
            0x00002814 : 'VMCS_GUEST_RTIT_CTL',
            0x00002815 : 'VMCS_GUEST_RTIT_CTL_HIGH'
            }

        self.ia32_msr_db = {

            #
            # Hyper-V MSRs
            #
            0x40000000 : 'HV_X64_MSR_GUEST_OS_ID',
            0x40000001 : 'HV_X64_MSR_HYPERCALL',
            0x40000002 : 'HV_X64_MSR_VP_INDEX',
            0x40000003 : 'HV_X64_MSR_RESET',
            0x40000010 : 'HV_X64_MSR_VP_RUNTIME',
            0x40000020 : 'HV_X64_MSR_TIME_REF_COUNT',
            0x40000021 : 'HV_X64_MSR_REFERENCE_TSC',
            0x40000022 : 'HV_X64_MSR_TSC_FREQUENCY',
            0x40000023 : 'HV_X64_MSR_APIC_FREQUENCY',
            0x40000070 : 'HV_X64_MSR_EOI',
            0x40000071 : 'HV_X64_MSR_ICR',
            0x40000072 : 'HV_X64_MSR_TPR',
            0x40000073 : 'HV_X64_MSR_VP_ASSIST_PAGE',
            0x40000080 : 'HV_X64_MSR_SCONTROL',
            0x40000081 : 'HV_X64_MSR_SVERSION',
            0x40000082 : 'HV_X64_MSR_SIEFP',
            0x40000083 : 'HV_X64_MSR_SIMP',
            0x40000084 : 'HV_X64_MSR_EOM',
            0x40000090 : 'HV_X64_MSR_SINT0',
            0x40000091 : 'HV_X64_MSR_SINT1',
            0x40000092 : 'HV_X64_MSR_SINT2',
            0x40000093 : 'HV_X64_MSR_SINT3',
            0x40000094 : 'HV_X64_MSR_SINT4',
            0x40000095 : 'HV_X64_MSR_SINT5',
            0x40000096 : 'HV_X64_MSR_SINT6',
            0x40000097 : 'HV_X64_MSR_SINT7',
            0x40000098 : 'HV_X64_MSR_SINT8',
            0x40000099 : 'HV_X64_MSR_SINT9',
            0x4000009A : 'HV_X64_MSR_SINT10',
            0x4000009B : 'HV_X64_MSR_SINT11',
            0x4000009C : 'HV_X64_MSR_SINT12',
            0x4000009D : 'HV_X64_MSR_SINT13',
            0x4000009E : 'HV_X64_MSR_SINT14',
            0x4000009F : 'HV_X64_MSR_SINT15',
            0x400000B0 : 'HV_X64_MSR_STIMER0_CONFIG',
            0x400000B1 : 'HV_X64_MSR_STIMER0_COUNT',
            0x400000B2 : 'HV_X64_MSR_STIMER1_CONFIG',
            0x400000B3 : 'HV_X64_MSR_STIMER1_COUNT',
            0x400000B4 : 'HV_X64_MSR_STIMER2_CONFIG',
            0x400000B5 : 'HV_X64_MSR_STIMER2_COUNT',
            0x400000B6 : 'HV_X64_MSR_STIMER3_CONFIG',
            0x400000B7 : 'HV_X64_MSR_STIMER3_COUNT',
            0x400000C1 : 'HV_X64_MSR_POWER_STATE_TRIGGER_C1',
            0x400000C2 : 'HV_X64_MSR_POWER_STATE_TRIGGER_C2',
            0x400000C3 : 'HV_X64_MSR_POWER_STATE_TRIGGER_C3',
            0x400000D1 : 'HV_X64_MSR_POWER_STATE_CONFIG_C1',
            0x400000D2 : 'HV_X64_MSR_POWER_STATE_CONFIG_C2',
            0x400000D3 : 'HV_X64_MSR_POWER_STATE_CONFIG_C3',
            0x400000E0 : 'HV_X64_MSR_STATS_PARTITION_RETAIL_PAGE',
            0x400000E1 : 'HV_X64_MSR_STATS_PARTITION_INTERNAL_PAGE',
            0x400000E2 : 'HV_X64_MSR_STATS_VP_RETAIL_PAGE',
            0x400000E3 : 'HV_X64_MSR_STATS_VP_INTERNAL_PAGE',
            0x400000F0 : 'HV_X64_MSR_GUEST_IDLE',
            0x400000F1 : 'HV_X64_MSR_SYNTH_DEBUG_CONTROL',
            0x400000F2 : 'HV_X64_MSR_SYNTH_DEBUG_STATUS',
            0x400000F3 : 'HV_X64_MSR_SYNTH_DEBUG_SEND_BUFFER',
            0x400000F4 : 'HV_X64_MSR_SYNTH_DEBUG_RECEIVE_BUFFER',
            0x400000F5 : 'HV_X64_MSR_SYNTH_DEBUG_PENDING_BUFFER',
            0x40000100 : 'HV_X64_MSR_CRASH_P0',
            0x40000101 : 'HV_X64_MSR_CRASH_P1',
            0x40000102 : 'HV_X64_MSR_CRASH_P2',
            0x40000103 : 'HV_X64_MSR_CRASH_P3',
            0x40000104 : 'HV_X64_MSR_CRASH_P4',
            0x40000105 : 'HV_X64_MSR_CRASH_CTL',

            0x40000106 : 'HV_X64_MSR_REENLIGHTENMENT_CONTROL',
            0x40000107 : 'HV_X64_MSR_TSC_EMULATION_CONTROL',
            0x40000108 : 'HV_X64_MSR_TSC_EMULATION_STATUS',


            #
            # XEON MSRs
            #
            0x0000004E : 'MSR_XEON_D_PPIN_CTL',
            0x0000004F : 'MSR_XEON_D_PPIN',
            0x000000CE : 'MSR_XEON_D_PLATFORM_INFO',
            0x000000E2 : 'MSR_XEON_D_PKG_CST_CONFIG_CONTROL',
            0x00000179 : 'MSR_XEON_D_IA32_MCG_CAP',
            0x0000017D : 'MSR_XEON_D_SMM_MCA_CAP',
            0x000001A2 : 'MSR_XEON_D_TEMPERATURE_TARGET',
            0x000001AD : 'MSR_XEON_D_TURBO_RATIO_LIMIT',
            0x000001AE : 'MSR_XEON_D_TURBO_RATIO_LIMIT1',
            0x00000606 : 'MSR_XEON_D_RAPL_POWER_UNIT',
            0x00000618 : 'MSR_XEON_D_DRAM_POWER_LIMIT',
            0x00000619 : 'MSR_XEON_D_DRAM_ENERGY_STATUS',
            0x0000061B : 'MSR_XEON_D_DRAM_PERF_STATUS',
            0x0000061C : 'MSR_XEON_D_DRAM_POWER_INFO',
            0x00000620 : 'MSR_XEON_D_MSRUNCORE_RATIO_LIMIT',
            0x00000639 : 'MSR_XEON_D_PP0_ENERGY_STATUS',
            0x00000690 : 'MSR_XEON_D_CORE_PERF_LIMIT_REASONS',
            0x00000C8D : 'MSR_XEON_D_IA32_QM_EVTSEL',
            0x00000C8F : 'MSR_XEON_D_IA32_PQR_ASSOC',
            0x000001AC : 'MSR_XEON_D_TURBO_RATIO_LIMIT3',
            0x00000C81 : 'MSR_XEON_D_IA32_L3_QOS_CFG',
            0x00000C90 : 'MSR_XEON_D_IA32_L3_QOS_MASK_0',
            0x00000C91 : 'MSR_XEON_D_IA32_L3_QOS_MASK_1',
            0x00000C92 : 'MSR_XEON_D_IA32_L3_QOS_MASK_2',
            0x00000C93 : 'MSR_XEON_D_IA32_L3_QOS_MASK_3',
            0x00000C94 : 'MSR_XEON_D_IA32_L3_QOS_MASK_4',
            0x00000C95 : 'MSR_XEON_D_IA32_L3_QOS_MASK_5',
            0x00000C96 : 'MSR_XEON_D_IA32_L3_QOS_MASK_6',
            0x00000C97 : 'MSR_XEON_D_IA32_L3_QOS_MASK_7',
            0x00000C98 : 'MSR_XEON_D_IA32_L3_QOS_MASK_8',
            0x00000C99 : 'MSR_XEON_D_IA32_L3_QOS_MASK_9',
            0x00000C9A : 'MSR_XEON_D_IA32_L3_QOS_MASK_10',
            0x00000C9B : 'MSR_XEON_D_IA32_L3_QOS_MASK_11',
            0x00000C9C : 'MSR_XEON_D_IA32_L3_QOS_MASK_12',
            0x00000C9D : 'MSR_XEON_D_IA32_L3_QOS_MASK_13',
            0x00000C9E : 'MSR_XEON_D_IA32_L3_QOS_MASK_14',
            0x00000C9F : 'MSR_XEON_D_IA32_L3_QOS_MASK_15',



            # 
            # QQQQQQQQQQ
            #

            0x00000048 : 'MSR_SPEC_CTRL',
            0x00000049 : 'MSR_PRED_CMD',

            0x000001C8 : 'MSR_LBR_SELECT',
            0x000001C9 : 'MSR_LBR_TOS',
            0x00000680 : 'MSR_LBR_NHM_FROM',
            0x000006C0 : 'MSR_LBR_NHM_TO',
            0x00000040 : 'MSR_LBR_CORE_FROM',
            0x00000060 : 'MSR_LBR_CORE_TO',


            0x000001DB : 'MSR_IA32_LASTBRANCHFROMIP',
            0x000001DC : 'MSR_IA32_LASTBRANCHTOIP',
            0x000001DD : 'MSR_IA32_LASTINTFROMIP',
            0x000001DE : 'MSR_IA32_LASTINTTOIP',

            # https://github.com/collectd/collectd/blob/master/src/msr-index.h
            #
            # C-state Residency Counters
            #
            0x000003F8 : 'MSR_PKG_C3_RESIDENCY',
            0x000003F9 : 'MSR_PKG_C6_RESIDENCY',
            0x000003FA : 'MSR_ATOM_PKG_C6_RESIDENCY',
            0x000003FA : 'MSR_PKG_C7_RESIDENCY',
            0x000003FC : 'MSR_CORE_C3_RESIDENCY',
            0x000003FD : 'MSR_CORE_C6_RESIDENCY',
            0x000003FE : 'MSR_CORE_C7_RESIDENCY',
            0x000003FF : 'MSR_KNL_CORE_C6_RESIDENCY',
            0x0000060D : 'MSR_PKG_C2_RESIDENCY',
            0x00000630 : 'MSR_PKG_C8_RESIDENCY',
            0x00000631 : 'MSR_PKG_C9_RESIDENCY',
            0x00000632 : 'MSR_PKG_C10_RESIDENCY',


            # https://sites.uclouvain.be/SystInfo/usr/include/asm/msr-index.h.html

            #
            # Centaur-Hauls/IDT defined MSRs.
            #
            0x00000107 : 'MSR_IDT_FCR1',
            0x00000108 : 'MSR_IDT_FCR2',
            0x00000109 : 'MSR_IDT_FCR3',
            0x0000010A : 'MSR_IDT_FCR4',
            0x00000110 : 'MSR_IDT_MCR0',
            0x00000111 : 'MSR_IDT_MCR1',
            0x00000112 : 'MSR_IDT_MCR2',
            0x00000113 : 'MSR_IDT_MCR3',
            0x00000114 : 'MSR_IDT_MCR4',
            0x00000115 : 'MSR_IDT_MCR5',
            0x00000116 : 'MSR_IDT_MCR6',
            0x00000117 : 'MSR_IDT_MCR7',
            0x00000120 : 'MSR_IDT_MCR_CTRL',


            0x000000CD : 'MSR_FSB_FREQ',
            0x00000119 : 'MSR_IA32_BBL_CR_CTL',

            # 
            # QQQQQQQQQQ
            #

            # AMD-V MSRs
            0xC0010114 : 'MSR_VM_CR',
            0xC0010115 : 'MSR_VM_IGNNE',
            0xC0010117 : 'MSR_VM_HSAVE_PA',



            ###########################################################
            # start https://github.com/wbenny/ia32-doc

            # @defgroup intel_manual \
            #           Intel Manual
            # BEGIN
            # @defgroup model_specific_registers \
            #           Model Specific Registers
            # BEGIN
            # @defgroup ia32_p5_mc \
            #           IA32_P5_MC_(x)
            # BEGIN
            0x00000000 : 'IA32_P5_MC_ADDR',
            0x00000001 : 'IA32_P5_MC_TYPE',
            # END

            0x00000006 : 'IA32_MONITOR_FILTER_SIZE',
            0x00000010 : 'IA32_TIME_STAMP_COUNTER',
            0x00000017 : 'IA32_PLATFORM_ID',
            0x0000001B : 'IA32_APIC_BASE',
            0x0000003A : 'IA32_FEATURE_CONTROL',
            0x0000003B : 'IA32_TSC_ADJUST',
            0x00000079 : 'IA32_BIOS_UPDT_TRIG',
            0x0000008B : 'IA32_BIOS_SIGN_ID',
            # @defgroup ia32_sgxlepubkeyhash \
            #           IA32_SGXLEPUBKEYHASH[(64*n+63):(64*n)]
            # BEGIN
            0x0000008C : 'IA32_SGXLEPUBKEYHASH0',
            0x0000008D : 'IA32_SGXLEPUBKEYHASH1',
            0x0000008E : 'IA32_SGXLEPUBKEYHASH2',
            0x0000008F : 'IA32_SGXLEPUBKEYHASH3',
            # END

            0x0000009B : 'IA32_SMM_MONITOR_CTL',
            0x0000009E : 'IA32_SMBASE',
            # @defgroup ia32_pmc \
            #           IA32_PMC(n)
            # BEGIN
            0x000000C1 : 'IA32_PMC0',
            0x000000C2 : 'IA32_PMC1',
            0x000000C3 : 'IA32_PMC2',
            0x000000C4 : 'IA32_PMC3',
            0x000000C5 : 'IA32_PMC4',
            0x000000C6 : 'IA32_PMC5',
            0x000000C7 : 'IA32_PMC6',
            0x000000C8 : 'IA32_PMC7',
            # END

            0x000000E7 : 'IA32_MPERF',
            0x000000E8 : 'IA32_APERF',
            0x000000FE : 'IA32_MTRRCAP',
            0x00000174 : 'IA32_SYSENTER_CS',
            0x00000175 : 'IA32_SYSENTER_ESP',
            0x00000176 : 'IA32_SYSENTER_EIP',
            0x00000179 : 'IA32_MCG_CAP',
            0x0000017A : 'IA32_MCG_STATUS',
            0x0000017B : 'IA32_MCG_CTL',
            # @defgroup ia32_perfevtsel \
            #           IA32_PERFEVTSEL(n)
            # BEGIN
            0x00000186 : 'IA32_PERFEVTSEL0',
            0x00000187 : 'IA32_PERFEVTSEL1',
            0x00000188 : 'IA32_PERFEVTSEL2',
            0x00000189 : 'IA32_PERFEVTSEL3',
            # END

            0x00000198 : 'IA32_PERF_STATUS',
            0x00000199 : 'IA32_PERF_CTL',
            0x0000019A : 'IA32_CLOCK_MODULATION',
            0x0000019B : 'IA32_THERM_INTERRUPT',
            0x0000019C : 'IA32_THERM_STATUS',
            0x000001A0 : 'IA32_MISC_ENABLE',
            0x000001B0 : 'IA32_ENERGY_PERF_BIAS',
            0x000001B1 : 'IA32_PACKAGE_THERM_STATUS',
            0x000001B2 : 'IA32_PACKAGE_THERM_INTERRUPT',
            0x000001D9 : 'IA32_DEBUGCTL',
            0x000001F2 : 'IA32_SMRR_PHYSBASE',
            0x000001F3 : 'IA32_SMRR_PHYSMASK',
            0x000001F8 : 'IA32_PLATFORM_DCA_CAP',
            0x000001F9 : 'IA32_CPU_DCA_CAP',
            0x000001FA : 'IA32_DCA_0_CAP',
            # @defgroup ia32_mtrr_physbase \
            #           IA32_MTRR_PHYSBASE(n)
            # BEGIN
            0x00000200 : 'IA32_MTRR_PHYSBASE0',
            0x00000202 : 'IA32_MTRR_PHYSBASE1',
            0x00000204 : 'IA32_MTRR_PHYSBASE2',
            0x00000206 : 'IA32_MTRR_PHYSBASE3',
            0x00000208 : 'IA32_MTRR_PHYSBASE4',
            0x0000020A : 'IA32_MTRR_PHYSBASE5',
            0x0000020C : 'IA32_MTRR_PHYSBASE6',
            0x0000020E : 'IA32_MTRR_PHYSBASE7',
            0x00000210 : 'IA32_MTRR_PHYSBASE8',
            0x00000212 : 'IA32_MTRR_PHYSBASE9',
            # END

            # @defgroup ia32_mtrr_physmask \
            #           IA32_MTRR_PHYSMASK(n)
            # BEGIN
            0x00000201 : 'IA32_MTRR_PHYSMASK0',
            0x00000203 : 'IA32_MTRR_PHYSMASK1',
            0x00000205 : 'IA32_MTRR_PHYSMASK2',
            0x00000207 : 'IA32_MTRR_PHYSMASK3',
            0x00000209 : 'IA32_MTRR_PHYSMASK4',
            0x0000020B : 'IA32_MTRR_PHYSMASK5',
            0x0000020D : 'IA32_MTRR_PHYSMASK6',
            0x0000020F : 'IA32_MTRR_PHYSMASK7',
            0x00000211 : 'IA32_MTRR_PHYSMASK8',
            0x00000213 : 'IA32_MTRR_PHYSMASK9',
            # END

            # @defgroup ia32_mtrr_fix \
            #           IA32_MTRR_FIX(x)
            # BEGIN
            # @defgroup ia32_mtrr_fix64k \
            #           IA32_MTRR_FIX64K(x)
            # BEGIN
            0x00000000 : 'IA32_MTRR_FIX64K_BASE',
            0x00010000 : 'IA32_MTRR_FIX64K_SIZE',
            0x00000250 : 'IA32_MTRR_FIX64K_00000',
            # END

            # @defgroup ia32_mtrr_fix16k \
            #           IA32_MTRR_FIX16K(x)
            # BEGIN
            0x00080000 : 'IA32_MTRR_FIX16K_BASE',
            0x00004000 : 'IA32_MTRR_FIX16K_SIZE',
            0x00000258 : 'IA32_MTRR_FIX16K_80000',
            0x00000259 : 'IA32_MTRR_FIX16K_A0000',
            # END

            # @defgroup ia32_mtrr_fix4k \
            #           IA32_MTRR_FIX4K(x)
            # BEGIN
            0x000C0000 : 'IA32_MTRR_FIX4K_BASE',
            0x00001000 : 'IA32_MTRR_FIX4K_SIZE',
            0x00000268 : 'IA32_MTRR_FIX4K_C0000',
            0x00000269 : 'IA32_MTRR_FIX4K_C8000',
            0x0000026A : 'IA32_MTRR_FIX4K_D0000',
            0x0000026B : 'IA32_MTRR_FIX4K_D8000',
            0x0000026C : 'IA32_MTRR_FIX4K_E0000',
            0x0000026D : 'IA32_MTRR_FIX4K_E8000',
            0x0000026E : 'IA32_MTRR_FIX4K_F0000',
            0x0000026F : 'IA32_MTRR_FIX4K_F8000',
            # END


            # """
            # >>> hex(((1 + 2 + 8) * 8))
            # '0x58'
            # """
            # '((1 + 2 + 8) * 8) : 'IA32_MTRR_FIX_COUNT',

            0x00000058 : 'IA32_MTRR_FIX_COUNT',
            0x000000FF : 'IA32_MTRR_VARIABLE_COUNT',
            # """>>> hex(0x00000058 + 0x000000FF)
            # '0x157'
            # """
            # '(IA32_MTRR_FIX_COUNT + IA32_MTRR_VARIABLE_COUNT) : 'IA32_MTRR_COUNT',
            0x00000157 : 'IA32_MTRR_COUNT',

            # END

            0x00000277 : 'IA32_PAT',
            # @defgroup ia32_mc_ctl2 \
            #           IA32_MC(i)_CTL2
            # BEGIN
            0x00000280 : 'IA32_MC0_CTL2',
            0x00000281 : 'IA32_MC1_CTL2',
            0x00000282 : 'IA32_MC2_CTL2',
            0x00000283 : 'IA32_MC3_CTL2',
            0x00000284 : 'IA32_MC4_CTL2',
            0x00000285 : 'IA32_MC5_CTL2',
            0x00000286 : 'IA32_MC6_CTL2',
            0x00000287 : 'IA32_MC7_CTL2',
            0x00000288 : 'IA32_MC8_CTL2',
            0x00000289 : 'IA32_MC9_CTL2',
            0x0000028A : 'IA32_MC10_CTL2',
            0x0000028B : 'IA32_MC11_CTL2',
            0x0000028C : 'IA32_MC12_CTL2',
            0x0000028D : 'IA32_MC13_CTL2',
            0x0000028E : 'IA32_MC14_CTL2',
            0x0000028F : 'IA32_MC15_CTL2',
            0x00000290 : 'IA32_MC16_CTL2',
            0x00000291 : 'IA32_MC17_CTL2',
            0x00000292 : 'IA32_MC18_CTL2',
            0x00000293 : 'IA32_MC19_CTL2',
            0x00000294 : 'IA32_MC20_CTL2',
            0x00000295 : 'IA32_MC21_CTL2',
            0x00000296 : 'IA32_MC22_CTL2',
            0x00000297 : 'IA32_MC23_CTL2',
            0x00000298 : 'IA32_MC24_CTL2',
            0x00000299 : 'IA32_MC25_CTL2',
            0x0000029A : 'IA32_MC26_CTL2',
            0x0000029B : 'IA32_MC27_CTL2',
            0x0000029C : 'IA32_MC28_CTL2',
            0x0000029D : 'IA32_MC29_CTL2',
            0x0000029E : 'IA32_MC30_CTL2',
            0x0000029F : 'IA32_MC31_CTL2',
            # END

            0x000002FF : 'IA32_MTRR_DEF_TYPE',
            # @defgroup ia32_fixed_ctr \
            #           IA32_FIXED_CTR(n)
            # BEGIN
            0x00000309 : 'IA32_FIXED_CTR0',
            0x0000030A : 'IA32_FIXED_CTR1',
            0x0000030B : 'IA32_FIXED_CTR2',
            # END

            0x00000345 : 'IA32_PERF_CAPABILITIES',
            0x0000038D : 'IA32_FIXED_CTR_CTRL',
            0x0000038E : 'IA32_PERF_GLOBAL_STATUS',
            0x0000038F : 'IA32_PERF_GLOBAL_CTRL',
            0x00000390 : 'IA32_PERF_GLOBAL_STATUS_RESET',
            0x00000391 : 'IA32_PERF_GLOBAL_STATUS_SET',
            0x00000392 : 'IA32_PERF_GLOBAL_INUSE',
            0x000003F1 : 'IA32_PEBS_ENABLE',
            # @defgroup ia32_mc_ctl \
            #           IA32_MC(i)_CTL
            # BEGIN
            0x00000400 : 'IA32_MC0_CTL',
            0x00000404 : 'IA32_MC1_CTL',
            0x00000408 : 'IA32_MC2_CTL',
            0x0000040C : 'IA32_MC3_CTL',
            0x00000410 : 'IA32_MC4_CTL',
            0x00000414 : 'IA32_MC5_CTL',
            0x00000418 : 'IA32_MC6_CTL',
            0x0000041C : 'IA32_MC7_CTL',
            0x00000420 : 'IA32_MC8_CTL',
            0x00000424 : 'IA32_MC9_CTL',
            0x00000428 : 'IA32_MC10_CTL',
            0x0000042C : 'IA32_MC11_CTL',
            0x00000430 : 'IA32_MC12_CTL',
            0x00000434 : 'IA32_MC13_CTL',
            0x00000438 : 'IA32_MC14_CTL',
            0x0000043C : 'IA32_MC15_CTL',
            0x00000440 : 'IA32_MC16_CTL',
            0x00000444 : 'IA32_MC17_CTL',
            0x00000448 : 'IA32_MC18_CTL',
            0x0000044C : 'IA32_MC19_CTL',
            0x00000450 : 'IA32_MC20_CTL',
            0x00000454 : 'IA32_MC21_CTL',
            0x00000458 : 'IA32_MC22_CTL',
            0x0000045C : 'IA32_MC23_CTL',
            0x00000460 : 'IA32_MC24_CTL',
            0x00000464 : 'IA32_MC25_CTL',
            0x00000468 : 'IA32_MC26_CTL',
            0x0000046C : 'IA32_MC27_CTL',
            0x00000470 : 'IA32_MC28_CTL',
            # END

            # @defgroup ia32_mc_status \
            #           IA32_MC(i)_STATUS
            # BEGIN
            0x00000401 : 'IA32_MC0_STATUS',
            0x00000405 : 'IA32_MC1_STATUS',
            0x00000409 : 'IA32_MC2_STATUS',
            0x0000040D : 'IA32_MC3_STATUS',
            0x00000411 : 'IA32_MC4_STATUS',
            0x00000415 : 'IA32_MC5_STATUS',
            0x00000419 : 'IA32_MC6_STATUS',
            0x0000041D : 'IA32_MC7_STATUS',
            0x00000421 : 'IA32_MC8_STATUS',
            0x00000425 : 'IA32_MC9_STATUS',
            0x00000429 : 'IA32_MC10_STATUS',
            0x0000042D : 'IA32_MC11_STATUS',
            0x00000431 : 'IA32_MC12_STATUS',
            0x00000435 : 'IA32_MC13_STATUS',
            0x00000439 : 'IA32_MC14_STATUS',
            0x0000043D : 'IA32_MC15_STATUS',
            0x00000441 : 'IA32_MC16_STATUS',
            0x00000445 : 'IA32_MC17_STATUS',
            0x00000449 : 'IA32_MC18_STATUS',
            0x0000044D : 'IA32_MC19_STATUS',
            0x00000451 : 'IA32_MC20_STATUS',
            0x00000455 : 'IA32_MC21_STATUS',
            0x00000459 : 'IA32_MC22_STATUS',
            0x0000045D : 'IA32_MC23_STATUS',
            0x00000461 : 'IA32_MC24_STATUS',
            0x00000465 : 'IA32_MC25_STATUS',
            0x00000469 : 'IA32_MC26_STATUS',
            0x0000046D : 'IA32_MC27_STATUS',
            0x00000471 : 'IA32_MC28_STATUS',
            # END

            # @defgroup ia32_mc_addr \
            #           IA32_MC(i)_ADDR
            # BEGIN
            0x00000402 : 'IA32_MC0_ADDR',
            0x00000406 : 'IA32_MC1_ADDR',
            0x0000040A : 'IA32_MC2_ADDR',
            0x0000040E : 'IA32_MC3_ADDR',
            0x00000412 : 'IA32_MC4_ADDR',
            0x00000416 : 'IA32_MC5_ADDR',
            0x0000041A : 'IA32_MC6_ADDR',
            0x0000041E : 'IA32_MC7_ADDR',
            0x00000422 : 'IA32_MC8_ADDR',
            0x00000426 : 'IA32_MC9_ADDR',
            0x0000042A : 'IA32_MC10_ADDR',
            0x0000042E : 'IA32_MC11_ADDR',
            0x00000432 : 'IA32_MC12_ADDR',
            0x00000436 : 'IA32_MC13_ADDR',
            0x0000043A : 'IA32_MC14_ADDR',
            0x0000043E : 'IA32_MC15_ADDR',
            0x00000442 : 'IA32_MC16_ADDR',
            0x00000446 : 'IA32_MC17_ADDR',
            0x0000044A : 'IA32_MC18_ADDR',
            0x0000044E : 'IA32_MC19_ADDR',
            0x00000452 : 'IA32_MC20_ADDR',
            0x00000456 : 'IA32_MC21_ADDR',
            0x0000045A : 'IA32_MC22_ADDR',
            0x0000045E : 'IA32_MC23_ADDR',
            0x00000462 : 'IA32_MC24_ADDR',
            0x00000466 : 'IA32_MC25_ADDR',
            0x0000046A : 'IA32_MC26_ADDR',
            0x0000046E : 'IA32_MC27_ADDR',
            0x00000472 : 'IA32_MC28_ADDR',
            # END

            # @defgroup ia32_mc_misc \
            #           IA32_MC(i)_MISC
            # BEGIN
            0x00000403 : 'IA32_MC0_MISC',
            0x00000407 : 'IA32_MC1_MISC',
            0x0000040B : 'IA32_MC2_MISC',
            0x0000040F : 'IA32_MC3_MISC',
            0x00000413 : 'IA32_MC4_MISC',
            0x00000417 : 'IA32_MC5_MISC',
            0x0000041B : 'IA32_MC6_MISC',
            0x0000041F : 'IA32_MC7_MISC',
            0x00000423 : 'IA32_MC8_MISC',
            0x00000427 : 'IA32_MC9_MISC',
            0x0000042B : 'IA32_MC10_MISC',
            0x0000042F : 'IA32_MC11_MISC',
            0x00000433 : 'IA32_MC12_MISC',
            0x00000437 : 'IA32_MC13_MISC',
            0x0000043B : 'IA32_MC14_MISC',
            0x0000043F : 'IA32_MC15_MISC',
            0x00000443 : 'IA32_MC16_MISC',
            0x00000447 : 'IA32_MC17_MISC',
            0x0000044B : 'IA32_MC18_MISC',
            0x0000044F : 'IA32_MC19_MISC',
            0x00000453 : 'IA32_MC20_MISC',
            0x00000457 : 'IA32_MC21_MISC',
            0x0000045B : 'IA32_MC22_MISC',
            0x0000045F : 'IA32_MC23_MISC',
            0x00000463 : 'IA32_MC24_MISC',
            0x00000467 : 'IA32_MC25_MISC',
            0x0000046B : 'IA32_MC26_MISC',
            0x0000046F : 'IA32_MC27_MISC',
            0x00000473 : 'IA32_MC28_MISC',
            # END

            0x00000480 : 'IA32_VMX_BASIC',
            0x00000481 : 'IA32_VMX_PINBASED_CTLS',
            0x00000482 : 'IA32_VMX_PROCBASED_CTLS',
            0x00000483 : 'IA32_VMX_EXIT_CTLS',
            0x00000484 : 'IA32_VMX_ENTRY_CTLS',
            0x00000485 : 'IA32_VMX_MISC',
            0x00000486 : 'IA32_VMX_CR0_FIXED0',
            0x00000487 : 'IA32_VMX_CR0_FIXED1',
            0x00000488 : 'IA32_VMX_CR4_FIXED0',
            0x00000489 : 'IA32_VMX_CR4_FIXED1',
            0x0000048A : 'IA32_VMX_VMCS_ENUM',
            0x0000048B : 'IA32_VMX_PROCBASED_CTLS2',
            0x0000048C : 'IA32_VMX_EPT_VPID_CAP',
            # @defgroup ia32_vmx_true_ctls \
            #           IA32_VMX_TRUE_(x)_CTLS
            # BEGIN
            0x0000048D : 'IA32_VMX_TRUE_PINBASED_CTLS',
            0x0000048E : 'IA32_VMX_TRUE_PROCBASED_CTLS',
            0x0000048F : 'IA32_VMX_TRUE_EXIT_CTLS',
            0x00000490 : 'IA32_VMX_TRUE_ENTRY_CTLS',
            # END

            0x00000491 : 'IA32_VMX_VMFUNC',
            # @defgroup ia32_a_pmc \
            #           IA32_A_PMC(n)
            # BEGIN
            0x000004C1 : 'IA32_A_PMC0',
            0x000004C2 : 'IA32_A_PMC1',
            0x000004C3 : 'IA32_A_PMC2',
            0x000004C4 : 'IA32_A_PMC3',
            0x000004C5 : 'IA32_A_PMC4',
            0x000004C6 : 'IA32_A_PMC5',
            0x000004C7 : 'IA32_A_PMC6',
            0x000004C8 : 'IA32_A_PMC7',
            # END

            0x000004D0 : 'IA32_MCG_EXT_CTL',
            0x00000500 : 'IA32_SGX_SVN_STATUS',
            0x00000560 : 'IA32_RTIT_OUTPUT_BASE',
            0x00000561 : 'IA32_RTIT_OUTPUT_MASK_PTRS',
            0x00000570 : 'IA32_RTIT_CTL',
            0x00000571 : 'IA32_RTIT_STATUS',
            0x00000572 : 'IA32_RTIT_CR3_MATCH',
            # @defgroup ia32_rtit_addr \
            #           IA32_RTIT_ADDR(x)
            # BEGIN
            # @defgroup ia32_rtit_addr_a \
            #           IA32_RTIT_ADDR(n)_A
            # BEGIN
            0x00000580 : 'IA32_RTIT_ADDR0_A',
            0x00000582 : 'IA32_RTIT_ADDR1_A',
            0x00000584 : 'IA32_RTIT_ADDR2_A',
            0x00000586 : 'IA32_RTIT_ADDR3_A',
            # END

            # @defgroup ia32_rtit_addr_b \
            #           IA32_RTIT_ADDR(n)_B
            # BEGIN
            0x00000581 : 'IA32_RTIT_ADDR0_B',
            0x00000583 : 'IA32_RTIT_ADDR1_B',
            0x00000585 : 'IA32_RTIT_ADDR2_B',
            0x00000587 : 'IA32_RTIT_ADDR3_B',
            # END

            # END

            0x00000600 : 'IA32_DS_AREA',
            0x000006E0 : 'IA32_TSC_DEADLINE',
            0x00000770 : 'IA32_PM_ENABLE',
            0x00000771 : 'IA32_HWP_CAPABILITIES',
            0x00000772 : 'IA32_HWP_REQUEST_PKG',
            0x00000773 : 'IA32_HWP_INTERRUPT',
            0x00000774 : 'IA32_HWP_REQUEST',
            0x00000777 : 'IA32_HWP_STATUS',
            0x00000802 : 'IA32_X2APIC_APICID',
            0x00000803 : 'IA32_X2APIC_VERSION',
            0x00000808 : 'IA32_X2APIC_TPR',
            0x0000080A : 'IA32_X2APIC_PPR',
            0x0000080B : 'IA32_X2APIC_EOI',
            0x0000080D : 'IA32_X2APIC_LDR',
            0x0000080F : 'IA32_X2APIC_SIVR',
            # @defgroup ia32_x2apic_isr \
            #           IA32_X2APIC_ISR(n)
            # BEGIN
            0x00000810 : 'IA32_X2APIC_ISR0',
            0x00000811 : 'IA32_X2APIC_ISR1',
            0x00000812 : 'IA32_X2APIC_ISR2',
            0x00000813 : 'IA32_X2APIC_ISR3',
            0x00000814 : 'IA32_X2APIC_ISR4',
            0x00000815 : 'IA32_X2APIC_ISR5',
            0x00000816 : 'IA32_X2APIC_ISR6',
            0x00000817 : 'IA32_X2APIC_ISR7',
            # END

            # @defgroup ia32_x2apic_tmr \
            #           IA32_X2APIC_TMR(n)
            # BEGIN
            0x00000818 : 'IA32_X2APIC_TMR0',
            0x00000819 : 'IA32_X2APIC_TMR1',
            0x0000081A : 'IA32_X2APIC_TMR2',
            0x0000081B : 'IA32_X2APIC_TMR3',
            0x0000081C : 'IA32_X2APIC_TMR4',
            0x0000081D : 'IA32_X2APIC_TMR5',
            0x0000081E : 'IA32_X2APIC_TMR6',
            0x0000081F : 'IA32_X2APIC_TMR7',
            # END

            # @defgroup ia32_x2apic_irr \
            #           IA32_X2APIC_IRR(n)
            # BEGIN
            0x00000820 : 'IA32_X2APIC_IRR0',
            0x00000821 : 'IA32_X2APIC_IRR1',
            0x00000822 : 'IA32_X2APIC_IRR2',
            0x00000823 : 'IA32_X2APIC_IRR3',
            0x00000824 : 'IA32_X2APIC_IRR4',
            0x00000825 : 'IA32_X2APIC_IRR5',
            0x00000826 : 'IA32_X2APIC_IRR6',
            0x00000827 : 'IA32_X2APIC_IRR7',
            # END

            0x00000828 : 'IA32_X2APIC_ESR',
            0x0000082F : 'IA32_X2APIC_LVT_CMCI',
            0x00000830 : 'IA32_X2APIC_ICR',
            0x00000832 : 'IA32_X2APIC_LVT_TIMER',
            0x00000833 : 'IA32_X2APIC_LVT_THERMAL',
            0x00000834 : 'IA32_X2APIC_LVT_PMI',
            0x00000835 : 'IA32_X2APIC_LVT_LINT0',
            0x00000836 : 'IA32_X2APIC_LVT_LINT1',
            0x00000837 : 'IA32_X2APIC_LVT_ERROR',
            0x00000838 : 'IA32_X2APIC_INIT_COUNT',
            0x00000839 : 'IA32_X2APIC_CUR_COUNT',
            0x0000083E : 'IA32_X2APIC_DIV_CONF',
            0x0000083F : 'IA32_X2APIC_SELF_IPI',
            0x00000C80 : 'IA32_DEBUG_INTERFACE',
            0x00000C81 : 'IA32_L3_QOS_CFG',
            0x00000C82 : 'IA32_L2_QOS_CFG',
            0x00000C8D : 'IA32_QM_EVTSEL',
            0x00000C8E : 'IA32_QM_CTR',
            0x00000C8F : 'IA32_PQR_ASSOC',
            0x00000D90 : 'IA32_BNDCFGS',
            0x00000DA0 : 'IA32_XSS',
            0x00000DB0 : 'IA32_PKG_HDC_CTL',
            0x00000DB1 : 'IA32_PM_CTL1',
            0x00000DB2 : 'IA32_THREAD_STALL',
            0xC0000080 : 'IA32_EFER',
            0xC0000081 : 'IA32_STAR',
            0xC0000082 : 'IA32_LSTAR',
            0xC0000083 : 'IA32_CSTAR',
            0xC0000084 : 'IA32_FMASK',
            0xC0000100 : 'IA32_FS_BASE',
            0xC0000101 : 'IA32_GS_BASE',
            0xC0000102 : 'IA32_KERNEL_GS_BASE',
            0xC0000103 : 'IA32_TSC_AUX',
            # END

            # end https://github.com/wbenny/ia32-doc
            #######################################################
        }

    def GetIa32VmcsDb(self):
        return self.ia32_vmcs_db

    def GetIa32MsrDb(self):
        return self.ia32_msr_db

# ------------------------------------------------------------------------------------- #
def PrettyHex(v):
    return '0x{0:08X}'.format(v)

def GetJumpAddr(call_addr, func_ea):
    func_addr = idc.LocByName(idc.GetFunctionName(func_ea))
    ret = int(call_addr - func_addr)
    return hex(ret).replace("0x", "")

def GetFunctionNamePlusRva(inst_ea):
        jump_addr = GetJumpAddr(inst_ea, inst_ea)
        return idc.GetFunctionName(inst_ea)+ '+' + jump_addr


def AddressToHex(ea):
    return hex(ea).rstrip('L')

def GetDelimeter(input_str,max_len = 30, delim_char = " "):
    if input_str == None:
        return delim_char * (max_len - len('NoneNone'))
    else:
        return delim_char * (max_len - len(input_str))

def GetValueWithDelimeter(input_str,max_len = 30, delim_char = " "):
    if input_str == None:
        return GetDelimeter(input_str,max_len,delim_char)
    return input_str+GetDelimeter(input_str,max_len,delim_char)

# ------------------------------------------------------------------------------------- #
class Config (object):
    PLUGIN_TITLTE = "IA32 VMX Helper"
    COLUMN_NAMES  = ["Address", "Mnemonic",  "Value_Hex", "Value_Name", "Function_Name"]
    COLUMN_SIZE   = [20,     10,         18, 40 , 30]
    PLUGIN_COLUMNS = [list(c) for c in
                      zip(COLUMN_NAMES, COLUMN_SIZE)]

    ROW_ITEM = collections.namedtuple("RowItem",  COLUMN_NAMES)

class PluginPage(idaapi.Choose2):
    
    def __init__(self, title, columns, items, icon, embedded=False):

        idaapi.Choose2.__init__(self, title, columns, embedded=embedded)
        self.items = items
        self.icon = icon

    def GetItems(self):
        return self.items

    def SetItems(self, items):
        self.items = [] if items is None else items
        self.Refresh()

    def OnClose(self):
        pass

    def OnGetLine(self, index):
        return self.items[index]

    def OnGetSize(self):
        return len(self.items)

    def OnSelectLine(self, index):
        try:
            row = self.items[index][0]
            addr = int(row,16)
            idc.Jump(addr)
        except Exception, e:
            idc.Warning('Exception : Selected item {}  = {}'.format(index,str(e))) 
            pass





class ReportEntry(object):
    
    def __init__(self, addr,mnemonic,value_hex,value_name,func_name):
        self.addr = addr
        self.mnemonic = mnemonic
        self.value_hex = value_hex
        self.value_name = value_name
        self.func_name = func_name

    def get_row(self):

        addr = "0x%x" % (self.addr)
        return list(Config.ROW_ITEM(addr, self.mnemonic, self.value_hex,self.value_name,self.func_name))

# ------------------------------------------------------------------------------------- #

def GetEnumItemByConst(const_value,constants):
    if constants == None:
        # idc.Warning('constants not loaded')
        return None
    for item_name,item_constant, enum_id in constants:
        if item_constant == const_value:
            return [item_name,enum_id]
    return None

class SymbolicConstant(object):
    def __init__(self,enum_name):
        self.enum_name = enum_name
        self.result = []
        self.count = 0

    def LoadEnums(self):
        enum_id = idc.GetEnum(self.enum_name)
        enum_count = idc.GetEnumSize(enum_id)
        
        if enum_id == idaapi.BADADDR:
            return None

        item_constant = idc.GetFirstConst(enum_id, -1)
        item_name     = idc.GetConstName(idc.GetConstEx(enum_id, item_constant, 0, -1))

        self.result.append([item_name,item_constant, enum_id])

        for i in range(enum_count):
            item_constant = idc.GetNextConst(enum_id, item_constant, -1)
            item_name = idc.GetConstName(idc.GetConstEx(enum_id, item_constant, 0, -1))

            self.result.append([item_name,item_constant, enum_id])

        return self.result
# ------------------------------------------------------------------------------------- #
OPERAND_INDEX_0 = 0
OPERAND_INDEX_1 = 1

class Instruction_t(object):
    def __init__(self,inst_ea):
        self.inst_ea = inst_ea
        self.mnemonic = idc.GetMnem(self.inst_ea)
        self.operand_0 = idc.GetOpnd(self.inst_ea, 0)
        self.operand_1 = idc.GetOpnd(self.inst_ea, 1)

        self.operand_value_0 = idc.GetOperandValue(self.inst_ea,0)
        self.operand_value_1 = idc.GetOperandValue(self.inst_ea,1)

    def GetOperandValue_32bit(self,operand_index):
        return idc.GetOperandValue(self.inst_ea,operand_index) & 0xffffffff

    def GetOperandType(self,operand_index):
        return idc.GetOpType(self.inst_ea,operand_index)
    
    @property
    def NextInstruction(self):
        return Instruction_t(idc.NextHead(self.inst_ea))

    @property
    def PrevInstruction(self):
        return Instruction_t(idc.PrevHead(self.inst_ea))
    
    @property
    def FunctionName(self):
        return idc.GetFunctionName(self.inst_ea)

    def IsCode(self):
        return idaapi.isCode(idaapi.getFlags(self.inst_ea))

    def IsCallInstruction(self):
        return self.mnemonic == 'call'

    def GetRvaFromBaseOfFunction(self):
        func_ea = idc.LocByName(self.FunctionName)
        return int(self.inst_ea - func_ea)

    def IsXoredThisReg(self,reg):
        return self.mnemonic == 'xor' and self.operand_0 == self.operand_1 == reg;

    def IsXoredWithItself(self):
        return self.mnemonic == 'xor' and self.operand_0 == self.operand_1;

# ------------------------------------------------------------------------------------- #
# @ IA32 MSR ANALYZER BEGIN ==>
# ------------------------------------------------------------------------------------- #
class Ia32MsrAnalyzePass(object):
    def __init__(self,func_ea,symbolic_constants,enable_symbolic_constants = False,enable_comment = False, \
        enable_colorize = False , color = 0x00441E):

        self.func_ea = func_ea
        self.inst_ea = None

        self.symbolic_constants = symbolic_constants

        self.ia32_msr_db = g_knowledge_db.GetIa32MsrDb()

        self.enable_symbolic_constants = enable_symbolic_constants
        self.enable_comment = enable_comment
        self.enable_colorize = enable_colorize
        self.color = color

        self.msr_code_imm_ea = None

        self.gui_report_rows = []

    def IsVmxInstruction(self,inst_ea):
        mnemonic = idc.GetMnem(inst_ea)
        return mnemonic =='rdmsr' or mnemonic=='wrmsr'

    def SetSymbolcsConstantOfMsrCode(self,inst_ea,msr_code):
        ret = GetEnumItemByConst(msr_code,self.symbolic_constants)
        if ret == None: 
            return
        item_name, enum_id = ret
        if enum_id  and self.ia32_msr_db.get(msr_code)== item_name:
            idc.OpEnumEx(inst_ea, 1,enum_id,0 )

    def GetMsrCodeFromOperand(self,inst_ea):
        curr_inst = Instruction_t(inst_ea)

        while True:
            curr_inst = curr_inst.PrevInstruction
            if(curr_inst.mnemonic == 'mov' and curr_inst.operand_0[-2:] == 'cx'):
                if (curr_inst.GetOperandType(OPERAND_INDEX_1) == idc.o_reg and cfg_enable_smart_mode == True):
                    reg_name = curr_inst.operand_1
                    rev_inst = Instruction_t(curr_inst.inst_ea)

                    if(reg_name[-2:] == 'ax' and rev_inst.PrevInstruction.IsCallInstruction()):
                        """
                        rcx value came from a function call

                        mov     r10d, 0A0013h
                        mov     ecx, r10d
                        call    sub_FFFFF800002E7C78
                        mov     ecx, eax
                        rdmsr                   ; rdmsr(Not imm value) 
                        """
                        return None

                    """
                    mov rax, 0xC0000082 ; IA32_LSTAR
                    mov rcx,rax
                    """
                    for i in range(cfg_smart_mode_max_back_step):
                        rev_inst = rev_inst.PrevInstruction 
                        if rev_inst.mnemonic == 'mov' and rev_inst.operand_0 == reg_name and \
                            rev_inst.GetOperandType(OPERAND_INDEX_1) == idc.o_imm:

                            self.msr_code_imm_ea = rev_inst.inst_ea
                            return rev_inst.GetOperandValue_32bit(OPERAND_INDEX_1)

                # mov rcx, 0xC0000082 ; IA32_LSTAR
                elif(curr_inst.GetOperandType(OPERAND_INDEX_1) == idc.o_imm):
                    self.msr_code_imm_ea = curr_inst.inst_ea
                    return curr_inst.GetOperandValue_32bit(OPERAND_INDEX_1)
                    
            # Not imm value :-(
            elif (curr_inst.mnemonic == 'lea' and  curr_inst.operand_0 in ['rcx','ecx']):
                return None
            # xor rcx,rcx 
            elif (curr_inst.operand_0 in ['rcx','ecx'] and curr_inst.IsXoredThisReg(curr_inst.operand_0)):
                self.msr_code_imm_ea = curr_inst.inst_ea
                return 0
            else:
                continue

    def GetMsrName(self,msr_code):
        name = self.ia32_msr_db.get(msr_code)
        if (name == None):
            name = PrettyHex(msr_code)
        return name

    def GetGuiReportRows(self):
        return self.gui_report_rows

    def RunPass(self):
        for self.inst_ea in idautils.FuncItems(self.func_ea):
            mnemonic = idc.GetMnem(self.inst_ea)
            if (self.IsVmxInstruction(self.inst_ea) == False):
                continue

            msr_name = None
            msr_hex  = None
            msr_code = self.GetMsrCodeFromOperand(self.inst_ea)

            if(msr_code == None):
                msr_name = 'Not imm value'
                msr_hex  = 'Not imm value'
            else:
                msr_name = self.GetMsrName(msr_code)
                msr_hex  = PrettyHex(msr_code)

            if(self.enable_comment):
                comment_message = '{}({})'.format(mnemonic,msr_name)
                idc.MakeComm(self.inst_ea, comment_message)
            
            if(self.enable_colorize):
                idc.SetColor(self.inst_ea,idc.CIC_ITEM, self.color)

            if(self.enable_symbolic_constants and msr_code != None):
                self.SetSymbolcsConstantOfMsrCode(self.msr_code_imm_ea,msr_code)

            self.gui_report_rows.append(ReportEntry(
                                        self.inst_ea,
                                        mnemonic,
                                        msr_hex,
                                        msr_name,
                                        GetFunctionNamePlusRva(self.inst_ea))
                                        )

# ------------------------------------------------------------------------------------- #
# @ IA32 MSR ANALYZER END <==
# ------------------------------------------------------------------------------------- #

# ------------------------------------------------------------------------------------- #
# @IA32 VMCS ANALYZER BEGIN ==>
# ------------------------------------------------------------------------------------- #

class Ia32VmcsAnalyzePass(object):
    def __init__(self,func_ea,symbolic_constants,enable_symbolic_constants = False, enable_comment = False, \
        enable_colorize = False , color = 0x00441E):

        self.func_ea = func_ea
        self.inst_ea = None

        self.symbolic_constants = symbolic_constants

        self.ia32_vmcs_db = g_knowledge_db.GetIa32VmcsDb()

        self.enable_symbolic_constants = enable_symbolic_constants
        self.enable_comment = enable_comment
        self.enable_colorize = enable_colorize
        self.color = color

        self.vmcs_code_imm_ea = None

        self.gui_report_rows = []

    def IsVmxInstruction(self,inst_ea):
        mnemonic = idc.GetMnem(inst_ea)
        return mnemonic =='vmread' or mnemonic=='vmwrite'

    def SetSymbolcsConstantOfVmcsCode(self,inst_ea,vmcs_code):
        ret = GetEnumItemByConst(vmcs_code,self.symbolic_constants)
        if ret == None: 
            return
        item_name, enum_id = ret
        if enum_id  and self.ia32_vmcs_db.get(vmcs_code)== item_name:
            idc.OpEnumEx(inst_ea, 1,enum_id,0 )

    def GetValueRegister(self,inst_ea):
        """
        vmread  buffer, vmcs_code
        vmwrite vmcs_code, buffer
        """
        inst_t = Instruction_t(inst_ea)
        if( inst_t.mnemonic == 'vmread' ):#and inst_t.GetOperandType(0) == idc.o_reg ):
            return inst_t.operand_0
        elif( inst_t.mnemonic == 'vmwrite'):# and inst_t.GetOperandType(1) == idc.o_reg ):
            return inst_t.operand_1
        else:
            return None

    def GetCodeRegister(self,inst_ea):
        """
        vmread  buffer, vmcs_code
        vmwrite vmcs_code, buffer
        """
        inst_t = Instruction_t(inst_ea)
        if( inst_t.mnemonic == 'vmread' and inst_t.GetOperandType(1) == idc.o_reg ):
            return inst_t.operand_1
        elif( inst_t.mnemonic == 'vmwrite' and inst_t.GetOperandType(0) == idc.o_reg ):
            return inst_t.operand_0
        else:
            return None

    def GetVmcsCodeFromOperand(self,inst_ea):
        curr_inst = Instruction_t(inst_ea)

        code_reg = self.GetCodeRegister(inst_ea)
        if( code_reg == None ):
            return None

        while True:
            curr_inst = curr_inst.PrevInstruction

            # mov  ['cx', 'ecx', 'rcx'] [-2:] ==> 'cx'  , ###
            if( curr_inst.mnemonic == 'mov' and curr_inst.operand_0[-2:] == code_reg[-2:]):
                if(curr_inst.GetOperandType(OPERAND_INDEX_1) == idc.o_imm):
                    self.vmcs_code_imm_ea = curr_inst.inst_ea
                    return curr_inst.GetOperandValue_32bit(OPERAND_INDEX_1)

                # if mov ecx, rcx keep looking for mov rax, imm
                elif(curr_inst.GetOperandType(OPERAND_INDEX_1) == idc.o_reg and cfg_enable_smart_mode == True):
                    reg_name = curr_inst.operand_1
                    rev_inst = Instruction_t(curr_inst.inst_ea)

                    if(reg_name[-2:] == 'ax' and rev_inst.PrevInstruction.IsCallInstruction()):
                        """
                        register value came from a function call
                        """
                        return None
                    
                    # while True:
                    for i in range(cfg_smart_mode_max_back_step):
                        rev_inst = rev_inst.PrevInstruction
                        if rev_inst.mnemonic == 'mov' and rev_inst.operand_0 == reg_name and \
                            rev_inst.GetOperandType(OPERAND_INDEX_1) == idc.o_imm:

                            self.vmcs_code_imm_ea = rev_inst.inst_ea
                            return rev_inst.GetOperandValue_32bit(OPERAND_INDEX_1)

            # Not imm value :-(
            elif (curr_inst.mnemonic == 'lea' and curr_inst.operand_0[-2:] == code_reg[-2:]):
                return None
            # xor ecx,ecx means vmcs code is 0x0 VMCS_CTRL_VPID
            elif(curr_inst.operand_0[-2:] == code_reg[-2:] and curr_inst.IsXoredThisReg(code_reg)):
                self.vmcs_code_imm_ea = curr_inst.inst_ea
                return 0

    def GetVmcsName(self,vmcs_code):
        name = self.ia32_vmcs_db.get(vmcs_code)
        if (name == None):
            name = PrettyHex(vmcs_code)
        return name

    def GetGuiReportRows(self):
        return self.gui_report_rows

    def RunPass(self):
        for self.inst_ea in idautils.FuncItems(self.func_ea):
            mnemonic = idc.GetMnem(self.inst_ea)
            if (self.IsVmxInstruction(self.inst_ea) == False):
                continue

            vmcs_name = None
            vmcs_hex  = None
            vmcs_code = self.GetVmcsCodeFromOperand(self.inst_ea)

            if(vmcs_code == None):
                vmcs_name = 'Not imm value'
                vmcs_hex  = 'Not imm value'
            else:
                vmcs_name = self.GetVmcsName(vmcs_code)
                vmcs_hex  = PrettyHex(vmcs_code)

            if(self.enable_comment):
                comment_message = '{}({}, {})'.format(mnemonic,vmcs_name,self.GetValueRegister(self.inst_ea))
                idc.MakeComm(self.inst_ea, comment_message)
            
            if(self.enable_colorize):
                idc.SetColor(self.inst_ea,idc.CIC_ITEM, self.color)

            if(self.enable_symbolic_constants and vmcs_code != None):
                self.SetSymbolcsConstantOfVmcsCode(self.vmcs_code_imm_ea,vmcs_code)

            self.gui_report_rows.append(ReportEntry(
                                        self.inst_ea,
                                        mnemonic,
                                        vmcs_hex,
                                        vmcs_name,
                                        GetFunctionNamePlusRva(self.inst_ea))
                                        )

# ------------------------------------------------------------------------------------- #
# @IA32 VMCS ANALYZER END <==
# ------------------------------------------------------------------------------------- #

# ------------------------------------------------------------------------------------- #


class Ia32VmxHelper(idaapi.plugin_t):
    def __init__(self):
        self.enable_comment  = 0
        self.enable_colorize = 0
        self.enable_symbolic_constants = 0
        self.gui_report_rows = []

        self.ia32_msr_symbolic_constants = SymbolicConstant('IA32_MSR_LIST_ENUM').LoadEnums()
        self.ia32_vmcs_symbolic_constants = SymbolicConstant('IA32_VMCS_LIST_ENUM').LoadEnums()

        global g_knowledge_db
        g_knowledge_db = KnowledgeDb()

        flags = idaapi.PLUGIN_UNL
        comment = ''
        help = ''
        wanted_name = ''
        # wanted_hotkey = 'Ctrl-Alt-Q'

    def AddRowsToGuiTable(self,rows):
        if self._plugin_page is None:
            self._plugin_page = PluginPage(
                    Config.PLUGIN_TITLTE, Config.PLUGIN_COLUMNS,rows, self.icon_id)
        else:
            self._plugin_page.SetItems(rows)

        self._plugin_page.Show()

    def init(self):
        self._plugin_page = None
        self.icon_id = None
        return idaapi.PLUGIN_OK

    def term(self):
        pass

    def run(self,_=0):
        only_show = idc.AskYN(1,'Only show the result')
        if(only_show == 0):
            add_enums = idc.AskYN(0,'Do you want to add symbolic constants enums?, **You Only need to do this for once not more!**')
            if(add_enums == 1):
                AddSymbolicConstantsEnumsToIda()
                if(self.ia32_msr_symbolic_constants == None):
                    self.ia32_msr_symbolic_constants = SymbolicConstant('IA32_MSR_LIST_ENUM').LoadEnums()
                if(self.ia32_vmcs_symbolic_constants == None):
                    self.ia32_vmcs_symbolic_constants = SymbolicConstant('IA32_VMCS_LIST_ENUM').LoadEnums()

            self.enable_comment  = idc.AskYN(0,'Do you want to add comment on all decoded values?')
            self.enable_colorize = idc.AskYN(0,'Do you want to colorize all decoded values?')
            self.enable_symbolic_constants = idc.AskYN(0,'Do you want to apply symbolic constants on all decoded values?')

        for func_ea in idautils.Functions():
            isa32_msr_pass = Ia32MsrAnalyzePass(func_ea,
                                                self.ia32_msr_symbolic_constants,
                                                self.enable_symbolic_constants,
                                                self.enable_comment,
                                                self.enable_colorize,
                                                0x00441E)

            isa32_vmcs_pass = Ia32VmcsAnalyzePass(func_ea,
                                                self.ia32_vmcs_symbolic_constants,
                                                self.enable_symbolic_constants,
                                                self.enable_comment,
                                                self.enable_colorize,
                                                0x00441E)
            
            isa32_msr_pass.RunPass()
            isa32_vmcs_pass.RunPass()
            

            for i in isa32_msr_pass.GetGuiReportRows():
                 self.gui_report_rows.append(i.get_row())

            for i in isa32_vmcs_pass.GetGuiReportRows():
                 self.gui_report_rows.append(i.get_row())

        self.AddRowsToGuiTable(self.gui_report_rows)
# ------------------------------------------------------------------------------------- #
def PLUGIN_ENTRY():
    return Ia32VmxHelper()

def main():
    f = PLUGIN_ENTRY()
    f.init()
    f.run()
    f.term()

if __name__ == '__main__':
    main()


