Age | Commit message (Collapse) | Author | Files | Lines |
|
Define human-readable string formatting for objects in repr, rather than
str, as this will enable an interactive interpreter to more conveniently
show this data to the user. I believe this especially makes sense in
cases where __str__ doesn't perform a semantic type conversion for its
class (currently, all affected cases).
Scripts can still easily yield this information by using
`print(object)`, as print will fallback to repr(object) when there is
not an explicitly defined __str__.
Furthermore, this patch still maintains backwards compatability (for the
time being) of using str(object) to retrieve the information. This is
because the default __str__ implementation will defer to __repr__ if
provided. This made the Symtbl case of providing both of them
especially redundant.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
Reviewed-by: Malfurious <m@lfurio.us>
|
|
The built in int's to_bytes and from_bytes functions have some weird
behavior with the signed parameter. Rather than expecting the user to
properly give btoi/itob the right signed value to pass through to
to_bytes/from_btyes, it makes more sense to just always convert an
unsigned number. Using the new int conversions, this can always be
unambiguous with respect to the width of the int.
There may also be situations where a user would like to truncate/sign
extend an int to a certain length other than the configured architecture
wordsize or convert to a different endianness. These are now
parameterized. There is no need to parameterize the width for btoi
because you will now always get an unsigned int back (and because of
python, the width is ambiguous). The user can convert it to whatever
width/sign they want after the fact with the new int conversion methods.
This also means that payload's int() does not need to take a signed
argument either. Whatever sign of int you give it, when it calls itob,
it will get the correct bytearray at the width of the configured
architecture's wordsize.
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
Reviewed-by: Malfurious <m@lfurio.us>
|
|
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
Reviewed-by: Malfurious <m@lfurio.us>
|
|
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
Reviewed-by: Malfurious <m@lfurio.us>
|
|
Python's dataclass annotation gives us a nice way to cleanly and
concisely define our list of supported architectures similar to namedtuple.
Unlike namedtuple, though, dataclass gives us an actual class that is
significantly more feature rich and even allows us to add functionality.
In general, these are meant to be like const records of info about an
architecture, so we use frozen=True to enforce some const correctness.
There were some issues when involving other classes for the ActiveArch
feature (subclassing and composition both had their respective issues),
so I'm removing __ActiveArch__ and putting a set() method directly on
Arch. This method will copy a given Arch into the self object. This
technically breaks const correctness as this does modify the object, but
it is intended to only be used on a single sentinel Arch that represents
the active arch. This arch is initialized with x86_64 by default.
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
Reviewed-by: Malfurious <m@lfurio.us>
|
|
A read of 0 isn't particularly useful, but it is weird that it will
cause a BrokenPipeError. Instead, it makes more sense to just return an
empty string.
A read of <0 would normally read until EOF, but we already have that
feature in readall() and it wouldn't be particularly useful here. A
similar functionality of reading the entire current contents of the
buffer is useful, though. This is already implemented in
readall_nonblock() and this would be a nice user-facing way of calling
that.
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
This effort was triggered by three immediate wants of the module:
An improved data container interface to support things like key
iteration and better key management. This is primarily wanted by
the ROP module (which is still in development).
The introduction of package documentation across the project. This
module is now fully documented.
To fix a bug in the Symtbl constructor, which would not allow a
caller to supply "self" as an initial symbol name, even though it is
legal in every other context. This problem was caused by the
constructor's bound instance parameter sharing this name.
This patch addresses all of these concerns, and also introduces some
fringe / QoL improvements that were discovered during the API refactor.
Element access may now be done via subscripting, as well as the previous
(and still generally perferred) .attribute notation. The syntax for
storing subtables within a parent Symtbl is now greatly streamlined due
to some implementation-level changes to the class. You may now directly
assign just a Symtbl object or a normal int, and you don't have to fuss
with tuples anymore. The subtable's base is taken as its offset in the
parent, and the new operator replacement for the .map() method may be
used to define a desired value for the parent.
This detail is actually a breaking change compared to the previous
version. While not technically a bug, it is unintuitive that the
previous version would not remove subtables when their offset was
changed by a simple assignment - the table would just move. This patch
make it such that any symbol assignment to a regular int will replace an
old mounted subtable if one exists.
There are now no normal instance methods on the Symtbl type (only dunder
method overrides). This is to free up the available symbol namespace as
much as possible. The previous methods map(), adjust(), and rebase()
are now implemented as operators which, in every case, yield a new
derivative object, rather than mutating the original. All operators are
listed here:
@ remap to absolute address
+ remap to relative address
- remap to negated relative address
>> adjust all symbol offsets upward
<< adjust all symbol offsets downward
% rebase all symbol offsets around an absolute zero point
Additionally, Symtbl objects will convert to an integer via int(),
hex(), oct(), or bin(), yielding the base value.
The addition of these operators presents another breaking change to the
previous version. Previously, symbol adjustments or rebases affected
the tracked offsets and caused symbols to shift around in linked tables
as well. Since these operators now preserve the state of their source
object, this is no longer the case. The amount of shift due to
adjustment or rebasing is localized in a specific Symtbl instance (and
is affected the the use of the related operators), however this value is
inherited by derivatives of that object.
There is a third breaking change caused by the use of operators as well.
Previously, the map() function allowed the caller to specify that the
given absolute address is not that of the table base, but of some offset
in the table, from which the new base is calculated. However, the
remapping operators take only a single numeric value as their right hand
side operand, which is the absolute or relative address. The new
intended way of accomplishing this (which is _nearly_ equivalent) is
through the combined use of the rebase and remap operations:
# The address of the puts() function in a libc tbl is leaked
sym = sym % sym.puts @ leak
aka: adjust offsets such that the known point is at the base, then move
that base to the known location. The way in which this is different to
what you would end up with before is that previously, following a
map(abs, off) the base of the table would be accurately valued
according to the known information. Now, the 'base' is considered to be
the leaked value, but internal offsets are shifted such that they still
resolve correctly.
Finally, a few new pieces of functionality are added to build out the
container API:
- symbol key deletion
- iteration over symbol:offset pairs
- can now check for symbol existence with the "in" keyword
- len(symtbl) returns the number of symbols defined
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
I assume that the preferred style is to leave one major class each to a
file. In this case, synchronize the names of the Symtbl class and its
containing module. Per PEP8, the module is lowercase, and the class
remains Pascal case.
If other memory-oriented utilities are introduced in the future, we may
wish to move them, as well as Symtbl, back into a subpackage named
'mem'.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Print the current version (sourced from git describe) when sploit
starts up.
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
Reviewed-by: Malfurious <m@lfurio.us>
|
|
Instead of hard-coding the version into the pyproject.toml, we can
dynamically source it at build time. Ideally, we want to use git
describe as a single authority source on the version. The version is
stored in sploit.__version__ and can be consumed during sploit runtime
or during a build/package to populate the project's core metadata
version in the toml file.
hatchling provides a tool.hatch.version plugin that can read out the
variable during a build/package. Because this variable is populated
from a git command, if the source tree isn't in a git repo, it will
fail. In this case, sploit will report a PEP 440 compliant fake version
"0+unknown.version" to let the user know.
Because a packaged distribution doesn't exist in a git repo, we want to
bake in the version at build time into the package. hatchling provides
a plugin to help with this, but it had some technical limitations that
didn't quite work for our use case. Instead, I added a custom build
hook which will take the version sourced from the package (and by proxy
the git command), and overwrite the __init__.py with a hard-coded
version in the __version__ variable. This means that built/packaged
distributions of this project will have a fixed version hard-coded in
rather than dynamically sourcing from git.
The build hook operates just before the build executes. It seems that
most build/packager front-ends (e.g. build, pip) will just run it in the
current source tree rather than making a temp copy. This means that
when we modify the __init__.py, it is modifying our git tree. Ideally,
we want this to be restored at the end of the build. The build hook
interface allows us to write a hook that happens after the build, but it
won't run in the case of a crash or failed build. Instead, I added a
custom solution to this using a member variable deconstructor. If the
build ends in any way, the original contents of __init__.py are written
back out.
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
Reviewed-by: Malfurious <m@lfurio.us>
|
|
Currently, the standard way to build and package a Python project is
through a pyproject.toml file rather than the old setup.py. This is
also build back-end agnostic and we can choose to use something other
than setuptools. After looking through a few options, I've decided to
use hatchling.
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
Reviewed-by: Malfurious <m@lfurio.us>
|
|
In interact(), we set stdin to be nonblocking for the duration of the
function. As an unexpected side-effect, this was setting stdout to be
nonblocking as well. This has caused at least one crash in the past.
Localizing the nonblock to just when we're reading from stdin should
solve this.
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
We had originally decided to use the os.read() function instead of the
actual buffered file object's read function. This was due to the
blocking behavior or os.read() being closer to POSIX read than the other
function.
As it turns out, os.read() is an unbuffered read. Every other read call
in this interface is buffered. This causes some undefined behavior in
certain cases and leads to some really confusing bugs.
After some discussion, we've decided that, in this application's domain,
the blocking behavior of the buffered file object's read is actually
often more useful anyways. Changing this call will deal with both
issues.
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
This behavior was accidentally removed in dcba5f2
interact mode works by polling for IO events, but it will miss any
unread data already in the buffer when it is first entered. We can
ensure this gets caught by just doing a read once at the beginning.
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Line-oriented reads now strip the newline from the end of their returned
string. Additionally, readall() strips the newline, but only from the
string that gets logged to the user's terminal (goodbye to all the "\n"
printed at the end of each line).
Of course, these functions are called by other parts of the read API and
have downstream effects. Consideration was given to the entire API with
these rules in mind:
- Raw reads (or non-line-oriented reads) will not filter ANY of
their read content. They are logged to the screen as one "line"
of log text with \n characters shown in-place (not actually
resetting the terminal cursor). If reading binary, these bytes
dont actually mean line termination anyway.
functions: read, readall(_nonblock) *, readuntil
- Line-oriented reads will strip the terminating \n, log the single
line to the screen, and return it.
functions: readline, readlineuntil **
* readall(_nonblock) functions turn out to be a special case. They will
operate as raw reads, returning a blob of content. However, we
generally want to run them on line-oriented input, so they log according
to the line-oriented rules.
** Although content returned from readlineuntil will have \n's stripped,
the lines are returned in an array, so we can still distinguish them.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
The writeline function will now default to send an empty line when
called without an argument. I don't believe any such default makes
sense for the plain write function, as writing nothing should have no
effect.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
This is normally not an issue, since logonread defaults to True.
However, if the user disables this setting, interact() becomes a lot
less useful. logonread is now forced on during io.interact(), but
respected through the rest of the API.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Failure to close target stdout is not interesting. Furthermore, if
sploit ever gets into this situation, the user script has likely already
raised a more useful error/backtrace. Handling this exception typically
results in a duplicate error.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Moving this io cleanup code to the finally block allows it to also run
when recovering from an exception. This prevents cases where the target
may hang if the user sploit script crashes, and avoids requiring the
user to press an additonal CTRL-C to move on.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
This branch brings some conveniences to the semantics behind Symtbl base
values.
* sploit/symtbl-base:
sploit: rev: Properly base Symtbls for non-PIC binaries
sploit: Fix bugs involving Symtbl base value
sploit: mem: Allow Symtbl base to be modified
|
|
The baddr property identified by r2 is now used as the base address for
ELF symbol tables. This should not change the addresses retrieved via
the table normally, however should fix the internal offsets of the table
so that rebasing makes sense.
Note that for PIC/PIE binaries we would already get a Symtbl with
'correct' offsets, as r2 is unable to absolutely resolve them for us.
In these cases, the Symtbl base value remains at zero.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Some code previously assumed a Symtbl's base value to always be zero.
This was often the case, however the assumption would break (for example)
when attempting to rebase() a mapped Symtbl.
As of the previous patch enabling freer modification of base, the
potentiality of these bugs will be higher.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Allow a Symtbl's base to be modified in-place, without mapping into a
new object. This is useful when working with the Symtbl aspect of a
Payload.
This includes setting a non-zero base on construction. As usual, when
defining base on construction, any additional kwargs symbols are
interpreted relative to the given base. The order of arguments does not
matter.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Lift restriction (name mangling) to Payload helper functions, as their
use will be useful in Payload subclasses.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
This is just a slight code reduction, but will make any future code
simpler as well. Explicit comparision to None is more correct as well;
centralizing this for reuse better justifies the wordier if statement.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Given the current design of Symtbl, creating subclasses of it gets more
tedious the further one goes down a potential class hierarchy. As I am
planning to introduce new features in the future that explicitly extend
Payload, make this change now to minimize the impact.
Additionally, switching Payload's relationship with Symtbl from "is-a"
to "has-a" makes it more consistent with rev.ELF, the other major user
of Symtbl. (And in both cases, the member is named 'sym')
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Often times, users of the Payload module wish to push a list of integers
to a payload buffer. Currently, the best (and intended) way to do this
is to make several calls to .int(). However, as part of the ROP effort,
I am planning to add function 'gadget(addr, *params)' to the Payload
class. Per the design of this function, calling it with an expanded
list of values would be equivalent to passing each to .int()
individually. In order to discourage the use of .gadget(), as a
shortcut to a series of .int()s, .int(), and most other insertion
functions, now accept arbitrarily many value arguments.
Functions that support additional options (such as .int()'s 'signed'
parameter) will apply such options to all values. If a symbol name is
defined, it will reference the beginning of the block of values.
Keep in mind, this will also allow inserting zero values. For example,
obj.bin(sym='end') will tag the end of the payload without extending its
content. This use-case is not intended to be particularly useful, but
exists as a consequence of the change.
Payload.rep() and the pad functions are not affected by this commit, as
I don't think changing their semantics in this way makes sense.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Due to line buffering, we may often trigger a burst of data to be sent
by the target, but resolve the non-blocking read only after the first
line is received. We would like to wait just a little longer to receive
the entire burst instead.
readall_nonblock() will now reset its timeout period whenever any data
becomes readable and will not return until we go an entire period of
silence. Under normal conditions, the full duration of readall_nonblock
should barely be any longer than the defined period itself.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
select's poll.poll() function expects its timeout argument to be in
milliseconds. This is an artifact from earlier developent where we were
using the higher-level 'selectors' API, which never got merged.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Add a layer of indirection to access the active arch config.
Currently when importing sploit.arch.arch, the name will be bound to
whatever the current reference is and won't follow if another module
(user script) updates the reference in sploit.arch. A layer of
indirection seemlessly solves that issue and also provides a cleaner
interface for setting the active arch from the user script.
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
The design/implementation of class Payload is updated for better
compatibility with Symtbl, and to address some usability issues that
have come up so far:
No more automatically fixed-up stack alignment by default: In fact,
alignment as a concept is fully removed from the tool, in preparation
for another upcoming ROP-centric addon to Payload. Therefore, insertion
of return addresses (via .ret()) are now equivalent to any other integer
value.
No instance size value: Each call to .pad() uses an independent size
passed as a parameter, but functions in the same manor as before.
Padding can also now be inserted at the beginning of the payload:
.pad_front() prepends the necessary amount of data, and updates the
tracked offsets of values that were already inserted to the payload.
Payload now directly extends Symtbl: Instead of possessing a Symtbl
member, payload objects can directly be treated as symbol tables for
things like mounting them as subtables, or mapping them to access
absolute addresses.
New call syntax to access binary data: As a shorthand, users may now
use the call syntax to get the bytes string built by the tool. If an
argument is passed, it is another byte string containing illegal bytes
that we check the built payload for. Unfortunately, the __str__ magic
func doesn't like returning bytes string; plus, that overload is already
in use for formatting the symbol table content (worth not hiding).
New semantic insertion functions: .bin(), .str() [C-style strings],
.int(), .ret(), and more. Some of these functions are direct synonyms,
however their use can provide more clarity in Sploit scripts.
Smarter default element symbol names: Instead of just using '_' as a
placeholder if no symbol name is ever given, we now uniquely name each
inserted element according to the API function that was used, then slap
on an incrementing number. An explicit name still bypasses this scheme.
Insertion functions can now be chained together: Functions previously
returned the offset/address of the inserted value. However, this
feature was seldom used, and there is now the possibility of
.pad_front() invalidating previously-returned offsets. Instead,
functional-style chaining is enabled to reduce boilerplate, and help
with quick oneliners.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
__getattribute__ is the low-level magic func and will intercept every
attribute lookup, whereas __getattr__ is high-level, and is only invoked
in specific conditions (such as __getattribute__'s failure).
As such, any overload of __getattribute__ which preferentially falls
back to object.__getattribute__() before serving a request, can more
simply be replaced by a __getattr__ overload without the fallback.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
The recent implementation of the new design for Symtbl contained a few
bugs:
- Attempting to access .base on a Symtbl or intermediate
__InnerTable__ caused an exception.
- Symtbl objects all used the same static collection of nested
subtables, rather than an instanced one. If two table objects
contained the same named key, they would refer to the same
nested table from both locations.
- Printing the contents of a table accessed via an absolute
nesting (aka: via an __InnerTable__ object) would not show the
offsets adjusted for the curent context.
In addition to these fixes, the class implementation is largely
simplified as well. This is in part due to the removal of unnecessary
logic, such as the way our __getattribute__ overloads were implemented.
Mainly, this came down to merging the redundant abstractions in our
original design.
Over time, the differences between these interfaces became blurred to
the point where simply reusing one is not at all problematic. It is
very much the intent of this patch to preserve the semantics of the
tool's design (that being: flexable, nestable tables, to which a
separate, but linked, mapped view may be obtained), but to state it as
cleanly as possible.
Note that all of the working state of a Symtbl is kept in its new
_namesp member. This is primarily done to enable subclassing the Symtbl
class. Ordinarily, setattr() on self would force the incoming value
into the actual symbol table, making it impossible for subclasses to
store separate instance data. Furthermore, the consolidation of
properties into this object creates fewer potential collisions with
user-defined symbols.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Add rev for basic reverse engineering
* tag 'pull-sploit-rev' of https://github.com/Dusoleil/lib-des-gnux:
sploit: Move __attr_filter__ to a general place in util
sploit: Filter all magic python members by default in mem module
sploit: add stack base pointer to locals symtbl
sploit: print hex of addresses in rev logs
sploit: add status logging to rev module
sploit: lazy load libs for ELF
sploit: cache results of external commands
sploit: add the rest of r2 functions through elf
sploit: typo fix in rev.r2
sploit: cache ELF loads
sploit: add ELF helper class to rev
sploit: consolidate r2 symbol search calls
sploit: fix r2 module syntax error
sploit: reverse direction of r2 get_locals offsets
sploit: add r2 funcionality to rev module
sploit: add ldd ability to rev module
sploit: add rev module to sploit
|
|
The previous patches in this series have needed to utilize similar logic
as Comm.interact() throughout other parts of the Comm class. This patch
just revisits .interact() to clean up redundant code.
Co-authored-by: dusoleil <howcansocksbereal@gmail.com>
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
This is a free-function in the comm module, intended to help setup
Sploit plumbing when working in the Python interactive interpreter.
At the moment, the intended user experience in the interpreter is to err
on the side of being interactive/responsive. As such, the Comm object
returned from popen() is initialized with overridden IO settings to
prefer 'readonwrite' by default. Addtionally, any early output from the
target is also read, so that it may be immediately visible.
A consequence of this configuration is that, until readonwrite is set
False, most target output will be consumed before any .read* function
has a chance to return it. While that would be a hard showstopper for
any Sploit script, an interactive user can simply copy/paste any
important data that is produced. Given that the interpreter workflow is
likely going to be most useful for quick prototyping and recon with the
proposed rev module, I consider this tradeoff appropriate at the moment,
but will consider revisiting this if its usage is problematic.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
If readonwrite is set to True (default False), Sploit will catch up and
read all available stdin data from the target in a non-blocking fashion.
If logonread is also set to True, this data will immediately be
presented to the user whenever data is sent, but is otherwise lost (not
returned).
This mode is primarily intended for use in the interactive Python
interpreter, where it can be cumbersome to keep alternating read and
write calls when one does not care to actually record the read values.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Function should consume all available incoming data from target and
return it, however will return 'immediately' (according to a
configurable timeout) if the pipe is empty.
Signed-off-by: Malfurious <m@lfurio.us>
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Found a spot to use __attr_filter__ in the rev module, so moving it out
of mem and into a shared place (util).
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
In the various __getattribute__() overloads in the mem module, we should
filter all of the built-in magic members to do the default
object.__getattribute__() behavior. This is opposed to the earlier
stance of just caring about the ones that I saw as realistically being
called.
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|
|
rather than cacheing ELF instantiations, just cache the results of
external commands
Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
|