diff --git a/.gitmodules b/.gitmodules index ca68aaa..a908805 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "third_party/subhook"] path = third_party/subhook - url = https://github.com/Zeex/subhook.git + url = https://github.com/Dasharo/subhook.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ddeff9..3cb143b 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.10) project (DarkTanos) +set(API_VERSION 7) + set(CMAKE_CXX_STANDARD 17) set(CMAKE_BUILD_TYPE Debug) set(CMAKE_POSITION_INDEPENDENT_CODE ON) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 650524e..d31122e 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -4,6 +4,8 @@ find_package(JNI REQUIRED) include_directories(${PROJECT_NAME} PRIVATE ${JNI_INCLUDE_DIRS}) +add_definitions(-DAPI_VERSION=${API_VERSION}) + add_library(${PROJECT_NAME} SHARED eu_darkbot_api_DarkTanos.cpp bot_client.cpp @@ -11,4 +13,8 @@ add_library(${PROJECT_NAME} SHARED sock_ipc.cpp ) +find_package(X11 REQUIRED) +target_include_directories(${PROJECT_NAME} PRIVATE ${X11_INCLUDE_DIR}) +target_link_libraries(${PROJECT_NAME} PRIVATE ${X11_LIBRARIES}) + target_compile_options(${PROJECT_NAME} PRIVATE -std=c++17) diff --git a/client/bot_client.cpp b/client/bot_client.cpp index d10c8fd..bcc08ea 100644 --- a/client/bot_client.cpp +++ b/client/bot_client.cpp @@ -1,9 +1,7 @@ #include "bot_client.h" #include -#include -#include -#include -#include +#include +#include #include "utils.h" #include "proc_util.h" @@ -16,9 +14,251 @@ #include #include +#include +#include + #define MEM_SIZE 1024 +namespace +{ + Window browser_window = 0; + + bool get_window_pid(Display *display, Window window, pid_t &pid) + { + Atom atom_pid = XInternAtom(display, "_NET_WM_PID", True); + if (atom_pid == None) + { + return false; + } + + Atom actual_type = None; + int actual_format = 0; + unsigned long nitems = 0; + unsigned long bytes_after = 0; + unsigned char *prop = nullptr; + + int status = XGetWindowProperty( + display, + window, + atom_pid, + 0, + 1, + False, + XA_CARDINAL, + &actual_type, + &actual_format, + &nitems, + &bytes_after, + &prop); + + if (status != Success || !prop || nitems == 0) + { + if (prop) + { + XFree(prop); + } + return false; + } + + pid = static_cast(*reinterpret_cast(prop)); + XFree(prop); + return true; + } + + bool is_browser_window_pid(pid_t owner_pid, pid_t browser_pid) + { + return owner_pid == browser_pid || ProcUtil::IsChildOf(owner_pid, browser_pid); + } + + bool x11_window_control_available() + { + const char *display = std::getenv("DISPLAY"); + return display && *display; + } + + bool try_get_window_attrs(Display *display, Window window) + { + XWindowAttributes attrs; + if (XGetWindowAttributes(display, window, &attrs) != 0) + { + return true; + } + + XSync(display, False); + return XGetWindowAttributes(display, window, &attrs) != 0; + } + + Window find_toplevel_root_child(Display *display, Window root, Window window) + { + if (!window) + { + return 0; + } + + Window current = window; + while (current) + { + Window root_return = 0; + Window parent_return = 0; + Window *children = nullptr; + unsigned int nchildren = 0; + + if (!XQueryTree(display, current, &root_return, &parent_return, &children, &nchildren)) + { + return current; + } + + if (children) + { + XFree(children); + } + + if (parent_return == 0 || parent_return == root) + { + return current; + } + + current = parent_return; + } + + return window; + } + + Window find_browser_owned_descendant_recursive(Display *display, Window root, pid_t browser_pid) + { + if (!root) + { + return 0; + } + + pid_t owner_pid = -1; + if (get_window_pid(display, root, owner_pid) && is_browser_window_pid(owner_pid, browser_pid)) + { + return root; + } + + Window root_return = 0; + Window parent_return = 0; + Window *children = nullptr; + unsigned int nchildren = 0; + + if (!XQueryTree(display, root, &root_return, &parent_return, &children, &nchildren)) + { + return 0; + } + + Window found = 0; + for (unsigned int i = 0; i < nchildren && !found; i++) + { + found = find_browser_owned_descendant_recursive(display, children[i], browser_pid); + } + + if (children) + { + XFree(children); + } + return found; + } + + Window find_browser_client_window(Display *display, pid_t browser_pid) + { + Window root = DefaultRootWindow(display); + Atom atom_client_list = XInternAtom(display, "_NET_CLIENT_LIST", True); + if (atom_client_list != None) + { + Atom actual_type = None; + int actual_format = 0; + unsigned long nitems = 0; + unsigned long bytes_after = 0; + unsigned char *prop = nullptr; + + int status = XGetWindowProperty( + display, + root, + atom_client_list, + 0, + 4096, + False, + XA_WINDOW, + &actual_type, + &actual_format, + &nitems, + &bytes_after, + &prop); + + if (status == Success && prop && actual_type == XA_WINDOW) + { + Window *windows = reinterpret_cast(prop); + Window child_fallback = 0; + for (unsigned long i = 0; i < nitems; i++) + { + pid_t owner_pid = -1; + if (!get_window_pid(display, windows[i], owner_pid)) + { + continue; + } + + if (owner_pid == browser_pid) + { + XFree(prop); + return windows[i]; + } + + if (!child_fallback && ProcUtil::IsChildOf(owner_pid, browser_pid)) + { + child_fallback = windows[i]; + } + } + + XFree(prop); + if (child_fallback) + { + return child_fallback; + } + } + else if (prop) + { + XFree(prop); + } + } + + Window any_owned = find_browser_owned_descendant_recursive(display, root, browser_pid); + if (!any_owned) + { + return 0; + } + + return find_toplevel_root_child(display, root, any_owned); + } + + void toggle_browser_visibility(pid_t browser_pid, bool visible) + { + Display *display = XOpenDisplay(nullptr); + if (!display) + { + return; // Failed to open display + } + + if (!browser_window || !try_get_window_attrs(display, browser_window)) + { + browser_window = find_browser_client_window(display, browser_pid); + } + + if (!browser_window) + { + XCloseDisplay(display); + return; // Failed to find browser window + } + + // Show or hide the browser window + visible ? XMapWindow(display, browser_window) : XUnmapWindow(display, browser_window); + + XFlush(display); + XCloseDisplay(display); + } +} + enum class MessageType { @@ -125,6 +365,16 @@ BotClient::BotClient() : { } +void BotClient::ToggleBrowserVisibility(bool visible) +{ + if (m_flash_pid == -1 || !x11_window_control_available()) + { + return; + } + + toggle_browser_visibility(m_browser_pid, visible); +} + BotClient::~BotClient() { if (m_browser_pid > 0) @@ -177,7 +427,17 @@ void BotClient::LaunchBrowser() sid.replace(0, 6, ""); } - execle(fpath, fpath, "--sid", sid.c_str(), "--url", url.c_str(), "--launch", NULL, envp.data()); + execle( + fpath, + fpath, + "--sid", sid.c_str(), + "--url", url.c_str(), + "--launch", + "--ozone-platform=x11", + "--disable-background-timer-throttling", + "--disable-renderer-backgrounding", + NULL, + envp.data()); break; } default: @@ -249,7 +509,6 @@ void BotClient::reset() m_flash_pid = -1; m_flash_sem = -1; m_flash_shmid = -1; - m_flash_pid = -1; } // Not a great name since it has side-effects like refreshgin or restarting the browser @@ -285,12 +544,10 @@ void BotClient::SendFlashCommand(Message *message, Message *response) return; } + if ((m_flash_shmid = shmget(m_flash_pid, MEM_SIZE, IPC_CREAT | 0666)) < 0) { - if ((m_flash_shmid = shmget(m_flash_pid, MEM_SIZE, IPC_CREAT | 0666)) < 0) - { - fprintf(stderr, "[SendFlashCommand] Failed to get shared memory\n"); - return; - } + fprintf(stderr, "[SendFlashCommand] Failed to get shared memory\n"); + return; } if (!m_shared_mem_flash || m_shared_mem_flash == (void *)-1) @@ -319,7 +576,6 @@ void BotClient::SendFlashCommand(Message *message, Message *response) sembuf sop[2] { { 0, -1, 0 }, { 1, 0, 0 } }; // Notify - sop[0] = { 0, -1, 0 }; if (semtimedop(m_flash_sem, &sop[0], 1, &timeout) == -1) { if (errno == EAGAIN) @@ -332,7 +588,6 @@ void BotClient::SendFlashCommand(Message *message, Message *response) } // Wait - sop[1] = { 1, 0, 0 }; if (semtimedop(m_flash_sem, &sop[1], 1, &timeout) == -1) { if (errno == EAGAIN) diff --git a/client/bot_client.h b/client/bot_client.h index b677bb8..4b28067 100644 --- a/client/bot_client.h +++ b/client/bot_client.h @@ -27,6 +27,7 @@ class BotClient bool IsValid(); void SendBrowserCommand(const std::string &&s, int sync); + void ToggleBrowserVisibility(bool visible); void SendFlashCommand(Message *message, Message *response = nullptr); diff --git a/client/eu_darkbot_api_DarkTanos.cpp b/client/eu_darkbot_api_DarkTanos.cpp index 4c00f5a..3d671f7 100644 --- a/client/eu_darkbot_api_DarkTanos.cpp +++ b/client/eu_darkbot_api_DarkTanos.cpp @@ -38,13 +38,15 @@ JNIEXPORT void JNICALL Java_eu_darkbot_api_DarkTanos_setSize JNIEXPORT void JNICALL Java_eu_darkbot_api_DarkTanos_setVisible (JNIEnv *, jobject, jboolean jv) { - client.SendBrowserCommand(utils::format("setVisible|{}", int(jv)), 0); + client.ToggleBrowserVisibility(jv); } JNIEXPORT void JNICALL Java_eu_darkbot_api_DarkTanos_setMinimized (JNIEnv *, jobject, jboolean jv) { - client.SendBrowserCommand(utils::format("minimize|{}", int(jv)), 0); + // Using the same hiding approach as with "setVisible", since minimizing causes lags and increases the tick. + // The boolean "jv" is inverted because "setMinimized(true)" should hide the window. + client.ToggleBrowserVisibility(!jv); } JNIEXPORT void JNICALL Java_eu_darkbot_api_DarkTanos_reload @@ -72,7 +74,7 @@ JNIEXPORT jlong JNICALL Java_eu_darkbot_api_DarkTanos_getMemoryUsage JNIEXPORT jint JNICALL Java_eu_darkbot_api_DarkTanos_getVersion (JNIEnv *, jobject) { - return 6; + return API_VERSION; } JNIEXPORT void JNICALL Java_eu_darkbot_api_DarkTanos_keyClick diff --git a/do_lib/avm.cpp b/do_lib/avm.cpp index f22de70..631c598 100755 --- a/do_lib/avm.cpp +++ b/do_lib/avm.cpp @@ -1,6 +1,8 @@ #include "avm.h" #include "binary_stream.h" #include "utils.h" +#include +#include avm::ClassClosure * avm::AbcEnv::finddef(const std::string &name) { @@ -37,6 +39,138 @@ avm::ClassClosure * avm::AbcEnv::finddef(std::function cache; + std::lock_guard lock(cache_mutex); + auto [it, inserted] = cache.try_emplace(this); + if (!inserted) + { + return it->second; + } + + std::string method_name = pool->get_method_name(id); + std::string &resolved_name = it->second; + resolved_name = method_name; + + if (!method_name.empty() && declarer.is_traits()) + { + auto *traits = declarer.traits(); + if (traits && traits->traits_pos) + { + BinaryStream s { traits->traits_pos }; + uint32_t trait_count = 0; + + switch (traits->pos_type) + { + case 0: // instance_info + { + /* name */ s.read_u32(); + /* super_name */ s.read_u32(); + + auto flags = s.read_u32(); + if ((flags & 0x8) != 0) + { + /* protected_ns */ s.read_u32(); + } + + auto interface_count = s.read_u32(); + for (uint32_t i = 0; i < interface_count; i++) + { + /* interface */ s.read_u32(); + } + + /* iinit */ s.read_u32(); + trait_count = s.read_u32(); + break; + } + case 1: // class_info + case 2: // script_info + { + /* cinit/init */ s.read_u32(); + trait_count = s.read_u32(); + break; + } + default: + { + break; + } + } + + for (uint32_t j = 0; j < trait_count; j++) + { + /* name */ s.read_u32(); + unsigned char tag = s.read(); + int kind = (tag & avm::TRAIT_mask); + + switch(kind) + { + case avm::TRAIT_Slot: + case avm::TRAIT_Const: + { + /* slot_id */ s.read_u32(); + /* type_name */ s.read_u32(); + uint32_t vindex = s.read_u32(); + if (vindex) + { + /* vkind */ s.read(); + } + break; + } + case avm::TRAIT_Class: + case avm::TRAIT_Function: + { + /* slot_id */ s.read_u32(); + /* class/function index */ s.read_u32(); + break; + } + case avm::TRAIT_Method: + case avm::TRAIT_Getter: + case avm::TRAIT_Setter: + { + /* disp_id */ s.read_u32(); + uint32_t method_index = s.read_u32(); + + if (static_cast(method_index) == id) + { + switch (kind) + { + case avm::TRAIT_Setter: + resolved_name = "set " + method_name; + break; + case avm::TRAIT_Getter: + resolved_name = "get " + method_name; + break; + default: + break; + } + + return resolved_name; + } + break; + } + default: + { + break; + } + } + + if (tag & avm::ATTR_metadata) + { + uint32_t metadata_count = s.read_u32(); + for (uint32_t i = 0; i < metadata_count; i++) + { + /* metadata index */ s.read_u32(); + } + } + } + } + } + + return resolved_name; +} + avm::MyTraits avm::Traits::parse_traits(avm::PoolObject *custom_pool) { BinaryStream s { traits_pos }; @@ -100,6 +234,13 @@ avm::MyTraits avm::Traits::parse_traits(avm::PoolObject *custom_pool) trait.id = class_index; break; } + case avm::TRAIT_Function: + { + /* uint32_t slot_id = */ s.read_u32(); + uint32_t function_index = s.read_u32(); + trait.id = function_index; + break; + } case avm::TRAIT_Method: case avm::TRAIT_Getter: case avm::TRAIT_Setter: diff --git a/do_lib/avm.h b/do_lib/avm.h index 3ca25ca..1ecbda2 100755 --- a/do_lib/avm.h +++ b/do_lib/avm.h @@ -39,6 +39,7 @@ namespace avm TRAIT_Getter = 0x02, TRAIT_Setter = 0x03, TRAIT_Class = 0x04, + TRAIT_Function = 0x05, TRAIT_Const = 0x06, TRAIT_COUNT = TRAIT_Const+1, TRAIT_mask = 15 @@ -469,10 +470,7 @@ namespace avm return result; } - std::string name() - { - return pool->get_method_name(id); - } + std::string name(); inline bool compiled() { return ((flags >> 21) & 1) == 1;