Sangavi Posted June 29 Posted June 29 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? 1
jackyjask Posted June 29 Posted June 29 are you sure you have correctly coppy-pasted the source code? smth looks weird - 冭 貾 are definitely NOT ASCII chars 2
Sangavi Posted June 29 Author Posted June 29 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. 1
PeterN Posted June 29 Posted June 29 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. 2
Stuttered Posted June 29 Posted June 29 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 2
Sangavi Posted June 29 Author Posted June 29 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? 2
adoxa Posted June 30 Posted June 30 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";). 1 3
kao Posted June 30 Posted June 30 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 3 1
jackyjask Posted June 30 Posted June 30 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....? 2
Sangavi Posted June 30 Author Posted June 30 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/ 1
kao Posted June 30 Posted June 30 (edited) @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 June 30 by kao Ooops, those are not kernel handles. Silly me. :) 2
jackyjask Posted June 30 Posted June 30 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 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) 1 1
kao Posted June 30 Posted June 30 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. 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 ... 1 1
atom0s Posted June 30 Posted June 30 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. 3 2
jackyjask Posted June 30 Posted June 30 @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 1
kao Posted June 30 Posted June 30 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. 1
jackyjask Posted June 30 Posted June 30 (edited) 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 June 30 by jackyjask 1
kao Posted June 30 Posted June 30 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: 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. 3 1
bluedevil Posted September 14 Posted September 14 (edited) Hello @Sangavi There is a small misleading situation. The very first code was shared is this: 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); } return 0; } I think the OP had his post changed. You can compile this very first code shared; with the information I have given before. Regards (Sorry for the late reply, btw). Edited September 14 by bluedevil typo 1
Sangavi Posted September 14 Author Posted September 14 9 hours ago, bluedevil said: I think the OP had his post changed. No... I did not change my post. It would show as "Edited..." under the post if the post was changed.
bluedevil Posted September 24 Posted September 24 On 9/15/2024 at 1:12 AM, Sangavi said: No... I did not change my post. It would show as "Edited..." under the post if the post was changed. No no; I don't mean you. You know CpuDbg had shared this code snippet. I was mentioning him 😅
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now