Skip to main content

I would adviceadvise you to also turn off any debugging information, even if it doesn't leak any majorly important information today it might do so tomorrow. The amount of information leakage from the debug information varies from compiler to compiler and from binary format to binary format. For instance, Microsoft Visual C++ keeps all important debugging information in an external database, usually in the form of a PDB. The most you might leak is the path you used when building your software.

When it comes to strings you should definitely encrypt them if you need them at all. I would aim to replace all the ones that are for error tracing and error logging with numeric enumerations. Strings whichthat reveal any sort of information about what is going on right now in your binary needsneed to be unavailable. If you encrypt the strings, they will be decrypted. Try to avoid them as much as possible.

Another strong source of information leakage is imports of system APIs. You want to make sure that any imported function which has a known signature is well-hidden and can not be found using automatic analysis. So an array of function pointers from something like LoadLibrary/GetProcAddress is out of the question. All calls to imported functions need to go through a one way-way function and needsneed to be embedded within an obfuscated block.

You should also cover any critical code with some sort of heavy obfuscation. Some of the heavier and cheaper obfuscations right now are CodeVirtualizer/Themida and VMProtect. Be aware that these packages have an abundance of defects though. They will sometimes transform your code to something which will not be the equivalent of the original which can lead to instability. They also slow down the obfuscated code significantly. A factor of 10000 times slower is not uncommon. There's also the issue of triggering more false positives with anti-virus software. I would adviceadvise you to sign your software using a reputable certificate authority.

The separation of code into functions is another thing whichthat makes it easier to reverse engineer-engineer a program. This applies especially when the functions are obfuscated because it creates boundaries around which the reverse engineer can reason about your software. This way the reverse engineer can solve your program in a divide and conquer-and-conquer manner. Ideally, you would want your software in one effective block with obfuscation applied uniformly to the entire block as one. SoTo reduce the number of blocks, use inlining very generously and wrap them in a good obfuscation algorithm. The compiler can easily do some heavy optimizations and stack ordering which will make the block harder to reverse engineer.

If you're designing a copy protection system prepare for it to be broken mentally. Make sure you have a plan for how you will deal with the break and how to make sure the next version of your software adds enough value to drive upgrades. Build your system on a solid ground which can not be broken, do not resort to generating your own license keys using some custom algorithm hidden in the manner I described above. The system needs to be built on a sound cryptographic foundation for the unforgeability of messages.

I would advice you to also turn off any debugging information, even if it doesn't leak any majorly important information today it might do so tomorrow. The amount of information leakage from the debug information varies from compiler to compiler and from binary format to binary format. For instance Microsoft Visual C++ keeps all important debugging information in an external database, usually in the form of a PDB. The most you might leak is the path you used when building your software.

When it comes to strings you should definitely encrypt them if you need them at all. I would aim to replace all the ones that are for error tracing and error logging with numeric enumerations. Strings which reveal any sort of information about what is going on right now in your binary needs to be unavailable. If you encrypt the strings, they will be decrypted. Try to avoid them as much as possible.

Another strong source of information leakage is imports of system APIs. You want to make sure that any imported function which has a known signature is well-hidden and can not be found using automatic analysis. So an array of function pointers from something like LoadLibrary/GetProcAddress is out of the question. All calls to imported functions need to go through a one way function and needs to be embedded within an obfuscated block.

You should also cover any critical code with some sort of heavy obfuscation. Some of the heavier and cheaper obfuscations right now are CodeVirtualizer/Themida and VMProtect. Be aware that these packages have an abundance of defects though. They will sometimes transform your code to something which will not be the equivalent of the original which can lead to instability. They also slow down the obfuscated code significantly. A factor of 10000 times slower is not uncommon. There's also the issue of triggering more false positives with anti-virus software. I would advice you to sign your software using a reputable certificate authority.

The separation of code into functions is another thing which makes it easier to reverse engineer a program. This applies especially when the functions are obfuscated because it creates boundaries around which the reverse engineer can reason about your software. This way the reverse engineer can solve your program in a divide and conquer manner. Ideally you would want your software in one effective block with obfuscation applied uniformly to the entire block as one. So reduce the number of blocks, use inlining very generously and wrap them in a good obfuscation algorithm. The compiler can easily do some heavy optimizations and stack ordering which will make the block harder to reverse engineer.

If you're designing a copy protection system prepare for it to be broken mentally. Make sure you have a plan for how you will deal with the break and how to make sure the next version of your software adds enough value to drive upgrades. Build your system on a solid ground which can not be broken, do not resort to generating your own license keys using some custom algorithm hidden in the manner I described above. The system needs to be built on a sound cryptographic foundation for unforgeability of messages.

