Skip to content

Commit

Permalink
Merge pull request #8 from VirusTotal/v0.9
Browse files Browse the repository at this point in the history
V0.9
  • Loading branch information
gerardofn authored Mar 12, 2020
2 parents 3476b74 + bee1f1c commit b2e6530
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 169 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# VT-IDA Plugin
This is the official VirusTotal plugin for Hex-Rays IDA Pro. This plugin integrates functionality from VirusTotal web services into the IDA Pro's user interface.

The current version is v0.8beta, This plugin is not production-ready yet, and unexpected behavior can still occur. This release integrates VTGrep into IDA Pro, facilitating the searching for similar code, strings, or sequences of bytes.
The current version is v0.9, This plugin is not production-ready yet, and unexpected behavior can still occur. This release integrates VTGrep into IDA Pro, facilitating the searching for similar code, strings, or sequences of bytes.

## Requirements
This plugin has been developed for **IDA Pro 7.0** and beyond and supports both Python 2.7 and 3.x.
Expand Down Expand Up @@ -36,4 +36,4 @@ These actions will launch a new instance of your default web browser, showing al

Check IDA Pro's output window for any message that may need your attention.

**Note**: This version only supports **Intel 32/64 bits** when searching for similar code. Support for more processors will be added in the next version.
**Note**: This version supports **Intel 32/64 bits** and **ARM** processor architectures when searching for similar code. Probably more architectures are supported but it hasn't been tested yet.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.8
0.9
File renamed without changes.
2 changes: 1 addition & 1 deletion plugin/vt_ida/config.py → plugin/virustotal/config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

# VT-IDA plugin global configuration file

API_KEY = ''
API_KEY = '' # Optional
DEBUG = False

Empty file.
147 changes: 147 additions & 0 deletions plugin/virustotal/vt_ida/disassembler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Copyright 2020 Google Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

__author__ = 'gerardofn@virustotal.com'

import binascii
import ida_idp
import idaapi
import idautils
import idc
import logging


class Disassembler(object):

@staticmethod
def bad_address():
return idaapi.BADADDR

@staticmethod
def next_address(addr):
return idc.next_head(addr)

@staticmethod
def get_bytes(start_addr, end_addr):
return idc.get_bytes(
start_addr,
end_addr - start_addr
)

@staticmethod
def valid_address_range(start_addr, end_addr):
if (start_addr == Disassembler.bad_address() or
end_addr == Disassembler.bad_address()):
return False
else:
return True

@staticmethod
def valid_range_size(start_addr, end_addr, max_size):
if (end_addr - start_addr) > max_size:
return False
else:
return True

@staticmethod
def wildcard_instruction(addr):
"""Replaces bytes related to memory addresses with wildcards.
Args:
addr: the address of the current instruction to be wildcarded
Returns:
String: hex-encoded representation of the bytes obtained at addr where
all the operands that refers to memmory addresses are wildcarded.
"""

pattern = ''
mask = ida_idp.ph_calcrel(addr)
mask_str = binascii.hexlify(mask).decode('utf-8')

logging.debug(
'[VTGREP] Wildcarding: %s',
idc.generate_disasm_line(addr, 0)
)

current_byte = 0
index_instr = 0
pattern = ' '

while current_byte < len(mask_str):
if mask_str[current_byte] != '0' or mask_str[current_byte+1] != '0':
pattern += '?? '
else:
instr_bytes = idc.get_bytes(addr+index_instr, 1)
pattern += binascii.hexlify(instr_bytes).decode('utf-8') + ' '
current_byte += 2
index_instr += 1

logging.debug('[VTGREP] Wildcarded: %s', pattern)

return pattern

@staticmethod
def get_opcodes(addr, strict):
"""Get current bytes of the instruction pointed at addr.
Args:
addr: address of the current instruction
strict: be more restrictive when applying wildcards (True) or not (False)
Returns:
String: hex-encoded representation of the bytes obtained at addr
"""

if strict:
offsets_types = {idaapi.o_far, idaapi.o_mem, idaapi.o_imm}
else:
offsets_types = {idaapi.o_far, idaapi.o_mem}

pattern = ''
mnem = idautils.DecodeInstruction(addr)

if mnem is not None:
op1_type = mnem.Op1.type
op2_type = mnem.Op2.type

logging.debug(
'[VTGREP] Instruction: %s [%d, %d, %d]',
idc.generate_disasm_line(addr, 0),
mnem.itype,
op1_type,
op2_type
)

inst_len = idc.get_item_size(addr)
drefs = [x for x in idautils.DataRefsFrom(addr)]

# Checks if any operand constains a memory address
if (drefs and
((op1_type == idaapi.o_imm) or (op2_type == idaapi.o_imm)) or
op1_type in offsets_types or op2_type in offsets_types):
pattern = Disassembler.wildcard_instruction(addr)
# Checks if the instruction is a CALL (near or far) or
# if it's a JMP (excluding near jumps)
else:
if ((mnem.itype == idaapi.NN_call) or
(mnem.itype == idaapi.NN_jmp and op1_type != idaapi.o_near)):
pattern = Disassembler.wildcard_instruction(addr)
# In any other case, concatenate the raw bytes to the current string
else:
pattern = binascii.hexlify(idc.get_bytes(addr, inst_len))
pattern = pattern.decode('utf-8')
return pattern
else: return 0


Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2019 Google Inc. All Rights Reserved.
# Copyright 2020 Google Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
Expand All @@ -22,14 +22,14 @@
import os
import requests
import threading
from vt_ida import config
from vt_ida import vtgrep
from virustotal import config
from virustotal import vtgrep
try:
import ConfigParser as configparser
except ImportError:
import configparser

