Traditionally, the way of doing this is by making that 'can know' step very difficult. Techniques to accomplish that include refusing to run under a debugger, multiple layers of protection, self-modifying code, loading the digest code from disk block checksums, from between tracks on a floppy disk or from blocks marked bad (back in the day when there weren't that many layers between application code and hardware), etc.
A lock does not have to be unbreakable; it just has to make breaking it costly enough to discourage even attempting breaking it.
With hackers, though, that does not quite work. They see even attempting to break the lock as enough of a reward in itself.