On code injection on MacOSX High Sierra - continued

nil

This the second post in the series on MacOSX code injection using MIP. The latter makes it very trivial to inject code into existing application, let's focus on the following use case: use newly introduced Apple's API for dark appearance windows. For the special case of Terminal.app.

For this, we need to understand a little bit about an Objective-C/Cocoa application lifecycle. I won't make a fuss about it but for the sake of what follows, just consider that it implies many design patterns, and that windows are managed by NSWindowController.

Objective-C has that awesome feature that is, you can extend existing classes using categories. It then ends in finding a hook to replace a relevant method, in this case, - (void)windowDidLoad.

The implementation

Using MIP, code injection ends up in writing a few line of codes compiled as a Bundle, and the corresponding Info.plist in order for MIP's injector to know when to act.

Let's start with the category interface, that simply declares a method that will be used in place of another by the Objective-C runtime.

// Version: $Id$
//
//

// Commentary:
//
//

// Change Log:
//
//

// Code:

#import <AppKit/AppKit.h>

// ///////////////////////////////////////////////////////////////////
//
// ///////////////////////////////////////////////////////////////////

@interface NSWindowController(DarkTerminal)

- (void)injected_windowDidLoad;

@end

//
// DarkTerminal.h ends here

Let's continue with it's implementation. A + (void) load class method is loaded whenever the category is discovered. It replaces the stock implementation with the custom one.

// Version: $Id$
//
//

// Commentary:
//
//

// Change Log:
//
//

// Code:

#import <Cocoa/Cocoa.h>

#import <objc/runtime.h>

#import "DarkTerminal.h"

// ///////////////////////////////////////////////////////////////////
//
// ///////////////////////////////////////////////////////////////////

@implementation NSWindowController(DarkTerminal)

+ (void)load
{
    method_exchangeImplementations(class_getInstanceMethod(self, @selector(windowDidLoad)),
                                   class_getInstanceMethod(self, @selector(injected_windowDidLoad)));
}

- (void)injected_windowDidLoad
{
    NSLog(@"MIP - injected_windowDidLoad");

    self.window.titlebarAppearsTransparent = true;
    self.window.titleVisibility = NSWindowTitleVisible;
    self.window.styleMask |= NSWindowStyleMaskTexturedBackground;
    self.window.appearance = [NSAppearance appearanceNamed:NSAppearanceNameVibrantDark];
}

@end

//
// DarkTerminal.m ends here

Finally, the mandatory Bundle's Info.plist. Note the MIP* directives.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>MIPUseBlacklistMode</key>
  <false/>
  <key>MIPBundleNames</key>
  <array>
    <string>com.apple.Terminal</string>
  </array>
  <key>MIPExecutableName</key>
  <string>Terminal</string>
  <key>CFBundleExecutable</key>
  <string>DarkTerminal</string>
  <key>CFBundleIdentifier</key>
  <string>local.DarkTerminal</string>
  <key>CFBundleInfoDictionaryVersion</key>
  <string>6.0</string>
  <key>CFBundleName</key>
  <string>DarkTerminal</string>
  <key>CFBundlePackageType</key>
  <string>BNDL</string>
  <key>CFBundleShortVersionString</key>
  <string>1.0</string>
  <key>CFBundleVersion</key>
  <string>1</string>
</dict>
</plist>

Using the Makefile as shown on Github, installs the Bundle is MIP's standard location.

Note

Beware that code injection is risky and can lead to undefined behavior. In this case, the hooked method - (void)windowDidLoad has a default empty implementation, which makes our approach safe. However when it is not the case, method swizzling is a preferred pattern, even though I did not get it to work yet.

On Code Injection on MacOSX High Sierra

nil

I've always fancied tweaking my system, and a distribution like ArchLinux and its open source Window Managers seem like a fine solution. However, I've been using MacOSX for ages, and despite the fact I'm having a close look at Xiaomi's expansion, I'm still in love with Darwin, a little bit less with Quartz.

Today I came across MIP a code injection platform that doesn't go for the obvious. Here's a starting guide to have it installed.

Disclaimer

The following is the first of a series of posts about code injection. It supposes SIP (System Integrity Protection) to be turned off at your own risks.

Prerequisistes

First, to build MIP, you'll have to create yourself a code signing identity. For now, we'll start with a self generated one.

Open Keychain Access.app and the menu, choose Keychain Access > Certificate Assistant > Create a Certificate.

nil

Follow the wizard, filling up fields to your likings, the certificate will end up in the login section of the Keychain Access application.

MIP

Building MIP is then straigthforward.

$ brew install binutils
$ cd ~/Development
$ git clone https://github.com/LIJI32/MIP.git mip
$ cd mip/MIP
$ emacs -nw -Q Makefile
$ # set SIGN_IDENTITY ?= "Julien Wintz" to match the name of your profile
$ make
$ make install

On blogging using Org mode through Jekyll

nil

I've been using Octopress with pleasure for up to ten years to build my corner on the internet. Sadly, as software lifecycle goes, it relied on a version of ruby that is no more supported by my favorite rbenv ruby version manager.

