yck1509 / ConfuserEx

An open-source, free protector for .NET applications
http://yck1509.github.io/ConfuserEx/
Other
3.57k stars 1.64k forks source link

Anti Dump does not work and can be easily dumped. #649

Open rollsch opened 6 years ago

rollsch commented 6 years ago

Anti dump does not work and can be easily dumped by MegaDumper. I also tried adding the anti dump code manually to my project so I could confirm it was definitely being executed.

Any ideas?

Baker68 commented 6 years ago

@rolandh A workaround is to start it from another thread ; wait 1-2 seconds and then call anti-dump method. The code works (on my .net 4.5 projects),using the above method. I`m not a guru of programing but I think the code is called to early of something...

rollsch commented 6 years ago

Then the dumping process can simply start my process and pause it immediately to defeat it.

Baker68 commented 6 years ago

Ogh ure so right;; Heres my 2nd option on this one

`using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks;

namespace B.antidump { internal class AntiDump { [DllImport("kernel32.dll")] static extern unsafe bool VirtualProtect(byte lpAddress, int dwSize, uint flNewProtect, out uint lpflOldProtect); public static int hop; AntiDump() { Initialize(); } public static unsafe void Initialize() { uint old; Module module = typeof(AntiDump).Module; var bas = (byte)Marshal.GetHINSTANCE(module); byte ptr = bas + 0x3c; byte ptr2; ptr = ptr2 = bas + (uint)ptr; ptr += 0x6; ushort sectNum = (ushort)ptr; ptr += 14; ushort optSize = (ushort)ptr; ptr = ptr2 = ptr + 0x4 + optSize;

