Files
2021-05-01 00:05:11 -04:00

208 lines
7.1 KiB
Python

#!/usr/bin/env python
from operator import add, sub, mul, mod, itruediv, pow, and_, or_, lshift, rshift
from operator import inv
global vars
vars = {}
def assign(a, b):
global vars
if not isinstance(a, str) or not a.startswith("$"):
raise ValueError("Cannot assign to a constant")
vars[a] = b
binary_ops = {
"+": add,
"-": sub,
"/": itruediv,
"*": mul,
"%": mod,
"**": pow,
"<<": lshift,
">>": rshift,
"&": and_,
"|": or_,
}
# Note:
# ^ means "dereference". In the airbag unit tests, this is accompanied by
# a memory region that can deal with dereferencing addresses (presumably).
# Their test uses FakeMemoryRegion, which appears to just return x+1 for
# any pointer x that is dereferenced. So we use lambda x: x+1 as a
# placeholder.
unary_ops = {
"~": inv,
"^": lambda x: x + 1,
}
def evaluate(pfstr):
global vars
stack = []
for tok in pfstr.split():
if tok == "=":
try:
b = stack.pop()
a = stack.pop()
except IndexError:
raise ValueError("Not enough values on the stack for requested operation")
if isinstance(b, str) and (b.startswith("$") or b.startswith(".")):
try:
b = vars[b]
except KeyError:
raise ValueError("Name %s referenced before assignment" % b)
assign(a, b)
elif tok in binary_ops:
try:
b = stack.pop()
a = stack.pop()
except IndexError:
raise ValueError("Not enough values on the stack for requested operation")
if isinstance(a, str) and (a.startswith("$") or a.startswith(".")):
try:
a = vars[a]
except KeyError:
raise ValueError("Name %s referenced before assignment" % a)
if isinstance(b, str) and (b.startswith("$") or b.startswith(".")):
try:
b = vars[b]
except KeyError:
raise ValueError("Name %s referenced before assignment" % b)
stack.append(binary_ops[tok](a, b))
elif tok in unary_ops:
try:
a = stack.pop()
except IndexError:
raise ValueError("Not enough values on the stack for requested operation")
if isinstance(a, str) and (a.startswith("$") or a.startswith(".")):
try:
a = vars[a]
except KeyError:
raise ValueError("Name %s referenced before assignment" % a)
stack.append(unary_ops[tok](a))
elif tok.startswith("$") or tok.startswith("."):
stack.append(tok)
else:
stack.append(int(tok, 0))
if stack:
raise ValueError("Values remain on the stack after processing")
if __name__ == "__main__":
pfstrs = [
("$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =",
True),
("$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + = $ebx $T0 28 - ^ =",
True),
("$T0 $ebp = $T2 $esp = $T1 .raSearchStart = $eip $T1 ^ = $ebp $T0 = $esp $T1 4 + = $L $T0 .cbSavedRegs - = $P $T1 4 + .cbParams + = $ebx $T0 28 - ^ =",
True),
]
vars["$ebp"] = 0xbfff0010
vars["$eip"] = 0x10000000
vars["$esp"] = 0xbfff0000
vars[".cbSavedRegs"] = 4
vars[".cbParams"] = 4
vars[".raSearchStart"] = 0xbfff0020
for (test, should_succeed) in pfstrs:
try:
evaluate(test)
if len(repr(test)) > 50:
test = test[:50] + "[...]"
if not should_succeed:
print('Test %-60s FAILED.' % repr(test))
else:
print('Test %-60s PASSED.' % repr(test))
except ValueError:
if should_succeed:
print('Test %-60s FAILED.' % repr(test))
else:
print('Test %-60s PASSED.' % repr(test))
validate_data_1 = {}
validate_data_1["$T0"] = 0xbfff0012
validate_data_1["$T1"] = 0xbfff0020
validate_data_1["$T2"] = 0xbfff0019
validate_data_1["$eip"] = 0xbfff0021
validate_data_1["$ebp"] = 0xbfff0012
validate_data_1["$esp"] = 0xbfff0024
validate_data_1["$L"] = 0xbfff000e
validate_data_1["$P"] = 0xbfff0028
validate_data_1["$ebx"] = 0xbffefff7
validate_data_1[".cbSavedRegs"] = 4
validate_data_1[".cbParams"] = 4
for k in validate_data_1:
assert vars[k] == validate_data_1[k]
vars = {}
validate_data_0 = {}
validate_data_0["$rAdd"] = 8
validate_data_0["$rAdd2"] = 4
validate_data_0["$rSub"] = 3
validate_data_0["$rMul"] = 54
validate_data_0["$rDivQ"] = 1
validate_data_0["$rDivM"] = 3
validate_data_0["$rDeref"] = 10
pfstrs = [
("$rAdd 2 2 + =", True), # $rAdd = 2 + 2 = 4
("$rAdd $rAdd 2 + =", True), # $rAdd = $rAdd + 2 = 6
("$rAdd 2 $rAdd + =", True), # $rAdd = 2 + $rAdd = 8
("99", False), # put some junk on the stack...
("$rAdd2 2 2 + =", True), # ...and make sure things still work
("$rAdd2\t2\n2 + =", True), # same but with different whitespace
("$rAdd2 2 2 + = ", True), # trailing whitespace
(" $rAdd2 2 2 + =", True), # leading whitespace
("$rAdd2 2 2 + =", True), # extra whitespace
("$T0 2 = +", False), # too few operands for add
("2 + =", False), # too few operands for add
("2 +", False), # too few operands for add
("+", False), # too few operands for add
("^", False), # too few operands for dereference
("=", False), # too few operands for assignment
("2 =", False), # too few operands for assignment
("2 2 + =", False), # too few operands for assignment
("2 2 =", False), # can't assign into a literal
("k 2 =", False), # can't assign into a constant
("2", False), # leftover data on stack
("2 2 +", False), # leftover data on stack
("$rAdd", False), # leftover data on stack
("0 $T1 0 0 + =", False), # leftover data on stack
("$T2 $T2 2 + =", False), # can't operate on an undefined value
("$rMul 9 6 * =", True), # $rMul = 9 * 6 = 54
("$rSub 9 6 - =", True), # $rSub = 9 - 6 = 3
("$rDivQ 9 6 / =", True), # $rDivQ = 9 / 6 = 1
("$rDivM 9 6 % =", True), # $rDivM = 9 % 6 = 3
("$rDeref 9 ^ =", True) # $rDeref = ^9 = 10 (FakeMemoryRegion)
]
for (test, should_succeed) in pfstrs:
try:
evaluate(test)
if len(repr(test)) > 50:
test = test[:50] + "[...]"
if not should_succeed:
print('Test %-60s FAILED.' % repr(test))
else:
print('Test %-60s PASSED.' % repr(test))
except ValueError:
if should_succeed:
print('Test %-60s FAILED.' % repr(test))
else:
print('Test %-60s PASSED.' % repr(test))
for k in validate_data_0:
assert vars[k] == validate_data_0[k]