Hacker Newsnew | past | comments | ask | show | jobs | submit | moopark's commentslogin

When Mac OS X updates the signature on a binary (for instance, when you configure a firewall rule for a previously unsigned binary), the actual Mach-O file will be changed -- and your digest will be incorrect.

Skype (which has notoriously complex obfuscation) had this problem for a short time when Mac OS X 10.5 was released:

http://securosis.com/blog/leopard-firewall-code-signing-brea...

You can work around this by validating only the important subset of the Mach-O contents, but it's probably not worth it. Cracked applications (rather than, say, reverse engineered serial number generators) are an annoying thing to use -- you'll have to refrain from applying updates until you get a new crack, trust the person distributing the crack, etc.

It's not something I (or, afaik, most other small Mac developers) really worry about.


The fact that most simple copy protection can be broken by someone that knows a bit of assembly shouldn't surprise anyone writing applications, and this post is just self-congratulatory silliness that doesn't actually help someone that wants to protect their software.

It wouldn't be any more responsible/ethical/useful of me to post a "I Can Crack Your Non-Mac App With Just A Copy Of IDA Pro and HexRays" tutorial. I could show you how I can press 'F5' and decompile your code back to surprisingly readable pseudo-C, but that's not going to help you secure your application, it's just patting myself on the back and showing you how cool I am.

On top of that, the author is still flogging the PT_DENY_ATTACH horse, despite the fact that it's been documented over, and over, and over again as trivial to bypass. PT_DENY_ATTACH was added to meet the minimal contractual requirements Apple had to movie studios and record companies by preventing users from attaching a debugger to DVD Player and iTunes. It's not a real security solution. There's a simple open source kext (that was first implemented for Mac OS X 10.3!) that simply disables it across the board:

https://github.com/dwalters/pt_deny_attach


  The fact that most simple copy protection can be broken by
  someone that knows a bit of assembly shouldn't surprise
  anyone writing applications, 
But it does, which is the point of the author.

  this post is just self-congratulatory silliness
You know, some people just like to write up something they did that they think is pretty interesting. People will keep inventing the wheel over and over again and still be proud of their wheel. This kind of derogatory remarks are uncalled for. You have no idea about the thoughts or feelings of the author and have no reason to think ugly things.

If I were to respond to your comment in the same way, I would say that you were just displaying your superiority complex over someone learning the ropes. Or perhaps bitterness and jealousy over the attention this article gets, while your more advanced knowledge does not get the attention it deserves. Or ... whatever. I can come up with a number of epithets to attribute to you based on that little bit of text, all equally uncalled for.


The fact that PT_DENY_ATTACH can be easily patched by modifying the xnu source code made me wonder about the politics of open source inside Apple.


Just like an insurance company -- by maintaining a car fleet across which relatively few instances of damage/theft/etc will occur. They can afford to take a significant hit on a few cars, and they can afford to hire people to maintain/verify/qualify the cars at scale.


Who covers maintenance?

Who covers the cost (often quite high) of fixing/cleaning dings, dents, rips, missing/broken parts, broken headrests, dog hair, dirt (from plants?), gum, and other such things?

These are car maintenance issues I see all the time when using both Zipcar and CityCarShare. If I had a personal car, I'd flip if it was subjected to the level of abuse that these fleet cars are -- the depreciation due to wear and tear would be significant.

[Edit] Since I got downvoted to 0 (and have no idea why), I'll expand on the relevant answers I found in the FAQ:

  What if my car is returned dirty?

  Renters are required to be responsible and keep
  the car as clean as possible. At the end of every
  rental, you can rate the user and leave feedback.
  If they return it in a dirty condition, you should
  note this when leaving feedback.
The owner is out the cleaning fee and time to do it (if it can even be cleaned). CityCarShare records people who leave the car dirty, too, but it doesn't stop people from dirtying up the cars.

... actually, that's the only FAQ item I can find on the subject. The cars are insured, but what's the deductible? Who pays it? Is the insurance company really going to pay out a claim for a broken headrest, a ripped seat, or a ruined floor mat?


I'm not sure why you were downvoted either but I suspect it was for the same reason I was for asking similar questions.

As you point out from the faq, the onus seems to be on the owner and the only consequence for the dirtiness is the ability to rate the user. I think a more serious question relates to the adjudication process in the case of more serious violations such as scratches, damage or theft of items from the car. Do they automatically take the word of the owner - and what happens in the case of false claims against a renter by an owner - what appeal process does the renter have in that case?


I don't have a Facebook account, don't want one, and even if I did have one, I wouldn't want to link it up with this service.

That may make me a luddite, a terrorist, or even a social-scrooge, but you'd think it would be worthwhile to make a minor concession to your potential customer base and allow people to create accounts without having to go through (and link them to) Facebook.


