[Release][Source]Eidolon Auto Link

04/10/2019 20:57 BOTCK#46
no it crashes as soon as the EAL tries to do anything. maybe someone would be so nice and fix it :) ?
04/11/2019 19:32 Bukekken#47
Is something wrong with inventory_access? It's able to inject smoothly, finish conversations and link normally, but as soon as it attempts retrieving a gift it crashes with an access violation. It also freezes for a brief period immediately after it finishes linking for the first time.

It can't be hasgift or ismeditating. Searching in Ollydbg, \x3B\x01\x7D\x1A\x0F signature still returns two addresses like before; inserting breakpoints while receiving a gift will pause the game like normal, so that's definitely the right signature, and without them you can't initiate conversations or link either, so the bitwise operations shouldn't be off.

Well, I don't really know what I'm doing since I'm still new to asm, but those are my hypotheses. I'll try figuring something out later, like removing the ability to receive gifts and seeing whether I crash at all. Granted it was at least possible to receive them before the patch.

EDIT: Something is definitely wrong with the inventory space check. Conversations normal, linking normal, wait 10 min and crash as soon as the first connection completes.

[Only registered and activated users can see links. Click Here To Register...]

If you remove the code below, then you're disabling the inventory space check during gift retrieval. No crashes, but it's not a long-term solution since Alain has said that you'll spam the server with packets if your inventory is full.


I guess I need to look into inventory_access. It's almost certain that the signature has changed since it didn't immediately crash upon retrieving gifts before the patch. That said, it used to arbitrarily crash at times and it's completely stable once you remove that code, which makes me wonder if inventory_access ever really worked.
04/12/2019 17:36 BOTCK#48
can you compile this ad hoc solution? for my purposes it is enough, my inventory is never full.
(I would have done it and shared it, but because I am not a Dev my 30days Visual Studio trail expired..)
04/12/2019 19:02 Bukekken#49
My injection.exe and eac.dll attached.
I can't remember all of the changes I've made, but for the injection.exe I've tried adding some simple nested while loops to prevent failed injection spam while also letting it still be possible to eject.

For the eac.dll, I removed the anti-gameguard since AKUS doesn't use it anymore. That was when I was wondering why my game would freeze shortly after injection - removing it didn't change anything though. Then there's that change that I mentioned earlier, removing the inventory check.

Don't ask me why the dll's filesize is so much bigger, I really have no idea. Might be the version of Visual Studio.

Some things to note:

1. It seems to crash very easily if you're changing channels (and by extension going to navea, guild hall, house, etc.) but works fine within maps on the same channel. I'm guessing that there might be something to be fixed with GetLocalPlayer somewhere.
This problem is present in the original as well, so it's best to eject before changing channels or etc. but you can essentially run dungeons, change maps and afk without being afraid of crashes.

2. It still freezes for a few seconds after the initial linking/first use, but has no major problems afterwards. On this patch, this problem is present in the original as well.

Tell me if you run into any issues since I'm honestly wondering how many of these problems are just because of my shitty PC, lol...
04/12/2019 19:17 BOTCK#50
Oh thanks a lot!
Tested: linking works , recieving works, switch to guild hall works. no crash so far.
04/12/2019 19:28 AlainProvist#51
Inventory ptr is still @[Only registered and activated users can see links. Click Here To Register...]54, but the pattern was fooled because a previous function with this pattern at occurence 2 was removed from the code. So the new occurence index is now 2 instead of 3 for INVENTORY_ACCESS_FUNCTION.

In any case the RetrieveAddresses function should have return false because the pattern is broken and detected as such, but I forgot to check the return value and display some error lol.



Edit : Don't get used to it but I fixed the code for this time. I'll update the release files later but you can grab the fix and recompile yourself now.
04/12/2019 19:56 Bukekken#52
Ahh, I had a feeling that it could've been the index. I actually went through all the occurrences for that signature before and set breakpoints in ollydbg but didn't manage to trigger a break, and it was at that point that I realized I had no idea how to dump the callstack or find the right addresses in the first place :kappa:

E.g. for eido conversations, finding the offset for changes in eidolon energy in CE, then finding the corresponding address in ollydbg, then... homework.
I've got a lot to learn.

Anyway, looks like you've already done it but yeah, changing the index in the original dll from 3 to 2 makes it work like a charm.
04/13/2019 14:45 AlainProvist#53
I performed some investigations about this shitty hanging problem when changing maps/switching channels etc...
My first guess was that the injector was the culprit because of this weird process duplication, but it seems that the only culprit is the eidolon stuff inside eal that triggers this crazyness. Compared to Skandia, and to remove some additionnal game functions and structures to maintain, I simplified the code supposed to detect we are in game and not still finishing loading. Because of this simplification, there are some frames at the end of the loading that start to perform my eidolon code while the game is not really ready to do so. And this results in this pseudo random crazy behavior. I'll see what I can do to fix this issue ;).
04/13/2019 19:55 Bukekken#54
Would it be this part in utils for GetLocalPlayer? The entity IDs load before you actually finish loading into a map, which initiates all the eido linking early?

Quote:
Finnish:
}
if(localPlayer && localPlayer->entityID == 0)
return NULL;

return localPlayer;
04/13/2019 20:50 AlainProvist#55
Quote:
Originally Posted by Bukekken View Post
Would it be this part in utils for GetLocalPlayer? The entity IDs load before you actually finish loading into a map, which initiates all the eido linking early?
Honestly, I never checked the entityID so I can't really say if it's sufficient or not.

Btw I submited a fix attempt and created a new release for binaries with both fix. I hope it will fix the problem because it's now the same code as Skandia for checking if we are currently in game or not.
04/13/2019 22:32 Bukekken#56
Played for a bit, seems like the fix breaks all eido interactions entirely atm; nothing happens after injection
04/13/2019 23:49 AlainProvist#57
rofl I forgot to remove that return...
04/15/2019 10:46 wertyl11#58
Hello Alain and everyone in this thread.
I got a bit of question regarding EAL.

I've been porting this function from the EAL source to C#
[Only registered and activated users can see links. Click Here To Register...]

This is my finnished port
[Only registered and activated users can see links. Click Here To Register...]

And you basically use it like this

Code:
static void Main(string[] args)
{
	if (!Mem.Attach("game.bin")) return;
	Constants.ProcModule main = Mem.FindProcessModule("game.bin");

	var GET_LOCAL_PLAYER = Mem.FindPattern(main, "C0 89 87 04 01", -5, 2, Constants.MemoryType.RT_REL_ADDRESS);
	var INVENTORY_ACCESS_FUNCTION = Mem.FindPattern(main, "FF 5E 5D C2 14", -13, 2, Constants.MemoryType.RT_REL_ADDRESS);
	var TARGETING_COLLECTIONS_BASE = Mem.FindPattern(main, "68 50 06 00 00", 30, 1, Constants.MemoryType.RT_ADDRESS);
	var WND_INTERFACE_BASE = Mem.FindPattern(main, "4E 24 83 B9 B0", 31, 1, Constants.MemoryType.RT_ADDRESS);
	var EUDEMON_GETEUDEMON_FUNCTION = Mem.FindPattern(main, "DB 0F 84 5E 01", -7, 1, Constants.MemoryType.RT_REL_ADDRESS);
	var EUDEMON_SENDCOMMAND_FUNCTION = Mem.FindPattern(main, "6A 04 6A 00 52", 6, 1, Constants.MemoryType.RT_REL_ADDRESS);

	var EUDEMON_SELECT_FUNCTION = Mem.FindPattern(main, "FB FF 56 8B F1", -33, 1, Constants.MemoryType.RT_LOCATION);
	var EUDEMON_ISMEDITATING_FUNCTION = Mem.FindPattern(main, "3B 01 7D 1A 0F", -17, 1, Constants.MemoryType.RT_LOCATION);
	var EUDEMON_HASGIFT_FUNCTION = Mem.FindPattern(main, "3B 01 7D 1A 0F", -17, 2, Constants.MemoryType.RT_LOCATION);

	var CURRENT_MAP_BASE = Mem.FindPattern(main, "C0 74 0D 83 3D", -10, 1, Constants.MemoryType.RT_ADDRESS);
	var DETOUR_MAIN_LOOP_OFFSET = Mem.FindPattern(main, "FF 80 BE 08 01", 1, 1, Constants.MemoryType.RT_LOCATION, true, "80 BE 08 01 00 00 00");
	var DETOUR_CRASH_HANDLER_OFFSET = Mem.FindPattern(main, "4D EC 51 6A 05", -86, 1, Constants.MemoryType.RT_LOCATION, true, "64 A1 00 00 00 00");

	Mem.Detach(true);
	Console.ReadLine();
}
It seems to "work" but when I for an example try to execute these mnemonics (from Alains source, function GetLocalPlayer)