I would advise you to also turn off any debugging information, even if it doesn't leak any majorly important information today it might do so tomorrow. The amount of information leakage from the debug information varies from compiler to compiler and from binary format to binary format. For instance, Microsoft Visual C++ keeps all important debugging information in an external database, usually in the form of a PDB. The most you might leak is the path you used when building your software.

When it comes to strings you should definitely encrypt them if you need them at all. I would aim to replace all the ones that are for error tracing and error logging with numeric enumerations. Strings that reveal any sort of information about what is going on right now in your binary need to be unavailable. If you encrypt the strings, they will be decrypted. Try to avoid them as much as possible.

Another strong source of information leakage is imports of system APIs. You want to make sure that any imported function which has a known signature is well-hidden and can not be found using automatic analysis. So an array of function pointers from something like LoadLibrary/GetProcAddress is out of the question. All calls to imported functions need to go through a one-way function and need to be embedded within an obfuscated block.

You should also cover any critical code with some sort of heavy obfuscation. Some of the heavier and cheaper obfuscations right now are CodeVirtualizer/Themida and VMProtect. Be aware that these packages have an abundance of defects though. They will sometimes transform your code to something which will not be the equivalent of the original which can lead to instability. They also slow down the obfuscated code significantly. A factor of 10000 times slower is not uncommon. There's also the issue of triggering more false positives with anti-virus software. I would advise you to sign your software using a reputable certificate authority.

The separation of code into functions is another thing that makes it easier to reverse-engineer a program. This applies especially when the functions are obfuscated because it creates boundaries around which the reverse engineer can reason about your software. This way the reverse engineer can solve your program in a divide-and-conquer manner. Ideally, you would want your software in one effective block with obfuscation applied uniformly to the entire block as one. To reduce the number of blocks, use inlining very generously and wrap them in a good obfuscation algorithm. The compiler can easily do some heavy optimizations and stack ordering which will make the block harder to reverse engineer.

If you're designing a copy protection system prepare for it to be broken mentally. Make sure you have a plan for how you will deal with the break and how to make sure the next version of your software adds enough value to drive upgrades. Build your system on solid ground which can not be broken, do not resort to generating your own license keys using some custom algorithm hidden in the manner I described above. The system needs to be built on a sound cryptographic foundation for the unforgeability of messages.

added 1403 characters in body
Source Link
Peter Andersson
  • 5.7k
  • 1
  • 32
  • 49

Runtime

When you hide information it is important that the information is well hidden at runtime as well. A competent reverse engineer will examine the state of your program as it is running. So using static variables that decrypt when loaded or by using packing which is completely unpacked upon loading will lead to a quick find. Be careful about what you allocate on the heap. All heap operations go via API calls and can be easily logged to a file and reasoned about. Stack operations are generally harder to keep track of just because of how frequent they are. Dynamic analysis is just as important as static. You need to be aware of what your program state is at all times and what information lies where.

Anti-debugging

Anti-debugging is worthless. Do not spend time on it. Spend time on making sure your secrets are well hidden independent of whether your software is at rest or not.

Packing and encrypting code segment

I will group encryption and packing into the same category. They both serve the same purpose and they both have the same issues. In order to execute the code, the CPU needs to see the plain text. So you have to provide the key in the binary. The only remotely effective way of encrypting and packing code segments is if you encrypt and decrypt them at functional boundaries and only if the decryption happens upon function entry and then re-encryption happens when leaving the function. This will provide a small barrier against dumping your binary as it is running but is must be coupled with strong obfuscation.

Finally

Finally

Runtime

When you hide information it is important that the information is well hidden at runtime as well. A competent reverse engineer will examine the state of your program as it is running. So using static variables that decrypt when loaded or by using packing which is completely unpacked upon loading will lead to a quick find. Be careful about what you allocate on the heap. All heap operations go via API calls and can be easily logged to a file and reasoned about. Stack operations are generally harder to keep track of just because of how frequent they are. Dynamic analysis is just as important as static. You need to be aware of what your program state is at all times and what information lies where.

Anti-debugging

Anti-debugging is worthless. Do not spend time on it. Spend time on making sure your secrets are well hidden independent of whether your software is at rest or not.

Packing and encrypting code segment

I will group encryption and packing into the same category. They both serve the same purpose and they both have the same issues. In order to execute the code, the CPU needs to see the plain text. So you have to provide the key in the binary. The only remotely effective way of encrypting and packing code segments is if you encrypt and decrypt them at functional boundaries and only if the decryption happens upon function entry and then re-encryption happens when leaving the function. This will provide a small barrier against dumping your binary as it is running but is must be coupled with strong obfuscation.

Finally

added 1403 characters in body
Source Link
Peter Andersson
  • 5.7k
  • 1
  • 32
  • 49

Another strong source of information leakage is imports of system APIs. You want to make sure that any imported function which has a known signature is well-hidden and can not be found using automatic analysis. So an array of function pointers from something like LoadLibrary/GetProcAddress is out of the question. All calls to imported functions need to go through a one way function and needs to be embedded within an obfuscated block.

