Educated opinions on new Test server anti-MQ code

A forum for feature requests/discussions and user submitted patches that improve MQ2

Moderator: MacroQuest Developers

User avatar
L124RD
Site Admin
Site Admin
Posts: 1343
Joined: Fri Jun 14, 2002 12:15 am
Location: Cyberspace
Contact:

Post by L124RD » Sun Mar 02, 2003 1:58 pm

Salutations,
Remember, /target changes memory, and there is a memory check. even if it is not actively checked, if this is implemented there will be a patch to break it.

Mckorr
Developer
Developer
Posts: 2326
Joined: Fri Oct 18, 2002 1:16 pm
Location: Texas

Post by Mckorr » Sun Mar 02, 2003 2:07 pm

The latest patch (2-26) checks for changes in the following address ranges:
468769-472BAA
472BB6-473177
473183-47A326
47B798-47E940
4AD046-4C8E55
497321-4A8EDB
4C930B-4CA6C7
4CC442-4EA4C3
Changing any of these offsets will get you booted.

This from cromwell at forever-hacking.net (credit where credit is due.) Looks like all the [Function Locations] fall in this range (if I'm finding the offsets correctly.)

User avatar
L124RD
Site Admin
Site Admin
Posts: 1343
Joined: Fri Jun 14, 2002 12:15 am
Location: Cyberspace
Contact:

Post by L124RD » Sun Mar 02, 2003 2:11 pm

Salutations,
Really? when I did a execution breakpoint inside the offset check that outputted the offset it was reading and where that offset was, it outputted the whole fuggin' EQgame.exe to my screen...

Mckorr
Developer
Developer
Posts: 2326
Joined: Fri Oct 18, 2002 1:16 pm
Location: Texas

Post by Mckorr » Sun Mar 02, 2003 2:26 pm

Eh... not my information, I haven't had time to work on it.

It is known that certain hacks will work, and certain one's won't. Of course I won't go into that here (don't use them anyway, just keep track of the state of things.) The one's that cause a boot fall into those address ranges apparently... which I can't verify without risking my account, which I'm not gonna do.

Just thought the info might be useful. If it's correct it's not reading memory that causes the boots, it's injecting commands, which includes most (all?) MQ functions.

User avatar
dont_know_at_all
Developer
Developer
Posts: 5450
Joined: Sun Dec 01, 2002 4:15 am
Location: Florida, USA
Contact:

Post by dont_know_at_all » Sun Mar 02, 2003 7:08 pm

L124RD wrote:Salutations,
Really? when I did a execution breakpoint inside the offset check that outputted the offset it was reading and where that offset was, it outputted the whole fuggin' EQgame.exe to my screen...
What is the offset of the memory check routine, please?

User avatar
Stargazer
a lesser mummy
a lesser mummy
Posts: 32
Joined: Mon Sep 30, 2002 3:30 pm

Post by Stargazer » Sun Mar 02, 2003 8:57 pm

The memory check routine that Mckorr was referring to and that I posted at FH is at 4F7D24 in the 2/26 version of eqgame.exe. I am not sure this is the one causes MQ problems but at this point it would probably be a good guess unless anyone else has found out anything different. I wouldn't think SOE would bother to implement a second section of memory checking code when they already had one in place. Does anyone have any ideas exactly what memory locations would be changed by MQ in the 2/26 version of eqgame? That would help me in trying to figure this out.

L124RD was this the address of the memory check routine you were looking at?

Thanks!
Cromwell Stargazer

eq_freak
a ghoul
a ghoul
Posts: 105
Joined: Mon Jun 24, 2002 7:17 am

Post by eq_freak » Mon Mar 03, 2003 12:30 pm

MQ detours:
- WriteChatColor(004DF524)
- Commands(004E7F38 according to Ohmz post)
- The dinput function GetDeviceData():
(*EQADDR_DIKEYBOARD)->lpVtbl->GetDeviceData
EDADDR_DIKEYBOARD=0073977C

User avatar
Stargazer
a lesser mummy
a lesser mummy
Posts: 32
Joined: Mon Sep 30, 2002 3:30 pm

Post by Stargazer » Mon Mar 03, 2003 4:24 pm

