保护可执行的逆向工程?
我一直在考虑如何保护我的C / C ++代码免受反汇编和逆向工程的影响。 通常我不会在我的代码中自己宽恕这种行为; 然而,为了各种人的安全,我一直在研究的当前协议不能被检查或理解。
现在这对我来说是一个新的课题,互联网对于防止逆向工程来说并不是真正的资源,而是描绘了大量关于如何进行逆向工程的信息
我到目前为止想到的一些事情是:
- 代码注入(在实际函数调用之前和之后调用虚函数)
- 代码混淆(篡改二进制的反汇编)
-
写我自己的启动程序(难以绑定到debugging器)
void startup(); int _start() { startup( ); exit (0) } void startup() { /* code here */ }
-
运行时检查debugging器(如果检测到则强制退出)
-
function蹦床
void trampoline(void (*fnptr)(), bool ping = false) { if(ping) fnptr(); else trampoline(fnptr, true); }
-
毫无意义的分配和释放(堆栈更改很多)
- 毫无意义的虚拟呼叫和蹦床(拆卸输出中跳跃的吨数)
- 铸造吨(混淆拆卸)
我的意思是这些是我想到的一些东西,但是它们都可以被解决,或者在正确的时间框架内被代码分析师弄清楚。 还有别的select吗?
琥珀说的是完全正确的。 你可以使逆向工程更难,但你永远无法阻止它。 你永远不要相信依靠防止逆向工程的“安全” 。
也就是说,我所见过的最好的反向逆向工程技术不是关注混淆代码,而是关注人们通常用来了解代码如何工作的工具。 寻找创造性的方式来打破反汇编,debugging器等,可能会更有效,更智慧的满足,而不仅仅是生成可怕的意大利面条代码。 这并没有阻止一个坚定的攻击者,但它确实增加了J随机cookies漫步的可能性,而是更容易地工作。
但他们都可以在代码分析师给出合适的时间框架内解决问题。
如果你给人们一个他们能够运行的程序,那么他们也可以在给定的时间内对其进行逆向工程。 这是scheme的性质。 一旦二进制文件被解密者使用,你就无法阻止最终的逆向工程。 毕竟,计算机必须能够解密才能运行,而人类只是一台速度较慢的计算机。
安全networking哨兵 (以前阿拉丁)。 注意,尽pipe – 他们的API很烂,文档很烂,而且这两个都比他们的SDK工具好。
我已经使用了他们的硬件保护方法( Sentinel HASP HL )多年。 它需要专用的USB密钥卡,作为软件的“许可证”。 他们的SDKencryption和模糊您的可执行文件和库,并允许您将应用程序中的不同function绑定到密钥中。 没有由许可人提供和激活的USB密钥,软件不能解密,因此将不能运行。 Key甚至使用定制的USB通信协议(在我的知识领域之外,我不是一个设备驱动程序人员)难以构build虚拟密钥或篡改运行时包装器和密钥之间的通信。 他们的SDK不是非常开发人员友好的,并且将添加保护与自动化构build过程(但是可能的)相集成是相当痛苦的。
在我们实施HASP HL保护之前,已知有7名盗版者从产品中剥离了“混淆器”的“保护”。 我们在对软件进行重大更新的同时,增加了HASP保护,该软件对video进行了实时计算。 从分析和基准testing中可以看出,HASP HL保护只会使密集计算速度减慢大约3%。 自从5年前发布该软件以来,没有发现一个新的海盗产品。 它所保护的软件在市场上的需求量很大,客户意识到有几个竞争对手正在积极尝试进行逆向工程(目前为止没有成功)。 我们知道,他们试图向俄罗斯的less数几个团体寻求帮助,这些团体宣称有一项打破软件保护的服务,因为各个新闻组和论坛上的许多post都包含了新版本的受保护产品。
最近我们在一个较小的项目上尝试了他们的软件许可证解决scheme(HASP SL),如果您已经熟悉HL产品,那么这个scheme足够简单。 它似乎工作; 目前还没有盗版事件报道,但是这个产品的需求量要低得多。
当然,没有保护是完美的。 如果有人有充分的动机,并有严重的现金烧,我敢肯定,HASP提供的保护可以规避。
以AESalgorithm为例。 这是一个非常非常公开的algorithm,它非常安全。 为什么? 原因有二:一个聪明的人被审查过,“秘密”部分不是algorithm本身 – 秘密部分是algorithminput的关键之一。 这是一个更好的方法来devise你的协议与生成的“秘密”是在你的代码之外,而不是使代码本身的秘密。 无论你做什么,代码都可以被解释,而且(理想情况下)所产生的秘密只能被大规模的暴力破解或盗窃所破坏。
我认为一个有趣的问题是“ 为什么要混淆你的代码?” 你想让攻击者很难破解你的algorithm吗? 为了让他们更难在代码中find可利用的错误? 如果代码首先是不可破解的,则不需要对代码进行混淆。 问题的根源是可破解的软件。 修复你的问题的根源,不要混淆它。
而且,你使代码越困惑,你越难find安全漏洞。 是的,这对黑客来说很难,但是你也需要find一些错误。 代码应该很容易维护几年,即使写得很好的明码也很难维护。 不要变得更糟。
最好的反编译器技巧,特别是在可变字长指令集上是汇编/机器代码,而不是C.例如
CLC BCC over .byte 0x09 over:
反汇编器必须解决多字节指令中分支目的地是第二个字节的问题。 指令集模拟器将不会有任何问题。 计算出来的地址可以从C中分支出来,这也使得反汇编变得很困难。 指令集模拟器将没有问题。 使用模拟器为您分拣分支目标可以帮助反汇编过程。 编译的代码相对干净,容易反汇编。 所以我认为一些组装是必需的。
我认为这是迈克尔·Abrash的汇编语言禅的开始,他展示了一个简单的反编译和反debugging技巧。 8088/6有一个预取队列,你所做的是有一条指令修改下一条指令或前面的一对。 如果单步执行修改后的指令,如果你的指令集模拟器没有完全模拟硬件,则执行修改后的指令。 在正常运行的实际硬件上,真正的指令已经在队列中,只要你没有再次执行那条指令,修改后的内存位置就不会造成任何损坏。 当stream水线处理器读取下一条指令时,您今天仍然可以使用这样的技巧。 或者,如果您知道硬件具有单独的指令和数据高速caching,则可以先修改大量字节,如果将这些代码正确alignment高速caching行,则修改后的字节将不会通过指令高速caching而是通过数据高速caching写入没有合适的caching模拟器的指令集模拟器将无法正确执行。 我认为软件解决scheme不会让你走得太远。
以上是老的和众所周知的,我不知道目前的工具,知道他们是否已经解决这样的事情。 自修改代码可以/将跳过debugging器,但是人可以/将在问题上缩小,然后看到自修改代码并解决它。
过去,黑客们大概需要18个月左右的时间才能解决问题。 现在他们平均2天到2周左右(如果有动力的话)(蓝光,手机等)。 这对我来说意味着如果我花了几天的安全时间,我很可能会浪费我的时间。 您将获得的唯一真正的安全性是通过硬件(例如,您的指令是encryption的,只有在芯片内部的处理器核心在执行之前才解密,以不能暴露解密的指令的方式)。 这可能会花费你几个月而不是几天。
另外,阅读凯文米特尼克的“欺骗的艺术”一书。 这样的人可以拿起电话,让你或同事把系统的秘密传递给系统,认为它是公司其他部门的经理或另一个同事或硬件工程师。 而你的安全被吹 安全不是关于pipe理技术,还得pipe理人类。
使代码难以反向工程被称为代码混淆。
你提到的大多数技术都相当容易解决。 他们集中在添加一些无用的代码。 但无用的代码很容易检测和删除,留给你一个干净的程序。
为了有效的混淆,您需要使程序的行为依赖于正在执行的无用位。 例如,而不是这样做:
a = useless_computation(); a = 42;
做这个:
a = complicated_computation_that_uses_many_inputs_but_always_returns_42();
或者,而不是这样做:
if (running_under_a_debugger()) abort(); a = 42;
这样做(其中running_under_a_debugger
不应该容易地被识别为testing代码是否在debugging器下运行的函数 – 它应该将有用的计算与debugging器检测混合在一起):
a = 42 - running_under_a_debugger();
有效的混淆不是纯粹在编译阶段可以完成的。 无论编译器能做什么,反编译器都可以做到。 当然,你可以增加反编译器的负担,但这不会太远。 有效的混淆技术,因为它们存在,涉及从第1天开始编写混淆的源代码。使您的代码自我修改。 用大量input产生的计算跳转消耗代码。 例如,而不是简单的调用
some_function();
做到这一点,你碰巧知道some_data_structure
中的位准确的预期布局:
goto (md5sum(&some_data_structure, 42) & 0xffffffff) + MAGIC_CONSTANT;
如果您对模糊不清,请在计划中加上几个月的时间; 混淆并不便宜。 并且认为到目前为止避免人们对你的代码进行反向工程的最好方法就是使它无用,这样他们就不用担心了。 这是一个简单的经济考虑:如果对他们的价值大于成本,他们将进行逆向工程; 但提高成本也会提高你的成本,所以试着降低价值。
现在我已经告诉你混淆是困难和昂贵的,我要告诉你它不适合你 。 你写
目前的协议我一直在努力不能被检查或理解,为了各种人的安全
这引起了一面红旗。 这是安全的,默默无闻 ,logging很差。 如果协议的安全性取决于不了解协议的人, 那么你已经失去了 。
推荐阅读:
- 安全圣经:罗斯·安德森的安全工程
- 混淆圣经:基督教柯尔伯格和Jasvir Nagra 暗中软件
很多时候,对产品进行逆向工程的恐惧是错误的。 是的,它可以得到逆向工程; 但是它会在短时间内变得如此出名,黑客们会觉得值得反转。 它呢? (这项工作不是一个小的时间活动,对于大量的代码行)。
如果真的成为赚钱的人,那么你应该收集足够的钱来保护它,使用像专利和/或版权的法律方式。
恕我直言,采取基本的预防措施,你要采取和释放它。 如果它成为逆向工程的一个意思,就意味着你做得很好,你自己会find更好的方法来克服它。 祝你好运。
阅读http://en.wikipedia.org/wiki/Security_by_obscurity#Arguments_against 。 我相信其他人也可能会提供一个更好的来源,为什么安全的隐瞒是一件坏事。
使用现代密码技术完全有可能让你的系统开放(我不是说它应该是开放的,只是它可能是开放的),并且仍然具有完全的安全性,只要密码algorithm不有一个洞(不太可能,如果你select一个好的),你的私钥/密码保持私密,你的代码没有安全漏洞( 这是你应该担心的)。
自2013年7月以来,对Amit Sahai的原始研究似乎已经激发了对密码强大的混淆(以不可分辨混淆的forms)的兴趣。
- Sahai,Garg,Gentry,Halevi,Raykova,沃特世, 所有电路的候选不可区分性混淆和functionencryption (2013年7月21日)。
- Sahai,Waters, 如何使用不可区分性混淆:可否认encryption等 。
- Sahai,Barak,Garg,Kalai,Paneth, 保护混淆代数攻击 (2014年2月4日)。
您可以在这篇Quanta杂志文章和IEEE Spectrum文章中find一些蒸馏的信息。
目前使用这种技术所需要的资源量是不切实际的,但AFAICT的共识对未来是相当乐观的。
我非常随便地说,但是对于那些习惯于本能地消除混淆技术的人来说 – 这是不同的。 如果它被certificate是真正的工作和实际,这确实是重要的,而不仅仅是混淆。
如果有人想花时间扭转你的二进制,那么你绝对没有办法阻止他们。 你可以做出中等难度,但这就是它。 如果你真的想了解这一点,然后得到一份http://www.hex-rays.com/idapro/和反汇编一些二进制文件。;
CPU需要执行代码的事实是你的失败。 CPU只执行机器代码…程序员可以读取机器代码。
这就是说…你可能有一个不同的问题,可以用另一种方式解决。 你想保护什么? 根据您的问题,您可能会使用encryption来保护您的产品。
要通知您自己,请阅读有关代码混淆的学术文献。 亚利桑那大学的克里斯蒂安•柯尔伯格(Christian Collberg)是这个领域的着名学者, 哈佛大学的Salil Vadhan也做了一些很好的工作。
我在这个文献中已经落后了,但是我知道的基本思想是,你不能阻止攻击者看到你将要执行的代码,但是你可以用未被执行的代码包围它,攻击者指数时间(使用最有名的技术)来发现你的代码片段被执行,哪些不是。
最近有一篇名为“ 程序混淆和一次性程序 ”的文章。 如果你真的想保护你的应用程序。 一般来说,本文通过使用简单和通用的硬件来解决理论上的不可能性结果。
如果你不能负担得起额外的硬件,那么还有另外一篇论文给出了理论上最好的可能的混淆,即在所有具有相同function和相同大小的程序中, 尽可能混淆 。 然而,本文表明信息论最好的可能意味着多项式层次的崩溃。
如果这些结果不能满足您的需求,那么这些论文至less应该给您提供足够的参考书目。
更新:混淆的新概念,称为难以区分的混淆,可以减轻不可能的结果(纸)
没有骰子,你不能保护你的代码不被拆卸。 你可以做的是设置业务逻辑的服务器,并使用web服务为您的应用程序提供它。 当然,这种情况并不总是可能的。
起初,虚拟机中受保护的代码似乎不可能进行反向工程。 Themida打包机
但是不再那么安全了。不pipe你如何打包你的代码,你总是可以执行任何加载的可执行文件的内存转储,并且用像IDA Pro这样的反汇编程序来反汇编它。
IDA Pro还附带了一个漂亮的汇编代码到C源代码转换器,虽然生成的代码将看起来更像是一个指针/地址math混乱..如果你与原来的比较,你可以修复所有的错误和撕裂任何东西。
可能你最好的select仍然是使用虚拟化,它引入了被绕过所需要的另一个级别的间接/混淆,但是SSpoke在他的回答中说,这种技术也不是100%安全的。
关键是你不会得到最终的保护,因为没有这样的事情,如果将来会不会持久,这意味着它不是最终的保护。
无论男人assembly什么,都可以拆卸。
通常情况下,(适当的)分解往往是(更多)更难的任务,所以你的对手必须更加熟练 ,但是你可以假定总是有人拥有这样的素质,这是一个安全的select。
如果你想保护一些抵抗RE的东西,你必须至less知道RE使用的常用技术。
这样的话
互联网对于防止逆向工程来说并不是真正的足够资源,而是描绘了大量关于如何进行逆向工程的信息
performance出你的坏态度。 我不是说要使用或embedded保护,你必须知道如何打破它,但明智地使用它,你应该知道它的弱点和陷阱。 你应该明白这一点。
(有些软件是以错误的方式使用保护的,这样的保护实际上是不存在的,为了避免模糊地说,我给你在网上简单介绍一个例子:牛津英语词典第二版,光盘版v4。在以下页面中使用SecuROM失败: 16位,32位或64位Windows环境下的光盘上的牛津英语字典(OED):硬盘安装,错误,文字处理macros,networking,字体和等等 )
一切都需要时间。
如果您对这个主题感兴趣,并且没有几个月或几年时间才能正确使用可再生能源,那么请使用其他人提供的解决scheme。 这里的问题是显而易见的,他们已经在那里,所以你已经知道他们不是100%安全的,但是让自己的新保护只会给你一个被保护的错觉,除非你真的知道逆向工程和保护(但是你至less在这个时候不行)。
软件保护的重点是吓唬新手,阻止常见的RE,并且在她/他的(希望有趣的)旅程到达你的应用程序的中心之后,对经验丰富的RE表情微笑。
在商业谈话中,你可能会说,尽可能地推迟竞争。
(在Black Hat 2006上,Philippe Biondi和Fabrice Desclaux 在Skype上演示了Silver Needle 。
你知道关于RE的东西有很多,所以开始阅读。 🙂
我说过关于虚拟化,所以我会给你一个EXETOOLS FORUM的示例主题的链接: 最好的软件保护:Themida或Enigma Protector? 。 它可能会帮助你进一步search。
我不认为任何代码都是不可靠的,但是对于想要尝试的人来说,奖励是非常重要的。
说了一些你应该做的事情,比如:
- 尽可能使用最高级别的优化级别(反向工程不仅是获取汇编序列,还是了解代码并将其移植到C等更高级的语言中)。 高度优化的代码可以作为一个b – h遵循。
- 由于没有比必要的更大的数据types而使结构更encryption集。 在官方代码版本之间重新排列结构成员。 在结构中重新排列的位域也是可以使用的。
- 您可以检查是否存在某些不应该更改的值(版权信息是一个示例)。 如果一个字节向量包含“vwxyz”,则可以有另一个包含“abcde”的字节向量并比较差异。 执行它的函数不应该传递指向向量的指针,而是使用其他模块中定义的外部指针作为(伪C代码)“char * p1 =&string1 [539];” 和“char p2 =&string2 [-11731];”。 这样就不会有任何指针指向两个string。 在比较代码中,比较“ (p1-539 + i) – *(p2 + 11731 + i)==某个值”。 黑客会认为改变string1是安全的,因为没有人会引用它。 将testing埋在一些意想不到的地方。
尝试自己破解汇编代码,看看什么是简单的,什么是困难的。 Ideas should pop up that you can experiment with to make the code more difficult to reverse engineer and to make debugging it more difficult.
As many already said: On a regular CPU you cant stop them from doing, you can just delay them. As my old crypto teacher told me: You dont need perfect encryption, breaking the code must be just more expensive than the gain. Same holds for your obfuscation.
But 3 additional notes:
-
It is possible to make reverse engineering impossible, BUT (and this is a very very big but), you cant do it on a conventional cpu. I did also much hardware development, and often FPGA are used. Eg the Virtex 5 FX have a PowerPC CPU on them, and you can use the APU to implement own CPU opcodes in your hardware. You could use this facility to really decrypt incstuctions for the PowerPC, that is not accessible by the outside or other software, or even execute the command in the hardware. As the FPGA has builtin AES encryption for its configuration bitstream, you could not reverse engineer it (except someone manages to break AES, but then I guess we have other problems…). This ways vendors of hardware IP also protect their work.
-
You speak from protocol. You dont say what kind of protocol it is, but when it is a network protocol you should at least protect it against network sniffing. This can you indeed do by encryption. But if you want to protect the en/decryption from an owner of the software, you are back to the obfuscation.
-
Do make your programm undebuggable/unrunnable. Try to use some kind of detection of debugging and apply it eg in some formula oder adding a debug register content to a magic constant. It is much harder if your program looks in debug mode is if it where running normal, but makes a complete wrong computation, operation, or some other. Eg I know some eco games, that had a really nasty copy-protection (I know you dont want copyprotection, but it is similar): The stolen version altered the mined resources after 30 mins of game play, and suddenly you got just a single resource. The pirate just cracked it (ie reverse engineered it) – checked if it run, and volia released it. Such slight behaviour changings are very hard to detect, esp. if they do not appear instantly to detection, but only delayed.
So finally I would suggest: Estimate what is the gain of the people reverse engineering your software, translate this into some time (eg by using the cheapest indian salary) and make the reverse engineering so time costing that it is bigger.
To be able to select the right option, You should think of the following aspects:
- Is it likely that "new users" do not want to pay but use Your software?
- Is it likely that existing customers need more licences than they have?
- How much are potential users willing to pay?
- Do You want to give licences per user / concurrent users / workstation / company?
- Does Your software need training / customization to be useful?
If the answer to question 5 is "yes", then do not worry about illegal copies. They wouldn't be useful anyway.
If the answer to question 1 is "yes", then first think about pricing (see question 3).
If You answer questions 2 "yes", then a "pay per use" model might be appropriate for You.
From my experience, pay per use + customization and training is the best protection for Your software, because:
- New users are attracted by the pricing model (little use -> little pay)
- There are almost no "anonymous users", because they need training and customization.
- No software restrictions scares potential customers away.
- There is a continuous stream of money from existing customers.
- You get valuable feedback for development from Your customers, because of a long-term business relationship.
Before You think of introducing DRM or obfuscation, You might think of these points and if they are applicable to Your software.
To avoid reverse engineering, you must not give the code to users. That said, I recommend using an online application…however (since you gave no context) that could be pointless on yours.
Contrary to what most people say, based on their intuition and personal experience, I don't think cryptographically-safe program obfuscation is proven to be impossible in general.
This is one example of a perfectly obfuscated program statement to demonstrate my point:
printf("1677741794\n");
One can never guess that what it really does is
printf("%d\n", 0xBAADF00D ^ 0xDEADBEEF);
There is an interesting paper on this subject, which proves some impossibility results. It is called "On the (Im)possibility of Obfuscating Programs" .
Although the paper does prove that the obfuscation making the program non-distinguishable from the function it implements is impossible, obfuscation defined in some weaker way may still be possible!
Security through obscurity doesn't work as has been demonstrated by people much cleverer than the both of us. If you must protect the communication protocol of your customers then you are morally obliged to use the best code that is in the open and fully scrutinized by experts.
This is for the situation where people can inspect the code. If your application is to run on an embedded microprocessor, you can choose one that has a sealing facility, which makes it impossible to inspect the code or observe more than trivial parameters like current usage while it runs. (It is, except by hardware invading techniques, where you carefully dismantle the chip and use advanced equipment to inspect currents on individual transistors.)
I'm the author of a reverse engineering assembler for the x86. If you're ready for a cold surprise, send me the result of your best efforts. (Contact me through my websites.) Few I have seen in the answers would present a substantial hurdle to me. If you want to see how sophisticated reverse engineering code works, you should really study websites with reverse engineering challenges.
Your question could use some clarification. How do you expect to keep a protocol secret if the computer code is amenable to reverse engineering? If my protocol would be to send an RSA encrypted message (even public key) what do you gain by keeping the protocol secret? For all practical purposes an inspector would be confronted with a sequence of random bits.
Groetjes Albert
Traditional reverse engineering techniques depend on the ability of a smart agent using a disassembler to answer questions about the code. If you want strong safety, you have do to things that provably prevent the agent from getting such answers.
You can do that by relying on the Halting Program ("does program X halt?") which in general cannot be solved. Adding programs that are difficult to reason about to your program, makes your program difficult to reason about. It is easier to construct such programs than to tear them apart. You can also add code to program that has varying degrees of difficulty for reasoning; a great candidate is the program of reasoning about aliases ("pointers").
Collberg et al have a paper ("Manufacturing Cheap, Resilient and Stealthy Opaque Constructs") that discusses these topics and defines a variety of "opaque" predicates that can make it very difficult to reason about code:
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.39.1946&rep=rep1&type=pdf
I have not seen Collberg's specific methods applied to production code, especially not C or C++ source code.
The DashO Java obfuscator seems to use similar ideas. http://www.cs.arizona.edu/~collberg/Teaching/620/2008/Assignments/tools/DashO/
FIRST THING TO REMEMBER ABOUT HIDING YOUR CODE : Not all of your code needs to be hidden.
THE END GOAL : My end goal for most software programs is the ability to sell different licenses that will turn on and off specific features within my programs.
BEST TECHNIQUE : I find that building in a system of hooks and filters like WordPress offers, is the absolute best method when trying to confuse your opponents. This allows you to encrypt certain trigger associations without actually encrypting the code.
The reason that you do this, is because you'll want to encrypt the least amount of code possible.
KNOW YOUR CRACKERS : Know this: The main reason for cracking code is not because of malicious distribution of licensing, it's actually because NEED to change your code and they don't really NEED to distribute free copies.
GETTING STARTED : Set aside the small amount of code that you're going to encrypt, the rest of the code should try and be crammed into ONE file to increase complexity and understanding.
PREPARING TO ENCRYPT : You're going to be encrypting in layers with my system, it's also going to be a very complex procedure so build another program that will be responsible for the encryption process.
STEP ONE : Obfuscate using base64 names for everything. Once done, base64 the obfuscated code and save it into a temporary file that will later be used to decrypt and run this code. 合理?
I'll repeat since you'll be doing this again and again. You're going to create a base64 string and save it into another file as a variable that will be decrypted and rendered.
STEP TWO : You're going to read in this temporary file as a string and obfuscate it, then base64 it and save it into a second temp file that will be used to decrypt and render it for the end user.
STEP THREE : Repeat step two as many times as you would like. Once you have this working properly without decrypt errors, then you're going to want to start building in land mines for your opponents.
LAND MINE ONE : You're going to want to keep the fact that you're being notified an absolute secret. So build in a cracker attempt security warning mail system for layer 2. This will be fired letting you know the specifics about your opponent if anything is to go wrong.
LAND MINE TWO : Dependencies. You don't want your opponent to be able to run layer one, without layer 3 or 4 or 5, or even the actual program it was designed for. So make sure that within layer one you include some sort of kill script that will activate if the program isn't present, or the other layers.
I'm sure you can come up with you're own landmines, have fun with it.
THING TO REMEMBER : You can actually encrypt your code instead of base64'ing it. That way a simple base64 willnt decrypt the program.
REWARD : Keep in mind that this can actually be a symbiotic relationship between you and you'r opponent. I always place a comment inside of layer one, the comment congratulates the cracker and gives them a promo code to use in order to receive a cash reward from you.
Make the cash reward significant with no prejudice involved. I normally say something like $500. If your guy is the first to crack the code, then pay him his money and become his friend. If he's a friend of yours he's not going to distribute your software. Ask him how he did it and how you can improve!
GOOD LUCK!
Have anyone tried CodeMorth: http://www.sourceformat.com/code-obfuscator.htm ? Or Themida: http://www.oreans.com/themida_features.php ?
Later one looks more promissing.