Advertisement
Guest User

Untitled

a guest
Mar 24th, 2013
101
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.76 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # -*- coding: ISO-8859-1 -*-
  3.  
  4. '''
  5. sku wrote this program. As long as you retain this notice you
  6. can do whatever you want with this stuff. If we meet some day, and you think
  7. this stuff is worth it, you can buy me a beer in return.
  8. '''
  9.  
  10.  
  11. from os import system as OMGDONT
  12. from ItemList import getItem, getItemName
  13. from NotifyItems import shouldNotify
  14. from ByteBuffer import ByteBuffer
  15. import ctypes
  16. import sys
  17. import threading
  18. import winsound
  19. import atexit
  20. import datetime
  21. import traceback
  22.  
  23. try:
  24.     from pydbg import *
  25.     from pydbg.defines import *
  26. except:
  27.     print 'You seem to be missing pydbg or pydasm.'
  28.     print 'Precompiled binaries can be downloaded from here: http://www.lfd.uci.edu/~gohlke/pythonlibs/#pydbg'
  29.     sys.exit(1)
  30.  
  31. ALERT_VERSION = '20130319a'
  32. POE_VERSION = '0.10.3c'
  33.  
  34. class PlaySoundWorker(threading.Thread):
  35.     def run(self):
  36.         winsound.PlaySound(r'C:\Windows\Media\Sonata\Windows Notify.wav', winsound.SND_FILENAME)
  37.  
  38. class ItemAlert(object):
  39.  
  40.     BP0 = 0x001d86c9 + 0x00400000
  41.     BP1 = 0x001d86c1 + 0x00400000
  42.     BP2 = 0x001d870f + 0x00400000
  43.  
  44.     def __init__(self):
  45.         atexit.register(self.atExit)
  46.         self.logFile = open('log.txt', 'a', 0)
  47.         print >>self.logFile, 40 * '='
  48.         print >>self.logFile, str.format('Started ItemAlertPoE version {0} at {1!s}.', ALERT_VERSION, datetime.datetime.now())
  49.         print >>self.logFile, str.format('Python version: {0!s}', sys.version_info)
  50.         self.dbg = pydbg()
  51.         self.dbg.attach(self.getProcessId())
  52.         self.baseAddress = self.getBaseAddress()
  53.         adjustment = self.baseAddress - 0x00400000
  54.         ItemAlert.BP0 += adjustment
  55.         ItemAlert.BP1 += adjustment
  56.         ItemAlert.BP2 += adjustment
  57.         self.lastPacketBufferAddress = 0
  58.         self.lastPacketSize = 0
  59.  
  60.     def __enter__(self):
  61.         return self
  62.  
  63.     def __exit__(self, type, value, traceback):
  64.         self.logFile.close()
  65.  
  66.     def grabPacketSize(self, dbg):
  67.         self.lastPacketSize = dbg.get_register('eax')
  68.         return DBG_CONTINUE
  69.  
  70.     def beforeDemanglingPacket(self, dbg):
  71.         self.lastPacketBufferAddress = dbg.get_register('eax')
  72.         return DBG_CONTINUE
  73.  
  74.     def parseWorldItemPacket(self, packetData):
  75.         try:
  76.             buffer = ByteBuffer(packetData)
  77.             buffer.setEndian(ByteBuffer.BIG_ENDIAN)
  78.  
  79.             id = buffer.nextByte()
  80.             objectType = buffer.nextDword()
  81.             unk1 = buffer.nextDword()
  82.             unk2 = buffer.nextByte()
  83.             if unk2 != 0:
  84.                 print >>self.logFile, 'The following packet has an odd unk2 field:'
  85.                 print >>self.logFile, self.dbg.hex_dump(map(lambda x: chr(x), packetData))
  86.                 return
  87.  
  88.             x = buffer.nextDword()
  89.             y = buffer.nextDword()
  90.             rot = buffer.nextDword()
  91.  
  92.             unk3 = buffer.nextDword(ByteBuffer.LITTLE_ENDIAN)
  93.             unk4 = buffer.nextDword()
  94.  
  95.             if unk3 >> 2 != 0:
  96.                 print >>self.logFile, 'The following packet has an odd unk3 field:'
  97.                 print >>self.logFile, self.dbg.hex_dump(map(lambda x: chr(x), packetData))
  98.                 buffer.nextDword()
  99.                 buffer.nextDword()
  100.  
  101.             unk5 = buffer.nextByte()
  102.             if unk5 != 0:
  103.                 print >>self.logFile, 'The following packet has an odd unk5 field:'
  104.                 print >>self.logFile, self.dbg.hex_dump(map(lambda x: chr(x), packetData))
  105.                 return
  106.  
  107.             unk5 = buffer.nextDword()
  108.             if unk5 != 0: buffer.nextDword()
  109.  
  110.             unk6 = buffer.nextByte()
  111.             itemId = buffer.nextDword()
  112.             itemName = getItemName(itemId)
  113.             print >>self.logFile, str.format('Detected item drop: {0} (id=0x{1:08x})', itemName, itemId)
  114.             if shouldNotify(itemName):
  115.                 print str.format('Detected item drop: {0}', itemName)
  116.                 worker = PlaySoundWorker()
  117.                 worker.start()
  118.         except: pass
  119.  
  120.     def afterDemanglingPacket(self, dbg):
  121.         if self.lastPacketBufferAddress != 0 and self.lastPacketSize > 1:
  122.             packetData = dbg.read_process_memory(self.lastPacketBufferAddress, self.lastPacketSize)
  123.             packetData = map(lambda x: ord(x), packetData)
  124.             for i in range(self.lastPacketSize):
  125.                 if packetData[i:i+5] == [0xf0, 0x54, 0x92, 0x8a, 0x3a]:
  126.                     self.parseWorldItemPacket(packetData[i:])
  127.         return DBG_CONTINUE
  128.  
  129.     def getProcessId(self):
  130.         clients = [x[0] for x in self.dbg.enumerate_processes() if x[1].lower() == 'client.exe']
  131.         print >>self.logFile, str.format('"Client.exe" processes found: {0!s}', clients)
  132.         pid = None
  133.         if not clients or len(clients) == 0: print 'No "client.exe" process found.'
  134.         elif len(clients) > 1: print 'Found more than one "client.exe" process.'
  135.         else: pid = clients[0]
  136.         return pid
  137.  
  138.     def getBaseAddress(self):
  139.         base = [x[1] for x in self.dbg.enumerate_modules() if x[0].lower() == 'client.exe'][0]
  140.         print >>self.logFile, str.format('Base address: 0x{0:08x}', base)
  141.         return base
  142.  
  143.     def run(self):
  144.         print >>self.logFile, str.format('bp0: 0x{0:08x}: {1}', ItemAlert.BP0, self.dbg.disasm(ItemAlert.BP0))
  145.         print >>self.logFile, str.format('bp1: 0x{0:08x}: {1}', ItemAlert.BP1, self.dbg.disasm(ItemAlert.BP1))
  146.         print >>self.logFile, str.format('bp2: 0x{0:08x}: {1}', ItemAlert.BP2, self.dbg.disasm(ItemAlert.BP2))
  147.         try:
  148.             self.dbg.bp_set(ItemAlert.BP0, handler=self.grabPacketSize)
  149.             self.dbg.bp_set(ItemAlert.BP1, handler=self.beforeDemanglingPacket)
  150.             self.dbg.bp_set(ItemAlert.BP2, handler=self.afterDemanglingPacket)
  151.         except Exception as inst:
  152.             print >>self.logFile, type(inst)
  153.             print >>self.logFile, inst.args
  154.             print >>self.logFile, inst
  155.             traceback.print_exc(file=self.logFile)
  156.         print >>self.logFile, 'Starting main loop.'
  157.         try: self.dbg.debug_event_loop()
  158.         except: pass
  159.  
  160.     def atExit(self):
  161.         try: self.dbg.detach()
  162.         except: pass
  163.  
  164. def checkVersion():
  165.     if ctypes.sizeof(ctypes.c_voidp) != 4:
  166.         print 'This program only works with a 32-bit Python installation!'
  167.         print 'The preferred (tested) version is Python 2.7, 32-bit.'
  168.         print 'You can download it from here: http://www.python.org/ftp/python/2.7.3/python-2.7.3.msi'
  169.         sys.exit(1)
  170.  
  171. def main():
  172.     OMGDONT('title Path of Exile ItemAlert by sku')
  173.     checkVersion()
  174.     print str.format('Starting ItemAlert {0} for Path of Exile {1} by sku', ALERT_VERSION, POE_VERSION)
  175.     with ItemAlert() as alerter: alerter.run()
  176.  
  177. if __name__ == '__main__':
  178.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement