Reverse Engineering Team Board

Reverse Engineering Team Board (
-   File Unpacking (
-   -   ACProtect - (

mMhCkB 12-09-2003 04:28 PM

ACProtect -
I'd like to start a new topic regarding this new packer. Looks quite advanced from what i've seen. It crashes OllyDbg before the file is completly loaded.

If you get any information, please post :)

sna 12-09-2003 06:05 PM

crashing OllyDbg
the fact that it is able to crash OllyDbg is most definately because of a parsing bug in OllyDbg itself.
remember that the size of IMAGE_NT_HEADERS32 is, contrary to popular belief, not static ;)

i'm looking forward to hearing more about what you find inside this protector's code.
maybe we could set up some sort of mini-project..?

cheers sna

Shub-Nigurrath 02-23-2004 01:12 PM

is there any news since this post on AcProtect??

Particularly I'm starting digging into a code protected with version 1.10, but as well as 1.21 might become used by the developers..

Is there any tutorial on this protector somewhere??


sna 02-26-2004 12:00 PM


i have been working on a paper that will document all of ACProtect.
work was done on version 1.10 but i suspect that little has changed since.
many a pages have already been written but i feel that the paper will
require a dozen, or so, more pages before i can publish it.

a couple of tools also had to be created to restore a protected file.

as for what others have written about ACProtect; it's the same old
"press F12 x times until you see the magic push eax" ...
with brute force approaches both to unpacking and rebuilding the target file,
as well as completely bogus information in some areas.

(btw, the problems mMhCkB was experiencing was between a specific
version of OllyDbg and one specific file.)

regards, sna

Shub-Nigurrath 03-01-2004 11:20 AM

whenever this paper will see the light I'll be outside waiting for a cigar for his dad ^__^

sna 04-24-2004 10:54 AM

ACProtect essay
I have, for a number of reasons, decided to discontinue this project. For one i havn't looked at any of this for months now. At this point i have no interest in finishing either the essay or tools i wrote to aid in the unpacking process. However, since a substantial amount of work has already been put into this project it would be a shame to completely discard what i came up with.

ACProtect is essentially an ASProtect rip-off with the same features, API and even help text (literally the same). Add to this, that it's a bad rip-off littered with both design and implementational bugs. The few strengths that it does have, data compression and code encryption using strong cryptography, can be added to projects by other much more professional means than using ACProtect.

The following are a couple of exerpts from my unfinished essay. They are from the first part of it, detailing mostly the anti-debugging techniques used. If you need additional information on the more gory parts of ACProtect, please ask questions. You might need to resize your browser's window to improve readability.


The virtual anatomy of ACProtect can be compared to that of an onion because it's layered in the same way. There's layer after layer, within containing the next after it, all the way until the centre of both objects.

There are roughly three different types of layers in ACProtect: the actively constructive layers, the anti-debugging layers and the layers that decrypt and transfer control between the two former types. From here on I'll be referring to the decrypting layers as \"bridging code\" because that's what they really are. Both the actively constructive layers and the anti-debugging layers are implemented as modules, with the bridging code there to help connect them. The modularity is especially noticeable with the anti-debugging modules since they are conditionally activated.

AD1: Uses an INT 1 instruction to throw an exception and detect SoftICE that way.


007649CE * MOV *AX, CS

007649D0 * TEST AL, 4 * * * * * * * * * * * * * // Windows NT ?

007649D2 * JNZ *ACProtec.00764A0C * * * * * * * // abort if not

007649D4 * NOP

007649D5 * NOP

007649D6 * NOP

007649D7 * NOP

007649D8 * CALL ACProtec.007649EB

007649DD * MOV *EBX, DWORD PTR SS:[ESP+C] * * * // SEH handler

007649E1 * ADD *DWORD PTR DS:[EBX+B8], 2 * * * *// EIP += 2

007649E8 * XOR *EAX, EAX

007649EA * RETN

007649EB * PUSH DWORD PTR FS:[0]

007649F1 * MOV *DWORD PTR FS:[0], ESP * * * * * // install handler

007649F7 * XOR *EAX, EAX

007649F9 * INT *1

007649FB * INC *EAX

007649FC * INC *EAX

007649FD * OR * EAX, EAX

007649FF * JNZ *ACProtec.00764A06

00764A01 * NOP

00764A02 * NOP

00764A03 * NOP

00764A04 * NOP

00764A05 * POPAD * * * * * * * * * * * * * * * *// unmatched

00764A06 * XOR *EAX, EAX

00764A08 * POP *DWORD PTR FS:[EAX]

00764A0B * POP *EAX

00764A0C * ...

Segment registers have different values in different versions of Windows. In this snippet the code segment value is TESTed to see if the code is currently executing on an NT-flavoured Windows version. The rest of the module is skipped if it is not. Right after this it installs its own handler in the SEH chain and potentially generates an exception (by executing the INT 1 instruction.) The new term SEH stands for Structured Exception Handling and requires an essay in itself. We'll keep it simple and establish that the code is only dangerous if it is not traced. If you have SoftICE loaded its internal INT 1 handler will change the instruction pointer before passing the exception to ACProtect. Without SoftICE the exception will be reported occurring at the INT 1 instruction but when you have SoftICE loaded the instruction pointer is incremented to indicate an exception after the INT 1 instruction.

The easiest way to skip this module is to force the Windows NT check to fail. That will jump over the offending code and we'll be on our way to the next module.


AD2: Uses SEH to clear hardware breakpoints.


00764C13 * CALL 00764C42

00764C18 * MOV *EAX, DWORD PTR SS:[ESP+4] * * * // SEH handler


00764C20 * INC *DWORD PTR DS:[ECX+B8] * * * * * // increment EIP


00764C28 * SUB *EAX, 80000003 * * * * * * * * * // exception code

00764C2D * JNZ *ACProtec.00764C41

00764C2F * NOP

00764C30 * NOP

00764C31 * NOP

00764C32 * NOP

00764C33 * XOR *EAX, EAX

00764C35 * MOV *DWORD PTR DS:[ECX+4], EAX * * * // DR0 = 0

00764C38 * MOV *DWORD PTR DS:[ECX+8], EAX * * * // DR1 = 0

00764C3B * MOV *DWORD PTR DS:[ECX+C], EAX * * * // DR2 = 0

00764C3E * MOV *DWORD PTR DS:[ECX+10], EAX * * *// DR3 = 0

00764C41 * RETN

00764C42 * XOR *EAX, EAX


00764C47 * MOV *DWORD PTR FS:[EAX], ESP * * * * // install handler

00764C4A * INT *3 * * * * * * * * * * * * * * * // throw exception

00764C4B * NOP

00764C4C * POP *DWORD PTR FS:[0] * * * * * * * *// uninstall handler

00764C52 * ADD *ESP, 4

00764C55 * ...

The INT3 throws a breakpoint exception when it's executed. If you have a debugger running it will catch the exception and pause on the INT3 instruction. A protection author would typically use this to their advantage because you know that a debugger is running if the exception is not passed to your own handler. However, this piece of code is worthless. Control would go straight to ACProtect's handler only if there was no debugger present, and then, what would be the point since all that the handler does is clear the debug registers? (It clears the debug address registers by updating the context structure that's being passed to it.)

The easiest way to skip this module is to not let it start execute at all. When you're at the call shown on the first line of the snippet, change the address of execution to the equivalence of 00764C55 shown here. Either way, just make sure you don't pass control to ACProtect's handler. If you find yourself on the IN3 instruction simply change the address of execution to point to the NOP right after it.


AD3: Detection of ring-3 debuggers by accessing PEB (Win2K) and TIB (Win9x).


00764E5C * MOV *AX, CS

00764E5E * TEST AL, 4 * * * * * * * * * * * * * // Windows NT ?

00764E60 * JNZ *00764E7C * * * * * * * * * * * *// jump if not

00764E62 * NOP

00764E63 * NOP

00764E64 * NOP

00764E65 * NOP

00764E66 * MOV *EAX, DWORD PTR FS:[30] * * * * *// pointer to PEB

00764E6B * MOVZX EAX, BYTE PTR DS:[EAX+2] * * * // BeingDebugged

00764E6F * OR * AL, AL

00764E71 * JNZ *00764E8E

00764E73 * NOP

00764E74 * NOP

00764E75 * NOP

00764E76 * NOP

00764E77 * JMP *00764EA1

00764E79 * NOP

00764E7A * NOP

00764E7B * NOP

00764E7C * MOV *EAX, DWORD PTR FS:[20] * * * * *// DebugContext

00764E81 * OR * EAX, EAX

00764E83 * JNZ *00764E8E

00764E85 * NOP

00764E86 * NOP

00764E87 * NOP

00764E88 * NOP

00764E89 * JMP *00764EA1

00764E8B * NOP

00764E8C * NOP

00764E8D * NOP

00764E8E * MOV *EDI, DWORD PTR SS:[EBP+41F16D]

00764E94 * ADD *EDI, DWORD PTR SS:[EBP+40CF7B]

00764E9A * MOV *ECX, 0A


00764EA1 * ...

We see two different techniques for detecting the presence of a ring-3 debugger. One is specific to Windows NT and the other is specific to Windows 9x, thereof the check and the branch at the beginning of the code. Trivia: the Windows NT technique is the same as used by the NT implementation of IsDebuggerPresent. All threads in the system have certain structures associated with them. These structures are used to keep track of the SEH list head, the TLS array pointer and debugging information among other things. The abbreviations PEB and TIB unfold to Process Environment Block and Thread Information Block, respectively. It's a rather big topic to cover so I'll leave it for you to research. The first 40 bytes of code (4 * 10) at the real entrypoint will be overwritten if the protection determines that you have a debugger active.

Our usual approach is still good. Skip it all together.


AD4: Verify that the parent process is one of the approved ones.

There's a lot more code in this module than in the other anti-debugging modules we've seen this far so I won't show it all. Instead, I'll try to explain what it does. *
First of all it makes sure that the CreateToolhelp32Snapshot API function was successfully resolved. The function is available in all Windows versions except versions of NT prior to Windows 2000 (version 5). If it was not resolved the module simply aborts execution. If, however, it was resolved, the module calls into bridging code to eventually connect with a sub-module. The sub-module is very careful to make sure that there are no breakpoints set on any of the functions it calls. We've already seen the code to do that. It's on page five if you need to refresh your memory.

The protected application retrieves its own process ID using GetCurrentProcessId and stores it away for a moment. It then uses the CreateToolhelp32Snapshot function and Process32First/Process32Next to cycle through all processes until it finds itself. Using the information it looked up, it is able to determine which process that started it. The same technique is then used to look up information about that process.

With the limited information comes the name of the file used to start the process. This would usually be \"Explorer.exe\" for the parent process. Unless, of course, you started the protected application through SoftICE's symbol loader (Loader32.exe) or using OllyDbg (OLLYDBG.EXE) or maybe using a completely different shell. The code around here is buggy and just generally stupid so I won't comment a whole lot on it. Basically, what it does is, it tries to hash the filename string and compare the resulting value to a table of pre-calculated hashes of \"approved\" parent names. As you can well imagine this is not a very smart thing to be doing to your customers. My choice of shell should not be dictated by anyone, obviously.

The proprietary algorithm used is this: (ESI points to the filename. Sometimes.)






The approved hashes are:

0xDB02D9C3, 0xBAD9D2D2

The first one is \"EXPLORER.EXE\" (filename is uppercased before it's hashed). We may only speculate in what the rest of them are. I can think of a number of popular desktop replacements but it doesn't really matter. At one moment I was tempted to try brute-forcing the rest of the filenames but the algorithm doesn't exactly yield a unique hash for any given string. Rough tests show that about 0,0000015 percent of possible likely filenames will match one of the values.

The easiest way to bypass this module is to fool the first check we saw, the one that makes sure the CreateToolhelp32Snapshot API function is available. *

It's important to emphasise that none of these modules are very well integrated with the surrounding code. Once you see what patterns they follow and what rules they play by you'll also see the obvious ways to cheat them. Remember that the tail of any given module looks practically the same as that of the next one. You can determine a pattern at the byte-level that will lead you to other modules' tails. This knowledge will prove itself valuable when we examine the more advanced features of ACProtect.


AD5: Search the system for \"enemy processes\".

This module maintains a list of enemy processes that it doesn't want to find running in the system. It proceeds to generate a page fault if it finds any of these processes. The idea is pretty smart as the list is actually a merge of two lists. It contains both the default names of popular tools' executables and their main class names.

The same old check for CreateToolhelp32Snapshot can be seen at the start. A sub-module is called upon if the function is found to be present. Code in the sub-module performs an enumeration of processes in the system, and for all of them, the name of the file that started it is compared to each of the entries in the blacklist:


EXESPY, * *WXR95, * * * * *REGMON,











The list has been split up and re-arranged to give you a better overview. That last item seen is my own custom \"enemy process\" that I specified in one of the ACProtect tabs before I built the project. You might be interested in knowing that the filename is uppercased before the comparison. This immediately invalidates the \"OllyDbg\" entry in the blacklist! It also doesn't do a full string compare. No, it scans the filename from the left. There's only a minor difference but I thought I'd tell you.

You're brought back to the super-module if all of your processes pass the test. Here's where the trick comes into play. The same list is checked against captions of top-level windows. It uses EnumWindows to find all the windows and GetWindowTextA with GetClassNameA to obtain information about them. As always, be careful about where you put your breakpoints.


AD6: Uses Get/SetThreadContext to clear hardware breakpoints.

You will see an attempt at updating the main thread's context to clear the debug address registers. It uses GetCurrentThread to obtain a pseudo-handle to the calling thread (the only thread in this case). It then uses GetThreadContext to store away its context where it can access and change it, after which it uses SetThreadContext to reload the thread with the updated context. Without revealing too much I think it's safe to say that the implementation is lacking something. We're off the hook this time as nothing whatsoever is changed in the context.


AD7: Detection of SoftICE by looking at changes to UnhandledExceptionFilter API.

If you have SoftICE loaded she'll have placed an INT 3 instruction at the start of UnhandledExceptionFilter. She does this in order to get first chance on all exceptions. This module looks at the first byte there and compares it to the INT 3 opcode. If it determines that you have SoftICE loaded it first installs an exception handler and generates an exception to enter into it. Once inside it continues to generate exceptions to make it harder for you to restore normal execution. Just skip past all of it.


AD8: Calls IsDebuggerPresent to catch your ring-3 debugger.

Yes, the name says it all. If it determines that you have a debugger present it starts scanning the code at the original entrypoint for calls and long jumps. When it finds one, it overwrites the target address and continues execution at the end of the module:



007650C3 * CMP *AL, 0E8h * * * * * * * * * * * * // opcode CALL

007650C5 * JE * ACProtec.007650CF

007650C7 * NOP

007650C8 * NOP

007650C9 * NOP

007650CA * NOP

007650CB * CMP *AL, 0E9h * * * * * * * * * * * * // opcode JMP

007650CD * JNZ *ACProtec.007650C2


007650D1 * ...


AD9: Tries to find known tool drivers loaded in the system.

This is an extended version of the MeltICE trick. The trick was originally used to detect SoftICE by the presence of her active device drivers. But why stop at that? Here's the full list of driver names it looks for:


SICE, * * * * * NTICE, * * * * *NTICE7871,

NTICED052, * * *TRWDEBUG, * * * TRW,

TRW2000, * * * *SUPERBPM, * * * ICEDUMP,

REGMON, * * * * FILEMON, * * * *REGVXD,

FILEVXD, * * * *VKEYPROD, * * * BW2K,


That about wraps our anti-debugging tour up.

Anti-dumping? Import address table re-directing? ACProtect's API? Any questions you may have, just ask.

Regards, sna

CoDe_InSiDe 04-24-2004 01:44 PM

Hi sna,

Nice explanations of the Anti-Debug stuff :)

The most funny thing about ACProtect is that you can simply skip "everything" ;)
Those Anti-Debug routines will be called multiple times in the, I believe you called them, Bridging Codes, just place a RET at the beginning of the Anti routines and it will be skipped forever ;) (It will never be checked and there's also no Checksum done from the loader...)

Anyway, I just wanted to say, nice explanations :)

