Stamping EXEs And DMGs

June 19th 15:15
by why

Here’s a brief Ruby script—one that demos Shoes’ events and animation—and it’s been injected into an EXE and a DMG:


These are web installers. Using native calls on each platform, they’ll install Shoes if no Shoes is found on the system. (If you already have Shoes installed, though, it won’t update for you.) Not a big deal, not worth mentioning really. Installers.

But, hold up. What if I told you that Shoes can build EXEs and DMGs on any of its platforms? Without needing a compiler. You can build EXEs and DMGs from Windows. And from Linux. And from OS X.

This is the built-in packager, which can be brought up with shoes -p (or shoes --package on Windows and Linux. On OS X, it’s in the Shoes main menu. (Or ⌘-x.)

Even as Ron Popeil is to the unflavored turkey, so is Shoes to these most willing binaries.


The magic is a little Ruby extension I use for manipulating EXEs and DMGs. It’s based on two programs: anal_pe and xpwn.

If you poke around with a hex editor inside Windows’ PE binary format, you’ll find an .rsrc section at the end of the file which contains the icons and dialog boxes. I insert the Ruby script into this mess.

binj = Binject::EXE.new("blank.exe")
binj.inject("SHOES_FILENAME", "simple-accordion.rb")
File.open("simple-accordion.rb") do |f|
  binj.inject("SHOES_PAYLOAD", f)
end
binj.save("accordion.exe")

The blank.exe is the empty web installer. It’s an executable that scans its own resources and then bases its moves on what it finds. If it finds a Ruby script (or Shy file) in the SHOES_PAYLOAD resource, it’ll run it. And if it finds an installer in the SHOES_SETUP resource, it’ll run the installer rather than going out to the web.

So, yeah, blank.exe comes with Shoes and we inject when you go to package. Yeah?


Fabricating DMGs is a different kind of binary hacking. In this case, we’ve got to build an HFS+ partition and then convert that to a DMG.

One would start by making a small, raw HFS+ file and gzipping it.

$ dd if=/dev/zero of=blank.hfs bs=512K count=1
$ mkfs.hfsplus -v Shoes blank.hfs
$ gzip blank.hfs
$ mv blank.hfs.gz blank.hfz

This blank disk gets included with Shoes as well. And at runtime, we use our extension to build the innards of the DMG.

binj = Binject::DMG.new("blank.hfz", "Accordion")
binj.inject_dir("Accordion.app", "/tmp/accordion.app")
binj.chmod_file(0755, "Accordion.app/Contents/MacOS/accordion-launch")
binj.save("accordion.dmg")

This amazing code is only possible due to tons of incredible work by the XPwn dev team. First of all, their DMG and HFS+ code is totally portable and only depends on zlib. But also, the API is just too easy. Their project is going to catch on big, not only in jail breaking the iPhone from any platform, but in building DMGs from the commandline on OS X itself!

Since both anal_pe and XPwn are GPL, I’m afraid this extension must be GPL as well. The rest of Shoes is MIT. Which is okay I guess since the packager isn’t really needed to run Shoes apps.


This is all a bleeding fresh part of today’s builds.

As for Linux. I’m not decided as to what to do about generating Linux binaries. Either using makeself or plain shell scripting will do. But do I download binaries or automate package manager steps? The unsurety.

30 comments

tstrokes

said on June 19th 10:42

Man this is awesome stuff. Shoes was already cool but now it is on a completely different level.

Inferis

said on June 19th 10:48

Wow, this is super. We can actually give our apps to other people now :)

Phil

said on June 19th 10:59

I’m going to ask people to give back the apps I’ve already given to them.

I wrote anal_pe

said on June 19th 13:06

I wrote anal_pe and I have no problem with changing the license to accommodate what you’re doing. You should have emailed me to let me know…

/mike

Dr Nic

said on June 19th 14:20

This is a new level of inspired genius.

Re: linux, on debian/ubu I’d… well… I can’t think of the last thing I installed that was’t a rubygem or via apt-get or via source. I only live in that world on ui-less production boxes, so no fancy Shoes apps in that world anyway. I could have not written this paragraph.

Dr Nic

said on June 19th 14:32

Those are great programs. Nice find indeed.

_why

said on June 19th 14:38

mike (of anal_pe): Since XPwn is GPL as well, I’m perfectly okay with this licensing. I’m not down on the GNU way, that was just a note. Thankyou for your nice work, of course!

Will

said on June 19th 14:44

It’s like being able to wear and tie your shoes. Great!

technomancy

said on June 19th 15:09

> But do I download binaries or automate package manager steps?

I’d suggest creating a .deb file and uploading it to a Launchpad PPA maybe? Launchpad is to debs as github is to gems in a way. If the client supports apturl, things are easy peasy. (Ignoring fedora/gentoo users.)

Feoh

said on June 19th 16:09

Seems like downloading binaries for Linux is the way to go. If you’re reasonably conservative about the shlibs you require a fairly vanilla Linux binary will run in virtually every distribution. The package approach would be nice but you’d need to support at least .deb and .rpm which could be more pain than it’s worth.

