Jump to content
Tuts 4 You

Unpublicized Anti-Debugging - WindowByte Anti-Debug: Compile Help Needed


Recommended Posts

Sangavi

This is anti-debug code for detecting ollydbg 32-bit version by @cpudbg. Got it from a Chinese forum.

typedef LONG (WINAPI* WINDOWSBYTE)(int, int);
bool CheckDebug()
{
    DWORD dwRet = 0;
    char szModuleName[15] = {0x75, 0x73, 0x65, 0x72, 0x33, 0x32, 0x2E, 0x64, 0x6C, 0x6C}; // user32.dll
    PWCHAR lpModuleName = NULL;
    PWCHAR lpModuleNameTemp = NULL;
    PCHAR lpSrc = szModuleName;
    DWORD dwAPI = 0;
    DWORD dwFunName = 0;
    DWORD dwBase = 0;
    
    _asm
    {
        xor ecx, ecx
        mov esi, fs: [0x30]
        mov esi, [esi + 0x0c]
        mov esi, [esi + 0x1c]
next_module:
        
        mov eax, [esi+0x8]
        cmp eax, 0
        je end
        mov edi,[esi+0x20]
        mov esi ,[esi]

        mov dwBase, eax
        mov lpModuleName, edi
        mov lpModuleNameTemp, edi
    }
    
    lpSrc = szModuleName;
    while(*lpModuleNameTemp != 0 && *lpSrc != 0)
    {
        if(*lpModuleNameTemp >= 'A' && *lpModuleNameTemp <= 'Z')
        {
            if(*lpModuleNameTemp != *lpSrc-0x20)
            {
                _asm
                {
                    jmp next_module
                }
            }
            else
            {
                lpSrc++;
                lpModuleNameTemp++;
            }
        }
        else if(*lpModuleNameTemp != *lpSrc)
        {
            _asm
            {
                jmp next_module
            }
        }
        else
        {
            lpSrc++;
            lpModuleNameTemp++;
        }
    }

end:

    
    PIMAGE_DOS_HEADER pDosHeader;
    PIMAGE_NT_HEADERS pNtHeader;
    PIMAGE_EXPORT_DIRECTORY pExportDirectory;
    
    char szFunName[15] = {0x47, 0x65, 0x74, 0x57, 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x4C, 0x6F, 0x6E, 0x67, 0x41, 0x00}; // GetWindowLongA
    
    
    pDosHeader = (PIMAGE_DOS_HEADER)dwBase;
    pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)dwBase + pDosHeader->e_lfanew);
    pExportDirectory = PIMAGE_EXPORT_DIRECTORY(pNtHeader->OptionalHeader.DataDirectory[0].VirtualAddress + (PBYTE)dwBase);
    
    PDWORD pAddressName = PDWORD((PBYTE)dwBase+pExportDirectory->AddressOfNames);
    PWORD pAddressOfNameOrdinals = (PWORD)((PBYTE)dwBase+pExportDirectory->AddressOfNameOrdinals);
    PDWORD pAddresOfFunction = (PDWORD)((PBYTE)dwBase+pExportDirectory->AddressOfFunctions);
    
    for(DWORD i = 0; i < pExportDirectory->NumberOfNames; i++)
    {
        PCHAR pFunc = (PCHAR)((PBYTE)dwBase + *pAddressName++);
        
        // printf("%s\r\n", pFunc);
        
        if (0 == strcmp(pFunc, szFunName))
        {
            dwAPI = (DWORD)dwBase + pAddresOfFunction[*pAddressOfNameOrdinals];
            break;
        }
        
        pAddressOfNameOrdinals++;
    }

    WINDOWSBYTE WindowsByte = (WINDOWSBYTE)dwAPI;

    for(int j = 0; j < 0xffffff; j++)
    {
        dwRet = WindowsByte(j, 0);
        
        // 1.x OD
        if(0x4cd6a8 == dwRet ||
            0x4cda75 == dwRet ||
            0x4cde42 == dwRet)
        {
            return TRUE;
        }
        
        // 2.x OD
        if(0x5e63c4 == dwRet ||
            0x5e6ab8 == dwRet ||
            0x5e6d24 == dwRet ||
            0x5e6fa8 == dwRet ||
            0x5e76b4 == dwRet)
        {
            return TRUE;
        }
    }

    return false;
}