        byte* @new = stackalloc byte[11];
        if (module.FullyQualifiedName[0] != '<') 
        {
            byte* mdDir = bas + *(uint*)(ptr - 16);
            if (*(uint*)(ptr - 0x78) != 0)
            {
                byte* importDir = bas + *(uint*)(ptr - 0x78);
                byte* oftMod = bas + *(uint*)importDir;
                byte* modName = bas + *(uint*)(importDir + 12);
                byte* funcName = bas + *(uint*)oftMod + 2;
                VirtualProtect(modName, 11, 0x40, out old);

                *(uint*)@new = 0x6c64746e;
                *((uint*)@new + 1) = 0x6c642e6c;
                *((ushort*)@new + 4) = 0x006c;
                *(@new + 10) = 0;

                for (int i = 0; i < 11; i++)
                    *(modName + i) = *(@new + i);

                VirtualProtect(funcName, 11, 0x40, out old);

                *(uint*)@new = 0x6f43744e;
                *((uint*)@new + 1) = 0x6e69746e;
                *((ushort*)@new + 4) = 0x6575;
                *(@new + 10) = 0;

                for (int i = 0; i < 11; i++)
                    *(funcName + i) = *(@new + i);
            }

            for (int i = 0; i < sectNum; i++)
            {
                VirtualProtect(ptr, 8, 0x40, out old);
                Marshal.Copy(new byte[8], 0, (IntPtr)ptr, 8);
                ptr += 0x28;
            }
            VirtualProtect(mdDir, 0x48, 0x40, out old);
            byte* mdHdr = bas + *(uint*)(mdDir + 8);
            *(uint*)mdDir = 0;
            *((uint*)mdDir + 1) = 0;
            *((uint*)mdDir + 2) = 0;
            *((uint*)mdDir + 3) = 0;

            VirtualProtect(mdHdr, 4, 0x40, out old);
            *(uint*)mdHdr = 0;
            mdHdr += 12;
            mdHdr += *(uint*)mdHdr;
            mdHdr = (byte*)(((ulong)mdHdr + 7) & ~3UL);
            mdHdr += 2;
            ushort numOfStream = *mdHdr;
            mdHdr += 2;
            for (int i = 0; i < numOfStream; i++)
            {
                VirtualProtect(mdHdr, 8, 0x40, out old);
                mdHdr += 4;
                mdHdr += 4;
                for (int ii = 0; ii < 8; ii++)
                {
                    VirtualProtect(mdHdr, 4, 0x40, out old);
                    *mdHdr = 0;
                    mdHdr++;
                    if (*mdHdr == 0)
                    {
                        mdHdr += 3;
                        break;
                    }
                    *mdHdr = 0;
                    mdHdr++;
                    if (*mdHdr == 0)
                    {
                        mdHdr += 2;
                        break;
                    }
                    *mdHdr = 0;
                    mdHdr++;
                    if (*mdHdr == 0)
                    {
                        mdHdr += 1;
                        break;
                    }
                    *mdHdr = 0;
                    mdHdr++;
                }
            }
        }
        else 
        {
            uint mdDir = *(uint*)(ptr - 16);
            uint importDir = *(uint*)(ptr - 0x78);

            var vAdrs = new uint[sectNum];
            var vSizes = new uint[sectNum];
            var rAdrs = new uint[sectNum];
            for (int i = 0; i < sectNum; i++)
            {
                VirtualProtect(ptr, 8, 0x40, out old);
                Marshal.Copy(new byte[8], 0, (IntPtr)ptr, 8);
                vAdrs[i] = *(uint*)(ptr + 12);
                vSizes[i] = *(uint*)(ptr + 8);
                rAdrs[i] = *(uint*)(ptr + 20);
                ptr += 0x28;
            }

            if (importDir != 0)
            {
                for (int i = 0; i < sectNum; i++)
                    if (vAdrs[i] <= importDir && importDir < vAdrs[i] + vSizes[i])
                    {
                        importDir = importDir - vAdrs[i] + rAdrs[i];
                        break;
                    }
                byte* importDirPtr = bas + importDir;
                uint oftMod = *(uint*)importDirPtr;
                for (int i = 0; i < sectNum; i++)
                    if (vAdrs[i] <= oftMod && oftMod < vAdrs[i] + vSizes[i])
                    {
                        oftMod = oftMod - vAdrs[i] + rAdrs[i];
                        break;
                    }
                byte* oftModPtr = bas + oftMod;
                uint modName = *(uint*)(importDirPtr + 12);
                for (int i = 0; i < sectNum; i++)
                    if (vAdrs[i] <= modName && modName < vAdrs[i] + vSizes[i])
                    {
                        modName = modName - vAdrs[i] + rAdrs[i];
                        break;
                    }
                uint funcName = *(uint*)oftModPtr + 2;
                for (int i = 0; i < sectNum; i++)
                    if (vAdrs[i] <= funcName && funcName < vAdrs[i] + vSizes[i])
                    {
                        funcName = funcName - vAdrs[i] + rAdrs[i];
                        break;
                    }
                VirtualProtect(bas + modName, 11, 0x40, out old);

                *(uint*)@new = 0x6c64746e;
                *((uint*)@new + 1) = 0x6c642e6c;
                *((ushort*)@new + 4) = 0x006c;
                *(@new + 10) = 0;

                for (int i = 0; i < 11; i++)
                    *(bas + modName + i) = *(@new + i);

                VirtualProtect(bas + funcName, 11, 0x40, out old);

                *(uint*)@new = 0x6f43744e;
                *((uint*)@new + 1) = 0x6e69746e;
                *((ushort*)@new + 4) = 0x6575;
                *(@new + 10) = 0;

                for (int i = 0; i < 11; i++)
                    *(bas + funcName + i) = *(@new + i);
            }

            for (int i = 0; i < sectNum; i++)
                if (vAdrs[i] <= mdDir && mdDir < vAdrs[i] + vSizes[i])
                {
                    mdDir = mdDir - vAdrs[i] + rAdrs[i];
                    break;
                }
            byte* mdDirPtr = bas + mdDir;
            VirtualProtect(mdDirPtr, 0x48, 0x40, out old);
            uint mdHdr = *(uint*)(mdDirPtr + 8);
            for (int i = 0; i < sectNum; i++)
                if (vAdrs[i] <= mdHdr && mdHdr < vAdrs[i] + vSizes[i])
                {
                    mdHdr = mdHdr - vAdrs[i] + rAdrs[i];
                    break;
                }
            *(uint*)mdDirPtr = 0;
            *((uint*)mdDirPtr + 1) = 0;
            *((uint*)mdDirPtr + 2) = 0;
            *((uint*)mdDirPtr + 3) = 0;

            byte* mdHdrPtr = bas + mdHdr;
            VirtualProtect(mdHdrPtr, 4, 0x40, out old);
            *(uint*)mdHdrPtr = 0;
            mdHdrPtr += 12;
            mdHdrPtr += *(uint*)mdHdrPtr;
            mdHdrPtr = (byte*)(((ulong)mdHdrPtr + 7) & ~3UL);
            mdHdrPtr += 2;
            ushort numOfStream = *mdHdrPtr;
            mdHdrPtr += 2;
            for (int i = 0; i < numOfStream; i++)
            {
                VirtualProtect(mdHdrPtr, 8, 0x40, out old);
                mdHdrPtr += 4;
                mdHdrPtr += 4;
                for (int ii = 0; ii < 8; ii++)
                {
                    VirtualProtect(mdHdrPtr, 4, 0x40, out old);
                    *mdHdrPtr = 0;
                    mdHdrPtr++;
                    if (*mdHdrPtr == 0)
                    {
                        mdHdrPtr += 3;
                        break;
                    }
                    *mdHdrPtr = 0;
                    mdHdrPtr++;
                    if (*mdHdrPtr == 0)
                    {
                        mdHdrPtr += 2;
                        break;
                    }
                    *mdHdrPtr = 0;
                    mdHdrPtr++;
                    if (*mdHdrPtr == 0)
                    {
                        mdHdrPtr += 1;
                        break;
                    }
                    *mdHdrPtr = 0;
                    mdHdrPtr++;
                }
            }
        }
    }
}

} `

Call hop from main() ??

rollsch commented 6 years ago

Calling the code works fine, problem is it simply doesn't prevent dumping.

Baker68 commented 6 years ago

@rolandh I managed to get it working by : -changing the watermark name (i dont think its important this) -changing the the NameService.cs string ObfuscateNameInternal(byte[] hash, RenameMode mode) ; case case RenameMode.Unicode to return a random string based on the hash.Length I also modifier the .ctor ( foreach (ModuleDefMD module in context.Modules)) under ConfuserEngine.cs like TypeRef attrRef = module.CorLibTypes.GetTypeRef("System", "Attribute"); ... var attrType = new TypeDefUser(Confuser.Core.RandomName.ctorName, Confuser.Core.RandomName.watermarkName, attrRef); random ctorName & the random watermark and attr.ConstructorArguments.Add(new CAArgument(module.CorLibTypes.String, Confuser.Core.RandomName.getShortString())); also under ConfuserEngine.cs

This actualy works

rollsch commented 6 years ago

So if you run megadumper on your application it fails to dump? You can simply rebuild the import table using universalfixer anyway so this seems like a useless anti dumper.

https://github.com/CodeCracker-Tools/MegaDumper http://www.connect-trojan.net/2011/09/universal-fixer-1.0-by-codecracker-snd.html

XenocodeRCE commented 6 years ago

Based on your approach and because of the DetectAntiDump.cs class in MegaDumper the only way to prevent it from working is by using WinAPI to grant your executable file some privilege at runtime.

RtlAdjustPrivilege, NtSetInformationThread from ntdll.dll. It's unsafe code, so beware . . .

Also on a side note

this seems like a useless anti dumper.

makes you sound like a plain d*ck, Yck worked hard into this project and I'm sure by the time he was actively working on it it was working. This AntiDump protection is using some very cool tricks to modify the file at runtime, it's not useless at all.

rollsch commented 6 years ago

MegaDumper must grant those privileges as it can dump my test executable.

Anyway I have written my own anti dumper in the mean time. By writing a signed kernel driver you can block openprocess calls by using obcallbacks and white listing system PIDs.

I uses this as an example and added block openprocess instead of blocking terminate process. Then I pass a white list to it via my loader and it prevents any application from reading the memory. This is the same way battle eye anti cheat etc work.

The disadvantage here is you require a code signing cert to make the driver install and run however you need a code signing cert to ensure no false positives when using a packer/obfuscator as well so I imagine most people already have one.

https://github.com/Microsoft/Windows-driver-samples/tree/master/general/obcallback

XenocodeRCE commented 6 years ago

@rolandh Setting SeDebugPrivilege did not help ? Thatis supposed to do the work

I'm sorry I don't know how to hook such thing and I think many of us don't neither.

According to this thread on Tuts4You it has to do with some WinAPI hooking :

https://i.imgur.com/JV00c5x.png

I looked at the driver source code and I don't know where to start even though I'm sure it's simple.

Either you need a signed driver certificate ($500) either you can't follow obcallback...

I looked at the driver source code and I don't know where to start even though I'm sure it's simple.

Either you need a signed driver certificate ($500) either you can't follow obcallback...

rollsch commented 6 years ago

Hooking all exes on the system is a dangerous thing to do, it is not something that you could do in a professional application, only viruses would exhibit such behavior.

I wrote a kernel driver using the obcallback method and signed it with a code signing cert and cross signed it with their ms cert. It only cost $180 AUD for the cert however you need to own a company to get the cert in the first place. Eg you need your company to be listed on a site like DnB or you won't get the cert.

obcallback can then block and whitelist certain processes by passing in a PID whitelist at runtime (example doesn't do anything like this, you need to code it yourself), it is fairly difficult to do but it appears this is how anti cheat programs like battle eye etc work. You NEED a kernel driver to do this properly, anything form userspace will be a hack at best and always be able to be bypassed by another user process. If it can't be bypassed by another user process then Virus makers will use it to bypass being scanned in which case MS will patch this in no time.

I imagine you could write a professional packer and test it using the above kernel driver in about 1-2 weeks if you had the time.

The method performed by ConfuserEx is extremely easy to bypass. Just launch the exe and instantly suspend the process before the anti dumper code has a chance to run. Megadumper even has this functionality built in.

XenocodeRCE commented 6 years ago

Your solution is not a valuable answer to that extent it discriminate more than 80% of ConfuserEX users (we are poor, and we don't own a company listed on the BnD website). I sincerely thank you for the details you provided however, I'm sure one will solve it's issue wit them.

That being said, a solution is still to be found 😞

You can store the antidump method in an external module you can call in memory, and this module would be executed only after antitamper. This way dnSpy cannot debug the module ran in memory which contains the antidump code, and you cannot remove it because it is protected by the antitamper method. ?

rollsch commented 6 years ago

Like I said if you run the program and immediately suspend the process, eg the second the first MSIL instruction is executed (eg before the anti tamper/dump code is run) you can then dump the program.

Once the program is dumped anti-tamper etc will only delay someone, you can remove the anti tamper code and you are good to go.

There is no solution unless you start using 0 days based on the fact that if the code runs on your machine, you can always modify it. Even if you use a kernel driver it doesn't stop you running windbg, pausing the windows kernel and dumping the memory that way.

The only way to truley achieve anti-dump is to run the program remotely on a web server.

harunkocacaliskan commented 6 years ago

@rolandh

Does kernel mode obcallback method really protects from megadumper, dnSpy or it's very easy to bypass? If they change process name will it still work?

rollsch commented 6 years ago

Let me know once you write your own driver.