public static void ReadTibia11Tabs(ReadMemoryResults results) { List <TabStructure> structures = new List <TabStructure>(); Interlocked.Exchange(ref structures, tabStructures); foreach (TabStructure structure in structures) { IEnumerable <string> tabMessages = structure.GetMessages(); SearchChunk(tabMessages, results); } }
public static ReadMemoryResults ReadMemory() { ReadMemoryResults results = null; SYSTEM_INFO sys_info; GetSystemInfo(out sys_info); IntPtr proc_min_address = sys_info.minimumApplicationAddress; IntPtr proc_max_address = sys_info.maximumApplicationAddress; long proc_min_address_l = (long)proc_min_address; long proc_max_address_l = (long)proc_max_address; Process[] processes = ProcessManager.GetTibiaProcesses(); if (processes == null || processes.Length == 0) { return(null); } results = new ReadMemoryResults(); skipDuplicateCommands = (FlashClient || !SettingsManager.getSettingBool("ScanInternalTabStructure")) && SettingsManager.getSettingBool("SkipDuplicateCommands"); Dictionary <int, HashSet <long> > newWhitelistedAddresses = null; Interlocked.Exchange(ref newWhitelistedAddresses, whiteListedAddresses); if (ProcessManager.TibiaClientType == "Tibia11") { ReadTibia11Tabs(results); } else { foreach (Process process in processes) { if (!FlashClient && SettingsManager.getSettingBool("ScanInternalTabStructure")) { ReadMemoryInternal(process, results); UseInternalScan = true; } else { ReadMemoryWhiteList(process, newWhitelistedAddresses, FlashClient, results); UseInternalScan = false; } process.Dispose(); } } FinalCleanup(results); return(results); }
private static void FinalCleanup(ReadMemoryResults res) { foreach (KeyValuePair <string, List <Tuple <string, string> > > kvp in res.commands) { string time = kvp.Key; if (res.itemDrops.ContainsKey(time)) { foreach (Tuple <string, string> command in kvp.Value) { if (res.itemDrops[time].Contains(command.Item2.Trim())) { res.itemDrops[time].Remove(command.Item2); } } } } }
public static void ReadMemoryInternal(Process process, ReadMemoryResults results) { int currentAddress = process.MainModule.BaseAddress.ToInt32(); IntPtr ptr = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_WM_READ, false, process.Id); if (ptr == null) { return; } int processHandle = ptr.ToInt32(); currentAddress += (int)MemoryReader.TabsBaseAddress; currentAddress = MemoryReader.ReadInt32(currentAddress, processHandle); currentAddress = MemoryReader.ReadInt32(currentAddress + 0x24, processHandle); currentAddress = MemoryReader.ReadInt32(currentAddress + 0x10, processHandle); currentAddress = MemoryReader.ReadInt32(currentAddress + 0x10, processHandle); int tabsAddress = MemoryReader.ReadInt32(currentAddress + 0x30, processHandle); //first tab node address is tabsAddress + 0x24 int tabNodeAddress = MemoryReader.ReadInt32(tabsAddress + 0x24, processHandle); int tabCount = 0; //repeat until tab node address = 0x0 while (tabNodeAddress != 0x0) { tabCount++; //use 0x30 for longer name (possibly upto 30 bytes) //0x2C will use '...' for names longer than 15 chars int tabNamePointer = MemoryReader.ReadInt32(tabNodeAddress + 0x2C, processHandle); string tabName = MemoryReader.ReadString(tabNamePointer, 16, processHandle); int tabMessagesDataStructure = MemoryReader.ReadInt32(tabNodeAddress + 0x24, processHandle); bool serverLog = tabName == "Server Log"; // only read log messages from the server log; only read chat messages from other tabs IEnumerable <string> tabMessages = ReadTabMessages(processHandle, tabMessagesDataStructure); SearchChunk(tabMessages, results, !serverLog, serverLog); //next tab node pointer is current tab node address + 0x10 tabNodeAddress = MemoryReader.ReadInt32(tabNodeAddress + 0x10, processHandle); } }
private static void FinalCleanup(ReadMemoryResults res) { foreach (KeyValuePair<string, List<Tuple<string, string>>> kvp in res.commands) { string time = kvp.Key; if (res.itemDrops.ContainsKey(time)) { foreach (Tuple<string, string> command in kvp.Value) { if (res.itemDrops[time].Contains(command.Item2.Trim())) { res.itemDrops[time].Remove(command.Item2); } } } } }
public static void ReadTibia11Tabs(ReadMemoryResults results) { List<TabStructure> structures = new List<TabStructure>(); Interlocked.Exchange(ref structures, tabStructures); foreach(TabStructure structure in structures) { IEnumerable<string> tabMessages = structure.GetMessages(); SearchChunk(tabMessages, results); } }
public static void ReadMemoryWhiteList(Process process, Dictionary<int, HashSet<long>> newWhitelistedAddresses, bool flashClient, ReadMemoryResults results) { HashSet<long> whitelist; if (!newWhitelistedAddresses.TryGetValue(process.Id, out whitelist)) { return; } IntPtr processHandle = MemoryReader.OpenProcess(process); int bytesRead = 0; // number of bytes read with ReadProcessMemory foreach (long addr in whitelist) { IntPtr proc_min_address = new IntPtr(addr); MEMORY_BASIC_INFORMATION mem_basic_info; VirtualQueryEx(processHandle, proc_min_address, out mem_basic_info, 28); if (mem_basic_info.Protect == PAGE_READWRITE && mem_basic_info.State == MEM_COMMIT) { if (memoryBuffer == null || memoryBuffer.Length < mem_basic_info.RegionSize) { memoryBuffer = new byte[mem_basic_info.RegionSize]; } // read everything in the buffer above ReadProcessMemory((int)processHandle, mem_basic_info.BaseAddress, memoryBuffer, mem_basic_info.RegionSize, ref bytesRead); // scan the memory for strings that start with timestamps and end with the null terminator ('\0') IEnumerable<string> timestampLines; if (!flashClient) { timestampLines = Parser.FindTimestamps(memoryBuffer, bytesRead); } else { timestampLines = Parser.FindTimestampsFlash(memoryBuffer, bytesRead); } SearchChunk(timestampLines, results); } } }
public static void ReadMemoryInternal(Process process, ReadMemoryResults results) { int currentAddress = process.MainModule.BaseAddress.ToInt32(); IntPtr ptr = MemoryReader.OpenProcess(process); if (ptr == null) { return; } int processHandle = ptr.ToInt32(); currentAddress += (int)MemoryReader.TabsBaseAddress; currentAddress = MemoryReader.ReadInt32(currentAddress, processHandle); currentAddress = MemoryReader.ReadInt32(currentAddress + 0x24, processHandle); currentAddress = MemoryReader.ReadInt32(currentAddress + 0x10, processHandle); currentAddress = MemoryReader.ReadInt32(currentAddress + 0x10, processHandle); int tabsAddress = MemoryReader.ReadInt32(currentAddress + 0x30, processHandle); //first tab node address is tabsAddress + 0x24 int tabNodeAddress = MemoryReader.ReadInt32(tabsAddress + 0x24, processHandle); int tabCount = 0; //repeat until tab node address = 0x0 while (tabNodeAddress != 0x0) { tabCount++; //use 0x30 for longer name (possibly upto 30 bytes) //0x2C will use '...' for names longer than 15 chars int tabNamePointer = MemoryReader.ReadInt32(tabNodeAddress + 0x2C, processHandle); string tabName = MemoryReader.ReadString(tabNamePointer, 16, processHandle); int tabMessagesDataStructure = MemoryReader.ReadInt32(tabNodeAddress + 0x24, processHandle); bool serverLog = tabName == "Server Log"; // only read log messages from the server log; only read chat messages from other tabs IEnumerable<string> tabMessages = ReadTabMessages(processHandle, tabMessagesDataStructure); SearchChunk(tabMessages, results, !serverLog, serverLog); //next tab node pointer is current tab node address + 0x10 tabNodeAddress = MemoryReader.ReadInt32(tabNodeAddress + 0x10, processHandle); } }
public static ReadMemoryResults ReadMemory() { ReadMemoryResults results = null; SYSTEM_INFO sys_info = new SYSTEM_INFO(); GetSystemInfo(out sys_info); IntPtr proc_min_address = sys_info.minimumApplicationAddress; IntPtr proc_max_address = sys_info.maximumApplicationAddress; long proc_min_address_l = (long)proc_min_address; long proc_max_address_l = (long)proc_max_address; Process[] processes = ProcessManager.GetTibiaProcesses(); if (processes == null || processes.Length == 0) { // Tibia process could not be found, wait for a bit and return Thread.Sleep(250); return(null); } int scanSpeed = SettingsManager.getSettingInt("ScanSpeed"); results = new ReadMemoryResults(); flashClient = ProcessManager.IsFlashClient(); foreach (Process process in processes) { if (!whitelistedAddresses.ContainsKey(process.Id)) { continue; } List <long> whitelist = whitelistedAddresses[process.Id]; IntPtr processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_WM_READ, false, process.Id); int bytesRead = 0; // number of bytes read with ReadProcessMemory for (int i = 0; i < whitelist.Count; i++) { long addr = whitelist[i]; if (addr < 0) { continue; } proc_min_address = new IntPtr(addr); MEMORY_BASIC_INFORMATION mem_basic_info; VirtualQueryEx(processHandle, proc_min_address, out mem_basic_info, 28); if (mem_basic_info.Protect == PAGE_READWRITE && mem_basic_info.State == MEM_COMMIT) { if (memoryBuffer == null || memoryBuffer.Length < mem_basic_info.RegionSize) { memoryBuffer = new byte[mem_basic_info.RegionSize]; } // read everything in the buffer above ReadProcessMemory((int)processHandle, mem_basic_info.BaseAddress, memoryBuffer, mem_basic_info.RegionSize, ref bytesRead); // scan the memory for strings that start with timestamps and end with the null terminator ('\0') IEnumerable <string> timestampLines; if (!flashClient) { timestampLines = Parser.FindTimestamps(memoryBuffer, bytesRead); } else { timestampLines = Parser.FindTimestampsFlash(memoryBuffer, bytesRead); } if (!SearchChunk(timestampLines, results)) { whitelist[i] = -1; } // performance throttling sleep after every scan (depending on scanSpeed setting) if (scanSpeed > 0) { Thread.Sleep(scanSpeed); } } } process.Dispose(); } FinalCleanup(results); return(results); }
private static void SearchChunk(IEnumerable <string> chunk, ReadMemoryResults res, bool readChatMessages = true, bool readLogMessages = true) { List <int> stamps = TimestampManager.getLatestStamps(3, ignoreStamp); foreach (string it in chunk) { string logMessage = it; string t = logMessage.Substring(0, 5); int hour = int.Parse(logMessage.Substring(0, 2)); int minute = int.Parse(logMessage.Substring(3, 2)); if (!stamps.Contains(TimestampManager.getStamp(hour, minute))) { continue; // the log message is not recent, so we skip parsing it } if (flashClient) { // there is some inconsistency with log messages, certain log messages use "12:00: Message.", others use "12:00 Message" // if there is a : after the timestamp we remove it if (logMessage[5] == ':') { logMessage = logMessage.Remove(5, 1); } } string message = logMessage.Substring(6); // message without timestamp if (readLogMessages) { if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " You see ") { // the message contains "you see", so it's a look message if (!res.lookMessages.ContainsKey(t)) { res.lookMessages.Add(t, new List <string>()); } res.lookMessages[t].Add(logMessage); continue; } else if (message.Contains(':')) { if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " Loot of ") // loot drop message { if (!res.itemDrops.ContainsKey(t)) { res.itemDrops.Add(t, new List <string>()); } res.itemDrops[t].Add(logMessage); continue; } } else if (logMessage.Length > 17 && logMessage.Substring(5, 12) == " You gained ") { // the message is an experience string, "You gained x experience." try { int experience = int.Parse(logMessage.Substring(17).Split(' ')[0]); if (!res.exp.ContainsKey(t)) { res.exp.Add(t, experience); } else { res.exp[t] = res.exp[t] + experience; } } catch { } continue; } else if (logMessage.Length == 19 && logMessage.Substring(5, 14) == " You are dead.") { if (!res.deaths.ContainsKey(t)) { res.deaths.Add(t, true); } } else if (logMessage.Length > 18) { string[] split = message.Split(' '); int index = split.IndexOf("hitpoints"); if (index > 0) { int ind; // damage log message (X loses Y hitpoints due to an attack by Z.) int damage = 0; if (!int.TryParse(split[index - 1], out damage)) { continue; } string player; if (logMessage.Substring(logMessage.Length - 12) == "your attack.") { // X lost Y hitpoints because of your attack. // attacker is the player himself player = "You"; } else if (split.Contains("by")) { // X lost Y hitpoints because of an attack by Z. // Z is the attacker => after the word "by" player = ""; ind = split.IndexOf("by") + 1; for (int i = ind; i < split.Length; i++) { player = (player == "" ? player : player + " ") + split[i]; } } else { continue; } string splitTerm; if (split.Contains("loses")) { splitTerm = "loses"; } else if (split.Contains("lose")) { splitTerm = "lose"; } else { continue; } ind = split.IndexOf(splitTerm); string target = ""; for (int i = 0; i < ind; i++) { target = (target == "" ? target : target + " ") + split[i]; } if (!res.damageDealt.ContainsKey(player)) { res.damageDealt.Add(player, new Dictionary <string, DamageEntry>()); } DamageEntry damageEntry; if (!res.damageDealt[player].ContainsKey(t)) { damageEntry = new DamageEntry(); damageEntry.damage = damage; damageEntry.targetDamage.Add(target, damage); res.damageDealt[player].Add(t, damageEntry); } else { damageEntry = res.damageDealt[player][t]; damageEntry.damage += damage; if (damageEntry.targetDamage.ContainsKey(target)) { damageEntry.targetDamage[target] += damage; } else { damageEntry.targetDamage.Add(target, damage); } } continue; } else { index = split.IndexOf("hitpoints."); if (index > 0) { // heal log message (X healed Y for Z hitpoints.) int healing = 0; if (!int.TryParse(split[index - 1], out healing)) { continue; } int forIndex = split.IndexOf("for"); if (forIndex <= 0) { continue; } string splitTerm; if (split.Contains("heal")) { splitTerm = "heal"; } else if (split.Contains("healed")) { splitTerm = "healed"; } else { continue; } int healIndex = split.IndexOf(splitTerm); if (healIndex >= forIndex) { continue; } string source = ""; for (int i = 0; i < healIndex; i++) { if (split[i] == "was" || split[i] == "were") { break; } if (split[i] == "by") { continue; } source = (source == "" ? source : source + " ") + split[i]; } string target = ""; for (int i = healIndex + 1; i < forIndex; i++) { if (split[i] == "was" || split[i] == "were") { break; } if (split[i] == "by") { continue; } target = (target == "" ? target : target + " ") + split[i]; } if (target == "yourself" || target == "itself" || target == "himself" || target == "herself") { target = source; } if (target.Length == 0 || source.Length == 0) { continue; } if (split.Contains("by")) { // X healed Y for Z. => X is the source and Y is the target (default) // X was healed by Y for Z. => X is the target and Y is the source, so swap source and target string temp = source; source = target; target = temp; } if (!res.healingDone.ContainsKey(source)) { res.healingDone.Add(source, new Dictionary <string, DamageEntry>()); } DamageEntry healingEntry; if (!res.healingDone[source].ContainsKey(t)) { healingEntry = new DamageEntry(); healingEntry.damage = healing; healingEntry.targetDamage.Add(target, healing); res.healingDone[source].Add(t, healingEntry); } else { healingEntry = res.healingDone[source][t]; healingEntry.damage += healing; if (healingEntry.targetDamage.ContainsKey(target)) { healingEntry.targetDamage[target] += healing; } else { healingEntry.targetDamage.Add(target, healing); } } } else if (logMessage.Substring(5, 14) == " You advanced " && logMessage.Contains("level", StringComparison.OrdinalIgnoreCase)) { // advancement log message (You advanced from level x to level x + 1.) if (logMessage[logMessage.Length - 1] == '.') { if (GlobalDataManager.AddLevelAdvance(logMessage)) { res.newAdvances.Add(logMessage); } continue; } } else if (logMessage.Substring(5, 7) == " Using " && logMessage.Substring(logMessage.Length - 3, 3) == "...") { // using log message (Using one of X items...) var values = Parser.ParseUsingMessage(logMessage); if (!res.usingMessages.ContainsKey(values.Item1)) { res.usingMessages.Add(values.Item1, new Dictionary <string, HashSet <int> >()); } if (!res.usingMessages[values.Item1].ContainsKey(t)) { res.usingMessages[values.Item1].Add(t, new HashSet <int>()); } res.usingMessages[values.Item1][t].Add(values.Item2); continue; } else { foreach (Event ev in StorageManager.eventIdMap.Values) { foreach (string evMessage in ev.eventMessages) { if (logMessage.Length == evMessage.Length + 6 && logMessage.Contains(evMessage.Trim(), StringComparison.OrdinalIgnoreCase)) { res.eventMessages.Add(new Tuple <Event, string>(ev, logMessage)); } } } } } } } if (readChatMessages) { if (message.Contains(':')) { if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " Loot of ") // loot drop message { continue; } else // if the message contains the ':' symbol but is not a loot drop message, it is a chat message, i.e. a command or url // we only split at most once, because the chat message can contain the ':' symbol as well and we don't want to discard that { string[] split = message.Split(new char[] { ':' }, 2); string command = split[1]; // now get the player name, we have to discard the level that is between brackets // players can also have spaces in their name, so we take that into account string[] playersplit = split[0].Split(' '); string player = ""; foreach (string str in playersplit) { if (str.Contains('[')) { break; } player = (player == "" ? player : player + " ") + str; } if (player == "http" || player == "https") { continue; // I don't remember why we do this, possible http link in a log message? not sure } if (command.Contains('@')) { // @ symbol symbolizes a command, so if there is an @ symbol, we treat the string as a command if (!res.commands.ContainsKey(t)) { res.commands.Add(t, new List <Tuple <string, string> >()); } res.commands[t].Add(new Tuple <string, string>(player, command)); } else if (command.Contains("www") || command.Contains("http") || command.Contains(".com") || command.Contains(".net") || command.Contains(".tv") || command.Contains(".br")) { // check if the command is an url, we aren't really smart about this, just check for a couple of common url-like things if (!res.urls.ContainsKey(t)) { res.urls.Add(t, new List <Tuple <string, string> >()); } res.urls[t].Add(new Tuple <string, string>(player, command)); } } } } } }
private ParseMemoryResults ParseLogResults(ReadMemoryResults res) { if (res == null) return null; ParseMemoryResults o = new ParseMemoryResults(); // first we add the new parsed damage logs to the totalDamageResults foreach (KeyValuePair<string, Dictionary<string, int>> kvp in res.damageDealt) { string player = kvp.Key; Dictionary<string, int> playerDamage = kvp.Value; if (!totalDamageResults.ContainsKey(player)) totalDamageResults.Add(player, new Dictionary<string, int>()); foreach (KeyValuePair<string, int> kvp2 in playerDamage) { string timestamp = kvp2.Key; int damage = kvp2.Value; // if the damage for the given timestamp does not exist yet, add it if (!totalDamageResults[player].ContainsKey(timestamp)) totalDamageResults[player].Add(timestamp, damage); // if it does exist, select the biggest of the two // the reason we select the biggest of the two is: // - if the timestamp is 'the current time', totalDamageResults may hold an old value, so we update it // - if timestamp is old, a part of the log for the time could have already been removed (because the log was full) // so the 'new' damage is only part of the damage for this timestamp else if (totalDamageResults[player][timestamp] < damage) totalDamageResults[player][timestamp] = damage; } } // now that we have updated the damage results, fill in the DPS meter, we use damage from the last 15 minutes for this List<string> times = getLatestTimes(15); foreach (KeyValuePair<string, Dictionary<string, int>> kvp in totalDamageResults) { string player = kvp.Key; int damage = 0; int minutes = 0; foreach (string t in times) { if (totalDamageResults[player].ContainsKey(t)) { damage += totalDamageResults[player][t]; minutes++; } } if (damage > 0) { o.damagePerSecond.Add(player, new Tuple<int, int>(damage, minutes)); } } // similar to damage, we keep a totalExperienceResults list // first update it with the new information int newExperience = 0; foreach (KeyValuePair<string, int> kvp in res.exp) { string time = kvp.Key; int experience = kvp.Value; if (!totalExperienceResults.ContainsKey(time)) { totalExperienceResults.Add(time, experience); newExperience += experience; } else if (totalExperienceResults[time] < experience) { newExperience += experience - totalExperienceResults[time]; totalExperienceResults[time] = experience; } } // now compute the experience per hour // we use the same formula Tibia itself does so we get the same value // this formula is basically, take the experience in the last 15 minutes and multiply it by 4 foreach (string t in times) { if (totalExperienceResults.ContainsKey(t)) o.expPerHour += totalExperienceResults[t]; } o.expPerHour *= 4; // Parse event messages foreach (Tuple<Event, string> tpl in res.eventMessages) { if (!eventMessages.Contains(tpl.Item2)) { eventMessages.Add(tpl.Item2); o.newEventMessages.Add(tpl); } } // Update the look information foreach (KeyValuePair<string, List<string>> kvp in res.lookMessages) { string t = kvp.Key; List<string> currentMessages = kvp.Value; if (!totalLooks.ContainsKey(t)) totalLooks[t] = new List<string>(); if (currentMessages.Count > totalLooks[t].Count) { List<string> unseenLooks = new List<string>(); List<string> lookList = totalLooks[t].ToArray().ToList(); foreach (string lookMessage in currentMessages) { if (!totalLooks[t].Contains(lookMessage)) { unseenLooks.Add(lookMessage); o.newLooks.Add(lookMessage); } else { totalLooks[t].Remove(lookMessage); } } lookList.AddRange(unseenLooks); totalLooks[t] = lookList; } } // Update death information foreach (KeyValuePair<string, bool> kvp in res.deaths) { if (!totalDeaths.ContainsKey(kvp.Key)) { totalDeaths.Add(kvp.Key, false); } if (kvp.Value && !totalDeaths[kvp.Key]) { o.death = true; totalDeaths[kvp.Key] = true; } } // now parse any new commands given by users foreach (KeyValuePair<string, List<Tuple<string, string>>> kvp in res.commands) { string t = kvp.Key; List<Tuple<string, string>> currentCommands = kvp.Value; if (!totalCommands.ContainsKey(t)) totalCommands[t] = new List<Tuple<string, string>>(); if (currentCommands.Count > totalCommands[t].Count) { List<Tuple<string, string>> unseenCommands = new List<Tuple<string, string>>(); List<Tuple<string, string>> commandsList = totalCommands[t].ToArray().ToList(); // create a copy of the list foreach (Tuple<string, string> command in currentCommands) { if (!totalCommands[t].Contains(command)) { unseenCommands.Add(command); string player = command.Item1; string cmd = command.Item2; if (SettingsManager.getSetting("Names").Contains(player)) { o.newCommands.Add(cmd); } } else { totalCommands[t].Remove(command); } } commandsList.AddRange(unseenCommands); totalCommands[t] = commandsList; } } // check new urls foreach (KeyValuePair<string, List<Tuple<string, string>>> kvp in res.urls) { string t = kvp.Key; List<Tuple<string, string>> currentURLs = kvp.Value; if (!totalURLs.ContainsKey(t)) { totalURLs.Add(t, currentURLs); } else if (currentURLs.Count > totalURLs[t].Count) { totalURLs[t] = currentURLs; } } ParseLootMessages(activeHunt, res.itemDrops, o.newItems, true, true); activeHunt.totalExp += newExperience; readWatch.Stop(); if (newExperience == 0) { if (ticksSinceExperience < 120) { ticksSinceExperience += readWatch.Elapsed.TotalSeconds; } } else { ticksSinceExperience = 0; } if (ticksSinceExperience < 120) { activeHunt.totalTime += readWatch.Elapsed.TotalSeconds; } readWatch.Restart(); saveHunts(); return o; }
public static ReadMemoryResults ReadMemory() { ReadMemoryResults results = null; SYSTEM_INFO sys_info = new SYSTEM_INFO(); GetSystemInfo(out sys_info); IntPtr proc_min_address = sys_info.minimumApplicationAddress; IntPtr proc_max_address = sys_info.maximumApplicationAddress; long proc_min_address_l = (long)proc_min_address; long proc_max_address_l = (long)proc_max_address; Process[] processes = ProcessManager.GetTibiaProcesses(); if (processes == null || processes.Length == 0) { // Tibia process could not be found, wait for a bit and return Thread.Sleep(250); return null; } int scanSpeed = SettingsManager.getSettingInt("ScanSpeed"); results = new ReadMemoryResults(); flashClient = ProcessManager.IsFlashClient(); foreach(Process process in processes) { if (!whitelistedAddresses.ContainsKey(process.Id)) continue; List<long> whitelist = whitelistedAddresses[process.Id]; IntPtr processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_WM_READ, false, process.Id); int bytesRead = 0; // number of bytes read with ReadProcessMemory for (int i = 0; i < whitelist.Count; i++) { long addr = whitelist[i]; if (addr < 0) continue; proc_min_address = new IntPtr(addr); MEMORY_BASIC_INFORMATION mem_basic_info; VirtualQueryEx(processHandle, proc_min_address, out mem_basic_info, 28); if (mem_basic_info.Protect == PAGE_READWRITE && mem_basic_info.State == MEM_COMMIT) { if (memoryBuffer == null || memoryBuffer.Length < mem_basic_info.RegionSize) { memoryBuffer = new byte[mem_basic_info.RegionSize]; } // read everything in the buffer above ReadProcessMemory((int)processHandle, mem_basic_info.BaseAddress, memoryBuffer, mem_basic_info.RegionSize, ref bytesRead); // scan the memory for strings that start with timestamps and end with the null terminator ('\0') IEnumerable<string> timestampLines; if (!flashClient) { timestampLines = Parser.FindTimestamps(memoryBuffer, bytesRead); } else { timestampLines = Parser.FindTimestampsFlash(memoryBuffer, bytesRead); } if (!SearchChunk(timestampLines, results)) { whitelist[i] = -1; } // performance throttling sleep after every scan (depending on scanSpeed setting) if (scanSpeed > 0) { Thread.Sleep(scanSpeed); } } } process.Dispose(); } FinalCleanup(results); return results; }
public static bool ScanMemory() { ReadMemoryResults readMemoryResults = ReadMemoryManager.ReadMemory(); ParseMemoryResults parseMemoryResults = Parser.ParseLogResults(readMemoryResults); if (parseMemoryResults != null) { lastResults = parseMemoryResults; if (parseMemoryResults.newDamage) { GlobalDataManager.UpdateDamage(); } } if (readMemoryResults != null && readMemoryResults.newAdvances.Count > 0) { if (SettingsManager.getSettingBool("AutoScreenshotAdvance")) { MainForm.mainForm.Invoke((MethodInvoker) delegate { ScreenshotManager.saveScreenshot("Advance", ScreenshotManager.takeScreenshot()); }); } if (SettingsManager.getSettingBool("CopyAdvances")) { foreach (object obj in readMemoryResults.newAdvances) { MainForm.mainForm.Invoke((MethodInvoker) delegate { Clipboard.SetText(obj.ToString()); }); } } readMemoryResults.newAdvances.Clear(); } if (parseMemoryResults != null && parseMemoryResults.death) { if (SettingsManager.getSettingBool("AutoScreenshotDeath")) { MainForm.mainForm.Invoke((MethodInvoker) delegate { ScreenshotManager.saveScreenshot("Death", ScreenshotManager.takeScreenshot()); }); } parseMemoryResults.death = false; } if (parseMemoryResults != null) { if (parseMemoryResults.newEventMessages.Count > 0) { if (SettingsManager.getSettingBool("EnableEventNotifications")) { foreach (Tuple <Event, string> tpl in parseMemoryResults.newEventMessages) { Event ev = tpl.Item1; Creature cr = StorageManager.getCreature(ev.creatureid); MainForm.mainForm.Invoke((MethodInvoker) delegate { if (!SettingsManager.getSettingBool("UseRichNotificationType")) { PopupManager.ShowSimpleNotification("Event in " + ev.location, tpl.Item2, cr.image); } else { PopupManager.ShowSimpleNotification(new SimpleTextNotification(cr.image, "Event in " + ev.location, tpl.Item2)); } }); } } parseMemoryResults.newEventMessages.Clear(); } } if (SettingsManager.getSettingBool("LookMode") && readMemoryResults != null) { foreach (string msg in parseMemoryResults.newLooks) { string itemName = Parser.parseLookItem(msg).ToLower(); if (StorageManager.itemExists(itemName)) { MainForm.mainForm.Invoke((MethodInvoker) delegate { CommandManager.ExecuteCommand("item@" + itemName); }); } else if (StorageManager.creatureExists(itemName) || (itemName.Contains("dead ") && (itemName = itemName.Replace("dead ", "")) != null && StorageManager.creatureExists(itemName)) || (itemName.Contains("slain ") && (itemName = itemName.Replace("slain ", "")) != null && StorageManager.creatureExists(itemName))) { MainForm.mainForm.Invoke((MethodInvoker) delegate { CommandManager.ExecuteCommand("creature@" + itemName); }); } else { NPC npc = StorageManager.getNPC(itemName); if (npc != null) { MainForm.mainForm.Invoke((MethodInvoker) delegate { CommandManager.ExecuteCommand("npc@" + itemName); }); } } } parseMemoryResults.newLooks.Clear(); } List <string> commands = parseMemoryResults == null ? new List <string>() : parseMemoryResults.newCommands.ToList(); commands.Reverse(); foreach (string command in commands) { MainForm.mainForm.Invoke((MethodInvoker) delegate { if (!CommandManager.ExecuteCommand(command, parseMemoryResults) && SettingsManager.getSettingBool("EnableUnrecognizedNotifications")) { if (!SettingsManager.getSettingBool("UseRichNotificationType")) { PopupManager.ShowSimpleNotification("Unrecognized command", "Unrecognized command: " + command, StyleManager.GetImage("tibia.png")); } else { PopupManager.ShowSimpleNotification(new SimpleTextNotification(null, "Unrecognized command", "Unrecognized command: " + command)); } } }); } if (parseMemoryResults != null) { if (parseMemoryResults.newItems.Count > 0) { MainForm.mainForm.Invoke((MethodInvoker) delegate { LootDatabaseManager.UpdateLoot(); }); } foreach (Tuple <Creature, List <Tuple <Item, int> > > tpl in parseMemoryResults.newItems) { Creature cr = tpl.Item1; List <Tuple <Item, int> > items = tpl.Item2; bool showNotification = PopupManager.ShowDropNotification(tpl); if (showNotification) { if (!SettingsManager.getSettingBool("UseRichNotificationType")) { Console.WriteLine("Rich Notification"); PopupManager.ShowSimpleNotification(cr.displayname, cr.displayname + " dropped a valuable item.", cr.image); } else { MainForm.mainForm.Invoke((MethodInvoker) delegate { PopupManager.ShowSimpleNotification(new SimpleLootNotification(cr, items)); }); } if (SettingsManager.getSettingBool("AutoScreenshotItemDrop")) { // Take a screenshot if Tibialyzer is set to take screenshots of valuable loot Bitmap screenshot = ScreenshotManager.takeScreenshot(); if (screenshot == null) { continue; } // Add a notification to the screenshot SimpleLootNotification screenshotNotification = new SimpleLootNotification(cr, items); Bitmap notification = new Bitmap(screenshotNotification.Width, screenshotNotification.Height); screenshotNotification.DrawToBitmap(notification, new Rectangle(0, 0, screenshotNotification.Width, screenshotNotification.Height)); foreach (Control c in screenshotNotification.Controls) { c.DrawToBitmap(notification, new Rectangle(c.Location, c.Size)); } screenshotNotification.Dispose(); int widthOffset = notification.Width + 10; int heightOffset = notification.Height + 10; if (screenshot.Width > widthOffset && screenshot.Height > heightOffset) { using (Graphics gr = Graphics.FromImage(screenshot)) { gr.DrawImage(notification, new Point(screenshot.Width - widthOffset, screenshot.Height - heightOffset)); } } notification.Dispose(); MainForm.mainForm.Invoke((MethodInvoker) delegate { ScreenshotManager.saveScreenshot("Loot", screenshot); }); } } } } return(readMemoryResults != null); }
public static ParseMemoryResults ParseLogResults(ReadMemoryResults res) { if (res == null) { return(null); } ParseMemoryResults o = new ParseMemoryResults(); // first we add the new parsed damage logs to the totalDamageResults o.newDamage = GlobalDataManager.UpdateDamageInformation(res.damageDealt); // now that we have updated the damage results, fill in the DPS meter, we use damage from the last 15 minutes for this List <string> times = TimestampManager.getLatestTimes(15); GlobalDataManager.GenerateDamageResults(o.damagePerSecond, times); // similar to damage, we keep a totalExperienceResults list // first update it with the new information int newExperience = GlobalDataManager.UpdateExperience(res.exp); // now compute the experience per hour // we use the same formula Tibia itself does so we get the same value // this formula is basically, take the experience in the last 15 minutes and multiply it by 4 o.expPerHour = GlobalDataManager.GetExperiencePerHour(); // Parse event messages foreach (Tuple <Event, string> newEvent in GlobalDataManager.UpdateEventInformation(res.eventMessages)) { o.newEventMessages.Add(newEvent); } // Update the look information foreach (string newLook in GlobalDataManager.UpdateLookInformation(res.lookMessages)) { o.newLooks.Add(newLook); } // Update death information o.death = GlobalDataManager.UpdateDeaths(res.deaths); // now parse any new commands given by users foreach (string newCommand in GlobalDataManager.UpdateCommands(res.commands)) { o.newCommands.Add(newCommand); } // check new urls GlobalDataManager.UpdateURLs(res.urls); HuntManager.AddUsedItems(HuntManager.activeHunt, res.usingMessages); Parser.ParseLootMessages(HuntManager.activeHunt, res.itemDrops, o.newItems, true, true); HuntManager.activeHunt.totalExp += newExperience; readWatch.Stop(); if (newExperience == 0) { if (ticksSinceExperience < 120) { ticksSinceExperience += readWatch.Elapsed.TotalSeconds; } } else { ticksSinceExperience = 0; } if (ticksSinceExperience < 120) { HuntManager.activeHunt.totalTime += readWatch.Elapsed.TotalSeconds; } readWatch.Restart(); HuntManager.SaveHunts(); return(o); }
private static bool SearchChunk(IEnumerable <string> chunk, ReadMemoryResults res) { List <int> stamps = TimestampManager.getLatestStamps(3, ignoreStamp); bool chunksExist = false; foreach (string it in chunk) { chunksExist = true; string logMessage = it; string t = logMessage.Substring(0, 5); int hour = int.Parse(logMessage.Substring(0, 2)); int minute = int.Parse(logMessage.Substring(3, 2)); if (!stamps.Contains(TimestampManager.getStamp(hour, minute))) { continue; // the log message is not recent, so we skip parsing it } if (flashClient) { // there is some inconsistency with log messages, certain log messages use "12:00: Message.", others use "12:00 Message" // if there is a : after the timestamp we remove it if (logMessage[5] == ':') { logMessage = logMessage.Remove(5, 1); } } string message = logMessage.Substring(6); // message without timestamp if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " You see ") { // the message contains "you see", so it's a look message if (!res.lookMessages.ContainsKey(t)) { res.lookMessages.Add(t, new List <string>()); } res.lookMessages[t].Add(logMessage); } else if (message.Contains(':')) { if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " Loot of ") // loot drop message { if (!res.itemDrops.ContainsKey(t)) { res.itemDrops.Add(t, new List <string>()); } res.itemDrops[t].Add(logMessage); } else // if the message contains the ':' symbol but is not a loot drop message, it is a chat message, i.e. a command or url // we only split at most once, because the chat message can contain the ':' symbol as well and we don't want to discard that { string[] split = message.Split(new char[] { ':' }, 2); string command = split[1]; // now get the player name, we have to discard the level that is between brackets // players can also have spaces in their name, so we take that into account string[] playersplit = split[0].Split(' '); string player = ""; foreach (string str in playersplit) { if (str.Contains('[')) { break; } player = (player == "" ? player : player + " ") + str; } if (player == "http" || player == "https") { continue; // I don't remember why we do this, possible http link in a log message? not sure } if (command.Contains('@')) { // @ symbol symbolizes a command, so if there is an @ symbol, we treat the string as a command if (!res.commands.ContainsKey(t)) { res.commands.Add(t, new List <Tuple <string, string> >()); } res.commands[t].Add(new Tuple <string, string>(player, command)); } else if (command.Contains("www") || command.Contains("http") || command.Contains(".com") || command.Contains(".net") || command.Contains(".tv") || command.Contains(".br")) { // check if the command is an url, we aren't really smart about this, just check for a couple of common url-like things if (!res.urls.ContainsKey(t)) { res.urls.Add(t, new List <Tuple <string, string> >()); } res.urls[t].Add(new Tuple <string, string>(player, command)); } } } else if (logMessage.Length > 17 && logMessage.Substring(5, 12) == " You gained ") { // the message is an experience string, "You gained x experience." try { int experience = int.Parse(logMessage.Substring(17).Split(' ')[0]); if (!res.exp.ContainsKey(t)) { res.exp.Add(t, experience); } else { res.exp[t] = res.exp[t] + experience; } } catch { continue; } } else if (logMessage.Length == 19 && logMessage.Substring(5, 14) == " You are dead.") { if (!res.deaths.ContainsKey(t)) { res.deaths.Add(t, true); } } else if (logMessage.Length > 18) { string[] split = message.Split(' '); if (split.Contains("hitpoints") && split.ToList().IndexOf("hitpoints") > 0) { // damage log message (X loses Y hitpoints due to an attack by Z.) int damage = 0; if (!int.TryParse(split[split.ToList().IndexOf("hitpoints") - 1], out damage)) { continue; } string player; if (logMessage.Substring(logMessage.Length - 12) == "your attack.") { // X lost Y hitpoints because of your attack. // attacker is the player himself player = "You"; } else { // X lost Y hitpoints because of an attack by Z. // Z is the attacker => after the word "by" if (!split.Contains("by")) { continue; } player = ""; int ind = split.ToList().IndexOf("by") + 1; for (int i = ind; i < split.Length; i++) { player = (player == "" ? player : player + " ") + split[i]; } } if (!res.damageDealt.ContainsKey(player)) { res.damageDealt.Add(player, new Dictionary <string, int>()); } if (!res.damageDealt[player].ContainsKey(t)) { res.damageDealt[player].Add(t, damage); } else { res.damageDealt[player][t] = res.damageDealt[player][t] + damage; } } else if (logMessage.Substring(5, 14) == " You advanced " && logMessage.Contains("level", StringComparison.OrdinalIgnoreCase)) { // advancement log message (You advanced from level x to level x + 1.) if (logMessage[logMessage.Length - 1] == '.') { if (GlobalDataManager.AddLevelAdvance(logMessage)) { res.newAdvances.Add(logMessage); } } } else { foreach (Event ev in StorageManager.eventIdMap.Values) { foreach (string evMessage in ev.eventMessages) { if (logMessage.Length == evMessage.Length + 6 && logMessage.Contains(evMessage.Trim(), StringComparison.OrdinalIgnoreCase)) { res.eventMessages.Add(new Tuple <Event, string>(ev, logMessage)); } } } } } } return(chunksExist); }
private static void SearchChunk(IEnumerable<string> chunk, ReadMemoryResults res, bool readChatMessages = true, bool readLogMessages = true) { List<int> stamps = TimestampManager.getLatestStamps(3, ignoreStamp); foreach (string it in chunk) { string logMessage = it; string t = logMessage.Substring(0, 5); int hour = int.Parse(logMessage.Substring(0, 2)); int minute = int.Parse(logMessage.Substring(3, 2)); if (!stamps.Contains(TimestampManager.getStamp(hour, minute))) continue; // the log message is not recent, so we skip parsing it if (FlashClient) { // there is some inconsistency with log messages, certain log messages use "12:00: Message.", others use "12:00 Message" // if there is a : after the timestamp we remove it if (logMessage[5] == ':') { logMessage = logMessage.Remove(5, 1); } } logMessage = logMessage.Replace(" (active prey bonus)", ""); string message = logMessage.Substring(6); // message without timestamp if (readLogMessages) { if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " You see " && logMessage[logMessage.Length - 1] == '.') { // the message contains "you see", so it's a look message if (!res.lookMessages.ContainsKey(t)) res.lookMessages.Add(t, new List<string>()); if (!skipDuplicateCommands || !res.lookMessages[t].Contains(logMessage)) { res.lookMessages[t].Add(logMessage); } continue; } else if (message.Contains(':')) { if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " Loot of ") { // loot drop message if (!res.itemDrops.ContainsKey(t)) res.itemDrops.Add(t, new List<string>()); res.itemDrops[t].Add(logMessage); continue; } } else if (logMessage.Length > 17 && logMessage.Substring(5, 12) == " You gained " && logMessage.EndsWith("experience points.")) { // the message is an experience string, "You gained x experience." try { int experience = int.Parse(logMessage.Substring(17).Split(' ')[0]); if (!res.exp.ContainsKey(t)) res.exp.Add(t, experience); else res.exp[t] = res.exp[t] + experience; } catch { } continue; } else if (logMessage.Length == 19 && logMessage.Substring(5, 14) == " You are dead.") { if (!res.deaths.ContainsKey(t)) res.deaths.Add(t, true); } else if (logMessage.Length > 18) { string[] split = message.Split(' '); int index = split.IndexOf("hitpoints"); if (index > 0) { int ind; // damage log message (X loses Y hitpoints due to an attack by Z.) int damage = 0; if (!int.TryParse(split[index - 1], out damage)) { continue; } string player; if ((logMessage.Substring(logMessage.Length - 12) == "your attack.") || (logMessage.Substring(logMessage.Length - 21) == "your critical attack.")) { // X lost Y hitpoints because of your attack. // attacker is the player himself player = "You"; } else if (split.Contains("by")) { // X lost Y hitpoints because of an attack by Z. // Z is the attacker => after the word "by" player = ""; ind = split.IndexOf("by") + 1; for (int i = ind; i < split.Length; i++) { player = (player == "" ? player : player + " ") + split[i]; } } else { continue; } string splitTerm; if (split.Contains("loses")) { splitTerm = "loses"; } else if (split.Contains("lose")) { splitTerm = "lose"; } else { continue; } ind = split.IndexOf(splitTerm); string target = ""; for (int i = 0; i < ind; i++) { target = (target == "" ? target : target + " ") + split[i]; } if (!res.damageDealt.ContainsKey(player)) { res.damageDealt.Add(player, new Dictionary<string, DamageEntry>()); } DamageEntry damageEntry; if (!res.damageDealt[player].ContainsKey(t)) { damageEntry = new DamageEntry(); damageEntry.damage = damage; damageEntry.targetDamage.Add(target, damage); res.damageDealt[player].Add(t, damageEntry); } else { damageEntry = res.damageDealt[player][t]; damageEntry.damage += damage; if (damageEntry.targetDamage.ContainsKey(target)) { damageEntry.targetDamage[target] += damage; } else { damageEntry.targetDamage.Add(target, damage); } } continue; } else { index = split.IndexOf("hitpoints."); if (index > 0) { // heal log message (X healed Y for Z hitpoints.) int healing = 0; if (!int.TryParse(split[index - 1], out healing)) { continue; } int forIndex = split.IndexOf("for"); if (forIndex <= 0) { continue; } string splitTerm; if (split.Contains("heal")) { splitTerm = "heal"; } else if (split.Contains("healed")) { splitTerm = "healed"; } else { continue; } int healIndex = split.IndexOf(splitTerm); if (healIndex >= forIndex) { continue; } string source = ""; for (int i = 0; i < healIndex; i++) { if (split[i] == "was" || split[i] == "were") break; if (split[i] == "by") continue; source = (source == "" ? source : source + " ") + split[i]; } string target = ""; for (int i = healIndex + 1; i < forIndex; i++) { if (split[i] == "was" || split[i] == "were") break; if (split[i] == "by") continue; target = (target == "" ? target : target + " ") + split[i]; } if (target == "yourself" || target == "itself" || target == "himself" || target == "herself") { target = source; } if (target.Length == 0 || source.Length == 0) { continue; } if (split.Contains("by")) { // X healed Y for Z. => X is the source and Y is the target (default) // X was healed by Y for Z. => X is the target and Y is the source, so swap source and target string temp = source; source = target; target = temp; } if (!res.healingDone.ContainsKey(source)) { res.healingDone.Add(source, new Dictionary<string, DamageEntry>()); } DamageEntry healingEntry; if (!res.healingDone[source].ContainsKey(t)) { healingEntry = new DamageEntry(); healingEntry.damage = healing; healingEntry.targetDamage.Add(target, healing); res.healingDone[source].Add(t, healingEntry); } else { healingEntry = res.healingDone[source][t]; healingEntry.damage += healing; if (healingEntry.targetDamage.ContainsKey(target)) { healingEntry.targetDamage[target] += healing; } else { healingEntry.targetDamage.Add(target, healing); } } } else if (logMessage.Substring(5, 14) == " You advanced " && logMessage.Contains("level", StringComparison.OrdinalIgnoreCase)) { // advancement log message (You advanced from level x to level x + 1.) if (logMessage[logMessage.Length - 1] == '.') { if (GlobalDataManager.AddLevelAdvance(logMessage)) { res.newAdvances.Add(logMessage); } continue; } } else if (logMessage.Substring(5, 7) == " Using " && logMessage.Substring(logMessage.Length - 3, 3) == "...") { // using log message (Using one of X items...) var values = Parser.ParseUsingMessage(logMessage); if (!res.usingMessages.ContainsKey(values.Item1)) res.usingMessages.Add(values.Item1, new Dictionary<string, HashSet<int>>()); if (!res.usingMessages[values.Item1].ContainsKey(t)) res.usingMessages[values.Item1].Add(t, new HashSet<int>()); res.usingMessages[values.Item1][t].Add(values.Item2); continue; } else if (logMessage.Length > 50 && logMessage.Substring(5, 45) == " Congratulations! You earned the achievement ") { Achievement achievement = StorageManager.getAchievement(Parser.GetAchievement(logMessage)); if (achievement != null) { res.achievements.Add(new Tuple<Achievement, string>(achievement, logMessage)); } else { Console.WriteLine("Unrecognized achievement {0}.", Parser.GetAchievement(logMessage)); } } else { foreach (Event ev in StorageManager.eventIdMap.Values) { foreach (string evMessage in ev.eventMessages) { if (logMessage.Length == evMessage.Length + 6 && logMessage.Contains(evMessage.Trim(), StringComparison.OrdinalIgnoreCase)) { res.eventMessages.Add(new Tuple<Event, string>(ev, logMessage)); } } } } } } } if (readChatMessages) { if (message.Contains(':')) { if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " Loot of ") { // loot drop message continue; } else { // if the message contains the ':' symbol but is not a loot drop message, it is a chat message, i.e. a command or url // we only split at most once, because the chat message can contain the ':' symbol as well and we don't want to discard that string[] split = message.Split(new char[] { ':' }, 2); string command = split[1]; // now get the player name, we have to discard the level that is between brackets // players can also have spaces in their name, so we take that into account string[] playersplit = split[0].Split(' '); string player = ""; foreach (string str in playersplit) { if (str.Contains('[')) break; player = (player == "" ? player : player + " ") + str; } if (player == "http" || player == "https") continue; // I don't remember why we do this, possible http link in a log message? not sure if (command.Contains('@')) { // @ symbol symbolizes a command, so if there is an @ symbol, we treat the string as a command if (!res.commands.ContainsKey(t)) res.commands.Add(t, new List<Tuple<string, string>>()); var tpl = new Tuple<string, string>(player, command); if (!skipDuplicateCommands || !res.commands[t].Contains(tpl)) { res.commands[t].Add(tpl); } } else if (command.Contains("www") || command.Contains("http") || command.Contains(".com") || command.Contains(".net") || command.Contains(".tv") || command.Contains(".br")) { // check if the command is an url, we aren't really smart about this, just check for a couple of common url-like things if (!res.urls.ContainsKey(t)) res.urls.Add(t, new List<Tuple<string, string>>()); res.urls[t].Add(new Tuple<string, string>(player, command)); } } } } } }
public static void ReadMemoryWhiteList(Process process, Dictionary <int, HashSet <long> > newWhitelistedAddresses, bool flashClient, ReadMemoryResults results) { HashSet <long> whitelist; if (!newWhitelistedAddresses.TryGetValue(process.Id, out whitelist)) { return; } IntPtr processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_WM_READ, false, process.Id); int bytesRead = 0; // number of bytes read with ReadProcessMemory foreach (long addr in whitelist) { IntPtr proc_min_address = new IntPtr(addr); MEMORY_BASIC_INFORMATION mem_basic_info; VirtualQueryEx(processHandle, proc_min_address, out mem_basic_info, 28); if (mem_basic_info.Protect == PAGE_READWRITE && mem_basic_info.State == MEM_COMMIT) { if (memoryBuffer == null || memoryBuffer.Length < mem_basic_info.RegionSize) { memoryBuffer = new byte[mem_basic_info.RegionSize]; } // read everything in the buffer above ReadProcessMemory((int)processHandle, mem_basic_info.BaseAddress, memoryBuffer, mem_basic_info.RegionSize, ref bytesRead); // scan the memory for strings that start with timestamps and end with the null terminator ('\0') IEnumerable <string> timestampLines; if (!flashClient) { timestampLines = Parser.FindTimestamps(memoryBuffer, bytesRead); } else { timestampLines = Parser.FindTimestampsFlash(memoryBuffer, bytesRead); } SearchChunk(timestampLines, results); } } }
private ReadMemoryResults ReadMemory() { ReadMemoryResults results = null; SYSTEM_INFO sys_info = new SYSTEM_INFO(); GetSystemInfo(out sys_info); IntPtr proc_min_address = sys_info.minimumApplicationAddress; IntPtr proc_max_address = sys_info.maximumApplicationAddress; long proc_min_address_l = (long)proc_min_address; long proc_max_address_l = (long)proc_max_address; Process process = GetTibiaProcess(); if (process == null) { // Tibia process could not be found, wait for a bit and return Thread.Sleep(250); return null; } flashClient = TibiaClientName.ToLower().Contains("flash") || TibiaClientName.ToLower().Contains("chrome"); IntPtr processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_WM_READ, false, process.Id); MEMORY_BASIC_INFORMATION mem_basic_info = new MEMORY_BASIC_INFORMATION(); int bytesRead = 0; // number of bytes read with ReadProcessMemory int scanSpeed = SettingsManager.getSettingInt("ScanSpeed"); try { results = new ReadMemoryResults(); while (proc_min_address_l < proc_max_address_l) { proc_min_address = new IntPtr(proc_min_address_l); // 28 = sizeof(MEMORY_BASIC_INFORMATION) VirtualQueryEx(processHandle, proc_min_address, out mem_basic_info, 28); // check if this memory chunk is accessible if (mem_basic_info.Protect == PAGE_READWRITE && mem_basic_info.State == MEM_COMMIT) { if (!memorySegments.Contains(mem_basic_info.BaseAddress)) { byte[] buffer = new byte[mem_basic_info.RegionSize]; // read everything in the buffer above ReadProcessMemory((int)processHandle, mem_basic_info.BaseAddress, buffer, mem_basic_info.RegionSize, ref bytesRead); // scan the memory for strings that start with timestamps and end with the null terminator ('\0') List<string> strings; if (!flashClient) { strings = FindTimestamps(buffer); } else { strings = FindTimestampsFlash(buffer); } if (strings.Count > 0) { // if any timestamp strings were found, scan the chunk for any messages SearchChunk(strings, results); } else { memorySegments.Add(mem_basic_info.BaseAddress); } // performance throttling sleep after every scan (depending on scanSpeed setting) if (scanSpeed > 0) { Thread.Sleep(scanSpeed); } } } // move to the next memory chunk proc_min_address_l += mem_basic_info.RegionSize; } } catch { return null; } if (memorySegments.Count > 10) { memorySegments.RemoveRange(0, 10); } else { memorySegments.Clear(); } process.Dispose(); FinalCleanup(results); return results; }
private void SearchChunk(List<string> chunk, ReadMemoryResults res) { List<int> stamps = getLatestStamps(3, ignoreStamp); for (int it = 0; it < chunk.Count; it++) { string logMessage = chunk[it]; string t = logMessage.Substring(0, 5); int hour = int.Parse(logMessage.Substring(0, 2)); int minute = int.Parse(logMessage.Substring(3, 2)); if (!stamps.Contains(getStamp(hour, minute))) continue; // the log message is not recent, so we skip parsing it if (flashClient) { // there is some inconsistency with log messages, certain log messages use "12:00: Message.", others use "12:00 Message" // if there is a : after the timestamp we remove it if (logMessage[5] == ':') { logMessage = logMessage.Remove(5, 1); } } string message = logMessage.Substring(6); // message without timestamp if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " You see ") { // the message contains "you see", so it's a look message if (!res.lookMessages.ContainsKey(t)) res.lookMessages.Add(t, new List<string>()); res.lookMessages[t].Add(logMessage); } else if (message.Contains(':')) { if (logMessage.Length > 14 && logMessage.Substring(5, 9) == " Loot of ") { // loot drop message if (!res.itemDrops.ContainsKey(t)) res.itemDrops.Add(t, new List<string>()); res.itemDrops[t].Add(logMessage); } else { // if the message contains the ':' symbol but is not a loot drop message, it is a chat message, i.e. a command or url // we only split at most once, because the chat message can contain the ':' symbol as well and we don't want to discard that string[] split = message.Split(new char[] { ':' }, 2); string command = split[1]; // now get the player name, we have to discard the level that is between brackets // players can also have spaces in their name, so we take that into account string[] playersplit = split[0].Split(' '); string player = ""; foreach (string str in playersplit) { if (str.Contains('[')) break; player = (player == "" ? player : player + " ") + str; } if (player == "http" || player == "https") continue; // I don't remember why we do this, possible http link in a log message? not sure if (command.Contains('@')) { // @ symbol symbolizes a command, so if there is an @ symbol, we treat the string as a command if (!res.commands.ContainsKey(t)) res.commands.Add(t, new List<Tuple<string, string>>()); res.commands[t].Add(new Tuple<string, string>(player, command)); } else if (command.Contains("www") || command.Contains("http") || command.Contains(".com") || command.Contains(".net") || command.Contains(".tv") || command.Contains(".br")) { // check if the command is an url, we aren't really smart about this, just check for a couple of common url-like things if (!res.urls.ContainsKey(t)) res.urls.Add(t, new List<Tuple<string, string>>()); res.urls[t].Add(new Tuple<string, string>(player, command)); } } } else if (logMessage.Length > 17 && logMessage.Substring(5, 12) == " You gained ") { // the message is an experience string, "You gained x experience." try { int experience = int.Parse(logMessage.Substring(17).Split(' ')[0]); if (!res.exp.ContainsKey(t)) res.exp.Add(t, experience); else res.exp[t] = res.exp[t] + experience; } catch { continue; } } else if (logMessage.Length == 19 && logMessage.Substring(5, 14) == " You are dead.") { if (!res.deaths.ContainsKey(t)) res.deaths.Add(t, true); } else if (logMessage.Length > 18) { string[] split = message.Split(' '); if (split.Contains("hitpoints") && split.ToList().IndexOf("hitpoints") > 0) { // damage log message (X loses Y hitpoints due to an attack by Z.) int damage = 0; if (!int.TryParse(split[split.ToList().IndexOf("hitpoints") - 1], out damage)) { continue; } string player; if (logMessage.Substring(logMessage.Length - 12) == "your attack.") { // X lost Y hitpoints because of your attack. // attacker is the player himself player = "You"; } else { // X lost Y hitpoints because of an attack by Z. // Z is the attacker => after the word "by" if (!split.Contains("by")) continue; player = ""; int ind = split.ToList().IndexOf("by") + 1; for (int i = ind; i < split.Length; i++) { player = (player == "" ? player : player + " ") + split[i]; } } if (!res.damageDealt.ContainsKey(player)) res.damageDealt.Add(player, new Dictionary<string, int>()); if (!res.damageDealt[player].ContainsKey(t)) res.damageDealt[player].Add(t, damage); else res.damageDealt[player][t] = res.damageDealt[player][t] + damage; } else if (logMessage.Substring(5, 14) == " You advanced " && logMessage.ToLower().Contains("level")) { // advancement log message (You advanced from level x to level x + 1.) if (logMessage[logMessage.Length - 1] == '.' && !levelAdvances.Contains(logMessage)) { res.newAdvances.Add(logMessage); levelAdvances.Add(logMessage); } } else { foreach (Event ev in eventIdMap.Values) { foreach (string evMessage in ev.eventMessages) { if (logMessage.Length == evMessage.Length + 6 && logMessage.ToLower().Contains(evMessage.ToLower().Trim())) { res.eventMessages.Add(new Tuple<Event, string>(ev, logMessage)); } } } } } } }
public static ReadMemoryResults ReadMemory() { ReadMemoryResults results = null; SYSTEM_INFO sys_info; GetSystemInfo(out sys_info); IntPtr proc_min_address = sys_info.minimumApplicationAddress; IntPtr proc_max_address = sys_info.maximumApplicationAddress; long proc_min_address_l = (long)proc_min_address; long proc_max_address_l = (long)proc_max_address; Process[] processes = ProcessManager.GetTibiaProcesses(); if (processes == null || processes.Length == 0) { return null; } results = new ReadMemoryResults(); skipDuplicateCommands = (FlashClient || !SettingsManager.getSettingBool("ScanInternalTabStructure")) && SettingsManager.getSettingBool("SkipDuplicateCommands"); Dictionary<int, HashSet<long>> newWhitelistedAddresses = null; Interlocked.Exchange(ref newWhitelistedAddresses, whiteListedAddresses); if (ProcessManager.TibiaClientType == "Tibia11") { ReadTibia11Tabs(results); } else { foreach (Process process in processes) { if (!FlashClient && SettingsManager.getSettingBool("ScanInternalTabStructure")) { ReadMemoryInternal(process, results); UseInternalScan = true; } else { ReadMemoryWhiteList(process, newWhitelistedAddresses, FlashClient, results); UseInternalScan = false; } process.Dispose(); } } FinalCleanup(results); return results; }
public static ParseMemoryResults ParseLogResults(ReadMemoryResults res) { if (res == null) return null; ParseMemoryResults o = new ParseMemoryResults(); // first we add the new parsed damage logs to the totalDamageResults o.newDamage = GlobalDataManager.UpdateDamageInformation(res.damageDealt); // now that we have updated the damage results, fill in the DPS meter, we use damage from the last 15 minutes for this List<string> times = TimestampManager.getLatestTimes(15); GlobalDataManager.GenerateDamageResults(o.damagePerSecond, times); // similar to damage, we keep a totalExperienceResults list // first update it with the new information int newExperience = GlobalDataManager.UpdateExperience(res.exp); // now compute the experience per hour // we use the same formula Tibia itself does so we get the same value // this formula is basically, take the experience in the last 15 minutes and multiply it by 4 o.expPerHour = GlobalDataManager.GetTotalExperience(times).Item1; // Parse event messages foreach(Tuple<Event, string> newEvent in GlobalDataManager.UpdateEventInformation(res.eventMessages)) { o.newEventMessages.Add(newEvent); } // Update the look information foreach(string newLook in GlobalDataManager.UpdateLookInformation(res.lookMessages)) { o.newLooks.Add(newLook); } // Update death information o.death = GlobalDataManager.UpdateDeaths(res.deaths); // now parse any new commands given by users foreach(string newCommand in GlobalDataManager.UpdateCommands(res.commands)) { o.newCommands.Add(newCommand); } // check new urls GlobalDataManager.UpdateURLs(res.urls); Parser.ParseLootMessages(HuntManager.activeHunt, res.itemDrops, o.newItems, true, true); HuntManager.activeHunt.totalExp += newExperience; readWatch.Stop(); if (newExperience == 0) { if (ticksSinceExperience < 120) { ticksSinceExperience += readWatch.Elapsed.TotalSeconds; } } else { ticksSinceExperience = 0; } if (ticksSinceExperience < 120) { HuntManager.activeHunt.totalTime += readWatch.Elapsed.TotalSeconds; } readWatch.Restart(); HuntManager.SaveHunts(); return o; }
public static ReadMemoryResults ReadMemory() { ReadMemoryResults results = null; SYSTEM_INFO sys_info; GetSystemInfo(out sys_info); IntPtr proc_min_address = sys_info.minimumApplicationAddress; IntPtr proc_max_address = sys_info.maximumApplicationAddress; long proc_min_address_l = (long)proc_min_address; long proc_max_address_l = (long)proc_max_address; Process[] processes = ProcessManager.GetTibiaProcesses(); if (processes == null || processes.Length == 0) { return(null); } results = new ReadMemoryResults(); flashClient = ProcessManager.IsFlashClient(); Dictionary <int, HashSet <long> > newWhitelistedAddresses = null; Interlocked.Exchange(ref newWhitelistedAddresses, whiteListedAddresses); foreach (Process process in processes) { HashSet <long> whitelist; if (!newWhitelistedAddresses.TryGetValue(process.Id, out whitelist)) { continue; } IntPtr processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_WM_READ, false, process.Id); int bytesRead = 0; // number of bytes read with ReadProcessMemory foreach (long addr in whitelist) { proc_min_address = new IntPtr(addr); MEMORY_BASIC_INFORMATION mem_basic_info; VirtualQueryEx(processHandle, proc_min_address, out mem_basic_info, 28); if (mem_basic_info.Protect == PAGE_READWRITE && mem_basic_info.State == MEM_COMMIT) { if (memoryBuffer == null || memoryBuffer.Length < mem_basic_info.RegionSize) { memoryBuffer = new byte[mem_basic_info.RegionSize]; } // read everything in the buffer above ReadProcessMemory((int)processHandle, mem_basic_info.BaseAddress, memoryBuffer, mem_basic_info.RegionSize, ref bytesRead); // scan the memory for strings that start with timestamps and end with the null terminator ('\0') IEnumerable <string> timestampLines; if (!flashClient) { timestampLines = Parser.FindTimestamps(memoryBuffer, bytesRead); } else { timestampLines = Parser.FindTimestampsFlash(memoryBuffer, bytesRead); } SearchChunk(timestampLines, results); } } process.Dispose(); } FinalCleanup(results); return(results); }