_why

said on June 19th 20:10

Feoh: I think I’ll follow your suggestion, only relying on openssl and zlib to be present on the system. I guess I could probably have the shell script sniff out exactly what it needs. That could prevent the download of gtk, glib, cairo, pango and vlc.

ms_windows

said on June 19th 20:55

Whoa, this is truly awesome. Congrats. However you would do well to remove your rubies from my TEMP after you’re done.

oh yeah

said on June 19th 21:03

as awesome as this is, it doesn’t actually seem to work on windows. It gets as far as ‘packaging’ and then nothing happens. It leaves behind a small, non-working exe with the same name as the ruby script.

_why

said on June 19th 21:38

oh yeah: Oh, I have no doubts that there are bugs. Send me the script you tried to bundle.

oh yeah

said on June 19th 22:25

I tried simple-accordian.rb from the Shoes sample dir.

_why

said on June 20th 00:08

Looks like the windows build has an older copy of the Ruby extension. I’ve got to run, but a new build should be out in a day or two. Thanks for finding that.

Bluebie

said on June 20th 00:22

It isn’t working on Mac OS Leopard intel either, frozen progress bar doodad. It does make the .dmg and .exe though, even though there is no progress, however when I mount the DMG and run the .app, shoes opens and asks me which ruby file to run. O_o

Also what about dependancies like pictures and junk? gotta figure those in too! Maybe shoes should be like camping, have appname.rb and a directory named appname, and put all your junk in there. Be easy to do on Mac, but on windows maybe you could shove the directory data in to a zip and write the zipped data in to the exe? Surely windows provides api’s for unzipping stuff, which could be shoved out to a temporary directory. :)

Julik

said on June 20th 00:44

On Leopard PPC the downloaded app does one jump in the dock and dies.
review:~ julik$ /SimpleAccordion.app/Contents/MacOS/simple-accordion-launch
/SimpleAccordion.app/Contents/MacOS/simple-accordion-launch: line 9: ./cocoa-install: Bad CPU type in executable
FSPathMakeRef(/Applications/Shoes.app) failed with error -43.

Are we PPC oldies with the 4-core number crunchers sent away from the door at the Shoes party?

oh yeah

said on June 20th 03:07

Bluebie: You can package a .shy (which can include pictures and stuff)

proppy

said on June 20th 04:16

On Linux, I heard one could use PackageKit to trigger installation of packages in a cross distribution way.

That could be handy for installing shoes and its dependencies, before installing the distributed shoes application.

cmw

said on June 20th 05:18

I got it figured out on an Intel-Leopard to build the apps. works like charm (even with pics and stuff if you build a shy).
Only there are some flaws I discovered:
- It won’t build any ‘Linux’ package for me
- It freezes after having created the exe and dmg and dies eventually (might be connected to the above)
- It does not include shoes in the exe/dmg if asked to do so

Oh and one minor thing is that I need to dig around to find a way of building the shy since shoes -s won’t work and you don’t have the option to build it in the menu.
shoes-launch -s is the way on Leopard.

jdefontes

said on June 20th 10:16

Punting on the version dependecies is kinda sucky.

I’ve got a really old version installed. The exe above launches fine but the app itself errors out on “uninitialized constant” or some such. If you’re going to detect that I have Shoes installed and not upgrade me, you could at least tell me that the version I have isn’t compatible with the app I’m trying to run. And then give me the option to upgrade.

tim

said on June 20th 10:43

woo hoo!

on ubuntu hardy:

said on June 20th 11:07

( :7693): Gtk- CRITICAL **: gtk_widget_show: assertion `GTK_IS_WIDGET (widget)’ failed

DeeJay

said on June 20th 11:44

“These are web installers. Using native calls on each platform, they’ll install Shoes if no Shoes is found on the system.”

On Windows, unsurprisingly, the installer fails – silently – if not run with Administrator rights. The short cuts get created, but the ‘Common Files\Shoes\…’ directory tree cannot be built.

But once it’s installed it’s really exciting news!

_why

said on June 20th 12:07

This hack is only a few days old, so it’s great to finally get some reaction and some bug reports. My complements to all of you.

jdefontes: Okay, good point, I will go with a prompt if it finds a new installer.

cmw

said on June 20th 15:29

on jdefontes’ post:

perhaps one could also add the version number it is built/tested with like that:
Shoes. app(:version => ’r726’) do … end

Or directly include the version of the builder?

Anyway, kudos to you _why. Awesome Shoes. Shiny!

Girvo

said on June 23rd 17:47

Hi there! Love shoes :)

I built the latest release from source on Ubuntu here, worked like a charm. Only the package manager won’t build the programs for me :(

(<unknown>:13474): Gtk-CRITICAL **: gtk_widget_show: assertion `GTK_IS_WIDGET (widget)' failed

That’s my error.

_why

said on June 23rd 21:52

Hey, Girvo. Yes, this is fixed in commit d1ac0de.

Girvo

said on June 25th 08:28

Wicked, I’ll test it when I get into work :D

Comments are closed for this entry.