anal/x86analyzer.py 0000644 0001750 0001750 00000040522 11524233714 013517 0 ustar ender ender #!/usr/bin/env python
"""
This file is part of Pyew
Copyright (C) 2009, 2010 Joxean Koret
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
"""
import sys
import time
class CX86CallGraph(object):
def __init__(self):
self.functions = []
self.connections = []
class CX86Function(object):
def __init__(self, addr):
self.address = addr
self.basic_blocks = []
self.edges = []
self.connections = []
self.stats = []
def addOutConnection(self, conn):
if conn not in self.connections:
self.connections.append(conn)
class CX86BasicBlock(object):
def __init__(self):
self.instructions = []
self.inrefs = []
self.connections = []
self.offset = 0
def addConnection(self, afrom, ato):
if (afrom, ato) not in self.connections:
self.connections.append((afrom, ato))
class CX86CodeAnalyzer:
def __init__(self, pyew, type="PE"):
self.pyew = pyew
self.type = type
self.names = {}
self.queue = ()
self._imports = self.pyew.imports.values()
self.analyzed = []
self.functions = {}
self.function_stats = {}
self.basic_blocks = {}
self.xrefs_to = {}
self.xrefs_from = {}
self.antidebug = []
self.timeout = None
self.timeout = 300
self.last_msg_size = 0
self.start_time = 0
def belongsTo(self, offset, func):
if not self.functions.has_key(func):
return False
for x in self.functions[func].basic_blocks:
for n in x.instructions:
if n == offset:
return True
return False
def resolveAddress(self, addr):
addr = str(addr)
if addr.find("[") > -1:
addr = addr.strip("[").strip("]")
if addr.find("+") > -1 or addr.find("-") > -1:
return addr, False, True
name = self.pyew.resolveName(addr)
if name in self._imports:
return addr, True, False
else:
try:
addr = int(addr, 16)
except:
# It's a CALL REG or something like this...
return None, False, True
return addr, False, False
def addXref(self, afrom , ato):
if self.xrefs_to.has_key(ato):
self.xrefs_to[ato].append(afrom)
else:
self.xrefs_to[ato] = [afrom]
if self.xrefs_from.has_key(afrom):
self.xrefs_from[afrom].append(ato)
else:
self.xrefs_from[afrom] = [ato]
def createFunction(self, addr):
if self.timeout != 0 and time.time() > self.start_time + self.timeout:
raise Exception("Code analysis for x86 timed-out")
if addr in self.analyzed or addr in self.functions:
#print "Function %08x already analyzed" % addr
return
self.names[addr] = "sub_%08x" % addr
f = CX86Function(addr)
lines = self.pyew.disasm(addr, self.pyew.processor, self.pyew.type, 100, 1500)
bb = CX86BasicBlock()
bb.offset = addr
flow = []
# Possible values for break_bb are:
#
# 0 = Do nothing
# 1 = Break the basic block
# 2 = Break the basic block and clear 'lines'
#
break_bb = 0
analyzed_total = 0
i = 0
#
# Iterate while there is at least one more line of code
# or there is some address to follow (in list flow)
#
while len(lines) > 0 or len(flow) > 0:
if len(lines) > 0:
l = lines[0]
lines = lines[1:]
if l.offset in self.analyzed:
# Already analyzed
"""analyzed_total += 1
if analyzed_total > 16:
lines = []"""
if l.offset in self.basic_blocks:
lines = []
if len(bb.instructions) > 0:
bb.addConnection(bb.instructions[-1].offset, l.offset)
# Save the current basic block
f.basic_blocks.append(bb)
self.basic_blocks[bb.instructions[0].offset] = bb
# Create a new one
bb = CX86BasicBlock()
continue
else:
continue
else:
analyzed_total = 0
else:
#flow.reverse()
naddr = flow.pop()
#print "Previously saved %08x" % naddr
if naddr in self.analyzed:
# Already analyzed
continue
# Create a new basic block
bb = CX86BasicBlock()
# And fill the assembly lines
lines = self.pyew.disasm(naddr, self.pyew.processor, self.pyew.type, 100, 1500)
l = lines[0]
#print "%08x" % lines[0].offset, lines[0].mnemonic, lines[0].operands
# Does the address belong to any already analyzed basic block?
if not self.basic_blocks.has_key(l.offset):
bb.instructions.append(l)
if bb.offset == 0:
bb.offset = l.offset
mnem = str(l.mnemonic).upper()
# Set the current offset as already analyzed
self.analyzed.append(l.offset)
# Check for typical antidebuggin/antiemulation techniques before
# doing anything else
if mnem.startswith("INT") or mnem.startswith("UD") or \
mnem.startswith("RDTSC") or mnem.find("IDT") > -1 or \
mnem.startswith("CPU") or mnem.find("GDT") > -1 or \
mnem.startswith("SYS") or (mnem == "NOP" and str(l.operands) != ""):
self.antidebug.append((l.offset, str(l.mnemonic)))
if self.basic_blocks.has_key(l.offset):
break_bb = 2
elif mnem.find("CALL") > -1: # JMP?
#
# Resolve the address of the call/jmp and check
# if it's an import and if it breaks the current
# basic block
#
val, isimport, isbreak = self.resolveAddress(l.operands)
self.addXref(l.offset, val)
# Register the connections for both the basic block and the
# current function
if val is None:
conn = str(l.operands)
else:
conn = val
#bb.addConnection(l.offset, conn)
f.addOutConnection(conn)
if isbreak and mnem == "JMP":
# We can't resolve the address, break the basic block
break_bb = 2
elif not isimport:
if mnem.find("CALL") > -1 and val is not None and val < self.pyew.maxsize:
#if val !=
if val not in self.queue and val not in self.analyzed and \
val != l.offset + l.size:
#print "Adding to queue %08x" % val
self.queue.append(val)
elif mnem == "JMP":
# Follow the jump if resolvable
#if type(val) is int:
if str(val).isdigit():
lines = self.pyew.disasm(val, self.pyew.processor, self.pyew.type, 100, 1500)
else:
break_bb = 2
elif mnem.startswith("J") or mnem.startswith("LOOP"):
# Break the basic block without clearing 'lines' as we will set
# the value here
break_bb = 1
# Follow the flow
val, isimport, isbreak = self.resolveAddress(l.operands)
self.addXref(l.offset, val)
# Register the connection only for the basic block
bb.addConnection(l.offset, val)
if mnem != "JMP" and val < self.pyew.maxsize and val is not None:
lines = self.pyew.disasm(val, self.pyew.processor, self.pyew.type, 100, 1500)
if mnem != "JMP":
bb.addConnection(l.offset, l.offset + l.size)
if l.offset + l.size not in self.analyzed:
# Save the next instruction for later analysis
flow.append(l.offset + l.size)
else:
if isbreak or isimport:
break_bb = 2
else:
# Follow the jump if resolvable
if str(val).isdigit():
#if type(val) is int:
lines = self.pyew.disasm(val, self.pyew.processor, self.pyew.type, 100, 1500)
else:
break_bb = 2
elif mnem.startswith("RET") or mnem.startswith("HLT") or \
mnem.startswith("UD"):
# Break the basic block and clear 'lines', we don't want to
# continue analyzing the next assembler lines
break_bb = 2
i += 1
# Do we have to clear anything?
if break_bb != 0:
if len(bb.instructions) > 0:
# Save the current basic block
f.basic_blocks.append(bb)
self.basic_blocks[bb.instructions[0].offset] = bb
# Create a new one
bb = CX86BasicBlock()
if break_bb == 2:
# ...and clear 'lines' if required
lines = []
i = 0
break_bb = 0
if len(bb.instructions) > 0:
f.basic_blocks.append(bb)
self.basic_blocks[bb.instructions[0].offset] = bb
"""
for bb in f.basic_blocks:
for i in bb.instructions:
try:
x = "0x%08x" % self.pyew.getVirtualAddressFromOffset(int(str(i[2]), 16))
except:
x = i[2]
print "%08x" % self.pyew.getVirtualAddressFromOffset(int(i[0])), i[1], x
print "--"
raw_input("Guay?")
"""
self.functions[f.address] = f
addr = None
def calculateFunctionStats(self, addr):
if not self.functions.has_key(addr):
#raw_input("Function doesn't exists?")
return
for bb in self.functions[addr].basic_blocks:
self.functions[addr].connections += bb.connections
nodes = len(self.functions[addr].basic_blocks)
edges = len(self.functions[addr].connections)
p = 2 # I know, I know...
cc = edges - nodes + p
self.functions[addr].stats = (nodes, edges, cc)
self.function_stats[addr] = (nodes, edges, cc)
def analyzeArea(self, addr):
if len(self.queue) == 0:
self.queue = [addr]
else:
self.queue.append(addr)
while addr is not None and len(self.queue) > 0:
if self.timeout != 0 and time.time() > self.start_time + self.timeout:
raise Exception("Code analysis for x86 timed-out")
addr = self.queue.pop()
if addr not in self.analyzed:
#print "Creating function 0x%08x" % addr
self.createFunction(addr)
self.calculateFunctionStats(addr)
if not self.pyew.batch:
msg = "\b"*self.last_msg_size + "Analyzing address 0x%08x" % addr + " - %d in queue / %d total" % (len(self.queue), len(self.functions))
#print "\b"*self.last_msg_size + " "*self.last_msg_size + "\b"*self.last_msg_size
self.last_msg_size = len(msg)
sys.stdout.write(msg)
sys.stdout.flush()
#print self.queue
for f in self.functions:
if len(self.functions[f].basic_blocks) == 1:
if len(self.functions[f].basic_blocks[0].instructions) >= 1:
x = self.functions[f].basic_blocks[0].instructions[0]
# if x.mnemonic == "JMP":
addr, isimport, isbreak = self.resolveAddress(x.operands)
try:
addr = int(addr, 16)
self.names[f] = "j_" + self.pyew.names[addr]
except:
continue
#self.calculeStats()
if not self.pyew.batch:
#sys.stdout.write("\b"*self.last_msg_size + " "*self.last_msg_size + "\b"*self.last_msg_size)
pass
return True
def analyzeEntryPoint(self):
try:
exports = self.pyew.exports
self.queue = self.pyew.exports.keys()
except:
# Just ignore the exception
pass
self.analyzeArea(self.pyew.ep)
def doCodeAnalysis(self, ep=True, addr=None):
self.start_time = time.time()
if ep:
self.analyzeEntryPoint()
else:
self.analyzeArea(addr)
self.pyew.antidebug = self.antidebug
self.pyew.names.update(self.functions)
self.pyew.names.update(self.names)
self.pyew.names[self.pyew.ep] = "start"
try:
for exp in self.pyew.pe.DIRECTORY_ENTRY_EXPORT.symbols:
try:
addr = self.pyew.pe.get_offset_from_rva(exp.address)
except:
addr = exp.address
if exp.name and exp.name != "":
self.pyew.names[addr] = exp.name
else:
self.pyew.names[addr] = expordinal
except:
pass
self.pyew.functions = self.functions
self.pyew.xrefs_to = self.xrefs_to
self.pyew.xrefs_from = self.xrefs_from
self.pyew.basic_blocks = self.basic_blocks
self.pyew.function_stats = self.function_stats
self.pyew.program_stats = self.calculeStats()
self.pyew.seek(0)
def calculeStats(self):
nodes = []
edges = []
ccs = []
for f in self.functions:
n, e, c = self.functions[f].stats
nodes.append(n)
edges.append(e)
ccs.append(c)
hash = {}
hash["nodes"] = {}
hash["nodes"]["max"] = max(nodes)
hash["nodes"]["min"] = min(nodes)
hash["nodes"]["avg"] = sum(nodes)/len(nodes)*1.00
hash["nodes"]["total"] = sum(nodes)
hash["edges"] = {}
hash["edges"]["max"] = max(edges)
hash["edges"]["min"] = min(edges)
hash["edges"]["avg"] = sum(edges)/len(edges)*1.00
hash["edges"]["total"] = sum(edges)
hash["ccs"] = {}
hash["ccs"]["max"] = max(ccs)
hash["ccs"]["min"] = min(ccs)
hash["ccs"]["avg"] = sum(ccs)/len(ccs)*1.00
hash["ccs"]["total"] = sum(ccs)
if self.pyew.debug:
print
print "Ciclomatic Complexity -> Max %d Min %d Media %2.2f" % (max(ccs), min(ccs), sum(ccs)/len(ccs)*1.00)
print "Total functions %d Total basic blocks %d" % (len(self.functions), len(nodes))
return hash
def test():
"""
sys.path.append("..")
from pyew import CPyew
pyew = CPyew(batch=False)
pyew.loadFile("test.exe")
"""
pass
if __name__ == "__main__":
test()
anal/__init__.py 0000644 0001750 0001750 00000000000 11524233714 013046 0 ustar ender ender AUTHORS 0000644 0001750 0001750 00000000044 11524233714 011102 0 ustar ender ender Joxean Koret
batch_antidebug.py 0000644 0001750 0001750 00000002705 11524233714 013515 0 ustar ender ender #!/usr/bin/python
import os
import sys
import time
import hashlib
from pyew_core import CPyew
def printData(pyew, path, msg):
buf = pyew.getBuffer()
print "File :", path
print "MD5 :", hashlib.md5(buf).hexdigest()
print "SHA1 :", hashlib.sha1(buf).hexdigest()
print "SHA256:", hashlib.sha256(buf).hexdigest()
print "Found :", msg
def checkAntidebug(path):
t = time.time()
pyew = CPyew(batch=True)
pyew.codeanalysis = True
try:
pyew.loadFile(path)
except KeyboardInterrupt:
print "Abort"
sys.exit(0)
except:
print "ERROR loading file %s" % path
return
if pyew.format not in ["PE", "ELF"]:
return
msg = pyew.antidebug
if len(antidebug) > 0:
print
printData(pyew, path, msg)
print "Time to analyze %f" % (time.time() - t)
print
def doChecks(path):
do_basic_graph_analysis(path)
def main(path):
buf = ""
for root, dirs, files in os.walk(path):
for x in files:
filepath = os.path.join(root, x)
sys.stdout.write("\b"*len(buf) + " "*len(buf) + "\b"*len(buf))
buf = "Analyzing file %s ..." % filepath
sys.stdout.write(buf)
sys.stdout.flush()
doChecks(filepath)
print
def usage():
print "Usage:", sys.argv[0], ""
if __name__ == "__main__":
if len(sys.argv) == 1:
usage()
else:
main(sys.argv[1]) batch_example.py 0000744 0001750 0001750 00000007761 11524233714 013216 0 ustar ender ender #!/usr/bin/python
"""
This is an example batch script for Pyew. It uses many APIs exported by Pyew like
getBytes, getMnems, disasm, getBuffer or hexdump.
"""
import os
import sys
import time
import hashlib
from pyew_core import CPyew
def printData(pyew, path, msg):
buf = pyew.getBuffer()
print "File :", path
print "MD5 :", hashlib.md5(buf).hexdigest()
print "SHA1 :", hashlib.sha1(buf).hexdigest()
print "SHA256:", hashlib.sha256(buf).hexdigest()
print "Found :", msg
def checkMebroot(path):
pyew = CPyew(batch=True)
pyew.codeanalysis = True
try:
pyew.loadFile(path)
except:
print "ERROR loading file %s" % path
return
if pyew.format == "PE":
# Get 6 bytes at offset 0xB8
if pyew.getBytes(0xB8, 6) != "Rich;\x2E":
return
printData(pyew, path, "Mebroot downloader")
print
def checkMnemonics(path):
pyew = CPyew(batch=True)
pyew.codeanalysis = True
try:
pyew.loadFile(path)
except:
print "ERROR loading file %s" % path
return
# Is it a PE file?
if pyew.format == "PE":
# The most common x86 mnemonics
commons = ["PUSH", "MOV", "SUB", "ADD", "LEA", "CALL", "JMP", "JZ", "JNZ", \
"OR", "XOR", "NOT", "POP", "AND", "TEST", "JL", "JG", "JE", \
"JLE", "CMP", "LEAVE", "RET", "NOP", "PUSHF", "POPF", "INC", \
"INT 3", "DEC", "PUSHA", "POPA"]
try:
# Get the 30 first mnemonics
mnems = pyew.GetMnems(pyew.ep, 30)
except:
print "ERROR scanning file %s" % path
return
ret = []
for x in mnems:
if x not in commons and x not in ret:
ret.append(x)
if len(ret) > 0:
printData(pyew, path, "Uncommon mnemonics")
print "Mnemonics:", ",".join(ret)
print
# Seek to the entry point
pyew.seek(pyew.ep)
# Hexdump the first 64 bytes at the entry point
print pyew.hexdump(pyew.buf, length=16, bsize=64)
def entryPointCalls(path):
pyew = CPyew(batch=True)
pyew.codeanalysis = True
try:
pyew.loadFile(path)
except KeyboardInterrupt:
print "Abort"
sys.exit(0)
except:
print "ERROR loading file %s" % path
return
if pyew.format != "PE":
return
calls = []
# Get the disassembl of the first 100 lines
l = pyew.disasm(pyew.ep, processor=pyew.processor, type=pyew.type, lines=100, bsize=1600)
for i in l:
mnem = str(i.mnemonic)
# Is it a direct or indirect jump or call?
if mnem == "CALL" or mnem.startswith("J") or mnem.startswith("LOOP"):
operands = str(i.operands).replace("[", "").replace("]", "")
try:
if pyew.imports.has_key(int(operands, 16)):
x = pyew.imports[int(operands, 16)]
if x not in calls:
calls.append(x)
except:
pass
if len(calls) > 0:
printData(pyew, path, "Library calls at Entry Point")
print "Library Calls:", ",".join(calls)
print
def doChecks(path):
# Example usage to check for the Mebroot downloader
checkMebroot(path)
# Example to extract the first (non common) mnemonics from the entry point
checkMnemonics(path)
# Example to print the API calls at the entry point
entryPointCalls(path)
def main(path):
for root, dirs, files in os.walk(path):
for x in files:
filepath = os.path.join(root, x)
print "Analyzing file %s" % filepath
t = time.time()
doChecks(filepath)
print "Time to analyze %f" % (time.time() - t)
def usage():
print "Usage:", sys.argv[0], ""
if __name__ == "__main__":
if len(sys.argv) == 1:
usage()
else:
main(sys.argv[1])
config.py 0000755 0001750 0001750 00000000346 11524233714 011661 0 ustar ender ender #!/usr/bin/env python
import os
CODE_ANALYSIS=True
DEEP_CODE_ANALYSIS=False
CONFIG_ANALYSIS_TIMEOUT=0
PLUGINS_PATH=os.path.dirname(__file__) + os.sep + "plugins"
DATABASE_PATH=os.path.dirname(__file__) + os.sep + "files.sqlite"
contrib/scripting.txt 0000644 0001750 0001750 00000007250 11524233714 014243 0 ustar ender ender Scripting
---------
This feature offers the option to pack a bunch of pyew commands and python code in a file in order to easily automate some tasks.
Scripting in pyew can be used in two different forms. First one is creating a script that will be executed automatiacally every time pyew is run and that can be used to store default settings or start up actions to customize pyew behaviour. By default the script must be named 'runme.py' and has to be placed on pyew's root directory. This is an example of init script:
----------------- code ----------------
pyew.case='low'
a
s ep
dis
----------------- code ----------------
This runme script sets asm to low case, analyzes code, seeks to Entry Point and runs disassembly. Also python code can be used:
----------------- code ----------------
print "I prefer low case ASM ;-)"
pyew.case='low'
a
print "Seek to Entry Point"
s ep
pyew.bsize=64
dis
----------------- code ----------------
The second option to run pyew scripts is by placing them under the 'scripts' directory and using the 'ls' command. This command can be run with or without arguments; when used without, it will list available scripts in the 'scripts' directory:
----------------- code ----------------
[0x00000000]> ls
Scripts available:
sample_runme
mebroot
----------------- code ----------------
If used with an argument, it will be used as the script name to launch:
----------------- code ----------------
[0x00000000]> ls sample_runme
I prefer lower case ASM :P
Code Analysis ...
Seek to Entry Point
0x00000bd0 ; FUNCTION start
0x00000bd0 (02) 31ed xor ebp, ebp
0x00000bd2 (01) 5e pop esi
0x00000bd3 (02) 89e1 mov ecx, esp
0x00000bd5 (03) 83e4 f0 and esp, -0x10
0x00000bd8 (01) 50 push eax
0x00000bd9 (01) 54 push esp
0x00000bda (01) 52 push edx
0x00000bdb (05) 68 c0d30408 push 0x804d3c0
0x00000be0 (05) 68 d0d30408 push 0x804d3d0
0x00000be5 (01) 51 push ecx
0x00000be6 (01) 56 push esi
0x00000be7 (05) 68 b08e0408 push 0x8048eb0
0x00000bec (05) e8 53feffff call 0x00000a44 ; 1 j___libc_start_main
0x00000bf1 (01) f4 hlt
0x00000bf2 (01) 90 nop
0x00000bf3 (01) 90 nop
0x00000bf4 (01) 90 nop
0x00000bf5 (01) 90 nop
0x00000bf6 (01) 90 nop
0x00000bf7 (01) 90 nop
0x00000bf8 (01) 90 nop
0x00000bf9 (01) 90 nop
0x00000bfa (01) 90 nop
0x00000bfb (01) 90 nop
0x00000bfc (01) 90 nop
0x00000bfd (01) 90 nop
0x00000bfe (01) 90 nop
0x00000bff (01) 90 nop
0x00000c00 (01) 55 push ebp
0x00000c01 (02) 89e5 mov ebp, esp
0x00000c03 (01) 53 push ebx
0x00000c04 (03) 83ec 04 sub esp, 0x4
0x00000c07 (07) 803d e4000508 00 cmp byte [0x80500e4], 0x0
0x00000c0e (02) 75 41 jnz 0x00000c51 ; 2
0x00000c0e ----------------------------------------------------------------------
0x00000c10 (06) 8b15 e8000508 mov edx, [0x80500e8]
0x00000c16 (05) b8 14ff0408 mov eax, 0x804ff14
0x00000c1b (05) 2d 10ff0408 sub eax, 0x804ff10
0x00000c20 (03) c1f8 02 sar eax, 0x2
[0x00000bd0]>
----------------- code ----------------
And that's all regarding pyew scripting. Now it's time for you to experiment and share your best scripts with us!
COPYING 0000644 0001750 0001750 00000043103 11524233714 011070 0 ustar ender ender GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
ChangeLog 0000644 0001750 0001750 00000006605 11524233714 011615 0 ustar ender ender Fixed minor bugs in code analysis. Fixed plugin loading system. It doesn't crash if you don't have any of the requirements. Added the "safe_unpickle.py" file that was missing in previous commits.
Today (12 minutes ago)
Added very basic support for adjacency list clusterization to gcluster.py. Added support for loading and saving databases. Fixed a bug in plugin 'graphs.py'. Pyew crashes if module PIL wasn't installed. Small bug-fixes to code analysis engine for x86.
Jan 28, 2011
Added tool gcluster.py to clusterize programs (ELF and PE).
Jan 23, 2011
A warning is issued when opening ELF 64 files.
Jan 16, 2011
Added command 'binvi' to show an image representing the contents of the file. Usefull to see different sections in a binary.
Jan 09, 2011
Added 'xdot.py' to the package. Added the plugin 'graphs' to show the callgraph of the program.
Jan 09, 2011
Fixed a bug with the deep code analysis.
Jan 09, 2011
Minor code analysis fixes.
Jan 09, 2011
Code analyzer for x86 completely rewritten from scratch. It's working OK now. Changed the copyright header adding the new date. Fixed a bug in the "packer" plugin. Fixed a bug in the PDF engine. Hexadecimal strings with spaces between the hexadecimal data caused Pyew to fail decoding it. Fixed a bug in the "url" plugin. URLs with a character "-" in the hostname weren't recognized. Fixed a lot of very stupid bugs in "pyew_core.py".
Nov 25, 2010
Added command "edit" for opening the file in r+w mode. Added commands "wx" and "wa" to patch the file. Added command "interact" to open an interactive Python console.
Nov 20, 2010
Fixed bugs in code analysis for x86. Fixed bug in VT module. Fixed bug opening URLs with SSL (https). Added support to search more functions during code analysis (x86).
Nov 13, 2010
Scripting support files. Thanks Hugo!
Jul 02, 2010
Added scripts support via Hugo Teso. Thanks!
Jul 02, 2010
Applied patch from wamcvey for bug #6 loading ELF objects using binary strings.
Jul 02, 2010
Added autocompletion support. Thanks to Hugo!
Jun 13, 2010
Applied a small patch to show assembly code in upper case or lower case (depending on user's choice). Thanks to Hugo!
Jun 10, 2010
Added the option to enable/disable code analysis. Thanks to Hugo!
Jun 10, 2010
Fixed one small bug in the PDF example usage (pdf_example.py).
Jun 09, 2010
Dirty hack to record in one SQLite database data about analyzed (PE/ELF) files.
Jun 08, 2010
Fixed a small bug and added license header.
Jun 07, 2010
Added a plugin to check PE's packers and fixed a minor bugs in the plugin loading mechanism.
Jun 07, 2010
Updated PDFid to version 0.0.11. Added support to pass commands via command line to plugin's registered commands. Added the command "pdfstream", "pdfobj", "pdfso" and "pdfss" to list streams/objs and seek to the offset where the selected stream/obj is.
Jun 07, 2010
Added the option to find x86 functions when analyzing memory dumps.
Jun 06, 2010
Fixed a bug parsing PDF files when spaces are used after the stream word. Thanks to binjo.cn!
Mar 29, 2010
A lot of fixes to the PDF plugins and engine (Thanks to scz ).
Mar 29, 2010
Nov 24 2009 - Version 1.1.1 - Joxean Koret:
* Code analysis now works the way it's suppossed to work ;)
Nov 23 2009 - Version 1.1.0 - Joxean Koret:
* Added support for ELF format.
Nov 22 2009 - Version 1.0.0 - Joxean Koret:
* Firt public release.
Elf/elf_lookup.py 0000644 0001750 0001750 00000035060 11524233714 013257 0 ustar ender ender ## EM enumeration definitions
EM_NONE = 0
EM_M32 = 1
EM_SPARC = 2
EM_386 = 3
EM_68K = 4
EM_88K = 5
EM_860 = 7
EM_MIPS = 8
EM_S370 = 9
EM_MIPS_RS3_LE = 10
EM_PARISC = 15
EM_VPP500 = 17
EM_SPARC32PLUS = 18
EM_960 = 19
EM_PPC = 20
EM_PPC64 = 21
EM_S390 = 22
EM_V800 = 36
EM_FR20 = 37
EM_RH32 = 38
EM_RCE = 39
EM_ARM = 40
EM_FAKE_ALPHA = 41
EM_SH = 42
EM_SPARCV9 = 43
EM_TRICORE = 44
EM_ARC = 45
EM_H8_300 = 46
EM_H8_300H = 47
EM_H8S = 48
EM_H8_500 = 49
EM_IA_64 = 50
EM_MIPS_X = 51
EM_COLDFIRE = 52
EM_68HC12 = 53
EM_MMA = 54
EM_PCP = 55
EM_NCPU = 56
EM_NDR1 = 57
EM_STARCORE = 58
EM_ME16 = 59
EM_ST100 = 60
EM_TINYJ = 61
EM_X86_64 = 62
EM_PDSP = 63
EM_FX66 = 66
EM_ST9PLUS = 67
EM_ST7 = 68
EM_68HC16 = 69
EM_68HC11 = 70
EM_68HC08 = 71
EM_68HC05 = 72
EM_SVX = 73
EM_ST19 = 74
EM_VAX = 75
EM_CRIS = 76
EM_JAVELIN = 77
EM_FIREPATH = 78
EM_ZSP = 79
EM_MMIX = 80
EM_HUANY = 81
EM_PRISM = 82
EM_AVR = 83
EM_FR30 = 84
EM_D10V = 85
EM_D30V = 86
EM_V850 = 87
EM_M32R = 88
EM_MN10300 = 89
EM_MN10200 = 90
EM_PJ = 91
EM_OPENRISC = 92
EM_ARC_A5 = 93
EM_XTENSA = 94
EM_NUM = 95
EM_ALPHA = 0x9026
# There are plenty more of these to
# fill in, but... this is all I need
# for now...
e_machine_32 = (
EM_386,
EM_PPC,
EM_ARM,
)
e_machine_64 = (
EM_PPC64,
EM_SPARCV9,
EM_X86_64
)
e_machine_types = {
EM_NONE:"No machine",
EM_M32:"AT&T WE 32100",
EM_SPARC:"SUN SPARC",
EM_386:"Intel 80386",
EM_68K:"Motorola m68k family",
EM_88K:"Motorola m88k family",
EM_860:"Intel 80860",
EM_MIPS:"MIPS R3000 big-endian",
EM_S370:"IBM System/370",
EM_MIPS_RS3_LE:"MIPS R3000 little-endian",
EM_PARISC:"HPPA",
EM_VPP500:"Fujitsu VPP500",
EM_SPARC32PLUS:"Suns v8plus",
EM_960:"Intel 80960",
EM_PPC:"PowerPC",
EM_PPC64:"PowerPC 64-bit",
EM_S390:"IBM S390",
EM_V800:"NEC V800 series",
EM_FR20:"Fujitsu FR20",
EM_RH32:"TRW RH-32",
EM_RCE:"Motorola RCE",
EM_ARM:"ARM",
EM_FAKE_ALPHA:"Digital Alpha",
EM_SH:"Hitachi SH",
EM_SPARCV9:"SPARC v9 64-bit",
EM_TRICORE:"Siemens Tricore",
EM_ARC:"Argonaut RISC Core",
EM_H8_300:"Hitachi H8/300",
EM_H8_300H:"Hitachi H8/300H",
EM_H8S:"Hitachi H8S",
EM_H8_500:"Hitachi H8/500",
EM_IA_64:"Intel Merced",
EM_MIPS_X:"Stanford MIPS-X",
EM_COLDFIRE:"Motorola Coldfire",
EM_68HC12:"Motorola M68HC12",
EM_MMA:"Fujitsu MMA Multimedia",
EM_PCP:"Siemens PCP",
EM_NCPU:"Sony nCPU embeeded RISC",
EM_NDR1:"Denso NDR1 microprocessor",
EM_STARCORE:"Motorola Start*Core processor",
EM_ME16:"Toyota ME16 processor",
EM_ST100:"STMicroelectronic ST100 processor",
EM_TINYJ:"Advanced Logic Corp. Tinyj",
EM_X86_64:"AMD x86-64 architecture",
EM_PDSP:"Sony DSP Processor",
EM_FX66:"Siemens FX66 microcontroller",
EM_ST9PLUS:"STMicroelectronics ST9+ 8/16 mc",
EM_ST7:"STmicroelectronics ST7 8 bit mc",
EM_68HC16:"Motorola MC68HC16 microcontroller",
EM_68HC11:"Motorola MC68HC11 microcontroller",
EM_68HC08:"Motorola MC68HC08 microcontroller",
EM_68HC05:"Motorola MC68HC05 microcontroller",
EM_SVX:"Silicon Graphics SVx",
EM_ST19:"STMicroelectronics ST19 8 bit mc",
EM_VAX:"Digital VAX",
EM_CRIS:"Axis Communications 32-bit embedded processor",
EM_JAVELIN:"Infineon Technologies 32-bit embedded processor",
EM_FIREPATH:"Element 14 64-bit DSP Processor",
EM_ZSP:"LSI Logic 16-bit DSP Processor",
EM_MMIX:"Donald Knuths educational 64-bit processor",
EM_HUANY:"Harvard University machine-independent object files",
EM_PRISM:"SiTera Prism",
EM_AVR:"Atmel AVR 8-bit microcontroller",
EM_FR30:"Fujitsu FR30",
EM_D10V:"Mitsubishi D10V",
EM_D30V:"Mitsubishi D30V",
EM_V850:"NEC v850",
EM_M32R:"Mitsubishi M32R",
EM_MN10300:"Matsushita MN10300",
EM_MN10200:"Matsushita MN10200",
EM_PJ:"picoJava",
EM_OPENRISC:"OpenRISC 32-bit embedded processor",
EM_ARC_A5:"ARC Cores Tangent-A5",
EM_XTENSA:"Tensilica Xtensa Architecture",
EM_NUM:"",
EM_ALPHA:"",
}
ET_NONE = 0
ET_REL = 1
ET_EXEC = 2
ET_DYN = 3
ET_CORE = 4
ET_NUM = 5
ET_LOOS = 0xfe00
ET_HIOS = 0xfeff
ET_LOPROC = 0xff00
ET_HIPROC = 0xffff
e_types = {
ET_NONE:"No file type",
ET_REL:"Relocatable file",
ET_EXEC:"Executable file",
ET_DYN:"Shared object file",
ET_CORE:"Core file",
ET_NUM:"Number of defined types",
ET_LOOS:"OS-specific range start",
ET_HIOS:"OS-specific range end",
ET_LOPROC:"Processor-specific range start",
ET_HIPROC:"Processor-specific range end",
}
EV_NONE = 0
EV_CURRENT = 1
EV_NUM = 2
e_versions = {
EV_NONE:"Invalid ELF version",
EV_CURRENT:"Current version",
EV_NUM:"",
}
R_68K_NONE = 0
R_68K_32 = 1
R_68K_16 = 2
R_68K_8 = 3
R_68K_PC32 = 4
R_68K_PC16 = 5
R_68K_PC8 = 6
R_68K_GOT32 = 7
R_68K_GOT16 = 8
R_68K_GOT8 = 9
R_68K_GOT32O = 10
R_68K_GOT16O = 11
R_68K_GOT8O = 12
R_68K_PLT32 = 13
R_68K_PLT16 = 14
R_68K_PLT8 = 15
R_68K_PLT32O = 16
R_68K_PLT16O = 17
R_68K_PLT8O = 18
R_68K_COPY = 19
R_68K_GLOB_DAT = 20
R_68K_JMP_SLOT = 21
R_68K_RELATIVE = 22
e_flags_68k = {
R_68K_NONE:"No reloc",
R_68K_32:"Direct 32 bit",
R_68K_16:"Direct 16 bit",
R_68K_8:"Direct 8 bit",
R_68K_PC32:"PC relative 32 bit",
R_68K_PC16:"PC relative 16 bit",
R_68K_PC8:"PC relative 8 bit",
R_68K_GOT32:"32 bit PC relative GOT entry",
R_68K_GOT16:"16 bit PC relative GOT entry",
R_68K_GOT8:"8 bit PC relative GOT entry",
R_68K_GOT32O:"32 bit GOT offset",
R_68K_GOT16O:"16 bit GOT offset",
R_68K_GOT8O:"8 bit GOT offset",
R_68K_PLT32:"32 bit PC relative PLT address",
R_68K_PLT16:"16 bit PC relative PLT address",
R_68K_PLT8:"8 bit PC relative PLT address",
R_68K_PLT32O:"32 bit PLT offset",
R_68K_PLT16O:"16 bit PLT offset",
R_68K_PLT8O:"8 bit PLT offset",
R_68K_COPY:"Copy symbol at runtime",
R_68K_GLOB_DAT:"Create GOT entry",
R_68K_JMP_SLOT:"Create PLT entry",
R_68K_RELATIVE:"Adjust by program base",
}
R_386_NONE = 0
R_386_32 = 1
R_386_PC32 = 2
R_386_GOT32 = 3
R_386_PLT32 = 4
R_386_COPY = 5
R_386_GLOB_DAT = 6
R_386_JMP_SLOT = 7
R_386_RELATIVE = 8
R_386_GOTOFF = 9
R_386_GOTPC = 10
R_386_32PLT = 11
R_386_TLS_TPOFF = 14
R_386_TLS_IE = 15
R_386_TLS_GOTIE = 16
R_386_TLS_LE = 17
R_386_TLS_GD = 18
R_386_TLS_LDM = 19
R_386_16 = 20
R_386_PC16 = 21
R_386_8 = 22
R_386_PC8 = 23
R_386_TLS_GD_32 = 24
R_386_TLS_GD_PUSH = 25
R_386_TLS_GD_CALL = 26
R_386_TLS_GD_POP = 27
R_386_TLS_LDM_32 = 28
R_386_TLS_LDM_PUSH = 29
R_386_TLS_LDM_CALL = 30
R_386_TLS_LDM_POP = 31
R_386_TLS_LDO_32 = 32
R_386_TLS_IE_32 = 33
R_386_TLS_LE_32 = 34
R_386_TLS_DTPMOD32 = 35
R_386_TLS_DTPOFF32 = 36
R_386_TLS_TPOFF32 = 37
r_types_386 = {
R_386_NONE:"No reloc",
R_386_32:"Direct 32 bit",
R_386_PC32:"PC relative 32 bit",
R_386_GOT32:"32 bit GOT entry",
R_386_PLT32:"32 bit PLT address",
R_386_COPY:"Copy symbol at runtime",
R_386_GLOB_DAT:"Create GOT entry",
R_386_JMP_SLOT:"Create PLT entry",
R_386_RELATIVE:"Adjust by program base",
R_386_GOTOFF:"32 bit offset to GOT",
R_386_GOTPC:"32 bit PC relative offset to GOT",
R_386_32PLT:"",
R_386_TLS_TPOFF:"Offset in static TLS block",
R_386_TLS_IE:"Address of GOT entry for static",
R_386_TLS_GOTIE:"GOT entry for static TLS",
R_386_TLS_LE:"Offset relative to static",
R_386_TLS_GD:"Direct 32 bit for GNU version",
R_386_TLS_LDM:"Direct 32 bit for GNU version",
R_386_16:"",
R_386_PC16:"",
R_386_8:"",
R_386_PC8:"",
R_386_TLS_GD_32:"Direct 32 bit for general",
R_386_TLS_GD_PUSH:"Tag for pushl in GD TLS code",
R_386_TLS_GD_CALL:"Relocation for call",
R_386_TLS_GD_POP:"Tag for popl in GD TLS code",
R_386_TLS_LDM_32:"Direct 32 bit for local",
R_386_TLS_LDM_PUSH:"Tag for pushl in LDM TLS code",
R_386_TLS_LDM_CALL:"Relocation for call",
R_386_TLS_LDM_POP:"Tag for popl in LDM TLS code",
R_386_TLS_LDO_32:"Offset relative to TLS block",
R_386_TLS_IE_32:"GOT entry for negated static",
R_386_TLS_LE_32:"Negated offset relative to",
R_386_TLS_DTPMOD32:"ID of module containing symbol",
R_386_TLS_DTPOFF32:"Offset in TLS block",
R_386_TLS_TPOFF32:"Negated offset in static TLS block",
#R_386_NUM:"",
}
## Define e_flags to 386
r_types = r_types_386
SHT_NULL = 0
SHT_PROGBITS = 1
SHT_SYMTAB = 2
SHT_STRTAB = 3
SHT_RELA = 4
SHT_HASH = 5
SHT_DYNAMIC = 6
SHT_NOTE = 7
SHT_NOBITS = 8
SHT_REL = 9
SHT_SHLIB = 10
SHT_DYNSYM = 11
SHT_INIT_ARRAY = 14
SHT_FINI_ARRAY = 15
SHT_PREINIT_ARRAY = 16
SHT_GROUP = 17
SHT_SYMTAB_SHNDX = 18
SHT_LOOS = 0x60000000
SHT_GNU_LIBLIST = 0x6ffffff7
SHT_CHECKSUM = 0x6ffffff8
SHT_LOSUNW = 0x6ffffffa
SHT_GNU_verdef = 0x6ffffffd
SHT_GNU_verneed = 0x6ffffffe
SHT_GNU_versym = 0x6fffffff
SHT_HISUNW = 0x6fffffff
SHT_HIOS = 0x6fffffff
SHT_LOPROC = 0x70000000
SHT_HIPROC = 0x7fffffff
SHT_LOUSER = 0x80000000
SHT_HIUSER = 0x8fffffff
sh_type = {
SHT_NULL:"Section header table entry unused",
SHT_PROGBITS:"Program data",
SHT_SYMTAB:"Symbol table",
SHT_STRTAB:"String table",
SHT_RELA:"Relocation entries with addends",
SHT_HASH:"Symbol hash table",
SHT_DYNAMIC:"Dynamic linking information",
SHT_NOTE:"Notes",
SHT_NOBITS:"Program space with no data (bss)",
SHT_REL:"Relocation entries, no addends",
SHT_SHLIB:"Reserved",
SHT_DYNSYM:"Dynamic linker symbol table",
SHT_INIT_ARRAY:"Array of constructors",
SHT_FINI_ARRAY:"Array of destructors",
SHT_PREINIT_ARRAY:"Array of pre-constructors",
SHT_GROUP:"Section group",
SHT_SYMTAB_SHNDX:"Extended section indeces",
SHT_LOOS:"Start OS-specific",
SHT_GNU_LIBLIST:"Prelink library list",
SHT_CHECKSUM:"Checksum for DSO content.",
SHT_LOSUNW:"Sun-specific low bound.",
SHT_GNU_verdef:"Version definition section.",
SHT_GNU_verneed:"Version needs section.",
SHT_GNU_versym:"Version symbol table.",
SHT_HISUNW:"Sun-specific high bound.",
SHT_HIOS:"End OS-specific type",
SHT_LOPROC:"Start of processor-specific",
SHT_HIPROC:"End of processor-specific",
SHT_LOUSER:"Start of application-specific",
SHT_HIUSER:"End of application-specific",
}
SHF_WRITE = 1
SHF_ALLOC = 2
SHF_EXECINSTR = 4
SHF_MERGE = 16
SHF_STRINGS = 32
SHF_INFO_LINK = 64
SHF_LINK_ORDER = 128
SHF_OS_NONCONFORMING = 256
SHF_GROUP = 512
SHF_TLS = 1024
SHF_ORDERED = 1073741824
SHF_EXCLUDE = 2147483648
sh_flags = {
SHF_WRITE:"Writable",
SHF_ALLOC:"Occupies memory during execution",
SHF_EXECINSTR:"Executable",
SHF_MERGE:"Might be merged",
SHF_STRINGS:"Contains nul-terminated strings",
SHF_INFO_LINK:"`sh_info' contains SHT index",
SHF_LINK_ORDER:"Preserve order after combining",
SHF_OS_NONCONFORMING:"Non-standard OS specific",
SHF_GROUP:"Section is member of a group.",
SHF_TLS:"Section hold thread-local data.",
SHF_ORDERED:"Special ordering",
SHF_EXCLUDE:"Section is excluded",
}
STB_LOCAL = 0
STB_GLOBAL = 1
STB_WEAK = 2
STB_LOOS = 10
STB_HIOS = 12
STB_LOPROC = 13
STB_HIPROC = 15
st_info_bind = {
STB_LOCAL:"Local symbol",
STB_GLOBAL:"Global symbol",
STB_WEAK:"Weak symbol",
STB_LOOS:"Start of OS-specific",
STB_HIOS:"End of OS-specific",
STB_LOPROC:"Start of processor-specific",
STB_HIPROC:"End of processor-specific",
}
STT_NOTYPE = 0
STT_OBJECT = 1
STT_FUNC = 2
STT_SECTION = 3
STT_FILE = 4
STT_COMMON = 5
STT_TLS = 6
STT_LOOS = 10
STT_HIOS = 12
STT_LOPROC = 13
STT_HIPROC = 15
st_info_type = {
STT_NOTYPE:"Symbol type is unspecified",
STT_OBJECT:"Symbol is a data object",
STT_FUNC:"Symbol is a code object",
STT_SECTION:"Symbol associated with a section",
STT_FILE:"Symbol's name is file name",
STT_COMMON:"Symbol is a common data object",
STT_TLS:"Symbol is thread-local data",
STT_LOOS:"Start of OS-specific",
STT_HIOS:"End of OS-specific",
STT_LOPROC:"Start of processor-specific",
STT_HIPROC:"End of processor-specific",
}
DT_NULL = 0
DT_NEEDED = 1
DT_PLTRELSZ = 2
DT_PLTGOT = 3
DT_HASH = 4
DT_STRTAB = 5
DT_SYMTAB = 6
DT_RELA = 7
DT_RELASZ = 8
DT_RELAENT = 9
DT_STRSZ = 10
DT_SYMENT = 11
DT_INIT = 12
DT_FINI = 13
DT_SONAME = 14
DT_RPATH = 15
DT_SYMBOLIC = 16
DT_REL = 17
DT_RELSZ = 18
DT_RELENT = 19
DT_PLTREL = 20
DT_DEBUG = 21
DT_TEXTREL = 22
DT_JMPREL = 23
DT_BIND_NOW = 24
DT_INIT_ARRAY = 25
DT_FINI_ARRAY = 26
DT_INIT_ARRAYSZ = 27
DT_FINI_ARRAYSZ = 28
DT_RUNPATH = 29
DT_FLAGS = 30
DT_ENCODING = 32
DT_PREINIT_ARRAY = 32
DT_PREINIT_ARRAYSZ = 33
DT_NUM = 34
DT_LOOS = 0x6000000d
DT_HIOS = 0x6ffff000
DT_LOPROC = 0x70000000
DT_HIPROC = 0x7fffffff
#DT_PROCNUM = DT_MIPS_NUM
dt_types = {
DT_NULL : "Marks end of dynamic section ",
DT_NEEDED : "Name of needed library ",
DT_PLTRELSZ : "Size in bytes of PLT relocs ",
DT_PLTGOT : "Processor defined value ",
DT_HASH : "Address of symbol hash table ",
DT_STRTAB : "Address of string table ",
DT_SYMTAB : "Address of symbol table ",
DT_RELA : "Address of Rela relocs ",
DT_RELASZ : "Total size of Rela relocs ",
DT_RELAENT : "Size of one Rela reloc ",
DT_STRSZ : "Size of string table ",
DT_SYMENT : "Size of one symbol table entry ",
DT_INIT : "Address of init function ",
DT_FINI : "Address of termination function ",
DT_SONAME : "Name of shared object ",
DT_RPATH : "Library search path (deprecated) ",
DT_SYMBOLIC : "Start symbol search here ",
DT_REL : "Address of Rel relocs ",
DT_RELSZ : "Total size of Rel relocs ",
DT_RELENT : "Size of one Rel reloc ",
DT_PLTREL : "Type of reloc in PLT ",
DT_DEBUG : "For debugging; unspecified ",
DT_TEXTREL : "Reloc might modify .text ",
DT_JMPREL : "Address of PLT relocs ",
DT_BIND_NOW : "Process relocations of object ",
DT_INIT_ARRAY : "Array with addresses of init fct ",
DT_FINI_ARRAY : "Array with addresses of fini fct ",
DT_INIT_ARRAYSZ : "Size in bytes of DT_INIT_ARRAY ",
DT_FINI_ARRAYSZ : "Size in bytes of DT_FINI_ARRAY ",
DT_RUNPATH : "Library search path ",
DT_FLAGS : "Flags for the object being loaded ",
DT_ENCODING : "Start of encoded range ",
DT_PREINIT_ARRAY : "Array with addresses of preinit fct",
DT_PREINIT_ARRAYSZ : "size in bytes of DT_PREINIT_ARRAY ",
DT_NUM : "Number used ",
DT_LOOS : "Start of OS-specific ",
DT_HIOS : "End of OS-specific ",
DT_LOPROC : "Start of processor-specific ",
DT_HIPROC : "End of processor-specific ",
#DT_PROCNUM : "Most used by any processor ",
}
PT_NULL = 0
PT_LOAD = 1
PT_DYNAMIC = 2
PT_INTERP = 3
PT_NOTE = 4
PT_SHLIB = 5
PT_PHDR = 6
PT_TLS = 7
PT_NUM = 8
PT_LOOS = 0x60000000
PT_GNU_EH_FRAME = 0x6474e550
PT_GNU_STACK = 0x6474e551
PT_GNU_RELRO = 0x6474e552
PT_LOSUNW = 0x6ffffffa
PT_SUNWBSS = 0x6ffffffa
PT_SUNWSTACK = 0x6ffffffb
PT_HISUNW = 0x6fffffff
PT_HIOS = 0x6fffffff
PT_LOPROC = 0x70000000
PT_HIPROC = 0x7fffffff
ph_types = {
PT_NULL:"Program header table entry unused",
PT_LOAD:"Loadable program segment",
PT_DYNAMIC:"Dynamic linking information",
PT_INTERP:"Program interpreter",
PT_NOTE:"Auxiliary information",
PT_SHLIB:"Reserved",
PT_PHDR:"Entry for header table itself",
PT_TLS:"Thread-local storage segment",
PT_NUM:"Number of defined types",
PT_LOOS:"Start of OS-specific",
PT_GNU_EH_FRAME:"GCC .eh_frame_hdr segment",
PT_GNU_STACK:"Indicates stack executability",
PT_GNU_RELRO:"Read-only after relocation",
PT_SUNWBSS:"Sun Specific segment",
PT_SUNWSTACK:"Stack segment",
PT_HIOS:"End of OS-specific",
PT_LOPROC:"Start of processor-specific",
PT_HIPROC:"End of processor-specific"}
Elf/__init__.py 0000644 0001750 0001750 00000067525 11524233714 012672 0 ustar ender ender """
Kenshoto's Elf parser
This package will let you use programatic ninja-fu
when trying to parse Elf binaries. The API is based
around several objects representing constructs in the
Elf binary format. The Elf object itself contains
parsed metadata and lists of things like section headers
and relocation entries. Additionally, most of the
objects implement repr() in some form or another which
allows you a bunch of readelf-like functionality.
*Eventually* this API will allow you to modify Elf binaries
and spit them back out in working order (not complete, you
may notice some of the initial code).
Send bug reports to Invisigoth or Metr0.
"""
# Copyright (C) 2007 Invisigoth - See LICENSE file for details
import os
import sys
import struct
import traceback
import zlib
from stat import *
from Elf.elf_lookup import *
verbose = False
class Elf:
"""
An Elf object representation which allows manipulation
and parsing of Elf executables. Brought to you by
kenshoto.
"""
def __init__(self, initstr):
"""
Constructacon: initstr can be a filename, or a big hunka Elf lovin
(If you only give it 52 bytes, it'll just parse the header, if you give it
more, it *will* assume it has the whole thing...
"""
self.sections = []
self.pheaders = []
self.secnames = {}
self.symbols = []
self.symbols_by_name = {}
self.symbols_by_addr = {}
self.e_ident = "NOTHINGHEREATALL"
self.e_type = 0
self.e_machine = 0
self.e_version = 0
self.e_entry = 0
self.e_phoff = 0
self.e_shoff = 0
self.e_flags = 0
self.e_ehsize = 0
self.e_phentsize = 0
self.e_phnum = 0
self.e_shentsize = 0
self.e_shnum = 0
self.e_shstrndx = 0
self.fmt = "2HI3LI6H"
self.hdrlen = struct.calcsize(self.fmt) + 16
self.myname = "unknown"
bytes = initstr
pbase = self.hdrlen
sbase = self.hdrlen
if len(initstr) > 0:
if not '\000' in initstr and os.path.exists(initstr):
bytes = file(initstr, "rb").read()
self.myname = initstr
self.initFromBytes(bytes)
# If we only got the 52 bytes, we have
# no symbols to parse etc...
if len(bytes) == self.hdrlen:
return
if self.e_shoff < self.e_phoff:
raise Exception("ERROR: we only support format now")
# Load up any program headers we find
if self.e_phoff:
pbase = self.e_phoff
plen = self.e_phentsize
for i in range(self.e_phnum):
if self.bits == 32:
pgm = Elf32Pheader(bytes[pbase:pbase+plen],elf=self)
else:
pgm = Elf64Pheader(bytes[pbase:pbase+plen],elf=self)
self.pheaders.append(pgm)
pbase += plen
# Load up all the section headers
if self.e_shoff:
# Load up the sections
sbase = self.e_shoff
# FIXME this assumes static sized section headers
slen = self.e_shentsize
for i in range(self.e_shnum):
if self.bits == 32:
sec = Elf32Section(bytes[sbase:sbase+slen],elf=self)
else:
sec = Elf64Section(bytes[sbase:sbase+slen],elf=self)
self.sections.append(sec)
sbase += slen
# Populate the section names
strsec = self.sections[self.e_shstrndx]
names = bytes[strsec.sh_offset:strsec.sh_offset+strsec.sh_size]
for sec in self.sections:
name = names[sec.sh_name:].split("\x00")[0]
if len(name) > 0:
sec.setName(name)
self.secnames[name] = sec
self.parseSymbols()
self.parseDynamic()
self.parseRelocs()
def getName(self):
return self.myname
def __str__(self):
""" Calls toString() to obtain a string summary of this ELF. Since no additional parameters make sense, default verbosity for the module is used
"""
return self.toString(verbose)
def toString(self, verbose=False):
""" Returns a string summary of this ELF. If (verbose) the summary will include Symbols, Relocs, Dynamics and Dynamic Symbol tables"""
mystr = "ELF HEADER OBJECT:" + self.myname
mystr+= "\n= Intimate Details:"
mystr+= "\n==Magic:\t\t\t\t" + self.e_ident
mystr+= "\n==Type:\t\t\t\t\t" + e_types.get(self.e_type)
mystr+= "\n==Machine Arch:\t\t\t\t" + e_machine_types.get(self.e_machine)
mystr+= "\n==Version:\t\t\t\t%d" % (self.e_version)
mystr+= "\n==Entry:\t\t\t\t0x%.8x" % (self.e_entry)
mystr+= "\n==Program Headers(offset):\t\t%d (0x%x) bytes" % (self.e_phoff, self.e_phoff)
mystr+= "\n==Section Headers(offset):\t\t%d (0x%x) bytes" % (self.e_shoff, self.e_shoff)
mystr+= "\n==Flags:\t\t\t\t" + repr(self.e_flags) + " "
mystr+= "\n==Elf Header Size:\t\t\t" + repr(self.e_ehsize) + " (" + hex(self.e_ehsize) + " bytes)"
mystr+= "\n==Program Header Size:\t\t\t" + repr(self.e_phentsize) + " (" + hex(self.e_phentsize) + " bytes)"
mystr+= "\n==Program Header Count:\t\t\t" + repr(self.e_phnum) + " (" + hex(self.e_phnum)+ ")"
mystr+= "\n==Section Header Size:\t\t\t" + repr(self.e_shentsize) + " (" + hex(self.e_shentsize) + " bytes)"
mystr+= "\n==Section Header Count:\t\t\t" + repr(self.e_shnum) + " (" + hex(self.e_shnum) + ")"
mystr+= "\n==Section Header String Index\t\t" + repr(self.e_shstrndx) + " (" + hex(self.e_shstrndx) + " bytes)"
mystr+= "\n\n= Sections:"
for sec in self.sections:
mystr+= "\n"+repr(sec)
mystr+= "\n\n= Program Headers:"
for ph in self.pheaders:
mystr+= "\n"+repr(ph)
if (verbose):
mystr+= "\n\n= Symbols table:"
for sym in self.symbols:
mystr+= "\n"+repr(sym)
mystr+= "\n\n= Relocation table:"
for reloc in self.relocs:
mystr+= "\n"+repr(reloc)
mystr+= "\n\n= Dynamics table:"
for dyn in self.dynamics:
mystr+= "\n"+repr(dyn)
mystr+= "\n\n= Dynamic Symbols table:"
for dyn in self.dynamic_symbols:
mystr+= "\n"+repr(dyn)
return mystr
def getStrtabString(self, offset, section=".strtab"):
bytes = self.getSection(section).getBytes()
index = bytes.find("\x00", offset)
return bytes[offset:index]
def initFromBytes(self, bytes):
if len(bytes) < self.hdrlen:
raise Exception("Elf format error: Not even a full Elf header (%d bytes)", self.hdrlen)
if bytes[:4] <> "\x7fELF":
raise Exception("Elf format error: Elf magic not found")
self.e_ident = bytes[:16]
(self.e_type,
self.e_machine,
self.e_version,
self.e_entry,
self.e_phoff,
self.e_shoff,
self.e_flags,
self.e_ehsize,
self.e_phentsize,
self.e_phnum,
self.e_shentsize,
self.e_shnum,
self.e_shstrndx) = struct.unpack(self.fmt, bytes[16:self.hdrlen])
if self.e_machine in e_machine_32:
self.bits = 32
elif self.e_machine in e_machine_64:
self.bits = 64
else:
raise Exception("ERROR - Unknown 32/64 bit e_machine: %d. Add to e_machine_XX" % self.e_machine)
self.data = bytes
def buildHeader(self):
"""
Return the byte representation for *just* the elf header
for this elf.
"""
hdr = struct.pack(self.fmt,
self.e_type,
self.e_machine,
self.e_version,
self.e_entry,
self.e_phoff,
self.e_shoff,
self.e_flags,
self.e_ehsize,
self.e_phentsize,
self.e_phnum,
self.e_shentsize,
self.e_shnum,
self.e_shstrndx)
return self.e_ident + hdr
def serialize(self, filename=None):
"""
If filename is specified, serialize this elf object to the specified
file, otherwise return the bytes (read string) for this elf object
"""
# Get the Elf header
buf = self.buildHeader()
# Get the program headers
#FIXME assumes order
for pgm in self.pheaders:
buf += pgm.serialize()
phlen = self.e_phentsize * self.e_phnum
# Append the actual file data
buf += self.data[self.e_ehsize+phlen:self.e_shoff]
# Append the section headers
for sec in self.sections:
buf += sec.serialize()
if filename:
f = file(filename,'wb')
f.write(buf)
f.close()
return
return buf
def lookupSymbolName(self, name):
"""
Lookup symbol entries in this elf binary by name. The result is
a long representing the address for the given symbol. Or None if
it's not found.
"""
return self.symbols_by_name.get(name, None)
def lookupSymbolAddr(self, address):
"""
lookup symbols from this elf binary by address.
This returns the name for the given symbol or None for not found
"""
return self.symbols_by_addr.get(address, None)
def getBytes(self, offset, size, file_offset=True):
"""
Modification to the bytes this returns will NOT
be saved to the file bytes.
"""
return self.data[offset:offset+size]
def insertBytes(self, offset, bytes,section=None,file_offset=True):
"""
Insert the bytes argument at offset in the data.
The default will insert the bytes at that offset
from the beginning of the file (to ease calculations
that are based on header values). The caller may optionally
specify file_offset=False to have the offset be from
the beginning of self.data. If the inserted data falls
directly on a section boundary,
The optional "section" argument specifies which section
you would like to "own" the data (aka. which one gets his
length updated. If none, the bytes will push other data down
essentially into padding between sections...
THIS CODE DOES NOT WORK YET!
"""
ilen = len(bytes)
if section:
if ( offset < section.sh_offset or
offset > (section.sh_offset+section.sh_size)):
raise Exception("ERROR - Specified section in insertBytes has wrong offset/size: offset: %d" % offset)
section.sh_size += ilen
if file_offset:
offset -= self.getDataOffset()
self.data = self.data[:offset] + bytes + self.data[offset:]
#FIXME deal with program headers...
#for pgm in self.pheaders:
for sec in self.sections:
if offset <= (sec.sh_offset-self.getDataOffset()):
sec.sh_offset += ilen
if sec.sh_offset % sec.sh_addralign:
align = sec.sh_addralign - (sec.sh_offset % sec.sh_addralign)
off = sec.sh_offset - self.getDataOffset()
# Insert the pad bytes if this insert messes up alignment
self.data = self.data[:off] + "\x00" * align + self.data[off:]
sec.sh_offset += align
ilen += align
if offset < self.e_shoff:
self.e_shoff += ilen
print "INSERTED: ",ilen," bytes"
def getDataOffset(self):
return self.hdrlen + (self.e_phentsize * self.e_phnum)
def modifyBytes(self, offset, bytes, file_offset=True):
"""
Arguments are the same as insertBytes() except that
this method will OVERWRITE the bytes at that location
(which shouldn't cause any recalculation)
"""
blen = len(bytes)
if file_offset:
offset -= self.getDataOffset()
self.data = self.data[:offset] + bytes + self.data[offset+blen:]
def appendSection(self, section, name=None):
"""
Append the section to the Elf. The optional
name will be put into the shstrtab...
"""
strtab = self.getSection(".shstrtab")
if not strtab and name:
raise Exception("ERROR - .shstrtab not found (and name specified)")
if name:
section.sh_name = strtab.sh_size
self.insertBytes(strtab.sh_offset+strtab.sh_size, name+"\x00", strtab)
self.secnames[name] = section
section.elf = self
self.sections.append(section)
self.e_shnum += 1
print repr(strtab.getBytes())
def getSection(self, secname):
return self.secnames.get(secname,None)
def getSections(self):
"""
Return the array of sections for this Elf
"""
return list(self.sections)
def getPheaders(self):
"""
Return a list of the program headers for this elf
"""
return list(self.pheaders)
def addSymbol(self, symbol):
self.symbols.append(symbol)
self.symbols_by_name[symbol.getName()] = symbol
self.symbols_by_addr[symbol.st_value] = symbol
def getSymbols(self):
return self.symbols
def parseSymbols(self):
"""
Parse out the symbols that this elf binary has for us.
"""
for sec in self.sections:
if sec.sh_type == SHT_SYMTAB:
symtab = sec.getBytes()
while symtab:
if self.bits == 32:
newsym = Elf32Symbol(symtab)
else:
newsym = Elf64Symbol(symtab)
#FIXME this is dorked!
if newsym.st_name:
name = self.getStrtabString(newsym.st_name, ".strtab")
newsym.setName(name)
self.addSymbol(newsym)
symtab = symtab[len(newsym):]
def parseRelocs(self):
"""
Parse all the relocation entries out of any sections with
sh_type == SHT_REL
"""
self.relocs = []
for sec in self.sections:
if sec.sh_type == SHT_REL:
bytes = sec.getBytes()
while bytes:
if self.bits == 32:
reloc = Elf32Reloc(bytes)
else:
reloc = Elf64Reloc(bytes)
index = reloc.getSymTabIndex()
try:
sym = self.dynamic_symbols[index]
reloc.setName(sym.getName())
except:
traceback.print_exc()
self.relocs.append(reloc)
bytes = bytes[len(reloc):]
elif sec.sh_type == SHT_RELA:
bytes = sec.getBytes()
while bytes:
if self.bits == 32:
reloc = Elf32Reloca(bytes)
else:
print "WARNING: 64bits ELF programs aren't supported yet"
return
index = reloc.getSymTabIndex()
try:
sym = self.dynamic_symbols[index]
reloc.setName(sym.getName())
except:
traceback.print_exc()
self.relocs.append(reloc)
bytes = bytes[len(reloc):]
def parseDynamic(self):
self.dynamic_symbols = []
self.dynamics = []
sec = self.getSection(".dynsym")
if not sec:
return
symtab = sec.getBytes()
while symtab:
if self.bits == 32:
newsym = Elf32Symbol(symtab)
else:
newsym = Elf64Symbol(symtab)
if newsym.st_name:
name = self.getStrtabString(newsym.st_name, ".dynstr")
newsym.setName(name)
self.dynamic_symbols.append(newsym)
symtab = symtab[len(newsym):]
dynsec = self.getSection(".dynamic")
dynbytes = dynsec.getBytes()
while dynbytes:
if self.bits == 32:
dyn = Elf32Dynamic(dynbytes)
else:
dyn = Elf64Dynamic(dynbytes)
if dyn.d_tag in Elf32Dynamic.has_string:
name = self.getStrtabString(dyn.d_value, ".dynstr")
dyn.setName(name)
self.dynamics.append(dyn)
if dyn.d_tag == DT_NULL: # Represents the end
break
dynbytes = dynbytes[len(dyn):]
def getDynamics(self):
return self.dynamics
def getDynSyms(self):
return self.dynamic_symbols
def getRelocs(self):
return self.relocs
class Elf32Dynamic:
has_string = [DT_NEEDED,DT_SONAME]
"""
An object to represent an Elf dynamic entry.
(linker/loader directives)
"""
def __init__(self, bytes=None):
self.name = ""
self.d_tag = 0
self.d_value = 0
if bytes:
self.initFromBytes(bytes)
def __repr__(self):
name = self.getName()
if not name:
name = hex(self.d_value)
return "%s %s" % (name,self.getTypeName())
def initFromBytes(self, bytes):
self.d_tag,self.d_value = struct.unpack("2L", bytes[:len(self)])
def getName(self):
return self.name
def setName(self, name):
self.name = name
def getTypeName(self):
return dt_types.get(self.d_tag,"Unknown: %s"%hex(self.d_tag))
def __len__(self):
return struct.calcsize("2L")
class Elf64Dynamic(Elf32Dynamic):
pass
class Elf32Reloc:
"""
Elf relocation entries consist mostly of "fixup" address which
are taken care of by the loader at runtime. Things like
GOT entries, PLT jmp codes etc all have an Elf relocation
entry.
"""
def __init__(self, bytes=None):
self.name = ""
self.r_offset = 0
self.r_info = 0
if bytes:
self.initFromBytes(bytes)
def __repr__(self):
return "%s %s <%s>" % (hex(self.r_offset),self.getName(),self.getTypeName())
def initFromBytes(self,bytes):
(self.r_offset, self.r_info) = struct.unpack("2L",bytes[:len(self)])
def setName(self, name):
self.name = name
def getName(self):
return self.name
def getType(self):
return self.r_info & 0xff
def getSymTabIndex(self):
return self.r_info >> 8
def getTypeName(self):
return r_types_386.get(self.getType(),"")
def __len__(self):
return struct.calcsize("2L")
class Elf32Reloca(Elf32Reloc):
def __init__(self, bytes=None):
self.r_addend = 0
Elf32Reloc.__init__(self, bytes)
def initFromBytes(self, bytes):
(self.r_offset, self.r_info, self.r_addend) = struct.unpack("3L", bytes[:len(self)])
def __len__(self):
return struct.calcsize("3L")
class Elf64Reloc(Elf32Reloc):
pass
class Elf32Symbol:
"""
An object which represents an Elf Symbol. It has the
following attributes (which are created/parsed by init:
st_name
st_value
st_size
st_info
st_other
st_shndx
"""
def __init__(self, bytes=None):
self.name = ""
self.st_name = 0
self.st_value = 0
self.st_size = 0
self.st_info = 0
self.st_other = 0
self.st_shndx = 0
if bytes:
self.initFromBytes(bytes)
def getInfoType(self):
return self.st_info & 0xf
def getInfoBind(self):
return self.st_info >> 4
def __cmp__(self, other):
if self.st_value > other.st_value:
return 1
return -1
def initFromBytes(self,bytes):
(self.st_name,
self.st_value,
self.st_size,
self.st_info,
self.st_other,
self.st_shndx) = struct.unpack("3L2BH",bytes[:len(self)])
def serialize(self):
return struct.pack("3L2BH",
self.st_name,
self.st_value,
self.st_size,
self.st_info,
self.st_other,
self.st_shndx)
def setName(self,name):
self.name = name
def getName(self):
return self.name
def __repr__(self):
return "0x%.8x %d %s" % (self.st_value, self.st_size, self.name)
def __len__(self):
return struct.calcsize("3L2BH")
class Elf64Symbol(Elf32Symbol):
def initFromBytes(self,bytes):
fmt = "IBBHLL"
(self.st_name,
self.st_info,
self.st_other,
self.st_shndx,
self.st_value,
self.st_size,
) = struct.unpack(fmt,bytes[:len(self)])
def serialize(self):
return struct.pack("IBBHLL",
self.st_name,
self.st_value,
self.st_size,
self.st_info,
self.st_other,
self.st_shndx)
def __len__(self):
return struct.calcsize("IBBHLL")
class Elf32Pheader:
"""
An object to represent ELF_Phdr structures and the segments they represent
"""
def __init__(self, bytes=None, elf=None):
self.elf = elf
self.p_type = 0
self.p_offset = 0
self.p_vaddr = 0
self.p_paddr = 0
self.p_filesz = 0
self.p_memsz = 0
self.p_flags = 0
self.p_align = 0
if bytes:
self.initFromBytes(bytes)
def __repr__(self):
return "[%35s] VMA: 0x%.8x offset: %8d memsize: %8d align: %8d (filesz: %8d) flags: %x" % (
self.getTypeName(),
self.p_vaddr,
self.p_offset,
self.p_memsz,
self.p_align,
self.p_filesz,
self.p_flags)
def getTypeName(self):
return ph_types.get(self.p_type, "Unknown")
def initFromBytes(self, bytes):
(
self.p_type,
self.p_offset,
self.p_vaddr,
self.p_paddr,
self.p_filesz,
self.p_memsz,
self.p_flags,
self.p_align,
) = struct.unpack("8L",bytes[:32])
def serialize(self):
hdr = struct.pack("8L",
self.p_type,
self.p_offset,
self.p_vaddr,
self.p_paddr,
self.p_filesz,
self.p_memsz,
self.p_flags,
self.p_align)
return hdr
def __len__(self):
return struct.calcsize("8L")
class Elf64Pheader(Elf32Pheader):
def initFromBytes(self, bytes):
fmt = "2I6L"
(
self.p_type,
self.p_flags,
self.p_offset,
self.p_vaddr,
self.p_paddr,
self.p_filesz,
self.p_memsz,
self.p_align,
) = struct.unpack(fmt,bytes[:len(self)])
def serialize(self):
fmt = "2I6L"
hdr = struct.pack(fmt,
self.p_type,
self.p_flags,
self.p_offset,
self.p_vaddr,
self.p_paddr,
self.p_filesz,
self.p_memsz,
self.p_align)
return hdr
def __len__(self):
return struct.calcsize("2I6L")
class Elf32Section:
"""
An object to represent the elf sections in the Elf binary. Constructor
takes a string representing the contents of the Elf section.
self.sh_name
self.sh_type
self.sh_flags
self.sh_addr
self.sh_offset
self.sh_size
self.sh_link
self.sh_info
self.sh_addralign
self.sh_entsize
"""
def __init__(self, initbytes=None, elf=None):
self.elf = elf
self.name = ""
self.bytes = "" # The actual data section
self.sh_name = 0 # Section name index
self.sh_type = 0
self.sh_flags = 0
self.sh_addr = 0
self.sh_offset = 0
self.sh_size = 0
self.sh_link = 0
self.sh_info = 0
self.sh_addralign = 0
self.sh_entsize = 0
self.extrajunx = "" # Stuff held in extended section headers
if initbytes:
self.initFromBytes(initbytes)
def __repr__(self):
return "Elf Section: [%20s] VMA: 0x%.8x offset: %8d ent/size: %8d/%8d align: %8d" % (
self.name,
self.sh_addr,
self.sh_offset,
self.sh_entsize,
self.sh_size,
self.sh_addralign)
def getPadSize(self, offset):
"""
Calculate the pad necissary for this section
based on the file offset given as an arg
"""
ret = 0
myalign = self.sh_addralign
if myalign > 1:
mymod = offset % myalign
if mymod:
ret = myalign-mymod
return ret
def initFromBytes(self, bytes):
(
self.sh_name,
self.sh_type,
self.sh_flags,
self.sh_addr,
self.sh_offset,
self.sh_size,
self.sh_link,
self.sh_info,
self.sh_addralign,
self.sh_entsize,
) = struct.unpack("10L", bytes[:40])
if len(bytes) > 40:
self.extrajunx = bytes[40:]
def serialize(self):
hdr = struct.pack("10L",
self.sh_name,
self.sh_type,
self.sh_flags,
self.sh_addr,
self.sh_offset,
self.sh_size,
self.sh_link,
self.sh_info,
self.sh_addralign,
self.sh_entsize)
return hdr + self.extrajunx
def getBytes(self):
"""
Get the bytes described by this section. Changes
to these bytes will NOT be changed in the Elf file data!
"""
if self.elf:
if self.sh_type == SHT_NOBITS:
return "\x00" * self.sh_size
return self.elf.getBytes(self.sh_offset,self.sh_size)
else:
raise Exception("ERROR - Section.getBytes() called when section has no elf!")
def getUncompressed(self):
"""
Get the bytes described by this section. If sh_entsize != sh_size, run uncompress before returning
"""
if self.elf:
if (self.sh_entsize > 0 and self.sh_size != self.sh_entsize):
return zlib.decompress(self.elf.getBytes(self.sh_offset,self.sh_size))
return self.elf.getBytes(self.sh_offset,self.sh_size)
else:
raise Exception("ERROR - Section.getBytes() called when section has no elf!")
def setName(self, name):
"""
The name of a section is not going to be known until
the sections have been parsed (to know which one is the
strtab)
"""
self.name = name
def getName(self):
return self.name
def __len__(self):
return struct.calcsize("10L")
class Elf64Section(Elf32Section):
"""
Elf binary section on 64 bit platforms
"""
def initFromBytes(self, bytes):
fmt = "2I4L2I2L"
(
self.sh_name,
self.sh_type,
self.sh_flags,
self.sh_addr,
self.sh_offset,
self.sh_size,
self.sh_link,
self.sh_info,
self.sh_addralign,
self.sh_entsize,
) = struct.unpack(fmt, bytes[:len(self)])
if len(bytes) > len(self):
self.extrajunx = bytes[len(self):]
def serialize(self):
fmt = "2I4L2I2L"
hdr = struct.pack(fmt,
self.sh_name,
self.sh_type,
self.sh_flags,
self.sh_addr,
self.sh_offset,
self.sh_size,
self.sh_link,
self.sh_info,
self.sh_addralign,
self.sh_entsize)
return hdr + self.extrajunx
def __len__(self):
return struct.calcsize("2I4L2I2L")
def getRelocType(val):
return val & 0xff
def getRelocSymTabIndex(val):
return val >> 8
gcluster.py 0000755 0001750 0001750 00000025003 11524233714 012241 0 ustar ender ender #!/usr/bin/env python
"""
A program's clusterization tool based on Pyew
Copyright (C) 2010, Joxean Koret
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
"""
import os, sys
from hashlib import sha256
from pyew_core import CPyew
def primes(n):
if n==2: return [2]
elif n<2: return []
s=range(3,n+1,2)
mroot = n ** 0.5
half=(n+1)/2-1
i=0
m=3
while m <= mroot:
if s[i]:
j=(m*m-3)/2
s[j]=0
while j 0:
addr = functions.pop()
f = pyew.functions[addr]
for c in f.connections:
if c in pyew.functions and c not in dones:
functions.append(c)
dones.append(c)
al.append((pyew.function_stats[addr], pyew.function_stats[c]))
return al
def getSimilarity(self, s1, s2):
m = max(len(s1), len(s2))
diff1 = len(s1.difference(s2))
diff2 = len(s2.difference(s1))
diff = (diff1 + diff2)*100./m
simil1 = len(s1.intersection(s2))
simil = simil1*100. / m
metric = simil + diff
diff = diff * 100. / metric
return diff
def compareTwoSets(self, set1, set2):
pyew1 = set1.values()[0]
pyew2 = set2.values()[0]
al1 = self.createAdjacencyList(pyew1)
al2 = self.createAdjacencyList(pyew2)
if al1 == al2:
return 0
else:
s1 = set(al1)
s2 = set(al2)
diff = len(s1.difference(s2)) + len(s2.difference(s1))
total = max(len(s1), len(s2))
simil = diff * 100. / total
return simil
def cluster(self):
if len(self.data) == 2:
set1 = self.data[0]
set2 = self.data[1]
return self.compareTwoSets(set1, set2)
class CPrimesCluster(object):
def __init__(self, data):
self.primes = primes(1024*1024)
self.data = data
def generateHash(self, pyew):
val = 1.
dones = []
primes_done = []
for f in pyew.functions:
nodes, edges, cc = pyew.function_stats[f]
if cc > 1 and (nodes, edges, cc) not in dones:
p = self.primes[cc]
if p not in primes_done:
val *= p
primes_done.append(p)
dones.append((nodes, edges, cc))
return val, dones
def compareManySets(self, sets):
files = {}
primes = {}
values = {}
print "File1;File2;Difference"
for s in sets:
pyew = s.values()[0]
val, prime = self.generateHash(pyew)
hash = sha256(pyew.getBuffer()).hexdigest()
primes[hash] = prime
values[hash] = val
files[hash] = pyew.filename
del pyew
dones = []
size = len(primes)
for h1 in values:
for h2 in values:
if h1 == h2 or (h1, h2) in dones or (h2, h1) in dones:
continue
if values[h1] == values[h2]:
print "%s;%s;0" % (files[h1], files[h2])
dones.append((h1, h2))
dones.append((h2, h1))
else:
dones.append((h1, h2))
dones.append((h2, h1))
s1 = set(primes[h1])
s2 = set(primes[h2])
diff = self.getSimilarity(s1, s2)
print "%s;%s;%f" % (files[h1], files[h2], diff)
def getSimilarity(self, s1, s2):
m = max(len(s1), len(s2))
diff1 = len(s1.difference(s2))
diff2 = len(s2.difference(s1))
diff = (diff1 + diff2)*100./m
simil1 = len(s1.intersection(s2))
simil = simil1*100. / m
metric = simil + diff
diff = diff * 100. / metric
return diff
def compareTwoSets(self, set1, set2):
pyew1 = set1.values()[0]
val1, primes1 = self.generateHash(pyew1)
pyew2 = set2.values()[0]
val2, primes2 = self.generateHash(pyew2)
s1 = set(primes1)
s2 = set(primes2)
if val1 == val2:
return 0
else:
diff = self.getSimilarity(s1, s2)
return diff
def cluster(self):
if len(self.data) == 2:
set1 = self.data[0]
set2 = self.data[1]
return self.compareTwoSets(set1, set2)
else:
return self.compareManySets(self.data)
class CExpertCluster(object):
def __init__(self, data):
self.data = data
def compareTwoSets(self, set1, set2):
# Get the ciclomatic complexity statistical data of the 2 samples
ccs1 = set1.values()[0].program_stats["ccs"]
ccs2 = set2.values()[0].program_stats["ccs"]
avg_cc_distance = abs(ccs1["avg"] - ccs2["avg"])
max_cc_distance = abs(ccs1["max"] - ccs2["max"])
min_cc_distance = abs(ccs1["min"] - ccs2["min"])
total_functions = abs(len(set1.values()[0].functions) - len(set2.values()[0].functions))
difference = avg_cc_distance*0.5 + \
max_cc_distance*0.3 + \
min_cc_distance*0.1 + \
total_functions*0.1
return difference
def cluster(self):
set1 = self.data[0]
set2 = self.data[1]
return self.compareTwoSets(set1, set2)
class CGraphCluster(object):
def __init__(self):
self.clear()
self.deep = True
self.timeout = 0
def addFile(self, filename):
self.files.append(filename)
def clear(self):
self.files = []
self.results = []
self.data = []
def processFile(self, filename):
#print "[+] Analyzing file %s" % filename
pyew = CPyew(batch=True)
pyew.deepcodeanalysis = self.deep
pyew.analysis_timeout = 0
pyew.loadFile(filename)
if pyew.format in ["PE", "ELF"]:
hash = sha256(pyew.getBuffer()).hexdigest()
self.data.append({hash:pyew})
else:
sys.stderr.writelines("Not a PE/ELF file")
sys.stderr.flush()
def comparePrimes(self):
cluster = CPrimesCluster(self.data)
val = cluster.cluster()
if val == 0:
print "Primes system: Programs are 100% equals"
else:
print "Primes system: Programs differs in", val, "% percent"
def compareAdjacencyLists(self):
cluster = CAdjacencyList(self.data)
val = cluster.cluster()
if val == 0:
print "ALists system: Programs are 100% equals"
else:
print "ALists System: Programs differs in %f%%" % val
def compareExpert(self):
cluster = CExpertCluster(self.data)
val = cluster.cluster()
if val == 0:
print "Expert system: Programs are 100% equals"
else:
print "Expert system: Programs differs in %f%s" % (round(val, 1), "%")
return val
def processFiles(self):
for f in self.files:
self.processFile(f)
def main(prog1, prog2):
cluster = CGraphCluster()
cluster.addFile(prog1)
cluster.addFile(prog2)
cluster.processFiles()
cluster.compareExpert()
cluster.comparePrimes()
cluster.compareAdjacencyLists()
def compareDirectory(path):
cluster = CGraphCluster()
cprimes = CPrimesCluster([])
alist = CAdjacencyList([])
if os.path.isdir(path):
for root, dirs, files in os.walk(path, topdown=False):
for name in files:
fname = os.path.join(root, name)
cluster.addFile(fname)
else:
cluster.addFile(path)
cluster.processFiles()
print "hash:filename:primes_hash:nodes_total:nodes_max:nodes_avg:nodes_min:edges_total:edges_max:edges_avg:edges_min:ccs_total:ccs_max:ccs_avg:ccs_min:functions:adjacency_list"
for x in cluster.data:
hash = x.keys()[0]
pyew = x.values()[0]
data = ""
for stat in pyew.program_stats:
data = data + ":".join(map(str, pyew.program_stats[stat].values())).replace(".", ",") + ":"
phash, dones = cprimes.generateHash(pyew)
print "%s:%s:%s:%s%d:%s" % (hash, pyew.f.name, str(phash.as_integer_ratio()[0]), data, len(pyew.functions), str(alist.adjacency_lists(pyew)))
def usage():
print "Usage:", sys.argv[0], " | "
print
print "When comparing 2 binaries the difference between them is printed out."
print "When comparing a directory, a csv file with all the relevant data is printed out."
print
print "Examples:"
print "%s /bin/ls /bin/cp" % sys.argv[0]
print "%s /bin" % sys.argv[0]
print
if __name__ == "__main__":
if len(sys.argv) == 1:
usage()
elif len(sys.argv) == 3:
main(sys.argv[1], sys.argv[2])
else:
compareDirectory(sys.argv[1])
LICENSE 0000644 0001750 0001750 00000043103 11524233714 011042 0 ustar ender ender GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
logo/pyew.jpg 0000644 0001750 0001750 00000005475 11524233714 012475 0 ustar ender ender JFIF H H C
C
K K 5 !1A"Qa 2Bq#$3Rb 1 !1AQa"q2B ? U(DJ"QD%(DJ"QbrDVorX6ZPuloZI[ms u^og$_I: U⻆IՋS
%uQ:?ڲWrZP#UZʊQ*\M][h