Кодерские tips’n’tricks В данной теме делимся программерскими трюками. Это могут быть советы по оптимизации, нестандартные приемы, интересные (но не слишком большие) куски кода. Не стоит превращать тему в свалку исходников, выбирайте наиболее интересные и полезные участки кода. Желательно откомметировать трюк, чтобы всем было понятно. Delphi Оптимизация [Передача аргументов] Никогда не передавай функции в качестве параметра структуру, лучше передавать указатель на нее Code: //Ни в коем случае не делай так. Procedure code (Data:TStructure); //Правильный вариант Procedure code (PData:PStructure); //гда PStructure = ^TStrucrure Старайся передавать своим функциям не более трех параметров, т.к. они передаются через регистры (по соглашению fastcall, принятом по умолчанию в Delphi), а все остальные через стек. [Функции - инварианты] Довольно распространенная ошибка программистов – присутствие функций - инвариантов в цикле. Code: //Неоптимизированный вариант While i<= lstrlen(str) do Begin X:=x+ord(str[i]); Inc(i); End; Очевидно, что длина str не меняется, но компилятор считает, что все, что передается по ссылке сожжет быть изменено, и lstrlen вычисляется много раз. Оптимизированный вариант выглядит так. Code: //Так лучше N:=lstrlen(str); While i<= n do Begin X:=x+ord(str[i]); Inc(i); End; [Экономия памяти] Когда класс располагается в памяти, то между полями появляются пустые ячейки памяти. Это происходит потому, что Delphi, оптимизируя код, каждое поле располагает от предыдущего со сдвигом в 4 байта. Code: // Неоптимизированный вариант TMyClass = class private field1: boolean;//1 байт field2: longint; //4 байт field3: char; //1 байт field4: string; //4 байт field5: byte; //1 байт field6: integer; //4 байт field7: byte; //1 байт public procedure code; end; Реально этот экземпляр класса будет занимать 28 байт. Если мы изменим порядок полей, то сможем добиться уменьшения размера до 16 байт. В нашем примере после field1 размером 1 байт идет field2 размером 4 байта, значит, мы теряем 3 байта на выравнивание. Если же размер field2 не превышал 3 байт, то Delphi не стал бы выравнивать, а поместил бы это поле сразу после первого. Code: // Оптимизированный вариант TMyClass = class private field1: boolean; //1 байт field3: char; //1 байт field5: byte; //1 байт field7: byte; //1 байт field2: longint; //4 байт field4: string; //4 байт field6: integer; //4 байт public procedure code; end; [Компиляция без RTL (Run Time Library)] Как известно минимальный размер скомпилированной в Delphi программы с настройками по умолчанию равен 13,5 Кб. Виной тому принудительно подключаемая Delphi RTL, в которой реализованы некоторые возможности языка Delphi. Для уменьшения размера скомпилированных прог исправим модели System.pas и SysInit.pas, удалив все «лишнее». Затем перекомпилируем их и полученные dcu-файлы поместим в папку с прогой. Минимальный System.pas Code: unit System; interface procedure _HandleFinally; type TGUID = record D1: LongWord; D2: Word; D3: Word; D4: array [0..7] of Byte; end; PInitContext = ^TInitContext; TInitContext = record OuterContext: PInitContext; ExcFrame: Pointer; InitTable: pointer; InitCount: Integer; Module: pointer; DLLSaveEBP: Pointer; DLLSaveEBX: Pointer; DLLSaveESI: Pointer; DLLSaveEDI: Pointer; ExitProcessTLS: procedure; DLLInitState: Byte; end; implementation procedure _HandleFinally; asm end; end. Минимальный SysInit.pas Code: unit SysInit; interface procedure _InitExe; procedure _halt0; procedure _InitLib(Context: PInitContext); var ModuleIsLib: Boolean; TlsIndex: Integer = -1; TlsLast: Byte; const PtrToNil: Pointer = nil; implementation procedure _InitLib(Context: PInitContext); asm end; procedure _InitExe; asm end; procedure _halt0; asm end; end. Компиляция Code: Dcc32.exe – Q System.pas SysInit.pas –M –Y –Z -$D- -O Некоторые трюки [Обмен] Обмен значений между двумя переменными без привлечения третьей. Code: x:=x xor y; y:=y xor x; x:=x xor y;
Оптимизация выходного файла в Microsoft Visual C++. Параметры линкера для уменьшения размера: - объединение секций Code: /MERGE:.data=.text /MERGE:.rdata=.text - можно еще снизить выравнивание, например, задать 512 байт или 32 байта Code: /ALIGN:32 или Code: /ALIGN:512 соответственно. - смена точки входа Code: /ENTRY:main или Code: /ENTRY:WinMain для консольного и виндового приложения соответственно. Уменьшает размер, т.к. тогда линкер не пихает в экзешник код стартовой функции mainCRTStartup (WinMainCRTStartup). Только надо помнить, что параметры main (WinMain) в этом случае не будут нести никакого смысла, аргументы командной строки придется получать явно через API GetCommandLine(). - еще можно убрать нафиг CRT или линковать ее динамически: Code: /NODEFAULTLIB msvcrt.lib С учетом всех рекомендаций: Code: #include <windows.h> #pragma comment(linker, "/NODEFAULTLIB /MERGE:.data=.text /MERGE:.rdata=.text /ALIGN:512 /ENTRY:WinMain") int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int) { MessageBox(0, "Hello, World!", "Tiny application", MB_ICONINFORMATION); return 0; } Результат - имеет хелловорлд размером 1к, состоящим из заголовка экзешника и одной секции. Дизассемблерный листинг точки входа не содержит ни одного лишнего байта: Code: 00400230 >/$ 55 PUSH EBP 00400231 |. 8BEC MOV EBP,ESP 00400233 |. 6A 40 PUSH 40 ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL 00400235 |. 68 08024000 PUSH tiny.00400208 ; |Title = "Tiny application" 0040023A |. 68 1C024000 PUSH tiny.0040021C ; |Text = "Hello, World!" 0040023F |. 6A 00 PUSH 0 ; |hOwner = NULL 00400241 |. FF15 00024000 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA 00400247 |. 33C0 XOR EAX,EAX 00400249 |. 5D POP EBP 0040024A \. C2 1000 RETN 10 UPD: Можно снизить выравнивание до 16-и байт, притворившись, что мы собираем драйвер: /ALIGN:16 /DRIVER это нужно вписать в настройки проекта, в #pragma comment(linker) это не прокатит
Delphi Чтобы во время выполнения циклов, не происходил эффект зависания, нужно в теле цикла вставить Code: Application.ProcessMessages; Эффект зависания происходит из-за того, что Windows ждет пока накопятся задания в очереди, а не выполняет их сразу. Application.ProcessMessages заставляет винду выполнить все задачи, которые накопились в данный момент.
в VCL (delphi) есть "перехват" закрытия формы OnCloseQuery (где canclose boolean типа который и дает "разрешение" на закрытие формы)...
Экстремально маленький Hello Word! на Delphi. Собирать исполняемый файл линкером от Microsoft. К сожалению, майкрософтовкий линкер понимает только COFF и Intel OMF, наотрез отказываясь работать с Borland OMF. Delphi же, начиная с третьей версии, перешла с формата Intel OMF на Borland OMF. Поэтому компилировать придется компилятором от Delphi 3. Пример минимального HelloWord. Code: Unit HelloWord; Interface Procedure Start; Implementation Function MessageBoxA(hWnd:cardinal; IpText, IpCaption:Pchar; uType:Cardinal): Integer; stdcall; external ‘user32.dll’ name ‘_MessageBoxA@16’; Procedure Start; Begin MessageBoxA(0,’Hello word!’,nil,0); End; End. Ти модуля Unit нужен для того, чтобы компилятор сгенерировал в объектном файле символьные имена объявленных процедур. Компилируем: Dcc32.exe –JP -$A-,B-,C-,D-,G-,H-,I-,J-,L-,M-,O+,P-,Q-,R-,T-,U-,V-,W+,X+,Y- HelloWord.pas Открываем файл HelloWord.obj в HEX-редакторе и смотрим во что превратилась наша точка входа. Допустим Start$wwrv. Теперь выполняем сборку Link.exe /ALIGN:32 /FORCE:UNRESOLVED /SUBSYSTEM:WINDOWS /ENTRY:Start$wwrv HelloWord.obj user32.lib /out:Hello.exe В результате имеем файл размером 832 байта.
в библиотеках (касается Delphi) если передаешь string данные от лучше заюзать переменную типа ShortString либо добавить ShareMem в раздел Uses... Имхо 1 лучше... а если явно передаваемые данные string (например Edit1.Text), преобразуй ShortString(Edit1.Text); таким образом можно пребразовывать не только ShortString, но допустим и из string в PChar=> pchar(edit1.text)
Вот нашел несколько полезных советов для M$ Visual Studio. Кому интересно - оригинал тут Я приведу собственный перевод с моими комментариями. 1. Учим Visual Studio раскрывать структуры и классы в отладчике Редактируем файл \Program Files\Microsoft Visual Studio\Common\MSDev98\Bin. Его формат можно определить методом тыка, посмотрев уже существующие строки. (Там все не так уж и сложно. В начале файла есть немаленький комментарий про синтаксис. Насколько я понял, просто вместо древовидной структуры объекта или структуры будет показываться одно значение. Например, для CString - m_pchData - Прим. Great). 2. Добавляем слова для подсветки. Если создать файл usertype.dat в каталоге \Program Files\Microsoft Visual Studio\Common\MSDev98\Bin со словами по одному на строчку, то эти слова студия будет выделять синим цветом как ключевые. (Я уже добавил туда main и WinMain для удобства - Прим. Great). 3. Как использовать расширение *.cc для C++ Нужно произвести следующие изменения в реестре: и добавить флаг /Tp к флагам компилятора в настройках проекта 4. Как закрепить меню. В Visual Studio панель меню перемещаемая (об этом говорят две полосы слева). Если это мешает, это можно убрать, выставив галочку "Use screen reader compatible menus" в диалоговом окне Tools -> Options -> закладка Worspace 5. Напоминания при компиляции Если у тебя плохо с памятью и тебе сложно запомнить, например, что вот тот кусок кода в финальной версии желательно убрать или что в этом коде содержится некритичная проблема, можно сделать себе напоминание: Code: #define Stringize( L ) #L #define MakeString( M, L ) M(L) #define $Line MakeString( Stringize, __LINE__ ) #define Reminder __FILE__ "(" $Line ") : Reminder: " использовать: #pragma message(Reminder "Fix me!") В результате компилятор сгенерирует при компиляции сообщение: 6. Как сделать вручную точку останова Просто поставить код Code: __asm int 3; При нажатии на F5, когда выполнение дойдет до этого места, процессор сгенерирует исключение EXCEPTION_BREAKPOINT, отладчик его поймает и выведет сообщение - User breakpoint called from code at 0x... 7. Отладочные значения в различных областях памяти программы 0xCDCDCDCD Память выделена в куче, не инициализирована 0xDDDDDDDD Память, выделенная в куче, уже освобождена 0xFDFDFDFD Заполнитель NoMansLand записывается у границ участка памяти для контроля распространенного типа ошибки выхода за границы массива и переполнения. После затирании это указатели при освобождлении этой памяти free/delete выдаст диалоговое окно: DAMAGE: after ТИП block (#номер) at 0xадрес., где ТИП - обычно Normal - тип освобождаемого участка, номер - номер блока. Пример неправильного кода: Code: char* a = new char[2]; strcpy(a, "aaaaaaaaaaaaaaaaaaaaa"); delete a; Во время выполнения delete отладочная сборка программы выдаст окно "DAMAGE: after Normal block (#55) at 0x00430030". 0xCCCCCCCC Выделено в стеке, не инициализировано 8. Предопределенные псевдопеременные во время отладки В окно Watch можно добавить следующие "переменные": @err - последняя ошибка (GetLastError) @tib - адрес Thread Information Block потока @clk - время выполнения программы (мс) 9. Просмотр указателей как массивов Обычно, если добавить в окно Watch переменную типа char*, она покажется как строка, а не как массив. Чтобы посмотреть отдельные элементы массива (например, чтобы узнать ASCII-код символа из этой строки), надо добавить в Watch выражение переменная,длина, где переменная - переменная указательного типа, длина - за массив какой длины отладчик ее должен считать. Например, пусть объявлен char* str; Выражение str,10 нам покажет всю строку + отдельно как массив ее первые 10 символов (в массиве - индексы 0-9). Выражение ((char*)&main),100 покажет нам первые 100 байт машинного кода функции main 10. Вызов программных функций во время отладки Если у тебя есть функция, например, Code: int function() { return 100*2; } а тебе хочется посмотреть, что бы она возвратила во время отладки, если бы стоял ее вызов в коде - нет проблем! Просто добавляем в окно Watch выражение function() и сразу там появится значение 200 11. Именование потоков при отладке Следущий код содержит функцию, позвляющую назвать поток с указанным ID'ом своим именем Code: #define MS_VC_EXCEPTION 0x406d1388 typedef struct tagTHREADNAME_INFO { DWORD dwType; // must be 0x1000 LPCSTR szName; // pointer to name (in same addr space) DWORD dwThreadID; // thread ID (-1 caller thread) DWORD dwFlags; // reserved for future use, most be zero } THREADNAME_INFO; void SetThreadName(DWORD dwThreadID, LPCTSTR szThreadName) { THREADNAME_INFO info; info.dwType = 0x1000; info.szName = szThreadName; info.dwThreadID = dwThreadID; info.dwFlags = 0; __try { RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(DWORD), (DWORD *)&info); } __except (EXCEPTION_CONTINUE_EXECUTION) { } } Имя потока появится в окне Debug->Threads.
Не совсем в тему, но все же. Поиск базы длл по ее имени в адресном пространстве другого процесса (аналог GetModuleHandle, но для др. адресного пространства). Может, кому понадобится. Писал я по просьбе протеуса ну и соотв. там мои норкоманские идеи Итак, че же для этого я сделал. Ищем первый поток процесса. С помощью GetThreadSelectorEntry получаем запись LDT по селектору FS = 0x3b. Открываем Thread Environment Block, из него Process Environment Block, из него - структуру данных загрузчика (все это через ReadProcessMemory), потом проходимся по списку модулей. Code: const DWORD FS = 0x3B; DWORD ownerProcessId = 38056; // Open process HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, ownerProcessId); if(!hProcess) return printf("Cannot open process\n"); // Find thread HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); THREADENTRY32 te = {sizeof(te)}; Thread32First(hSnapshot, &te); DWORD threadId = 0; do { if(te.th32OwnerProcessID == ownerProcessId) threadId = te.th32ThreadID; } while(Thread32Next(hSnapshot, &te)); if(!threadId) return printf("No threads for specified process id\n"); // Get FS base HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, 0, threadId); if(!hThread) return printf("Cannot open thread\n"); LDT_ENTRY fs_entry; GetThreadSelectorEntry(hThread, FS, &fs_entry); DWORD fs_base = fs_entry.BaseLow | (fs_entry.HighWord.Bytes.BaseMid<<16) | (fs_entry.HighWord.Bytes.BaseHi<<24); // Read modules list TEB teb; DWORD read; if(!ReadProcessMemory(hProcess, (LPCVOID) fs_base, &teb, sizeof(teb), &read)) return printf("Can't read process memory for TEB, %d bytes read\n", read); PEB peb; if(!ReadProcessMemory(hProcess, (LPCVOID) teb.Peb, &peb, sizeof(peb), &read)) return printf("Can't read process memory for PEB, %d bytes read\n", read); PEB_LDR_DATA pld; if(!ReadProcessMemory(hProcess, (LPCVOID) peb.LoaderData, &pld, sizeof(pld), &read)) return printf("Can't read process memory for PEB_LDR_DATA, %d bytes read\n", read); LDR_MODULE entry; if(!ReadProcessMemory(hProcess, (LPCVOID) pld.InInitializationOrderModuleList.Flink, &entry, sizeof(entry), &read)) return printf("Can't read process memory for LDR_MODULE, %d bytes read\n", read); WORD wbuffer[20]; char buffer[1024]; // Walk the list do { // Print the name if(!ReadProcessMemory(hProcess, (LPCVOID) entry.BaseDllName.Buffer, &wbuffer, sizeof(wbuffer), &read)) return printf("Can't read process memory for wbuffer in loop, %d bytes read\n", read); WideCharToMultiByte(CP_ACP, 0, wbuffer, -1, buffer, sizeof(buffer), 0, 0); printf("%s\t[0x%08x]\n", buffer, entry.BaseAddress); // Next if(!ReadProcessMemory(hProcess, (LPCVOID) entry.ModuleList.Flink, &entry, sizeof(entry), &read)) return printf("Can't read process memory for LDR_MODULE in loop, %d bytes read\n", read); } while(entry.ModuleList.Flink != pld.InInitializationOrderModuleList.Flink); CloseHandle(hThread); CloseHandle(hProcess); CloseHandle(hSnapshot);
уж0с... вот то же самое, но без изврата =/ Code: DWORD RemoteGetModuleHandle(char *name, DWORD dwPid) { MODULEENTRY32 m = {sizeof(m)}; DWORD dwRet = 0; HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPid); if (hSnap == INVALID_HANDLE_VALUE) return NULL; if (!Module32First(hSnap, &m)) return NULL; do { if (!lstrcmpi(m.szModule, name)) { dwRet = m.modBaseAddr; break; } } while (Module32Next(hSnap, &m)); CloseHandle(hSnap); return dwRet; }
Cr4sh, нуууууу ))) так не прикольно ) Прикольно вручную анализировать )) Хотя изобретать велосипед, конечно, тоже не стоит )
зато я больше чем уверен, что этот код будет стабильно работать на всей линейке от NT 4.0 и до Висты )
Перенос программы в другое адресное пространство. Был замечательный метод по инжекту кода в другую программу - вся наша программа постранично копировалась в чужое АП (адресное пространство) по тем же адресам со всеми заголовками, секциями и прочим. Все было бы хорошо, пока там память свободна. Если там память занята, метод не предусматривал решения в этом случае. Предлагаю модификацию: поиск наилучшего достаточного места в другом АП, копирование программы, коррекция фиксапов: Code: // Get VA #define RVATOVA( base, offset )(((DWORD)(base) + (DWORD)(offset))) // Move program's memory void __CopyMemoryAcrossProcesses( HANDLE hProcess, char* pMemLocal, char* pMemRemote ) { DWORD dwOldProt, dwNumBytes, i; MEMORY_BASIC_INFORMATION mbi; VirtualQueryEx(hProcess, pMemRemote, &mbi, sizeof(MEMORY_BASIC_INFORMATION)); while (mbi.Protect!=PAGE_NOACCESS && mbi.RegionSize!=0) { if (!(mbi.Protect & PAGE_GUARD)) { for (i = 0; i < mbi.RegionSize; i += 0x1000) { VirtualProtectEx(hProcess, pMemRemote + i, 0x1000,PAGE_EXECUTE_READWRITE, &dwOldProt); WriteProcessMemory(hProcess, pMemRemote + i, pMemLocal + i, 0x1000, &dwNumBytes); } } pMemLocal += mbi.RegionSize; pMemRemote += mbi.RegionSize; VirtualQueryEx(hProcess, pMemRemote, &mbi, sizeof(MEMORY_BASIC_INFORMATION)); } } bool TransferProgram(HANDLE hProcess) { HMODULE g_module = GetModuleHandle(0); VirtualFreeEx(hProcess, g_module, 0, MEM_RELEASE); DWORD dwSize = ((PIMAGE_OPTIONAL_HEADER)((LPVOID)((BYTE *)(g_module) + ((PIMAGE_DOS_HEADER)(g_module))->e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER))))->SizeOfImage; char *pMem = (char *)VirtualAllocEx(hProcess, g_module, dwSize, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE); if(pMem == NULL) return FALSE; __CopyMemoryAcrossProcesses( hProcess, (char*) g_module, pMem ); return true; } DWORD TransferProgramEx(HANDLE hProcess) /* Return value: image base delta, -1 on error */ { HMODULE hmodule = GetModuleHandle(0); DWORD dwSize = ((PIMAGE_OPTIONAL_HEADER)((LPVOID)((BYTE *)(hmodule) + ((PIMAGE_DOS_HEADER)(hmodule))->e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER))))->SizeOfImage; MEMORY_BASIC_INFORMATION mbi = {0}; VirtualQueryEx( hProcess, hmodule, &mbi, sizeof(mbi) ); LPVOID Allocated; // Memory isn't free, relocate if( mbi.Protect!=PAGE_NOACCESS && mbi.RegionSize!=0 ) { __try_relocate: LPVOID DesiredAddress = hmodule; *(DWORD*)&DesiredAddress += mbi.RegionSize; for(;;) { Allocated = VirtualAllocEx( hProcess, DesiredAddress, dwSize, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE ); if( Allocated ) break; *(DWORD*)&DesiredAddress += dwSize; } } else { Allocated = VirtualAllocEx( hProcess, hmodule, dwSize, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE ); if( !Allocated ) goto __try_relocate; } // move memory __CopyMemoryAcrossProcesses( hProcess, (char*) hmodule, (char*) Allocated ); DWORD ImageBaseDelta = (DWORD)Allocated - (DWORD)hmodule; // Nonzero imagebase delta if( ImageBaseDelta ) { // Apply fixups typedef struct { WORD Offset:12; WORD Type:4; } IMAGE_FIXUP_ENTRY, *PIMAGE_FIXUP_ENTRY; PIMAGE_OPTIONAL_HEADER poh = (PIMAGE_OPTIONAL_HEADER)( (DWORD)hmodule + ((PIMAGE_DOS_HEADER)hmodule)->e_lfanew + sizeof(IMAGE_NT_SIGNATURE) + sizeof(IMAGE_FILE_HEADER) ); // No fixups? if( !poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress ) { VirtualFreeEx( hProcess, Allocated, dwSize, MEM_DECOMMIT ); VirtualFreeEx( hProcess, Allocated, dwSize, MEM_RELEASE ); return -1; } PIMAGE_BASE_RELOCATION Reloc = (PIMAGE_BASE_RELOCATION) RVATOVA(poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress, hmodule); int i = 0; // Process fixups for( PIMAGE_FIXUP_ENTRY Fixup = (PIMAGE_FIXUP_ENTRY)( (DWORD)Reloc + sizeof(IMAGE_BASE_RELOCATION) ); (DWORD)Fixup < (DWORD)Reloc + Reloc->SizeOfBlock -2; Fixup ++, i ++ ) { if( Fixup->Type == IMAGE_REL_BASED_HIGHLOW ) { DWORD* Patch = (DWORD*)RVATOVA( Reloc->VirtualAddress + Fixup->Offset, Allocated ); DWORD t, r; BOOL b = 1; // correct fixup b &= ReadProcessMemory( hProcess, Patch, &t, 4, &r ); t += ImageBaseDelta; b &= WriteProcessMemory( hProcess, Patch, &t, 4, &r ); if( !b ) { // smth wrong VirtualFreeEx( hProcess, Allocated, dwSize, MEM_DECOMMIT ); VirtualFreeEx( hProcess, Allocated, dwSize, MEM_RELEASE ); return -1; } } else { // unsupported fixup type VirtualFreeEx( hProcess, Allocated, dwSize, MEM_DECOMMIT ); VirtualFreeEx( hProcess, Allocated, dwSize, MEM_RELEASE ); return -1; } } } return ImageBaseDelta; } TransferProgram - старый вариант TransferProgramEx - моя модификация Использовать примерно так: Code: DWORD ImageBaseDelta = TransferProgramEx( hProcess ); switch(ImageBaseDelta) { case -1: return printf("Cannot copy body\n"), 0; case 0: printf("Program copied at the same addresses\n"); break; default: printf("Program relocated (delta = 0x%08x)\n", ImageBaseDelta); break; } DWORD thID; HANDLE hTh; hTh = CreateRemoteThread( hProcess, 0, 0, (LPTHREAD_START_ROUTINE)((char*)RemoteThread+ImageBaseDelta), 0, 0, &thID); WaitForSingleObject( hTh, INFINITE );
_Great_ Вообще для бряка я создаю макрос #define __INT3 __asm int 3; более наглядней. Удобно при написании дров использовать Int 3 причём с опкодом CC, а не с CD 03. тем более что некоторые ядерные отладчики имеют функцию: заменить все int3 (0xCC) на nop (0x90). НО ,как я понимаю, в VC такой возможности нет. обидно. ------------------------------------------------------------------------ Иногда функция должна возвращать результат своей работы (допустим ошибку), который нужно показать юзеру. Удобно делать следующим образом: PCHAR func(...) { .. return "ERROR"; ... return "STATUS_SUCCESS"; } ------------------------------------------------------------------------ Что касается сишных функций - тож можно передавать параметры через регистры. достаточно при объявлении указать __fastcall : DWORD __fastcall func1(arg1,arg2,...) { ... } ------------------------------------------------------------------------ Допустим, необходимо проверить 4 символа в строке. PCHAR str1; ULONG i; ... if ( (str == 'd') && (str[i+1]== 'c') &&...) ... НО лучше так: if ( *(PDWORD)&str1 == 'abcd' ) ... только нужно учитывать, что компилятор переворачивает символы в строке.
Способ конвертировать любые типы практически в любые другие. К примеру если необходимо загнать в массив значение переменной типа dword, но в том формате в котором она хранится в оперативе (т.е. 4 байта). Code: type massiv=array[0..3] of byte; // тип массива для хранения DW2MAS = ^massiv; procedure TForm1.Button1Click(Sender: TObject); var mas:massiv; // наш массив d:dword; // наша Dword переменная begin d:=$12345678; mas:=DW2MAS(@d)^; //теперь содержимое массива будет таким: $78,$56,$34,$12 end; такимже образом можно окнвертировать и другие типы. Главное чтобы из размер был одинаков. т.е. Dword - некогда не засунуть в array[0..1] of byte; Кстати, такимже образом можно и наоборот. засовывать массив из 4-х байт в dword Этот метод хорошь тем что работа идет напрямую с память, без использования каких либо дополнительный вычислений. ----------------- При работе с IP адресами многие видели, что они храняться в озу в прямом порядку, т.е. старший байт на старшем месте. т.е. IP - 127.0.0.1 бедет храниться 7F000001 Это очень не удобно для увелицения значения IP адреса. т.е. к примеру появилась необходимость пройтись по диапазону 192.168.2.1 - 192.168.2.128. Сложность заключается в том что при увеличении значения на 1 будет увеличиваться старшая часть IP адреса, а не младшая (как это нам нужно). Для этих целей пожно использовать простой метод основанный на инструкции МП - bswap которая меняет порядок байт. Code: function incIP(d:dword):dword;assembler; asm bswap eax // поменять поряд байт inc eax // увеличить на 1 bswap eax // поменять обратно end; -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. var ca:sockaddr_in; d:dword; begin d:=inet_addr('127.0.0.1'); // сохраним IP адресс d:=incIP(d); // увеличим на 1 d:=incIP(d); // увеличим на 1 d:=incIP(d); // увеличим на 1 ca.sin_addr.s_addr:=d; // запишем в структуру. end; Тем самым в ca.sin_addr.s_addr будет число которое будет соотвествовать IP - 127.0.0.4
Ещё не понимаю код типа: d=inet_addr('127.0.0.1'); здесь логичней присваивать значение напрямую: d=0x0100007F; хотя, это не так наглядно, но ведь можно d=0x0100007F; // 127.0.0.1 тоже самое с инициализацией портов, юникод строк: #define InitUnicodeString(string) {sizeof(string)-2,sizeof(string),string}
2 gevara Ну это я просто для наглядности юзал функцию винсоковскую. Суть была не в том как записать IP адрес, а в том, как увеличить его
Вообще-то, __asm int 3 геренит CC, а не CD 03. Команда CD 03 отнюдь не аналогична CC и таких "отладочных" свойств, насколько я помню, не имеет. CC - Это НЕ просто однобайтовая замена для CD 03. Поэтому __asm int 3 генерит сразу CC, а чтобы сгенерить CD 03 надо явно написать __asm emit 0xCD __asm emit 0x03 2all - постирал несколько постов, заканчиваем офтопить
Delphi. Функция округления числа до нужного количества знаков: Code: function RoundFloat(R: Extended; Decimals: Integer): Extended; var Factor: Extended; begin Factor := Int(Exp(Decimals * Ln(10))); Result := Round(Factor * R) / Factor; end; Используя вышеописанную функцию, получаем окгугленный размер файла в килобайтах (наиболее универсальная величина для большинства файлов): Code: Function FileSizeInKb(YourFile : String) : string; Var arg: extended; F : Integer; Begin F:=FileOpen(YourFile,0); arg := FileSeek(F,0,2); FileClose(F); arg:=roundfloat(arg/1024,1); result:=floattostr(arg); End; Функция проверяет, открыт ли порт на хосте, с указанием времени ожидания ответа. Отличная вещь для написания сканера одного порта на диапазоне адресов. Зависимость - winsock2, обертку под делфню можно найти хде угодно Code: function PingPort(host: string; portnum: word; tim: integer): boolean; var k: integer; s, opt: Integer; FSocket: TSOCKET; busy : boolean; addr : TSockAddr; hEvent : THandle; fset : TFDset; tv : TTimeval; tec :PServEnt; PName:String; GInitData : TWSADATA; begin result := false; WSAStartup(MAKEWORD(2,0), GInitData); addr.sin_family := AF_INET; addr.sin_addr.s_addr := INADDR_ANY; addr.sin_port := htons(portnum); hEvent := WSACreateEvent(); busy:=false; FSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_IP); WSAEventSelect(FSocket, hEvent, FD_WRITE + FD_CONNECT); addr.sin_addr := LookupName(host); connect(FSocket, @addr, sizeof(addr)); if WSAGetLastError()=WSAEINPROGRESS then begin closesocket (FSocket); busy:=true; end; FD_Zero(fset); if busy <> true then FD_SET (FSocket, fset); tv.tv_sec := tim; tv.tv_usec := 0; s:=select (1, nil, @fset, nil, @tv); if busy then exit; if FD_ISSET (FSocket, fset) then begin s:=Sizeof(Opt); opt:=1; getsockopt(FSocket, SOL_SOCKET, SO_ERROR, @opt, s); if opt=0 then result := true; closesocket(FSocket); end; WSACloseEvent(hEvent); WSACleanup; end; Шустрый метод копирования файлов. При правильной подборке размера буфера можно добиться космической скорости Code: procedure CopyFile(file1,file2: string); var FromF, ToF: file; NumRead, NumWritten: Integer; Buf: array[1..2048] of Char; begin AssignFile(FromF, file1); Reset(FromF, 1); AssignFile(ToF, File2); Rewrite(ToF, 1); repeat BlockRead(FromF, Buf, SizeOf(Buf), NumRead); BlockWrite(ToF, Buf, NumRead, NumWritten); until (NumRead = 0) or (NumWritten <> NumRead); CloseFile(FromF); CloseFile(ToF); end;
>>Шустрый метод копирования файлов. При правильной подборке размера буфера можно добиться космической скорости вообщето апишка есть для этого