Files

289 lines
11 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}