Listening to music doesn't drive a constant 22 kHz tone.

[Edit] I really don't understand -- why was I downvoted here? Can someone explain why they think I'm wrong? Isn't a constant, high-frequency tone going to draw more power than variable output with significant larger gaps (and a likely lower peak voltage difference?)


How do you define (and enforce) those rules? My general position when writing library code is that none of my classes should be subclassed.

The very few classes that may be subclassed are documented as such, and the methods that may be overridden are explicitly documented, as well as what behavior is required from the subclass when overriding those methods.

The invariants of complex inheritance hierarchies are very hard to understand. What happens if the superclass method isn't called? What happens if one of those methods is called, but another isn't, and the object is placed into an indeterminate state? What enforces that your subclass -- and all other subclasses -- will conform to these often complex and difficult to describe invariants?

This is very similar to multi-threading with mutable vs. immutable data. By making your data immutable, you grossly simplify the understanding of your system's behavior.


Why do you need to enforce rules, other than documenting the classes/methods defined?

     What happens if the superclass method isn't called
Shit doesn't work or it breaks, then the person who sub-classed needs to fix it. Sometimes it also means your class is leaking encapsulation.

This also happens with plain aggregation/composition btw. It also happens with data-immutability (which has nothing to do with inheritance, as your object can be immutable and extend a dozen classes).


Why do you need to enforce rules, other than documenting the classes/methods defined?

The less repetitive work we delegate to human fallibility and instead delegate to a machine, the more time we have for human ingenuity.

Shit doesn't work or it breaks, then the person who sub-classed needs to fix it.

It's not that simple. The more difficult it is to understand the rules of behavior before changing the code, the more difficult it is to change the behavior. It's not just a question of expressing valuable -- but simple -- kindergarden requirements (this value may not be NULL), but higher-level requirements (this method must be called in the context of a READ-COMMITTED transaction).

The more you can express concisely, the easier it is to mutate the system over time. It's not a question of breaking code -- or noticing when it breaks -- but having the language assist in simply not breaking it at all.

This also happens with plain aggregation/composition btw.

Composition makes invariants easier to understand. If you then design your classes so poorly as to fail to enforce correct behavior through their API insofar as it is possible to do so, that is the programmer's failure.

It also happens with data-immutability (which has nothing to do with inheritance, as your object can be immutable and extend a dozen classes).

Data immutability is related to the avoidance of inheritance insofar as they both very significantly facilitate the full and easy comprehension of an implementation invariants.


     this method must be called in the context of a 
     READ-COMMITTED transaction
I get what you're saying, but I like conventions and clear APIs with proper encapsulation.

Here's a sample from Python/Django ...

      @transaction.commit_on_success
      def do_stuff_with_the_db():
           db.execute("insert into tmp values (1)")
           raise Exception
Or if you need to supply the DB queries yourself, you can implement your own context-manager than use it with a with block ...

     with stuff.inside_transaction() as obj:
          obj.execute("query")
No need to extend a class that represents a transaction or some other shit like that.

       having the language assist in simply not breaking it at all
You know that's an utopian goal. What I dislike most about languages that try to detect too much shit for me is that it gives me a false sense of security. And the worst offender is Java: not only is its type-system too weak, because it is manifest-typed you get the false impression that it guarantees stuff for you, when it doesn't.


There, problem solved.

The problem isn't solved. You now know what the type is for that particular moment. What you don't know are the invariants of the don_t_know_the_return_type() function.

What types/subtypes is can return, whether it can return None, what exceptions it can throw, and whether those will change in the future. That information can only come from a type system and/or documentation. Simply reading the implementation only tells you the current state of the system, not the rules that will govern future iterations of it.

The more invariants that can be expressed concisely by the language itself and enforced by the compiler, the less work is left to the user of the function to review documentation/implementation.

This is one large reason why well-designed advanced type systems are so valuable -- you can express complex invariants using them, and then let the compiler enforce those invariants.


First, even in Java you don't know the return type / invariants ...

     int n = func_that_returns_positive_even_number()
What you need is to document the thing:

    def func_that_returns_positive_even_number():
          """Returns positive even number."""
This comment will be available when typing "help(func_that_returns_positive_even_number)" in the Python console btw.

Or if you're paranoid and that function can totally break your code:

         n = func_that_returns_positive_even_number()
         assert isinstance(n, int) and n % 2 == 0 and n >= 0
Or to make extra sure this will hold in the future (i.e. protecting from code-changes done by other people) ...

     import unittest

     class TestMyFunc(unittest.TestCase):
           def test_is_positive_and_even(self):
                n = func_that_returns_positive_even_number()
                self.assertTrue( n % 2 == 0 and n >= 0 )