Code:
__asm
{
	mov eax, lpBase;
	mov ecx, ds:[eax];
	test ecx, ecx;
	jz Finnish;
	call lpFunction;
	mov localPlayer, eax;
Finnish:
}
I get a crash, this is how I try to execute them in the game (Creating a remote thread and writing assembled bytes into a cave etc)

Code:
static void Main(string[] args)
		{
			if (!Mem.Attach("game.bin"))
			{
				Environment.Exit(-1);
			}

			var localPlayer = Mem.AllocateMemory(4); // Allocate 4 bytes inside process where the Localplayer will be stored within the executable
			var TARGETING_COLLECTIONS_BASE = Mem.FindPattern(main, "68 50 06 00 00", 30, 1, Constants.MemoryType.RT_ADDRESS); 
			var GET_LOCAL_PLAYER = Mem.FindPattern(main, "C0 89 87 04 01", -5, 2, Constants.MemoryType.RT_REL_ADDRESS);

			string[] array = new string[]
			{
				"use32",
				$"mov eax,{TARGETING_COLLECTIONS_BASE}",
				"mov ecx, [ds:eax]",
				"test ecx,ecx",
				"jz .Finnish"
				$"call {GET_LOCAL_PLAYER}", // This could be an issue with relative instruction, can I just move this into lets say ebx or something and do call ebx instead of address directly?
				$"mov {localPlayer.Pointer},eax" // Move contents of EAX into our allocated space so we can read from it
				".Finnish:",
				// "retn",
			}

			RemoteAllocatedMemory alloc = AllocateMemory(0x10000, MemoryProtection.ExecuteReadWrite, AllocationType.Commit); // Allocate some space to write our assembly code into
			byte[] assembled = Assemble(mnemonics); // Using FASM assembler to assemble mnemonics into bytes

			WriteBytes(alloc.Pointer.ToInt32(), assembled); // Write assembled bytes into our little code cave location
			IntPtr hThread = Native.CreateRemoteThread(MiniMem.AttachedProcess.ProcessHandle,
				IntPtr.Zero,
				IntPtr.Zero,
				alloc.Pointer, // Start of code cave
				IntPtr.Zero /* LP PARAMETER  */,
				(uint) ThreadCreationFlags.Run,
				IntPtr.Zero);

			// Here here is the crash happening
			// Here here is the crash happening
			// Here here is the crash happening

			if (hThread == IntPtr.Zero)
			{
				FreeMemory(alloc);
				return;
			}

                        WaitForSingleObject(hThread, 0xFFFFFFFF);
			CloseHandle(hThread);

			// Remote thread has finnished, read the value from 
			var localPlayerReadResult = Mem.Read<int>(localPlayer.Pointer.ToInt64());

			// Do something with localPlayerReadResult here
		} // End of Main()
What could I be doing wrong? My pattern scans do return something, but maybe they return the wrong addresses, I dont know how to compare the addresses with the results the EAL function RetrieveAddresses() gets

Sorry for wall of text, but if anyone has a clue and feel like helping out please do :)

RIP skandia :handsdown: (I am "Ohm" or "Ohmnicient" from skandia forums)

