Note: This is my first post. I've been exploring memory reading for about a week now. Thanks to all who provided enough information to get me started! I haven't seen this technique documented, so hopefully it will help someone out.
I wanted to monitor chat messages from a memory reading program. But I had no idea how, couldn't find any information, and the thought of reverse-engineering WoW's chatlog memory structures seemed daunting at best.
However, it's really easy to get chat messages in a LUA add-on - and a lot of other information too. The problem is getting that information out of the add-on. Add-ons don't provide any direct means of sending data to another program, at least while WoW is running.
I've seen some hackish ways around it, where an add-on displays data on the screen in a way that can easily be read by an external program. For example, creating a fixed grid of 1-pixel boxes, and changing their color to send data. Or converting the data to binary, then to a string where ones are represented by periods and zeroes by spaces, then displaying that in a textbox. But neither of these methods is suited for transferring large amounts of data. They are also prone to breakage if something covers that region of the screen, or if screen resolution changes.
What we need is a more direct method. We're memory reading, so why not read LUA variables directly?
LUA has two basic data types: strings and floating point numbers.
In LUA, you cannot change part of an existing string variable, you can only assign it a new value. This means it's likely that the string will be stored at a new address each time it's changed. That's no good for us.
But an array of floating point numbers holds promise. We can put a series of unique numbers at the beginning of the array, which gives a memory reading program a way to find the array. The rest of the array can store data we want to pass.
Let's get down to business. I loaded the simplest chat add-on I could find as a template. Then I added this to the beginning:
That's going to be our array. In the add-on initialization function, I then added:Code:local chatbuf;
Line 1 makes chatbuf an array. Line two makes it have 20,000 elements (from 1-20,000), since LUA automatically expands arrays when you set an element outside its existing bounds. Lines 4-7 initialize all elements to zero. Lines 8-10 assign a unique series of numbers to the beginning of the array, that our memory reader can search for to find the array. Note that I assigned these numbers in reverse, so the memory reader won't accidentally find these numbers in the proper order in our code, instead of the array.Code:chatbuf={}; chatbuf[20000]=0; local l1; for l1=1,20000,1 do chatbuf[l1]=0; incptr(); end chatbuf[3]=3494.38; chatbuf[2]=1982.44; chatbuf[1]=9918.71;
When we run this add-on, we'll have a nice neat array in memory, and we can easily find the beginning of it by searching for the unique numbers in the proper sequence. Each number is stored as an eight-byte double-precision float. Each array element is spaced 16 bytes apart.
For my purpose of monitoring chat messages, I implemented a ring buffer. chatbuf[4] holds the head pointer. chatbuf[5-20000] holds the actual chat messages as well as some extra formatted data and delimiters, each character stored as a number.
I added another global variable:
A function:Code:local chatptr=5;
And at the beginning of the existing event handler that receives chat messages:Code:function incptr() chatptr=chatptr+1; if chatptr == 20001 then chatptr=5 end end
As for the memory reading program, I'm including only a brief description. On startup, it locates the start of the array, and reads the head pointer. Any time that pointer changes, it reads data until it catches up with the new pointer. Then it parses the data to break it into lines and retrieve the Dat, r, g, and b variables.Code:local msg2="[Dat:"..id..","..r..","..g..","..b.."]"..msg; local l1; for l1=1,strlen(msg2),1 do chatbuf[chatptr]=strbyte(msg2,l1); incptr(); end chatbuf[chatptr]=13; incptr(); chatbuf[chatptr]=10; incptr(); chatbuf[4]=chatptr;
That's it! This technique can be used to pass lots of other data too, which is easily accessible in a LUA add-on, but hard to get with memory reading alone.
Although I'm moving on to other challenges at the moment, here's something something interesting I'll be looking into later. It may interest you as well, and if you explore it before I do, please reply.
Note that the array elements are stored 16 bytes apart, whereas the actual value only takes up 8 bytes. LUA arrays aren't typed and are heterogenous, meaning you can store a number or a string to any element. It's likely those 16 bytes contain a flag describing what type of data is stored in each element. In the case of a number, the actual number is stored; but in the case of a string, the string pointer may be stored. This might allow a more direct method of passing strings to a memory reading program.