Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Top Posters

Who's Online (0)

Powered by Vanilla. Made with Bootstrap.
Creating a pseudo MMO-bot/dual-boxing tool in C#
  • sangf
    Posts: 203
    i used to play the game Lineage II quite a bit, and had an idea for a simple way to create a dual boxing tool which can also double as a 'bot'. i quote that because using this method it'd be overly difficult to create something that would play the game for you - but it's a perfect solution to automated support characters. just figured i'd share it here while i wait for an ep of ano hana to dl. it's nothing too special, but if you ever needed an auto-buff/auto-assist bot without the hastle; this might just help.

    the method is simple: send key/mouse input to the target window using win32 api's PostMessage. yeah, that's pretty much it - an obvious way to send virtual input to a game window but i've never seen it done generally so here's some methods to help. as an idea to using this method effectively, i think mapping unused keys to trigger the 'bot' or dual box targets performance is the best bet, and certainly the easiest. for example you might have numeric keys 0 through 9 activate certain skills by simply sending the hotkeys associated to those skills using PostMessage.. games that support macros or text/chat commands will allow more flexibility for automation, but you get the idea.

    to make use of the PostMessage function you'll need to specify a dllimport thingie (i don't know a name for it) like so -- note: this depends on the module/import/whatever the .NET terminology is called System.Runtime.InteropServices


    [DllImport(\"user32.dll\", EntryPoint = \"PostMessage\")]
    public static extern int PostMessage(IntPtr hWnd, uint Msg, uint wParam, uint lParam);


    before using the function you need a handle to the window, which can be done using the process class in System.Diagnostics -- here is an example using a partial text search to match the title of a window.


    IntPtr win_handle; // Store found window handle here
    string win_title = \"Lineage \";
    foreach (Process p in Process.GetProcesses())
    {
    if (p.MainWindowTitle.Contains(win_title))
    {
    win_handle = p.MainWindowHandle;
    break;
    }
    }


    then the PostMessage function can be used as follows, assuming i didn't forget something silly - this will send a keydown event for the F1 key at the target window.


    PostMessage(win_handle, 0x100, (uint) Keys.F1, 0);


    note: 0x100 specifies the mouse down event, but using magic numbers is never reccommended, so here's a list that might help:


    // Virtual Messages
    public enum WMessages : int
    {
    WM_LBUTTONDOWN = 0x201, //Left mousebutton down
    WM_LBUTTONUP = 0x202, //Left mousebutton up
    WM_LBUTTONDBLCLK = 0x203, //Left mousebutton doubleclick
    WM_RBUTTONDOWN = 0x204, //Right mousebutton down
    WM_RBUTTONUP = 0x205, //Right mousebutton up
    WM_RBUTTONDBLCLK = 0x206, //Right mousebutton doubleclick
    WM_KEYDOWN = 0x100, //Key down
    WM_KEYUP = 0x101, //Key up
    WM_CHAR = 0x0102
    }


    you'll notice that some of these events relate to mouse processing too - pretty much anything is possible, but it might involve some playing around with the PostMessage parameters, just take a look here or google using the appropriate event type to figure that out. anyway that's about it for now, i'll clean this up another day but for now.. i'll just leave some key constants incase they're needed (Keys.* should be fine for C# though).


    // Virtual Keys
    public enum VKeys : int
    {
    VK_LBUTTON = 0x01, //Left mouse button
    VK_RBUTTON = 0x02, //Right mouse button
    VK_CANCEL = 0x03, //Control-break processing
    VK_MBUTTON = 0x04, //Middle mouse button (three-button mouse)
    VK_BACK = 0x08, //BACKSPACE key
    VK_TAB = 0x09, //TAB key
    VK_CLEAR = 0x0C, //CLEAR key
    VK_RETURN = 0x0D, //ENTER key
    VK_SHIFT = 0x10, //SHIFT key
    VK_CONTROL = 0x11, //CTRL key
    VK_MENU = 0x12, //ALT key
    VK_PAUSE = 0x13, //PAUSE key
    VK_CAPITAL = 0x14, //CAPS LOCK key
    VK_ESCAPE = 0x1B, //ESC key
    VK_SPACE = 0x20, //SPACEBAR
    VK_PRIOR = 0x21, //PAGE UP key
    VK_NEXT = 0x22, //PAGE DOWN key
    VK_END = 0x23, //END key
    VK_HOME = 0x24, //HOME key
    VK_LEFT = 0x25, //LEFT ARROW key
    VK_UP = 0x26, //UP ARROW key
    VK_RIGHT = 0x27, //RIGHT ARROW key
    VK_DOWN = 0x28, //DOWN ARROW key
    VK_SELECT = 0x29, //SELECT key
    VK_PRINT = 0x2A, //PRINT key
    VK_EXECUTE = 0x2B, //EXECUTE key
    VK_SNAPSHOT = 0x2C, //PRINT SCREEN key
    VK_INSERT = 0x2D, //INS key
    VK_DELETE = 0x2E, //DEL key
    VK_HELP = 0x2F, //HELP key
    VK_0 = 0x30, //0 key
    VK_1 = 0x31, //1 key
    VK_2 = 0x32, //2 key
    VK_3 = 0x33, //3 key
    VK_4 = 0x34, //4 key
    VK_5 = 0x35, //5 key
    VK_6 = 0x36, //6 key
    VK_7 = 0x37, //7 key
    VK_8 = 0x38, //8 key
    VK_9 = 0x39, //9 key
    VK_A = 0x41, //A key
    VK_B = 0x42, //B key
    VK_C = 0x43, //C key
    VK_D = 0x44, //D key
    VK_E = 0x45, //E key
    VK_F = 0x46, //F key
    VK_G = 0x47, //G key
    VK_H = 0x48, //H key
    VK_I = 0x49, //I key
    VK_J = 0x4A, //J key
    VK_K = 0x4B, //K key
    VK_L = 0x4C, //L key
    VK_M = 0x4D, //M key
    VK_N = 0x4E, //N key
    VK_O = 0x4F, //O key
    VK_P = 0x50, //P key
    VK_Q = 0x51, //Q key
    VK_R = 0x52, //R key
    VK_S = 0x53, //S key
    VK_T = 0x54, //T key
    VK_U = 0x55, //U key
    VK_V = 0x56, //V key
    VK_W = 0x57, //W key
    VK_X = 0x58, //X key
    VK_Y = 0x59, //Y key
    VK_Z = 0x5A, //Z key
    VK_NUMPAD0 = 0x60, //Numeric keypad 0 key
    VK_NUMPAD1 = 0x61, //Numeric keypad 1 key
    VK_NUMPAD2 = 0x62, //Numeric keypad 2 key
    VK_NUMPAD3 = 0x63, //Numeric keypad 3 key
    VK_NUMPAD4 = 0x64, //Numeric keypad 4 key
    VK_NUMPAD5 = 0x65, //Numeric keypad 5 key
    VK_NUMPAD6 = 0x66, //Numeric keypad 6 key
    VK_NUMPAD7 = 0x67, //Numeric keypad 7 key
    VK_NUMPAD8 = 0x68, //Numeric keypad 8 key
    VK_NUMPAD9 = 0x69, //Numeric keypad 9 key
    VK_SEPARATOR = 0x6C, //Separator key
    VK_SUBTRACT = 0x6D, //Subtract key
    VK_DECIMAL = 0x6E, //Decimal key
    VK_DIVIDE = 0x6F, //Divide key
    VK_F1 = 0x70, //F1 key
    VK_F2 = 0x71, //F2 key
    VK_F3 = 0x72, //F3 key
    VK_F4 = 0x73, //F4 key
    VK_F5 = 0x74, //F5 key
    VK_F6 = 0x75, //F6 key
    VK_F7 = 0x76, //F7 key
    VK_F8 = 0x77, //F8 key
    VK_F9 = 0x78, //F9 key
    VK_F10 = 0x79, //F10 key
    VK_F11 = 0x7A, //F11 key
    VK_F12 = 0x7B, //F12 key
    VK_SCROLL = 0x91, //SCROLL LOCK key
    VK_LSHIFT = 0xA0, //Left SHIFT key
    VK_RSHIFT = 0xA1, //Right SHIFT key
    VK_LCONTROL = 0xA2, //Left CONTROL key
    VK_RCONTROL = 0xA3, //Right CONTROL key
    VK_LMENU = 0xA4, //Left MENU key
    VK_RMENU = 0xA5, //Right MENU key
    VK_PLAY = 0xFA, //Play key
    VK_ZOOM = 0xFB, //Zoom key
    }

  • m0rph
    Posts: 332
    nice dude! I've never seen a tutorial on making any kind of game bot before! so basically you have a series of events that your bot accomplishes:

    1. It searches for any process that has the window title "Lineage "
    2. It then defines it's own map for any input
    3. Then we have to decide what input we want to send

    I guess the only question I have is, if we wanted the bot to perform a specific action, like attack an enemy for example. What would we have to do to make it be able to search for enemies? Not looking for code on this one, more of just concepts. Designing any kind of bot has always been an interest of mine.
    while( !(succeed = try() ) );
  • sangf
    Posts: 203
    yeash~ i should have probably included a sample for hooking keyboard/mouse activity globally - so here's a keyboard one, and here's one for the mouse~. and making it attack on it's own would be difficult - when i thought of this i intended for it to be mainly for assistance purposes. say for example, a main character you play would send a signal to background clients to assist when attacking.

    but, for making it attack automatically you'd probably need to do some reverse engineering in order to get the data you need (something you can identify nearby enemies with, current HP/MP if you wanted to make use of auto-heal/run, and current location or some alternative of those). cheat engine might help you finding the useful offsets you need which can be read by the bot program (i'll find a sample for this soon, too).
  • sangf
    Posts: 203
    i recently started working on something related to this, so here's some information/a tutorial about how you can get data from the game using C# and cheat engine (i'm using 6.1).

    i'm going to get the currently logged account name from the game Lineage II. so to start, open the process, log in game and search for the account name by text (unicode if necessary). there will often be more than one result, and it can sometimes be difficult to find the correct result, if games have complex protection (especially for memory) - cheatengine tutorials/documentation/community can help here.

    http://i.imgur.com/lVCbE.jpg

    you can see, i found 3 addresses containing the values i searched for, but one of them is displayed in green. this means we have an easy job, because this address is most likely constant; meaning it won't change the next time the game is loaded to memory. if we double click that address to add it to our address table, and then double click the address section of the entry in that table it will give us an offset address (offset from processes base address, or a module loaded by the processes base address). this is important, because there will likely not be an absolute address (it will change with each new game instance loaded in most cases).

    http://i.imgur.com/uuQWO.jpg

    cool, so now we should be able to constantly get the account name without knowing it by finding the base address of Engine.dll loaded by the main process and combining it with the offset to read memory from the process!

    starting inside the loop for processes in the previous example where p is our process, here we get the base address of the Engine.dll module (note that our application should be built for the same architecture (ie. x86/x64), or there might be complications with the modules retrieved):


    int base_address = -1;
    for (int cur_mod = 0; cur_mod < p.Modules.Count; cur_mod++)
    {
    if (p.Modules[cur_mod].ModuleName == \"Engine.dll\")
    {
    base_address = p.Modules[cur_mod].BaseAddress.ToInt32();
    break;
    }
    }


    assuming the base_address was found (>0) we can now just read memory from the base_address + offset. we need to first add another dllimport thingie (we need a winapi function to accomplish this (ReadProcessMemory()).


    [DllImport(\"kernel32.dll\")]
    public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [in, out] byte[] lpBuffer, unit dwSize, out IntPtr lpNumberOfBytesRead);


    the string was found in Unicode (UTF-16) in this particular instance, so here's how we read it. Using normal strings, you can just replace 'Unicode'with 'UTF8'.


    IntPtr bytes_read, offset;
    offset = (IntPtr) base_address + 0x6CBE74; // read from this loc
    byte[] buffer = new byte[32]; // 32 bytes to store what we read!
    ReadProcessMemory(p.Handle, offset, buffer, 32, out bytes_read);

    // and, here we have our final account name as a string (might need some checking that it is valid before-hand)
    string account_name = Encoding.Unicode.GetString(buffer);


    and that's the general idea of this technique, there are some decent memory libraries around which make this a bit easier (ie. functions to find base addresses, processes, read data from memory and write data to memory, etc, etc..).

    http://www.codeproject.com/KB/cs/sojane ... anner.aspx
    http://forum.cheatengine.org/viewtopic.php?p=5185623