Coder Social home page Coder Social logo

Comments (12)

koekeishiya avatar koekeishiya commented on August 16, 2024 1

@FelixKratz I put together some quick code that looks up images loaded alongside the running program, which then looks up the symbol table to determine the address of the symbol. As it is part of the yabai codebase it is MIT license as well, so feel free to use it in your projects if it is sufficient. Note that this is not a complete symbol resolver, it just does the most basic thing needed to do what was necessary in this issue.

https://github.com/koekeishiya/yabai/blob/master/src/misc/macho_dlsym.h

Usage

static mach_port_t (* CGSGetConnectionPortById)(int);

CGSGetConnectionPortById = macho_find_symbol("/System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight", "_CGSGetConnectionPortById");

from yabai.

FelixKratz avatar FelixKratz commented on August 16, 2024 1

@koekeishiya Thanks for this. It surely is something I will eventually want to use. I went a different path for solving this particular problem in JankyBorders, where I ask the WindowServer politely for a mach_port_t which I can use to communicate with it:
FelixKratz/JankyBorders@7ba72e5

from yabai.

FelixKratz avatar FelixKratz commented on August 16, 2024

Would you be open to making this behavior opt-in instead of opt-out?

The reason being that it is not possible to retreive the sub-level of a window without screen recording permissions (I think, for whatever reason...) and this change would probably mean that yabai should always require screen recording permissions in order to reliably output the sublevel in the window query (or you retrieve the info from the scripting addition...).

Additionally, this change forces users using yabai and JankyBorders to also grant screen recording permission to JankyBorders because I am no longer able to assume that the window sub level is the default window sub level.

from yabai.

koekeishiya avatar koekeishiya commented on August 16, 2024

Does SLSGetWindowLevel return the correct value both with and without screen-recording permissions?

from yabai.

FelixKratz avatar FelixKratz commented on August 16, 2024

Does SLSGetWindowLevel return the correct value both with and without screen-recording permissions?

Yes, it works with and without screen-recording permissions. I am not sure what qualifies the SubLevel to require it. Additionally, the level can be acquired from SLSWindowIteratorGetLevel, where there is no way to get the sub level from the iterator.

from yabai.

koekeishiya avatar koekeishiya commented on August 16, 2024

There is no difference in the server-side function of the WindowServer when requesting the level (XGetWindowLevel) vs sublevel (XGetWindowSubLevel), so this check can very likely be bypassed.

from yabai.

FelixKratz avatar FelixKratz commented on August 16, 2024

Yeah I think this is because of the jump that occurs if the window is not a mapped window, where the SubLevel function simply does nothing at all while the Level function captures this case.

from yabai.

koekeishiya avatar koekeishiya commented on August 16, 2024

Yes that is correct. I assume that it would be possible to simply do the mach_msg call manually, and it would work without screen recording permissions. The annoying part is that to do so the program needs access to a non-exported symbol CGSGetConnectionPortById. There are multiple ways to get access to this function, but to do it robustly (for different macOS versions/builds) one would have to parse the symbols section of the mach-o image.

from yabai.

koekeishiya avatar koekeishiya commented on August 16, 2024

I have confirmed that constructing and sending the mach_msg manually does work and will properly return the sub_level without requiring screen recording permissions.

from yabai.

FelixKratz avatar FelixKratz commented on August 16, 2024

This is what I came up with:

struct mach_message {
  mach_msg_header_t header;

  int32_t mig_ver;
  int32_t mig_info;
  int32_t wid;
  int32_t response;
  int64_t padding;
};

extern int SLSMainConnectionID();
extern mach_port_t mig_get_special_reply_port(void);
void window_sub_layer(uint32_t wid) {
  mach_port_t response_port = mig_get_special_reply_port();
  struct mach_message msg = { 0 };

  int32_t offset = -0x2b29e8;
  mach_port_t remote_port = ((mach_port_t (*)(int)) ((void*)SLSMainConnectionID + offset))(SLSMainConnectionID());

  msg.wid = wid;
  msg.response = 0;
  msg.mig_ver = *(uint32_t*)&NDR_record;
  msg.mig_info = *(uint64_t*)&NDR_record >> 0x20;

  msg.header.msgh_local_port = response_port;
  msg.header.msgh_remote_port = remote_port;
  msg.header.msgh_bits = 0x1513;
  msg.header.msgh_voucher_port = 0;

  msg.header.msgh_id = 0x73c3;
  kern_return_t error = mach_msg((mach_msg_header_t*)&msg,
                                 0x304003,
                                 0x24,
                                 0x30,
                                 response_port,
                                 MACH_MSG_TIMEOUT_NONE,
                                 MACH_PORT_NULL           );
  
  printf("%d\n", msg.response);
}

it uses a hardcoded offset so thats not so elegant, but it works.

from yabai.

koekeishiya avatar koekeishiya commented on August 16, 2024

I did something similar as a proof of concept:

#define SLSMainConnectionID_OFFSET      0x0000000185e11848
#define CGSGetConnectionPortById_OFFSET 0x0000000185b5ee60

extern mach_port_t mig_get_special_reply_port(void);
static mach_port_t (* CGSGetConnectionPortById)(int);

#pragma pack(push,4)
struct mach_msg_get_window_sub_level {
    mach_msg_header_t header;
    NDR_record_t NDR_record;
    uint32_t window_id;
    int32_t sub_level;
    int32_t padding1;
    int32_t padding2;
};
#pragma pack(pop)

int window_sub_level(uint32_t wid)
{
    CGSGetConnectionPortById = (void *)((uint8_t*)SLSMainConnectionID - SLSMainConnectionID_OFFSET + CGSGetConnectionPortById_OFFSET);
    struct mach_msg_get_window_sub_level msg = {0};

    msg.NDR_record = NDR_record;
    msg.window_id = wid;
    msg.header.msgh_bits = 0x1513;
    msg.header.msgh_remote_port = CGSGetConnectionPortById(g_connection);
    msg.header.msgh_local_port = mig_get_special_reply_port();
    msg.header.msgh_id = 0x73C3;
    mach_msg(&msg.header, MACH_SEND_MSG|MACH_RCV_MSG, 0x24, 0x30, msg.header.msgh_local_port, 0, 0);

    return msg.sub_level;
}

The offsets are of course macOS build specific. A robust solution would have to parse the mach-o image as I mentioned above. There are very likely libraries that do this already. Seems like a fun exercise to write something like that from scratch though.

I will refrain from releasing this until I decide how to further approach the situation. For this POC I simply used nm to grab the offsets used above.

from yabai.

diocletiann avatar diocletiann commented on August 16, 2024

@koekeishiya I think this new sub-layer property is causing some windows to be stuck under others when moving from one stack to another. I was able to work around the previous version by sending --layer normal. Would it be possible have an option to where everything stays normal? Thanks.

edit: I'm not able to repro the issue. If it happens again I'll report back.

from yabai.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.