• Updated 2023-07-12: Hello, Guest! Welcome back, and be sure to check out this follow-up post about our outage a week or so ago.

Announcing MacTasks - "To do" list for the classic Mac

paws

Well-known member
Hello!

As I've talked about in a few places, I've been teaching myself Mac programming and reactivating what little knowledge I had of C++ by making a to do list.

1691185316208.png

It's very much one of those "we chose to do it not because it was easy, but because we thought it would be easy" things. A to do list like this is a half-hour job on any modern platform, this was... extremely not. I don't know if anyone needs it or wants it. Me, I just wanted to make it, but that's no reason not to release it, is it?

Here's the web site with more information and a download link (at the bottom). You might have to right click to save the file.


There's a shareware nag screen because this is the 90's. The registration cost is 1 e-mail, sent to mactasks@rven.se. Also, I'd be very flattered if anyone wants to crack it - send me a crack and I'll put your name or handle in the about box of future releases :)

I'm calling it beta 1 because it's only ever been run on my G4 tower under 9.2.2, so who knows what happens on other computers. I might add more features, but the first thing I want to tackle is the lack of a 68k build. It should be possible, and it should run just fine even on very old machines. Unfortunately I think this will require nuking every trace of Codewarrior from this machine and setting it up again, maybe trying a few different versions... so I wanted get a first version out in case that ends up costing me my last scraps of sanity - toolchains are always fun like that. If not back avenge death, etc.

This is written using the very lovely MacZoop framework (v. 2.5.2) by a chap called Graham Cox, to whom I am much indebted. A while back I wrote a little bit about my first impressions of MacZoop. I hope to get the change to update that and release a template in case anyone else wants to try their hand at this. It's been great fun, and MacZoop speaks to me in a way that 900 tons of PowerPlant never could.

I hope you like it!
 

bigmessowires

Well-known member
Hi, I tried this on a Power Mac 9500 (emulated) and Power Mac G3 (emulated) with several different OS versions.

9500 7.6 - Application could not be opened because "Appearance Lib" could not be found
9500 8.0 - Application could not be opened because "ControlsLib" could not be found
9500 8.5 - App launches and then immediately shows "Emulator error: Runtime error: index out of bounds"
9500 9.0.4 - App launches and then immediately shows "Emulator error: Runtime error: index out of bounds"
G3 9.0.4 - The app opens and the 10 second shareware countdown begins. The first time I ran it, the countdown stopped at 1 and I was never able to get past it. The second time, the countdown reached 0 and I pressed "not now" to reach the main window. I was able to add some to-do items, reorder them, mark and unmark them as completed. Nice.
 

bigmessowires

Well-known member
Also, I'd be very flattered if anyone wants to crack it - send me a crack and I'll put your name or handle in the about box of future releases :)
I took a shot at this but did not get very far. As a PowerPC-only app, the available tools for disassembling and debugging are fewer than for 68k. Resourcerer is supposed to be able to disassemble PowerPC code in the data fork, but I couldn't make it work and it only showed raw hex bytes. The later versions of MacsBug are PowerPC capable, and I was able to install MacsBug under OS9 with an emulated Power Mac, but there's no way to press the interrupt button to invoke the debugger from within the emulator. So that approach will have to be tried later, with real hardware.

I had more success using m68kdasm in a modern Mac to disassemble the data fork's Power PC code. This was able to generate a text file disassembly, but I didn't find it to be especially useful. I thought I'd search the disassembly for places where it calls _ShowDialog or _ShowWindow, check the window/dialog IDs against the program's resources, and use that to identify code sections of interest. I admit that I'm a 68k guy and have never looked at a Power PC disassembly before, but I couldn't make much sense of it. There are no direct calls to Macintosh Toolbox routines anywhere, as far as I can see, nor any other symbolic info. The disassembly is mostly just thousands of lines like "stw [r1 + 0x0014], r2" with some headers and linkage/imports info. The use of MacZoop probably also makes the cracking job somewhat harder, since it adds more levels of indirection between your application code and the Toolbox calls (wherever they are).

