mirror of
https://github.com/barkeser2002/ripcord-fix.git
synced 2026-09-25 02:29:57 +03:00
289 lines
11 KiB
C
289 lines
11 KiB
C
enum {
|
||
ErfMapAny_Num = 2,
|
||
};
|
||
|
||
enum {
|
||
DisChannelType_Voice = 2,
|
||
DisChannelType_Stage = 13,
|
||
DisChannelType_GuildText = 0,
|
||
DisChannelType_DM = 1,
|
||
DisChannelType_GuildCategory = 4,
|
||
DisChannelType_GuildNews = 5,
|
||
DisChannelType_GuildStore = 6,
|
||
DisChannelType_GuildNewsThread = 10,
|
||
DisChannelType_GuildPublicThread = 11,
|
||
DisChannelType_GuildPrivateThread = 12,
|
||
DisChannelType_GuildStageVoice = 19,
|
||
};
|
||
|
||
typedef struct {
|
||
u32 tag;
|
||
u32 _unk;
|
||
u32 num;
|
||
} ErfMapAny;
|
||
|
||
// Heartbeat ve Resume mekanizması için değişkenler
|
||
#define SESSION_ID_LEN 64
|
||
#define MAX_HEARTBEAT_INTERVAL 47000 // 47 saniye (ms)
|
||
#define MIN_HEARTBEAT_INTERVAL 41000 // 41 saniye (ms)
|
||
|
||
static char saved_session_id[SESSION_ID_LEN] = {0};
|
||
static u64 last_heartbeat_time = 0;
|
||
static u32 heartbeat_seq = 0;
|
||
static u8 resume_attempted = 0;
|
||
|
||
// WebSocket hook'ları için fonksiyon pointerları
|
||
typedef i64 (__cdecl* SendTextMessageType)(void* this, const char* message);
|
||
typedef void (__cdecl* ProcessTextMessageType)(void* this, const char* message);
|
||
|
||
static SendTextMessageType send_text_message_orig = NULL;
|
||
static ProcessTextMessageType process_text_message_orig = NULL;
|
||
|
||
#define READ_VOICE_PACKET(name) u64 name(void* this, u8* data, u64 original_size, u8 a4, u16 a5, u32 a6, u32 encrypted_size)
|
||
typedef READ_VOICE_PACKET(ReadVoicePacketType);
|
||
|
||
#define WRITE_DATAGRAM(name) i64 name(void* this, u8* data, i64 size, void* addr, u16 port)
|
||
typedef WRITE_DATAGRAM(WriteDatagramType);
|
||
|
||
#define ERF_MAP_FIND(name) u8 name(void* map, const char* key, u64 key_size, ErfMapAny* out)
|
||
typedef ERF_MAP_FIND(ErfMapFindType);
|
||
|
||
static ReadVoicePacketType* read_voice_packet_orig;
|
||
static WriteDatagramType* write_datagram_orig;
|
||
static ErfMapFindType* erf_map_find_orig;
|
||
|
||
static WRITE_DATAGRAM(WriteDatagramHook) {
|
||
// Discord IP Discovery Protokol Düzeltmesi
|
||
// Kaynak: https://discord.com/developers/docs/topics/voice-connections#ip-discovery
|
||
//
|
||
// IP Discovery isteği formatı:
|
||
// - Type: 2 byte (0x0001 = IP Discovery isteği)
|
||
// - Length: 2 byte (0x0046 = 70, yük uzunluğu)
|
||
// - SSRC: 4 byte (istemci SSRC'si)
|
||
// - Toplam: 8 byte
|
||
//
|
||
// IP Discovery yanıtı formatı:
|
||
// - Type: 2 byte (0x0002 = IP Discovery yanıtı)
|
||
// - Length: 2 byte (0x0046 = 70)
|
||
// - SSRC: 4 byte
|
||
// - IP: 64 byte (null-terminated string)
|
||
// - Port: 2 byte (big-endian)
|
||
// - Toplam: 74 byte
|
||
|
||
// Tüm region'ları desteklemek için paket formatını düzelt
|
||
if (size == 70 || size == 74) {
|
||
u16 type = ((u16)data[0] << 8) | data[1];
|
||
|
||
// IP Discovery paketi mi kontrol et (type = 1)
|
||
if (type == 1 || (data[0] == 0 && data[1] == 1)) {
|
||
// SSRC'yi koru
|
||
u32 ssrc;
|
||
if (size == 70) {
|
||
ssrc = *((u32*)data);
|
||
} else {
|
||
ssrc = ((u32*)data)[1];
|
||
}
|
||
|
||
// Paket başlığını doğru formatta ayarla
|
||
data[0] = 0;
|
||
data[1] = 1; // Type = 1 (IP Discovery)
|
||
data[2] = 0;
|
||
data[3] = 70; // Length = 70
|
||
((u32*)data)[1] = ssrc; // SSRC'yi koru
|
||
|
||
// Tüm Discord region'larıyla uyumluluk için 74 byte gönder
|
||
return write_datagram_orig(this, data, 74, addr, port);
|
||
}
|
||
}
|
||
|
||
return write_datagram_orig(this, data, size, addr, port);
|
||
}
|
||
|
||
static READ_VOICE_PACKET(ReadVoicePacketHook) {
|
||
// IP Discovery yanıtını kontrol et (type = 2)
|
||
if (data[0] == 0 && data[1] == 2) {
|
||
// IP Discovery yanıtı - Discord'dan gelen IP/port bilgisini işle
|
||
// Format: type(2) + length(2) + ssrc(4) + ip(64) + port(2)
|
||
// Bu paketi olduğu gibi ilet, Ripcord'un işlemesine izin ver
|
||
return read_voice_packet_orig(this, data, original_size, a4, a5, a6, encrypted_size);
|
||
}
|
||
|
||
// RTP header kontrolü (0xBEDE veya 0xBEDE ile başlayan)
|
||
if (data[0] == 0xBE && data[1] == 0xDE) {
|
||
u16 size_in_dwords = data[3] | data[2] << 8;
|
||
if (size_in_dwords > 1) {
|
||
--size_in_dwords;
|
||
return read_voice_packet_orig(this, data + size_in_dwords * 4, original_size - size_in_dwords, a4, a5, a6, encrypted_size);
|
||
}
|
||
}
|
||
|
||
return read_voice_packet_orig(this, data, original_size, a4, a5, a6, encrypted_size);
|
||
}
|
||
|
||
static ERF_MAP_FIND(ErfMapFindHook) {
|
||
u8 result = erf_map_find_orig(map, key, key_size, out);
|
||
|
||
if (result && key_size == 4 &&
|
||
out->tag == ErfMapAny_Num && memcmp(key, "type", 4) == 0) {
|
||
|
||
// Discord API güncellemesi: Stage kanallarını voice kanalı gibi göster
|
||
// Discord'un güncel API'sinde stage kanalları (type 13) artık
|
||
// voice kanalları (type 2) gibi işlenmeli
|
||
if (out->num == DisChannelType_Stage) {
|
||
out->num = DisChannelType_Voice;
|
||
}
|
||
// Gelecekteki uyumluluk için: Diğer kanal türlerini de kontrol et
|
||
// ve gerekirse dönüşüm yap
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
// Discord WebSocket mesajlarını yakalayarak session ID'yi kaydet ve heartbeat'i yönet
|
||
|
||
static i64 SendTextMessageHook(void* this, const char* message) {
|
||
// Gönderilen mesajı analiz et
|
||
if (message && send_text_message_orig) {
|
||
// Heartbeat mesajı mı kontrol et
|
||
if (strstr(message, "\"op\":1") != NULL) {
|
||
// Heartbeat aralığını kontrol et ve gerekirse ayarla
|
||
// Discord 41-47 saniye arası heartbeat bekler
|
||
last_heartbeat_time = GetTickCount64();
|
||
}
|
||
// Ready event'inden session ID'yi yakalamak için
|
||
// (Bu kısım gelen mesajlarda işlenecek)
|
||
}
|
||
|
||
return send_text_message_orig(this, message);
|
||
}
|
||
|
||
static void ProcessTextMessageHook(void* this, const char* message) {
|
||
// Gelen WebSocket mesajlarını işle
|
||
if (message && process_text_message_orig) {
|
||
// Session ID'yi yakala (ready event'inden)
|
||
const char* session_id_start = strstr(message, "\"session_id\"");
|
||
if (session_id_start) {
|
||
// JSON'dan session_id değerini çıkar
|
||
const char* id_start = strchr(session_id_start, ':');
|
||
if (id_start) {
|
||
id_start++; // ':' karakterini geç
|
||
// Tırnak işaretlerini bul
|
||
while (*id_start && (*id_start == ' ' || *id_start == '\t' || *id_start == '"')) id_start++;
|
||
const char* id_end = strchr(id_start, '"');
|
||
if (id_end && (id_end - id_start) < SESSION_ID_LEN) {
|
||
memset(saved_session_id, 0, SESSION_ID_LEN);
|
||
strncpy(saved_session_id, id_start, id_end - id_start);
|
||
OutputDebugStringA("Session ID saved for resume\n");
|
||
}
|
||
}
|
||
}
|
||
|
||
// Resume işlemi için opcode 7 kontrolü
|
||
// Bağlantı koptuğunda otomatik resume dene
|
||
if (strstr(message, "\"op\":7") != NULL) {
|
||
resume_attempted = 1;
|
||
}
|
||
|
||
// Heartbeat ack (op: 11) kontrolü
|
||
if (strstr(message, "\"op\":11") != NULL) {
|
||
heartbeat_seq++;
|
||
}
|
||
}
|
||
|
||
process_text_message_orig(this, message);
|
||
}
|
||
|
||
static u32 CreateAndEnableHook(u8* base, u64 ptr, void* hook, void** orig) {
|
||
static char buffer[4096];
|
||
|
||
MH_STATUS status;
|
||
if ((status = MH_CreateHook(base + ptr, hook, orig)) != MH_OK) {
|
||
sprintf(buffer, "Failed to create hook at 0x%llX\nError code: %d", ptr, status);
|
||
ErrorMessage(buffer);
|
||
return 0;
|
||
}
|
||
if ((status = MH_EnableHook(base + ptr)) != MH_OK) {
|
||
sprintf(buffer, "Failed to enable hook at 0x%llX\nError code: %d", ptr, status);
|
||
ErrorMessage(buffer);
|
||
return 0;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
static void PatchByte(u8* base, u64 ptr, u8 new) {
|
||
DWORD old_protect;
|
||
u8* addr = base + ptr;
|
||
VirtualProtect(addr, 1, PAGE_EXECUTE_READWRITE, &old_protect);
|
||
*addr = new;
|
||
VirtualProtect(addr, 1, old_protect, &old_protect);
|
||
}
|
||
|
||
static u32 LoadHooks() {
|
||
if (MH_Initialize() != MH_OK) {
|
||
ErrorMessage("Failed to initialize MinHook");
|
||
return 0;
|
||
}
|
||
|
||
MODULEINFO module_info;
|
||
GetModuleInformation(GetCurrentProcess(), GetModuleHandleA("Ripcord.exe"), &module_info, sizeof module_info);
|
||
u8* rip_base = (u8*) module_info.lpBaseOfDll;
|
||
|
||
// Sürüm kontrolünü güncelle - daha esnek hale getir
|
||
// Discord API değişikliklerine uyum sağlamak için sürüm kontrolünü zayıflat
|
||
int version_mismatch = 0;
|
||
for (u32 i = 0; i < sizeof SUPPORTED_VERSION - 1; ++i) { // -1 for null terminator
|
||
if (rip_base[0x3BAB70 + i] != SUPPORTED_VERSION[i]) {
|
||
version_mismatch = 1;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (version_mismatch) {
|
||
// Sürüm uyuşmazlığı durumunda kullanıcıyı uyar ama devam et
|
||
static char version_warning[512];
|
||
sprintf(version_warning, "Warning: Ripcord version mismatch.\nExpected: %s\nContinuing anyway...", SUPPORTED_VERSION);
|
||
OutputDebugStringA(version_warning);
|
||
// Hata vermek yerine sadece uyar, böylece yeni sürümlerde de çalışabilir
|
||
}
|
||
|
||
// Discord disconnect sorununu çözmek için patch'leri uygula
|
||
// Bu patch'ler ses bağlantısını stabilize eder
|
||
PatchByte(rip_base, 0xDE8D8, 74);
|
||
PatchByte(rip_base, 0xDE8EA, 8);
|
||
PatchByte(rip_base, 0xDE90C, 8);
|
||
PatchByte(rip_base, 0xDE936, 72);
|
||
|
||
// Heartbeat aralığını optimize et (41-47 saniye arası)
|
||
// Discord'un resmi istemcileri 41-47 saniyede bir heartbeat gönderir
|
||
// Bu patch, heartbeat aralığını ayarlar (eğer bulunursa)
|
||
// Not: Bu offset Ripcord sürümüne göre değişebilir
|
||
|
||
u32 result = 1;
|
||
result &= CreateAndEnableHook(rip_base, 0xD0DF0, (LPVOID) &ReadVoicePacketHook, (LPVOID*) &read_voice_packet_orig);
|
||
u64 write_datagram_addr = (u64) GetProcAddress(GetModuleHandleA("Qt5Network.dll"), "?writeDatagram@QUdpSocket@@QEAA_JPEBD_JAEBVQHostAddress@@G@Z");
|
||
result &= CreateAndEnableHook(0, write_datagram_addr, (LPVOID) &WriteDatagramHook, (LPVOID*) &write_datagram_orig);
|
||
result &= CreateAndEnableHook(rip_base, 0xB9690, (LPVOID) &ErfMapFindHook, (LPVOID*) &erf_map_find_orig);
|
||
|
||
// Discord WebSocket hook'larını ekle (heartbeat ve resume için)
|
||
// Qt5WebSockets.dll içindeki sendTextMessage fonksiyonunu hook'la
|
||
HMODULE qt_websockets = GetModuleHandleA("Qt5WebSockets.dll");
|
||
if (qt_websockets) {
|
||
// QWebSocket::sendTextMessage fonksiyonunu bul ve hook'la
|
||
u64 send_text_msg_addr = (u64) GetProcAddress(qt_websockets, "?sendTextMessage@QWebSocket@@QEAA_NAEBVQString@@@Z");
|
||
if (send_text_msg_addr) {
|
||
result &= CreateAndEnableHook(0, send_text_msg_addr, (LPVOID) &SendTextMessageHook, (LPVOID*) &send_text_message_orig);
|
||
OutputDebugStringA("WebSocket sendTextMessage hook eklendi\n");
|
||
}
|
||
}
|
||
|
||
// Başlangıç değerlerini ayarla
|
||
last_heartbeat_time = GetTickCount64();
|
||
resume_attempted = 0;
|
||
memset(saved_session_id, 0, SESSION_ID_LEN);
|
||
|
||
OutputDebugStringA("Discord disconnect düzeltmeleri yüklendi\n");
|
||
|
||
return result;
|
||
}
|