kuazi GA Posted July 5 Posted July 5 8 hours ago, The Binary Expert said: 您需要通过以下方式禁用 SharpOD 的选项@Xjun 使用这个插件 @启动 它就像魔法一样。非常感谢您的努力。 顺便问一下,您是如何修改原始 ScyllaHide x64 插件的源代码的? 问候。 肖恩。 https://bbs.kanxue.com/thread-282244.htm 1 1
karan Posted July 7 Author Posted July 7 (edited) ahhhh! he modified ntdll's export table to trick VMP like the wine_get_version function exists! Very nice trick 3.8.8 is working. Edited July 7 by karan 1
boot Posted July 7 Posted July 7 On 7/6/2024 at 12:46 AM, kuazi GA said: https://bbs.kanxue.com/thread-282244.htm This method has some shortcomings. If compiling a 32-bit ScyllaHide plugin, although it can bypass VMP's Anti-DeBug on Win7 x64 SP1, it does not work on Win10/11 x64. 1
Sean the hard worker Posted July 7 Posted July 7 36 minutes ago, karan said: ahhhh! he modified ntdll's export table to trick VMP like the wine_get_version function exists! Very nice trick 3.8.8 is working. @karan Is this vmprotect a cracked version? Regards. sean. 1
karan Posted July 7 Author Posted July 7 10 minutes ago, The Binary Expert said: @karan Is this vmprotect a cracked version? Regards. sean. I bought it because I personally respect VMP developers. He can take my money with him! 1
Sean the hard worker Posted July 7 Posted July 7 2 minutes ago, karan said: I bought it because I personally respect VMP developers. He can take my money with him! @karan Okay, I got it. Thanks. Regards.. sean. 1
kao Posted July 7 Posted July 7 46 minutes ago, boot said: If compiling a 32-bit ScyllaHide plugin, although it can bypass VMP's Anti-DeBug on Win7 x64 SP1, it does not work on Win10/11 x64. Haven't tried yet but I don't see a (theoretical) reason why it should not work on Win10/11 x64. Can you explain that? 2
karan Posted July 7 Author Posted July 7 44 minutes ago, kao said: Haven't tried yet but I don't see a (theoretical) reason why it should not work on Win10/11 x64. Can you explain that? I tested the original author's code and found that it doesn't seem to bypass the protection properly on x86 systems. VMProtect does not appear to search through the entire Export Table to find the desired function. So, I modified the code to overwrite the last export function of ntdll.dll with wine_get_version and then place the original function right after it. As a result, the bypass worked successfully! void AddWineFunctionName(HANDLE hProcess) { BYTE* remote_ntdll = (BYTE*)GetModuleBaseRemote(hProcess, L"ntdll.dll"); if (!remote_ntdll) return; SIZE_T readed = 0; IMAGE_DOS_HEADER dos_header; ReadProcessMemory(hProcess, remote_ntdll, &dos_header, sizeof(IMAGE_DOS_HEADER), &readed); if (dos_header.e_magic != IMAGE_DOS_SIGNATURE) return; IMAGE_NT_HEADERS pe_header; ReadProcessMemory(hProcess, (BYTE*)remote_ntdll + dos_header.e_lfanew, &pe_header, sizeof(IMAGE_NT_HEADERS), &readed); if (pe_header.Signature != IMAGE_NT_SIGNATURE) return; DWORD export_adress = pe_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; if (!export_adress) return; DWORD export_size = pe_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; BYTE* new_export_table = (BYTE*)VirtualAllocEx(hProcess, remote_ntdll + 0x1000000, export_size + 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); IMAGE_EXPORT_DIRECTORY export_directory; ReadProcessMemory(hProcess, remote_ntdll + export_adress, &export_directory, sizeof(IMAGE_EXPORT_DIRECTORY), &readed); BYTE* tmp_table = (BYTE*)malloc(export_size + 0x1000); if (tmp_table == nullptr) return; // Copy functions table BYTE* new_functions_table = new_export_table; ReadProcessMemory(hProcess, remote_ntdll + export_directory.AddressOfFunctions, tmp_table, export_directory.NumberOfFunctions * sizeof(DWORD), &readed); WriteProcessMemory(hProcess, new_functions_table, tmp_table, export_directory.NumberOfFunctions * sizeof(DWORD), &readed); g_log.LogInfo(L"[VMPBypass] new_functions_table: %p", new_functions_table); // Copy ordinal table BYTE* new_ordinal_table = new_functions_table + export_directory.NumberOfFunctions * sizeof(DWORD) + 0x100; ReadProcessMemory(hProcess, remote_ntdll + export_directory.AddressOfNameOrdinals, tmp_table, export_directory.NumberOfNames * sizeof(WORD), &readed); WriteProcessMemory(hProcess, new_ordinal_table, tmp_table, export_directory.NumberOfNames * sizeof(WORD), &readed); g_log.LogInfo(L"[VMPBypass] new_ordinal_table: %p", new_ordinal_table); // Copy name table BYTE* new_name_table = new_ordinal_table + export_directory.NumberOfNames * sizeof(WORD) + 0x100; ReadProcessMemory(hProcess, remote_ntdll + export_directory.AddressOfNames, tmp_table, export_directory.NumberOfNames * sizeof(DWORD), &readed); WriteProcessMemory(hProcess, new_name_table, tmp_table, export_directory.NumberOfNames * sizeof(DWORD), &readed); g_log.LogInfo(L"[VMPBypass] new_name_table: %p", new_name_table); free(tmp_table); tmp_table = nullptr; // Setup new name & name offset BYTE* wine_func_addr = new_name_table + export_directory.NumberOfNames * sizeof(DWORD) + 0x100; WriteProcessMemory(hProcess, wine_func_addr, "wine_get_version\x00", 17, &readed); DWORD wine_func_offset = (DWORD)(wine_func_addr - remote_ntdll); WriteProcessMemory(hProcess, new_name_table + export_directory.NumberOfNames * sizeof(DWORD), &wine_func_offset, 4, &readed); // Set fake ordinal WORD last_ordinal = export_directory.NumberOfNames; WriteProcessMemory(hProcess, new_ordinal_table + export_directory.NumberOfNames * sizeof(WORD), &last_ordinal, 2, &readed); // Get address of GetCurrentTeb function to be placed after the new function BYTE* get_current_teb = reinterpret_cast<BYTE*>(GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtCurrentTeb")); DWORD get_current_teb_offset = (DWORD)(get_current_teb - remote_ntdll); // Set new function address (wine_get_version) and GetCurrentTeb function address DWORD new_function_offset = get_current_teb_offset; WriteProcessMemory(hProcess, new_functions_table + export_directory.NumberOfFunctions * sizeof(DWORD), &new_function_offset, 4, &readed); // Setup new directory export_directory.NumberOfNames++; export_directory.NumberOfFunctions++; DWORD name_table_offset = (DWORD)(new_name_table - remote_ntdll); export_directory.AddressOfNames = name_table_offset; DWORD function_table_offset = (DWORD)(new_functions_table - remote_ntdll); export_directory.AddressOfFunctions = function_table_offset; DWORD ordinal_table_offset = (DWORD)(new_ordinal_table - remote_ntdll); export_directory.AddressOfNameOrdinals = ordinal_table_offset; // Change the offset of header data DWORD old_prot; VirtualProtectEx(hProcess, remote_ntdll + export_adress, sizeof(IMAGE_EXPORT_DIRECTORY), PAGE_EXECUTE_READWRITE, &old_prot); WriteProcessMemory(hProcess, remote_ntdll + export_adress, &export_directory, sizeof(IMAGE_EXPORT_DIRECTORY), &readed); VirtualProtectEx(hProcess, remote_ntdll + export_adress, sizeof(IMAGE_EXPORT_DIRECTORY), old_prot, &old_prot); } I confirmed that my Windows 10 version works fine. cheers! ScyllaHide_x86.zip 3 3
kao Posted July 7 Posted July 7 55 minutes ago, karan said: VMProtect does not appear to search through the entire Export Table to find the desired function. That's correct. It does binary search (see the implementation here), as the names in the export table are supposed to be ordered lexically. The code in https://bbs.kanxue.com/thread-282244.htm doesn't take that into account. However, this hasn't changed between Win7/Win10/Win11 - if it worked on one system, it should have worked on all of them. 1
Progman Posted July 7 Posted July 7 I think the issue has to do with ntdll differences between Win7 vs Win10/11. Keep in mind the WinSDK is free to change and there are many times where they don't maintain backwards compatibility on internal APIs, unlike the normal WinAPI 1
boot Posted July 8 Posted July 8 10 hours ago, kao said: Win7/Win10/Win11 - if it worked on one system, it should have worked on all of them. I have already conducted testing before, and if you compile the 32-bit plugin according to the original source code provided here (https://bbs.kanxue.com/thread-282244.htm). Original 32-bit (Imperfect Version).zip This plugin is effective on Win7 x64 SP1; But it fails in Win10/11 x64. e.g. VMP_3.8.7_x86_32-bit.vmp.exe Win7 x64 SP1 √ Spoiler Win7_x64_SP1_2024-07-08_114815.mp4 Win10 x64 × Spoiler Win10_x64_2024-07-08_114324.mp4 Win11 x64 × Spoiler Win11_x64_2024-07-08_115629.mp4 By recompiling the 32-bit plugin according to the modified code provided by karan, the above issue has been resolved. The revised and recompiled complete version is now uploaded as follows, and has been tested to be effective in Win7/10/11 x64. ScyllaHide_2024_x86_x64_v0.002.zip 3 7
fReestYler Posted July 13 Posted July 13 (edited) @boot Could you help me compile the InjectorCLIx64.exe ? Just want to try it with ollydbg 64 ! Thanks Edited July 13 by fReestYler 3
boot Posted July 13 Posted July 13 1 hour ago, fReestYler said: InjectorCLIx64.exe Spoiler InjectorCLIx64.exe (1.33 MB) 2 1
RADIOX Posted July 13 Posted July 13 6 hours ago, jackyjask said: @boot do you have IDA8.3 plugin in your collection? I think are available in the forum a nice guy shared already the most useful plugins and also he included them in his repostery in GitHub. 1
kao Posted July 13 Posted July 13 (edited) 11 hours ago, jackyjask said: IDA8.3 plugin IIRC, you can use the generic injector with IDA. Check the docs and let us know, if you test it.. Edited July 13 by kao 1
jackyjask Posted July 14 Posted July 14 14 hours ago, RADIOX said: he included them in his repostery in GitHub. yes, exactly, good pointer, found and used 1
TRISTAN Pro Posted July 14 Posted July 14 (edited) On 7/7/2024 at 6:58 PM, karan said: I tested the original author's code and found that it doesn't seem to bypass the protection properly on x86 systems. VMProtect does not appear to search through the entire Export Table to find the desired function. So, I modified the code to overwrite the last export function of ntdll.dll with wine_get_version and then place the original function right after it. As a result, the bypass worked successfully! void AddWineFunctionName(HANDLE hProcess) { BYTE* remote_ntdll = (BYTE*)GetModuleBaseRemote(hProcess, L"ntdll.dll"); if (!remote_ntdll) return; SIZE_T readed = 0; IMAGE_DOS_HEADER dos_header; ReadProcessMemory(hProcess, remote_ntdll, &dos_header, sizeof(IMAGE_DOS_HEADER), &readed); if (dos_header.e_magic != IMAGE_DOS_SIGNATURE) return; IMAGE_NT_HEADERS pe_header; ReadProcessMemory(hProcess, (BYTE*)remote_ntdll + dos_header.e_lfanew, &pe_header, sizeof(IMAGE_NT_HEADERS), &readed); if (pe_header.Signature != IMAGE_NT_SIGNATURE) return; DWORD export_adress = pe_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; if (!export_adress) return; DWORD export_size = pe_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; BYTE* new_export_table = (BYTE*)VirtualAllocEx(hProcess, remote_ntdll + 0x1000000, export_size + 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); IMAGE_EXPORT_DIRECTORY export_directory; ReadProcessMemory(hProcess, remote_ntdll + export_adress, &export_directory, sizeof(IMAGE_EXPORT_DIRECTORY), &readed); BYTE* tmp_table = (BYTE*)malloc(export_size + 0x1000); if (tmp_table == nullptr) return; // Copy functions table BYTE* new_functions_table = new_export_table; ReadProcessMemory(hProcess, remote_ntdll + export_directory.AddressOfFunctions, tmp_table, export_directory.NumberOfFunctions * sizeof(DWORD), &readed); WriteProcessMemory(hProcess, new_functions_table, tmp_table, export_directory.NumberOfFunctions * sizeof(DWORD), &readed); g_log.LogInfo(L"[VMPBypass] new_functions_table: %p", new_functions_table); // Copy ordinal table BYTE* new_ordinal_table = new_functions_table + export_directory.NumberOfFunctions * sizeof(DWORD) + 0x100; ReadProcessMemory(hProcess, remote_ntdll + export_directory.AddressOfNameOrdinals, tmp_table, export_directory.NumberOfNames * sizeof(WORD), &readed); WriteProcessMemory(hProcess, new_ordinal_table, tmp_table, export_directory.NumberOfNames * sizeof(WORD), &readed); g_log.LogInfo(L"[VMPBypass] new_ordinal_table: %p", new_ordinal_table); // Copy name table BYTE* new_name_table = new_ordinal_table + export_directory.NumberOfNames * sizeof(WORD) + 0x100; ReadProcessMemory(hProcess, remote_ntdll + export_directory.AddressOfNames, tmp_table, export_directory.NumberOfNames * sizeof(DWORD), &readed); WriteProcessMemory(hProcess, new_name_table, tmp_table, export_directory.NumberOfNames * sizeof(DWORD), &readed); g_log.LogInfo(L"[VMPBypass] new_name_table: %p", new_name_table); free(tmp_table); tmp_table = nullptr; // Setup new name & name offset BYTE* wine_func_addr = new_name_table + export_directory.NumberOfNames * sizeof(DWORD) + 0x100; WriteProcessMemory(hProcess, wine_func_addr, "wine_get_version\x00", 17, &readed); DWORD wine_func_offset = (DWORD)(wine_func_addr - remote_ntdll); WriteProcessMemory(hProcess, new_name_table + export_directory.NumberOfNames * sizeof(DWORD), &wine_func_offset, 4, &readed); // Set fake ordinal WORD last_ordinal = export_directory.NumberOfNames; WriteProcessMemory(hProcess, new_ordinal_table + export_directory.NumberOfNames * sizeof(WORD), &last_ordinal, 2, &readed); // Get address of GetCurrentTeb function to be placed after the new function BYTE* get_current_teb = reinterpret_cast<BYTE*>(GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtCurrentTeb")); DWORD get_current_teb_offset = (DWORD)(get_current_teb - remote_ntdll); // Set new function address (wine_get_version) and GetCurrentTeb function address DWORD new_function_offset = get_current_teb_offset; WriteProcessMemory(hProcess, new_functions_table + export_directory.NumberOfFunctions * sizeof(DWORD), &new_function_offset, 4, &readed); // Setup new directory export_directory.NumberOfNames++; export_directory.NumberOfFunctions++; DWORD name_table_offset = (DWORD)(new_name_table - remote_ntdll); export_directory.AddressOfNames = name_table_offset; DWORD function_table_offset = (DWORD)(new_functions_table - remote_ntdll); export_directory.AddressOfFunctions = function_table_offset; DWORD ordinal_table_offset = (DWORD)(new_ordinal_table - remote_ntdll); export_directory.AddressOfNameOrdinals = ordinal_table_offset; // Change the offset of header data DWORD old_prot; VirtualProtectEx(hProcess, remote_ntdll + export_adress, sizeof(IMAGE_EXPORT_DIRECTORY), PAGE_EXECUTE_READWRITE, &old_prot); WriteProcessMemory(hProcess, remote_ntdll + export_adress, &export_directory, sizeof(IMAGE_EXPORT_DIRECTORY), &readed); VirtualProtectEx(hProcess, remote_ntdll + export_adress, sizeof(IMAGE_EXPORT_DIRECTORY), old_prot, &old_prot); } I confirmed that my Windows 10 version works fine. cheers! ScyllaHide_x86.zip 307.15 kB · 16 downloads I don't know coding and understand this code but I see they import some api and try to protect one function which I was protected in my old loader at that times because they have src code of code and can devirtualized with it. I mean after virtualized some function they still virtualized the part of code. @boot would yuo like send one example of vmp latest version with api redirection for the vb please ? Mazotoa daholy😁 TP-Mada. Edited July 14 by TRISTAN Pro Edit some word 2
boot Posted July 19 Posted July 19 On 7/8/2024 at 12:08 PM, boot said: The revised and recompiled complete version is now uploaded as follows, and has been tested to be effective in Win7/10/11 x64. I tried to rebuild ScyllaHide using some old versions of project code to make it compatible with Windows XP 32-bit/x86. I think XP is a classic OS... 1
Keichi Sakamoto Posted July 21 Posted July 21 Hey, I'm new to this. I'm testing this with vmprotected game, it does not show debugger present error but it shows `File corrupted! This program has been manipulated and maybe it's infected by a Virus or cracked. This file won't work anymore` anyone have similar issue? 1
karan Posted July 22 Author Posted July 22 (edited) O~~K.. VMProtect has been updated now! The latest version changed to find the "wine_get_host_version" function instead of "wine_get_version". Edited July 22 by karan 3
Sean the hard worker Posted August 9 Posted August 9 On 7/8/2024 at 1:08 PM, boot said: I have already conducted testing before, and if you compile the 32-bit plugin according to the original source code provided here (https://bbs.kanxue.com/thread-282244.htm). Original 32-bit (Imperfect Version).zip This plugin is effective on Win7 x64 SP1; But it fails in Win10/11 x64. e.g. VMP_3.8.7_x86_32-bit.vmp.exe 856 kB · 12 downloads Win7 x64 SP1 √ Reveal hidden contents Win7_x64_SP1_2024-07-08_114815.mp4 1.46 MB · 0 downloads Win10 x64 × Reveal hidden contents Win10_x64_2024-07-08_114324.mp4 2.08 MB · 0 downloads Win11 x64 × Reveal hidden contents Win11_x64_2024-07-08_115629.mp4 1.9 MB · 0 downloads By recompiling the 32-bit plugin according to the modified code provided by karan, the above issue has been resolved. The revised and recompiled complete version is now uploaded as follows, and has been tested to be effective in Win7/10/11 x64. ScyllaHide_2024_x86_x64_v0.002.zip 1.77 MB · 52 downloads @boot View this video. Can I debug the vmprotected .net executable with your one? Regards. sean. 1
fReestYler Posted October 23 Posted October 23 (edited) @karan Could you help me compile a 32 bit unpackme with kernel + user mode ? (for the latest version (3.9.1) if it is possible !) Just want to try it with SharpOD v0.6e ! Thanks Edited October 23 by fReestYler 1
karan Posted October 25 Author Posted October 25 (edited) On 10/23/2024 at 11:54 PM, fReestYler said: @karan Could you help me compile a 32 bit unpackme with kernel + user mode ? (for the latest version (3.9.1) if it is possible !) Just want to try it with SharpOD v0.6e ! Thanks Sharp OD is no longer bypassed, I think that have to use TitanHide now. and, Bypassing Anti-Debug using wine-related functions also no longer works. Edited October 25 by karan 1
jackyjask Posted October 25 Posted October 25 you could build up ScyllaHide plugin with addons mentioned in this topic it does the job 1
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now