Ir para conteúdo

Os Melhores

  1. Grego

    Grego

    Velha Guarda Ex-Staffer


    • Pontos

      15

    • Total de Posts

      4.083


  2. gcr_fc

    gcr_fc

    Silver Member


    • Pontos

      13

    • Total de Posts

      305


  3. HanKravich

    HanKravich

    Bronze Member


    • Pontos

      10

    • Total de Posts

      137


  4. hamiltoncw

    hamiltoncw

    VIP EXTREME Doador Comunidade


    • Pontos

      9

    • Total de Posts

      393


Conteúdo popular

Mostrando conteúdo com a maior reputação em 19/05/2026 in todas as áreas

  1. 📌 [LANÇAMENTO] FoxMap Studio V1 — Open Source O editor de mapas mais completo para WYD Fala galera da WebCheats! Se você administra ou está desenvolvendo um servidor de With Your Destiny, sabe o quão trabalhoso e limitado costuma ser o processo de criação e edição de mapas usando ferramentas antigas. Pensando nisso, foi desenvolvido o FoxMap Studio V1, uma ferramenta revolucionária que vai mudar completamente a forma como você customiza o seu servidor! Assista ao vídeo demonstrativo completo e veja a ferramenta em ação: 👉 [Hidden Content] 🔥 O que faz o FoxMap Studio ser diferente? O FoxMap Studio foi criado do zero para ser intuitivo, rápido e extremamente poderoso. Chega de sofrer com ferramentas travadas que exigem reinicializações constantes ou processos manuais complexos. [Hidden Content] [Hidden Content] [/hide] 🌟 Principais Recursos: Edição Visual Completa: Interface limpa, moderna e totalmente interativa para você focar no que realmente importa: a criatividade do seu mapa. Modificações em Tempo Real: Veja cada alteração de terreno, texturas e objetos acontecer instantaneamente na sua tela. Editor Avançado de Terrenos e Alturas: Suba montanhas, crie vales e ajuste o relevo do mapa com pincéis dinâmicos e precisos. Pintura Dinâmica de Texturas: Aplique e misture diferentes texturas de solo diretamente no mapa de forma suave e rápida. Importação e Posicionamento de Objetos 3D: Adicione construções, decorações, portais e elementos de cenário de maneira extremamente simples (com suporte a arquivos .glb/.gltf e conversão inteligente para o formato do cliente). Gestão de Atributos do Mapa: Configure zonas de PvP, áreas seguras (No-PvP), zonas de drop ou teleportes diretamente pela interface do estúdio. Integração Direta com o Cliente: Teste e salve suas alterações diretamente nos arquivos do jogo de forma prática. 📺 Veja o Passo a Passo no Vídeo! No vídeo oficial, mostramos desde a interface de login da plataforma WYD Fox Studio até a criação prática de um cenário completo: Como abrir e carregar os mapas existentes do seu cliente. Utilização das ferramentas de pintura de terreno e elevação de mesh. Importação e rotação livre de objetos e decorações 3D no cenário. Aplicação de efeitos climáticos e iluminação personalizada no mapa. Conversão e salvamento automático dos arquivos para rodar perfeitamente no jogo. Assista aqui: Como Criar Mapas Customizados para WYD — FoxMap Studio 🚀 Como ter acesso? [Hidden Content] A ferramenta conta com uma versão de testes gratuita para você conhecer o potencial do editor. Desenvolvedor: Foxdemo / WYD Fox Studio Para suporte, atualizações constantes e mais ferramentas da nossa suíte de desenvolvimento (Editores de Itens, NPCs, Monstros e Quests em breve!), entre no nosso Discord oficial (link disponível na descrição do vídeo do YouTube). Deixe seu comentário abaixo dizendo o que achou da ferramenta ou qual recurso você gostaria de ver nas próximas atualizações!
    5 pontos
  2. MEUS CANAIS DE VÍDEOS HC CURSOS E TUTORIAIS 🎬 ▶️ PLAYLIST DO MEU CANAL NO YOUTUBE 🎬 ▶️ VEICARECA GAMES 🎬 ▶️ PLAYLIST DO MEU CANAL NO YOUTUBE 🎬 ▶️
    4 pontos
  3. Pack Grátis - MC RAPOSINHA | Michelle Rabbit MC RAPOSINHA Download Pack: [Hidden Content] MICHELLE RABBIT Download Pack: [Hidden Content]
    4 pontos
  4. Conteúdo da MARTINA OLIVEIRA, conhecida também como BEIÇOLA DO PRIVACY, PRIVACY DELA GRATIS + Videos vazados totalmente pelada, mostrando e fazendo de tudo. Sem frescura e nem anuncios, basta clicar no link e dar play no video, para ver o Privacy gratis é só se cadastrar. PRIVACY OFICIAL GRÁTIS, BASTA SE CADASTRAR [Hidden Content] QUASE 40 VIDEOS VAZADOS [Hidden Content]
    3 pontos
  5. Fala pessoal blz, estou descompilando o sistema de painéis do WYD Global e pra facilitar minha vida criei esse script .idc que faz a analise completa e extrai diversas informações digamos interessantes do cliente, o que facilita bem a vida. Como fiz para o WYD trabalha só com o exes compilados em MSVC, mas pode facilmente ser ajustado para trabalhar com outros compiladores e tbm outros exes. No próprio script tem a descrição das opções de configuração. Para usarem, salvem como XXXXX.idc, basta carregar o exe no IDA Shift+F2 importar o script ou colar o conteúdo no editor de script e rodar. A analise pode demorar a depender do tamanho do exe analisado e do seu pc tbm. Seguem alguns prints. bom é isso... /* * ============================================================================ * Vtable Analyzer Pro v5.3 - By Guilherme Candiotto * MSVC RTTI / vtable harvester for IDA Free 9.x and IDA Pro 7.x+ * ============================================================================ * * WHAT IT DOES * ------------ * Detects C++ classes in MSVC binaries via RTTI (CompleteObjectLocator), * reconstructs the inheritance hierarchy (including multi-inheritance via * secondary vtables), names virtual methods, applies __thiscall + this * typing for Hex-Rays, infers sizeof, detects singletons, annotates * virtual call sites, and emits a C++ header with the recovered classes. * * HOW TO USE * ---------- * File > Script file... -> select this .idc * * Outputs in <OUTPUT_DIR>: * vtables_analysis.json structured class data (parents, methods, ...) * vtables_analysis.log plain-text run log * vtables_overrides.json override relationships (child slot -> parent) * wyd_classes.h C++ header with reconstructed classes * * Re-run with Alt+F5 (configurable via REGISTER_HOTKEY). * * REQUIREMENTS * ------------ * IDA Free 9.x or IDA Pro >= 7.x. * MSVC binary with RTTI enabled (CompleteObjectLocator present). * Tested on x86; x64 supported via BRUTE_FORCE_X64_RVA path. * * CONFIG * ------ * See #defines below. Each one is documented at its declaration. * * License: MIT. Use freely; attribution appreciated. * ============================================================================ */ #include <idc.idc> // ---------------------- CONFIG ---------------------- // Pasta onde gravar os arquivos de saida. // "" = mesma pasta do .idb (recomendado). // "D:/path" = caminho absoluto. Use / ou \\ no Windows. #define OUTPUT_DIR "" #define OUTPUT_JSON_NAME "vtables_analysis.json" // structured class data (parents, methods, sizeof, ...) #define OUTPUT_LOG_NAME "vtables_analysis.log" // plain-text run log (progress + warnings) #define OUTPUT_OVERRIDES_NAME "vtables_overrides.json" // override map: child slot -> parent function #define OUTPUT_HEADER_NAME "wyd_classes.h" // C++ header with reconstructed classes (importable in IDA Local Types) #define DRY_RUN 0 // 1 = preview only (no IDB modification, no file writes) #define CREATE_STRUCTS 1 // 1 = create opaque <Class>_t struct in IDA (vptr at offset 0) #define RENAME_OVERRIDES 0 // 1 = rename even functions with existing custom names (e.g. from PDB); 0 = preserve them #define SCAN_DATA_TOO 1 // 1 = scan .data segment too (some binaries have vtables there, not just .rdata) #define STRING_FIRST_SCAN 1 // 1 = scan via RTTI strings (.?AV/.?AU), reconstruct TD/COL/vtable from xrefs #define POINTER_FIRST_SCAN 1 // 1 = sequential DWORD scan in .rdata looking for TD->COL pointer pattern (classic mode) #define PROPAGATE_OVERRIDES 1 // 1 = final pass detecting overrides (child slot != parent func) vs inherited methods #define BRUTE_FORCE_X64_RVA 1 // so executa em x64; inerte em x86 #define BRUTE_FORCE_X86 1 // varredura simetrica para x86 (sig=0) #define APPLY_THISCALL_TYPES 1 // aplica __thiscall(_t *this) nos vmethods #define DETECT_OBJECT_SIZES 1 // tenta deduzir sizeof via operator new #define DETECT_SINGLETONS 1 // marca globais que recebem instancias #define ANNOTATE_VCALLS 1 // comenta call [reg+N] com possibilidades #define EMIT_CPP_HEADER 1 // emite wyd_classes.h #define VCALL_MAX_METHODS 4 // max metodos listados por slot #define MAX_VTABLE_METHODS 2048 // safety cap: vtables larger than this are rejected (likely false positives) #define MIN_VTABLE_METHODS 1 // minimum slots to consider a vtable valid // v5.2 ---- #define CLEAR_OUTPUT_ON_START 1 // limpa Output do IDA no inicio #define REGISTER_HOTKEY 1 // Alt+F5 re-roda o script #define HOTKEY_COMBO "Alt-F5" // hotkey to re-run the script (IDA hotkey syntax: "Mod-Key") #define PROGRESS_EVERY 4096 // emite msg() a cada N iteracoes // v5.3: filtro de classes framework (MFC/STL/ATL/etc) #define EXCLUDE_FRAMEWORK_CLASSES 1 // 1 = pula MFC, STL, ATL, Gdiplus... // v5.3: detectores melhorados #define SIZE_INFER_FROM_BODY 1 // estima sizeof pelo max [this+N] no ctor #define SIZE_BODY_SCAN_LIMIT 80 // max instrucoes a olhar no body do ctor #define SINGLETON_AGGRESSIVE 1 // tenta padroes adicionais alem do basico // v5.3: limite de varredura para string-first scan (evita travar em // binarios de servidor com .data gigante (>100MB de tabelas de jogo). // 0 = sem limite. Default: 50MB. #define STRING_SCAN_MAX_SEG_SIZE 0x3200000 // 50MB // Cores BGR usadas pra colorir cada tipo de item no Disassembly View do IDA. // Formato: 0xBBGGRR. Mude pra 0 pra desabilitar coloracao especifica. #define COLOR_VTABLE_SLOT 0xFFE8E0 #define COLOR_VFUNC 0xE0FFE0 #define COLOR_PURE 0xC0C0C0 #define COLOR_DTOR 0xC0C0FF #define COLOR_CTOR 0xC0FFFF #define COLOR_RTTI_COL 0xE0E0FF #define COLOR_TYPE_DESC 0xFFE0FF #define COLOR_OVERRIDE 0xE0FFFF // Nomes de arrays internos do IDB (mantem cache entre runs do script). // Mude o sufixo (_v5_/_v4_) se quiser invalidar o cache forcando re-scan. #define ARR_VT_SEEN "vap_v4_vt_seen" #define ARR_TD2VT "vap_v4_td2vt" #define ARR_VT2TD "vap_v4_vt2td" #define ARR_VT2COL "vap_v4_vt2col" #define ARR_VT2COUNT "vap_v4_vt2count" #define ARR_VT2NAME "vap_v4_vt2name" #define ARR_VT2PARENTS "vap_v4_vt2parents" #define ARR_VT_LIST "vap_v4_vt_list" #define ARR_VT2OFFSET "vap_v5_vt2offset" // COL.offset (0=primary) #define ARR_VT2SIZE "vap_v5_vt2size" #define ARR_VT2SINGLETON "vap_v5_vt2singleton" #define ARR_SLOT_METHODS "vap_v5_slot_methods" #define ARR_HDR_EMITTED "vap_v5_hdr_emitted" #define ARR_TOPO_VISITED "vap_v5_topo_visited" #define ARR_TOPO_ORDER "vap_v5_topo_order" #define ARR_TOPO_COUNT "vap_v5_topo_count" // ---------------------- GLOBAIS ---------------------- // FIX IDC 9.x: variaveis globais usam "extern", nao "static" (que eh // reservado pra funcoes). Senao o parser cuspia "Missing brace". extern g_ptr_size; extern g_is_64; extern g_imagebase; extern g_log_handle; extern g_json_handle; extern g_overrides_handle; extern g_json_first; extern g_overrides_first; extern g_vt_list_count; extern g_stat_classes; extern g_stat_methods; extern g_stat_renamed; extern g_stat_ctors; extern g_stat_dtors; extern g_stat_pures; extern g_stat_overrides; extern g_stat_inherited; extern g_stat_brute; extern g_stat_thiscall; extern g_stat_sizes; extern g_stat_singletons; extern g_stat_vcalls; extern g_stat_header_classes; extern g_stat_renamed_inherited; extern g_stat_errors; extern g_purecall_csv; extern g_stat_filtered; // ========================================================================= // user_cancelled() - cooperative cancel hook (IDC stub) // ========================================================================= // IDC 9.x removed the wait_box / user_cancelled API; only IDAPython still // exposes it (idaapi.user_cancelled / ida_kernwin.user_cancelled). // // This stub always returns 0, so the if (user_cancelled()) checks scattered // across the heavy scan loops below are dead code - a long run cannot be // aborted gracefully from IDC (you have to kill the IDA process). // // TO ENABLE REAL CANCEL SUPPORT (IDAPython port): // 1. Wrap this script as an IDAPython plugin (.py instead of .idc). // 2. Replace this stub with: // import ida_kernwin // def user_cancelled(): // return ida_kernwin.user_cancelled() // 3. Bracket the main() entry with: // ida_kernwin.show_wait_box("HIDECANCEL\nVtable Analyzer: running...") // try: // main() // finally: // ida_kernwin.hide_wait_box() // 4. The 10 existing `if (user_cancelled()) return;` calls in this script // will then start working as cooperative cancel points. // // Until then, this stub keeps the IDC source compatible and side-effect-free. // ========================================================================= static user_cancelled() { return 0; } // ========================================================================= // LOG // ========================================================================= static log_line(level, line) { auto stamp = "[" + level + "] " + line; msg("%s\n", stamp); if (g_log_handle != 0) fprintf(g_log_handle, "%s\n", stamp); } static log_info(line) { log_line("INFO", line); } static log_warn(line) { log_line("WARN", line); } static log_error(line) { log_line("ERR ", line); g_stat_errors = g_stat_errors + 1; } // ========================================================================= // PATH HELPERS // ========================================================================= static find_last_char(s, c) { if (s == 0 || s == "") return -1; auto i; auto last = -1; auto n = strlen(s); for (i = 0; i < n; i = i + 1) { if (substr(s, i, i + 1) == c) last = i; } return last; } static dirname_of(path) { if (path == 0 || path == "") return ""; auto p1 = find_last_char(path, "\\"); auto p2 = find_last_char(path, "/"); auto pos = (p1 > p2) ? p1 : p2; if (pos == -1) return ""; return substr(path, 0, pos); } static normalize_path(p) { if (p == 0 || p == "") return ""; auto out = ""; auto i; auto n = strlen(p); for (i = 0; i < n; i = i + 1) { auto c = substr(p, i, i + 1); if (c == "\\") out = out + "/"; else out = out + c; } return out; } static path_join(dir, name) { if (dir == "" || dir == 0) return name; auto d = normalize_path(dir); auto last = substr(d, strlen(d) - 1, strlen(d)); if (last == "/") return d + name; return d + "/" + name; } static resolve_output_dir() { if (OUTPUT_DIR != "" && OUTPUT_DIR != 0) return normalize_path(OUTPUT_DIR); auto idb = get_idb_path(); auto d = dirname_of(idb); if (d == "" || d == 0) return "."; return normalize_path(d); } static safe_fopen(path, mode) { auto h = fopen(path, mode); if (h == 0) { msg("[ERR ] FOPEN FALHOU: '%s' (modo '%s')\n", path, mode); msg(" -> Verifique se a pasta existe e tem permissao de escrita.\n"); msg(" -> Ajuste OUTPUT_DIR no topo do script se necessario.\n"); return 0; } msg("[INFO] arquivo aberto OK: %s\n", path); return h; } // ========================================================================= // HELPERS BASICOS // ========================================================================= static read_ptr(ea) { return g_is_64 ? get_qword(ea) : get_dword(ea); } static rva_to_ea(rva) { return g_imagebase + rva; } static seg_is_executable(ea) { if (!is_loaded(ea)) return 0; return (get_segm_attr(ea, SEGATTR_PERM) & SEGPERM_EXEC) != 0; } static seg_name_of(ea) { if (!is_loaded(ea)) return ""; return get_segm_name(ea); } static is_valid_code_ptr(ea) { if (ea == BADADDR || ea == 0) return 0; if (!is_loaded(ea)) return 0; return seg_is_executable(ea); } static is_valid_data_ptr(ea) { if (ea == BADADDR || ea == 0) return 0; return is_loaded(ea); } static starts_with(s, prefix) { if (s == 0 || prefix == 0) return 0; if (strlen(s) < strlen(prefix)) return 0; return substr(s, 0, strlen(prefix)) == prefix; } static ends_with(s, suffix) { if (s == 0 || suffix == 0) return 0; auto sl = strlen(s); auto pl = strlen(suffix); if (sl < pl) return 0; return substr(s, sl - pl, sl) == suffix; } static sanitize_name(s) { if (s == 0 || s == "") return "anon"; auto out = ""; auto i; auto n = strlen(s); for (i = 0; i < n; i = i + 1) { auto c = substr(s, i, i + 1); if ((c >= "a" && c <= "z") || (c >= "A" && c <= "Z") || (c >= "0" && c <= "9") || c == "_") { out = out + c; } else if (c == ":" || c == "<" || c == ">" || c == "," || c == " " || c == "*" || c == "&" || c == "(" || c == ")" || c == "[" || c == "]" || c == "-" || c == "." || c == "+" || c == "/" || c == "\\" || c == "$" || c == "@" || c == "?") { out = out + "_"; } } if (out == "") out = "anon"; return out; } // ========================================================================= // JSON ESCAPING // ========================================================================= static json_escape(s) { if (s == 0 || s == "") return ""; auto out = ""; auto i; auto n = strlen(s); for (i = 0; i < n; i = i + 1) { auto c = substr(s, i, i + 1); if (c == "\"") out = out + "\\\""; else if (c == "\\") out = out + "\\\\"; else if (c == "\n") out = out + "\\n"; else if (c == "\r") out = out + "\\r"; else if (c == "\t") out = out + "\\t"; else out = out + c; } return out; } // ========================================================================= // PURECALL ADDRESS LOOKUP (cache de enderecos conhecidos via imports) // ========================================================================= static csv_contains_long(csv, val) { if (csv == "" || csv == 0) return 0; auto buf = ""; auto i; auto n = strlen(csv); for (i = 0; i <= n; i = i + 1) { auto c = (i < n) ? substr(csv, i, i + 1) : ","; if (c == ",") { if (buf != "" && atol(buf) == val) return 1; buf = ""; } else { buf = buf + c; } } return 0; } static csv_append_long(csv, val) { if (csv == "") return ltoa(val, 10); return csv + "," + ltoa(val, 10); } static init_purecall_addresses() { g_purecall_csv = ""; auto names_buf = "__purecall|_purecall|purecall|__cxa_pure_virtual|_cxa_pure_virtual"; auto buf = ""; auto i; auto n = strlen(names_buf); for (i = 0; i <= n; i = i + 1) { auto c = (i < n) ? substr(names_buf, i, i + 1) : "|"; if (c == "|") { if (buf != "") { auto ea = get_name_ea_simple(buf); if (ea != BADADDR && !csv_contains_long(g_purecall_csv, ea)) { g_purecall_csv = csv_append_long(g_purecall_csv, ea); } } buf = ""; } else { buf = buf + c; } } } static is_known_purecall_addr(ea) { return csv_contains_long(g_purecall_csv, ea); } // ========================================================================= // PARSER MANUAL DO NOME MANGLED (com tratamento de templates) // // .?AV<Classe>@<NS_inner>@<NS_outer>@@ // .?AU<Struct>@<NS_inner>@<NS_outer>@@ // // Resultado em C++ pseudo: NS_outer::NS_inner::Classe // Templates "?$Nome@..." viram "tpl_Nome" para nao quebrar o sanitize. // ========================================================================= static cleanup_template_segment(seg) { if (!starts_with(seg, "?$")) return seg; auto rest = substr(seg, 2, strlen(seg)); auto pos = strstr(rest, "@"); auto name; if (pos != -1) name = substr(rest, 0, pos); else name = rest; if (name == "") name = "tpl"; return "tpl_" + name; } static manual_parse_rtti_name(mangled) { if (mangled == 0 || mangled == "") return ""; if (strlen(mangled) < 6) return ""; auto prefix = substr(mangled, 0, 4); if (prefix != ".?AV" && prefix != ".?AU") return ""; auto rest = substr(mangled, 4, strlen(mangled)); if (!ends_with(rest, "@@")) return ""; rest = substr(rest, 0, strlen(rest) - 2); if (rest == "") return ""; auto i; auto n = strlen(rest); auto cur = ""; auto result = ""; for (i = 0; i < n; i = i + 1) { auto c = substr(rest, i, i + 1); if (c == "@") { if (cur != "") { auto seg = cleanup_template_segment(cur); if (result == "") result = seg; else result = seg + "::" + result; } cur = ""; } else { cur = cur + c; } } if (cur != "") { auto seg2 = cleanup_template_segment(cur); if (result == "") result = seg2; else result = seg2 + "::" + result; } return result; } // ========================================================================= // v5.2: leitor de string C RAW (le bytes ate \0 sem depender do IDA // ter definido a string). Substitui o que create_strlit fazia, agora // que essa funcao mudou de assinatura no 9.x e nao podemos mais usar. // ========================================================================= static read_c_string_raw(ea) { if (!is_loaded(ea)) return ""; auto out = ""; auto i = 0; auto max_len = 512; while (i < max_len) { auto b = get_wide_byte(ea + i); if (b == 0) break; if (b < 0x20 || b > 0x7E) break; out = out + sprintf("%c", b); i = i + 1; } return out; } // ========================================================================= // RTTI - CompleteObjectLocator (COL) // +0x00 signature (0 = x86, 1 = x64) // +0x04 offset // +0x08 cdOffset // +0x0C pTypeDescriptor (RVA em x64, ptr em x86) // +0x10 pClassDescriptor(RVA em x64, ptr em x86) // +0x14 pSelf (so x64, RVA) // ========================================================================= static col_read_field(col_ea, off) { auto v = get_wide_dword(col_ea + off); if (g_is_64) return rva_to_ea(v); return v; } static validate_col(col_ea) { if (!is_valid_data_ptr(col_ea)) return 0; auto sig = get_wide_dword(col_ea); auto expected = g_is_64 ? 1 : 0; if (sig != expected) return 0; auto offset = get_wide_dword(col_ea + 0x04); auto cdOffset = get_wide_dword(col_ea + 0x08); if (offset > 0x10000) return 0; if (cdOffset > 0x10000) return 0; auto type_desc = col_read_field(col_ea, 0x0C); if (!is_valid_data_ptr(type_desc)) return 0; auto chd = col_read_field(col_ea, 0x10); if (!is_valid_data_ptr(chd)) return 0; auto chd_sig = get_wide_dword(chd); if (chd_sig != 0) return 0; auto nb = get_wide_dword(chd + 0x08); if (nb == 0 || nb > 256) return 0; if (g_is_64) { auto self_rva = get_wide_dword(col_ea + 0x14); if (rva_to_ea(self_rva) != col_ea) return 0; } return 1; } static read_typedesc_name(td_ea) { if (!is_valid_data_ptr(td_ea)) return ""; auto name_ea = td_ea + (g_ptr_size * 2); auto mangled = get_strlit_contents(name_ea, -1, STRTYPE_C); if (mangled == 0 || mangled == "") { mangled = read_c_string_raw(name_ea); } if (mangled == 0 || mangled == "") return ""; return mangled; } static demangle_class_name(mangled) { if (mangled == 0 || mangled == "") return ""; if (!starts_with(mangled, ".?AV") && !starts_with(mangled, ".?AU")) return ""; auto wrapped = "??_R0" + substr(mangled, 1, strlen(mangled)) + "@8"; auto dem = demangle_name(wrapped, INF_LONG_DN); if (dem != 0 && dem != "") { if (starts_with(dem, "class ")) dem = substr(dem, 6, strlen(dem)); if (starts_with(dem, "struct ")) dem = substr(dem, 7, strlen(dem)); auto suf = " `RTTI Type Descriptor'"; if (ends_with(dem, suf)) dem = substr(dem, 0, strlen(dem) - strlen(suf)); if (dem != "") return dem; } auto man = manual_parse_rtti_name(mangled); if (man != "") return man; return mangled; } // ========================================================================= // CHD + BaseClassArray -> lista de pais (TD addresses CSV) // // CHD: // +0x00 signature (==0) // +0x04 attributes // +0x08 numBaseClasses // +0x0C pBaseClassArray (RVA x64 / ptr x86) // // BCD (cada entrada da BCA aponta pra um BCD): // +0x00 pTypeDescriptor // +0x04 numContainedBases // +0x08 PMD (mdisp, pdisp, vdisp - 12 bytes) // +0x14 attributes // +0x18 pClassDescriptor // // BCA[0] eh sempre a propria classe; pulamos. // ========================================================================= static parse_base_classes(col_ea) { auto chd = col_read_field(col_ea, 0x10); if (!is_valid_data_ptr(chd)) return ""; auto sig = get_wide_dword(chd); if (sig != 0) return ""; auto num_bases = get_wide_dword(chd + 0x08); if (num_bases <= 1 || num_bases > 64) return ""; auto bca_field = get_wide_dword(chd + 0x0C); auto bca_ea = g_is_64 ? rva_to_ea(bca_field) : bca_field; if (!is_valid_data_ptr(bca_ea)) return ""; auto out = ""; auto i; for (i = 1; i < num_bases; i = i + 1) { auto bcd_field = get_wide_dword(bca_ea + i * 4); auto bcd_ea = g_is_64 ? rva_to_ea(bcd_field) : bcd_field; if (!is_valid_data_ptr(bcd_ea)) continue; auto td_field = get_wide_dword(bcd_ea); auto td_ea = g_is_64 ? rva_to_ea(td_field) : td_field; if (!is_valid_data_ptr(td_ea)) continue; if (out == "") out = ltoa(td_ea, 10); else out = out + "," + ltoa(td_ea, 10); } return out; } static parents_csv_to_names(csv) { if (csv == "" || csv == 0) return ""; auto out = ""; auto buf = ""; auto i; auto n = strlen(csv); for (i = 0; i <= n; i = i + 1) { auto c = (i < n) ? substr(csv, i, i + 1) : ","; if (c == ",") { if (buf != "") { auto td = atol(buf); auto mang = read_typedesc_name(td); auto nm = (mang != "") ? demangle_class_name(mang) : "??"; if (out == "") out = nm; else out = out + ", " + nm; } buf = ""; } else { buf = buf + c; } } return out; } // ========================================================================= // CACHE + HIERARQUIA (arrays do IDB) // ========================================================================= static arr_init(name) { auto id = get_array_id(name); if (id != -1) delete_array(id); create_array(name); } static arr_set_long(name, key, val) { auto id = get_array_id(name); if (id == -1) return; set_array_long(id, key, val); } static arr_get_long(name, key) { auto id = get_array_id(name); if (id == -1) return -1; return get_array_element(AR_LONG, id, key); } static arr_set_str(name, key, val) { auto id = get_array_id(name); if (id == -1) return; set_array_string(id, key, val); } static arr_get_str(name, key) { auto id = get_array_id(name); if (id == -1) return ""; auto v = get_array_element(AR_STR, id, key); if (v == 0 || v == -1) return ""; return v; } static cache_init() { arr_init(ARR_VT_SEEN); arr_init(ARR_TD2VT); arr_init(ARR_VT2TD); arr_init(ARR_VT2COL); arr_init(ARR_VT2COUNT); arr_init(ARR_VT2NAME); arr_init(ARR_VT2PARENTS); arr_init(ARR_VT_LIST); arr_init(ARR_VT2SIZE); arr_init(ARR_VT2SINGLETON); arr_init(ARR_VT2OFFSET); g_vt_list_count = 0; } static cache_seen(ea) { return arr_get_long(ARR_VT_SEEN, ea) > 0; } static cache_mark(ea) { arr_set_long(ARR_VT_SEEN, ea, 1); } static hier_register(td_ea, vt_ea, col_ea, sname, count, parents_csv, col_offset) { arr_set_long(ARR_VT2TD, vt_ea, td_ea); arr_set_long(ARR_VT2COL, vt_ea, col_ea); arr_set_long(ARR_VT2COUNT, vt_ea, count); arr_set_long(ARR_VT2OFFSET, vt_ea, col_offset); arr_set_str (ARR_VT2NAME, vt_ea, sname); arr_set_str (ARR_VT2PARENTS,vt_ea, parents_csv); arr_set_long(ARR_VT_LIST, g_vt_list_count, vt_ea); g_vt_list_count = g_vt_list_count + 1; if (col_offset == 0) { if (arr_get_long(ARR_TD2VT, td_ea) <= 0) { arr_set_long(ARR_TD2VT, td_ea, vt_ea); } } } static hier_offset(vt_ea) { auto v = arr_get_long(ARR_VT2OFFSET, vt_ea); if (v == -1) return 0; return v; } static hier_col(vt_ea) { auto v = arr_get_long(ARR_VT2COL, vt_ea); if (v == -1) return BADADDR; return v; } static hier_vt_by_td(td_ea) { auto v = arr_get_long(ARR_TD2VT, td_ea); if (v == -1) return BADADDR; return v; } static hier_count(vt_ea) { auto v = arr_get_long(ARR_VT2COUNT, vt_ea); if (v == -1) return 0; return v; } static hier_name(vt_ea) { return arr_get_str(ARR_VT2NAME, vt_ea); } static hier_parents(vt_ea) { return arr_get_str(ARR_VT2PARENTS, vt_ea); } static hier_vt_at(idx) { auto v = arr_get_long(ARR_VT_LIST, idx); if (v == -1) return BADADDR; return v; } // ========================================================================= // PURECALL // ========================================================================= static is_pure_call(faddr) { if (faddr == BADADDR || faddr == 0) return 0; if (is_known_purecall_addr(faddr)) return 1; auto name = get_func_name(faddr); if (name != 0 && name != "") { if (strstr(name, "purecall") != -1) return 1; if (strstr(name, "pure_virtual") != -1) return 1; if (strstr(name, "cxa_pure_virtual") != -1) return 1; } auto fend = get_func_attr(faddr, FUNCATTR_END); if (fend == BADADDR) return 0; if ((fend - faddr) > 24) return 0; auto mnem = print_insn_mnem(faddr); if (mnem == "int" || mnem == "hlt" || mnem == "ud2") return 1; if (mnem == "jmp") { auto dref = get_first_dref_from(faddr); while (dref != BADADDR) { auto target_via_iat = get_wide_dword(dref); if (g_is_64) target_via_iat = get_qword(dref); if (is_known_purecall_addr(target_via_iat)) return 1; auto dn = get_name(dref); if (dn != 0 && dn != "") { if (strstr(dn, "purecall") != -1) return 1; if (strstr(dn, "pure_virtual") != -1) return 1; } dref = get_next_dref_from(faddr, dref); } auto tgt = get_first_fcref_from(faddr); if (tgt != BADADDR) { if (is_known_purecall_addr(tgt)) return 1; auto tn = get_func_name(tgt); if (tn != 0 && tn != "") { if (strstr(tn, "purecall") != -1) return 1; if (strstr(tn, "pure_virtual") != -1) return 1; } } } return 0; } // ========================================================================= // DESTRUTOR // ========================================================================= static is_destructor(faddr) { if (faddr == BADADDR || faddr == 0) return 0; auto name = get_func_name(faddr); if (name != 0 && name != "") { if (strstr(name, "::~") != -1) return 1; if (strstr(name, "??1") != -1) return 1; if (strstr(name, "??_E") != -1) return 1; if (strstr(name, "??_G") != -1) return 1; if (strstr(name, "destructor") != -1) return 1; if (strstr(name, "scalar_dtor")!= -1) return 1; if (strstr(name, "vector_dtor")!= -1) return 1; if (strstr(name, "__dtor") != -1) return 1; } auto fend = get_func_attr(faddr, FUNCATTR_END); if (fend == BADADDR) return 0; if ((fend - faddr) > 0x200) return 0; auto ea = faddr; auto guard = 0; while (ea < fend && ea != BADADDR && guard < 80) { auto m = print_insn_mnem(ea); if (m == "call" || m == "jmp") { auto tgt = get_first_fcref_from(ea); if (tgt != BADADDR) { auto tn = get_func_name(tgt); if (tn != 0 && tn != "") { if (strstr(tn, "operator delete") != -1) return 1; if (strstr(tn, "??3@") != -1) return 1; if (strstr(tn, "??_V") != -1) return 1; if (tn == "_free" || tn == "free" || tn == "_free_base") return 1; } } } ea = next_head(ea, fend); guard = guard + 1; } return 0; } // ========================================================================= // DETECCAO DE SIZEOF VIA operator new // Pattern em x86 thiscall: // push <imm> ; size // call ?? operator new // add esp, 4 ; (opcional) // mov ecx, eax ; this = obj // call <ctor> // ========================================================================= static find_object_size_from_call(call_ea) { auto fstart = get_func_attr(call_ea, FUNCATTR_START); if (fstart == BADADDR) fstart = call_ea - 0x80; auto cur = call_ea; auto guard = 0; while (guard < 15) { cur = prev_head(cur, fstart); if (cur == BADADDR || cur == 0 || cur < fstart) break; auto m = print_insn_mnem(cur); if (m == "ret" || m == "jmp") break; if (m == "call") { auto tgt = get_first_fcref_from(cur); if (tgt != BADADDR) { auto tn = get_func_name(tgt); if (tn != 0 && tn != "") { auto is_alloc = 0; if (strstr(tn, "operator new") != -1) is_alloc = 1; if (strstr(tn, "??2@") != -1) is_alloc = 1; if (strstr(tn, "_malloc") != -1) is_alloc = 1; if (tn == "malloc" || tn == "_malloc") is_alloc = 1; if (is_alloc) { auto pcur = cur; auto pg = 0; while (pg < 6) { pcur = prev_head(pcur, fstart); if (pcur == BADADDR || pcur < fstart) break; auto pm = print_insn_mnem(pcur); if (!g_is_64 && pm == "push" && get_operand_type(pcur, 0) == o_imm) { return get_operand_value(pcur, 0); } if (g_is_64 && pm == "mov" && get_operand_type(pcur, 0) == o_reg && get_operand_type(pcur, 1) == o_imm) { auto v = get_operand_value(pcur, 1); if (v > 0 && v < 0x10000) return v; } pg = pg + 1; } } } } return 0; } guard = guard + 1; } return 0; } static find_object_size_for_ctor(ctor_ea) { auto xr = get_first_cref_to(ctor_ea); while (xr != BADADDR) { auto sz = find_object_size_from_call(xr); if (sz > 0 && sz < 0x10000) return sz; xr = get_next_cref_to(ctor_ea, xr); } return 0; } // ========================================================================= // DETECCAO DE SINGLETON // Heuristica: depois da call ao construtor, se ha "mov ds:G, eax" com G // sendo um global valido, marca G como instancia singleton da classe. // ========================================================================= // Marca um global como singleton da classe. static mark_singleton(g_ea, class_name, ctor_ea) { if (DRY_RUN) return; auto cur_name = get_name(g_ea); if (cur_name == "" || starts_with(cur_name, "dword_") || starts_with(cur_name, "qword_") || starts_with(cur_name, "byte_") || starts_with(cur_name, "off_") || starts_with(cur_name, "unk_")) { set_name(g_ea, "g_" + class_name + "_instance", SN_NOWARN | SN_FORCE); } set_cmt(g_ea, "Singleton " + class_name + " ctor=0x" + ltoa(ctor_ea, 16), 1); } static detect_singleton_for_ctor(ctor_ea, class_name) { auto xr = get_first_cref_to(ctor_ea); while (xr != BADADDR) { auto caller_end = get_func_attr(xr, FUNCATTR_END); if (caller_end == BADADDR) caller_end = xr + 0x100; auto tracked = "|eax|rax|ecx|rcx|"; auto guard = 0; auto cur = xr; while (guard < 8) { cur = next_head(cur, caller_end); if (cur == BADADDR || cur >= caller_end) break; auto m = print_insn_mnem(cur); if (m == "call" || m == "ret" || m == "jmp") break; if (m == "mov") { auto t0 = get_operand_type(cur, 0); auto t1 = get_operand_type(cur, 1); if (t0 == o_mem && t1 == o_reg) { auto src_needle = "|" + print_operand(cur, 1) + "|"; if (strstr(tracked, src_needle) != -1) { auto g_ea = get_operand_value(cur, 0); if (is_loaded(g_ea) && g_ea > 0x1000) { mark_singleton(g_ea, class_name, ctor_ea); return g_ea; } } } if (t0 == o_reg && t1 == o_reg) { auto src_needle2 = "|" + print_operand(cur, 1) + "|"; if (strstr(tracked, src_needle2) != -1) { auto dst_needle = "|" + print_operand(cur, 0) + "|"; if (strstr(tracked, dst_needle) == -1) { tracked = tracked + substr(dst_needle, 1, strlen(dst_needle)); } } } } guard = guard + 1; } xr = get_next_cref_to(ctor_ea, xr); } return BADADDR; } // ========================================================================= // APLICACAO DE __thiscall + tipo "this" NAS FUNCOES VIRTUAIS // ========================================================================= // FIX A1: nao caimos mais em SetType (pode nao existir em IDA 9.x). // FIX A2: usamos TINFO_GUESSED (0) para nao sobrescrever tipos que o // usuario tenha definido manualmente. static apply_type_safe(ea, decl) { auto ti = parse_decl(decl, 0); if (ti == 0) return 0; return apply_type(ea, ti, 0); } static ensure_class_struct(sname_class) { auto sname = sname_class + "_t"; auto sid = get_struc_id(sname); if (sid != BADADDR) return sid; sid = add_struc(-1, sname, 0); if (sid == BADADDR) return BADADDR; auto flag = g_is_64 ? (FF_QWORD | FF_DATA) : (FF_DWORD | FF_DATA); add_struc_member(sid, "vptr", 0, flag, -1, g_ptr_size); return sid; } static apply_thiscall_to_vtable(vt_ea) { if (!APPLY_THISCALL_TYPES || DRY_RUN) return 0; auto sname = hier_name(vt_ea); auto count = hier_count(vt_ea); if (sname == "" || count == 0) return 0; auto effective_sname = sname; auto col_offset = hier_offset(vt_ea); if (col_offset != 0) { auto col = hier_col(vt_ea); if (col != BADADDR) { auto ptd = find_parent_td_for_offset(col, col_offset); if (ptd != 0) { auto pvt = hier_vt_by_td(ptd); if (pvt != BADADDR) { auto pn = hier_name(pvt); if (pn != "") effective_sname = pn; } } } } auto sid = ensure_class_struct(effective_sname); if (sid == BADADDR) return 0; auto applied = 0; auto i; for (i = 0; i < count; i = i + 1) { auto slot = vt_ea + i * g_ptr_size; auto faddr = read_ptr(slot); if (faddr == 0 || faddr == BADADDR) continue; if (is_pure_call(faddr)) continue; auto decl = "void __thiscall sub(struct " + effective_sname + "_t *this);"; if (apply_type_safe(faddr, decl)) applied = applied + 1; } return applied; } static apply_all_thiscall_types() { if (!APPLY_THISCALL_TYPES) return; log_info("=== Aplicando __thiscall + tipo this (em ordem topologica) ==="); auto tcount = topo_count(); auto total = (tcount > 0) ? tcount : g_vt_list_count; auto i; for (i = 0; i < total; i = i + 1) { if (user_cancelled()) return; auto vt = (tcount > 0) ? topo_at(i) : hier_vt_at(i); if (vt != BADADDR) { auto n = apply_thiscall_to_vtable(vt); g_stat_thiscall = g_stat_thiscall + n; } if ((i & 0x1F) == 0) { msg("[thiscall] %d/%d classes | %d metodos\n", i, total, g_stat_thiscall); } } log_info("__thiscall aplicado em " + ltoa(g_stat_thiscall, 10) + " metodos"); } // ========================================================================= // ANOTACAO DE CHAMADAS VIRTUAIS (call dword ptr [reg+N]) // ========================================================================= // // Plausibilidade v2: rastreia o registrador-base do call ate sua origem. // // 1) extrai o reg-base de "call [reg+N]" (ex: "eax" em "[eax+0Ch]") // 2) caminha pra tras ate o inicio da funcao (ou ate ret/jmp/call), // acompanhando o conjunto de regs que carregam aquele valor (lida // com propagacoes "mov ebx, eax" ao longo do prologo) // 3) declara plausivel quando encontra o write final que veio de // memoria (mov reg, [reg2+disp] ou lea reg, [reg2+disp]) - o // vptr load // 4) se o write veio de imediato/aritmetica/etc, descarta // // Janela aumentada pra 32 instrucoes (ou ate o inicio da funcao). // ========================================================================= static get_call_displ_base_reg(ea) { auto op = print_operand(ea, 0); if (op == 0 || op == "") return ""; auto bp = strstr(op, "["); if (bp == -1) return ""; auto i = bp + 1; auto n = strlen(op); auto reg = ""; while (i < n) { auto c = substr(op, i, i + 1); if ((c >= "a" && c <= "z") || (c >= "A" && c <= "Z") || (c >= "0" && c <= "9")) { reg = reg + c; } else { break; } i = i + 1; } return reg; } static is_likely_vcall(call_ea) { auto call_reg = get_call_displ_base_reg(call_ea); if (call_reg == "") return 0; auto fstart = get_func_attr(call_ea, FUNCATTR_START); if (fstart == BADADDR) fstart = call_ea - 0x100; auto tracked = "|" + call_reg + "|"; auto cur = call_ea; auto guard = 0; while (guard < 32) { cur = prev_head(cur, fstart); if (cur == BADADDR || cur < fstart) break; auto m = print_insn_mnem(cur); if (m == "ret" || m == "jmp" || m == "call") break; if (m == "mov" || m == "lea") { auto t0 = get_operand_type(cur, 0); if (t0 == o_reg) { auto dst = print_operand(cur, 0); auto dst_needle = "|" + dst + "|"; if (strstr(tracked, dst_needle) != -1) { auto t1 = get_operand_type(cur, 1); if (t1 == o_phrase || t1 == o_displ) return 1; if (t1 == o_reg) { auto src = print_operand(cur, 1); auto src_needle = "|" + src + "|"; if (strstr(tracked, src_needle) == -1) { tracked = tracked + substr(src_needle, 1, strlen(src_needle)); } } else { return 0; } } } } guard = guard + 1; } return 0; } static build_slot_methods_map() { arr_init(ARR_SLOT_METHODS); auto i; for (i = 0; i < g_vt_list_count; i = i + 1) { auto vt = hier_vt_at(i); if (vt == BADADDR) continue; auto sname = hier_name(vt); auto count = hier_count(vt); auto j; for (j = 0; j < count; j = j + 1) { auto cur = arr_get_str(ARR_SLOT_METHODS, j); if (cur == "") { arr_set_str(ARR_SLOT_METHODS, j, sname); } else { if (strlen(cur) < 400) { arr_set_str(ARR_SLOT_METHODS, j, cur + "," + sname); } } } } } static get_slot_summary(slot) { auto cur = arr_get_str(ARR_SLOT_METHODS, slot); if (cur == "") return ""; auto out = ""; auto buf = ""; auto emitted = 0; auto total = 0; auto i; auto n = strlen(cur); for (i = 0; i <= n; i = i + 1) { auto c = (i < n) ? substr(cur, i, i + 1) : ","; if (c == ",") { if (buf != "") { total = total + 1; if (emitted < VCALL_MAX_METHODS) { if (out != "") out = out + ", "; out = out + buf + "::v" + ltoa(slot, 10); emitted = emitted + 1; } } buf = ""; } else { buf = buf + c; } } if (total > VCALL_MAX_METHODS) { out = out + " (+" + ltoa(total - VCALL_MAX_METHODS, 10) + " mais)"; } return out; } static annotate_virtual_calls() { if (!ANNOTATE_VCALLS || DRY_RUN) return; log_info("=== Anotando chamadas virtuais ==="); build_slot_methods_map(); auto annotated = 0; auto func_count = 0; auto f = get_next_func(0); while (f != BADADDR) { if (user_cancelled()) return; func_count = func_count + 1; auto end = get_func_attr(f, FUNCATTR_END); auto ea = f; while (ea < end && ea != BADADDR) { auto m = print_insn_mnem(ea); if (m == "call" && get_operand_type(ea, 0) == o_displ) { auto disp = get_operand_value(ea, 0); if (disp >= 0 && disp < 0x800 && (disp - ((disp / g_ptr_size) * g_ptr_size)) == 0) { auto slot = disp / g_ptr_size; auto summary = get_slot_summary(slot); if (summary != "" && is_likely_vcall(ea)) { auto cur_cmt = get_cmt(ea, 0); auto can_set = 0; if (cur_cmt == "" || cur_cmt == 0) can_set = 1; else if (starts_with(cur_cmt, "vcall ")) can_set = 1; if (can_set) { set_cmt(ea, "vcall slot[" + ltoa(slot, 10) + "]: " + summary, 0); annotated = annotated + 1; } } } } ea = next_head(ea, end); } f = get_next_func(f); if ((func_count & 0xFF) == 0) { msg("[vcall] %d funcoes | %d call sites anotados\n", func_count, annotated); } } g_stat_vcalls = annotated; log_info("vcall: " + ltoa(annotated, 10) + " call sites anotados"); } // ========================================================================= // GERACAO DE wyd_classes.h (topological) // ========================================================================= static emit_class_header_rec(hf, vt_ea) { if (arr_get_long(ARR_HDR_EMITTED, vt_ea) > 0) return; arr_set_long(ARR_HDR_EMITTED, vt_ea, 1); if (hier_offset(vt_ea) != 0) return; auto parents_csv = hier_parents(vt_ea); auto parent_names_buf = ""; auto parent_count = 0; auto buf = ""; auto i; auto n = strlen(parents_csv); for (i = 0; i <= n; i = i + 1) { auto c = (i < n) ? substr(parents_csv, i, i + 1) : ","; if (c == ",") { if (buf != "") { auto td = atol(buf); auto pvt = hier_vt_by_td(td); if (pvt != BADADDR) { emit_class_header_rec(hf, pvt); auto pname = hier_name(pvt); if (parent_names_buf == "") parent_names_buf = pname; else parent_names_buf = parent_names_buf + "|" + pname; parent_count = parent_count + 1; } } buf = ""; } else { buf = buf + c; } } auto cname = hier_name(vt_ea); auto count = hier_count(vt_ea); auto size = arr_get_long(ARR_VT2SIZE, vt_ea); auto sgl = arr_get_long(ARR_VT2SINGLETON, vt_ea); fprintf(hf, "// vtable=0x%x methods=%d", vt_ea, count); if (size > 0) fprintf(hf, " sizeof=0x%x", size); if (sgl != -1 && sgl != BADADDR) fprintf(hf, " singleton=0x%x", sgl); if (parent_count > 0) { fprintf(hf, " inherits="); auto pn = parent_names_buf; auto j; auto pbuf = ""; auto first_p = 1; auto pl = strlen(pn); for (j = 0; j <= pl; j = j + 1) { auto pc = (j < pl) ? substr(pn, j, j + 1) : "|"; if (pc == "|") { if (pbuf != "") { if (!first_p) fprintf(hf, ","); fprintf(hf, "%s", pbuf); first_p = 0; } pbuf = ""; } else { pbuf = pbuf + pc; } } } fprintf(hf, "\n"); fprintf(hf, "struct vtable_%s_t;\n", cname); fprintf(hf, "struct %s {\n", cname); fprintf(hf, " struct vtable_%s_t *vptr;\n", cname); if (size > g_ptr_size) { fprintf(hf, " char data[0x%x];\n", size - g_ptr_size); } fprintf(hf, " // %d virtual methods:\n", count); auto k; for (k = 0; k < count; k = k + 1) { auto slot = vt_ea + k * g_ptr_size; auto faddr = read_ptr(slot); auto fname = get_func_name(faddr); auto pos = strstr(fname, "__"); auto mp; if (pos != -1) mp = substr(fname, pos + 2, strlen(fname)); else if (fname != "" && fname != 0) mp = fname; else mp = "vmethod_" + ltoa(k, 10); fprintf(hf, " // [%d] @ 0x%x %s", k, faddr, mp); if (is_pure_call(faddr)) fprintf(hf, " (pure)"); fprintf(hf, "\n"); } fprintf(hf, "};\n\n"); g_stat_header_classes = g_stat_header_classes + 1; } static emit_cpp_header(out_path) { if (!EMIT_CPP_HEADER) return; log_info("=== Gerando header C++ ==="); auto hf = fopen(out_path, "w"); if (hf == 0) { log_warn("Nao consegui abrir " + out_path); return; } fprintf(hf, "// =====================================================\n"); fprintf(hf, "// Gerado por Vtable Analyzer Pro v5.1\n"); fprintf(hf, "// Total de classes: %d\n", g_vt_list_count); fprintf(hf, "// Use no IDA: File > Load file > Parse C header file...\n"); fprintf(hf, "// para importar como Local Types.\n"); fprintf(hf, "// =====================================================\n"); fprintf(hf, "\n#pragma pack(push, 1)\n\n"); arr_init(ARR_HDR_EMITTED); auto i; for (i = 0; i < g_vt_list_count; i = i + 1) { if (user_cancelled()) break; auto vt = hier_vt_at(i); if (vt != BADADDR) emit_class_header_rec(hf, vt); } fprintf(hf, "#pragma pack(pop)\n"); fclose(hf); log_info("Header gerado: " + out_path + " (" + ltoa(g_stat_header_classes, 10) + " classes)"); } // ========================================================================= // CONSTRUTOR / DESTRUTOR via xref // ========================================================================= static rename_constructors(vtable_ea, sname_class) { auto xr = get_first_dref_to(vtable_ea); auto ctor_idx = 0; auto dtor_idx = 0; auto first_ctor = BADADDR; while (xr != BADADDR) { auto fstart = get_func_attr(xr, FUNCATTR_START); auto xr_mnem = print_insn_mnem(xr); auto vtable_assign = (xr_mnem == "mov" || xr_mnem == "lea"); if (fstart != BADADDR && vtable_assign) { auto fend = get_func_attr(fstart, FUNCATTR_END); auto fsize = fend - fstart; auto rel = xr - fstart; auto early = (fsize > 0) && (rel < (fsize / 2 + 8) || rel < 64); if (early) { auto cur_name = get_func_name(fstart); auto auto_named = (cur_name == "" || starts_with(cur_name, "sub_")); auto is_dtor = is_destructor(fstart); if (is_dtor) { g_stat_dtors = g_stat_dtors + 1; if ((auto_named || RENAME_OVERRIDES) && !DRY_RUN) { auto dname; if (dtor_idx == 0) dname = sname_class + "__dtor"; else dname = sname_class + "__dtor_" + ltoa(dtor_idx, 10); set_name(fstart, dname, SN_NOWARN | SN_FORCE); set_color(fstart, CIC_FUNC, COLOR_DTOR); log_info(" [DTOR-renamed] " + dname + " @ 0x" + ltoa(fstart, 16)); } else { if (!DRY_RUN) set_color(fstart, CIC_FUNC, COLOR_DTOR); log_info(" [DTOR-detected] " + cur_name + " @ 0x" + ltoa(fstart, 16)); } dtor_idx = dtor_idx + 1; } else { g_stat_ctors = g_stat_ctors + 1; if ((auto_named || RENAME_OVERRIDES) && !DRY_RUN) { auto cname2; if (ctor_idx == 0) cname2 = sname_class + "__ctor"; else cname2 = sname_class + "__ctor_" + ltoa(ctor_idx, 10); set_name(fstart, cname2, SN_NOWARN | SN_FORCE); set_color(fstart, CIC_FUNC, COLOR_CTOR); log_info(" [CTOR-renamed] " + cname2 + " @ 0x" + ltoa(fstart, 16)); } else { if (!DRY_RUN) set_color(fstart, CIC_FUNC, COLOR_CTOR); log_info(" [CTOR-detected] " + cur_name + " @ 0x" + ltoa(fstart, 16)); } ctor_idx = ctor_idx + 1; if (first_ctor == BADADDR) first_ctor = fstart; } } } xr = get_next_dref_to(vtable_ea, xr); } if (first_ctor != BADADDR) { if (DETECT_OBJECT_SIZES) { auto size = find_object_size_for_ctor_v2(first_ctor); if (size > 0 && size < 0x10000) { arr_set_long(ARR_VT2SIZE, vtable_ea, size); if (!DRY_RUN) { set_cmt(vtable_ea, sprintf("vtable de %s sizeof=0x%X (%d bytes)", sname_class, size, size), 1); } g_stat_sizes = g_stat_sizes + 1; log_info(" [SIZE] sizeof(" + sname_class + ") = 0x" + ltoa(size, 16)); } } if (DETECT_SINGLETONS) { auto sgl = detect_singleton_for_ctor(first_ctor, sname_class); if (sgl == BADADDR) { sgl = detect_singleton_aggressive(first_ctor, sname_class); } if (sgl != BADADDR) { arr_set_long(ARR_VT2SINGLETON, vtable_ea, sgl); g_stat_singletons = g_stat_singletons + 1; log_info(" [SGTN] " + sname_class + " instance @ 0x" + ltoa(sgl, 16)); } } } } // ========================================================================= // TAMANHO DA VTABLE // ========================================================================= static is_another_vtable_starting_here(slot_ea) { auto xr = get_first_dref_to(slot_ea); while (xr != BADADDR) { auto sn = seg_name_of(xr); if (strstr(sn, ".rdata") != -1 || strstr(sn, ".data") != -1) return 1; xr = get_next_dref_to(slot_ea, xr); } return 0; } static vtable_size(vtable_ea) { auto count = 0; auto cur = vtable_ea; while (count < MAX_VTABLE_METHODS) { auto p = read_ptr(cur); if (!is_valid_code_ptr(p)) break; if (count > 0 && is_another_vtable_starting_here(cur)) break; count = count + 1; cur = cur + g_ptr_size; } return count; } // ========================================================================= // STRUCT DA VTABLE // ========================================================================= static create_vtable_struct(sname_class, vtable_ea, count) { if (!CREATE_STRUCTS) return; if (DRY_RUN) return; auto sname = "vtable_" + sname_class + "_t"; auto sid = get_struc_id(sname); if (sid != BADADDR) del_struc(sid); sid = add_struc(-1, sname, 0); if (sid == BADADDR) { log_warn(" Falha ao criar struct " + sname); return; } auto i; for (i = 0; i < count; i = i + 1) { auto slot = vtable_ea + (i * g_ptr_size); auto faddr = read_ptr(slot); auto fname = get_func_name(faddr); auto member; if (fname == 0 || fname == "" || starts_with(fname, "sub_")) { member = "vmethod_" + ltoa(i, 10); } else { auto pos = strstr(fname, "::"); if (pos != -1) member = substr(fname, pos + 2, strlen(fname)); else member = fname; member = sanitize_name(member); } auto base_member = member; auto suffix = 0; while (get_member_offset(sid, member) != -1 && suffix < 64) { suffix = suffix + 1; member = base_member + "_" + ltoa(suffix, 10); } auto flag = g_is_64 ? (FF_QWORD | FF_DATA) : (FF_DWORD | FF_DATA); add_struc_member(sid, member, i * g_ptr_size, flag, -1, g_ptr_size); } log_info(" [STRUCT] " + sname + " (" + ltoa(count, 10) + " slots)"); } // ========================================================================= // EMISSAO DE JSON (uma classe) // ========================================================================= // // FIX item 2: emit_parents_json itera direto sobre o CSV numerico de TDs, // sem nunca passar pela string "pretty" (que tinha ", " e quebrava com // templates demangled). // static emit_parents_json(jf, csv) { if (jf == 0 || csv == "" || csv == 0) return; auto buf = ""; auto first = 1; auto i; auto n = strlen(csv); for (i = 0; i <= n; i = i + 1) { auto c = (i < n) ? substr(csv, i, i + 1) : ","; if (c == ",") { if (buf != "") { auto td = atol(buf); auto mang = read_typedesc_name(td); auto nm = (mang != "") ? demangle_class_name(mang) : "??"; if (!first) fprintf(jf, ", "); fprintf(jf, "\"%s\"", json_escape(nm)); first = 0; } buf = ""; } else { buf = buf + c; } } } static emit_class_json(cname, sname, mangled, vt_ea, col_ea, count, parents_csv) { auto jf = g_json_handle; if (jf == 0) return; if (!g_json_first) fprintf(jf, ",\n"); g_json_first = 0; fprintf(jf, " {\n"); fprintf(jf, " \"class\": \"%s\",\n", json_escape(cname)); fprintf(jf, " \"sanitized\": \"%s\",\n", json_escape(sname)); fprintf(jf, " \"mangled\": \"%s\",\n", json_escape(mangled)); fprintf(jf, " \"vtable_ea\": \"0x%x\",\n", vt_ea); fprintf(jf, " \"col_ea\": \"0x%x\",\n", col_ea); fprintf(jf, " \"method_count\": %d,\n", count); fprintf(jf, " \"parents\": ["); emit_parents_json(jf, parents_csv); fprintf(jf, "],\n"); auto col_offset_v = arr_get_long(ARR_VT2OFFSET, vt_ea); if (col_offset_v == -1) col_offset_v = 0; if (col_offset_v != 0) { fprintf(jf, " \"is_secondary\": true,\n"); fprintf(jf, " \"sub_offset\": %d,\n", col_offset_v); } auto size_v = arr_get_long(ARR_VT2SIZE, vt_ea); auto sgl_v = arr_get_long(ARR_VT2SINGLETON, vt_ea); if (size_v > 0) fprintf(jf, " \"sizeof\": %d,\n", size_v); if (sgl_v != -1 && sgl_v != BADADDR) fprintf(jf, " \"singleton_ea\": \"0x%x\",\n", sgl_v); fprintf(jf, " \"methods\": [\n"); auto j; for (j = 0; j < count; j = j + 1) { auto slot = vt_ea + (j * g_ptr_size); auto faddr = read_ptr(slot); auto kind = "vmethod"; if (is_pure_call(faddr)) kind = "pure"; else if (j <= 1 && is_destructor(faddr)) kind = "deleting_dtor"; fprintf(jf, " {\"index\": %d, \"slot_ea\": \"0x%x\", \"func_ea\": \"0x%x\", \"name\": \"%s\", \"kind\": \"%s\"}", j, slot, faddr, json_escape(get_func_name(faddr)), kind); if (j < count - 1) fprintf(jf, ","); fprintf(jf, "\n"); } fprintf(jf, " ]\n"); fprintf(jf, " }"); } // ========================================================================= // v5.3: FILTRO DE FRAMEWORK CLASSES // Evita poluir o output com classes do MFC/STL/ATL que o usuario raramente // quer mexer. Identificacao por prefixo de nome. // ========================================================================= static is_framework_class(name) { if (name == 0 || name == "") return 0; if (starts_with(name, "std::")) return 1; if (starts_with(name, "ATL::")) return 1; if (starts_with(name, "Gdiplus::")) return 1; if (starts_with(name, "Json::")) return 1; if (starts_with(name, "CMFC")) return 1; if (starts_with(name, "CMap<")) return 1; if (starts_with(name, "CArray<")) return 1; if (starts_with(name, "CList<")) return 1; if (starts_with(name, "CTypedPtr")) return 1; if (starts_with(name, "CResourcePool<")) return 1; if (starts_with(name, "DataPool<")) return 1; if (starts_with(name, "COle")) return 1; if (starts_with(name, "CDoc")) return 1; if (starts_with(name, "CDock")) return 1; if (starts_with(name, "CWnd")) return 1; if (starts_with(name, "CWin")) return 1; if (starts_with(name, "CCmd")) return 1; if (starts_with(name, "_AFX")) return 1; if (starts_with(name, "AFX_")) return 1; if (strstr(name, "tpl_") != -1) return 1; if (name == "type_info") return 1; return 0; } // ========================================================================= // v5.3: SIZEOF DETECTOR v2 // Tenta multiplos padroes pra detectar sizeof do objeto: // 1. push imm + call new (padrao MSVC stdlib) // 2. mov ecx/edx, imm + call <custom_alloc> // 3. mov [esp], imm + call <alloc> // 4. (fallback) inferir pelo max offset escrito em [this+N] no body do ctor // ========================================================================= static find_object_size_v2_caller(call_ea) { auto fstart = get_func_attr(call_ea, FUNCATTR_START); if (fstart == BADADDR) fstart = call_ea - 0x80; auto cur = call_ea; auto guard = 0; while (guard < 20) { cur = prev_head(cur, fstart); if (cur == BADADDR || cur == 0 || cur < fstart) break; auto m = print_insn_mnem(cur); if (m == "ret" || m == "jmp") break; if (m == "call") { auto tgt = get_first_fcref_from(cur); if (tgt != BADADDR) { auto tn = get_func_name(tgt); if (tn != 0 && tn != "") { auto is_alloc = 0; if (strstr(tn, "operator new") != -1) is_alloc = 1; if (strstr(tn, "??2@") != -1) is_alloc = 1; if (strstr(tn, "_malloc") != -1) is_alloc = 1; if (tn == "malloc" || tn == "_malloc") is_alloc = 1; if (strstr(tn, "Alloc") != -1) is_alloc = 1; if (strstr(tn, "alloc") != -1) is_alloc = 1; if (strstr(tn, "MemNew") != -1) is_alloc = 1; if (is_alloc) { auto pcur = cur; auto pg = 0; while (pg < 8) { pcur = prev_head(pcur, fstart); if (pcur == BADADDR || pcur < fstart) break; auto pm = print_insn_mnem(pcur); if (pm == "push" && get_operand_type(pcur, 0) == o_imm) { return get_operand_value(pcur, 0); } if (pm == "mov" && get_operand_type(pcur, 0) == o_reg && get_operand_type(pcur, 1) == o_imm) { auto v = get_operand_value(pcur, 1); if (v > 0 && v < 0x10000) return v; } if (pm == "mov" && get_operand_type(pcur, 0) == o_displ && get_operand_type(pcur, 1) == o_imm) { auto v2 = get_operand_value(pcur, 1); if (v2 > 0 && v2 < 0x10000) return v2; } pg = pg + 1; } } } } return 0; } guard = guard + 1; } return 0; } static infer_size_from_ctor_body(ctor_ea) { if (!SIZE_INFER_FROM_BODY) return 0; auto fend = get_func_attr(ctor_ea, FUNCATTR_END); if (fend == BADADDR) return 0; if ((fend - ctor_ea) > 0x800) return 0; auto max_off = 0; auto ea = ctor_ea; auto guard = 0; while (ea < fend && ea != BADADDR && guard < SIZE_BODY_SCAN_LIMIT) { auto i; for (i = 0; i < 2; i = i + 1) { if (get_operand_type(ea, i) == o_displ) { auto disp = get_operand_value(ea, i); if (disp > max_off && disp < 0x10000) { auto op_str = print_operand(ea, i); if (strstr(op_str, "ecx") != -1 || strstr(op_str, "esi") != -1 || strstr(op_str, "edi") != -1 || strstr(op_str, "ebx") != -1) { max_off = disp; } } } } ea = next_head(ea, fend); guard = guard + 1; } if (max_off == 0) return 0; return max_off + g_ptr_size; } static find_object_size_for_ctor_v2(ctor_ea) { auto xr = get_first_cref_to(ctor_ea); while (xr != BADADDR) { auto sz = find_object_size_v2_caller(xr); if (sz > 0 && sz < 0x10000) return sz; xr = get_next_cref_to(ctor_ea, xr); } auto body_sz = infer_size_from_ctor_body(ctor_ea); if (body_sz > 0) return body_sz; return 0; } // ========================================================================= // v5.3: SINGLETON DETECTOR v2 (mais agressivo) // Alem de "mov ds:G, eax" pos-ctor, detecta: // - Funcao curta que retorna sempre o mesmo global ("GetInstance" pattern) // - Lazy init pattern: "if (!g_X) g_X = new X(); return g_X;" // ========================================================================= static detect_singleton_aggressive(ctor_ea, class_name) { if (!SINGLETON_AGGRESSIVE) return BADADDR; auto xr = get_first_cref_to(ctor_ea); while (xr != BADADDR) { auto caller_start = get_func_attr(xr, FUNCATTR_START); if (caller_start == BADADDR) { xr = get_next_cref_to(ctor_ea, xr); continue; } auto caller_end = get_func_attr(caller_start, FUNCATTR_END); auto caller_size = caller_end - caller_start; if (caller_size > 0 && caller_size < 0x100) { auto cur = caller_start; auto g = 0; while (cur < caller_end && cur != BADADDR && g < 30) { auto m = print_insn_mnem(cur); if (m == "mov" && get_operand_type(cur, 0) == o_reg && get_operand_type(cur, 1) == o_mem) { auto g_ea = get_operand_value(cur, 1); if (is_loaded(g_ea) && g_ea > 0x1000) { auto cn = get_func_name(caller_start); if (cn != "" && (strstr(cn, "etInstance") != -1 || strstr(cn, "etSingleton") != -1 || strstr(cn, "_instance") != -1 || caller_size < 0x40)) { mark_singleton(g_ea, class_name, ctor_ea); return g_ea; } } } cur = next_head(cur, caller_end); g = g + 1; } } xr = get_next_cref_to(ctor_ea, xr); } return BADADDR; } // ========================================================================= // ANALISE DE UMA VTABLE // ========================================================================= static analyze_vtable(ea) { if (cache_seen(ea)) return 0; auto first_ptr = read_ptr(ea); if (!is_valid_code_ptr(first_ptr)) return 0; auto col_ea = read_ptr(ea - g_ptr_size); if (!validate_col(col_ea)) return 0; auto type_desc = col_read_field(col_ea, 0x0C); auto mangled = read_typedesc_name(type_desc); if (mangled == "") return 0; auto cname = demangle_class_name(mangled); if (cname == "") return 0; if (EXCLUDE_FRAMEWORK_CLASSES && is_framework_class(cname)) { g_stat_filtered = g_stat_filtered + 1; cache_mark(ea); return 0; } auto col_offset = get_wide_dword(col_ea + 0x04); auto sname = sanitize_name(cname); auto display_name = cname; if (col_offset != 0) { sname = sname + "_sub_" + ltoa(col_offset, 16); display_name = cname + " [secondary @ +0x" + ltoa(col_offset, 16) + "]"; } auto count = vtable_size(ea); if (count < MIN_VTABLE_METHODS) return 0; auto parents_csv = parse_base_classes(col_ea); auto parents_pretty; if (col_offset != 0) { auto ptd = find_parent_td_for_offset(col_ea, col_offset); if (ptd != 0) { auto pmang = read_typedesc_name(ptd); parents_pretty = (pmang != "") ? demangle_class_name(pmang) : "??"; } else { parents_pretty = ""; } } else { parents_pretty = parents_csv_to_names(parents_csv); } log_info("Classe: " + display_name + " (" + ltoa(count, 10) + " metodos) vtable=0x" + ltoa(ea, 16) + " COL=0x" + ltoa(col_ea, 16)); log_info(" mangled = " + mangled); if (parents_pretty != "") { if (col_offset != 0) log_info(" base = " + parents_pretty); else log_info(" pais = " + parents_pretty); } g_stat_classes = g_stat_classes + 1; g_stat_methods = g_stat_methods + count; if (!DRY_RUN) { set_name(ea, "vtable_" + sname, SN_NOWARN | SN_FORCE); set_name(col_ea, "RTTI_COL_" + sname, SN_NOWARN | SN_FORCE); set_color(col_ea, CIC_ITEM, COLOR_RTTI_COL); set_cmt(col_ea, "CompleteObjectLocator de " + cname + (col_offset != 0 ? sprintf(" (offset 0x%X)", col_offset) : ""), 1); if (col_offset == 0) { set_name(type_desc, "RTTI_TD_" + sname, SN_NOWARN | SN_FORCE); set_color(type_desc, CIC_ITEM, COLOR_TYPE_DESC); set_cmt(type_desc, "TypeDescriptor de " + cname, 1); } if (col_offset != 0) { set_cmt(ea, "vtable secundaria de " + cname + " base " + (parents_pretty != "" ? parents_pretty : "??") + " @ offset 0x" + ltoa(col_offset, 16), 1); } else if (parents_pretty != "") { set_cmt(ea, "vtable de " + cname + " herda de: " + parents_pretty, 1); } else { set_cmt(ea, "vtable de " + cname, 1); } } auto i; for (i = 0; i < count; i = i + 1) { auto slot = ea + (i * g_ptr_size); auto faddr = read_ptr(slot); auto vname; auto vcolor = COLOR_VFUNC; if (is_pure_call(faddr)) { vname = sname + "__pure_v" + ltoa(i, 10); vcolor = COLOR_PURE; g_stat_pures = g_stat_pures + 1; } else if (i <= 1 && is_destructor(faddr)) { vname = sname + "__deleting_dtor_v" + ltoa(i, 10); vcolor = COLOR_DTOR; } else { vname = sname + "__vmethod_" + ltoa(i, 10); } auto cur_name = get_func_name(faddr); auto can_rename = (cur_name == "" || starts_with(cur_name, "sub_") || RENAME_OVERRIDES); if (!DRY_RUN && can_rename) { if (set_name(faddr, vname, SN_NOWARN | SN_FORCE)) { g_stat_renamed = g_stat_renamed + 1; } } if (!DRY_RUN) { set_color(slot, CIC_ITEM, COLOR_VTABLE_SLOT); set_color(faddr, CIC_FUNC, vcolor); set_cmt(slot, sprintf("[%d] +0x%X -> %s", i, i * g_ptr_size, vname), 0); } } create_vtable_struct(sname, ea, count); if (col_offset == 0) rename_constructors(ea, sname); hier_register(type_desc, ea, col_ea, sname, count, parents_csv, col_offset); emit_class_json(display_name, sname, mangled, ea, col_ea, count, parents_csv); cache_mark(ea); return count; } // ========================================================================= // STRING-FIRST: helpers // ========================================================================= // v5.2 DEBUG: loga em detalhe cada xref do TD e o resultado da validacao // pra entender por que estamos perdendo as vtables. static validate_col_verbose(col_ea) { if (!is_valid_data_ptr(col_ea)) { msg(" [reject] data_ptr invalido\n"); return 0; } auto sig = get_wide_dword(col_ea); auto expected = g_is_64 ? 1 : 0; if (sig != expected) { msg(" [reject] sig=0x%x esperado=%d\n", sig, expected); return 0; } auto offset = get_wide_dword(col_ea + 0x04); auto cdOffset = get_wide_dword(col_ea + 0x08); if (offset > 0x10000) { msg(" [reject] offset=0x%x grande demais\n", offset); return 0; } if (cdOffset > 0x10000) { msg(" [reject] cdOffset=0x%x grande demais\n", cdOffset); return 0; } auto type_desc = col_read_field(col_ea, 0x0C); if (!is_valid_data_ptr(type_desc)) { msg(" [reject] type_desc=0x%x invalido\n", type_desc); return 0; } auto chd = col_read_field(col_ea, 0x10); if (!is_valid_data_ptr(chd)) { msg(" [reject] chd=0x%x invalido\n", chd); return 0; } auto chd_sig = get_wide_dword(chd); if (chd_sig != 0) { msg(" [reject] chd_sig=0x%x (esperado 0)\n", chd_sig); return 0; } auto nb = get_wide_dword(chd + 0x08); if (nb == 0 || nb > 256) { msg(" [reject] num_bases=%d\n", nb); return 0; } if (g_is_64) { auto self_rva = get_wide_dword(col_ea + 0x14); if (rva_to_ea(self_rva) != col_ea) { msg(" [reject] pSelf nao bate\n"); return 0; } } msg(" [accept] COL valido!\n"); return 1; } static process_typedesc_xrefs(td_ea) { auto found = 0; auto xr = get_first_dref_to(td_ea); while (xr != BADADDR) { auto col_candidate = xr - 0x0C; if (validate_col(col_candidate)) { auto xv = get_first_dref_to(col_candidate); while (xv != BADADDR) { auto vtable_candidate = xv + g_ptr_size; if (is_valid_code_ptr(read_ptr(vtable_candidate))) { if (analyze_vtable(vtable_candidate) > 0) found = found + 1; } xv = get_next_dref_to(col_candidate, xv); } } xr = get_next_dref_to(td_ea, xr); } return found; } static byte_at(ea) { return get_wide_byte(ea); } static scan_via_rtti_strings() { log_info("=== Modo string-first ==="); auto total = 0; auto strings_found = 0; auto iters = 0; auto seg; for (seg = get_first_seg(); seg != BADADDR; seg = get_next_seg(seg)) { auto sn = get_segm_name(seg); if (strstr(sn, ".rdata") == -1 && strstr(sn, ".data") == -1) continue; auto ss = get_segm_start(seg); auto se = get_segm_end(seg); if (STRING_SCAN_MAX_SEG_SIZE > 0 && (se - ss) > STRING_SCAN_MAX_SEG_SIZE) { log_warn("Pulando " + sn + " (tamanho " + ltoa(se - ss, 16) + " > limite " + ltoa(STRING_SCAN_MAX_SEG_SIZE, 16) + ")"); continue; } log_info("Procurando .?AV/.?AU em " + sn + " [0x" + ltoa(ss, 16) + " - 0x" + ltoa(se, 16) + "]"); auto ea = ss; while (ea < se - 8) { if (user_cancelled()) return total; iters = iters + 1; if ((iters % (PROGRESS_EVERY * 4)) == 0) { msg("[string-scan] 0x%x | strings: %d | classes: %d\n", ea, strings_found, g_stat_classes); } if (byte_at(ea) == 0x2E && byte_at(ea + 1) == 0x3F && byte_at(ea + 2) == 0x41 && (byte_at(ea + 3) == 0x56 || byte_at(ea + 3) == 0x55)) { auto mangled = read_c_string_raw(ea); if (mangled != 0 && mangled != "" && ends_with(mangled, "@@")) { strings_found = strings_found + 1; auto td_ea = ea - (g_ptr_size * 2); if (is_valid_data_ptr(td_ea)) { total = total + process_typedesc_xrefs(td_ea); } auto sl = strlen(mangled); ea = ea + sl + 1; } else { ea = ea + 1; } } else { ea = ea + 1; } } } log_info("string-first: " + ltoa(strings_found, 10) + " strings RTTI vistas, " + ltoa(total, 10) + " novas vtables analisadas"); return total; } // ========================================================================= // PONTEIRO-SEQUENCIAL // ========================================================================= static scan_via_pointers() { log_info("=== Modo ponteiro-sequencial ==="); auto total = 0; auto iters = 0; auto seg; for (seg = get_first_seg(); seg != BADADDR; seg = get_next_seg(seg)) { auto sn = get_segm_name(seg); auto match = (strstr(sn, ".rdata") != -1); if (SCAN_DATA_TOO && strstr(sn, ".data") != -1 && strstr(sn, ".rdata") == -1) match = 1; if (!match) continue; auto ss = get_segm_start(seg); auto se = get_segm_end(seg); log_info("Varrendo " + sn + " [0x" + ltoa(ss, 16) + " - 0x" + ltoa(se, 16) + "]"); auto ea = ss; while (ea < se) { if (user_cancelled()) return total; iters = iters + 1; if ((iters % PROGRESS_EVERY) == 0) { msg("[ponteiro-scan] 0x%x | classes: %d | metodos: %d\n", ea, g_stat_classes, g_stat_methods); } auto consumed = analyze_vtable(ea); if (consumed > 0) { total = total + 1; ea = ea + (consumed * g_ptr_size); } else { ea = ea + g_ptr_size; } } } log_info("ponteiro-scan: " + ltoa(total, 10) + " vtables analisadas"); return total; } // ========================================================================= // BRUTE-FORCE RTTI (x64 e x86) - fallback quando IDA nao tracou xrefs // // Estrategia identica em ambas arquiteturas, apenas a assinatura do COL // e o tamanho do ponteiro mudam: // // 1) varremos .rdata em alinhamento 4 procurando COL candidatos: // x64: signature == 1 e pSelf RVA consistente // x86: signature == 0 e validate_col passa // 2) para cada COL valido, tentamos xrefs; se nao houver, fazemos // um segundo scan procurando ptrs (qword em x64, dword em x86) // que apontam pro COL - isso eh vtable[-1]. // // ========================================================================= static align_up_4(ea) { auto rem = ea - ((ea / 4) * 4); if (rem == 0) return ea; return ea + (4 - rem); } static align_up_8(ea) { auto rem = ea - ((ea / 8) * 8); if (rem == 0) return ea; return ea + (8 - rem); } static brute_force_vt_for_col(col_ea) { auto found = 0; auto seg; for (seg = get_first_seg(); seg != BADADDR; seg = get_next_seg(seg)) { auto sn = get_segm_name(seg); if (strstr(sn, ".rdata") == -1) continue; auto ss = get_segm_start(seg); auto se = get_segm_end(seg); auto ea = g_is_64 ? align_up_8(ss) : align_up_4(ss); while (ea < se - g_ptr_size * 2) { if (user_cancelled()) return found; auto cur_val = read_ptr(ea); if (cur_val == col_ea) { auto vt = ea + g_ptr_size; if (is_valid_code_ptr(read_ptr(vt))) { if (analyze_vtable(vt) > 0) found = found + 1; } } ea = ea + g_ptr_size; } } return found; } static brute_force_rtti() { if (g_is_64) { if (!BRUTE_FORCE_X64_RVA) return 0; } else { if (!BRUTE_FORCE_X86) return 0; } auto expected_sig = g_is_64 ? 1 : 0; auto arch_label = g_is_64 ? "x64" : "x86"; log_info("=== Brute-force " + arch_label + " (COL signature=" + ltoa(expected_sig, 10) + ") ==="); auto found = 0; auto iters = 0; auto seg; for (seg = get_first_seg(); seg != BADADDR; seg = get_next_seg(seg)) { if (user_cancelled()) return found; auto sn = get_segm_name(seg); if (strstr(sn, ".rdata") == -1) continue; auto ss = get_segm_start(seg); auto se = get_segm_end(seg); log_info("Brute-scan em " + sn + " [0x" + ltoa(ss, 16) + " - 0x" + ltoa(se, 16) + "]"); auto ea = align_up_4(ss); while (ea < se - 24) { if (user_cancelled()) return found; iters = iters + 1; if ((iters % PROGRESS_EVERY) == 0) { msg("[brute-scan] 0x%x | achadas: %d\n", ea, g_stat_brute); } if (get_wide_dword(ea) == expected_sig) { auto cheap_ok = 1; if (g_is_64) { auto self_rva = get_wide_dword(ea + 0x14); if (rva_to_ea(self_rva) != ea) cheap_ok = 0; } if (cheap_ok && validate_col(ea)) { auto vt_found = 0; auto xv = get_first_dref_to(ea); while (xv != BADADDR) { auto vt = xv + g_ptr_size; if (is_valid_code_ptr(read_ptr(vt))) { if (analyze_vtable(vt) > 0) { vt_found = vt_found + 1; found = found + 1; g_stat_brute = g_stat_brute + 1; } } xv = get_next_dref_to(ea, xv); } if (vt_found == 0) { auto extra = brute_force_vt_for_col(ea); found = found + extra; g_stat_brute = g_stat_brute + extra; } } } ea = ea + 4; if ((ea & 0xFFFF) == 0) { } } } log_info("brute " + arch_label + ": " + ltoa(found, 10) + " novas vtables"); return found; } static brute_force_x64() { return brute_force_rtti(); } // ========================================================================= // PROPAGACAO DE OVERRIDES // // Para cada classe com pais, percorre o primeiro pai (ancestral imediato) // e compara slot a slot: // - Mesmo func ptr -> METODO HERDADO (apenas registra) // - Func ptr difere -> OVERRIDE (comenta no slot e na funcao) // // Tambem emite vtables_overrides.json com as relacoes encontradas. // ========================================================================= static emit_override_json(child_name, child_vt, slot_idx, parent_name, parent_func, child_func) { auto of = g_overrides_handle; if (of == 0) return; if (!g_overrides_first) fprintf(of, ",\n"); g_overrides_first = 0; fprintf(of, " {\"child\": \"%s\", \"slot\": %d, \"parent\": \"%s\", \"parent_func\": \"0x%x\", \"child_func\": \"0x%x\"}", child_name, slot_idx, parent_name, parent_func, child_func); } static first_parent_td(parents_csv) { if (parents_csv == "" || parents_csv == 0) return 0; auto pos = strstr(parents_csv, ","); auto first = (pos != -1) ? substr(parents_csv, 0, pos) : parents_csv; return atol(first); } static find_parent_td_for_offset(col_ea, target_offset) { if (target_offset == 0) return 0; auto chd = col_read_field(col_ea, 0x10); if (!is_valid_data_ptr(chd)) return 0; auto num_bases = get_wide_dword(chd + 0x08); if (num_bases <= 1 || num_bases > 64) return 0; auto bca_field = get_wide_dword(chd + 0x0C); auto bca_ea = g_is_64 ? rva_to_ea(bca_field) : bca_field; if (!is_valid_data_ptr(bca_ea)) return 0; auto i; for (i = 1; i < num_bases; i = i + 1) { auto bcd_field = get_wide_dword(bca_ea + i * 4); auto bcd_ea = g_is_64 ? rva_to_ea(bcd_field) : bcd_field; if (!is_valid_data_ptr(bcd_ea)) continue; auto mdisp = get_wide_dword(bcd_ea + 0x08); if (mdisp == target_offset) { auto td_field = get_wide_dword(bcd_ea); return g_is_64 ? rva_to_ea(td_field) : td_field; } } return 0; } // ========================================================================= // TOPOLOGICAL SORT (pais antes de filhos) // ========================================================================= static topo_dfs(vt_ea, count) { if (arr_get_long(ARR_TOPO_VISITED, vt_ea) > 0) return count; arr_set_long(ARR_TOPO_VISITED, vt_ea, 1); auto parents = hier_parents(vt_ea); auto buf = ""; auto i; auto n = strlen(parents); for (i = 0; i <= n; i = i + 1) { auto c = (i < n) ? substr(parents, i, i + 1) : ","; if (c == ",") { if (buf != "") { auto td = atol(buf); auto pvt = hier_vt_by_td(td); if (pvt != BADADDR) count = topo_dfs(pvt, count); } buf = ""; } else { buf = buf + c; } } arr_set_long(ARR_TOPO_ORDER, count, vt_ea); return count + 1; } static topo_build() { arr_init(ARR_TOPO_VISITED); arr_init(ARR_TOPO_ORDER); arr_init(ARR_TOPO_COUNT); auto count = 0; auto i; for (i = 0; i < g_vt_list_count; i = i + 1) { if (user_cancelled()) break; auto vt = hier_vt_at(i); if (vt != BADADDR) count = topo_dfs(vt, count); } arr_set_long(ARR_TOPO_COUNT, 0, count); log_info("Topological order: " + ltoa(count, 10) + " classes"); return count; } static topo_at(idx) { auto v = arr_get_long(ARR_TOPO_ORDER, idx); if (v == -1) return BADADDR; return v; } static topo_count() { auto v = arr_get_long(ARR_TOPO_COUNT, 0); if (v == -1) return 0; return v; } static propagate_overrides() { if (!PROPAGATE_OVERRIDES) return; if (g_vt_list_count == 0) return; auto tcount = topo_count(); if (tcount == 0) return; log_info("=== Passada de overrides em ordem topologica (" + ltoa(tcount, 10) + " classes) ==="); auto i; for (i = 0; i < tcount; i = i + 1) { if (user_cancelled()) return; auto vt_ea = topo_at(i); if (vt_ea == BADADDR) continue; auto parents_csv = hier_parents(vt_ea); if (parents_csv == "") continue; auto col_offset = hier_offset(vt_ea); auto parent_td; if (col_offset == 0) { parent_td = first_parent_td(parents_csv); } else { auto col_ea2 = hier_col(vt_ea); if (col_ea2 == BADADDR) continue; parent_td = find_parent_td_for_offset(col_ea2, col_offset); } if (parent_td == 0) continue; auto parent_vt = hier_vt_by_td(parent_td); if (parent_vt == BADADDR) { continue; } auto child_name = hier_name(vt_ea); auto parent_name = hier_name(parent_vt); auto own_count = hier_count(vt_ea); auto parent_count = hier_count(parent_vt); auto limit = (parent_count < own_count) ? parent_count : own_count; auto j; for (j = 0; j < limit; j = j + 1) { auto cslot = vt_ea + j * g_ptr_size; auto pslot = parent_vt + j * g_ptr_size; auto cfunc = read_ptr(cslot); auto pfunc = read_ptr(pslot); if (cfunc == pfunc) { g_stat_inherited = g_stat_inherited + 1; if (!DRY_RUN) { auto pname = get_func_name(pfunc); auto child_pattern = child_name + "__vmethod_" + ltoa(j, 10); if (pname == child_pattern) { auto canonical = parent_name + "__vmethod_" + ltoa(j, 10); if (set_name(pfunc, canonical, SN_NOWARN | SN_FORCE)) { g_stat_renamed_inherited = g_stat_renamed_inherited + 1; pname = canonical; } } set_cmt(cslot, sprintf("[%d] HERDADO de %s (-> %s)", j, parent_name, pname), 0); } } else { g_stat_overrides = g_stat_overrides + 1; if (!DRY_RUN) { auto pname2 = get_func_name(pfunc); set_cmt(cslot, sprintf("[%d] OVERRIDE de %s::%s", j, parent_name, pname2), 0); set_color(cslot, CIC_ITEM, COLOR_OVERRIDE); auto cfn = get_func_name(cfunc); auto rep = "Override de " + parent_name + "::" + pname2; auto existing = get_func_cmt(cfunc, 1); auto can_set_func_cmt = 0; if (existing == "" || existing == 0) can_set_func_cmt = 1; else if (starts_with(existing, "Override de ")) can_set_func_cmt = 1; if (can_set_func_cmt) set_func_cmt(cfunc, rep, 1); } emit_override_json(child_name, vt_ea, j, parent_name, pfunc, cfunc); } } } log_info("Overrides: " + ltoa(g_stat_overrides, 10) + " Herdados: " + ltoa(g_stat_inherited, 10)); } // ========================================================================= // MAIN // ========================================================================= static main() { if (CLEAR_OUTPUT_ON_START) { process_ui_action("msglist:Clear", 0); } msg("\n========================================\n"); msg("Vtable Analyzer Pro v5.3 - iniciando...\n"); msg("========================================\n"); if (REGISTER_HOTKEY) { add_idc_hotkey(HOTKEY_COMBO, "main"); msg("[INFO] Hotkey %s registrada para re-rodar o script\n", HOTKEY_COMBO); } g_is_64 = (get_inf_attr(INF_LFLAGS) & LFLG_64BIT) ? 1 : 0; g_ptr_size = g_is_64 ? 8 : 4; g_imagebase = get_imagebase(); g_json_first = 1; g_overrides_first = 1; g_vt_list_count = 0; g_stat_classes = 0; g_stat_methods = 0; g_stat_renamed = 0; g_stat_ctors = 0; g_stat_dtors = 0; g_stat_pures = 0; g_stat_overrides = 0; g_stat_inherited = 0; g_stat_brute = 0; g_stat_thiscall = 0; g_stat_sizes = 0; g_stat_singletons = 0; g_stat_vcalls = 0; g_stat_header_classes = 0; g_stat_renamed_inherited = 0; g_purecall_csv = ""; g_stat_filtered = 0; g_stat_errors = 0; auto out_dir = resolve_output_dir(); auto json_path = path_join(out_dir, OUTPUT_JSON_NAME); auto log_path = path_join(out_dir, OUTPUT_LOG_NAME); auto overrides_path = path_join(out_dir, OUTPUT_OVERRIDES_NAME); auto header_path = path_join(out_dir, OUTPUT_HEADER_NAME); msg("[INFO] Pasta de saida: %s\n", out_dir); msg("[INFO] JSON esperado em: %s\n", json_path); msg("[INFO] LOG esperado em: %s\n", log_path); msg("[INFO] OVERRIDES esperado em: %s\n", overrides_path); msg("[INFO] HEADER esperado em: %s\n", header_path); g_log_handle = safe_fopen(log_path, "w"); if (g_log_handle == 0) { msg("[WARN] Log em arquivo desabilitado, continuando so com console.\n"); } log_info("===================================================="); log_info(" Vtable Analyzer Pro v5.3 - IDA Free 9.x"); log_info("===================================================="); log_info("Arquitetura : " + (g_is_64 ? "x64" : "x86")); log_info("Ptr size : " + ltoa(g_ptr_size, 10)); log_info("Image base : 0x" + ltoa(g_imagebase, 16)); log_info("Pasta saida : " + out_dir); log_info("Dry run : " + (DRY_RUN ? "SIM" : "NAO")); log_info("Cria struct : " + (CREATE_STRUCTS ? "SIM" : "NAO")); log_info("Sobrescreve : " + (RENAME_OVERRIDES ? "SIM" : "NAO")); log_info("Ponteiro-scan: " + (POINTER_FIRST_SCAN ? "SIM" : "NAO")); log_info("String-scan : " + (STRING_FIRST_SCAN ? "SIM" : "NAO")); log_info("Overrides : " + (PROPAGATE_OVERRIDES ? "SIM" : "NAO")); log_info("Brute x64 : " + (BRUTE_FORCE_X64_RVA ? "SIM" : "NAO") + (g_is_64 ? "" : " (inerte em x86)")); log_info("Brute x86 : " + (BRUTE_FORCE_X86 ? "SIM" : "NAO") + (g_is_64 ? " (inerte em x64)" : "")); log_info("__thiscall : " + (APPLY_THISCALL_TYPES ? "SIM" : "NAO")); log_info("Sizeof obj : " + (DETECT_OBJECT_SIZES ? "SIM" : "NAO")); log_info("Singletons : " + (DETECT_SINGLETONS ? "SIM" : "NAO")); log_info("VCall annotat: " + (ANNOTATE_VCALLS ? "SIM" : "NAO")); log_info("CPP header : " + (EMIT_CPP_HEADER ? "SIM" : "NAO")); log_info("Excl framewrk: " + (EXCLUDE_FRAMEWORK_CLASSES ? "SIM" : "NAO")); log_info("Size inferenc: " + (SIZE_INFER_FROM_BODY ? "SIM" : "NAO")); log_info("Singl agressv: " + (SINGLETON_AGGRESSIVE ? "SIM" : "NAO")); cache_init(); init_purecall_addresses(); if (g_purecall_csv != "") { log_info("purecall enderecos conhecidos: " + g_purecall_csv); } g_json_handle = safe_fopen(json_path, "w"); if (g_json_handle != 0) { fprintf(g_json_handle, "{\n"); fprintf(g_json_handle, " \"meta\": {\n"); fprintf(g_json_handle, " \"arch\": \"%s\",\n", g_is_64 ? "x64" : "x86"); fprintf(g_json_handle, " \"ptr_size\": %d,\n", g_ptr_size); fprintf(g_json_handle, " \"imagebase\": \"0x%x\",\n", g_imagebase); fprintf(g_json_handle, " \"tool\": \"vtable_analyzer_pro_v5.2\",\n"); fprintf(g_json_handle, " \"stats_note\": \"methods=total slots; pures/inherited/overrides sao subconjuntos disjuntos de methods\"\n"); fprintf(g_json_handle, " },\n"); fprintf(g_json_handle, " \"classes\": [\n"); } g_overrides_handle = safe_fopen(overrides_path, "w"); if (g_overrides_handle != 0) { fprintf(g_overrides_handle, "{\n \"overrides\": [\n"); } if (POINTER_FIRST_SCAN) scan_via_pointers(); if (STRING_FIRST_SCAN) scan_via_rtti_strings(); if ((g_is_64 && BRUTE_FORCE_X64_RVA) || (!g_is_64 && BRUTE_FORCE_X86)) brute_force_rtti(); topo_build(); propagate_overrides(); apply_all_thiscall_types(); annotate_virtual_calls(); emit_cpp_header(header_path); if (g_json_handle != 0) { fprintf(g_json_handle, "\n ],\n"); fprintf(g_json_handle, " \"stats\": {\n"); fprintf(g_json_handle, " \"classes\": %d,\n", g_stat_classes); fprintf(g_json_handle, " \"methods\": %d,\n", g_stat_methods); fprintf(g_json_handle, " \"renamed\": %d,\n", g_stat_renamed); fprintf(g_json_handle, " \"ctors\": %d,\n", g_stat_ctors); fprintf(g_json_handle, " \"dtors\": %d,\n", g_stat_dtors); fprintf(g_json_handle, " \"pures\": %d,\n", g_stat_pures); fprintf(g_json_handle, " \"overrides\": %d,\n", g_stat_overrides); fprintf(g_json_handle, " \"inherited\": %d,\n", g_stat_inherited); fprintf(g_json_handle, " \"brute_x64\": %d,\n", g_stat_brute); fprintf(g_json_handle, " \"thiscall\": %d,\n", g_stat_thiscall); fprintf(g_json_handle, " \"sizes\": %d,\n", g_stat_sizes); fprintf(g_json_handle, " \"singletons\":%d,\n", g_stat_singletons); fprintf(g_json_handle, " \"vcalls\": %d,\n", g_stat_vcalls); fprintf(g_json_handle, " \"hdr_class\": %d,\n", g_stat_header_classes); fprintf(g_json_handle, " \"renamed_inherited\": %d,\n", g_stat_renamed_inherited); fprintf(g_json_handle, " \"methods_concrete\": %d,\n", g_stat_methods - g_stat_pures); fprintf(g_json_handle, " \"filtered\": %d,\n", g_stat_filtered); fprintf(g_json_handle, " \"errors\": %d\n", g_stat_errors); fprintf(g_json_handle, " }\n}\n"); fclose(g_json_handle); } if (g_overrides_handle != 0) { fprintf(g_overrides_handle, "\n ]\n}\n"); fclose(g_overrides_handle); } log_info("===================================================="); log_info(" RESUMO"); log_info(" Classes encontradas : " + ltoa(g_stat_classes, 10)); log_info(" Metodos virtuais : " + ltoa(g_stat_methods, 10)); log_info(" Funcoes renomeadas : " + ltoa(g_stat_renamed, 10)); log_info(" Construtores : " + ltoa(g_stat_ctors, 10)); log_info(" Destrutores : " + ltoa(g_stat_dtors, 10)); log_info(" Purecalls : " + ltoa(g_stat_pures, 10)); log_info(" Overrides marcados : " + ltoa(g_stat_overrides, 10)); log_info(" Metodos herdados : " + ltoa(g_stat_inherited, 10)); log_info(" Brute x64 : " + ltoa(g_stat_brute, 10)); log_info(" __thiscall : " + ltoa(g_stat_thiscall, 10)); log_info(" Sizes detectados : " + ltoa(g_stat_sizes, 10)); log_info(" Singletons : " + ltoa(g_stat_singletons, 10)); log_info(" VCall sites : " + ltoa(g_stat_vcalls, 10)); log_info(" Classes no header : " + ltoa(g_stat_header_classes, 10)); log_info(" Renorm. herdados : " + ltoa(g_stat_renamed_inherited, 10)); log_info(" Metodos concretos : " + ltoa(g_stat_methods - g_stat_pures, 10)); log_info(" Filtradas (framework): " + ltoa(g_stat_filtered, 10)); log_info(" Erros : " + ltoa(g_stat_errors, 10)); log_info("===================================================="); log_info("JSON : " + json_path); log_info("OVERRIDES : " + overrides_path); log_info("HEADER : " + header_path); log_info("LOG : " + log_path); log_info(""); log_info("=== ARQUIVOS GERADOS ==="); auto check_h; check_h = fopen(json_path, "r"); if (check_h != 0) { fseek(check_h, 0, 2); log_info(sprintf(" JSON : %d bytes", ftell(check_h))); fclose(check_h); } else { log_info(" JSON : NAO GERADO"); } check_h = fopen(overrides_path, "r"); if (check_h != 0) { fseek(check_h, 0, 2); log_info(sprintf(" OVERRIDES : %d bytes", ftell(check_h))); fclose(check_h); } else { log_info(" OVERRIDES : NAO GERADO"); } check_h = fopen(header_path, "r"); if (check_h != 0) { fseek(check_h, 0, 2); log_info(sprintf(" HEADER : %d bytes", ftell(check_h))); fclose(check_h); } else { log_info(" HEADER : NAO GERADO"); } if (g_log_handle != 0) fclose(g_log_handle); msg("\n=== Vtable Analyzer Pro v5.3 finalizado ===\n"); msg("Classes=%d Metodos=%d Renomeadas=%d Ctors=%d Dtors=%d Pures=%d Overrides=%d\n", g_stat_classes, g_stat_methods, g_stat_renamed, g_stat_ctors, g_stat_dtors, g_stat_pures, g_stat_overrides); msg("ThisCall=%d Sizes=%d Singletons=%d VCalls=%d HdrClasses=%d\n", g_stat_thiscall, g_stat_sizes, g_stat_singletons, g_stat_vcalls, g_stat_header_classes); }
    3 pontos
  6. IDA Free Vtable Analyzer 5.3 - Script IDC - Pastebin.com
    3 pontos
  7. Basta deslizar e assistir o quanto quiser de vídeos +18 curtos e longos! [hide][Hidden Content]] Basta clicar na opção do Tiktok e aproveitar os milhares de vídeos!
    3 pontos
  8. ⚔️WYD INFINITY ⚔️ 📅 Inaugurado dia: 01/05/2026 às 13h 📌 Informações Gerais Versão: 7.89 Dificuldade: Easy / Medium UP Rápido: Mortal/Arch UP Celestial: Easy até 200/200 - 200+ Medium Contas por IP: (5) 🔥 "O Farm te prepara. O PVP te define." 📌 Sistemas Guerras de Guilds Arena Real Pesadelo celestial liberado Dominação de Bosses com espólios valiosos Droplist com filtro de drop Sistema de Honra Valioso e Diferenciado Sistema Guerra entre Reinos Player Info Ranking atualizado Sistema de Balanceamento PVP e PVE Baú de carga com 4 slots Resoluções Widescreen sem distorções Loja desvinculada: Progrida livremente Loja de Donate: Acesse de qualquer lugar Novidades de NPCs: recicladores e compositores Novos slots: colar, cinto, traje montaria Sistema Revigorante (proteção de montaria) Fada Dourada: agrupador de itens, filtro e macro de água Auto Grupo Anti-Hack · Anti-DDoS Servidor Estável 24/7 Entre outros... 🌐Redes Sociais 🌍 Site: [Hidden Content] 💬 Discord: [Hidden Content] 📸 Instagram: [Hidden Content] 📱 WhatsApp: [Hidden Content] 🌐Download: Download do Cliente : DOWNLOAD DO CLIENT Scan CLIENT Scan EXE Administrador por: G2K 📌 Imagens sobre o WYD INFINITY Campo iniciante de ARMIA: Sistemas: Painéis no servidor: NPC'S (Se compra em gold e itens)(Ambos são FARM) VALE ESCONDIDO (CONTÉM 3 ANDARES) CAPAS CELESTIAIS
    2 pontos
  9. /* Estilos CSS adaptados para a cor exata do fórum (#272626) */ .gh-topic-container { font-family: 'Segoe UI', 'Roboto', Helvetica, Arial, sans-serif; background-color: #272626; /* Cor exata solicitada */ color: #eeeeee; padding: 30px; border-radius: 10px; margin: 20px auto; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3); border: 1px solid #363535; /* Borda sutil levemente mais clara que o fundo */ } /* ---- Estilos do Banner ---- */ .gh-banner-wrapper { width: 100%; margin-bottom: 35px; border-radius: 12px; overflow: hidden; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3); border-bottom: 3px solid #8b5cf6; } .gh-banner-img { width: 100%; height: auto; display: block; transition: transform 0.5s ease; } .gh-banner-wrapper:hover .gh-banner-img { transform: scale(1.01); } /* ---- Tipografia e Conteúdo ---- */ .gh-main-title { font-size: 2.8rem; font-weight: 900; text-transform: uppercase; margin-bottom: 0px; text-align: center; letter-spacing: -1px; color: #ffffff; } .gh-highlight-purple { color: #a855f7; text-shadow: 0 0 10px rgba(168, 85, 247, 0.4); } .gh-subtitle-topic { font-size: 1.3rem; color: #bbbbbb; text-align: center; margin-bottom: 40px; font-weight: 300; border-bottom: 1px solid #363535; } /* Caixa de Destaque Positiva (Ajustada para o fundo #272626) */ .gh-feature-box { background: #1e2920; /* Tom verde escuro fechado para contrastar com o grafite */ border: 1px solid rgba(34, 197, 94, 0.2); border-left: 5px solid #22c55e; padding: 25px; border-radius: 8px; margin-bottom: 25px; } .gh-feature-title { font-size: 1.4rem; color: #4ade80; font-weight: bold; margin-bottom: 12px; display: flex; align-items: center; } .gh-feature-title::before { content: '✓'; margin-right: 10px; font-weight: 900; } .gh-text-content { font-size: 1.05rem; line-height: 1.6; color: #e0e0e0; margin-bottom: 20px; } /* ---- Botão estilo o "Desbloquear" do print ---- */ .gh-button-container { text-align: center; margin: 0px 30px 30px 30px; } .gh-cta-button { background: linear-gradient(to right, #8b5cf6, #6366f1); /* Roxo vibrante idêntico à imagem */ color: white !important; padding: 16px 45px; text-decoration: none; font-size: 1.15rem; font-weight: 700; border-radius: 8px; transition: all 0.2s ease; display: inline-block; text-transform: uppercase; border: none; cursor: pointer; box-shadow: 0 5px 20px rgba(139, 92, 246, 0.4); } .gh-cta-button:hover { transform: translateY(-2px); box-shadow: 0 8px 25px rgba(139, 92, 246, 0.6); filter: brightness(1.1); } .gh-footer-topic { text-align: center; font-size: 0.85rem; color: #888888; margin-top: 40px; } .gh-footer-topic a { color: #a855f7; text-decoration: none; } /* ---- Caixa Alerta (Ajustada para o fundo #272626) ---- */ .gh-virustotal-box { background: #2d1919; /* Tom avermelhado escuro para contrastar com o grafite */ border: 1px solid rgba(239, 68, 68, 0.2); border-left: 5px solid #ef4444; padding: 25px; margin: 25px 0; border-radius: 8px; } .gh-virustotal-title { font-size: 1.4rem; color: #f87171; font-weight: bold; margin-bottom: 12px; } PRIME VÍDEO ACESSAR DADOS DA CONTA Crunchyroll ACESSAR DADOS DA CONTA paramount ACESSAR DADOS DA CONTA Manter este projeto ativo tem custos. Se você gostar da experiência e quiser mais estabilidade, suporte 24h e um perfil exclusivo para você, considere apoiar o projeto adquirindo uma assinatura a partir de R$ 1,90. Sua Conta Privada Acesso completo por 30 dias com perfil exclusivo e tela privada — apenas você assiste, sem limites ou interrupções. Seu histórico fica organizado e protegido, sem interferências. Entrega automática em segundos via PIX, com suporte garantido via chat. ❌ Conta Compartilhada Compartilhada com dezenas de pessoas, sem tela privada — outros usuários veem seu histórico e o que você assiste. Limite de telas excedido frequentemente, causando interrupções. Senha alterada periodicamente, tornando o acesso temporário e instável. Perfis bagunçados e histórico misturado, gerando recomendações erradas baseadas no consumo de terceiros. Perfis genéricos, sem personalização de nome ou foto. Sem suporte ou garantia em caso de problemas. Mais de 80% dos membros do fórum começaram testando as contas gratuitas e migraram para o plano privado. Garanta sua estabilidade hoje mesmo! Quero minha Conta Privada agora
    2 pontos
  10. 2 pontos
  11. NÃO É O FIM. É APENAS UM ATÉ LOGO! Olá, família GlobalHax! Espero que todos estejam bem. Hoje venho comunicar que nossa comunidade entrará em uma pausa por tempo indeterminado. Nos últimos tempos, não estou conseguindo dedicar ao projeto a atenção que ele realmente merece. Além disso, grande parte dos nossos sistemas relacionados aos serviços de streaming deixou de funcionar, sendo necessário estudar novos métodos, desenvolver novamente e reconstruir muitas coisas do zero. No momento, infelizmente, não tenho o tempo necessário para realizar tudo isso e entregar o projeto com a qualidade que sempre busquei. Por isso, acredito que o melhor caminho agora seja dar uma pausa, organizar as ideias e aguardar o momento certo para recomeçar. Mas quero deixar algo muito claro: isso não é uma despedida e muito menos o fim da GlobalHax. É apenas um até logo. Todo ciclo tem um começo, um meio e um fim. Mas depois do fim, sempre existe a possibilidade de um novo começo: mais forte, mais preparado e ainda melhor do que antes. Obrigado de coração a cada pessoa que confiou, participou, apoiou e fez parte da nossa história. Cada mensagem, cada acesso, cada conteúdo compartilhado e cada momento vivido dentro da comunidade teve sua importância. A GlobalHax pode estar pausando suas atividades agora, mas tudo o que construímos continuará fazendo parte da nossa história. Nos encontramos novamente no recomeço. Até logo, família GlobalHax . 🖤
    2 pontos
  12. [Hidden Content]
    2 pontos
  13. Scan da release caso algum literal CORAJOSO resolver abrir isso (tem que ser louco) 38/60 VirusTotal - File - 0ea47ddd395b92e4a14efed4974304b06fc4674fc67e0e15f0c5a38dba6f5304
    2 pontos
  14. [Hidden Content]
    2 pontos
  15. Fala pessoal blz. Bom comentei em outro tópico que estava fazendo um conversor de arquivos do WYD. Estou fazendo para o meu projeto, porem esta ficando bem interessante e vou compartilhar com vocês para terem uma ideia do que a ferramenta será capaz. Usa DirectX 11. Conseguimos trabalhar com todos os arquivos do cliente. .MSH .MSA .WYS .WYT .BON .ANI .TRN e etc. Temos o modo de pré-visualização conseguimos ver além da meshs as animações e etc., Se temos um .MSH que possui um .BON e um .ANI associados quando carregamos o .MSH ele puxa automaticamente todos os outros arquivos correspondentes. Logo podemos dar play e ver a animação na preview. Podemos avançar ou retroceder frame a frame e etc. Todos os arquivos carregados ficam na aba da esquerda vc pode ver um a um clicando neles. O mesmo acontece quando carregamos uma .TRN todos os arquivos relacionados são carregados junto, logo conseguimos ver um a um selecionando na esquerda. A aba a direita do preview é a aba de propriedades e fornece informações importantes sobre os arquivos carregados. Conseguimos trabalhar com as meshs de qualquer cliente, na versão que vou postar para vocês não trabalha com as meshs do MYTH. Ainda temos alguma limitações com meshs novas do Global, pois não mapeei todas ainda, então algumas inda ficam transparentes, ou na visualização do .TRN ficam pretas. Na aba Conversores de dados conseguimos trabalhar com alguns arquivos legais como EnvTextureList ainda não trabalhamos com a Env do Global, mas esta em desenvolvimento. Temos Conversores de ItemList e SkillData aqui conseguimos trabalhar com arquivos da TMProject e do Global tanto leitura quando conversão BIN <--> CSV. Temos um Editor de ServerList.bin e trabalhamos com arquivos da TMProject e do Global basta carregar editar e salvar. Temos um Conversor de AttributeMap DAT <--> CSV logo converte para .CSV edita e converte novamente para .DAT A versão completa que tem os conversores de meshs todos habilitados, nessa versão da pra ver exatamente quais os arquivos suportados. Ainda temos um erros de encode nas mensagens do app e estou corrigindo aos poucos, pois são só estéticos. seguem algumas prints segue o scan - teve um apontamento, porem falso positivo, o app não tem assinatura digital então acusa... podem usar tranquilamente que não tem nada malicioso. VirusTotal Download GuiCan Acho que é isso, caso encontrem algum bug que não citei favor reportarem. A sim estava me esquecendo, no roda pé do app mostra as teclas de atalho.
    2 pontos
  16. Fala, pessoal. Quis compartilhar uma ferramenta de apoio que desenvolvi para acompanhar o uso do editor de cena. Que você pode encontrar nesse link: A ideia desse projeto foi facilitar o trabalho com arquivos UITextureListN.bin, tanto na parte de conversão quanto na conferência dos registros atuais do arquivo. Em muitos casos, enquanto a gente vai ajustando recurso, caminho ou estrutura, acaba sendo útil ter uma ferramenta separada para visualizar rapidamente a lista do BIN e reconstruir o arquivo no formato correto. A ferramenta permite: - converter BIN para TXT - converter TXT para BIN - visualizar os registros do BIN carregado - pesquisar por índice, caminho e tipo - trabalhar com compatibilidade entre layouts 264 e 528 - usar BIN de referência para reconstrução mais segura Fiz isso como um utilitário de apoio ao fluxo de edição, justamente para complementar o trabalho no editor principal sem deixar o programa de cena mais poluído. Repositório: [Hidden Content] Se alguém quiser testar e dar feedback, vai ajudar bastante.
    2 pontos
  17. @ Grego Comprei uma conta disney em [Hidden Content] por conta deste post, e agora fica pedindo o código para confirmar a tela ou se estou fora de casa, já tem uma semana suporte n responde, nem email, nem whatsapp, usei o serviço apenas uma vez... Não recomendo. [edit] Comentei no discord da wc falando do problema, e me fizeram o reembolso.
    2 pontos
  18. Fala, pessoal. Queria compartilhar com vocês esse repositório do WYD FieldScene Studio Pro. Mesmo não sendo programador, o trabalho do Hamilton me inspirou bastante. A base do projeto e toda a ideia inicial me chamaram atenção justamente pelo quanto essa ferramenta veio pra ajudar quem mexe com interface e edição de painéis no WYD. Sem exagero algum, eu cheguei em 90 versoes em testes "ta comm tempo né" kkk A partir disso, tive a ideia de tentar tornar o programa mais acessível, principalmente para quem não tem monitor com resolução muito alta ou acaba sofrendo com a limitação de espaço na tela durante a edição. Além disso, fui buscando implementar melhorias de usabilidade, organização visual e praticidade no fluxo de trabalho, sempre tentando deixar a ferramenta mais confortável e funcional no uso do dia a dia. O objetivo não foi apagar a origem do projeto, mas sim evoluir em cima de uma base que já era boa, trazendo adaptações, correções e recursos que pudessem facilitar a vida de mais pessoas. Entre os pontos que busquei melhorar estão: - melhor adaptação para diferentes resoluções - visualização em escala 1:1 - conversão e ajuste entre resoluções - carregamento e compatibilidade de recursos - melhorias no uso geral do editor - remoção de fundo em imagens compatíveis - mais praticidade para edição, importação e visualização -o programa tem régua -arraste por mouse **OBS IMPORTANTE, É RECOMENDADO QUE QUANDO FOR SOLICITADO O CARREGAMENTO DE RECURSOS ABRA ATÉ A PASTA DO CLIENTE ONDE ESTÃO AS PASTAS UI, NUI E MESH, ASSIM O PROGRRAMA JÁ CARREGA AS 3 PASTAS NO LUGGAR DE ABRIR UMA POR VEZ, ALGUNS PAINEIS AS VEZES ESTAO FORA DA UI** e mais coisas que não me lembro agora haha Estou compartilhando porque pode ser útil para outras pessoas da área, e também porque acho justo reconhecer que essa iniciativa só existiu porque o trabalho original me motivou a tentar fazer algo também Repositório: [Hidden Content] Se alguém quiser testar, dar feedback ou sugerir melhorias, vai ser muito bem-vindo. E LEMBRANDDO CRÉDITOS TOTAIS AO HAMILTON DO CANAL HC CURSOS E TUTORIAIS
    2 pontos
  19. ASSINATURAS ATUALIZADAS Patrocinadora: streamingsbarato.com [Hidden Content] [Hidden Content] [Hidden Content] [Hidden Content] Assinatura será atualizada todos os meses.
    2 pontos
  20. ATENÇÃO! O CHEAT FICARÁ FREE ATÉ 01/05/2026,APROVEITE! Fala seus arr0mbados, trago para a comunidade um WALLHACK INDETECTÁVEL, já faz ANOS que utilizo e nunca desatualizou, joguem com responsabilidade! Sem enrolação, vamos direto ao ponto! 1 - EXECUTE O SEU POINT BLANK E LOGUE NA SUA CONTA; 2 - ATIVE O MODO JANELA (PARA O MELHOR FUNCIONAMENTO DO CHEAT, EVITANDO TRAVAMENTOS); 3 - EXECUTE O ARQUIVO "WALL PREMIUM - LOADER PB" COMO ADMINISTRADOR E FECHE A JANELA DO NAVEGADOR QUE ABRIRÁ; 4 - VOLTE PARA O POINT BLANK, O WALLHACK JÁ ESTARÁ ATIVO (OBS: NÃO EXISTE A OPÇÃO PARA ATIVAR/DESATIVAR). WALLHACK PREMIUM VITALÍCIO APENAS R$25,00 >> CLIQUE AQUI PARA COMPRAR! <<
    2 pontos
  21. Implementado Editor de UI do cliente, conseguimos trabalhar com todos os paineis originais, conseguimos criar novos paineis e etc. as texturas que são configuradas nos Scenes podem ser editadas logos por exemplo, as texturas que são carregadas dinamicamente pelo cliente podem ser reposicionadas pelo editor e são carregadas corretamente quando roda o cliente. vai da imaginação, pode modificar inventario, guarda carga e tudo mais... LoginScene.bin FieldScene2.bin Inicio da implementação de um editor de CamAction onde podera ser editado a visão da camera, o caminho que a camera faz, formas de visualização e etc.
    2 pontos
  22. 2 pontos
  23. O link para download já foi upado em outro site, sem restrições! @SaphireL
    2 pontos
  24. O link do drive está pedindo solicitação para o acesso! Peço que corrija. @ userweedd [Hidden Content]
    2 pontos
  25. Conheci essa IA sem censura: pergunta o que quiser que ela responde. Tem limite diário, mas gera muitos tokens e mantém as conversas. [Hidden Content]
    2 pontos
  26. Cansado de mapa genérico? Cansado de depender de editor bugado? 👉 Então presta atenção nisso aqui. Estou liberando um Editor de Mapas WYD COMPLETO, feito pra quem quer dominar o servidor de verdade. 💣 O QUE VOCÊ CONSEGUE FAZER ✔ Montar cidades, dungeons, eventos exclusivos ✔ Controlar terreno, relevo e objetos com precisão ✔ Trabalhar com velocidade (sem travar, sem dor de cabeça) ⚡ DIFERENCIAL (E AQUI MUDA O JOGO) ✔ Interface direta (abriu, usou) ✔ Leve e rápido (roda liso) ✔ Estável (sem crash aleatório) ✔ Pensado pra WYD (não é gambiarra adaptada) 👉 Isso aqui não é editor meia boca. 🧠 PRA QUEM É ISSO? Donos de servidor que querem SE DESTACAR Devs que cansaram de ferramenta limitada Quem quer parar de copiar mapa dos outros 🚨 REALIDADE Servidor com mapa padrão = mais do mesmo Servidor com mapa custom = destaque 👉 Simples assim. 📸 PREVIEW [Hidden Content] 💰 VALOR 💲 Chama no privado / Discord 💥 Preço justo pelo que entrega (Pode rolar desconto pra primeiros) 📩 CONTATO 📬 Discord: .leowt 📬 PM no fórum ⚠️ AVISO ✔ Ferramenta exclusiva ✔ Suporte incluso ✔ Não é free 🔒 RESUMO Quer servidor comum → continua como tá Quer algo diferente → isso aqui resolve 🚀 GARANTA ANTES QUE GERAL COMECE A USAR Depois que começar a aparecer mapa diferente por aí… você já sabe de onde veio.
    1 ponto
  27. Há muito tempo, desenvolvi este editor, mas ele estava desatualizado. Agora, estou compartilhando a versão mais recente, atualizada com a última atualização do WYD Global, v763 (15/03/2025). Atualização 16/03/2025: Foi adicionado suporte para versão 759,763 e 769. Foi adicionado a exibição de ícones. Atualização 19/03/2025: Foi adicionado suporte para ExtraItem.bin para aqueles que não sabem todo item da itemlist que estiver dentro da extraitem.bin será substituído pelo mesmo. Download:
    1 ponto
  28. SERVIDOR MÉDIO O mundo de Mystic Destiny já está em movimento, e a corrida pelo topo não espera. TOP 1 em disputa Level máximo: 354 Arch Você vai assistir… ou dominar? Guild Apex apenas de players estrangeiros Guild Blacklist apenas de players brasileiros Venha participar! Exclusividades que você só encontra aqui Codex Mineração Pescaria Dungeon Guilda Gemas Mágicas Montarias Lendárias Consumíveis Exclusivos Arena para até 104 jogadores Mercado Global Painel Guilda Loja de Guilda Info do Jogador World Bosses LAN Reformulada Guerra de Torres Reformulada Guerra de Noatun Reformulada Castelo de Loki Sistema de Roleta Passe de Batalha completo VIP para qualidade de vida do jogador Sistema de tradução em tempo real Drop 100% refeito do zero Comunidade Entre no nosso Discord e acompanhe todas as informações oficiais: [Hidden Content] [Hidden Content] [Hidden Content]
    1 ponto
  29. MEUS CANAIS DE VÍDEOS HC CURSOS E TUTORIAIS 🎬 ▶️ PLAYLIST DO MEU CANAL NO YOUTUBE 🎬 ▶️ VEICARECA GAMES 🎬 ▶️ PLAYLIST DO MEU CANAL NO YOUTUBE 🎬 ▶️ 🎮 Fala, galera! Sejam bem-vindos ao canal! Se você joga WYD (With Your Destiny) e está cansado daquele editor de painéis antigo, ultrapassado e cheio de limitações… ESSE VÍDEO É PARA VOCÊ! 🔥 📌 Eu estava desenvolvendo um projeto que seria VENDIDO, mas resolvi COMPARTILHAR DE GRAÇA com a comunidade. Porque todo mundo merece uma ferramenta digna, né? 🖌️ **O que esse editor tem de especial?** ✅ Interface gráfica MODERNA e atualizada ✅ Trabalha DIRETAMENTE nos painéis com a imagem (nada de ficar decorando códigos!) ✅ Muito mais rápido e intuitivo ✅ Compatível com os painéis do WYD ✅ Ideal para criar, editar e organizar seus painéis de forma profissional 💡 Chega de sofrer com ferramentas jurássicas! Aqui você vai ver na prática como esse editor transforma o trabalho com painéis. 👇 **DOWNLOAD:** 🔗 📁 CLIQUE AQUI PARA BAIXAR O EDITOR NA DESCRIÇÃO DO VÍDEO ⚠️ **Importante:** A ferramenta é totalmente gratuita, mas se quiser apoiar o canal, deixe aquele like e se inscreva! Isso ajuda demais a trazer mais conteúdos exclusivos. 💬 Dúvidas ou sugestões? Manda aí nos comentários! 🔔 Se inscreva no canal e ative o sininho para não perder nenhum conteúdo sobre WYD e ferramentas exclusivas! #WYD #WYDGlobal #EditorDePainéis #MMORPGRetrô #WYDBrasil #FerramentasWYD
    1 ponto
  30. no WYDConverter eu fiz uma área de tutorial e um deles é a criação de novos itens/mobs, veja se te ajuda. copia e crie um .md pode ser no próprio VS que vai ficar bem mais legível. # Guia: Criar Mobs, Objetos e Meshes Novos para o WYD > Passo a passo completo para criar conteudo 3D novo e integrar no cliente WYD. > Cobre mobs (MSH skinned), objetos estaticos (MSA), modelagem, exportacao, > conversao e registro nos arquivos do cliente. --- ## 1. Sistema de Arquivos do WYD ### 1.1 Arquivos 3D por mob | Arquivo | Exemplo | Conteudo | Formato | |---|---|---|---| | `.msh` | `nm010101.msh` | Mesh skinned (vertices, indices, bones, skinning) | Binario 32B header | | `.bon` | `nm01.bon` | Hierarquia do esqueleto (pares parentID/boneID) | Binario 8B por bone | | `.ani` | `nm010101.ani` | Animacao (matrizes 4x4 locais por bone por frame) | Binario 8B header + matrices | | `.wys` | `nm010101.wys` | Textura DXT1 comprimida | Binario WYS | | `.wyt` | `nm010101.wyt` | Textura BGRA raw (formato antigo) | Binario WYT | ### 1.2 Convencao de nomes ``` {prefixo}{PPVV}.ext prefixo = skeleton type (4 chars): nm01, ch01, ch02, tr09, hs01... PP = parte/slot (2 digitos): 01=corpo, 02=helm, 03=armor... VV = visual/variante (2 digitos): 01=base, 02=alt... ``` **Regra critica:** os primeiros 4 caracteres definem o skeleton. Todos os arquivos com mesmo prefixo compartilham o mesmo `.bon` e os mesmos `.ani`. Exemplos: - `nm010101.msh`, `nm010102.msh` → usam `nm01.bon` - `ch010597.msh` → usa `ch01.bon`, slot 05 (luva), visual 97 ### 1.3 Arquivos de registro do cliente Estes arquivos precisam ser editados para o cliente reconhecer mobs novos. | Arquivo | Path | Funcao | Editar quando | |---|---|---|---| | `BoneAni4.txt` | `Mesh\BoneAni4.txt` | Registra skeletons: index, num ANIs, num partes, prefixo | Skeleton NOVO | | `ValidIndex.bin` | `Mesh\ValidIndex.bin` | Mapeia quais ANIs existem para cada skeleton | Skeleton NOVO | | `MeshTextureList.bin` | `Mesh\MeshTextureList.bin` | Tabela de texturas de meshes (nome → index) | Textura NOVA | | `MeshList.txt` | `Mesh\MeshList.txt` | Registra objetos estaticos MSA: index → path | MSA NOVO | | `AniSound4.txt` | `AniSound4.txt` | Tabela de animacoes/velocidades/sons por skeleton type | Skeleton NOVO | | `ItemList.bin` | Server-side | Define nClass do mob → mapeia para skeleton via `BASE_DefineSkinMeshType` | Mob NOVO | | `Basedef.cpp` | Compilado no .exe | `BASE_DefineSkinMeshType(nClass)` → nBoneAniIndex (hardcoded switch) | Skeleton NOVO (recompilacao) | **Para mob com skeleton EXISTENTE:** nenhum destes arquivos precisa ser editado. Basta colocar o MSH + WYS na pasta `Mesh\` com o prefixo correto. **Para mob com skeleton NOVO:** todos os arquivos acima precisam ser editados. --- ## 2. Formatos Binarios Detalhados ### 2.1 MSH (Mesh Skinned) ``` Header (32 bytes): [00] DWORD dwParentID — ID do bone pai (0xFFFFFFFF = root) [04] DWORD dwMeshID — ID do bone deste mesh [08] DWORD dwFVF — Flexible Vertex Format (D3D9) [0C] DWORD sizeVertex — Bytes por vertice [10] DWORD numFaceInfluence — Numero de face influences [14] DWORD numPalette — Numero de bones na palette [18] DWORD vertexCount — Numero de vertices [1C] DWORD faceCount — Numero de INDICES (nao faces!) Bones (se numPalette > 0): XMFLOAT4X4[numPalette] — Offset matrices (64 bytes cada) DWORD[numPalette] — Bone IDs (4 bytes cada) Vertices: Layout depende do FVF. Tipico para skinned: Position XYZ (12B) + BlendWeights (4-12B) + PackedBoneIndices (4B) + Normal (12B) + UV (8B) Indices: uint16[faceCount] — Index buffer 16-bit ``` ### 2.2 BON (Bone Hierarchy) ``` Arquivo = array de pares (8 bytes cada): [parentID: DWORD] [boneID: DWORD] parentID = 0xFFFFFFFF ou 0 → root (sem pai) parentID = N → bone filho de bone N Tamanho total = numBones * 8 bytes ``` Exemplo para esqueleto com 5 bones: ``` [0xFFFFFFFF] [0] — Bone 0 e root [0] [1] — Bone 1 filho de Bone 0 [0] [2] — Bone 2 filho de Bone 0 [1] [3] — Bone 3 filho de Bone 1 [1] [4] — Bone 4 filho de Bone 1 ``` ### 2.3 ANI (Animation) ``` Header (8 bytes): [00] DWORD numTicks — Numero de keyframes (frames de animacao) [04] DWORD numBones — Numero de bones no skeleton Dados: Para cada tick (0..numTicks-1): XMFLOAT4X4[numBones] — Matriz LOCAL de cada bone neste frame (64 bytes cada) Tamanho total = 8 + (numTicks * numBones * 64) bytes ``` As matrizes sao LOCAL (relativas ao pai na hierarquia BON). O cliente propaga pela hierarquia para obter as combined (world) matrices: `combined[bone] = local[bone] * combined[parent]` ### 2.4 BoneAni4.txt ``` Formato texto, uma linha por skeleton: {index} {numAniTypes} {numParts} {szAniName} Campos: index — Indice no array m_BoneAnimationList (0-99) numAniTypes — Quantos ANIs diferentes este skeleton tem numParts — Quantos slots de equipamento (1 para mobs simples, 7 para players) szAniName — Prefixo completo com path (ex: mesh\nm01, mesh\ch01) ``` Exemplo: ``` 0 38 7 mesh\ch01 <- personagem masculino, 38 animacoes, 7 partes 1 37 7 mesh\ch02 <- personagem feminino 2 1 1 mesh\hs01 <- cavalo, 1 animacao, 1 parte 3 2 1 mesh\nm01 <- mob tipo 1, 2 animacoes, 1 parte ... ``` ### 2.5 ValidIndex.bin ``` Array binario fixo: stValidAniList[100][186] stValidAniList = struct { int nI; } — 4 bytes Tamanho total = 100 * 186 * 4 = 74.400 bytes Indexacao: m_stValidAniList[skeletonIndex][aniSlot].nI = aniFileIndex O cliente usa nI+1 para montar o nome do ANI: sprintf(szTemp, "%s%04d.ani", szAniName, m_stValidAniList[nCount][nFI].nI + 1) Exemplo: m_stValidAniList[3][0].nI = 0 → mesh\nm010001.ani (idle) m_stValidAniList[3][1].nI = 1 → mesh\nm010002.ani (walk) m_stValidAniList[3][2].nI = 100 → mesh\nm010101.ani (arma 01, idle) ``` Codificacao dos 4 digitos do ANI: ``` XXYY onde: XX = tipo de arma (weapon): nArrayIndex / 100 - 1 YY = tipo de acao (motion): nArrayIndex % 100 - 1 Exemplos (nI+1 → arquivo): 0001 → arma 0, acao 0 (idle) 0002 → arma 0, acao 1 (walk) 0003 → arma 0, acao 2 (run) 0004 → arma 0, acao 3 (attack1) 0101 → arma 1, acao 0 (idle com espada) ``` ### 2.6 MeshList.txt ``` Formato texto, uma linha por objeto estatico: {index} {path_relativo} Campos: index — Indice no array m_stCommonMesh (0-3047) path_relativo — Caminho do MSA relativo ao diretorio do cliente ``` Exemplo: ``` 0 mesh\cas001.msa 1 mesh\cas002.msa 50 mesh\tree01.msa 100 mesh\sword01.msa ``` O engine chama `GetCommonMesh(dwObjType)` onde dwObjType = index. Mapas (.trn) referenciam objetos pelo dwObjType. ### 2.7 MeshTextureList.bin ``` Array binario fixo de stTextureListInfo (264 bytes cada): char szFileName[255] — path relativo da textura (ex: "mesh\nm010101.wys") char cAlpha — tipo de alpha: 'N'=none, 'A'=alpha, 'a'=1bit, 'C'=compressed DWORD dwLastUsedTime — runtime (ignorar, preencher com 0) DWORD dwShowTime — runtime (ignorar, preencher com 0) Tamanho por versao: TMProject classico: 2048 slots = 540.672 bytes WYD Global: 6096 slots = 1.609.344 bytes ``` O cliente busca textura por nome (`GetModelTextureIndex`): percorre TODOS os slots comparando `szFileName` com o nome calculado pelo `TMSkinMesh`. Se nao encontra, o mesh **NAO CARREGA** (retorna erro). **Para adicionar textura nova:** 1. Encontrar um slot vazio (szFileName[0] == '\0') 2. Preencher szFileName com path relativo (ex: `mesh\xx010101.wys`) 3. Preencher cAlpha com 'N' (sem alpha) ou 'A' (com alpha) 4. dwLastUsedTime e dwShowTime = 0 ### 2.8 AniSound4.txt ``` Formato texto. Uma secao por skeleton type (MAX_ANI_TYPE = 60 skeletons). Cada secao tem 56 linhas (MAX_ANI_MOTION), uma por tipo de acao. Para skeleton types 0 e 1 (players): {nomeAcao} {ani0} {speed0} {ani1} {speed1} {ani2} {speed2} {ani3} {speed3} {sound} Para skeleton types 2+ (mobs/NPCs): {nomeAcao} {aniIndex} {speed} {sound} Campos: nomeAcao — label (ex: "IDLE", "WALK", "ATK1") — ignorado pelo parser aniIndex — indice na lista de animacoes do skeleton (posicao no array concatenado) speed — velocidade da animacao (ticks por frame) sound — indice do som a tocar ``` Controla qual animacao tocar para cada acao do mob (idle, walk, attack, death, etc.) e a velocidade de reproducao. Se o skeleton novo for um mob simples, basta preencher as acoes basicas (idle=0, walk=1, attack=2, death=3). ### 2.9 Fluxo de resolucao no cliente ``` Servidor envia pacote com mob → nClass (tipo do mob) ↓ BASE_DefineSkinMeshType(nClass) → nBoneAniIndex (switch hardcoded em Basedef.cpp) ↓ BoneAni4.txt[nBoneAniIndex] → szAniName (ex: "mesh\nm01"), numParts, numAniTypes ↓ Skeleton: szAniName + ".bon" → mesh\nm01.bon Mesh: szAniName + PPVV + ".msh" → mesh\nm010101.msh Textura: szAniName + PPVV + ".wys" → mesh\nm010101.wys → GetModelTextureIndex(texName) → busca em MeshTextureList.bin Animacao: ValidIndex.bin[nBoneAniIndex][slot] → nI+1 → mesh\nm010001.ani → AniSound4.txt[nBoneAniIndex] → mapeia acao → indice de animacao ``` ### 2.10 STRUCT_ITEMLIST (ItemList.bin) ``` Struct STRUCT_ITEMLIST — 120 bytes por item: char Name[64] — Nome do item short nIndexMesh — Indice visual do mesh short nIndexTexture — Indice visual da textura short nIndexVisualEffect — Efeito visual (brilho, aura) short nReqLvl — Nivel minimo short nReqStr / nReqInt / — Requisitos de atributo nReqDex / nReqCon STRUCT_STATICEFFECT stEffect[12] — 12 efeitos estaticos (4 bytes cada) int nPrice — Preco do item short nUnique — Tipo de raridade short nPos — Bitmask de slots onde pode equipar short nExtra — Dados extras short nGrade — Grau/rank do item ``` **nIndexMesh e nIndexTexture** sao os campos que conectam o item ao mesh visual. O significado muda COMPLETAMENTE dependendo do tipo de item: **ARMAS E ESCUDOS (slots 6 e 7 — MSA):** nIndexMesh = indice DIRETO no MeshList.txt O cliente chama: GetCommonMesh(nIndexMesh) → LoadMsa(MeshList[nIndexMesh]) Nenhuma formula intermediaria. Se a arma esta no index 500 do MeshList, nIndexMesh = 500. nIndexTexture = preenche LOOK_INFO.SkinX mas NAO e usado para renderizar a arma 3D. A textura da arma vem do nome embeddado no MSA → MeshTextureList. Na pratica, para armas, nIndexTexture = 0. **EQUIPAMENTOS SKINNED (slots 0-5 — MSH):** nIndexMesh = variante visual usada na FORMULA do nome do MSH: sprintf("%s%02d%02d.msh", szAniName, slot+1, nIndexMesh + offset + 1) NAO e um indice de MeshList. E um numero de variante (0, 1, 2...). Exemplo: nIndexMesh=5 no slot coat → ch010306.msh (prefixo + slot03 + visual06) nIndexTexture = modificador de textura na FORMULA do nome da WYS: sprintf("%s%02d%02d.wys", szAniName, slot+1, (nIndexTexture & 0xFFF) + nIndexMesh + offset + 1) Permite usar texturas diferentes para o mesmo mesh visual. Exemplo: nIndexMesh=5, nIndexTexture=2 + 1 → ch010308.wys **MONTARIAS (slot 14 — MSH skinned, skeleton proprio):** O item de montaria fica em Equip[14]. sIndex do item → transformado (sIndex - 2045, com overrides hardcoded) → sIndex final usado como indice no g_pItemList: nIndexMesh = g_pItemList[sIndex].nIndexMesh → Mesh0 (variante visual) nIndexTexture = g_pItemList[sIndex].nIndexTexture → Skin0 (variante textura) nClass (efeito 18) → BASE_DefineSkinMeshType(nClass) → skeleton da montaria Funciona igual a equipamentos skinned: nIndexMesh e variante visual, NAO indice do MeshList. Vem do ItemList.bin. **nPos** — bitmask de slots de equipamento: ``` Bit 0 (0x01) = Slot 0: Face/Corpo Bit 1 (0x02) = Slot 1: Helm (elmo) Bit 2 (0x04) = Slot 2: Coat (armadura) Bit 3 (0x08) = Slot 3: Pants (calca) Bit 4 (0x10) = Slot 4: Gloves (luva) Bit 5 (0x20) = Slot 5: Boots (bota) Bit 6 (0x40) = Slot 6: Left (mao esq/escudo) Bit 7 (0x80) = Slot 7: Right (mao dir/arma) ``` **nClass (efeito 18)** — tipo de classe que determina o skeleton: Usado por `BASE_DefineSkinMeshType(nClass)` para mobs e montarias. Para players: nClass 1/4 = masculino (ch01), 2/8 = feminino (ch02). Para mobs: nClass 16-74 mapeiam para indices 2-57 no BoneAni4.txt. ### 2.11 Fluxo visual de armas ``` Item no Equip[7] (mao direita) ou Equip[6] (mao esquerda) ↓ g_pItemList[sIndex].nIndexMesh → nIndexMesh ↓ GetCommonMesh(nIndexMesh) ← DIRETO, sem formula intermediaria! ↓ MeshList.txt[nIndexMesh] → path do MSA (ex: mesh\wp001.msa) ↓ TMMesh::LoadMsa(path) → carrega MSA ↓ Textura: nome embeddado no MSA → GetModelTextureIndex → MeshTextureList.bin ``` ### 2.12 Fluxo visual de equipamentos (armaduras, helmos, etc.) ``` Item no Equip[0-5] (face, helm, coat, pants, gloves, boots) ↓ g_pItemList[sIndex].nIndexMesh → LOOK_INFO.MeshX (variante) g_pItemList[sIndex].nIndexTexture → LOOK_INFO.SkinX (textura mod) ↓ TMSkinMesh monta nome do arquivo: Mesh: sprintf("%s%02d%02d.msh", szAniName, slot+1, MeshX + offset + 1) Tex: sprintf("%s%02d%02d.wys", szAniName, slot+1, (SkinX & 0xFFF) + MeshX + offset + 1) ↓ Exemplo: ch01, slot 3 (coat), nIndexMesh=5, nIndexTexture=0 Mesh: ch010306.msh Tex: ch010306.wys ``` --- ## 3. Restricoes Tecnicas do Cliente ### 3.1 MSH (Skinned Meshes) | Item | Limite | Origem | |---|---|---| | Bones por mesh | 40 max | `safePalette = min(n, 40)` em CMesh | | Influences por vertice | 4 max | FVF XYZB1-B4 + LASTBETA_UBYTE4 | | Vertices por mesh | 65535 max | Indices 16-bit no MSH | | Textura por mesh | 1 | MSH nao tem attribute ranges | | Textura dimensao | Potencia de 2 | DXT1 | | Coordenadas | Z-Up | Nativo WYD | | UV | V=0 no topo (D3D) | Nativo WYD e 3ds Max | | Skeletons registrados | 100 max | MAX_BONE_ANIMATION_LIST | | ANIs por skeleton | 186 max | Array fixo ValidAniList | | Bones por skeleton | Sem limite hard | Limitado pela RAM | ### 3.2 MSA (Static Meshes) | Item | Limite | Origem | |---|---|---| | Vertices por mesh | 65535 max | Indices 16-bit | | Materiais por mesh | 32 max | dwAttCount no header | | Texturas por material | 1 | Uma WYS por attribute range | | Textura dimensao | Potencia de 2 | DXT1 | | Coordenadas | Z-Up | Nativo WYD | | UV | V=0 no topo (D3D) | Nativo WYD | | Objetos em MeshList | 3048 max | MAX_COMMON_MESH | | FVF suportados | 274 (normal) ou 322 (diffuse) | Engine hardcoded | --- ## 4. Workflow: Novo Mob com Skeleton Existente **Cenario:** criar um mob novo que reutiliza o esqueleto e animacoes de um mob existente. Este e o caminho mais rapido e seguro. ### Passo 1 — Exportar mob de referencia No WYDConverter, abrir o MSH do mob que quer reusar (ex: `nm010101.msh`). Exportar como FBX (target 3ds Max). O FBX contem: - Geometria do mob original - Esqueleto completo com bone IDs - Animacao do ANI companion - Textura companion como PNG ### Passo 2 — Importar no 3ds Max Importar o FBX. O mesh aparece: - Z-Up (nativo do Max e do WYD — sem conversao) - Escala amplificada (conversor aplica x100 para edicao confortavel) - Bones com nomes `Bone_000`, `Bone_001`, etc. **NAO mover, rotacionar ou escalar o Armature.** A posicao dos bones e o bind pose que as animacoes existentes esperam. ### Passo 3 — Modelar - Deletar a geometria do mob original (so poligonos, NAO os bones) - Modelar o novo mob ao redor do esqueleto existente - Manter o mesh alinhado com os bones na posicao de bind Regras: - Apenas triangulos (sem quads/n-gons) - Max 65535 vertices - 1 material/textura por mesh - Polycount razoavel (~500-3000 tris para mobs) ### Passo 4 — Skinning - Aplicar Skin modifier no mesh - Adicionar os bones do esqueleto importado - Pintar weights: max 4 influences por vertice - Weights devem somar 1.0 - Todo vertice deve ter pelo menos 1 bone com peso > 0 ### Passo 5 — UV e Textura - UV Unwrap no Max (UV convention D3D: V=0 topo — default do Max) - Textura PNG/TGA em potencia de 2 (256x256 ou 512x512) - O WYDConverter converte para WYS automaticamente ### Passo 6 — Exportar FBX File → Export → FBX: - Formato: FBX Binary - Eixos: Z-Up (default Max) - Smoothing Groups: ON - Skin/Deformers: ON - Embed Media: OFF ### Passo 7 — Converter No WYDConverter: 1. Abrir o FBX exportado 2. Converter para MSH 3. Nomear: `nm010199.msh` (mesmo prefixo, numero livre) 4. Converter textura PNG → WYS: `nm010199.wys` 5. Copiar para `Mesh\` do cliente ### Passo 8 — Testar O cliente carrega automaticamente: - `nm010199.msh` — mesh novo - `nm01.bon` — hierarquia (ja existe) - `nm010001.ani` etc — animacoes (ja existem) - `nm010199.wys` — textura nova Nenhuma edicao de BoneAni4.txt ou ValidIndex.bin necessaria (mesmo skeleton). --- ## 5. Workflow: Mob com Skeleton e Animacoes Novas **Cenario:** criar um mob completamente novo com esqueleto e animacoes proprias. Requer ferramentas adicionais e edicao de arquivos de registro. ### Passo 1 — Modelar e Riggar no 3ds Max - Modelar o mob - Criar esqueleto com bones (usar nomes `Bone_000`, `Bone_001`, etc.) - Bone 0 deve ser o root - Max 40 bones - Skin modifier com max 4 influences ### Passo 2 — Criar Animacoes No 3ds Max, criar as animacoes necessarias: - Idle (parado) - Walk (andando) - Run (correndo) - Attack (atacando) - Death (morrendo) - etc. Cada animacao sera exportada como um FBX separado (ou ranges no mesmo FBX). ### Passo 3 — Exportar Exportar cada animacao como arquivo separado com mesh + skeleton + animation. Formatos suportados: - **FBX Binary** (3ds Max, Maya, Blender via plugin) - **GLB/GLTF** (Blender nativo, qualquer DCC moderno) ### Passo 4 — Converter Usar o WYDConverter para gerar: | De | Para | Ferramenta | Status | |---|---|---|---| | FBX mesh | `.msh` | MSHEncoder (FBX→MSH) | ✅ Funciona | | GLB/GLTF mesh | `.msh` | MSHEncoder (GLTF→MSH) | ✅ Funciona | | FBX skeleton | `.bon` | **BONEncoder (FBX)** | ❌ NAO EXISTE | | GLB/GLTF skeleton | `.bon` | **BONEncoder (GLTF)** | ❌ NAO EXISTE | | FBX animation | `.ani` | **ANIEncoder (FBX)** | ❌ NAO EXISTE | | GLB/GLTF animation | `.ani` | **ANIEncoder (GLTF)** | ❌ NAO EXISTE | | PNG textura | `.wys` | WYSEncoder | ✅ Funciona | ### Passo 5 — Registrar no cliente Para um skeleton completamente novo, TODOS estes arquivos precisam ser editados: #### 5.1 Editar BoneAni4.txt Adicionar uma linha para o novo skeleton: ``` {novoIndex} {numAnis} {numParts} mesh\{prefixo} ``` Exemplo: adicionar mob `xx01` com 3 animacoes, 1 parte: ``` 50 3 1 mesh\xx01 ``` O index (50) deve ser um slot livre no array (0-99). #### 5.2 Editar ValidIndex.bin Adicionar as entradas para mapear os ANIs do novo skeleton: ``` m_stValidAniList[50][0].nI = 0 → mesh\xx010001.ani (idle) m_stValidAniList[50][1].nI = 1 → mesh\xx010002.ani (walk) m_stValidAniList[50][2].nI = 2 → mesh\xx010003.ani (attack) ``` O ValidIndex.bin e um array binario fixo de 74400 bytes. Offset para skeleton index N = N * 186 * 4 bytes. Cada slot = 4 bytes (int). #### 5.3 Editar MeshTextureList.bin Adicionar a textura do novo mob em um slot vazio: ``` Slot livre → szFileName = "mesh\xx010101.wys", cAlpha = 'N' ``` **CRITICO:** sem esta entrada, o cliente NAO encontra a textura e o mesh nao carrega. O `GetModelTextureIndex` faz busca linear por nome — se nao encontra, retorna -1, e o CMesh aborta o carregamento. Para encontrar slots vazios: qualquer entrada com `szFileName[0] == '\0'`. Cada slot tem 264 bytes. Para editar diretamente no binario: ``` Offset = slotIndex * 264 [+0..+254] = szFileName (null-terminated, zero-padded) [+255] = cAlpha ('N' ou 'A') [+256..+259] = dwLastUsedTime (0) [+260..+263] = dwShowTime (0) ``` #### 5.4 Editar AniSound4.txt Adicionar uma secao para o novo skeleton type. A posicao na lista corresponde ao nBoneAniIndex. Para mob simples (56 linhas, formato `{nome} {aniIdx} {speed} {sound}`): ``` TYPE50 IDLE 0 30 0 WALK 1 30 0 RUN 1 20 0 ATK1 2 25 0 ATK2 2 25 0 DEATH 2 30 0 ... (preencher restante com 0 0 0) ``` #### 5.5 Editar BASE_DefineSkinMeshType (recompilacao) Em `Basedef.cpp`, a funcao `BASE_DefineSkinMeshType(nClass)` e um switch hardcoded que mapeia o nClass do item/mob → nBoneAniIndex. Para adicionar um novo mob com nClass=75: ```cpp case 75: return 50; // nosso novo skeleton xx01, index 50 no BoneAni4.txt ``` **Isto requer recompilacao do cliente.** Nao ha como evitar — o mapeamento e hardcoded no executavel. #### 5.6 Registrar no servidor (ItemList) O servidor define o `nClass` de cada mob/NPC no `ItemList.bin` ou na tabela de mobs. O nClass e o que o cliente recebe no pacote e usa para chamar `BASE_DefineSkinMeshType`. #### 5.7 Colocar arquivos ``` Mesh\xx01.bon — hierarquia Mesh\xx010001.ani — idle Mesh\xx010002.ani — walk Mesh\xx010003.ani — attack Mesh\xx010101.msh — mesh Mesh\xx010101.wys — textura ``` --- ## 6. Plano de Implementacao: Encoders Faltantes ### 6.1 BONEncoder (FBX/GLTF/GLB → BON) **Complexidade:** Baixa (formato trivial: pares de DWORDs) **Input:** FBX ou GLTF/GLB com skeleton **Output:** arquivo .bon **Algoritmo (FBX path):** 1. Percorrer todos os FbxNode com FbxSkeleton attribute 2. Para cada bone, extrair o boneID do nome (`Bone_XXX` → XXX) 3. Para cada bone, determinar o parentID: - Root: parentID = 0xFFFFFFFF - Outros: parentID = boneID do pai 4. Escrever pares [parentID, boneID] em ordem (root primeiro, BFS) **Algoritmo (GLTF/GLB path):** 1. Carregar documento GLTF com glTF SDK 2. Ler `doc.skins[0].jointIds` — lista de nodes que sao joints 3. Para cada joint node, extrair boneID do nome (`Bone_XXX` → XXX) 4. Para cada joint, determinar parentID pela hierarquia de nodes: - Se parent node nao esta em jointIds → root (0xFFFFFFFF) - Senao → boneID do parent node 5. Escrever pares [parentID, boneID] **Arquivos a criar:** - `Conversores/BON/BONEncoder.h` — declaracao - `Conversores/BON/BONEncoder.cpp` — `FromFBX()` e `FromGLTF()` + `SaveBON()` compartilhado **Estimativa:** ~120 linhas de codigo, 2-3 horas. ### 6.2 ANIEncoder (FBX/GLTF/GLB → ANI) **Complexidade:** Media (precisa avaliar animacao por frame e extrair matrizes locais) **Input:** FBX ou GLTF/GLB com skeleton + animation **Output:** arquivo .ani **Algoritmo (FBX path):** 1. Ler o FBX com FBX SDK 2. Determinar numBones (do skeleton) e numTicks (do animation stack) 3. Para cada tick (frame): a. Setar o tempo: `FbxTime::SetFrame(tick, eFrames30)` b. Para cada bone (por boneID, 0..numBones-1): - Avaliar `node->EvaluateLocalTransform(time)` → FbxAMatrix - Converter FbxAMatrix para XMFLOAT4X4 - Se o FBX veio do WYDConverter: reverter anchor correction e scale - Se e novo conteudo: usar local transforms direto c. Gravar as numBones matrizes para este tick 4. Escrever header (numTicks, numBones) + todas as matrizes **Algoritmo (GLTF/GLB path):** 1. Carregar documento GLTF com glTF SDK 2. Ler `doc.animations[0]` — channels e samplers 3. Determinar numTicks pelo max timestamp / (1/30) + 1 4. Para cada tick: a. Calcular tempo: `t = tick / 30.0f` b. Para cada bone: - Interpolar TRS dos samplers (translation, rotation, scale) no tempo t - Rotation: quaternion → matrix (GLTF usa quaternions nativamente) - Compor local matrix: S * R * T - Converter Y-Up → Z-Up (GLTF e Y-Up, ANI e Z-Up) c. Gravar as numBones matrizes 5. Escrever header + matrizes **Cuidados comuns:** - As matrizes no ANI sao LOCAL (relativas ao pai), nao world - FBX: `EvaluateLocalTransform` ja retorna local - GLTF: channels ja sao local (por spec) - FBX Z-Up (nosso export) = WYD Z-Up → sem conversao - GLTF Y-Up → precisa converter para Z-Up: conjugar cada local matrix - Scale: matrizes ANI sao puras (rotacao + translacao) - Normalizar rotation columns para magnitude 1.0 antes de salvar - Frame rate: WYD usa 30fps - GLTF: samplers podem ter interpolacao LINEAR ou STEP - LINEAR: interpolar entre keyframes - STEP: usar valor anterior **Arquivos a criar:** - `Conversores/ANI/ANIEncoder.h` — declaracao - `Conversores/ANI/ANIEncoder.cpp` — `FromFBX()`, `FromGLTF()` + `SaveANI()` compartilhado **Estimativa:** ~350 linhas de codigo, 5-8 horas. ### 6.3 ValidIndexEditor (editor de ValidIndex.bin) **Complexidade:** Baixa (array binario fixo, UI simples) **Funcionalidade:** - Carregar ValidIndex.bin (74400 bytes) - Mostrar grid: skeleton index × ani slot → nI value - Editar nI values - Salvar ValidIndex.bin **Integracao:** nova tab ou dialog no WYDConverter. **Estimativa:** ~150 linhas, 2-3 horas. ### 6.4 BoneAni4Editor (editor de BoneAni4.txt) **Complexidade:** Baixa (texto simples, UI simples) **Funcionalidade:** - Carregar BoneAni4.txt - Mostrar tabela: index, numAniTypes, numParts, szAniName - Adicionar/remover/editar linhas - Salvar BoneAni4.txt **Integracao:** mesma tab do ValidIndexEditor. **Estimativa:** ~100 linhas, 1-2 horas. ### 6.5 MeshTextureListEditor (editor de MeshTextureList.bin) **Complexidade:** Baixa (array binario fixo, ja temos decoder) **Funcionalidade:** - Carregar MeshTextureList.bin (auto-detecta classico 2048 vs Global 6096 slots) - Mostrar tabela: index, szFileName, cAlpha - Busca por nome (filtro) - Adicionar textura em slot vazio (auto-encontrar proximo livre) - Editar/remover entradas - Salvar MeshTextureList.bin **Nota:** Ja temos `MeshTextureListDecoder` com `LoadBIN`, `SaveBIN`, `LoadTXT`, `SaveTXT`. O editor so precisa de UI — a logica de I/O ja existe. **Integracao:** tab no DataEditor do WYDConverter. **Estimativa:** ~120 linhas (UI apenas), 2-3 horas. ### 6.6 AniSound4Editor (editor de AniSound4.txt) **Complexidade:** Baixa (texto formatado, parser simples) **Funcionalidade:** - Carregar AniSound4.txt - Mostrar: skeleton type → 56 linhas (acao, aniIndex, speed, sound) - Editar valores por celula - Duplicar secao de skeleton existente (template para novo mob) - Salvar AniSound4.txt **Integracao:** mesma tab dos editores de registro. **Estimativa:** ~150 linhas, 2-3 horas. ### 6.7 Ordem de Implementacao ``` Fase 1 — BONEncoder FBX + GLTF/GLB (2-3h) Permite: criar hierarquia de skeleton novo de qualquer formato Dependencias: nenhuma Teste: exportar mob existente MSH→FBX→BON e MSH→GLB→BON, comparar com original Fase 2 — ANIEncoder FBX (3-5h) Permite: criar animacoes novas a partir de FBX Dependencias: nenhuma Teste: exportar mob existente MSH+ANI→FBX→ANI, comparar com original Fase 3 — ANIEncoder GLTF/GLB (3-5h) Permite: criar animacoes novas a partir de GLTF/GLB Dependencias: SaveANI() da Fase 2 Teste: exportar mob existente MSH+ANI→GLB→ANI, comparar com original Nota: GLTF usa quaternions + timestamps flutuantes (vs FBX Euler + frames fixos) Precisa: interpolacao de samplers, quat→matrix, Y-Up→Z-Up Fase 4 — Editores de registro (5-8h) 4a. BoneAni4Editor (1-2h) — ja temos parser 4b. ValidIndexEditor (2-3h) — binario fixo 4c. MeshTextureListEditor (2-3h) — ja temos LoadBIN/SaveBIN 4d. AniSound4Editor (2-3h) — parser texto Permite: registrar skeleton, animacoes, texturas e sons Teste: adicionar entradas, verificar que cliente carrega Fase 5 — Workflow integrado (3-4h) Batch: FBX/GLB → MSH + BON + ANI + WYS em um click Auto-registro: adiciona entradas nos 4 arquivos de registro UI: wizard "Novo Mob" no WYDConverter Validacao: checar limites (40 bones, 65535 verts, etc.) Suporte: arrastar FBX ou GLB, detectar conteudo automaticamente ``` **Total estimado: 18-27 horas de desenvolvimento.** --- ## 7. Checklist Final ### Para mob com skeleton existente: - [ ] Exportar mob de referencia como FBX ou GLB - [ ] Modelar novo mesh no DCC ao redor do skeleton - [ ] Skin com max 4 influences, weights somam 1.0 - [ ] UV unwrap, textura potencia de 2 - [ ] Exportar FBX Binary Z-Up ou GLB - [ ] Converter FBX/GLB → MSH no WYDConverter - [ ] Converter textura → WYS - [ ] Adicionar textura nova em MeshTextureList.bin (se textura nova) - [ ] Copiar MSH + WYS para Mesh\ do cliente - [ ] Testar in-game ### Para mob com skeleton novo: - [ ] Tudo acima + - [ ] Criar skeleton com bones `Bone_000` a `Bone_NNN` - [ ] Criar animacoes (idle, walk, attack, death) - [ ] Exportar FBX ou GLB por animacao - [ ] Gerar BON via BONEncoder (FBX ou GLTF) - [ ] Gerar ANIs via ANIEncoder (FBX ou GLTF) - [ ] Editar BoneAni4.txt (adicionar linha com index, numAnis, numParts, prefixo) - [ ] Editar ValidIndex.bin (mapear slots → nI para cada ANI) - [ ] Editar MeshTextureList.bin (adicionar textura em slot vazio) - [ ] Editar AniSound4.txt (adicionar secao com aniIndex/speed/sound por acao) - [ ] Editar BASE_DefineSkinMeshType em Basedef.cpp (nClass → nBoneAniIndex) - [ ] Registrar mob no servidor (ItemList: nClass + LOOK_INFO) - [ ] Copiar BON + ANIs + MSH + WYS para Mesh\ do cliente - [ ] Testar in-game --- ## 8. Criar Objetos Estaticos Novos (MSA) Objetos MSA sao meshes estaticos SEM bones nem animacao. Usados para: - Construcoes (casas, torres, portais, altares) - Decoracoes de cenario (arvores, pedras, caixas, barris) - Armas e itens de chao (espadas, escudos, pocoes) - Efeitos estaticos (circulos, plataformas) ### 8.1 Diferenca entre MSH e MSA | Aspecto | MSH (Skinned) | MSA (Static) | |---|---|---| | Bones/Skeleton | Sim (max 40) | Nao | | Animacao | Sim (.ani + .bon) | Nao | | Skinning/Weights | Sim (max 4 por vertice) | Nao | | Multi-material | Nao (1 textura) | Sim (1-32 attribute ranges) | | Diffuse color | Nao (FVF 274) | Opcional (FVF 322) | | Uso tipico | Personagens, mobs, montarias | Cenario, itens, armas | ### 8.2 Sistema de registro MSA MSA sao registrados via **MeshList.txt** — uma tabela texto simples: ``` {index} {path_relativo} Exemplo: 0 mesh\cas001.msa 1 mesh\cas002.msa 2 mesh\tree01.msa ... ``` O index e o `dwObjType` que o engine usa para referenciar o objeto. `GetCommonMesh(dwObjType)` busca o path no MeshList e chama `LoadMsa()`. As texturas sao resolvidas de 2 formas: 1. Nome embeddado no MSA (campo de 11 bytes por attribute range) 2. Busca em MeshTextureList.bin por `mesh\{nomeBase}.wys` ### 8.3 Formatos de vertice MSA (FVF) | FVF | Hex | Layout | Bytes | Uso | |---|---|---|---|---| | 274 | 0x112 | Position(12) + Normal(12) + UV(8) | 32 | Maioria dos objetos | | 322 | 0x142 | Position(12) + Diffuse(4) + UV(8) | 24 | Objetos com vertex color | **CRITICO:** o FVF deve ser preservado no round-trip. Converter um MSA de FVF 322 para FVF 274 QUEBRA a renderizacao no cliente (o engine trata 322 de forma especial). ### 8.4 Workflow: Criar MSA Novo #### Passo 1 — Modelar Crie o objeto no 3ds Max ou Blender: - Apenas geometria (sem bones, sem skeleton) - Triangulos apenas (sem quads/n-gons) - Max 65.535 vertices - Escala: 1 unidade = proporcional ao mundo WYD (exporte um objeto existente como referencia) #### Passo 2 — Multi-material (opcional) MSA suporta ate 32 materiais/texturas por mesh: - Atribua materiais diferentes a grupos de faces - Cada material pode ter sua propria textura - Os attribute ranges sao preservados no round-trip Para objetos simples, 1 material e suficiente. #### Passo 3 — UV e Textura - UV Unwrap normal - Textura PNG ou TGA em potencia de 2 (256x256, 512x512) - Para multi-material: 1 textura por material - O WYDConverter converte PNG → WYS #### Passo 4 — Exportar No 3ds Max: File > Export > FBX Binary, Z-Up, Smoothing Groups ON No Blender: File > Export > glTF 2.0 (.glb), +Y Up, Apply Modifiers ON **NAO precisa** incluir Skin/Deformers (MSA e estatico). #### Passo 5 — Converter no WYDConverter 1. Abrir o FBX ou GLB no WYDConverter 2. Dropdown "MSA" > "FBX -> MSA" (ou "GLB -> MSA") 3. Nomear: `cas999.msa` (ou qualquer nome livre) 4. Converter textura: PNG -> WYS: `cas999.wys` #### Passo 6 — Registrar **MeshList.txt** — adicionar linha: ``` {indexLivre} mesh\cas999.msa ``` O index deve ser um slot livre (0-3047, MAX_COMMON_MESH = 3048). **MeshTextureList.bin** — adicionar textura: ``` Slot vazio → szFileName = "mesh\cas999.wys", cAlpha = 'N' ``` Para multi-material, adicionar CADA textura separadamente. #### Passo 7 — Colocar e testar ``` Mesh\cas999.msa ← mesh estatico Mesh\cas999.wys ← textura ``` O engine carrega via `GetCommonMesh(index)` quando o terreno ou objeto referencia o `dwObjType` correspondente ao index. Para testar: colocar o objeto em um mapa editando o .trn (terrain file) com o index do MeshList.txt. ### 8.5 Checklist MSA - [ ] Modelar objeto (sem bones, triangulos apenas) - [ ] UV unwrap, textura potencia de 2 - [ ] Exportar FBX ou GLB (sem skeleton) - [ ] Converter FBX/GLB → MSA no WYDConverter - [ ] Converter textura → WYS - [ ] Adicionar textura em MeshTextureList.bin - [ ] Adicionar entrada em MeshList.txt (index + path) - [ ] Copiar MSA + WYS para Mesh\ do cliente - [ ] Testar no mapa (colocar objeto no .trn)
    1 ponto
  31. Suporte ao 3DsMax. MSH->FBX_3Ds
    1 ponto
  32. 1x Conta RecordPlus [Hidden Content]
    1 ponto
  33. 1 ponto
  34. WEBCHEATS A P R E S E N T A ASSISTA A QUALQUER VÍDEO NOS SITES OFICIAIS DE VÍDEOS AULTOS +18 SEM PRECISAR PAGAR NADA! A plataforma a seguir consegue liberar todo e qualquer tipo de vídeos/filmes adultos restritos exclusivamente para assinantes. Você consegue assistir os conteúdos exclusivos sem precisa pagar nada mensalmente! COMO USAR A PLATAFORMA: A plataforma é bem autoexplicativa, não há muito segredo. Acesse o site do Mineiro (link no final do tópico); Vai aparecer diversos sites pornográficos, selecione o site que deseja assistir (Por exemplo, o site da Brad Montana); Em seguida você será redirecionado para adicionar o link do vídeo restrito; Pois bem, o próximo passo é acessar o site oficial da Brad Montana; Agora escolha um vídeo/filme premium, restrito somente para assinantes; Após escolher, copie o link do vídeo no campo de URL e retorne a plataforma do Mineiro; Retornando ao site do Mineiro, cole o link do vídeo no campo em que está sendo pedido; Depois, clique no botão "Quero assistir esse vídeo/filme". Caso queria entender melhor a explicação em vídeo, assista: 𝗢𝗯𝘀.: Quando você clicar no botão "Quero assistir esse vídeo/filme", vai aparecer alguns anúncios pedindo para você permitir a notificação. Basta você ficar recusando/bloqueando. A solicitação dessa notificação vai aparecer umas 6x depois que você recusar, basta ficar recusando que logo você será direcionado para assistir o vídeo em FULL HD. 𝗢𝗯𝘀.: Se você clicar em permitir, os anúncios vão deixar de aparecer. Lembre-se de remover a notificação depois, pois é muito chato. LINK DO SITE [Hidden Content] ESTE MATERIAL ERA EXCLUSIVAMENTE PARA MEMBROS VIP! USE COM MODERAÇÃO!
    1 ponto
  35. Download 35GB Belle Delphine OnlyFans Collection – Full Exclusive Videos 2025 Looking for the ultimate Belle Delphine OnlyFans video collection? This is your chance to download a massive 35GB archive of Belle Delphine’s most exclusive, uncensored videos, all in high quality. Perfect for fans and collectors who want the full collection in one place. Download Now – Belle Delphine 35GB Collection What’s Included in the Belle Delphine 35GB OnlyFans Pack? Over hundreds of exclusive videos Full-length HD and 4K content Private content from OnlyFans, Patreon, and Snapchat Rare and deleted videos not available anywhere else Leaked Belle Delphine files from 2020 to 2025 Download Now – Belle Delphine 35GB Collection Download Information Total Size: 35GB Format: MP4, ZIP Content: NSFW / 18+ Only Why Download From Us? Direct high-speed download links – No annoying ads or waiting times Verified collection – 100% working files Regular updates – New videos added all the time How to Download Belle Delphine's 35GB Collection? Click the button below to access the download page. Complete a quick verification (to confirm you are 18+). Get instant access to the 35GB Belle Delphine OnlyFans Collection. Download Now – Belle Delphine 35GB Collection
    1 ponto
  36. Este tópico reúne as principais ferramentas de OSINT, divididas por categoria, com descrições técnicas, casos de uso reais e links de acesso para facilitar seu trabalho investigativo, de segurança ou pesquisa. 1. Coleta Geral / Indexadores de OSINT • OSINT Framework Mapa de navegação de centenas de ferramentas categorizadas. 🔗 [Hidden Content] • IntelTechniques Search Tools Coleção de módulos para buscas avançadas em redes sociais, pessoas, veículos, telefone e mais. 🔗 [Hidden Content] • Aleph / OpenSanctions Buscas em bases de dados públicas, vazamentos, pessoas expostas, listas de sanções, empresas. 🔗 [Hidden Content] 2. Buscas em Deep Web, Dark Web e Fóruns • Ahmia Indexador de serviços .onion (Tor). 🔗 [Hidden Content] • DarkSearch Motor de busca projetado exclusivamente para Tor. 🔗 [Hidden Content] • OnionLand Search Indexador atualizado de onion links e dumps. 🔗 [Hidden Content] • Hunchly Dark Web Capture Captura legal forense de páginas (inclui suporte a onion sites). 🔗 [Hidden Content] 3. Consultas e Investigações em Telegram • Telegago Busca pública de grupos, canais e chats. 🔗 [Hidden Content] • Telegram Group Links Search Outro buscador de canais e grupos. 🔗 [Hidden Content] • SpyPet (OSINT Telegram) Monitora canais, mensagens públicas e movimentações. 🔗 [Hidden Content] 4. Redes Sociais • Sherlock Busca de um mesmo nome de usuário em centenas de plataformas. 🔗 [Hidden Content] • Maigret Versão mais avançada do Sherlock com maior cobertura. 🔗 [Hidden Content] • SocialScan Valida em tempo real disponibilidade de usernames e e-mails. 🔗 [Hidden Content] • SnoopSnitch / Twint (Twitter/X OSINT) Coleta tweets, perfis, metadados e histórico público. 🔗 [Hidden Content] 5. Pessoas, Endereços, Telefones, Documentos Públicos • Pipl (Profissional) Busca profunda de identidades digitais, histórico e redes. 🔗 [Hidden Content] • Truecaller Web Identificação de telefones. 🔗 [Hidden Content] • Scylla / Epieos Busca por e-mail, fotos, contas conectadas e serviços usados. 🔗 [Hidden Content] • HIBP – Have I Been Pwned Verifica exposição em vazamentos. 🔗 [Hidden Content] 6. Reconhecimento Facial e Análise Visual • FaceCheck.id Busca reversa facial (OSINT). 🔗 [Hidden Content] • PimEyes Busca imagem → pessoa em sites públicos. 🔗 [Hidden Content] • Exif.tools Leitura completa de metadados de imagem. 🔗 [Hidden Content] • PhotoForensics Erro Level Analysis, clone detection, metadados e ajustes de foto. 🔗 [Hidden Content] 7. Análise de Plataformas, Infraestrutura e Domínios • Shodan Pesquisa por dispositivos conectados, banners e vulnerabilidades. 🔗 [Hidden Content] • Censys Scanner global da internet com API para investigações. 🔗 [Hidden Content] • Whois.domaintools Dados de domínio, DNS, histórico, WHOIS. 🔗 [Hidden Content] • DNSTrails Geolocalização, histórico e investigação DNS. 🔗 [Hidden Content] 8. Veículos (Placas, Dados Públicos e OSINT Automotivo) (Importante: use dentro do que a legislação permite no lugar onde você vive.) • Sinesp (Brasil — Aplicativo Oficial) Consulta situação da placa, roubos e restrições. 🔗 [Hidden Content] • FullContact Car OSINT (Internacional) Busca rastreamento OSINT de placas em países que permitem. 🔗 [Hidden Content] • CarVertical Histórico público de veículos e ocorrências. 🔗 [Hidden Content] 9. Vazamentos, Dumps e Investigação de Credenciais • Dehashed Busca em bases de dados vazadas: e-mail, senhas, IP, domínio. 🔗 [Hidden Content] • BreachDirectory Busca por e-mail e telefone em leaks globais. 🔗 [Hidden Content] • LeakCheck Verificação de dados expostos. 🔗 [Hidden Content] 10. Geolocalização e Inteligência Visual • Google Earth Pro / Street View Reconstrução visual de locais. 🔗 [Hidden Content] • Mapillary Imagens de rua colaborativas, boa para OSINT. 🔗 [Hidden Content] • SunCalc Simulação solar para análise de sombras e horário na geolocalização. 🔗 [Hidden Content] 11. Monitoramento e Coleta Automática • Hunchly (Forensics/OSINT) Captura navegacional com cadeia de custódia. 🔗 [Hidden Content] • Maltego CE Ferramenta completa de investigação com grafos. 🔗 [Hidden Content] • Spiderfoot Scanner automatizado com dezenas de módulos OSINT. 🔗 [Hidden Content] 12. Análise de Arquivos (EXIF, Hash, Metadados) • VirusTotal Análise de arquivos, URLs e metadados. 🔗 [Hidden Content] • Hybrid Analysis Sandbox e inteligência de arquivo. 🔗 [Hidden Content] • Metadata2Go Extrai metadados de documentos, PDFs, vídeos e fotos. 🔗 [Hidden Content] 13. Comunicações, E-mails e Cabeçalhos • MXToolbox Análise de servidores, DNS, e-mail e IPs. 🔗 [Hidden Content] • Email-Checker Validação de e-mails públicos. 🔗 [Hidden Content] • Email Header Analyzer (Google) Analisa headers e rota. 🔗 [Hidden Content] 14. Inteligência Sobre Empresas, Organizações e Dados Corporativos • OpenCorporates Maior base de empresas do mundo. 🔗 [Hidden Content] • Crunchbase Informações corporativas, investimentos e estrutura. 🔗 [Hidden Content] • RocketReach Coleta e verificação de contatos profissionais. 🔗 [Hidden Content] 15. Extra: Ferramentas Investigativas Avançadas • Paladin Forensics Suite Coleta forense e preservação digital. 🔗 [Hidden Content] • Bellingcat Tools Coleção de ferramentas de investigação jornalística. 🔗 [Hidden Content] Conclusão Este conjunto cobre praticamente todo o ecossistema OSINT moderno, desde buscas em redes sociais até investigações corporativas, reconhecimento facial, dark web, análise de veículos e estruturas de domínio. Espero eu que vocês façam de bom uso tais ferramentas! Deu um certo trabalho formata isso. E se tiver algum erro de ortografia me desculpe, desde já agradeço por ler até aqui se gostou do conteúdo deixa um like ou comenta sobre o que você achou. É claro sugestões de novos tópicos são sempre bem vindos até a próxima fui 🫡
    1 ponto
  37. Fala Galera, como tive bastante dificuldade em encontrar um conversor de strdef.bin compatível com meu servidor resolvi fazer um script. Vou disponibilizar o script que para utilizar é necessário ter o Python instalado. Eu gerei .exe dele que não precisa do Python instalado, mas por eu ter usado Pyinstaler o VirusTotal acusa virus nele mesmo sem ter nada, vou disponibilizar também de qualquer maneira, agradeço se alguem da moderação verificar e confirmar. Caso baixe o script, tenha o Python instalado, se for abaixo do 3.14 execute no cmd python "C:\caminho para o script\strdef_converter.py". Sendo o 3.14 dois cliques funciona. A interface é auto explicativa. Script: [Hidden Content] Executável: [Hidden Content]
    1 ponto
  38. 392.502.978-82 Lucas323289 131.267.588.86 Vtz110570 02830093739 - Giulia1202
    1 ponto
  39. CONTA ATUALIZADA Patrocinadora: streamingsbarato.com Faça o login no Prime Video. O acesso à Crunchyroll, HBOMAX e PARAMOUNT já está incluído dentro da assinatura. Dados da Conta: [Hidden Content]
    1 ponto
  40. Pack Grátis - Nati Sweetmlr & Mariana Ladeira & Emanuelly Raquel Nati Sweetmlr Download Pack: [Hidden Content] Mariana Ladeira Download Pack: [Hidden Content] Emanuelly Raquel Download Pack: [Hidden Content]
    1 ponto
Esta tabela de classificação está definida para São Paulo/GMT-03:00
×
×
  • Criar Novo...

Informação Importante

Nós fazemos uso de cookies no seu dispositivo para ajudar a tornar este site melhor. Você pode ajustar suas configurações de cookies , caso contrário, vamos supor que você está bem para continuar.