#! /usr/bin/env python2.7
# -*- coding: utf8 -*-
#
# Copyright (C) 2015 Evol Online
# Author: Andrei Karas (4144)
import os
import re
serverSrcPath = "../../server-code/src/"
pluginSrcPath = "../../server-plugin/src/"
filtC = re.compile(".+[.]c", re.IGNORECASE)
filtH = re.compile(".+[.]h", re.IGNORECASE)
comaSplit = re.compile(",")
class Session:
pass
class Status:
exitCode = 0
def walkFiles(parentDir, session, filt):
files = os.listdir(parentDir)
for file1 in files:
if file1[0] == ".":
continue
file2 = os.path.abspath(parentDir + os.path.sep + file1)
if not os.path.isfile(file2):
walkFiles(file2, session, filt)
elif filt.search(file1):
session.func(file2, session)
def extractPlugFuncions(path, session):
with open(path, "r") as f:
for line in f:
for s in session.funcList:
idx = line.find(" " + s)
if idx < 0:
idx = line.find("*" + s)
if idx > 0 and line.find("__attribute__((nonnull") < 0:
if len(line) < idx + len(s) + 1:
continue
ch = line[idx + len(s) + 1]
if ch != '(' and ch != ' ' and ch != '\t':
continue
data = line
if data.find(");") < 0:
for line in f:
data = data + line
if data.find(");") > 1:
session.decls[s] = data
break
else:
session.decls[s] = data
def extractFuncFuncions(path, session):
with open(path, "r") as f:
for line in f:
for s in session.funcList:
idx = line.find(" " + s)
if idx < 0:
idx = line.find("*" + s)
if idx > 0 and line.find("__attribute__((nonnull") < 0:
if len(line) < idx + len(s) + 1:
continue
ch = line[idx + len(s) + 1]
if ch != '(' and ch != ' ' and ch != '\t':
continue
data = line
line2 = line.strip()
if len(line2) > 2 and line2[:2] == "//":
continue
if data.find(")") < 0:
for line in f:
data = data + line
if data.find(")") > 1:
session.decls[s] = data
# print "'{0}'".format(data)
break
else:
session.decls[s] = data
# print "'{0}'".format(data)
def loadHookedList(name):
funcToPlug = dict()
plugToFunc = dict()
with open(name, "r") as f:
for line in f:
idx = line.find("addHook")
if idx > 0:
idx = line.find(",")
idx2 = line.find(")")
plug = line[idx + 1 : idx2].strip()
func = plug[1:]
if func[-5:] == "_post":
func = func[:-5]
if func[-4:] == "_pre":
func = func[:-4]
if func not in funcToPlug:
funcToPlug[func] = set()
funcToPlug[func].add(plug);
if plug not in plugToFunc:
plugToFunc[plug] = set()
plugToFunc[plug].add(func);
return (funcToPlug, plugToFunc)
def checkMissingFunctions(session):
for f in session.funcList:
if f not in session.decls:
# ignore false positive
if f != "login_parse_ping":
print "Missing function {0} and {1}".format(f, session.funcList[f])
Status.exitCode = 1
def decodeTypes(funcs):
arr = dict()
for f in funcs:
#print "{0}: {1}".format(f, funcs[f])
fstr = funcs[f]
# idx = fstr.find("(")
fstr = fstr[fstr.find("(") + 1 : fstr.rfind(")")]
parts = comaSplit.split(fstr)
lst = []
cnt = 0
if funcs[f][:5] == "void " and f[-5:] == "_post":
lst.append(("-", "-"))
cnt = cnt + 1
for part in parts:
part = part.strip()
idx1 = part.rfind(" ")
idx2 = part.rfind("*")
if idx2 > idx1:
idx1 = idx2
if idx1 < 0:
old = part
else:
# skip retVal first parameters
if cnt == 0 and part[idx1 + 1:].lower() == "retval" and f[-5:] == "_post":
lst.append(("-", "-"))
continue
old = part[:idx1 + 1]
old = old.replace("\r", "")
old = old.replace("\n", "")
part = old.replace(" ", "")
lst.append((old, part))
cnt = cnt + 1
#print "'{0}' -> '{1}'".format(old, part)
arr[f] = lst
return arr
def fixParam(param):
if param[-6:] == "*const":
param = param[:-5]
if param == "structmap_session_data*":
param = "TBL_PC*"
if param == "structflooritem_data*":
param = "TBL_ITEM*"
return param
def compareFuncs(session):
for func in session.plugDecls:
pfunc = session.plugDecls[func]
for func2 in session.plugToFunc[func]:
if func2 not in session.funcDecls:
if func2 != "login_parse_ping":
print "Error: function {0} not found".format(func2)
Status.exitCode = 1
continue
ffunc = session.funcDecls[func2]
# print "{0} - {1}".format(func, func2)
# print pfunc
# print ffunc
if func[-5:] == "_post":
if pfunc[0][1] != "-":
print "Error: missing first retVal parameter in function {0}.".format(func)
Status.exitCode = 1
else:
pfunc = pfunc[1:]
sz1 = len(pfunc)
sz2 = len(ffunc)
# print "{0} - {1}".format(sz1, sz2)
if sz1 != sz2:
print "Error: wrong number of parameters in function {0}.".format(func)
Status.exitCode = 1
continue
for idx in range(0, sz1):
pf = pfunc[idx][1]
ff = ffunc[idx][1]
pf = fixParam(pf)
ff = fixParam(ff)
if ff[-1] != "*" and ff != "void" and ff != "va_list":
ff = ff + "*"
# print pf
# print ff
if pf != ff:
print "Error: wrong parameters in function {0}.".format(func)
Status.exitCode = 1
## print "{0} vs {1}".format(pf, ff);
break
def processServer(name1, name2):
(funcToPlug, plugToFunc) = loadHookedList(pluginSrcPath + name2 + "/init.c")
session = Session()
session.func = extractPlugFuncions
session.funcList = plugToFunc
session.altList = funcToPlug
session.funcToPlug = funcToPlug
session.plugToFunc = plugToFunc
session.decls = dict()
walkFiles(pluginSrcPath + name2, session, filtH)
checkMissingFunctions(session)
session.plugDecls = session.decls
session.plugDecls = decodeTypes(session.plugDecls)
#print session.plugDecls
# pluginfunctions = session.decls
session.func = extractFuncFuncions
session.funcList = funcToPlug
session.altList = plugToFunc
session.decls = dict()
walkFiles(serverSrcPath + name1, session, filtC)
checkMissingFunctions(session)
session.funcDecls = session.decls
session.funcDecls = decodeTypes(session.funcDecls)
#print session.funcDecls
compareFuncs(session)
processServer("login", "elogin")
processServer("char", "echar")
processServer("map", "emap")
exit(Status.exitCode)