char szCheckDebug[] =
"u\"Unpublicized anti-debug by cpudbg 冭貾YIIIIIIIIIIIIIIII7QZjAXP0A0AkAAQ2AB2BB0BB"
"ABXP8ABuJI3eLKZLOsZL1hscRvaGkwBezlGpS05Pwpo6g5jL3Ekv3uHmpszfg5ZNREKv75zORRZfqU8pUc"
"IVReya7BIVqUkBVNjfaUhs2Do6reKD2L9VG5huPlvSKpniReXvMXW5kJo7SuYtUPePwpS0YWRekXs0s0uP"
"EPLMPM8lMYrmMxKwreoPS0GpuPwpjgaUoLWpWpuPePo7SuyPgpUPWpuPUcKyrDlKtuDpc0GpUPLK1fVlNk"
"0v5LnkQVwxNcXxs06olDOUwpWpwpLK1nUpNkvVlICu9PoyQmItoySMYxLMRuzLNi65lhNkPExh6SKy0flK"
"Vhouky4OotOqUPuPC0lKPU98VoMnC2nekpD4QglKpMM8ecKbE6LKtQMSzZg1alvZLKsuKXucjiavnk4HNc"
"l9PZ3O4mlK3eyxuckpBFlKfblKPMoXdOMnDQMSJJ7P7KYRadwtHkOeJKUBlKW5LhMSkpwqk9qUMxNkPMKX"
"ncKqgrnibmKXZKfKLKqEKXecKpQvlKS2lKRmoX6oONtQekO244uWM9Qxyo9o9oZKvrLKreY8mSIPGqK9Cu"
"OXNkpMJHosKquRmY0MYxYy2OIokOyokvpEN4CwIVQUzupeJfpEZvsDKv75XW67KvsuzxQyjfW5Jy0nkvQU"
"KjsTKvaUKkbOhFSuYLBW8FpEyMpLHFSuyN2OhFCuKo2No6BekP0g8FQUyqQQHFre8b7pnkru9PoyBuN0LK"
"QUN0nkRmkp4CqXUlmYPMx4lKceMdlK2ekpS3g2d8k9w5OTNkpMMtlKPUKp6cpQQ0k9buNLNkSuy4lKrmiP"
"C3bhVDk9rmlpNkSei4NkqUKpfcqRELk9QUZdKwPEXHUPS05PUPjKuYnk0MKxmSzaUQMYPMZhlK65mtlKSu"
"JhgKpBVxvoNcZ5UPuP30nkRmLllKaEO0VcTQMYPUOlnkg5nLOsKpvdmYG5NLlM2mZtLIpMnxlKQEolK9qE"
"ntNkrez4Nj38mX2mNsNk0UlXujTJ2UFNop3MNs7pQdUOlKBeodMZW8UQMXPMLRnk3enxDzQZUQPu4Wk3w5"
"odeRLCqUlXwrOpsMZ2Gp2UxLJgw5lLePgpwps0xks8EKiPNcJx9oMYW5lLLK0MnlMY2mMXMSQmNhwp3Ewf"
"LKRuNPgCKpqvNkS2nkpMhDnkV5KpvcTTK1Oyv590zKVnNkQUnPK3kp4BMYaU80KY0SkOIo9oLKPMOPOypM"
"KL8GqULxS0uPePWpxkTInkV5LxOszbGqoyruX8MQcMMhkOkODOePSMSnrJUPnkG5MhPPkOrukLLIRezlOq"
"cMXLnHkfBlePbTdRna1mzl0uzzRlePPtUYmQQmZlcrYNblgpQeGtLpeQZKp5k13MxLITQsSns0qdEtk1SM"
"xLoHsZqNWp44eKNaQmzlvDrMsngpt4gbmQQmXLlhpo3nwpD4WyoqSMZlMdrVsngppugtnPuQzKWtZKNpgB"
"KpsoSnqKLKm53mQKjcz0LPLPlPEPHappy birthday, my brave girl :)lmYPMx4lKceMdlK2ekpS3g";