You should also cover any critical code with some sort of heavy obfuscation. Some of the heavier and cheaper obfuscations right now are CodeVirtualizer/Themida and VMProtect. Be aware that these packages have an abundance of defects though. They will sometimes transform your code to something which will not be the equivalent of the original which can lead to instability. They also slow down the obfuscated code significantly. A factor of 10000 times slower is not uncommon. There's also the issue of triggering more false positives with anti-virus software. I would advice you to sign your software using a reputable certificate authority.

Separation of functional blocks

The separation of code into functions is another thing which makes it easier to reverse engineer a program. This applies especially when the functions are obfuscated because it creates boundaries around which the reverse engineer can reason about your software. This way the reverse engineer can solve your program in a divide and conquer manner. Ideally you would want your software in one effective block with obfuscation applied uniformly to the entire block as one. So reduce the number of blocks, use inlining very generously and wrap them in a good obfuscation algorithm. The compiler can easily do some heavy optimizations and stack ordering which will make the block harder to reverse engineer.

Study your software in something like the free version of IDA. Your goal is to make sure that it becomes virtually impossible for the reverse engineer to find a steady mental footing. The less information you leak and the more changing the environment is, the harder it will be to study. If you're not an experienced reverse engineer, designing something hard to reverse engineer is almost impossible.

If you're designing a copy protection system prepare for it to be broken mentally. Make sure you have a plan for how you will deal with the break and how to make sure the next version of your software adds enough value to drive upgrades. Build your system on a solid ground which can not be broken, do not resort to generating your own license keys using some custom algorithm hidden in the manner I described above. The system needs to be built on a sound cryptographic foundation for unforgeability of messages.

Another strong source of information leakage is imports of system APIs. You want to make sure that any imported function which has a known signature is well-hidden and can not be found using automatic analysis. So an array of function pointers from something like LoadLibrary/GetProcAddress is out of the question. All calls to imported functions need to go through a one way function and needs to be obfuscated.

You should also cover any critical code with some sort of heavy obfuscation. Some of the heavier and cheaper obfuscations right now are CodeVirtualizer/Themida and VMProtect. Be aware that these packages have an abundance of defects though. They will sometimes transform your code to something which will not be the equivalent of the original which can lead to instability. They also slow down the obfuscated code significantly. A factor of 10000 times slower is not uncommon. There's also the issue of triggering more false positives with anti-virus software. I would advice you to sign your software using a reputable certificate authority.

Study your software in something like the free version of IDA. Your goal is to make sure that it becomes virtually impossible for the reverse engineer to find a steady mental footing. The less information you leak and the more changing the environment is, the harder it will be to study.

Another strong source of information leakage is imports of system APIs. You want to make sure that any imported function which has a known signature is well-hidden and can not be found using automatic analysis. So an array of function pointers from something like LoadLibrary/GetProcAddress is out of the question. All calls to imported functions need to go through a one way function and needs to be embedded within an obfuscated block.

You should also cover any critical code with some sort of heavy obfuscation. Some of the heavier and cheaper obfuscations right now are CodeVirtualizer/Themida and VMProtect. Be aware that these packages have an abundance of defects though. They will sometimes transform your code to something which will not be the equivalent of the original which can lead to instability. They also slow down the obfuscated code significantly. A factor of 10000 times slower is not uncommon. There's also the issue of triggering more false positives with anti-virus software. I would advice you to sign your software using a reputable certificate authority.

Separation of functional blocks

The separation of code into functions is another thing which makes it easier to reverse engineer a program. This applies especially when the functions are obfuscated because it creates boundaries around which the reverse engineer can reason about your software. This way the reverse engineer can solve your program in a divide and conquer manner. Ideally you would want your software in one effective block with obfuscation applied uniformly to the entire block as one. So reduce the number of blocks, use inlining very generously and wrap them in a good obfuscation algorithm. The compiler can easily do some heavy optimizations and stack ordering which will make the block harder to reverse engineer.

Study your software in something like the free version of IDA. Your goal is to make sure that it becomes virtually impossible for the reverse engineer to find a steady mental footing. The less information you leak and the more changing the environment is, the harder it will be to study. If you're not an experienced reverse engineer, designing something hard to reverse engineer is almost impossible.

If you're designing a copy protection system prepare for it to be broken mentally. Make sure you have a plan for how you will deal with the break and how to make sure the next version of your software adds enough value to drive upgrades. Build your system on a solid ground which can not be broken, do not resort to generating your own license keys using some custom algorithm hidden in the manner I described above. The system needs to be built on a sound cryptographic foundation for unforgeability of messages.

Source Link
Peter Andersson
  • 5.7k
  • 1
  • 32
  • 49
Loading