EDIT:
Here is an image of example addresses I get from my pattern scans (if it is to any help)
04/15/2019 13:25 AlainProvist#59
Try to add this function in memorybrowser.cpp and call it after RetrieveAddresses() call :
Code:
bool DumpAddressesTxt()
{
	std::string path = GetDllPath();

	std::string filename = path + "addresses.txt";

	FILE* file = fopen(filename.c_str(), "w");
	if (!file)
		return false;
	{
		char str[128];
		sprintf(str, "Version : ");
		sprintf(str, "%d.%d.%d.%d\n\n", 
			(FixedInfo.dwFileVersionMS&0xffff0000)>>16, FixedInfo.dwFileVersionMS&0xffff, 
			(FixedInfo.dwFileVersionLS&0xffff0000)>>16, FixedInfo.dwFileVersionLS&0xffff);
		fwrite(str, 1, strlen(str), file);
	}

	for(int i = 0; i < GAID_MAX; ++i)
	{
		MemorySearchEntry& entry = MemoryPatternTable[i];
		char value[16];
		sprintf(value, " 0x%08X \n", entry.Address);
		std::string str = "#define ";
		str += MemoryPatternTableStrings[i];
		str += value;
		fwrite(str.c_str(), 1, str.size(), file);
	}

	fclose(file);
	return true;
}
04/15/2019 14:32 wertyl11#60
Quote:
Originally Posted by AlainProvist View Post
Try to add this function in memorybrowser.cpp and call it after RetrieveAddresses() call :
Code:
bool DumpAddressesTxt()
{
	std::string path = GetDllPath();

	std::string filename = path + "addresses.txt";

	FILE* file = fopen(filename.c_str(), "w");
	if (!file)
		return false;
	{
		char str[128];
		sprintf(str, "Version : ");
		sprintf(str, "%d.%d.%d.%d\n\n", 
			(FixedInfo.dwFileVersionMS&0xffff0000)>>16, FixedInfo.dwFileVersionMS&0xffff, 
			(FixedInfo.dwFileVersionLS&0xffff0000)>>16, FixedInfo.dwFileVersionLS&0xffff);
		fwrite(str, 1, strlen(str), file);
	}

	for(int i = 0; i < GAID_MAX; ++i)
	{
		MemorySearchEntry& entry = MemoryPatternTable[i];
		char value[16];
		sprintf(value, " 0x%08X \n", entry.Address);
		std::string str = "#define ";
		str += MemoryPatternTableStrings[i];
		str += value;
		fwrite(str.c_str(), 1, str.size(), file);
	}

	fclose(file);
	return true;
}
OK, that definately helped and as I suspected the results of my version of your "RetrieveAddresses()" fucked something up and they returned completely wrong values :confused:

Not sure what I did wrong when i converted your function..., anyways thanks man

OK UPDATE:

I manually updated the patterns (lul)
Code:
static void Main(string[] args)
{
	if (!Mem.Attach("game.bin")) return;
	Constants.ProcModule main = Mem.FindProcessModule("game.bin");

	var GET_LOCAL_PLAYER = Mem.FindPattern(main, "8B 81 ?? ?? 00 00 85 C0 7C ??", 0, 2, Constants.MemoryType.RT_REL_ADDRESS);
	var TARGETING_COLLECTIONS_BASE = Mem.FindPattern(main, "83 3D ?? ?? ?? ?? 00 75 05 E8 ?? ?? ?? ?? 8B 0D ?? ?? ?? ?? 53 56 E8 ?? ?? ?? ?? 8B D8", 1, 2, Constants.MemoryType.RT_READNEXT4_BYTES_RAW);

	var localPlayer = Mem.AllocateMemory(4); // Allocate 4 bytes inside process where the Localplayer will be stored within the executable

	string[] array = new string[]
	{
		"use32",
		$"mov eax,{TARGETING_COLLECTIONS_BASE}",
		"mov ecx, [ds:eax]",
		"test ecx,ecx",
		"jz .Finnish",
		$"mov ebx, {GET_LOCAL_PLAYER}",
		"call ebx", // This could be an issue with relative instruction, can I just move this into lets say ebx or something and do call ebx instead of address directly?
		$"mov [{localPlayer.Pointer}],eax", // Move contents of EAX into our allocated space so we can read from it
		".Finnish:",
		"retn",
	};

	Constants.RemoteAllocatedMemory alloc = Mem.AllocateMemory(0x10000, Constants.MemoryProtection.ExecuteReadWrite, Constants.AllocationType.Commit); // Allocate some space to write our assembly code into
	byte[] assembled = Mem.Assemble(array, false); // Using FASM assembler to assemble mnemonics into bytes

	Mem.WriteBytes(alloc.Pointer.ToInt32(), assembled); // Write assembled bytes into our little code cave location
	IntPtr hThread = Native.CreateRemoteThread(Mem.AttachedProcess.ProcessHandle,
		IntPtr.Zero,
		IntPtr.Zero,
		alloc.Pointer, // Start of code cave
		IntPtr.Zero /* LP PARAMETER  */,
		(uint)Constants.ThreadCreationFlags.Run,
		IntPtr.Zero);

	if (hThread == IntPtr.Zero)
	{
		Mem.FreeMemory(alloc);
		return;
	}

	Native.WaitForSingleObject(hThread, 0xFFFFFFFF);
	Native.CloseHandle(hThread);

	// Remote thread has finnished, read the value from 
	var localPlayerReadResult = Mem.ReadMemory<int>(localPlayer.Pointer.ToInt64());

	Mem.Detach(true);
	Console.ReadLine();
}
And now it successfully reads the locaplayer address
I guess this is better than nothing