Page 4 of 6
Posted: Sun Mar 02, 2003 1:58 pm
by L124RD
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.
Posted: Sun Mar 02, 2003 2:07 pm
by Mckorr
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.)
Posted: Sun Mar 02, 2003 2:11 pm
by L124RD
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...
Posted: Sun Mar 02, 2003 2:26 pm
by Mckorr
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.
Posted: Sun Mar 02, 2003 7:08 pm
by dont_know_at_all
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?
Posted: Sun Mar 02, 2003 8:57 pm
by Stargazer
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!
Posted: Mon Mar 03, 2003 12:30 pm
by eq_freak
MQ detours:
- WriteChatColor(004DF524)
- Commands(004E7F38 according to Ohmz post)
- The dinput function GetDeviceData():
(*EQADDR_DIKEYBOARD)->lpVtbl->GetDeviceData
EDADDR_DIKEYBOARD=0073977C
Posted: Mon Mar 03, 2003 4:24 pm
by Stargazer
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.
Posted: Mon Mar 03, 2003 5:04 pm
by dont_know_at_all
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;
}
Posted: Mon Mar 03, 2003 5:25 pm
by eq_freak
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.
Posted: Mon Mar 03, 2003 5:36 pm
by Clawed
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).
Posted: Mon Mar 03, 2003 6:11 pm
by Clawed
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. :) )
Posted: Mon Mar 03, 2003 6:11 pm
by ap50
Way to go peeps!..
I hears no fat ladies singin'
Posted: Mon Mar 03, 2003 6:21 pm
by Vendor001
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).
Posted: Mon Mar 03, 2003 6:54 pm
by Clawed
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.