So I made a drastical choice: starting from scratch, not porting the existing content, using technologies I love, that is Emacs (Doom, but I'll come to it in another post), it's fabulous Org mode, its big brother Jekyll, and of course, GitHub pages for hosting.

There is a paramount reason for which Jekyll is a first choice when it comes to blogging hosted by Github, is that it actually powers Github pages. Therefore, as opposed to other static sites generators, pushing the sources to the master branch of your repository triggers an automatic build of the site. Awesome.

Installing Jekyll

There are many examples on how to install Jekyll, the most simple way is to follow the excellent tutorials on its website. The only tweaks I made and that are still WIP, is to take the theme I chose to be very minimalistic and extensible through Liquid, aka lanyon, out of the bundle system and integrate it directly into my source tree structure, see https://github.com/jwintz/jwintz.github.io.

Configuring Emacs

Org is very powerful and can be used in a vast variety of context, from agenda to GTD, mail redaction, or even as a publishing basis for scientific publications or presentations. Here's the tricky part. I'll assume for the following that you are a bit familiar with Org mode, at least when it comes to its syntax and headers, which is more than sufficiant for a start. Org comes with a publish module that can be used in order to generate HTML pages and it is what we will use in order to replace Mardown.

Emacs in its recent versions comes with a package management system, and we'll focus on one package in particular: org2jekyll. I won't repeat its README section, however, since Jekyll has evolved since the first drafts of this Emacs package, I'll post my setup of the package.

(def-package! org2jekyll
  :defer t
  :commands
  (org2jekyll-create-draft org2jekyll-publish org2jekyll-list-posts)
  :config
  (setq org2jekyll-blog-author "jwintz")
  (setq org2jekyll-source-directory (expand-file-name "~/Sites/jwintz.github.io/"))
  (setq org2jekyll-jekyll-directory (expand-file-name "~/Sites/jwintz.github.io/"))
  (setq org2jekyll-jekyll-drafts-dir "_drafts/")
  (setq org2jekyll-jekyll-posts-dir "_posts/")
  (setq org-publish-project-alist
    `(("default"
       :base-directory ,(org2jekyll-input-directory)
       :base-extension "org"
       :publishing-directory ,(org2jekyll-output-directory)
       :publishing-function org-html-publish-to-html
       :headline-levels 4
       :section-numbers nil
       :with-toc nil
       :html-preamble t
       :recursive t
       :make-index t
       :html-extension "html"
       :body-only t)
      ("post"
       :base-directory ,(org2jekyll-input-directory)
       :base-extension "org"
       :publishing-directory ,(org2jekyll-output-directory org2jekyll-jekyll-posts-dir)
       :publishing-function org-html-publish-to-html
       :headline-levels 4
       :section-numbers nil
       :with-toc nil
       :html-preamble t
       :recursive t
       :make-index t
       :html-extension "html"
       :body-only t)
      ("home"
       :base-directory ,(org2jekyll-input-directory)
       :base-extension "org"
       :publishing-directory ,(org2jekyll-output-directory)
       :publishing-function org-html-publish-to-html
       :headline-levels 4
       :section-numbers nil
       :with-toc nil
       :html-preamble t
       :recursive t
       :make-index t
       :html-extension "html"
       :body-only t)
      ("page"
       :base-directory ,(org2jekyll-input-directory)
       :base-extension "org"
       :publishing-directory ,(org2jekyll-output-directory)
       :publishing-function org-html-publish-to-html
       :headline-levels 4
       :section-numbers nil
       :with-toc nil
       :html-preamble t
       :recursive t
       :make-index t
       :html-extension "html"
       :body-only t)
      ("img"
       :base-directory ,(org2jekyll-input-directory "img")
       :base-extension "jpg\\|png"
       :publishing-directory ,(org2jekyll-output-directory "img")
       :publishing-function org-publish-attachment
       :recursive t)
      ("js"
       :base-directory ,(org2jekyll-input-directory "js")
       :base-extension "js"
       :publishing-directory ,(org2jekyll-output-directory "js")
       :publishing-function org-publish-attachment
       :recursive t)
      ("css"
       :base-directory ,(org2jekyll-input-directory "css")
       :base-extension "css\\|el"
       :publishing-directory ,(org2jekyll-output-directory "css")
       :publishing-function org-publish-attachment
       :recursive t)
      ("web" :components ("img" "js" "css")))))

Note the home and page components that are essential in order for latest Jekyll versions to work as expected.

Usage

There is a bit of terminology to be aware of, once again quickly grasped from Jekyll's excellent documentation. In short, an entry in your website can either be a post (when it comes to blogging) or a page for dedicated content.

org2jekyll provides functions that can be bound within Emacs to quickly create one of those, here's my bindings:

 "C-c b c" 'org2jekyll-create-draft
 "C-c b p" 'org2jekyll-publish
 "C-c b l" 'org2jekyll-list-posts

Invoking those commands will prompt you for some contents to fill up such as title, description, tags, category and create the Org header accordingly. I have not forked org2jekyll to fix it, but make sure to move the AUTHOR, DATE and TITLE at the top of the header otherwise the publish step will fail.

Then, simply compose the content using Org syntax and invoke 'org2jekyll-publish in order to create the HTML file.

Here the rule of thumb:

  • pages are located in the root directory (both ORG and exported HTML)
  • posts drafts are located (WRT/ the setup) in the _drafts folder
  • posts exports are located (WRT/ the setup) in the _posts folder
  • once done, commit both the ORG and HTML files
  • ignore the _site folder under version control

Firing up an *eshell*, run bundle exec jekyll serve and observe a preview at http://localhost:4000.

Remind that ORG files are not automatically exported as you edit them, call 'org2jekyll-publish whenever you are done with your modification and if jekyll-serve is running, the website will automatically be updated.

Publishing

Just push your master branch to origin remote, and the site will be online in a few seconds.