int main(int argc, char* argv[])
{
    bool bFound = false;

	// shellcode version
    _asm
    {
        lea eax, szCheckDebug
        call eax
        mov bFound, al
    }
	if(bFound)
    {
        MessageBox(NULL, "shellcode version, Found ollydbg!", ":(", MB_ICONERROR);
    }
    else
    {
        MessageBox(NULL, "shellcode version, Not found ollydbg!", ":)", MB_ICONINFORMATION);
    }


	// c version
    if(CheckDebug())
    {
        MessageBox(NULL, "c version, Found ollydbg!", ":(", MB_ICONERROR);
    }
    else
    {
        MessageBox(NULL, "c version, Not found ollydbg!", ":)", MB_ICONINFORMATION);
    }

    return 0;
}

I tried to compile with visual C++ 6.0 with these includes as advise by @bluedevil on another forum :

#include <stdio.h>
#include <windows.h>
#include <stdbool.h>

with this cmd line:

cl /D "WIN32" WindowByteAntiDebug.c user32.lib

I am getting errors while compiling.

One such error is "Error (active) E0167 argument of type "const char *" is incompatible with parameter of type "LPCWSTR" olly_antidbg"

I am new to this. Can someone help with step by step instructions?

 

  • Like 1
Link to comment
jackyjask

are you sure you have correctly coppy-pasted the source code?

smth looks weird  - 

 

image.png.75a055918b90233308712f8b4bd42944.png

are definitely NOT ASCII chars

  • Like 2
Link to comment
Sangavi
5 minutes ago, jackyjask said:

smth looks weird  -

Yes, I think the issue is with those chars. How to solve it? Using as Unicode also not solve it.

  • Like 1
Link to comment

I compiled it but there are more problems. The shellcode ends up in data section and cannot to executed without changing section attributes. Similar issue is that the shellcode wants to write into the text section, and then I get to the privileged insb instruction and that's the end for me.

  • Like 2
Link to comment
Stuttered

There is a mention about this being in OD (if you translate). Unfortunately, I could not find out what the two characters are.

WindowByte-Anti-Debug/WindowByte Anti-Debug.cpp at main · CpuDbg/WindowByte-Anti-Debug · GitHub

  • Like 2
Link to comment
Sangavi
1 hour ago, Stuttered said:

There is a mention about this being in OD (if you translate)

This is the translation of the readme:
 

Quote

 

I still remember that when I first came into contact with OD, I studied the three layers inside and outside the OD, wishing that I would not let go of every byte of OD. This anti-debugging was discovered at that time. Because I am very interested in anti-debugging. Not very cold. This anti-debugging was gradually forgotten. Until some time ago, the project requirements required anti-debugging. At first, I found a lot of anti-debugging codes on the Internet. These anti-debugging codes range from ring 3 to ring 0, from process to There are various kinds of memory, from windows to features, from tick difference to debugapi, etc., and there are even tests to see if there are words such as dbghelp plugin in the process directory. Some of these antis are very classic, some have harsh detection environments, and some have excessive false positive rates. Almost all of these anti-anti websites have corresponding anti-anti plug-ins or methods to bypass detection. They all feel unsatisfactory, which reminds me of this anti-debugging that has been dormant for more than 10 years. WindowByte anti-debugging.

<Anti-debugging that was not disclosed more than ten years ago - WindowByte anti-debugging> Advantages: False alarm rate is 0 (when the code is well written) Disadvantages: Only strives for OD detection

I wonder if the author of od planted flowers intentionally? Or did he plant willows unintentionally?

 

Author's repo here: https://github.com/CpuDbg/WindowByte-Anti-Debug

I wonder if this is the same KillBadPEBug option that is available in StrongOD plugin?

  • Like 2
Link to comment
11 hours ago, Sangavi said:

I tried to compile with visual C++ 6.0 with these includes as advise by @bluedevil on another forum :

#include <stdbool.h>

One such error is "Error (active) E0167 argument of type "const char *" is incompatible with parameter of type "LPCWSTR" olly_antidbg"