Thanks eq_freak. That makes alot of sense now. The memory check for the 4CC442-4EA4C3 range was essentially added in the patch last Monday when MQ stopped working. Looks like this area does get two of the MQ offsets plus a couple of the FH offsets that had been missed. If the FH board comes up with a work around it might also be of some benefit to MQ users.
Cromwell Stargazer

User avatar
dont_know_at_all
Developer
Developer
Posts: 5450
Joined: Sun Dec 01, 2002 4:15 am
Location: Florida, USA
Contact:

Post by dont_know_at_all » Mon Mar 03, 2003 5:04 pm

Okay, I have re-engineered the memory checker routine. I don't have the disk space to build MQ on my home PC. The memory checker routine takes three params, buffer, count, and key. They use a single pad encryption scheme to prevent automatic packet returns.

Anyway, all that needs to be done is:
1. Any buffer references that we detour need to be replaced with the correct (unmodified) bytes. To follow up a previous thread, it appears that they can check any address range but it is probably configured on the server. This is easy, though, because there is only one place where they read the memory.
2. The encription pad table needs to be added as an offset. The variable extern_array below is this table.
3. The real memory checker needs to be detoured to our memory checker.

I have done some unit checks on this routine and it seems to work correctly.

Here's the code:

Code: Select all

//  004F7D24: 55                 push        ebp
//  004F7D25: 8B EC              mov         ebp,esp
//  004F7D27: 8A 45 10           mov         al,byte ptr [ebp+10h]
//  004F7D2A: 56                 push        esi

struct mckey {
    union {
        int x;
        unsigned char a[4];
    };
};

extern unsigned int extern_array[];


int memcheck(unsigned char *buffer, int count, struct mckey key)
{
    unsigned int x, i;
    unsigned int ecx;
    unsigned int eax = ~key.a[0] & 0xff;

//  004F7D2B: F6 D0              not         al
//  004F7D2D: 0F B6 C0           movzx       eax,al
    unsigned int edx = key.a[1] & 0xff;
//  004F7D30: 0F B6 55 11        movzx       edx,byte ptr [ebp+11h]
//  004F7D34: 8B 04 85 6C 7B 5C  mov         eax,dword ptr [eax*4+005C7B6Ch]
//            00

    eax = extern_array[eax];

//  004F7D3B: BE FF FF FF 00     mov         esi,0FFFFFFh
//  004F7D40: 33 C6              xor         eax,esi
    eax ^= 0xffffff;

//  004F7D42: 57                 push        edi
//  004F7D43: 8B C8              mov         ecx,eax
//  004F7D45: BF FF 00 00 00     mov         edi,0FFh

//  004F7D4A: 23 CF              and         ecx,edi
    ecx = eax & 0xff; 
//  004F7D4C: 33 CA              xor         ecx,edx
    ecx ^= edx;
//  004F7D4E: 0F B6 55 12        movzx       edx,byte ptr [ebp+12h]
    edx = key.a[2] & 0xff;

//  004F7D52: 8B 0C 8D 6C 7B 5C  mov         ecx,dword ptr [ecx*4+005C7B6Ch]
//            00
    ecx = extern_array[ecx];

//  004F7D59: C1 F8 08           sar         eax,8
//  004F7D5C: 23 C6              and         eax,esi
    eax = ((int)eax>>8) & 0xffffff;

//  004F7D5E: 33 C8              xor         ecx,eax
    ecx ^= eax;

//  004F7D60: 8B C1              mov         eax,ecx
//  004F7D62: 23 C7              and         eax,edi
//  004F7D64: 33 C2              xor         eax,edx
    eax = (ecx & 0xff) ^ edx;

//  004F7D66: C1 F9 08           sar         ecx,8
    ecx = (int)ecx >> 8;

//  004F7D69: 8B 14 85 6C 7B 5C  mov         edx,dword ptr [eax*4+005C7B6Ch]
//            00
    edx = extern_array[eax];

//  004F7D70: 23 CE              and         ecx,esi
//  004F7D72: 33 D1              xor         edx,ecx
    edx ^= ecx & 0xffffff;
    
//  004F7D74: 0F B6 4D 13        movzx       ecx,byte ptr [ebp+13h]
    ecx = key.a[3] & 0xff;

//  004F7D78: 8B C2              mov         eax,edx
//  004F7D7A: 23 C7              and         eax,edi
//  004F7D7C: 33 C1              xor         eax,ecx
//  004F7D7E: 8B 4D 08           mov         ecx,dword ptr [ebp+8]
//  004F7D81: C1 FA 08           sar         edx,8
//  004F7D84: 8B 04 85 6C 7B 5C  mov         eax,dword ptr [eax*4+005C7B6Ch]
//            00

    eax = extern_array[(edx & 0xff) ^ ecx];

//  004F7D8B: 23 D6              and         edx,esi
//  004F7D8D: 33 C2              xor         eax,edx
    eax ^= ((int)edx>>8) & 0xffffff;

//  004F7D8F: 8B 55 0C           mov         edx,dword ptr [ebp+0Ch]
//  004F7D92: 03 D1              add         edx,ecx
//  004F7D94: 89 4D 10           mov         dword ptr [ebp+10h],ecx
//  004F7D97: 3B CA              cmp         ecx,edx
//  004F7D99: 73 24              jae         004F7DBF
    if (count == 0) return eax;

//  004F7D9B: 53                 push        ebx
//  004F7D9C: 8B 5D 10           mov         ebx,dword ptr [ebp+10h]
//  004F7D9F: 8B C8              mov         ecx,eax
//  004F7DA1: 23 CF              and         ecx,edi
//  004F7DA3: 0F B6 1B           movzx       ebx,byte ptr [ebx]
//  004F7DA6: 33 CB              xor         ecx,ebx
//  004F7DA8: C1 F8 08           sar         eax,8
//  004F7DAB: 8B 0C 8D 6C 7B 5C  mov         ecx,dword ptr [ecx*4+005C7B6Ch]
//            00
//  004F7DB2: 23 C6              and         eax,esi
//  004F7DB4: 33 C1              xor         eax,ecx
//  004F7DB6: FF 45 10           inc         dword ptr [ebp+10h]
//  004F7DB9: 39 55 10           cmp         dword ptr [ebp+10h],edx
//  004F7DBC: 72 DE              jb          004F7D9C

//  004F7DBE: 5B                 pop         ebx
//  004F7DBF: 5F                 pop         edi
//  004F7DC0: 5E                 pop         esi
//  004F7DC1: F7 D0              not         eax
//  004F7DC3: 5D                 pop         ebp
//  004F7DC4: C3                 ret	
    for (i=0;i<count;i++) {
        x = (int)buffer[i] ^ (eax & 0xff);
        eax = ((int)eax >> 8) & 0xffffff;
        x = extern_array[x];
        eax ^= x;
    }
    return ~eax;
}