If somebody else is successful at cracking it, I'd like to know what approach was used. If you publish a 68k version of the application then I'll give it another try.
 

joshc

Well-known member
This is neat, something I definitely want to try some day is just making a little app like that. I only ever got as far as REALBasic and then more recently I did a bit of Swift development but never done anything in C.

OK, so trying on OS 8.6 under SheepShaver and the app opens but it gets stuck at count 10 and the count never goes down:

1691225249015.png
 

bigmessowires

Well-known member
Still trying to hack this. I got a Power Mac 7100 running, with MacsBug 6.6.3 installed, and MacTasks. The computer has OS 8.0, which unfortunately doesn't work with MacTasks (it complains ControlsLib is missing), but I wanted to at least test the concept of hacking with MacsBug. I launched a different application and hit the interrupt button, and landed in MacsBug. This was interesting because I've never used MacsBugs to debug a mixed mode or Power PC application before. It shows a combined stack frame, and annotates which frames are PPC code and which are 68k code. Unfortunately debugging across mode changes was very confusing, with calls to _MixedModeMagic and tables of numbers that are probably UPP structures. I found it nearly impossible to look at code up and down the stack frame and make any sense of what was going on, so I quickly gave up. Maybe somebody with more experience debugging mixed mode and PPC code would do better. For me, I think the static dissassembly provided by m68kdasm was more helpful than MacsBug, but that's not saying much. It seems that I don't make a very good warez hacker.
 

Phipli

Well-known member
Still trying to hack this. I got a Power Mac 7100 running, with MacsBug 6.6.3 installed, and MacTasks. The computer has OS 8.0, which unfortunately doesn't work with MacTasks (it complains ControlsLib is missing), but I wanted to at least test the concept of hacking with MacsBug. I launched a different application and hit the interrupt button, and landed in MacsBug. This was interesting because I've never used MacsBugs to debug a mixed mode or Power PC application before. It shows a combined stack frame, and annotates which frames are PPC code and which are 68k code. Unfortunately debugging across mode changes was very confusing, with calls to _MixedModeMagic and tables of numbers that are probably UPP structures. I found it nearly impossible to look at code up and down the stack frame and make any sense of what was going on, so I quickly gave up. Maybe somebody with more experience debugging mixed mode and PPC code would do better. For me, I think the static dissassembly provided by m68kdasm was more helpful than MacsBug, but that's not saying much. It seems that I don't make a very good warez hacker.
My lazy approach would be to register it and check for differences before and after, in the app and in the system folder / hidden files. See how it records that it is registered, rather than how it does the registration.

Yes, it "costs" you a registration, but if the aim is to crack it more generally, or without your credentials. It means you're working on the filesystem instead of RAM.
 

paws

Well-known member
I found mentions of the same issue with missing ControlsLib and AppearanceLib in an old forum post (scammy looking site, maybe content harvested from UseNet). The suggested solution was to set the libraries to "Weak link" in Codewarrior, so I've done that and also flipped the define in MacZoop that makes it use the Appearance Manager (so now I don't think it does, or it uses it in a more limited way). I haven't had time to set up an emulated environment myself, unfortunately, but I'm very curious about the count down issues. If anyone wants to give it a second go, it's here.

This is neat, something I definitely want to try some day is just making a little app like that.
I will be first in line to try it out when you do! I think RealBasic looks great, by the way, that Legacy AI app is written in it and it's a beautiful Mac application.
 

paws

Well-known member
It seems that I don't make a very good warez hacker.
I don't think that's a fair conclusion at all! I'm far from an expert on this, but I think you're right that PPC is a lot harder to follow than 68k. I think the reason you're not seeing Toolbox calls is that they're wrapped in these stub libraries. Something about the Code Fragment Manager, which seems like one of those very, err, engineer-driver mid-90's Apple projects. I've looked at a few things using PEF Viewer, and there's always this big section at the end with small functions that you pass through to call imported libraries like the Toolbox. At least that's my understanding. Thankfully you don't have to worry about it as a developer.
 

