public String fetchRoflDownloadUrl(UInt64 matchId) { IntPtr funcAddr = _leagueClientMemoryEditor.AllocateMemory(asm.Length); //Structs not packed so 0x18 instead of 0x14 and 0xC is skipped IntPtr paramsAddr = _leagueClientMemoryEditor.AllocateMemory(0x18); //Write our custom function _leagueClientMemoryEditor.WriteBytes(funcAddr, asm); //Function Address is the address which our custom asm calls _leagueClientMemoryEditor.WriteUInt32(paramsAddr, _functionAddress); //Class Address is ECX, a class which is initialized which gets passed in to above function _leagueClientMemoryEditor.WriteUInt32(paramsAddr + 0x4, _classAddress); //Successful call will place a ptr to the download link here _leagueClientMemoryEditor.WriteUInt32(paramsAddr + 0x8, 0); //+16 (0x10) not +12 (0xC) because it wasnt packed, MatchID is a MatchID _leagueClientMemoryEditor.WriteUInt64(paramsAddr + 0x10, matchId); //Call Func and wait for completion _leagueClientMemoryEditor.WaitForSingleObject(_leagueClientMemoryEditor.CreateRemoteThread(funcAddr, paramsAddr)); IntPtr downloadUrlIntPtr; //Read the download Ptr _leagueClientMemoryEditor.ReadIntPtr(paramsAddr + 0x8, out downloadUrlIntPtr); String downloadUrl = ""; //If its null then call failed, Match Might not exist, User might not be logged in etc if (downloadUrlIntPtr != IntPtr.Zero) { _leagueClientMemoryEditor.ReadNullTerminatedString(downloadUrlIntPtr, out downloadUrl); } //Free Memory of the ones we allocated and also the injected function //TODO: Get LeagueClient to free the memory of the download URL though this is minor, though still a memory leak //TODO: Potentially just allocated and reuse memory once? _leagueClientMemoryEditor.FreeMemory(funcAddr); _leagueClientMemoryEditor.FreeMemory(paramsAddr); return(downloadUrl); }