sna 04-25-2004 11:17 AM

Hi CoDe_InSiDe,

I'm glad you like it. I figured if nothing else it's a good reference to commonly encountered anti-debugging techniques.
You know... it is pretty funny to see how utterly worthless the integration between modules is.

IIRC ACProtect itself uses a technique similar to the one you suggested. I think i had that mentioned in the introductory part of my essay:



__declspec(naked) ulong RDTSC_Randomise(void) {

 *__asm {

 * *PUSH * EDX


 * *RCL * *EAX, 2

 * *ADD * *EAX, 12345678h * * * * * * * * * *// randomiser

 * *ADC * *EAX, ESP

 * *XOR * *EAX, ECX

 * *XOR * *DWORD PTR SS:[EBP+40DED6], EAX * *// patch self


 * *RCL * *EAX, 1

 * *POP * *EDX



Basically, what it does is to create a random value based on time. RDTSC is used to access a time-stamp counter (ReaD Time-Stamp Counter). The instruction is typically used with lax code profiling and is available beginning with the Pentium processor. The internal randomiser is patched so that the next time the code is executed it will be using another randomiser. The intention seems to be to create \"more unique\" values.


... And as I previously also mentioned the anti-debugging modules are conditionally activated. The core of it is the RDTSC_Randomise function that we saw earlier. From inside these randomly activated modules it's a call to ShouldNotActivateModule:


__declspec(naked) bool ShouldNotActivateModule(void) {

 *__asm {

 * *MOV * *EAX, 0Ah

 * *CALL * RandomiseWrapper



ulong __fastcall RandomiseWrapper(ulong divisor) {

 *__asm {

 * *PUSH * EDX

 * *PUSH * ECX

 * *MOV * *EDX, 0

 * *PUSH * EAX

 * *CALL * RDTSC_Randomise

 * *POP * *ECX

 * *DIV * *ECX

 * *XCHG * EAX, EDX * * * * * * * * * * // EAX = random value % 10

 * *POP * *ECX

 * *POP * *EDX



The condition to activate a module is that the random value generated be evenly divisible by 10. Execution of the module is aborted if it is not. The modules are called a number of times in the hope that they will eventually activate themselves. When that happens the module patches a RETN instruction somewhere in the path that lead up to it, to return immediately on sub-sequent calls and thereby \"shutting itself down\".

It should be noted that the RDTSC_Randomise function is also used for other things than module activation.

I would love to get a discussion going about the import table and anti-dumping trick.
Regards, sna

seven 02-15-2005 10:59 PM

ollydbg +lordpe + importrec

1- load proggy ( ollydbg )

2- run proggy ( nagscreen )

3- mem bp access

4- ( click nagscreen OK ) = OEP

5- load OEP ( importrec ) IAT search

Getimport Tracelevel Cut thunks Fixdump .

thiz work if u checked all protect optionz exept OEP obfuscation .

by the way y can,t we upload filez ?!

sna 02-16-2005 08:25 AM


you obviously missed the whole point of this thread. Feel free to be all "hey i can click a few buttons and cheat the protection" if you like, but take your business elsewhere in that case. The primary reason for this thread was to discuss the internal workings of ACProtect, document them, and find ways around the protections. Alas, the general lack of interest from board visitors put the thread to sleep (and only the kiss of a noble prince...)

Anyway. Ever wondered what 'cut thunks' actually does? Did the application you protected by any chance use MessageBoxA? Did it also use the ACProtect API? Didn't think so. So what you're really saying is "hey i can cheat the protection in a controlled environment, and the reason it works is because i didn't enable any advanced ACProtect features".

Also, i woulda thought the reason we don't want 'filez' here was obvious to anyone that had taken the time to register and write a handfull of posts. Guess i was wrong. If i ever needed any kind of 'filez' i could get them from someone that's not you.

Regards, sna

All times are GMT -4. The time now is 07:30 AM.

Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2022, Jelsoft Enterprises Ltd.