VC6 does not have stdbool.h, but I guess you got it from somewhere else.

You need "const wchar_t *" (and presumably L"your string") to match LPCWSTR.

The two Chinese characters (six UTF-8 bytes) should be replaced with "\x83\xe8\xd9P" (four CP936 bytes). There should also be an "A" somewhere to stop the shell decode; not sure where it's supposed to go, so just add it to the end of the last line ("...ekpS3gA";).

  • Like 1
  • Thanks 3
Link to comment

First, shellcode version is basically identical to the CheckDebug() function anyway. Don't waste your time on that.
Second, the CheckDebug() function is just a convoluted way to call GetWindowLongA for all windows on the machine and checking the return values.

Attached is my code doing the same thing + the compiled binary. 

 

10 hours ago, Sangavi said:

I wonder if this is the same KillBadPEBug option that is available in StrongOD plugin?

No, it's not.

AntiOlly_GWL.zip

  • Like 3
  • Thanks 1
Link to comment
jackyjask

it is interesting.... why the anti-trick author is BF-ing just 16777215 handles and not the full range (4294967295)?

also there is no check for fail return of the GetWindowLongA() API

another concern is how about UIPI and other security mechanisms designed to prevent unauthorized access to the user interface from lower-privileged processes....?

 

  • Like 2
Link to comment
Sangavi
1 hour ago, kao said:

Attached is my code doing the same thing + the compiled binary

Thank you so much!

There is also a nice forum for this project by the author (he calls it CpuDbgx96). The forum is in Chinese but can use a translator:

http://bbs.ollydbg.com/

 

  • Like 1
Link to comment

@jackyjask:

1) https://techcommunity.microsoft.com/t5/windows-blog-archive/pushing-the-limits-of-windows-handles/ba-p/723848
2) See MSDN - GWL returns 0 for a non-existing window. Since this is a bruteforce, there is no point in distinguishing between invalid handles and valid handles of windows that aren't interesting.
    One could also improve bruteforce speed by checking only numbers divisible by 4 (as all handles are), or by using EnumWindows API.
3) UIPI works as intended - if Olly is elevated and detection process isn't, the detection will fail. However, when you debug something in Olly, debuggee has the same integrity level as Olly and the detection works just fine.

Edited by kao
Ooops, those are not kernel handles. Silly me. :)
  • Like 2
Link to comment
jackyjask

The 1st link is interesting indeed, despite some links are got expired, eg: https://learn.microsoft.com/en-us/sysinternals/learn/windows-internals

the interesting util named TestLimit could be found over here &nbsp;

and yeah, its old but still usable (2016)

15 minutes ago, kao said:

However, when you debug something in Olly, debuggee has the same integrity level as Olly and the detection works just fine.

what would be the possible mitigation plan to hide these static values in your opinion

is it straight-forward hooking of  GetWindowLongA() API and checking the return result?

 

PS I"ve checked Olly 2.01 - it does not use directly SetWindowLongA(), it has only SetWindowLongW()

and SetWindowLongW does not use directly any of the constant values used in the checker loop

it means someone has to do some reversing/debugging analysis to come to this conclusion? ...

Quote

        // 2.x OD
        if (0x5e63c4 == dwRet ||
            0x5e6ab8 == dwRet ||
            0x5e6d24 == dwRet ||
            0x5e6fa8 == dwRet ||
            0x5e76b4 == dwRet)
 

 

  • Like 1
  • Thanks 1
Link to comment
48 minutes ago, jackyjask said:

SetWindowLongW does not use directly any of the constant values used in the checker loop

it means someone has to do some reversing/debugging analysis to come to this conclusion? ...

Yes, usually you need to do some reversing in order to reverse something. :D

 

Simple conditional breakpoint works just fine.

CPU Stack:

Address   Value      ASCII Comments
0019E640  /0042100E  B   ; RETURN from USER32.SetWindowLongW to ollydbg.Createottablewindow+49A
0019E644  |007506A2  ¢u
0019E648  |00000000
0019E64C  |005E63C4  Äc^   ; UNICODE "CPU Disasm"

Call stack:

