Просмотр полной версии : Кодерские tips and tricks
Кодерские tips’n’tricks
В данной теме делимся программерскими трюками. Это могут быть советы по оптимизации, нестандартные приемы, интересные (но не слишком большие) куски кода. Не стоит превращать тему в свалку исходников, выбирайте наиболее интересные и полезные участки кода. Желательно откомметировать трюк, чтобы всем было понятно.
Delphi
Оптимизация
[Передача аргументов] Никогда не передавай функции в качестве параметра структуру, лучше передавать указатель на нее
//Ни в коем случае не делай так.
Procedure code (Data:TStructure);
//Правильный вариант
Procedure code (PData:PStructure); //гда PStructure = ^TStrucrure
Старайся передавать своим функциям не более трех параметров, т.к. они передаются через регистры (по соглашению fastcall, принятом по умолчанию в Delphi), а все остальные через стек.
[Функции - инварианты] Довольно распространенная ошибка программистов – присутствие функций - инвариантов в цикле.
//Неоптимизированный вариант
While i<= lstrlen(str) do
Begin
X:=x+ord(str[i]);
Inc(i);
End;
Очевидно, что длина str не меняется, но компилятор считает, что все, что передается по ссылке сожжет быть изменено, и lstrlen вычисляется много раз. Оптимизированный вариант выглядит так.
//Так лучше
N:=lstrlen(str);
While i<= n do
Begin
X:=x+ord(str[i]);
Inc(i);
End;
[Экономия памяти] Когда класс располагается в памяти, то между полями появляются пустые ячейки памяти. Это происходит потому, что Delphi, оптимизируя код, каждое поле располагает от предыдущего со сдвигом в 4 байта.
// Неоптимизированный вариант
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 не стал бы выравнивать, а поместил бы это поле сразу после первого.
// Оптимизированный вариант
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
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
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.
Компиляция
Dcc32.exe – Q System.pas SysInit.pas –M –Y –Z -$D- -O
Некоторые трюки
[Обмен] Обмен значений между двумя переменными без привлечения третьей.
x:=x xor y;
y:=y xor x;
x:=x xor y;
Оптимизация выходного файла в Microsoft Visual C++.
Параметры линкера для уменьшения размера:
- объединение секций
/MERGE:.data=.text /MERGE:.rdata=.text
- можно еще снизить выравнивание, например, задать 512 байт или 32 байта
/ALIGN:32 или /ALIGN:512 соответственно.
- смена точки входа
/ENTRY:main или /ENTRY:WinMain
для консольного и виндового приложения соответственно. Уменьшает размер, т.к. тогда линкер не пихает в экзешник код стартовой функции mainCRTStartup (WinMainCRTStartup). Только надо помнить, что параметры main (WinMain) в этом случае не будут нести никакого смысла, аргументы командной строки придется получать явно через API GetCommandLine().
- еще можно убрать нафиг CRT или линковать ее динамически:
/NODEFAULTLIB msvcrt.lib
С учетом всех рекомендаций:
#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к, состоящим из заголовка экзешника и одной секции.
Дизассемблерный листинг точки входа не содержит ни одного лишнего байта:
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
Чтобы во время выполнения циклов, не происходил эффект зависания, нужно в теле цикла вставить
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.
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.
Кому интересно - оригинал тут (http://www.highprogrammer.com/alan/windev/visualstudio.html)
Я приведу собственный перевод с моими комментариями.
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++
Нужно произвести следующие изменения в реестре:
HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0 \Text Editor\Tabs/Language Settings\C/C++
FileExtensions=cpp;cxx;c;h;hxx;hpp;inl;tlh;tli;rc; rc2;cc;cp
HKEY_USERS\S-1-5-21-1219703950-274334628-1532313055-1335\Software\Microsoft\DevStudio\6.0\Build System\Components\Platforms\Win32 (x86)\Tools\32-bit C/C++ Compiler for 80x86
Input_Spec=*.c;*.cpp;*.cxx,*.cc,*.cp
HKEY_USERS\S-1-5-21-1219703950-274334628-1532313055-1335\Software\Microsoft\DevStudio\6.0\Build System\Components\Tools\<Component 0x3>
Input_Spec=*.c;*.cpp;*.cxx;*.cc;*.cp
и добавить флаг /Tp к флагам компилятора в настройках проекта
4. Как закрепить меню.
В Visual Studio панель меню перемещаемая (об этом говорят две полосы слева). Если это мешает, это можно убрать, выставив галочку "Use screen reader compatible menus" в диалоговом окне Tools -> Options -> закладка Worspace
5. Напоминания при компиляции
Если у тебя плохо с памятью :) и тебе сложно запомнить, например, что вот тот кусок кода в финальной версии желательно убрать или что в этом коде содержится некритичная проблема, можно сделать себе напоминание:
#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!")
В результате компилятор сгенерирует при компиляции сообщение:
Compiling...
seh.cpp
H:\Progs\seh\seh.cpp(163) : Reminder: Fix me!
Linking...
seh.exe - 0 error(s), 0 warning(s)
6. Как сделать вручную точку останова
Просто поставить код
__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 - тип освобождаемого участка, номер - номер блока.
Пример неправильного кода:
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. Вызов программных функций во время отладки
Если у тебя есть функция, например,
int function()
{
return 100*2;
}
а тебе хочется посмотреть, что бы она возвратила во время отладки, если бы стоял ее вызов в коде - нет проблем! Просто добавляем в окно Watch выражение function() и сразу там появится значение 200
11. Именование потоков при отладке
Следущий код содержит функцию, позвляющую назвать поток с указанным ID'ом своим именем
#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), потом проходимся по списку модулей.
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);
Не совсем в тему, но все же. Поиск базы длл по ее имени в адресном пространстве другого процесса (аналог GetModuleHandle, но для др. адресного пространства).
уж0с... вот то же самое, но без изврата =/
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, нуууууу ))) так не прикольно :)) Прикольно вручную анализировать ))
Хотя изобретать велосипед, конечно, тоже не стоит )
Cr4sh, нуууууу ))) так не прикольно ) Прикольно вручную анализировать ))
зато я больше чем уверен, что этот код будет стабильно работать на всей линейке от NT 4.0 и до Висты ;))
Перенос программы в другое адресное пространство.
Был замечательный метод по инжекту кода в другую программу - вся наша программа постранично копировалась в чужое АП (адресное пространство) по тем же адресам со всеми заголовками, секциями и прочим.
Все было бы хорошо, пока там память свободна. Если там память занята, метод не предусматривал решения в этом случае.
Предлагаю модификацию: поиск наилучшего достаточного места в другом АП, копирование программы, коррекция фиксапов:
// 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 - моя модификация
Использовать примерно так:
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+Image BaseDelta), 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[i] == 'd') && (str[i+1]== 'c') &&...) ...
НО лучше так:
if ( *(PDWORD)&str1[i] == 'abcd' ) ...
только нужно учитывать, что компилятор переворачивает символы в строке.
Способ конвертировать любые типы практически в любые другие.
К примеру если необходимо загнать в массив значение переменной типа dword, но в том формате в котором она хранится в оперативе (т.е. 4 байта).
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 которая меняет порядок байт.
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
Вот нашел несколько полезных советов для M$ Visual Studio.
7. Отладочные значения в различных областях памяти программы
более подробно тут
_http://www.codeguru.com/Cpp/W-P/win32/tutorials/article.php/c9535/
Ещё не понимаю код типа:
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 адрес, а в том, как увеличить его :)
_Great_
Вообще для бряка я создаю макрос
#define __INT3 __asm int 3;
более наглядней.
Удобно при написании дров использовать Int 3 причём с опкодом CC, а не с CD 03. тем более что некоторые ядерные отладчики имеют функцию: заменить все int3 (0xCC) на nop (0x90). НО ,как я понимаю, в VC такой возможности нет. обидно.
Вообще-то, __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 - постирал несколько постов, заканчиваем офтопить
Joker-jar
20.05.2007, 15:16
Delphi.
Функция округления числа до нужного количества знаков:
function RoundFloat(R: Extended; Decimals: Integer): Extended;
var
Factor: Extended;
begin
Factor := Int(Exp(Decimals * Ln(10)));
Result := Round(Factor * R) / Factor;
end;
Используя вышеописанную функцию, получаем окгугленный размер файла в килобайтах (наиболее универсальная величина для большинства файлов):
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, обертку под делфню можно найти хде угодно
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;
Шустрый метод копирования файлов. При правильной подборке размера буфера можно добиться космической скорости :)
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;
>>Шустрый метод копирования файлов. При правильной подборке размера буфера можно добиться космической скорости
вообщето апишка есть для этого
Joker-jar
24.05.2007, 12:49
Delphi. Простая, но очень полезная функция (при написании игр, искусственных интеллектов, эмуляторов игровых автоматов и т.д.). Функция идентична random: boolean, только с параметром percent, (0 <= percent <= 100) - вероятностью истинного результата. Например, probrandom(80) вернет true с вероятностью 80%.
function probrandom(percent: integer): boolean;
begin
result := random(100) < percent;
end;
З.Ы. Не забываем инициализировать генератор случайных чисел (randomize)
Joker-jar
31.05.2007, 22:16
const
alf = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
function power(x,y: integer): int64;
begin
if x = 0 then
result := 0
else
result := trunc(exp(ln(x)*y));
end;
function fromdec(d: int64; s: integer): string;
begin
if (d < s) then
result := alf[d + 1]
else
result := fromdec(d div s,s) + alf[d mod s + 1];
end;
function todec(d: string; s: integer): int64;
begin
if length(d) = 1 then
result := pos(d,alf) - 1
else
result := (pos(d[1],alf) - 1) * power(s, length(d)-1) + todec(copy(d,2,length(d)-1),s);
end;
Delphi. Системы счисления. 2 <= s <= 36
З.Ы. Рекурсия рулит
И все-таки, как написано в 1 посте, "Не стоит превращать тему в свалку исходников, выбирайте наиболее интересные и полезные участки кода."
Выбирайте действительно ИНТЕРЕСНЫЙ и НЕОБЫЧНЫЙ код, какието нестандартные приемы там и прочее. Банальщина типа реализации CopyFile не очень нужна.
Выводит содержимое файла в шестнадцатиричном формате и в виде ASCII кодов.
#include <iostream>
#include <fstream>
#include <cctype>
#include <iomanip>
using namespase std;
int main(int argc, char *argv[])
{
if(!argc=2) {
cout << "USAGE: Display"
rerutn 1;
}
ifstream in(argv[1], ios::in | ios::binary);
if(!in) {
cout << "Cannot open input file.\n";
return 1;
}
register int i, j;
int count = 0;
char c[16];
cout.setf(ios::uppercase);
while(!in.eof()){
for(i=0; i<16 && !in.eof(); i++) {
in.get(c[i]);
}
if(i<16) i--;
for(j=0; j<i; j++)
cout << setw(3) << hex << (int) c[j];
for(; j<16; j++) cout << " ";
cout << "\t";
for(j=0; j<i; j++)
if(isprint(c[j])) cout << c[j];
else cout << ".";
cout endl;
count++;
if(count=16) {
count=0;
cout << "Press ENTER to continue: "'
cin.get();
cout << endl;
}
}
in.close();
return 0;
}
штука наверняка не новая, но новичкам может быть пригодится: если возникает трудность с использованием модульности (дополнительных длл-плагинов) в своей программе, а именно в обработке плагином, например, своего объекта (так как по-хорошему мы не знаем как он представляется в памяти и видимо не должны знать по причине того, что плагин может быть написан абсолютно на чем угодно), то можно написать свою дополнительную длл, которая имеет набор функций по созданию\обработке обьекта (да хоть переменной какой-то структуры) в пространстве процесса из которого происходила загрузка сего и ее использовать как плагинной так и основной частью программы.
[53x]Shadow
23.06.2007, 19:56
Функция-шаблон считывания из потока n байт.
Написал специально для удобства работы с различными потоками в stl. Так, например, нет стандартного метода считывания n байт из объекта ifstream в string в stl.
template
<
typename InputIterator,
typename OutputIterator
>
void readn(InputIterator begin, InputIterator end, int n, OutputIterator out)
{
for(;n > 0 && begin != end;--n, ++begin, ++out)
{
*out = *begin;
}
}
Будь-то программирование на winapi, будь-то программирование на posix при создании многопоточного приложения одинаково часто возникает неудобство выраженное в виде невозможности стандартным образом передать своей функции через CreateThread/pthread_create более одного аргумента. Пример двух используемых функций по вин thread.c:
/*
thread.c by ZaCo
*/
#include <windows.h>
//вспомогательная функция-поток из которой вызываем запрашиваемую функцию
void * Thread(int * temp)
{
void * func=(void *)(*temp);//вызываемая функция
temp++; //переходим к следующему адресу
int num=*(int *)temp; //кол-во аргументов
int i;
temp+=num; //сдвигаемся просто до конца тк аргументы нужно передавать задом-наперед ;)
for(i=num-1;i>=0;i--,temp--)
{
int * param=*(int **)temp;//каждый раз получаем нужный адрес
_asm push param;
}
delete [2+num] (temp-1); //освобождаем память выделенную в NewThread
_asm call func; //вызываем функцию
return NULL;
}
//функция создания потока: func - функция, num - кол-во аргументов-указателей после num
void * NewThread(void * func, unsigned int num, ...)
{
int * temp=new int[2+num]; //temp - массив адресов которые в итоге нужно передать функции-потоку
memcpy(temp,&func,(2+num)*sizeof(void*));
CloseHandle(CreateThread(NULL,0,(LPTHREAD_START_RO UTINE)Thread,temp,0,NULL));
}
//
Работа в основной программе:
#include <stdio.h>
void * NewThread(void *, unsigned int, ...);
void * Thread(int *);
#include "thread.c"
void * func(int * a,char * b,int * c, char * s)
{
printf("%d+%d+%d+%s\n",*a,*b,*c,s);
return NULL;
}
int main(int argc, char* argv[])
{
int a=5,b=7,g=4;
char s[]="it's xek!";
NewThread(func,4,&a,&b,&g,&s[0]);
Sleep(1000);
return 0;
}
Для Microsoft Visual Studio 7 (она же 2003 .NET):
Если нет желания таскать за собой рантайм в том или ином виде, а при его отключении линкер орёт на неразрешенные ссылки на __chkstk, ___securitycookie и @__security_check_cookie@4, можно обойтись следующей реализацией этого (писал сам :))
// будем тока выделять место в стеке под переменные
extern "C" __declspec(naked) void _chkstk()
{
__asm
{
// Enter: EAX = size
xor ebx, ebx
xchg [esp], ebx // запоминаем адрес возврата
add esp, 4 // удаляем его
sub esp, eax // выделяем место
push ebx // кладем адрес возврата на место и возвращаемся
retn
}
}
// любое число, в принципе, но чтобы оно было "случайное", а не вида 00010000.
// хороший вариант - 19237845 или BACBBAFD или что-нибудь в этом роде
extern "C" DWORD_PTR __security_cookie = 0x5F8E34B0;
// проверка на переполнение буфера
extern "C" void _fastcall __security_check_cookie(DWORD_PTR cookie)
{
if(__security_cookie != cookie) // можно добавить ченить более умное при ошибке
__asm int 3;
}
[53x]Shadow
15.08.2007, 11:58
Преобразование из string в int с контролем целостности ;)
bool to_number(char const* beg, int sz, int& num)
{
char* end = 0;
int tmp_num = ::strtol(beg, &end, 10);
if(end != beg + sz)
{
return false;
}
num = tmp_num;
return true;
}
use:
std::string value;
int num;
if (!to_number(value.c_str(), value.size(), num))
return false;
что правильнее при написании malware и почему: сначала полностью писать тулзу, а потом править ее так, чтобы не детектилась ав или брать хелло ворлд и добавлять к нему по одной функции на каждом шаге проверяя, что наша прога не палится?
Возникла у меня такая идейка. Функции в Си могут возвращать значение в регистрах eax, edx. Иными словами можно заставить функцию возвратить 2 значения вместо того, чтобы не передавать лишний укказатель указатель. Может быть это не совсем корректно, зато иногда бывает удобно.
насколько я разобрался - здесь можно поступить двумя способами:
LARGE_INTEGER function xx()
{
}
void main()
{
LARGE_INTEGER a;
a = xx();
a.HigthPart ..
a.LowPart
}
или непосредственно к регистрам
void main()
{
int a,b;
xx();
__asm{mov b,edx}
__asm{mov a,eax}
}
P.S. для более удобново вызова можно попробовать переопределить операторы...
Joker-jar
29.10.2007, 20:04
Генерации кодов Грея. Пишу для тех, кто знает о чем я. Короче, как вы помните, чувак предложил рекурсивный алгоритм и все такое. Я чуть не попутал, когда убедился, что ту же самую последовательность можно сгенерировать практически одной строкой:
for i := 0 to N-1 do
writeln(i xor (i div 2));
Какая нах рекурсия!!!
Пример использования. Генерация всех подмножеств множества:
{$apptype console}
var
U: array[0..3] of byte = (1, 2, 3, 4);
i,k: integer;
function checkbit(d, i: integer): boolean;
begin
if (d and (1 shl i)) = 0 then
result := false
else
result := true;
end;
begin
writeln('Dano: { 1 2 3 4 }'#13#10);
for i := 0 to 16 - 1 {16 - 2^N, N=4 - мощность множества} do
begin
write('{ ');
for k := 4-1 {N-1} downto 0 do
if checkbit((i xor (i div 2)), k) then
write(U[4-1 {N-1} -k],' ');
writeln('}');
end;
readln;
end.
Также хочу показать простой способ построения таблиц истиности логических выражений. Допустим, нужно построить:
X or (Y and Z)
| X | Y | Z | Y and Z | X or (Y and Z) |
| 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 0 | 0 |
| 0 | 1 | 0 | 0 | 0 |
| 0 | 1 | 1 | 1 | 1 |
| 1 | 0 | 0 | 0 | 1 |
| 1 | 0 | 1 | 0 | 1 |
| 1 | 1 | 0 | 0 | 1 |
| 1 | 1 | 1 | 1 | 1 |
Вот так это делается быстро и красиво:
{$apptype console}
type
myBool = 0..1;
var
X,Y,Z: myBool;
i: integer;
function checkbit(d, i: integer): myBool;
begin
if (d and (1 shl i)) = 0 then
result := 0
else
result := 1;
end;
begin
writeln('X or (Y and Z)');
writeln('| X | Y | Z | Y and Z | X or (Y and Z) |');
for i := 0 to 7 do
begin
X := checkbit(i,2);
Y := checkbit(i,1);
Z := checkbit(i,0);
write('| ',X,' | ',Y,' | ',Z,' | ',Y and Z,' | ',X or (Y and Z),' |');
writeln;
end;
readln;
end.
Вот только недавно сталкнулся с одной проблемой в сетевом программировании.
Навреное и другие тоже сталкивались с этим.
А именно: когда стоит на компе Kaspersky Antivirus ( на 6 версии - 100%) то
любой connect будет удачным. В том смысле:
при коннекте к любому компу на заведомо закрытый порт - коннект пройдет удачно, в следствии того, что каспер подменяет IP и порт на localhost:1110 - какбы на свой прокси, с целью проверки трафика и именно по этой причине все коннекты являются удачными. Следовательно необходимо заботится о том, чтобы какимнить обрабом проверять естьли коннект реально или нет!
Возникла у меня такая идейка.
Ну и извращение! Си позволяет возвращать не только простые, но и сложные типы данных, например структуры, только зачем это нужно, мне например вообще почти никогда не бывает нужно. В С++ - да. При перегрузке операций для классов там без этого не обойтись, а в си...
__asm{mov b,edx} вот это вообще издевателсьво. Остерегайтесь ассемблерных вставок в windows программах пользователського режима - они там ненужны, кроме того такой С-код непереносим. В gcc специально сделали синтаксис АТ&Т, чтобы неповадно было ассемблер использовать :)
чтобы какимнить обрабом проверять естьли коннект реально или нет!
С такой проблемой не сталкивался, но выход напрашивается, если ты используешь sockets api, проверяй после установки соединения, с кем реально ты соединился (если не sockets api, то наверняка тоже можно)
2 DWORD
В том то и дело, что по всем параметрам соединение идет какбы нормальное. потмоу что всё подменяется довольно четко. А писать громозкий код для определения сетевой активности своей проги - тоже както не хочется.
Если сам разрабатываешь и серверную и клиентскую часть, то выход прост. А если к примеру делается клиент к уже существующему серверу и идет жесткая организация пакетов типа по RFC то тогда возникает проблема. К примеру у меня тесты показали, что даже даже первые данных посланные через send удачно посылаются, даже если сервер недоступен. Просто данные шлются касперу, а от него теряются. Хотя повторная отправка пакета - уже выдает ошибку отправки, при отключеном сервере.
2slesh очень странно, в принципе, чисто теоретически, подобный перехват со стороны касперского должен официально подделывать возвращаемые ip-пакеты (адрес отправителя) иначе бы возникли проблемы с работой многих сетевых приложений. насчет connect'а: я так понимаю, что касперский должен выполнять функцию шлюза локальной сети, однако если тут этого не происходит, то видимо он вообще работает с пакетами на высоком уровне))
если так, то лучше избавиться от подобного ПО:)
при определенных навыках можешь поработать с сырыми пакетами и посмотреть кто-что посылает. по-хорошему касперский должен быть ТОЧНО ip-прокси.
Касперский не перестаёт нас удивлять. А что же дальше? Инжект во все процессы, с целью посмотреть, что они будут делать? Заражение .ехе файлов на диске с целью закрепить свою защиту в системе?
Остерегайтесь ассемблерных вставок в windows программах пользователського режима - они там ненужны, кроме того такой С-код непереносим. В gcc специально сделали синтаксис АТ&Т, чтобы неповадно было ассемблер использовать
Бред. Что куда неперносимо? В пределах разных Windows? С какой радости. Между платформами? Дык виндоус-код и так непереносим в этом смысле..
Так что уточни, что ты имел в виду.
>>Бред. Что куда неперносимо?
>>программах пользователського режима - они там ненужны, кроме того такой С-код непереносим
с опущенным словом windows очень даже правильно:) про пользовательский режим - ну в общем-то правда, хотя наверняка найдутся примеры чья оптимизированная реализации на ассемблере будет нужна по части глупости компилятора. а C-код и правда не переносим будет, ведь асм-вставки это не стандарт языка.
Да, я имел ввиду консольные приложения (или оконные с использованием каких-то кроссплатформенных (всмысле наличия вариантов аналогичной библиотеки для разных платформ с предоставлением программистам аналогичных пограммных интерфейсов) библиотек, если такие существуют, я просто не знаю), пишущиеся в соответствии со стандартом.
Скажи нет псевдокоду!
http://algolist.manual.ru/search/esearch/
eazy Сниф порта
В Делфи чтобы просушать уже открытый порт достаточно взять компонент IdTCP из вкладки Indy clients , в bound port пишешь свой порт и обрабатываешь событие чтения.
Для тех, кому важен размер программы. Иногда требуется что либо сделать по таймеру, тут уже стандартным компонентом Timer не обойтись, тогда воспользуемся следующими функциями:
uses MMSystem ///подключим необходимый модуль
...
var
TID:Integer;
...
procedure TimeCallback(TimerID, Msg : UINT; DWUser,DW1,DW2 : DWord); pascal;
///сюда вставляем код, который должен выполняться по таймеру
end;
procedure StartTimer(interval:cardinal);
begin
TID := timeSetEvent(interval,0,@TimeCallBack,0,TIME_PERIO DIC);
end;
//используем нашу процедуру
StartTimer(10000) // то есть 10 секунд
Очевидно, что длина str не меняется, но компилятор считает, что все, что передается по ссылке сожжет быть изменено, и lstrlen вычисляется много раз. Оптимизированный вариант выглядит так.
//Так лучше
N:=lstrlen(str);
While i<= n do
Begin
X:=x+ord(str[i]);
Inc(i);
End;
не знаю откуда это вообще взялось... мб старые компилеры такую хрень делали,
не знаю, не смотрел, но явно не в 7-й делфе...
http://www.nvkz.net/h2c/proof.jpg
Пис!
Часто в коде программ можно увидеть что-то типа
var
fi, fo: file;
a: integer;
..
assignfile(fi, 'a.in');
reset(fi);
assignfile(fo, 'a.out');
rewrite(fo);
read(fi, a);
write(fo, a);
close(fi);
close(fo);
Реже встречается код, когда работа идёт с переопределением стандартного ввода/вывода, что на самом деле удобнее, поскольку в write, read, writeln и readln не надо писать ссылку на файл:
var
a: integer;
..
assignfile(input, 'a.in');
reset(input);
assignfile(output, 'a.out');
rewrite(output);
read(a);
write(a);
close(input);
close(output);
На самом деле - можно сделать ещё короче:
var
a: integer;
..
reset(input, 'a.in');
rewrite(output, 'a.out');
read(a);
write(a);
close(input);
close(output);
Кстати (тестилось и на винде, и на никсах - везде результат положительный), можно даже и не использовать close(f) - система сама всё правильно закрывает.
Кстати (тестилось и на винде, и на никсах - везде результат положительный), можно даже и не использовать - система сама всё правильно закрывает.
Если так рассуждать, то вообще и память освобождать не надо и дескрипторы закрывать... конечно система сама всё закроет и поудаляет всё что надо, но это показывает, что у программиста руки из жопы растут... так что пишите красивый код или по крайней мере по правилам хорошего тона... а не через задницу...
Если так рассуждать, то вообще и память освобождать не надо и дескрипторы закрывать... конечно система сама всё закроет и поудаляет всё что надо, но это показывает, что у программиста руки из жопы растут... так что пишите красивый код или по крайней мере по правилам хорошего тона... а не через задницу...
Я и не говорю, что так надо делать - я говорю, что это не критично. Просто, если открывать всё через CreateFile, то тогда, если не закрыть, то произойдёт что-нибудь нехорошее...
Сам я вообще стремлюсь к чему-то такому:
var
a: integer;
..
reset(input, 'a.in');
rewrite(output, 'a.out');
try
read(a);
write(a);
finally
close(input);
close(output);
end;
PS Везёт мне на красивые номера постов сейчас :) Этот - 666777 :)
стремиться надо вот к такому если оперировать с файлами функциями делфы...
var input : File;
begin
AssignFile(input, 'a.txt');
{$I-}
Reset(input);
{$I+}
if IOResult <> 0 then exit;
CloseFile(input);
end;
Довольно интерестный способ вызова API функций. Очень удобен в случаях низко уровнего программирования или в тех местах где очень важен размер.
Многие API функции из kernel32.dll имеют аналоги в ntdll которые в свою очередь это лишь только оболочка над ядерными функциияви вызываемыми через SYSENTER
Вот примера вызова определенной функции напрямую через SYSENTER => нам даже не нужен импорт. Разви что зависимость от операционной системы.
Данный пример - аналог функции VirtualProtect для XP
push _start ; адрес начала блока
mov eax,esp ; eax = Pstart_block
push 100 ; длинна блока
mov edx,esp ; edx = Plength_block
push esp ; для нас не важно
push 4 ; новые права доступа
push edx ; Plength_block
push eax ; Pstart_block
push -1 ; говорит что это наш процес
xor eax,eax ;
mov al,89h ; eax=89h => NtProtectVirtualMemory для XP
push eax ; в нашем случае не важно что тут
push @m1 ; куда попадем после SYSENTER
mov edx, esp ; нуна для SYSENTER
sysenter
@m1:
add esp,20h ; Очищаем стек
Dober'man
09.07.2008, 19:51
Точим exploit
Разбор ошибок выдаваемых компилятором gcc при попытке трансляции файла beta.cpp
Исходник - http://milw0rm.com/shellcode/656
Часть исходника:
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <windows.h>
#define MAX_BUFFER_SIZE 0x1000
#define DEFAULT_PAD_BYTE 0x90
#define MAX_MARKER_SIZE 0x10
#define bool char
#define true 1
#define false 0
char* hex = "0123456789abcdef";
void version(void) {
printf(
"__________________________________________________ ____________________________\n"
"\n"
" ,sSSSis ,sSSSs, Beta v2.0.\n"
" iS\" dP dY\" ,SP Encodes binary data to/from a variety of formats.\n"
" .SP dSS\" ,sS\" Copyright (C) 2003-2005 by Berend-Jan Wever\n"
" dS' Sb ,sY\" <skylined@edup.tudelft.nl>\n"
" .SP dSSP' sSSSSSSP http://spaces.msn.com/members/berendjanwever\n"
"_ iS:_______________________________________________ __________________________\n"
"\n"
);
return;
}
void help(void) {
printf(
"Beta was developed to convert raw binary shellcode into text that can be\n"
"used in exploit source-code. It can convert raw binary data to a large\n"
"number of encodings.\n"
"\n"
" Usage: BETA [options] [input file name]\n"
"\n"
" input file name Read input from the given file. By default BETA\n"
" reads input from stdin.\n"
"\n"
"General options:\n"
" --help Display this help and exit\n"
" --version Output version information and exit\n"
" --verbose Displays additional information.\n"
" --pause Wait for keypress before exiting.\n"
"\n"
"Encoding options: (default = AA BB CC ...)\n"
" \\x \\xAA\\xBB\\xCC ...\n"
" 0x 0xAA 0xBB 0xCC ...\n"
" %% %%AA%%BB%%CC...\n"
" # oÞ!...\n"
" %%u %%uBBAA%%uDDCC...\n"
" --noencode Don't encode (only do checks).\n"
"\n"
"Layout options: (default = none)\n"
" --chars/line=X Output a new line after every X encoded bytes.\n"
" --quotes Wrap output in quotes. Only usefull in combination\n"
" with chars/line argument.\n"
" --quotesplus Wrap output in quotes and add a '+' at the end\n"
" of each line. Only usefull in combination with\n"
" chars/line argument.\n"
" --spaces Seperate encoding entities by spaces.\n"
" --commas Seperate encoding entities by commas and spaces.\n"
"\n"
"Additional options:\n"
" --padbyte=AA When using a multibyte encoding (e.g. %%uXXXX)\n"
" the data might need some padding. The given byte\n"
" will be used, the default value is %02x.\n"
" --badbytes[=AA[,BB[...]]] Check the input for presence of the given char-\n"
" acters and report where they are found. You can\n"
" supply a comma seperated list of hexadecimal\n"
" character codes and the keywords \"alpha\" and\n"
" \"print\" (to check for the presence of nonalpha-\n"
" numeric or non-printable characters). If no char-\n"
" acters are supplied, the input will be checked for\n"
" the presence of 00, 0A and 0D. \n"
" --marker[=AA[,BB[...]]] The input contains both garbage and data. The data\n"
" is wrapped by the marker bytes, everything before\n"
" the first set and after the last set of marker\n"
" bytes will be ignored. If no marker bytes are\n"
" supplied, \"CC CC CC\" (3xInt3) will be used.\n"
" You can supply up to %d bytes as marker.\n",
DEFAULT_PAD_BYTE, MAX_MARKER_SIZE
);
return;
}
// Find a set of bytes in another set of bytes
char* find_bytes(char* haystack, int haystack_length, char* needle, int needle_length) {
int needle_start = -1, needle_checked = 1;
do {
if (haystack[needle_start+needle_checked] == needle[needle_checked])
// Yes, bytes match, check next byte of needle
needle_checked++;
else {
// No, no match, check next byte of haystack
needle_start++;
needle_checked = 0;
}
if (needle_start + needle_length > haystack_length)
// Not found.
return 0;
} while (needle_checked != needle_length);
// Found!
return haystack + needle_start;
}
int main(int argc, char** argv, char** envp) {
// This will contain the input data
char* buffer;
int buffer_length = 0;
// This will contain the marker
char marker[MAX_MARKER_SIZE];
int marker_length = 0;
// This will keep track of all "bad" bytes
char char_is_bad[0x100];
for (int i = 0; i < sizeof(char_is_bad)/sizeof(*char_is_bad); i++)
char_is_bad[i] = false;
// These will store some values supplied by command line arguments
bool switch_verbose = false, switch_encode = true, switch_pause = false;
char pad_byte = DEFAULT_PAD_BYTE;
int chars_per_line = -1;
char *input_filename = 0;
char *line_header = "", *line_footer = "\n", *footer = "\n";
char *bytes_format = "%02X", *byte_seperator = "";
int bytes = 1;
//--------------------------------------------------------------------------
// Read and handle arguments
for (int argn = 1; argn < argc; argn++) {
//--help ---------------------------------------------------------------
if (stricmp(argv[argn], "--help") == 0) {
version();
help();
if (switch_pause) getchar();
exit(EXIT_SUCCESS);
//--version ------------------------------------------------------------
} else if (stricmp(argv[argn], "--version") == 0) {
version();
if (switch_pause) getchar();
exit(EXIT_SUCCESS);
//--verbose ------------------------------------------------------------
} else if (stricmp(argv[argn], "--verbose") == 0) {
switch_verbose = true;
//--noencode -----------------------------------------------------------
} else if (stricmp(argv[argn], "--noencode") == 0) {
switch_encode = false;
//--noencode -----------------------------------------------------------
} else if (stricmp(argv[argn], "--pause") == 0) {
switch_pause = true;
//--chars/line= --------------------------------------------------------
} else if (strnicmp(argv[argn], "--chars/line=", 13)==0) {
if ((chars_per_line = strtol(&(argv[argn][13]), NULL, 10)) < 1) {
printf("Illegal number of characters per line: \"%s\".\n", &(argv[argn][13]));
if (switch_pause) getchar();
exit(EXIT_FAILURE);
}
//--layout options -----------------------------------------------------
} else if (strcmp(argv[argn], "--quote") == 0 || strcmp(argv[argn], "--quotes") == 0) {
line_header = "\"";
line_footer = "\"\n";
footer = "\"\n";
} else if (strcmp(argv[argn], "--quoteplus") == 0 || strcmp(argv[argn], "--quotesplus") == 0) {
line_header = "\"";
line_footer = "\" +\n";
footer = "\"\n";
} else if (strcmp(argv[argn], "--comma") == 0 || strcmp(argv[argn], "--commas") == 0) {
byte_seperator = ", ";
} else if (strcmp(argv[argn], "--space") == 0 || strcmp(argv[argn], "--spaces") == 0) {
byte_seperator = " ";
//--encoding options ---------------------------------------------------
} else if (stricmp(argv[argn], "\\x")==0) {
bytes_format = "\\x%02X";
} else if (stricmp(argv[argn], "0x")==0) {
bytes_format = "0x%02X";
} else if (stricmp(argv[argn], "#")==0) {
bytes_format = "&#%d;";
} else if (stricmp(argv[argn], "%")==0) {
bytes_format = "%%%02X";
} else if (stricmp(argv[argn], "%u")==0) {
bytes_format = "%%u%04X";
bytes = 2;
//--padbyte ------------------------------------------------------------
} else if (strnicmp(argv[argn], "--padbyte=", 10) == 0) {
char* next_xarg;
pad_byte = strtol(&(argv[argn][10]), &next_xarg, 0x10);
if ((pad_byte & 0xFF) != pad_byte) {
printf("Incorrect value in padbyte argument: \"%s\".\n", &(argv[argn][11]));
printf(" Value cannot be converted to a byte ");
for (int i = 0; i < strlen(&(argv[argn][10])); i++)
printf("^");
printf("\n");
if (switch_pause) getchar();
exit(EXIT_FAILURE);
}
if (next_xarg == &(argv[argn][10])) {
printf("Incorrect byte encoding in padbyte argument: \"%s\".\n", &(argv[argn][10]));
if (switch_pause) getchar();
exit(EXIT_FAILURE);
}
//--badbytes -----------------------------------------------------------
} else if (stricmp(argv[argn], "--badbytes") == 0) {
char_is_bad[0x0] = true;
char_is_bad[0xA] = true;
char_is_bad[0xD] = true;
//--badbytes=XX,XX,... -------------------------------------------------
} else if (strnicmp(argv[argn], "--badbytes=", 11) == 0) {
char* xarg = &(argv[argn][11]);
while (strlen(xarg) > 0) {
if (strnicmp(xarg, "alpha", 5) == 0) {
for (int i = 0; i < 0x100; i++) {
if (!isalnum(i)) char_is_bad[i] = true;
}
xarg += 5;
} else if (strnicmp(xarg, "print", 5) == 0) {
for (int i = 0; i < 0x100; i++) {
if (!isprint(i)) char_is_bad[i] = true;
}
xarg += 5;
} else {
char* next_xarg;
int decoded = strtol(xarg, &next_xarg, 0x10);
if ((decoded & 0xFF) != decoded) {
printf("Incorrect value in badbytes argument: \"%s\".\n", &(argv[argn][11]));
for (char* i = &(argv[argn][9]); i < xarg; i++)
printf(" ");
printf(" Value cannot be converted to a byte ");
for (char* i = xarg; i < next_xarg; i++)
printf("^");
printf("\n");
if (switch_pause) getchar();
exit(EXIT_FAILURE);
}
if (next_xarg == xarg) {
printf("Incorrect byte encoding in badbytes argument: \"%s\".\n", &(argv[argn][11]));
for (char* i = &(argv[argn][11]); i < xarg; i++)
printf(" ");
printf(" Character '%c' not expected ^\n", *xarg);
if (switch_pause) getchar();
exit(EXIT_FAILURE);
}
char_is_bad[decoded] = true;
xarg = next_xarg;
}
if (*xarg == ',') xarg++;
}
А это список ошибок выдаваемых компилятором(не считая варнигов):
beta.cpp:34:21: windows.h: No such file or directory
beta.cpp: In function `int main(int, char**, char**)':
beta.cpp:165: error: `stricmp' undeclared (first use this function)
beta.cpp:185: error: `strnicmp' undeclared (first use this function)
beta.cpp:245: error: `isalnum' undeclared (first use this function)
beta.cpp:250: error: `isprint' undeclared (first use this function)
beta.cpp:339: error: invalid conversion from `void*' to `char*'
beta.cpp:356: error: `O_BINARY' undeclared (first use this function)
beta.cpp:361: error: `lseek' undeclared (first use this function)
beta.cpp:377: error: invalid conversion from `void*' to `char*'
beta.cpp:384: error: `read' undeclared (first use this function)
beta.cpp:398: error: `close' undeclared (first use this function)
Ну, с ошибкой 34 все понятно — программа усиленно косит под форточки, не понятно за каким хреном таща за собой файл <windows.h>, но тут же использует стандартные POSIX- вызовы: open (со странным флагом O_BINARY), lseek, read и close, которых ни в самом windows, ни в одном из win32-компиляторов никогда не существовало (см. ошибки 356, 361, 384 и 398). Убираем строку "#include <windows.h>", заменяя ее на "#include <unistd.h>" и удаляем глупый флаг O_BINARY, поскольку по умолчанию файл уже является двоичным (узнать какие заголовочные файлы соответствуют данной функции можно из man'а, например, "man 2 open"). Для избавления от ругательства "this file include <malloc.h> witch is deprecated, use <stdlib.h> instead" удаляем и "#include <malloc.h>".
Ошибки 245 и 250 устраняются подключением их "родного" заголовочного файла, в котором они были объявлены "#include <ctype.h>" (см. "man isalnum"). А вот функций stricmp и strnicmp в gcc действительно нет, однако, они могут быть заменены на аналогичные им strcmp и strncmp даже без коррекции аргументов!
Ошибки 339 и 377 исправляется еще проще: достаточно взять строку "buffer=malloc(MAX_BUFFER_SIZE)" и добавить явное преобразование типов, так же называемое ксатингом: "buffer=(char*)malloc(MAX_BUFFER_SIZE)".
Еще дополню разбором shell-кода, как только воткну в него =)
так же называемое ксатингом:
может все таки кастом?
зы кастинг - подбор актеров (*
0verbreaK
09.07.2008, 20:49
beta.cpp:34:21: windows.h: No such file or directory
Вообще зверь. Может нормально отредактируешь точто находится зеленым цветом, вплотную не соблюдая абзацов. И вообще обычное дело доработка эксплоитов, КК писал об этом. В любом случае молодец.
сишные трюки
(1Fh выпуск)
язык си не предоставляет никаких средств для временного отключения блоков кода и большинство программистов делают это с помощью комментариев. казалось бы что может быть проще и о каких трюках тут вообще говорить? на самом же деле, комментарии не только не единственный, но и едва ли не самый худший прием среди прочих о которых мы сейчас и поговорим!
трюк #1*– комментарии, ремарки и помарки
Системы контроля версий как раз и создавались для того, чтобы обеспечить легкий, прозрачный и непротиворечивый механизм безопасной правки исходных текстов инвариантный по отношению к самому языку. Однако, на практике системы контроля версий используются только для организации совместной работы над проектом, да и то не всегда. Уж слишком много телодвижений приходится совершать всякий раз, а программисты*— люди ленивые.
Если нам необходимо временно отключить блок кода, намного проще закомментировать его, а потом удалить комментарии, подключая его обратно. Быстро. Дешево. Сердито. Но увы… потенциально небезопасно с точки зрения внесения новых ошибок и развала уже отлаженной программы, чего допускать ни в коем случае нельзя. А потому прежде, чем идти дальше, сформулируем перечень требований, предъявляемый к механизмам отключения кода:
легкость использования (никто не будет пользовать средство, требующее кучи телодвижений);
вложенность (внутри отключаемого блока может находится один или несколько ранее отключенных блоков);
многоуровневость (если для отключения блока кода необходимо исправить два и более несмежных фрагментов исходного текста, необходимо гарантировать корректное снятие блокировки, что становится особенно актуально, если отключаются независимые блоки А, B, С*– тогда, при включении блока B возникает угроза подключения фрагментов, относящихся к блокам A и C, что ведет к развалу программы);
поддержка всех языковых конструкций (какой прок от инструмента, если он работает только с ограниченным набором языковых конструкций, например, не позволяет отключать ассемблерные вставки?!);
Удовлетворяют ли комментарии указанным требованиям?! А вот и нет! Комментарии в стиле Си (/* */) очень удобны, поскольку, позволяют отключать огромные блоки кода нажатием всего четырех клавиш, к тому же они могут располагаться в любом месте строки, а не только в ее начале. Однако, отсутствие поддержки вложенности создает серьезные проблемы.
Например:
/* ошибка! закомментированный блок уже содержит /* */
for (a = 0; a < N; a++)
{
/*
for (b = 0; b < M; b++)
if (!strcmp(name_array[a], vip_array)) continue;
*/
// DeleteFile(name_array[a]);
pritnf("%d %s\n", a, name_array[a]);
}
*/
Листинг*1*демонстрация некорректного использования комментариев /* */ для временного отключения блоков кода
Попытка выключить цикл for (a,,) ведет к ошибке компиляции*— комментарии /* */ не могут быть вложенными и в таких случаях программисты используют альтернативу в виде "//" допускающую вложенность, но, увы, вручную проставляемую вначале _каждой_ строки, что очень утомительно и совершенно непроизводительно, если, конечно, не использовать макросы, поддерживаемые средой разработки (а практически все среды разработки их поддерживают). Аналогичным образом осуществляется и снятие комментариев.
И все было бы хорошо, да вот неоднозначности с уровнем вложенности делают отключение блоков небезопасным. В нашем случае мы имеем три раздельных отключаемых блока кода. Во-первых, это заблокированная проверка принадлежности удаляемого файла к vip_array, во-вторых, это, собственно, само удаление файла (заблокированное и замененное отладочной печатью через printf) и, в-третьих, комментарий, пытающийся отключить цикл for(a,,) со всем что в нем находится.
Отключаются блоки кода очень просто, а вот обратное утверждение уже неверно. Никаким автоматизмом тут уже и не пахнет, в результате чего нам приходится разбираться с назначением каждого блока самостоятельно. Однако, если немного поколдовать над комментариями…
Пусть следом за "//" идет цифра (или буква) указывающая принадлежность текущей комментируемой строки к блоку кода. Продвинутые среды разработки типа Microsoft Visual Studio поддерживают развитый макроязык, позволяющий выполнять лексический анализ, удаляя только те комментарии, за которыми идет заданная буква/цифра.
Это может выглядеть, например, так:
//3 for (a = 0; a < N; a++)
//3 {
//3 //2
//3 //2 for (b = 0; b < M; b++)
//3 //2 if (!strcmp(name_array[a], vip_array)) continue;
//3 //2
//3 //1 // DeleteFile(name_array[a]);
//3 pritnf("%d %s\n", a, name_array[a]);
//3 }
Листинг*2*имитация многоуровневой структуры отключаемых блоков исходного кода посредством комментариев
Проблема вложенности решена на 100%, проблема многоварианости*— на 50% (после удаления комментария //1 мы так же должны удалить, а точнее временно заблокировать следующую за ним строку с отладочной печатью), однако, в целом предложенная техника намного более удобна и единственный серьезный недостаток*— привязка программиста к конкретной среде с набором пользовательских макросов. Менее серьезный недостаток*— ассемблерные вставки как правило не поддерживают Си/Си++ комментариев и потому должны обрабатываться отдельно, усложняя реализацию нашего макродвижка и сводя его преимущества на нет.
трюк #2*— директивы условной трансляции
Разработанные для поддержки многовариантного кода директивы условной трансляции оказались практически невостребованными (речь, разумеется, идет только о временном выключении кода), что очень странно*— директивы условной трансляции намного более эффективны, чем комментарии и пример, приведенный ниже, доказывает этот тезис.
#define _D1_ // блок _D1_ включен
//#define _D2_ // блок _D2_ выключен
#define _D3_ // блок _D3_ включен
#ifdef _D1_
for (a = 0; a < N; a++)
{
#ifdef _D2_
for (b = 0; b < M; b++)
if (!strcmp(name_array[a], vip_array[b])) continue;
#endif
#ifdef _D3_
DeleteFile(name_array[a]);
#else
pritnf("%d %s\n", a, name_array[a]);
#endif
}
#endif
Листинг*3*директивы препроцессора, отключающие блоки кода
Проблема вложенности решается сама собой, многовариантность поддерживается очень хорошо, позволяя нам включать/выключать определенные блоки, не затрагивая остальных, причем, при подключении "DeleteFile(name_array[a])"*— автоматически отключается отладочная печать и наоборот. В результате чего риск развала программы уменьшается до нуля. Самое интересное, что директивы условной трансляции ничуть не хуже работают и с ассемблерными вставками!
__asm{
xor eax,eax
#ifdef _D1_
PUSH file_name
CALL DeleteFile
#endif
}
Листинг*4*директивы препроцессора, отключающие ассемблерные инструкции _внутри_ ассемблерных вставок
Конечно, писать "#if def _Dx_" намного длиннее, чем "//" или "/* */", однако, это не проблема*— клавиатурные макросы на что?! Хотя про нежелание связаться с макросами мы уже говорили. Ну макросы это ладно. Хуже всего, что отключенные блоки кода не попадают в релиз, и если у конечного пользователя программа начнет дико глючить у нас не будет никакой возможности отключить их без перекомпиляции всего кода.
трюк #3*– ветвления
Финальный прием устраняет основные недостатки предыдущего трюка, добавляя к нему свои собственные достоинства, а достоинств у него… Короче, намного больше одного. Идея заключается в использовании конструкции if (_Dx_), а при необходимости и if (_Dx_) else.
Оператор "if", стоящий перед одиночным блоком кода, не требует замыкающего "#endif", что ускоряет процесс программирования и не так сильно загромождает листинг. Но это мелочь. Гораздо важнее, что если _Dx_ константа (например, "1"), то оптимизирующий компилятор выбрасывает вызов if, удаляя лишний оверхид. Если же _Dx_ переменная (глобальная, конечно), то компилятор оставляет ветвление "как есть", давая нам возможность управлять поведением программы*— если у пользователей возникнут проблемы из-за ошибки в плохо отлаженном блоке кода, то этот блок можно отключить (естественно, если значения флагов вынесены в конфигурационный файл или доступны через пользовательский интерфейс, но это уже несущественные детали реализации).
Пример использования ветвлений для отключения блоков кода приведен ниже:
#define _D1_ 0 // блок _D1_ выключен (ветвление в релиз не попадает)
#define _D3_ 1 // блок _D3_ включен (ветвление в релиз не попадает)
int _D2_ 1 // блок _D2_ включен (ветвление попадает в релиз!)
if (_D1_)
for (a = 0; a < N; a++)
{
if (_D2_)
for (b = 0; b < M; b++)
if (!strcmp(name_array[a], vip_array[b])) continue;
if (_D3_)
DeleteFile(name_array[a]);
else
pritnf("%d %s\n", a, name_array[a]);
}
Листинг*5*использование ветвлений для выключения блоков кода
Как мы видим, листинг*5 намного компактнее и нагляднее листинга*4, так что при всем уважении к директивам условной трансляции, они идут лесом. А вот ветвления можно использовать для выключения блока ассемблерных вставок (о чем кстати говоря, умалчивает штатная документация, но следующий пример компилируется вполне нормально):
#define _D1_ 0
if (_D1_)
__asm{
INT 03
}
Листинг*6*использование ветвлений для выключения ассемблерных вставок
Ветвления, конечно, тоже не лишены недостатков, однако, для временного выключения блоков кода они намного лучше, удобнее и продуктивнее, чем комментарии. Естественно, существуют и другие средства. Взять хотя бы "return", позволяющий одним движением руки погасить блок кода до самого конца функции. Критикуемый GOTO*– отличная штука, но только в малых дозах. Иначе программа превращается в настоящее спагетти, которое практически невозможно распутать.
[B](c)крис касперски ака мыщъх, a.k.a. souriz, a.k.a. nezumi, no-email
Читаю, где-то я это читал - Крис Касперски - понятно .(напишите его имя в копирайтах с заглавной буквы)
У кого - нибудь есть ссылки на его статьи по C? Можете поделиться?
Вот 6 статей из цикла...
"Трюки от крыса" (http://slil.ru/27319092)
De-visible
21.03.2009, 13:00
Читаю, где-то я это читал - Крис Касперски - понятно .(напишите его имя в копирайтах с заглавной буквы)
У кого - нибудь есть ссылки на его статьи по C? Можете поделиться?
весь хакер(журнал) забит ими, интересно кстати почитать.
Ну вот я их выложил в посте выше...
vBulletin® v3.8.14, Copyright ©2000-2026, vBulletin Solutions, Inc. Перевод: zCarot