posted by Rich Wareham on Wed 20th Apr 2005 19:25 UTC
"Reverse engineering OSX, Page 2/2"
/* Block 2 */
int pt2[5]; /* An array which starts at r1 + 0x40 */
memset(pt2, 0, 20); /* 20 = 0x14 */

where I've avoided checking what value the memory gets cleared with since it isn't interesting at this moment in time.

Our next block is interesting because it calls one of the API functions we don't know about.

; Block 3
91f43314	addi	r4,r1,0x40
91f43318	lwz	r3,0x378(r1)
91f4331c	addi	r0,r2,0x74
91f43320	addi	r5,r1,0x370
91f43324	stw	r0,0x50(r1)
91f43328	bl	0x91f6708c	; symbol stub for: _CGSNewTransition

Straight away we can see that only r3, r4 and r5 are set implying that this API call takes 3 arguments. The first argument, r3, is loaded from the memory location r1 + 0x378. Referring back to block 1 we see that this is the value returned from _CGSDefaultConnection(). The second argument, r4, is set to point to memory location r1 + 0x40. As we have seen above, this is a 20 byte area of memory previously cleared by memset and then modified by the code between blocks 2 and 3. The final argument, r5, is set to point to memory location r1 + 0x370. Since this isn't really set anywhere in the disassembly we can assume that this is a pointer to some temporary variable. We can now write a C version of this block:

/* Block 3 */
int t3;     /* Temp. variable stored at r1 + 0x370 */
CGSNewTransition(t1, pt2, &t3);

We now investigate the last two blocks which contain interesting API calls.

; Block 4
91f433f0	lwz	r4,0x370(r1)
91f433f4	cmpwi	cr7,r4,__register_frame_info.eh
91f433f8	beq+	cr7,0x91f43404
91f433fc	lwz	r3,0x378(r1)
91f43400	bl	0x91f66fec	; symbol stub for: _CGSReleaseTransition
...
; Block 5
91f43484	lwz	r3,0x378(r1)
91f43488	addis	r2,r31,0x3
91f4348c	lwz	r4,0x370(r1)
91f43490	lfs	f1,0xde60(r2)
91f43494	bl	0x91f66fcc	; symbol stub for: _CGSInvokeTransition
...
91f4357c	mtcrf	56,r11
91f43580	b	restFP
; End of method

By examining the values of r3 and r4 and comparing with block 3 we can easily write equivalent C for these blocks:

/* Block 4 */
CGSReleaseTransition(t1, t3);

/* Block 5*/
CGSInvokeTransition(t1, t3, f1);

where f1 is some floating point argument. Comparing it with CGSSetWorkspaceWithTransition() we can probably be fairly sure it is the duration of the transition in seconds. The hard work is now over. We've discovered the number of arguments each API call takes and where their values come from. The remaining task is to combine these arguments with what we already know to give them names more meaningful than t1, etc.

From the call to _CGSDefaultConnection() it is clear that t1 holds a reference to the application's connection to the Window Server. The variable t3 is set by CGSNewTransition() and used by CGSReleaseTransition() and CGSInvokeTransition(). We can therefore suppose that it is some handle which refers to the created transition. The memory at pt2 is passed only to CGSNewTransition() so we can suppose that it is a structure which specifies the various parameters of the transition. The call CGSReleaseTransition(), interestingly, happens before CGSInvokeTransition(). It seems unlikely that both of these functions would be called in that order so we may suppose that the transition is only released if some other operation fails and that invoking the transition implicitly releases it. Using this information we can flesh our our C version:

CGSConnectionRef cid;
int transitionHandle;
int transitionSpec[5];
float duration;

cid = _CGSDefaultConnection();
memset(transitionSpec, 0, 20); /* 20 = 0x14 */
/* ... modifiy contents of transitionSpec (between blocks 2 and 3) ... */
CGSNewTransition(cid, transitionSpec, &transitionHandle);
/* ... find out if we failed ... */
if(failed)
       CGSReleaseTransition(cid, transitionHandle);
else
{
       /* set duration */
       CGSInvokeTransition(cid, transitionHandle, duration);
}

All that remains now is to work out the format of transitionSpec. After a little trial and error we find the following code works just as well as our initial code:

CGSConnectionRef cid;
int transitionHandle;
int transitionSpec[5];

cid = _CGSDefaultConnection();

/* Set up the transition specification */
memset(transitionSpec, 0, 20); /* 20 = 0x14 */
transitionSpec[1] = transition;
transitionSpec[2] = direction;

/* Set up the transition itself */
CGSNewTransition(cid, transitionSpec, &transitionHandle);
/* Switch desktop */
CGSSetWorkspace(cid, workspaceNumber);
/* A little pause to let the Window Server sort itself out ... */
usleep(10000); 
/* ... and fire off the transition */
CGSInvokeTransition(cid, transitionHandle, seconds);
Conclusions

The above code was added to Desktop Manager and a version sent to those people with Tiger. To my great relief (and suprise) people reported that transitions were working! There are still problems to be worked out but, because of this bit of reverse engineering, Desktop Manager on Tiger will soon be a reality.

Can we now write some documentation on these new API calls? Indeed we can. A little experimentation has shown that these are not just useful for virtual desktops. Indeed anyone can use them in a full-screen app to provide Keynote-style transitions. Simply call CGSNewTransition() which will freeze the screen, draw your new screen and call CGSInvokeTransition() to reveal it to the user.

About the author:
Rich Wareham is a 3rd year PhD student in the Signal Processing Group of the Cambridge University Engineering Department. When not playing with Geometric Algebra and proving theorems he enjoys learning about technology, experimenting with new Operating Systems and teaching undergraduates about Software Engineering.


If you would like to see your thoughts or experiences with technology published, please consider writing an article for OSNews.
Table of contents
  1. "Reverse engineering OSX, Page 1/2"
  2. "Reverse engineering OSX, Page 2/2"
e p (0)    38 Comment(s)

Related Articles

posted by Thom Holwerda on Tue 2nd Sep 2008 16:38
posted by Adam S on Tue 26th Aug 2008 12:40, submitted by estherschindler
posted by Thom Holwerda on Wed 13th Aug 2008 23:50