eq_freak
a ghoul
a ghoul
Posts: 105
Joined: Mon Jun 24, 2002 7:17 am

Post by eq_freak » Mon Mar 03, 2003 5:25 pm

Hehe, nice job on the reversing. Anyway the range of addresses posted above seem to come from the following(just a snippet of it):

Code: Select all

* Referenced by a CALL at Addresses:
|:00491C55   , :004FC988   
|
:004FC7F1 56                      push esi
:004FC7F2 57                      push edi
:004FC7F3 8B7C240C                mov edi, dword ptr [esp+0C]
:004FC7F7 B9C3A44E00              mov ecx, 004EA4C3
:004FC7FC B842C44C00              mov eax, 004CC442
:004FC801 57                      push edi
:004FC802 2BC8                    sub ecx, eax
:004FC804 51                      push ecx
:004FC805 50                      push eax
:004FC806 E819B5FFFF              call 004F7D24
:004FC80B 8BF0                    mov esi, eax
:004FC80D 8BC7                    mov eax, edi
:004FC80F F7D0                    not eax
:004FC811 33F0                    xor esi, eax
:004FC813 B9AA2B4700              mov ecx, 00472BAA
:004FC818 B869874600              mov eax, 00468769
:004FC81D 57                      push edi
:004FC81E 2BC8                    sub ecx, eax
:004FC820 51                      push ecx
:004FC821 50                      push eax
:004FC822 E8FDB4FFFF              call 004F7D24
So basically it goes(correct me when I am wrong, my assembler knowledge is very rusty :p):