First, even in Java you don't know the return type / invariants ...

This is a classic type system straw man. The language doesn't support encoding integer ranges in the type system, ergo, the type system is not ever a significant advantage and all invariants must be documented. You fool!

What you need is to document the thing:

Some invariants require further documentation. The more you can express concisely in code via the type system, the more time you and your API clients save in both development and maintenance.

More succinctly: By expressing them in code you let the compiler automate the work of enforcing them.

Or if you're paranoid and that function can totally break your code:

An assert doesn't "protect" your code from future changes (better phrasing would be: make your code adaptable to change, loosely coupled with its dependencies as to allow iteration of your code and its dependencies independently).

An assert simply causes your code to fail in an obvious way. It's still up to you to track them down (at runtime) and figure out where you went wrong.


"""The more you can express concisely in code via the type system, the more time you and your API clients save in both development and maintenance"""

That's not necessarily true ... the more complex the type-system, the more time you lose feeding it.

"""It's still up to you to track them down (at runtime) and figure out where you went wrong"""

A language with runtime-dispatching and/or where NULL is allowed will have the same problems. I mean, personally I had more null-pointer-exceptions than all the other kinds of errors combined and multiplied by a dozen.

We are talking about Python versus Java here ... Haskell's type system is smarter and can detect lots of errors for you, but then again we were also talking about beginner-friendliness.


Being sympathetic because you like the target is embracing the rule of men, not the rule of law.

No. I'm sympathetic because it was a very stupid prank, not because of the target.

What was the actual damage caused? It certainly didn't "paralyze" Palin's campaign.


... Marissa actually proved to be good at it.

Except that, as far as I can tell from the vast majority of Google's products, Google (perhaps not Marissa?) has a design sense that ranges from "poor" to "incredibly poor". Chrome, perhaps, being a singularly notable exception.


They've been pretty key in advancing the state of the art^, so I don't know how you can say that. Maybe you don't have a sense of history.

^: I'm talking about search, mail, and maps specifically.


These are technological advances, not design advances.

As for "not having a sense of history", that's a strange ad hominem, but I'll lend it a response:

- I was on the internet when there was only gopher (from terminals at the library).

- I was also on the internet when Netscape 1.0 was the shiny new thing (and the 56k frame relay I used to download it was considered quite fast).

- I used Mapquest, Altavista, and Yahoo, but before that, I used people's random collections of favorite links that you found on their home pages.

I don't lack perspective on history, but I don't equate Google's technological improvements in "advancing the state of the art" with overall quality UX design. The minimalist home page was brilliant in comparison to Yahoo at the time, but it's not an aesthetic they've been able to continue to apply successfully.

Google consistently produces fantastic technology, lackluster design, and then rarely, a design outliers that is actually good.


I'm curious - where do you draw the line between a technological advance and a design advance?

Apple, for example, is usually held up as an example of awesome design. But behind Apple's design is some pretty impressive technology. Why is this different than Maps or GMail or Websearch? Is it solely due to reputation, because Apple hides many of the technical details of its products behind this cloud of secrecy, while Google is openly proud of having advanced technology?


Actually, (and as a former Apple engineer) I'd say Apple is barely acceptable with most technology but absolutely fantastic leveraging that technology to produce amazing product designs.

What Apple technology in specific do you think is so advanced?


My impression (as someone not an Apple engineer) is that much of the iPhone hardware is pretty impressive, their visual skin layer is awesome and does some truly impressive computer graphics to get high performance on battery-constrained devices, and a lot of the MacOS underlying tech is pretty solid.

I'm really quite curious as to where you put the dividing line - my impression (as a Google engineer who works frequently in UI) is that there're a lot of really subtle design decisions in both Search and Maps that are nothing more than HTML/CSS, but really improve the usability of the product.


Every once in a while some company comes out with a really great solution to a problem. The most recent was with hipmunk and flights. When I used hipmunk for the first time, I had that feeling that after them, the whole landscape had changed.

I got the same feeling when I saw google maps - it was so far ahead of everyone, and not just technically but design-wise too. The same thing happened with mail. So on one hand you could say that their maps and mail are sort of standard, and that other companies have similar offerings.

But on the other hand, who set the standard in the first place?


Slippy maps are great. The UX was a great idea, but it was almost entirely dependent on having the technology and infrastructure to implement them, including asynchronous map tiling client side.

I don't think that is in conflict with my original statement, and the remainder of the Maps API is truly bad. I regularly make the mistake of using the scroll wheel, get stuck trying to figure out how to show (or hide) street view, etc.

Regarding GMail -- it's a poor approximation of an existing, very basic desktop experience. There's not a lot of design innovation there, if you look beyond webmail.


But Maps' UI is atrocious for anything beyond the most basic tasks.


Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: