Ruby for Pentesters #1: Use Modules For Lists Of Constants
Thomas Ptacek | July 3rd, 2008 | Filed Under: Uncategorized
Almost 2 years ago, Dino declared Python to be the “lingua-franca of over-the-hill hackers”, boldly asserting that 5 out of 6 security hackers under the age of 30 preferred Ruby instead. Being 30 at the time, I was an easy psychological target for this argument. I made the switch and haven’t regretted it. You can tell me all you want that “named nested functions are just as good as lambdas”, or that “you can fake Ruby blocks with a for loop and a generator”. Ruby is just nicer to write testing code in, and makes me feel at least 2 years younger and less experienced than I really am. Thanks, Ruby!
I’ve been meaning to write a long post about our house Ruby style, and some of the Ruby tips and tricks we’ve picked up along the way. But every time I sit down to write it, that post starts sounding a lot like work. So instead, I’d like to inaugurate a new series of much easier posts: Ruby for Pen-testers.
Where was I?
1. Use Modules For Lists Of Constants
If you test protocols or C code, you run into lists of magic numbers all the time. For example, here’s a bit of ptrace(2):
#define PT_READ_I 1 /* read word in child’s I space */
#define PT_READ_D 2 /* read word in child’s D space */
#define PT_READ_U 3 /* read word in child’s user structure */
#define PT_WRITE_I 4 /* write word in child’s I space */
#define PT_WRITE_D 5 /* write word in child’s D space */
#define PT_WRITE_U 6 /* write word in child’s user structure */
#define PT_CONTINUE 7 /* continue the child */
#define PT_KILL 8 /* kill the child process */
This is gross, but it’s C code, so you give them a break. But here’s some code from Pedram’s PyDbg:
TH32CS_SNAPPROCESS = 0x00000002
TH32CS_SNAPTHREAD = 0x00000004
TH32CS_SNAPMODULE = 0x00000008
TH32CS_INHERIT = 0x80000000
Now, Pedram does have the excuse of writing in Python. But here’s Ruby-MySql:
COM_QUIT = 1
COM_INIT_DB = 2
COM_QUERY = 3
This code has no excuse. (Here’s a rewrite that is much faster). Now, let’s look at net-ssh; if you haven’t read Jamis’ net-ssh code, you shouldn’t write any more packet processing code until you do.
# Transport layer generic messages
DISCONNECT = 1
IGNORE = 2
UNIMPLEMENTED = 3
DEBUG = 4
# …
end
Getting closer. But not there yet. Here’s an even better way:
CARRY = (1<< 0)
X0 = (1<< 1)
PARITY = (1<< 2)
# …
VINT = (1<< 19)
VINTPENDING = (1<< 20)
CPUID = (1<< 21)
end
That’s right: one module per set of constants. In other words, substitute “module” for “enum”. This has many benefits:
It’s clean. You can immediately find all the related magic numbers, both from the list, and by looking at code that uses the magic numbers —- you see Ragweed::EFlags::CARRY, you know to look for “EFlags”.
Modules come with special bonus features.
For instance:
def to_name_hash
@name_hash ||= constants.map {|k| [k.intern, const_get(k.intern)]}.to_hash
end
def to_value_hash
@key_hash ||= constants.map {|k| [const_get(k.intern), k.intern]}.to_hash
end
end
EFlags.to_value_hash[1 << 19] # => :VINT
… which is super nice when you’re printing out the contents of packets.


Lee Hinman
July 3rd, 2008 5:59 pmYour github link is broken, it links to http://www.matasano.com/log/1084/ruby-for-pentesters-1-use-modules-for-lists-of-constants/www.github.com/tqbf/asymy
Just wanted to let you know
I <3 Ruby
kowsik
July 4th, 2008 12:03 amI use this all the time. In general, I’m just a big fan of self-enumerating/annotating code. Saves a lot of time. To do this in C is a little bit of work, but saves a ton of mistakes and also propagates new additions to the enum list automatically. See:
http://labs.mudynamics.com/2007/01/03/enums-strings-and-laziness/
Thomas Ptacek
July 4th, 2008 1:37 amNice catch, thanks!
Person
July 4th, 2008 5:28 pmPython and Perl have equivalent mechanisms.
Thomas Ptacek
July 4th, 2008 9:10 pmWhat’s the equivalent mechanism in Python? You get no credit for using a dict.
snake in the grass
July 5th, 2008 1:00 amclass Flags(type):
def __new__(cls, clsname, clsbases, clsdict):
clsdict[’to_value_hash’] = clsdict
clsdict[’to_name_hash’] = dict([ (y, x) for x, y in clsdict.iteritems() if x.isupper() ])
return type.__new__(cls, clsname, clsbases, clsdict)
class EFlags:
__metaclass__ = Flags
CARRY = (1<< 0)
X0 = (1<< 1)
PARITY = (1<< 2)
# …
VINT = (1<< 19)
VINTPENDING = (1<< 20)
CPUID = (1<< 21)
if __name__ == ‘__main__’:
print ‘CARRY’, EFlags.CARRY
print ‘CPUID’, EFlags.to_value_hash[’CPUID’]
print EFlags.to_name_hash[1 << 19]
hidden_pythonista
July 5th, 2008 4:26 amWhen you want to do equivalent stuff in Python, use a class and a metaclass.
—8<—
class MetaEnum(type):
def __new__(cls, name, bases, classdict):
classdict[’to_name_hash’] = \
dict([(enum, val) for enum, val in classdict.iteritems()
if not enum.startswith(’__’)])
classdict[’to_value_hash’] = \
dict([(val, enum) for enum, val in classdict[’to_name_hash’].iteritems()])
return type.__new__(cls, name, bases, classdict)
class EFlags(object):
__metaclass__ = MetaEnum
CARRY = (1<< 0)
X0 = (1<< 1)
PARITY = (1<< 2)
# …
VINT = (1<< 19)
VINTPENDING = (1<< 20)
CPUID = (1<< 21)
EFlags.to_name_hash[’VINT’] # 524288
EFlags.to_value_hash[1 << 19] # ‘VINT’
—8<—
Oh, I used 2 dicts. So I got 2×0 = 0 credit :’(
Thomas
July 5th, 2008 5:32 amin python all class data is stored in a dict
class C:
pass
C.a = 0
C.b = 1
C.__dict__
{’a': 0, ‘__module__’: ‘__main__’, ‘b’: 1, ‘__doc__’: None}
Florian Gross
July 5th, 2008 6:52 amThis code I wrote ages ago might also be somewhat interesting:
http://flgr.0×42.net/code/enum.rb
The main benefit being that inspection of an enum member (wherever it appears) will show you the name instead of the value.
Matt
July 5th, 2008 3:23 pmWhat’s wrong with dicts?
Thomas Ptacek
July 5th, 2008 5:49 pmNothing. Even C has this feature. Just use a hash table library to store the enum values!
Phill
July 5th, 2008 11:32 pmSyntactic sugar causes cancer of the semicolon.
Stephen Reese
July 6th, 2008 9:27 pmGreat, I can’t wait to see more in this series!
Asted Habibbi
July 22nd, 2008 12:37 amDon’t get the argument — I’m not a computer scientist. I’m just a hacker (the good definition) that needs to exploit something.
Perl has Net::RawIP to help
Python has scapy
Ruby has scruby
C has libnet and libdnet
Python is prob the best IDA Pro automation language (IdaRub has not been updated in forever). Python also is handy for the Immunity debugger. And a bunch of other good RE tools are in python.
Python and Ruby do not have libwhisker so perl has to stick around.
Just use what works and learn them all like i had to…..
peace out.
Leave a reply