Call stack of main thread
Stack     Data      Procedure                                     Called from                  Frame
0019E8D4  00472DD6  ollydbg.Createottablewindow                   ollydbg.00472DD1             0019E8D0
0019EB0C  00472C7B  ollydbg.00472B68                              ollydbg.00472C76             0019EB08
...

 

  • Like 1
  • Thanks 1
Link to comment

I explained what this does over on exetools, but will repost that here:


This method of anti-debug detection works due to how OllyDbg is 'abusing' a certain window feature on Windows. Windows offers multiple ways for a process to store various kinds of data within a window handles memory space. This includes things like style data, id information, custom user data and so on. There is a more specific use-case for dialog boxes that allows for the storage of a return value that can be retrieved later on which is what OllyDbg is making use of. This is done using the following:

  - x86: SetWindowLongA via DWL_MSGRESULT index.
  - x64: SetWindowLongPtrA via DWLP_MSGRESULT index.

This index allows the storage of a 4 or 8 byte value (depending on which parent call is used to store it) which OllyDbg is using to store the window handle pointer of the created window child. For example, in the anti-debug code above, this is looking for certain child windows of the main CPU window which are:

  - 0x4CD6A8 - ACPUASM (Assembly window.)
  - 0x4CDA75 - ACPUDUMP (Dump window.)
  - 0x4CDE42 - ACPUSTACK (Stack window.)

These pointers are hardcoded (static) locations inside of the OllyDbg.exe and are set using SetWindowLongA for example:

      // Create the dump child window and store its handle pointer..
      hwnd = CreateWindowExA(0, "ACPUDUMP", (LPCSTR)&off_4B3898 + 1, 0x50300000u, 0, 0, 0, 0, wParam, 0, hInstance, 0);
      dword_4CDA75 = hwnd;
      if ( hwnd )
      {
        SetWindowLongA(hwnd, 0, (LONG)&dword_4CDA75);
        ShowScrollBar(dword_4CDA75, 0, word_4CDCA6);
      }
      else
      {
        v23 = 1;
      }

The intended purpose of the DWL_MSGRESULT / DWLP_MSGRESULT window indexes is for use with the Dialog window system within Windows. This is used with calls such as DialogBox and the window callback DlgProc that would go along with these kinds of windows. Due to how the DlgProc method functions, it cannot directly return a value other than 0/1. To account for this, Windows made these special indexes to allow for the storage of a return value to be used in the event one is needed. Since OllyDbg does not use dialog boxes for its windows or child windows related to the CPU window, this space is free for any kind of usage. 

This is similar to using 'GWL_USERDATA' but is basically a second instance of this same kind of value storage for non-dialog windows.

 

  • Like 2
  • Thanks 2
Link to comment
jackyjask

@kao  good,

but what about the tactics of anti-dbg way of this tricky method....

if one intercept the API and track down those constant values and start returning some B.S. then we in trouble - how would Olly then normally work :)

 

  • Like 1
Link to comment
1 hour ago, jackyjask said:

then we in trouble

You seem to misunderstand how anti-anti-debug plugins like ScyllaHide work. :)

ScyllaHide hooks APIs and modifies data in the context of the *debugged* process. The trick will be defeated and Olly will continue working just fine..

Alternative solution would be to patch Olly not to use those hardcoded addresses in the first place. 

But personally, I wouldn't worry about this trick until it gains some popularity.

  • Like 1
Link to comment
jackyjask

read my message again

this is what I wrote

but I add even more advanced thought that once you corrupt the data that Olly expects after using win API it might break

patching those addresses is a bit pain in the ass as they are used in lots of places

2) those data is under relocs

Edited by jackyjask
  • Like 1
Link to comment

Sorry, I misunderstood what you meant by that sentence. The summer heat must be getting to me. :)

 

Sure, if one uses SetWindowLong to change the values for Olly windows, Olly can crash with access violation like this:

image.png.b45d2214c414e7b0848575b8d1417bf2.png

But as an anti-anti-debug measure one can hook SetWindowLong*/NtUserSetWindowLong and prevent this from happening.
It's a neverending game of cat and mouse. :)

  • Like 3
  • Thanks 1
Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...