bigmessowires

Well-known member
I haven't had time to set up an emulated environment myself, unfortunately, but I'm very curious about the count down issues. If anyone wants to give it a second go, it's here.
You can run an emulator inside your browser at https://infinitemac.org/1999/Mac OS 9.0 , it's a G3 running OS 9.0. Drag and drop your HQX file onto the window, and it'll appear in the emulated Mac's "The Outside World" volumes in the Downloads folder. I tried your b2 and I'm still seeing some oddness with the countdown, but it might be an artifact of running inside an emulator. The count doesn't decrease smoothly - one time it counted down by twos, and a second time it skipped from 6 directly to 3.
 

KnobsNSwitches

Well-known member
Always seems a bit rude to ask for 'more' when there's free software, but any chance you can host on a non-https site so we can download direct to old Macs?
 

bigmessowires

Well-known member
Hacking success!! Here's a patched version of Mac Tasks b1 that will accept anything as a valid serial number and then say "thank you for registering". This was my first successful crack of any 68k or PowerPC Mac software, inspired by your invitation to attempt it.

Since I didn't have much success trying to hack it in MacsBug, I went back to the static disassembly of the application's code that was generated by m68kdasm on a modern Mac. Once I realized that all of the Toolbox function calls were located at the end of the code, and were called through a layer of indirection, I could begin to manually trace backwards and identify all the places in the code where the application called a Toolbox function. From ResEdit I knew that the registration dialog box was DLOG resource 9000, and I found the one place in the application code that called GetNewDialog() and confirmed that it was passing 9000 as the ID. This was part of the application that shows the registration dialog - let's call it DoRegDialog() although I don't know what name it actually has in your source code.

Then I looked through the code to find where DoRegDialog() is called. This was complicated because I knew zero about PowerPC code before starting this hacking attempt. It took me quite a while to learn that the PowerPC bl instruction (branch and link) is effectively like JSR or CALL, and the blr instruction (branch to link register) is like RTS or RETURN. Other useful knowledge is that r1 is the stack pointer, function parameters are passed in r3 and higher (with r3 being the first parameter), and function return values are in r3. PowerPC has some frankly ridiculous instruction names, like "rlwinm." for (rotate left word immediate then AND with mask. But after many hours of manual annotating, the code started to make sense.

I observed that the code which called DoRegDialog() would then call EqualString() immediately afterwards, and it branched away if the strings didn't match. If the strings did match, the code continued on, and did something that looked like it was setting a global variable to TRUE, maybe an applicationIsRegistered flag. It seemed likely that this code was checking whether the string that was entered in the registration dialog box matched the expected serial number string. I decided I could just replace the branch instruction with a nop, so the result of EqualString() would be ignored and the code would never branch, and would always fall through to the path that sets the global flag. I used HexEdit 1.4 to edit the data fork of Mac Tasks b1 at offset 0x37454, replacing the bytes 41820089 (beq PC+0x98) with 60000000 (nop). Ta dah! It worked. With the patched version, you can enter anything or nothing into the registration dialog's text field, and the Register button will successfully register the application and thank you for it. This whole effort took me "several" hours, but I enjoyed the challenge.
 

Attachments

  • mac-tasks-b1-patched.sit
    228.9 KB · Views: 2

paws

Well-known member
@bigmessowires Well done! I'm glad you had a bit of fun with it, and thanks for the write-up. I'm afraid it really is just a simple string compare, I hope you weren't too disappointed that it was so simple. I looked at a few methods, but most of what I could find discussed various algorithms to obfuscate the code after it's entered, but best I can tell unless there's some sort of call and response system based on a system ID or user-supplied name, it all seems to be a variation on a string comparison? I do remember stepping through Exile 2's registration process when I was a teenager and getting it to throw up the "Thanks for registering!" dialog *without* actually going through with the registration, so I suppose more complicated schemes are possible if you want to think about it... and now that's got me thinking about how to obfuscate it a little more.

Anyways, the cracked version will be the official version until my pointy-haired boss forces me to upgrade it. I'll comment out the comparison and put "[k]racked by bigmessowires" in the about box in the next version later this week.

Always seems a bit rude to ask for 'more' when there's free software, but any chance you can host on a non-https site so we can download direct to old Macs?
Not rude at all, and I apologise for the exclusivity! Unfortunately the technical underpinnings for my web site are quite unmaintained, I set it up ages ago and the software it runs on has been replaced by a new version that I don't like. I want to replace it but I haven't found the time. It might make sense to make an entry on the Garden for this, but it's already past my bedtime and tomorrow's a bit packed.
 

bigmessowires

Well-known member
I'll comment out the comparison and put "[k]racked by bigmessowires" in the about box in the next version later this week.
:)