Load (something) into edi (what?), push it on stack
Load end adress of memory range to check into ecx
Load start adress of memory range to check into eax
Subtract start from end, and push the result(Length of area to check) on stack
Push eax on stack (start adress)
Call the memcheck routine.

Clawed
a ghoul
a ghoul
Posts: 105
Joined: Mon Jan 20, 2003 6:17 am

Post by Clawed » Mon Mar 03, 2003 5:36 pm

Today I was able to duplicate what eq_freak had been doing quite easily. I simply disabled the HookXXX functions and put a log file poller into the main execution loop. It passes any command it gets (after stripping the timestamp) to DoCommand. So far I've tested /who and /items, both work perfectly and exactly as they used to. I haven't had to modify any of the actual command functions yet (i.e. SuperWho), since we have the offsets for what is needed so far.

I am going to disable all commands that won't work at this point, essentially leaving me with a stripped-down MQ that requires a /note in front of every command.

This was extremely easy to do for someone with little to no knowledge of offset hacking, just good C++ skills.

So, until we get this memory checker bypassed (which I am confident is a relatively easy prospect), I will happily use this version. Thanks to eq_freak for the general concept, and to Ohmz for the offsets, which I am unable to generate myself (having no backups of earlier executables, or any decent debugging software installed).
Clawed

Clawed
a ghoul
a ghoul
Posts: 105
Joined: Mon Jan 20, 2003 6:17 am

Post by Clawed » Mon Mar 03, 2003 6:11 pm

Did some more testing. It seems that /target works perfectly, since it doesn't require a hook (or in Microsoft-speak, a "detour"). So far the only stuff that won't work is any DirectInput code (keyboard/mouse stuff), and /face. But since /who returns the direction and distance to the spawn, it's quite easy to find it on your own.

Also, I was in error when I said that /who worked exactly as it used to -- it does not identify which guilds players are in. Other than that the functionality is intact.

If anybody is following this and still doesn't fully understand what it was that Sony disabled when they broke MQ, here's a summary. There are 3 things that MQ needs to "hook" into in order to function:

1. Whenever the mouse or keyboard is used
2. When any text is sent to the chat window
3. When you type a command in

Those three areas must be disabled for MacroQuest to work without tripping the memory checking routine. Any MQ function that does NOT require those three areas will work just fine, once you've found a way to input commands (as we've done with the notes.txt file). Therefore /who and /target work fine, since all we're doing is reading or modifying data structures -- not code.

Code: Select all

pTarget = EQADDR_TARGET;
*pTarget = pSpawnClosest;
That sets your target. It doesn't require a detour, it doesn't modify any code, and it can NOT be detected with Sony's current scheme. (If I'm wrong, I'm sure you fine people here will be quick to correct me. Please do. :) )
Last edited by Clawed on Mon Mar 03, 2003 7:00 pm, edited 1 time in total.
Clawed

User avatar
ap50
a snow griffon
a snow griffon
Posts: 425
Joined: Sun Aug 18, 2002 2:29 pm

Post by ap50 » Mon Mar 03, 2003 6:11 pm

Way to go peeps!..

I hears no fat ladies singin'
[color=yellow][size=92][b]Just because you're paranoid, it doesn't mean everyone isn't out to get you![/b][/size][/color]

Vendor001
Cheezily Banned
Cheezily Banned
Posts: 78
Joined: Wed Nov 13, 2002 1:37 pm

Post by Vendor001 » Mon Mar 03, 2003 6:21 pm

Could we also set MQ up to have a "events" type log file to watch?

That way it would still be able monitor in-game events as well as process commands.


BTW, am I correct in assuming the char(hp, mana, etc) commands still work? We would all die w/o our Super-Duper-Uber-Cleric-Bot(tm).

Great work guys! Keep the hope alive!(and Sony a paycheck).

Clawed
a ghoul
a ghoul
Posts: 105
Joined: Mon Jan 20, 2003 6:17 am

Post by Clawed » Mon Mar 03, 2003 6:54 pm

Yes, /mana and the like work just fine.

You could get events to work by monitoring your character's log file, but I doubt it would be worth the effort at this point since I think we'll get the memory sniffer bypassed soon. But yes, it could be done.
Last edited by Clawed on Mon Mar 03, 2003 7:16 pm, edited 1 time in total.
Clawed