Хочу написать свою процедуру для динамического вызова API. Для этого я формирую шеллкод, который пушит аргументы вызываемой функции на стек, копирует в eax адрес функции и делает вызов call eax. Шеллкод формируется правильно, но при выполнении call eax ничего не происходит. С чем это связано? Когда я делаю то же самое в ассемблерной вставке внутри main(), вызов функции успешен, но когда я пытаюсь выполнить сформированный шеллкод при помощи CallWindowProc, CreateThread или просто __asm call shelcode, результата нет. В чем здесь проблема? Код (Text): #include <windows.h> #include <stdio.h> #pragma comment(lib, "user32.lib") #define BEEP 0x8ca07e28 __declspec(naked) unsigned int GetKernel32(void) { __asm { mov eax, fs:[0x30]; /* Get pointer to PEB */ mov eax, [eax + 0xC]; /* Get pointer to PEB_LDR_DATA */ mov eax, [eax + 0x14]; /* InMemoryOrderModuleList */ mov eax, [eax]; /* Get the 2-nd entry */ mov eax, [eax]; /* Get the 3-rd entry */ mov eax, [eax + 0x10]; /* Get the 3-rd entry base address */ ret; }; } /*DWORD Hash(char *name) { DWORD h = 0x74D8; while (*name++) h ^= (h << 7) + *name; return h; }*/ unsigned int Hash(char* str) { unsigned int hash = 0x01b63a; for(; *str; str++) { hash = ((hash << 5) + hash) + (*str); } return hash; } DWORD GetAPI(const DWORD library, const DWORD APIHASH) { if (library) { PIMAGE_DOS_HEADER dos_hdr = (PIMAGE_DOS_HEADER)library; PIMAGE_NT_HEADERS nt_hdr = (PIMAGE_NT_HEADERS)(library + dos_hdr->e_lfanew); PIMAGE_OPTIONAL_HEADER optional_hdr = &nt_hdr->OptionalHeader; PIMAGE_DATA_DIRECTORY data_directory = optional_hdr->DataDirectory; PIMAGE_EXPORT_DIRECTORY export = (PIMAGE_EXPORT_DIRECTORY)(library + data_directory[0].VirtualAddress); DWORD *names = (DWORD *)(library + export->AddressOfNames); WORD *ordinals = (WORD *)(library + export->AddressOfNameOrdinals); DWORD *functions = (DWORD *)(library + export->AddressOfFunctions); for (int i = 0; i < export->NumberOfNames; ++i) { char *name = (char *)(library + names[i]); if (Hash(name) == APIHASH) return functions[ordinals[i]] + (DWORD)library; } } } DWORD CallAPI(DWORD dwHash, DWORD nArgs, ...) { va_list arg_list; char *shellcode = VirtualAlloc(NULL, 40, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); va_start(arg_list, nArgs); /* push arguments from right to left */ for (int i = nArgs - 1; i >= 0; --i) { shellcode[5 * i] = 0x68; /* opcode of push*/ shellcode[5 * i + 1] = va_arg(arg_list, DWORD); /* what to push */ } va_end(arg_list); *(WORD *) &shellcode[5 * nArgs] = 0xB8; /* mov eax, value */ *(DWORD *) &shellcode[5 * nArgs + 1] = GetAPI(GetKernel32(), dwHash); *(WORD *) &shellcode[5 * nArgs + 5] = 0xD0FF; /* call eax */ *(WORD *) &shellcode[5 * nArgs + 7] = 0xC3; /* ret */ __asm call shellcode; //CallWindowProc((WNDPROC)shellcode, 0, 0, 0, 0); //HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)shellcode, NULL, 0, 0); //WaitForSingleObject(hThread, INFINITE); return 0; } typedef BOOL (WINAPI * noimport_beep) (DWORD, DWORD); #define dwBeep 0xfa2c2f56 int main(int argc, char *argv[]) { __asm { push dwBeep; call GetKernel32; push eax; call GetAPI; push 700; push 700; call eax; mov eax, 0x12345678; }; CallAPI(dwBeep, 2, 700, 700); //noimport_beep beep = (noimport_beep)GetAPI(GetKernel32(), dwBeep); //beep(700, 1400); }
Выполнял шеллкод в олли, но не понял, почему произошло небольшое зависание на call eax и почему нет результата. Я же формирую шеллкод точно так же, как в ассемблерной вставке в main. Что могло пойти не так?
Во-первых, ошибка: Код (Text): shellcode[5 * i + 1] = va_arg(arg_list, DWORD) Тебе нужно писать DWORD, а ты пишешь char. Второй момент - кто будет следить за стеком? В ассемблерной вставке в функции main ты при вызове GetAPI нарушаешь структуру стека. Либо делай GetAPI __stdcall функцией либо добавляй к esp.
Спасибо, проблема в самом деле была в размере записываемых данных. Как лучше вызывать этот шеллкод, чтобы из функции можно было вернуть, например, адрес выделенной памяти, если динамически была вызвана VirtualAlloc? __asm call shellcode должно ведь хватить, потому что возвращаемое значение тогда окажется в eax, а эпилог функции CallAPI никак не помешает PS. Проверил сам.
Я бы сделал так: Код (C): void* (*p)(void) = (void*(*)(void))shellcode; return p(); Сам тип функции тоже бы поменял на void*. Исключение асма облегчит переход на x64.