Да, я имел ввиду консольные приложения (или оконные с использованием каких-то кроссплатформенных (всмысле наличия вариантов аналогичной библиотеки для разных платформ с предоставлением программистам аналогичных пограммных интерфейсов) библиотек, если такие существуют, я просто не знаю), пишущиеся в соответствии со стандартом.
eazy Сниф порта В Делфи чтобы просушать уже открытый порт достаточно взять компонент IdTCP из вкладки Indy clients , в bound port пишешь свой порт и обрабатываешь событие чтения.
Delphi Для тех, кому важен размер программы. Иногда требуется что либо сделать по таймеру, тут уже стандартным компонентом Timer не обойтись, тогда воспользуемся следующими функциями: Code: [COLOR=DarkOrange]uses MMSystem[/COLOR] [COLOR=Cyan]///подключим необходимый модуль[/COLOR] ... var TID:Integer; ... procedure TimeCallback(TimerID, Msg : UINT; DWUser,DW1,DW2 : DWord); pascal; [COLOR=Cyan]///сюда вставляем код, который должен выполняться по таймеру[/COLOR] end; procedure StartTimer(interval:cardinal); begin TID := timeSetEvent(interval,0,@TimeCallBack,0,TIME_PERIODIC); end; [COLOR=Cyan]//используем нашу процедуру[/COLOR] StartTimer(10000) [COLOR=Cyan]// то есть 10 секунд[/COLOR]
не знаю откуда это вообще взялось... мб старые компилеры такую хрень делали, не знаю, не смотрел, но явно не в 7-й делфе... Пис!
Delphi - простая работа с файлами Часто в коде программ можно увидеть что-то типа Code: 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 не надо писать ссылку на файл: Code: var a: integer; .. assignfile(input, 'a.in'); reset(input); assignfile(output, 'a.out'); rewrite(output); read(a); write(a); close(input); close(output); На самом деле - можно сделать ещё короче: Code: var a: integer; .. reset(input, 'a.in'); rewrite(output, 'a.out'); read(a); write(a); close(input); close(output); Кстати (тестилось и на винде, и на никсах - везде результат положительный), можно даже и не использовать - система сама всё правильно закрывает.
Если так рассуждать, то вообще и память освобождать не надо и дескрипторы закрывать... конечно система сама всё закроет и поудаляет всё что надо, но это показывает, что у программиста руки из жопы растут... так что пишите красивый код или по крайней мере по правилам хорошего тона... а не через задницу...
Я и не говорю, что так надо делать - я говорю, что это не критично. Просто, если открывать всё через CreateFile, то тогда, если не закрыть, то произойдёт что-нибудь нехорошее... Сам я вообще стремлюсь к чему-то такому: Code: var a: integer; .. reset(input, 'a.in'); rewrite(output, 'a.out'); try read(a); write(a); finally close(input); close(output); end; PS Везёт мне на красивые номера постов сейчас Этот - 666777
стремиться надо вот к такому если оперировать с файлами функциями делфы... Code: 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 Code: 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 ; Очищаем стек
Точим exploitРазбор ошибок выдаваемых компилятором gcc при попытке трансляции файла beta.cpp Исходник - http://milw0rm.com/shellcode/656 Часть исходника: Code: #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\" <[email protected]>\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++; } А это список ошибок выдаваемых компилятором(не считая варнигов): HTML: 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-кода, как только воткну в него =)
Вообще зверь. Может нормально отредактируешь точто находится зеленым цветом, вплотную не соблюдая абзацов. И вообще обычное дело доработка эксплоитов, КК писал об этом. В любом случае молодец.
сишные трюки сишные трюки (1Fh выпуск) язык си не предоставляет никаких средств для временного отключения блоков кода и большинство программистов делают это с помощью комментариев. казалось бы что может быть проще и о каких трюках тут вообще говорить? на самом же деле, комментарии не только не единственный, но и едва ли не самый худший прием среди прочих о которых мы сейчас и поговорим! трюк #1*– комментарии, ремарки и помарки Системы контроля версий как раз и создавались для того, чтобы обеспечить легкий, прозрачный и непротиворечивый механизм безопасной правки исходных текстов инвариантный по отношению к самому языку. Однако, на практике системы контроля версий используются только для организации совместной работы над проектом, да и то не всегда. Уж слишком много телодвижений приходится совершать всякий раз, а программисты*— люди ленивые. Если нам необходимо временно отключить блок кода, намного проще закомментировать его, а потом удалить комментарии, подключая его обратно. Быстро. Дешево. Сердито. Но увы… потенциально небезопасно с точки зрения внесения новых ошибок и развала уже отлаженной программы, чего допускать ни в коем случае нельзя. А потому прежде, чем идти дальше, сформулируем перечень требований, предъявляемый к механизмам отключения кода: легкость использования (никто не будет пользовать средство, требующее кучи телодвижений); вложенность (внутри отключаемого блока может находится один или несколько ранее отключенных блоков); многоуровневость (если для отключения блока кода необходимо исправить два и более несмежных фрагментов исходного текста, необходимо гарантировать корректное снятие блокировки, что становится особенно актуально, если отключаются независимые блоки А, B, С*– тогда, при включении блока B возникает угроза подключения фрагментов, относящихся к блокам A и C, что ведет к развалу программы); поддержка всех языковых конструкций (какой прок от инструмента, если он работает только с ограниченным набором языковых конструкций, например, не позволяет отключать ассемблерные вставки?!); Удовлетворяют ли комментарии указанным требованиям?! А вот и нет! Комментарии в стиле Си (/* */) очень удобны, поскольку, позволяют отключать огромные блоки кода нажатием всего четырех клавиш, к тому же они могут располагаться в любом месте строки, а не только в ее начале. Однако, отсутствие поддержки вложенности создает серьезные проблемы. Например: Code: /* ошибка! закомментированный блок уже содержит /* */ for (a = 0; a < N; a++) { /* for (b = 0; b < M; b++) if (!strcmp(name_array[a], vip_array[b])) continue; */ // DeleteFile(name_array[a]); pritnf("%d %s\n", a, name_array[a]); } */ Листинг*1*демонстрация некорректного использования комментариев /* */ для временного отключения блоков кода[/B] Попытка выключить цикл for (a,,) ведет к ошибке компиляции*— комментарии /* */ не могут быть вложенными и в таких случаях программисты используют альтернативу в виде "//" допускающую вложенность, но, увы, вручную проставляемую вначале _каждой_ строки, что очень утомительно и совершенно непроизводительно, если, конечно, не использовать макросы, поддерживаемые средой разработки (а практически все среды разработки их поддерживают). Аналогичным образом осуществляется и снятие комментариев. И все было бы хорошо, да вот неоднозначности с уровнем вложенности делают отключение блоков небезопасным. В нашем случае мы имеем три раздельных отключаемых блока кода. Во-первых, это заблокированная проверка принадлежности удаляемого файла к vip_array, во-вторых, это, собственно, само удаление файла (заблокированное и замененное отладочной печатью через printf) и, в-третьих, комментарий, пытающийся отключить цикл for(a,,) со всем что в нем находится. Отключаются блоки кода очень просто, а вот обратное утверждение уже неверно. Никаким автоматизмом тут уже и не пахнет, в результате чего нам приходится разбираться с назначением каждого блока самостоятельно. Однако, если немного поколдовать над комментариями… Пусть следом за "//" идет цифра (или буква) указывающая принадлежность текущей комментируемой строки к блоку кода. Продвинутые среды разработки типа Microsoft Visual Studio поддерживают развитый макроязык, позволяющий выполнять лексический анализ, удаляя только те комментарии, за которыми идет заданная буква/цифра. Это может выглядеть, например, так: Code: //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[b])) continue; //3 //2 //3 //1 // DeleteFile(name_array[a]); //3 pritnf("%d %s\n", a, name_array[a]); //3 } Листинг*2*имитация многоуровневой структуры отключаемых блоков исходного кода посредством комментариев Проблема вложенности решена на 100%, проблема многоварианости*— на 50% (после удаления комментария //1 мы так же должны удалить, а точнее временно заблокировать следующую за ним строку с отладочной печатью), однако, в целом предложенная техника намного более удобна и единственный серьезный недостаток*— привязка программиста к конкретной среде с набором пользовательских макросов. Менее серьезный недостаток*— ассемблерные вставки как правило не поддерживают Си/Си++ комментариев и потому должны обрабатываться отдельно, усложняя реализацию нашего макродвижка и сводя его преимущества на нет. трюк #2*— директивы условной трансляции Разработанные для поддержки многовариантного кода директивы условной трансляции оказались практически невостребованными (речь, разумеется, идет только о временном выключении кода), что очень странно*— директивы условной трансляции намного более эффективны, чем комментарии и пример, приведенный ниже, доказывает этот тезис. Code: #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])"*— автоматически отключается отладочная печать и наоборот. В результате чего риск развала программы уменьшается до нуля. Самое интересное, что директивы условной трансляции ничуть не хуже работают и с ассемблерными вставками! Code: __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_ переменная (глобальная, конечно), то компилятор оставляет ветвление "как есть", давая нам возможность управлять поведением программы*— если у пользователей возникнут проблемы из-за ошибки в плохо отлаженном блоке кода, то этот блок можно отключить (естественно, если значения флагов вынесены в конфигурационный файл или доступны через пользовательский интерфейс, но это уже несущественные детали реализации). Пример использования ветвлений для отключения блоков кода приведен ниже: Code: #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, так что при всем уважении к директивам условной трансляции, они идут лесом. А вот ветвления можно использовать для выключения блока ассемблерных вставок (о чем кстати говоря, умалчивает штатная документация, но следующий пример компилируется вполне нормально): Code: #define _D1_ 0 if (_D1_) __asm{ INT 03 } Листинг*6*использование ветвлений для выключения ассемблерных вставок Ветвления, конечно, тоже не лишены недостатков, однако, для временного выключения блоков кода они намного лучше, удобнее и продуктивнее, чем комментарии. Естественно, существуют и другие средства. Взять хотя бы "return", позволяющий одним движением руки погасить блок кода до самого конца функции. Критикуемый GOTO*– отличная штука, но только в малых дозах. Иначе программа превращается в настоящее спагетти, которое практически невозможно распутать. (c)крис касперски ака мыщъх, a.k.a. souriz, a.k.a. nezumi, no-email
Читаю, где-то я это читал - Крис Касперски - понятно .(напишите его имя в копирайтах с заглавной буквы) У кого - нибудь есть ссылки на его статьи по C? Можете поделиться?