VT_IDA_PLUGIN_VERSION = '0.8'
VT_IDA_PLUGIN_VERSION = '0.9'


def PLUGIN_ENTRY():
Expand Down Expand Up @@ -222,11 +222,12 @@ class WarningForm(ida_kernwin.Form):
def __init__(self):
self.invert = False
ida_kernwin.Form.__init__(self, r"""STARTITEM 0
BUTTON YES* Ok
BUTTON NO No
BUTTON YES Ok
BUTTON NO* No
BUTTON Cancel Cancel
VirusTotal Plugin for IDA Pro 7
Welcome to the Beta Version of VirusTotal IDA Pro Plugin !
Welcome to the Beta Version of the VirusTotal IDA Pro Plugin !
Auto uploads of samples is enabled by default. By submitting
your file to VirusTotal you are asking VirusTotal to share
Expand Down Expand Up @@ -307,23 +308,30 @@ def upload_file_to_VT(self):
"""Upload input file to VirusTotal."""

user_agent = 'IDA Pro VT Plugin upload - v' + VT_IDA_PLUGIN_VERSION
if config.API_KEY == '':
headers = {
'User-Agent': user_agent,
}
else:
headers = {
'User-Agent': user_agent,
'x-apikey': config.API_KEY
}

headers = {
'User-Agent': user_agent,
'Accept': 'application/json'
}
norm_path = os.path.normpath(self.input_file)
file_path, file_name = os.path.split(norm_path)

if os.path.isfile(self.input_file):
logging.info('[VT Plugin] Uploading input file to VirusTotal.')
url = 'https://www.virustotal.com/ui/files'
files = {'file': (self.input_file, open(self.input_file, 'rb'))}
files = {'file': (file_name, open(self.input_file, 'rb'))}

try:
response = requests.post(url, files=files, headers=headers)
except:
logging.error('[VT Plugin] Unable to connect to VirusTotal.com')

if response.status_code == 200:
if response.ok:
logging.debug('[VT Plugin] Uploaded successfully.')
else:
logging.error('[VT Plugin] Upload failed.')
Expand Down Expand Up @@ -455,7 +463,7 @@ def __init__(self, cfgfile):
)

logging.info(
'\n** VT Plugin for IDA Pro v%s (c) Google, 2019',
'\n** VT Plugin for IDA Pro v%s (c) Google, 2020',
VT_IDA_PLUGIN_VERSION
)
logging.info('** VirusTotal integration plugin for Hex-Ray\'s IDA Pro 7')
Expand All @@ -468,9 +476,13 @@ def __init__(self, cfgfile):
class VTplugin(idaapi.plugin_t):
"""VirusTotal plugin interface for IDA Pro."""

SUPPORTED_PROCESSORS = ['80286r', '80286p', '80386r', '80386p', '80486r',
'80486p', '80586r', '80586p', '80686p', 'k62', 'p2',
'p3', 'athlon', 'p4', 'metapc']
SEARCH_CODE_SUPPORTED = ['80286r', '80286p', '80386r', '80386p', '80486r',
'80486p', '80586r', '80586p', '80686p', 'k62', 'p2',
'p3', 'athlon', 'p4', 'metapc', 'ARM']
SEARCH_STRICT_SUPPORTED = ['80286r', '80286p', '80386r', '80386p', '80486r',
'80486p', '80586r', '80586p', '80686p', 'k62',
'p2', 'p3', 'athlon', 'p4', 'metapc']

flags = idaapi.PLUGIN_UNL
comment = 'VirusTotal Plugin for IDA Pro'
help = 'VirusTotal integration plugin for Hex-Ray\'s IDA Pro 7'
Expand Down Expand Up @@ -513,13 +525,16 @@ def init(self):
arch_info = idaapi.get_inf_structure()

try:
if arch_info.procName in self.SUPPORTED_PROCESSORS:
if arch_info.procName in self.SEARCH_STRICT_SUPPORTED:
VTGrepWildcards.register(self, 'Search for similar code')
VTGrepWildCardsStrict.register(
self,
'Search for similar code (strict)'
)
VTGrepWildCardsFunction.register(self, 'Search for similar functions')
elif arch_info.procName in self.SEARCH_CODE_SUPPORTED:
VTGrepWildcards.register(self, 'Search for similar code')
VTGrepWildCardsFunction.register(self, 'Search for similar functions')
else:
logging.info('\n - Processor detected: %s', arch_info.procName)
logging.info(' - Searching for similar code is not available.')
Expand Down
28 changes: 28 additions & 0 deletions plugin/virustotal/vt_ida/widgets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright 2020 Google Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

__author__ = 'gerardofn@virustotal.com'

import ida_kernwin


class Widgets(object):

@staticmethod
def show_info(msg):
ida_kernwin.info(msg)

@staticmethod
def show_warning(msg):
ida_kernwin.warning(msg)

Loading

0 comments on commit b2e6530

Please sign in to comment.