Is there any solution?
For there to be a complete, perfect solution, you cannot be in a situation where the licensee has full control over the whole runtime environment; for, when this happens, by definition the program can be made to run in any number of copies one desires.
Control must be taken away from the licensee, which means there need to be something they do not control. So, you can have a solution if you change your premises to include this:
- obviously they cannot control an external license server (but you want to work offline)
- it was believed (wrongly) that they could not control some specific characteristics or features of the hardware (disk serial numbers, MAC addresses...). Virtual environments put paid to that notion (but maybe you can do something there?)
- they cannot control an embedded license server - that is, a smart key/dongle (this is another option. Keep in mind that this goes against ease of use, and often leads to a loss of customer base - would you rather have 50% licensing on 10000 customers, or 100% on one thousand?).
- they cannot control the flow of time and the software's inputs both.
Any other setup will fail in a very simple way like this:
- I install the software on a computer. That computer is actually a virtual machine residing on a server I control. This, by the way, is where the market is going: few, redundated physical machines with VMs that share storage and CPU resources. Requiring a software to run in an "unvirtualized environment" might soon become similar to requiring the OS to be 32 bit.
- I make however many separated, isolated copies of that VM as I want. Each of them "believes" to be the original one and "sees" itself in a computer that is indistinguishable from the original, because it is.
- I now have however many copies of the software as I want.
Some protection schemes try to leverage blockchain and a periodic update system to prevent the software from being "reverted" to a previous state. This way, the software can ensure that it has been running more than a specific set time or that it has processed more than a given quantity of information; when this happens, the software requires an "update" to continue running, and the "update" embeds a random public key. As a result, the update released for instance #1 of the software will not work to update any other instances, that, lacking the update, will legitimately refuse to run (and you cannot require ten updates for the two licenses you have). Thwarting this scheme is sometimes possible but typically requires a premeditated, expensive hacking effort, and therefore is usually never done; which makes this approach perhaps suitable for your purposes.
For example, a software that examines invoice information for tax optimization purposes will refuse to examine invoices dated farther in the future than 180 days from the last update. To force it to work, it would need to be fed invoices whose dates are artificially set back, then the optimization data would need to be reprocessed to bring the dates back to normal, at the risk of introducing calculation errors that could result in huge fines instead of the expected deductions. So, cracking the program is not feasible. In this case, the thing the licensee cannot (afford to) control is the program's input.
However, the larger part of the "license" battle is fought and won on licensing and software flexibility and affordability. You might... let's say "not discourage" copying, if that allows you to get new customers (this is common for those softwares that require significant customizations to reap the full benefits). Knowing you're not going to gouge the customers for additional licenses will go a long way against plans to replace your software altogether in a few months' time. Also, much depends on the customers themselves: a large firm has a strong incentive towards running a tight ship and only using fully licensed software, just in case they might get audited. The savings from pirate copies might simply not be worth it.