Having looked through the assembly code, I have a guess why the registration countdown isn't always working as expected. I see you're using the Time Manager and PrimeTime() to schedule a task once each second, and that task will get the text string in the dialog box and modify it. When the timer reaches zero it will also enable the Not Now button.

There is no multi-threading here, but I think Time Manager callback functions can still be called during interrupt time, so the function may appear to run "in the middle" of some operation your main code loop is doing. If the main loop involving ModalDialog() is manipulating the dialog state at the same time the Time Manager callback tries to do the same, it could cause problems. I might try changing your code so that the Timing Manager callback only decrements a global variable for time remaining, but does nothing more. Then in your main loop you can check the time remaining after each call to ModalDialog(), and update the text string and button state when needed. It could be worth a try, at least.
 

cheesestraws

Well-known member
I think Time Manager callback functions can still be called during interrupt time

They can. One shouldn't call toolbox routines that can move memory from timer routines, the results can be ... interesting. And the calls that change dialog box item texts move memory, quite aside from re-entrancy concerns. So yeah, @paws, I'd second @bigmessowires' suggestion - change a global variable, and use that in your main loop.
 

bigmessowires

Well-known member
Wasn't there also a requirement for these kinds of callback functions to set up the application's globals in register A5? Failure to do this would result in havoc when the callback function read/wrote a global variable. I don't know what the equivalent would be for PowerPC or if that's somehow handled automatically by the compiler or some glue code.
 

cheesestraws

Well-known member
Yeah, there is - and I don't know how this works under PPC either. But since there's a framework in play here and (presumably?) the timer is part of that framework, hopefully that would abstract the details away.

(Not having a space for A5 to be stored by default in the task definitions these kinds of routines was a big oversight, IMO - you have to create your own)
 

Crutch

Well-known member
Also not a PPC guy but I recall you don’t have to worry about the A5 whatnots when writing PPC code, I’m pretty sure access to application globals from Toolbox callbacks Just Works in PPC code.
 

Crutch

Well-known member
(Not having a space for A5 to be stored by default in the task definitions these kinds of routines was a big oversight, IMO - you have to create your own)
Yeah that’s really very glaringly dumb, I always wondered about that. (before 6.0.3 there isn’t even a natural way to get a pointer to the task record from inside your callback, you have to stick a DC.L in your code and stuff your A5 into there …from 6.0.3 on at least you can grab a pointer to the task record from A1 on entry, but you still need to have subclassed the task record struct to glom an A5 pointer onto the end)
 

paws

Well-known member
Thank you all for the input!

The nag screen and timer are actually done with the toolbox directly. It was simpler to do it that way since I don't think there's a simple to block startup in MacZoop without messing with features like opening a file by double clicking.

There is indeed a lot of stuff being done in the callback. The Time Manager isn't the easiest to understand when you're new to the Toolbox. I'll look into it! It's interesting that this seems to only/mostly be a problem in emulation?
 
Top