Dia: 13/08/2012
A import\342ncia do questionamento Sexta-feira, 10/08/2012 \340s 11h00, por Roger Hughes
Um dos meus coment\341rios sobre um artigo que escrevi - Emperor\u2019s New Clothes e Catch 22 (ingl\352s) - foi que "uma das coisas mais valiosas que esses padr\365es oferecem \351 o vocabul\341rio. Eles d\343o nome a algo.\u201d, o que \351 um ponto importante que acho que deixei passar. A ideia \351 que, uma vez algo possui um nome, voc\352 pode comunicar facilmente sobre esse algo. Por exemplo, \351 mais f\341cil dizer "Eu usei um Singleton" em vez de "Eu criei esta classe, onde h\341 sempre apenas uma inst\342ncia e essa inst\342ncia est\341 dispon\355vel para todas as outras classes no aplicativo".
Sempre pensei que a nomea\347\343o, em geral, \351 crucial para escrever um bom software e que eu definitivamente vou escrever um artigo (ou dois) sobre o assunto algum dia. Por enquanto, basta dizer que, quando voc\352 v\352 um m\351todo ou uma classe que est\341 mal nomeada, seja corajoso: corra e aperte o bot\343o direito, Refatorar e renomear... Menu Op\347\365es, mas lembre-se: o truque consiste em encontrar o nome mais significativo.
Lendo artigo que citei acima, voc\352 pode ter a impress\343o de que sou contra padr\365es. Isso n\343o pode estar mais distante da verdade, j\341 que ao mesmo tempo que sou muito anti-anti-padr\365es, eu sou totalmente a favor deles, se eles forem exatamente de acordo com os padr\365es \u201cGang Of Four\u201d, \u201cMartin Fowler\u2019s Patterns of Enterprise Application Architecture\u201d ou qualquer outro padr\343o, mas eu ainda penso que o uso correto de padr\365es \351 tamb\351m muito importante.
Uma das observa\347\365es que deveriam ser feitas a respeito dos padr\365es GOF \351 que eles podem ser ranqueados em termos de utilidade, import\342ncia e popularidade. Por exemplo, praticamente todo desenvolvedor conhece Singleton (talvez por ele ser o mais simples?) e, caso j\341 tenha usado o Spring, ent\343o voc\352 j\341 se deparou com Strategy.
Se voc\352 n\343o usa Spring, ent\343o suspeito que \351 mais prov\341vel que voc\352 conhe\347a os padr\365es de cria\347\343o: Abstract Factory, Factory Method e Builder. Se voc\352 j\341 fez algum trabalho JEE (conhecido como J2EE), ent\343o com certeza voc\352 j\341 viu Facade sendo usado como interface para um EJB2.
Al\351m disso, voc\352 pode usar ou ter usado Observer para informar um grupo de objetos sobre a mudan\347a de estado de um, ou usado Template para definir um algoritmo estrutural, mas h\341 aqueles padr\365es que voc\352 n\343o costuma achar a necessidade de usar.
Pegue o Memento, a maioria dos desenvolvedores j\341 se deparou com isso na forma de Serializable, mas n\343o me lembro de ter que implementar isso sozinho, exceto como um exerc\355cio de aprendizagem de Memento e ent\343o existe a State Machine.
Vi esse padr\343o pela primeira vez em 1989, cinco anos antes de o GOF ter publicado seu livro. Ele estava sendo usado em um emulador de terminal VT escrito em "C" no Windows 2.1 como parte de um projeto j\341 extinto chamado CityDesk, produzido por uma empresa extinta chamada Apricot Computers. State Machine \351 muito \372til na an\341lise de fluxos de bytes de entrada, um byte de cada vez, para descobrir que a\347\365es tomar e, apenas para a posteridade, h\341 uma parte do c\363digo CityDesk abaixo: n\343o \351 bonito, n\343o \351 Java, n\343o \351 OO, mas \351 uma State Machine e que \351 anterior a GOF.
No entanto, voltando aos trilho... Enquanto State Machine \351 realmente \372til para fazer os tipos de trabalho descritos acima, agora existem maneiras mais modernas de processamento de fluxos de bytes, que no caso do Java s\343o fornecidos pela String do SDK, Streams e classes associadas como Pattern Matcher e etc. Ent\343o, State Machine est\341 se tornando mais obsoleta ou existem bons outros usos para ela? Para concluir este artigo, acho que devo reiterar que sou pr\363-padr\365es: um padr\343o utilizado no lugar apropriado fornece uma solu\347\343o muito \372til para um problema comum, mas acho que eles devem ser questionados de tempos em tempos. Padr\365es GOF t\352m estado por a\355 por muito tempo: eles podem ser melhorados? Eles ainda s\343o t\343o relevantes hoje como eram quando o livro foi escrito? Questionar coisas \351 uma habilidade importante, se voc\352 n\343o o fizer, ent\343o \351 prov\341vel que voc\352 se torne um programador Cargo Cult.
Voc\352 realmente n\343o quer ver isto: /*****************************************************************************/ /* */ /* Program / Project Name Vt320 Terminal Emulation Package */ /* */ /* Module Name Vt220.c */ /* */ /* Description Much changed but still compatible version of the earlier */ /* version of the Term220 file */ /* Contains the Vt320/220/100/52 State machine functions */ /* */ /* Procedures Included EmulateVT100 CheckForEscSequence VT100Cntl */ /* VT100Esc VT100Private */ /* */ /* Updates None */ /* MID 11.07.1989 2.00.02 Cursor up and down now checks */ /* for cursor movement beyond the */ /* screen bounds. */ /* */ /* Va 2.00.05 4:35pm 14/7/89 */ /* Mod to clear the Status line of data if in HostWrite mode */ /* */ /* Vb 2.00.01 9:40am 22/8/89 Mods around line 547 on EndRow resetting */ /* */ /* Vb 2.00.01 10:20am 22/8/89 */ /* - Mod to new line action so that if the scroll region is smaller than */ /* the screen then the cursor cannot LF past the bottom of the screen */ /* */ /*****************************************************************************/ /************************************************************************ * EmulateVT220 - VT220 Emulator * *************************************************************************/ #include\u2042 Texto original dispon\355vel em http://www.captaindebug.com/2012/04/importance-of-questioning-design.html#include #include #include #include #include #include #include #include #include #include /* Imports for grafs */ #include #include "term220.h" #include "Scroll.h" #include "externs.h" VOID EmulateVT100 (lpCharBuff, Cnt) LPSTR lpCharBuff; int Cnt; { #ifdef TERM320 extern BOOL tWatch; #endif extern BOOL DisplayControls; extern UTC utc; BYTE c; while (Cnt--) /* For all the characters in the buffer loop around and sort them out */ { c = *lpCharBuff++; /* make c the next char to process */ /* Allow the VT320 double height stuff to work * in the clearing screen bit */ if(DecDWLHoldOff) { DWLReset(ScreenVars.CurrentRow,ScreenVars.EndRow); DecDWLHoldOff = FALSE; } if(utc.Mode != UTC_NORMAL) ProcessTheMessage(c); /* call this if we're in Unit trust mode */ else { if(DisplayControls) /* for control characters display every thing */ { BufferCharacters(c); continue; } if(PrintController(c)) /* sort out any printing we may be doing */ continue; switch (c) /* Check for control characters */ { case NULL: /* 11.07.1989 MID 2.00.02 */ break; /* this is here to stop the output th the screen when print controller is on */ case 11: case '\\n': /* NEWLINE */ AddToScrollScreen(ScreenVars.CurrentRow); /* put the line in the scrolling screen region */ AddLineAutoPrint(c); /* put this line in the file */ if (fCaptureLine) CaptureLine (); if (ScreenVars.CurrentRow == ScreenVars.ScrollBottom) { //ScrollVEx (&ScreenImage[0][0], // &AttributeMap[0][0], // NULL, // (LPSCREENVARS)&ScreenVars, // hWndMain, // ScreenVars.ScrollTop, // ScreenVars.ScrollBottom, // 1, // (BUTTONDATA FAR *)&bd, // (LPFONTVARS)&FontVars); ScrollVEx ( hWndMain, ScreenVars.ScrollTop, ScreenVars.ScrollBottom, 1); DWLScroll(ScreenVars.ScrollTop,ScreenVars.ScrollBottom,1); } else if(ScreenVars.CurrentRow < ScreenVars.EndRow) /* RH 22/8/89 Vb 2.0.01 */ ++ScreenVars.CurrentRow; /* IF SEQUENCE LNM IS ACTIVE MOVE CURSOR TO COL 0 */ if (fLFCR) ScreenVars.CurrentCol = 0; faNewLine = FALSE; break; case '\\07': /* BELL */ MessageBeep (0); break; case '\\r': /* CARRIAGE RETURN */ ScreenVars.CurrentCol = 0; faNewLine = FALSE; break; case '\\b': /* BACKSPACE */ if (ScreenVars.CurrentCol) --ScreenVars.CurrentCol; break; case '\\t': /* TAB */ if (ScreenVars.CurrentCol < ScreenVars.EndCol) { for (++ScreenVars.CurrentCol; ScreenVars.CurrentCol <= ScreenVars.EndCol; ScreenVars.CurrentCol++) { if (ScreenVars.TabStops[ScreenVars.CurrentCol]) break; } } break; case 0x7f: /* Ignore delete character */ if(bd.Terminal.TermOptions.MapBackSpaceToDel && ScreenVars.CurrentCol) --ScreenVars.CurrentCol; break; case 5: /* ENQ */ if(tWatch) SendDataBlock(bd.Data2,lstrlen(bd.Data2),FALSE); SendAnswerBack(); break; case SO: /* move G1 on to GL */ CurrentSet = &CharacterSet1; break; case SI: /* move G0 into GL */ CurrentSet = &CharacterSet0; break; default: /* Check for Escape sequences */ CheckForEscSequence (c); break; } } } } /************************************************************************ * This function checks for escape sequences. If one is found it is processed. If not then it is a normal character and is displayed. * *************************************************************************/ VOID CheckForEscSequence (c) BYTE c; { extern BOOL DecUKKeyBoard; extern BYTE PoundSign; switch (State) { case NORMAL: /* CHANGE STATE TO LOOK OF ESCAPE SEQUENCE IF ESCAPE RECEIVED */ if (c == ESC) State = ESCED; /* check for an 8 bit escape sequence */ else if(bd.Terminal.TermOptions.Bits87 && ((c >= 0x80)&&(c <= 0x9f))) { State = ESCED; /* c - 0x40 puts the char back into the correct range */ c -= (BYTE)0x40; CheckForEscSequence(c); /* ie. 0x9b - 0x40 = 0x5b or'['. hence respond to an ESC [ call */ } /* NORMAL CHARACTER THEN PUT INTO SCREEN BUFFER */ else if (c >= 32) { if(DecUKKeyBoard && (c == PoundSign)) c = 0x9c; BufferCharacters (c); } break; case POSS_DECSTR: /* a possible soft reset is on the way */ if(c == ESC) { State = ESCED; break; } else if((c == 'p') && (bd.Terminal.TermOptions.TermType != CDK_VT100)) DecStr();u if(c != 'p') State = SQU_ERROR; else State = NORMAL; break; case ESCED: if(c == ESC) { State = ESCED; break; } else if (c == '(') State = CHARSET_G0; else if (c == ')') State = CHARSET_G1; else if (c == 'P') { ClrParam(); State = PARAMS; Pnum2 = 0; } else /* CONTROL SEQUENCE INTRODUCER */ if (c == '[') { ClrParam (); State = PARAMS; } else /* INTERMEDIATE CHARACTERS */ if (c > 0x20 && c <= 0x2f) State = ESCSQU; else if(c == ' ') /* check for an ESC' ' input to switch mode */ State = CONTROLXMIT; else if (c >= 0x30 && c <= 0x7e) VT100Esc (c); else State = SQU_ERROR; break; case ESCSQU: if(c == ESC) { State = ESCED; break; } else if (c >= 0x30 && c <= 0x7e) VT100Esc (c); break; case PARAMS: /* PARAMETER CHARACTERS */ /*NEWBIT*/ if (Pnum == 0 && c >= 0x3c && c <= 0x3f) fPrivate = TRUE; else if (c == IMMED) State = NORMAL; else if ((c == '|')&&(Pnum2 < NO2PARAMS)) { FlagDCS = TRUE; Param2[Pnum2++] = c; } else if ((FlagDCS == TRUE) && (c == 0x1b)) FlagDCS = FALSE; else if((c == 0x22) && (Pnum2 < NO2PARAMS)) Param2[Pnum2] = c; else if (c >= '0' && c <= '9') { if (Pnum2 < NO2PARAMS) Param2[Pnum2++] = c; if(Pnum < NOPARAMS) { Param[Pnum] *= 10; Param[Pnum] += (c - '0'); } } else if (c == ';') { if(++Pnum > (NOPARAMS - 1)) State = NORMAL; if(++Pnum2 < NO2PARAMS) Param2[Pnum2] = c; } else if ((FlagDCS == TRUE)&&(Pnum2 < NO2PARAMS)) Param2[Pnum2++] = c; else if(c == ' ) /* a dollar sign for the status line Escape sequences */ State = STATUSLINE_ESCSEQ; else if(c == '!') State = POSS_DECSTR; /* a possible soft reset is on the way */ else if(c == ESC) State = ESCED; /* an unterminated esc sequence */ else { if (fPrivate || (c >= 0x70 && c <= 0x7e)) VT100Private (c); else VT100Cntl (c); fPrivate = FALSE; } break; case CHARSET_G1: if(c == ESC) { State = ESCED; break; } else if (c == '0') CharacterSet1 = TRUE; /* move Dec Graphics in to G1 */ else /* this covers A and B */ CharacterSet1 = FALSE; /* ASCII into G1 */ State = NORMAL; break; case CHARSET_G0: if(c == ESC) { State = ESCED; break; } else if (c == '0') CharacterSet0 = TRUE; /* Graphics */ else /* this covers A and B */ CharacterSet0 = FALSE; /* ASCII */ State = NORMAL; break; case STATUSLINE_ESCSEQ: /* act on a status line esacpe sequence */ if(c == ESC) { State = ESCED; break; } VT100Cntl(c); break; case CONTROLXMIT: /* a possible change of control code bits is on the way */ if(c == ESC) { State = ESCED; break; } SelectTransmissionStyle(c); break; case SQU_ERROR: if(c == ESC) State = ESCED; else if ((c >= '@') && (c < 0x7f)) State = NORMAL; break; default: if(c == ESC) State = ESCED; else State = NORMAL; break; } } /************************************************************************ * VT100Cntl - Check for VT100 Control escape sequences * *************************************************************************/ VOID VT100Cntl (c) BYTE c; { #ifdef TERM320 extern BOOL tWatch; #endif int lines; State = NORMAL; switch (c) { /* INSERT PARAM[0] BLANK CHARS AT CURSOR POSITION WITH THE CHARACTER ATTRIBUTES SET TO NORMAL */ case ICH: /* Vt200 and greater */ if(bd.Terminal.TermOptions.TermType != CDK_VT100) { /* INSERT AT LEAST 1 CHARACTER */ if (Param[0] == 0) Param[0] = 1; InsertCharacterEx (&ScreenImage[0][0], &AttributeMap[0][0], NULL, (LPSCREENVARS)&ScreenVars, Param[0]); } break; /* DELETE PARAM[0] CHARACTERS STARTING AT THE CURSOR POSITION */ case DCH: /* DELETE AT LEAST 1 CHARACTER */ if (Param[0] == 0) Param[0] = 1; DeleteCharacterEx (&ScreenImage[0][0], &AttributeMap[0][0], NULL, (LPSCREENVARS)&ScreenVars, Param[0]); break; /* INSERT PARAM[0] LINES */ case IL: /* INSERT AT LEAST 1 LINE */ if (Param[0] == 0) Param[0] = 1; InsertLineEx (&ScreenImage[0][0], &AttributeMap[0][0], NULL, (LPSCREENVARS)&ScreenVars, Param[0]); SetCursorPosition ((LPSCREENVARS)&ScreenVars, (LPVTFLAGS)&VTFlags, ScreenVars.CurrentRow, 0, 2); break; /* DELETE PARAM[0] LINES STARTING AT THE LINE WITH CURSOR */ case DL: /* DELETE AT LEAST 1 LINE */ if (Param[0] == 0) Param[0] = 1; DeleteLineEx (&ScreenImage[0][0], &AttributeMap[0][0], NULL, (LPSCREENVARS)&ScreenVars, Param[0]); SetCursorPosition ((LPSCREENVARS)&ScreenVars, (LPVTFLAGS)&VTFlags, ScreenVars.CurrentRow, 0, 2); break; case 0x5c: /* DECUDK VT220 ONLY */ SetupFunctionKeys (Param2); break; /* CURSOR MOVEMENT */ case CUB: /* LEFT */ if (ScreenVars.CurrentCol) { if (Param[0] == 0) Param[0] = 1; if ((ScreenVars.CurrentCol - Param[0]) >= 0) ScreenVars.CurrentCol = ScreenVars.CurrentCol - Param[0]; else ScreenVars.CurrentCol = 0; } break; case CUF: /* RIGHT */ if (ScreenVars.CurrentCol < ScreenVars.EndCol) { if (Param[0] == 0) Param[0] = 1; if ((ScreenVars.CurrentCol + Param[0]) <= ScreenVars.EndCol) ScreenVars.CurrentCol = ScreenVars.CurrentCol + Param[0]; else ScreenVars.CurrentCol = ScreenVars.EndCol; } break; case CUU: /* UP */ if(!bSLWrite) { AddToScrollScreen(ScreenVars.CurrentRow); /* put the line in the scrolling screen region */ if (fCaptureLine) CaptureLine (); AddLineAutoPrint(LF); /* put this line in the file */ /**** MID 11.07.1989 2.00.02 */ if (ScreenVars.CurrentRow > ScreenVars.ScrollTop) { if (Param[0] == 0) Param[0] = 1; ScreenVars.CurrentRow = ScreenVars.CurrentRow - Param[0]; if (ScreenVars.CurrentRow < ScreenVars.ScrollTop) ScreenVars.CurrentRow = ScreenVars.ScrollTop; } } break; case CUD: /* DOWN */ if(!bSLWrite) { AddToScrollScreen(ScreenVars.CurrentRow); /* put the line in the scrolling screen region */ if (fCaptureLine) CaptureLine (); AddLineAutoPrint(LF); /* put this line in the file */ /**** MID 11.07.1989 2.00.02 */ if (ScreenVars.CurrentRow < ScreenVars.ScrollBottom) { if (Param[0] == 0) Param[0] = 1; ScreenVars.CurrentRow = ScreenVars.CurrentRow + Param[0]; if (ScreenVars.CurrentRow > ScreenVars.ScrollBottom) ScreenVars.CurrentRow = ScreenVars.ScrollBottom; } } break; case CUP: /* CURSOR POSITION (HOME) */ case HVP: /* HORIZONTAL & VERTICAL POSITION */ /* RH Vb 2.00.01 Save the current value */ lines = ScreenVars.EndRow; /* save the current end row in a convenient store */ if (fCaptureLine) CaptureLine (); AddLineAutoPrint(LF); /* put this line in the file */ AddToScrollScreen(ScreenVars.CurrentRow); /* put the line in the scrolling screen region */ if (Param[0] > 0) --Param[0]; if (Param[1] > 0) --Param[1]; /* if a VT320 term and in host write mode with the SL accessed then only the columns are affected */ if((bd.Terminal.TermOptions.TermType == CDK_VT320)&& bSLWrite) ScreenVars.EndRow = Param[0] = LASTLINE; SetCursorPosition ((LPSCREENVARS)&ScreenVars, (LPVTFLAGS)&VTFlags, Param[0], Param[1], 2); faNewLine = FALSE; /* RH Vb 2.00.01 added the ifdef */ ScreenVars.EndRow = lines; /* reset the bottom row */ break; case 'i': switch (Param[0]) { case 0: /* PRINT SCREEN */ ScreenPrint(); break; case 5: /* ACTIVATE PRINTER CONTROLLER Esc[5i */ DoPrintControl(); break; } break; case 'h': switch (Param[0]) { case 20: /* CAUSES LINE FEED TO DO A CR. RETURN - CR & LF */ fLFCR = TRUE; break; case 4: /* IRM Insert mode */ /*NEWBIT*/ VTFlags.WriteMode = TRUE; break; case 12: /* SRM Local echo off */ bd.Terminal.TermOptions.LocalEcho = FALSE; break; case 2: /* KAM Lock keyboard */ fKeyLock = TRUE; break; } break; case 'l': /* SWITCH ABOVE OFF */ switch (Param[0]) { case 20: /* LINE FEED TO JUST LF */ fLFCR = FALSE; break; case 4: /* IRM Replace mode */ VTFlags.WriteMode = FALSE; break; case 12: /* SRM Local echo on */ bd.Terminal.TermOptions.LocalEcho = TRUE; break; case 2: /* KAM Unlock keyboard */ fKeyLock = FALSE; break; } break; case ECH: /* ERASE CHARACTER VT220 ONLY */ if (Param[0] == 0) Param[0] = 1; if (Param[0] <= (BYTE)(((ScreenVars.EndCol + 1) - ScreenVars.CurrentCol))) ClearEx (&ScreenImage[0][0], &AttributeMap[0][0], NULL, hWndMain, (LPSCREENVARS)&ScreenVars, (LPFONTVARS)&FontVars, ScreenVars.CurrentRow, ScreenVars.CurrentCol, ScreenVars.CurrentRow, ScreenVars.CurrentCol + Param[0] - 1, FALSE); else { Param[0] = Param[0] - ((ScreenVars.EndCol + 1) - ScreenVars.CurrentCol); lines = Param[0] / (ScreenVars.EndCol + 1); Param[0] = Param[0] - ((ScreenVars.EndCol + 1) * lines); ClearEx (&ScreenImage[0][0], &AttributeMap[0][0], NULL, hWndMain, (LPSCREENVARS)&ScreenVars, (LPFONTVARS)&FontVars, ScreenVars.CurrentRow, ScreenVars.CurrentCol, ScreenVars.CurrentRow + lines + 1, Param[0], FALSE); } break; case ED: /* DISPLAY */ if (Param[0] == 0) /* Erase from cursor to end of screen */ { ClearEx (&ScreenImage[0][0], &AttributeMap[0][0], NULL, hWndMain, (LPSCREENVARS)&ScreenVars, (LPFONTVARS)&FontVars, ScreenVars.CurrentRow, ScreenVars.CurrentCol, ScreenVars.EndRow, ScreenVars.EndCol, FALSE); DecDWLHoldOff = TRUE; } else if (Param[0] == 1) /* Erase from screen start to cursor */ { ClearEx (&ScreenImage[0][0], &AttributeMap[0][0], NULL, hWndMain, (LPSCREENVARS)&ScreenVars, (LPFONTVARS)&FontVars, 0, 0, ScreenVars.CurrentRow, ScreenVars.CurrentCol, FALSE); DWLReset(0,ScreenVars.CurrentRow); } else if (Param[0] == 2) /* Erase all of screen */ { ClearEx (&ScreenImage[0][0], &AttributeMap[0][0], NULL, hWndMain, (LPSCREENVARS)&ScreenVars, (LPFONTVARS)&FontVars, 0, 0, ScreenVars.EndRow, ScreenVars.EndCol, FALSE); DWLReset(0,ScreenVars.EndRow); } break; case EL: /* LINE */ if (Param[0] == 0) /* Erase from cursor to EOL */ ClearEx (&ScreenImage[0][0], &AttributeMap[0][0], NULL, hWndMain, (LPSCREENVARS)&ScreenVars, (LPFONTVARS)&FontVars, ScreenVars.CurrentRow, ScreenVars.CurrentCol, ScreenVars.CurrentRow, ScreenVars.EndCol, FALSE); else if (Param[0] == 1) /* Erase from line start to cursor */ ClearEx (&ScreenImage[0][0], &AttributeMap[0][0], NULL, hWndMain, (LPSCREENVARS)&ScreenVars, (LPFONTVARS)&FontVars, ScreenVars.CurrentRow, 0, ScreenVars.CurrentRow, ScreenVars.CurrentCol, FALSE); else if (Param[0] == 2) /* Erase whole line */ ClearEx (&ScreenImage[0][0], &AttributeMap[0][0], NULL, hWndMain, (LPSCREENVARS)&ScreenVars, (LPFONTVARS)&FontVars, ScreenVars.CurrentRow, 0, ScreenVars.CurrentRow, ScreenVars.EndCol, FALSE); break; case TBC: /* TABULATION CLEAR */ if (Param[0] == 0) /* CLEAR TAB STOP AT CURSOR POSITION */ ClearTabStop ((LPSCREENVARS)&ScreenVars, ScreenVars.CurrentCol); else if (Param[0] == 3) /* CLEAR ALL TAB STOPS */ ClearTabStop ((LPSCREENVARS)&ScreenVars, -1); break; case DA: if (Param[0] == 0) { #ifdef TERM320 if(tWatch) SendDataBlock(IdentifyString,16,FALSE); #endif PutComm(hWndMain,(LPSTR)IdentifyString, 16,(LPBUTTONDATA)&bd,(LPCOMMSVARS)&CommsVars,(BOOL FAR *)&fWaitingConnection); } break; case DECSSDT: /* if the status line is being talked to then switch it off */ SLReset(); SetSLMode(); /* switch the status line mode */ break; case DECSASD: SwitchStatusLine(); break; case SGR: /* Select Graphic Rendition */ Sgr(); break; default: if((c < '@') || (c > 0x7e)) State = SQU_ERROR; break; } } /************************************************************************ * VT100Esc - Check for VT100 normal escape sequences * *************************************************************************/ VOID VT100Esc (c) BYTE c; { #ifdef TERM320 extern BOOL tWatch; #endif int i; State = NORMAL; switch (c) { case RIS: ResetTerminal(); break; case '=': VTFlags.fAppKeypad = TRUE; break; case '>': VTFlags.fAppKeypad = FALSE; break; case DECID: /* Device attributes (WHAT ARE YOU) */ #ifdef TERM320 if(tWatch) SendDataBlock(IdentifyString,16,FALSE); #endif PutComm(hWndMain,(LPSTR)IdentifyString, 16,(LPBUTTONDATA)&bd,(LPCOMMSVARS)&CommsVars,(BOOL FAR *)&fWaitingConnection); break; case IND: /* CURSOR DOWN WITH SCROLL */ if (ScreenVars.CurrentRow < ScreenVars.ScrollBottom) ++ScreenVars.CurrentRow; else if (ScreenVars.CurrentRow == ScreenVars.ScrollBottom) { //ScrollVEx (&ScreenImage[0][0], // &AttributeMap[0][0], // NULL, // (LPSCREENVARS)&ScreenVars, // hWndMain, // ScreenVars.ScrollTop, // ScreenVars.ScrollBottom, // 1, // (BUTTONDATA FAR *)&bd, // (LPFONTVARS)&FontVars); ScrollVEx ( hWndMain, ScreenVars.ScrollTop, ScreenVars.ScrollBottom, 1); DWLScroll(ScreenVars.ScrollTop,ScreenVars.ScrollBottom,1); } break; case RI: /* CURSOR UP WITH SCROLL */ if (ScreenVars.CurrentRow > ScreenVars.ScrollTop) --ScreenVars.CurrentRow; else if (ScreenVars.CurrentRow == ScreenVars.ScrollTop) { //ScrollVEx (&ScreenImage[0][0], // &AttributeMap[0][0], // NULL, // (LPSCREENVARS)&ScreenVars, // hWndMain, // ScreenVars.ScrollTop, // ScreenVars.ScrollBottom, // -1, // (BUTTONDATA FAR *)&bd, // (LPFONTVARS)&FontVars); ScrollVEx (hWndMain, ScreenVars.ScrollTop, ScreenVars.ScrollBottom, -1); DWLScroll(ScreenVars.ScrollTop,ScreenVars.ScrollBottom,-1); } break; case NEL: /* NEXT LINE WITH SCROLL */ if (ScreenVars.CurrentRow == ScreenVars.ScrollBottom) { //ScrollVEx (&ScreenImage[0][0], // &AttributeMap[0][0], // NULL, // (LPSCREENVARS)&ScreenVars, // hWndMain, // ScreenVars.ScrollTop, // ScreenVars.ScrollBottom, // 1, // (BUTTONDATA FAR *)&bd, // (LPFONTVARS)&FontVars); ScrollVEx(hWndMain, ScreenVars.ScrollTop, ScreenVars.ScrollBottom, 1); DWLScroll(ScreenVars.ScrollTop,ScreenVars.ScrollBottom,1); } else ++ScreenVars.CurrentRow; ScreenVars.CurrentCol = 0; break; case DECSC: DecSc(); /*NEWBIT*/ break; case DECRC: if(!CurSaveDone) /* if not save was done then we use the default settings */ SaveCursor = DefaultCursor; CurSaveDone = FALSE; ScreenVars.CurrentCol = SaveCursor.Col; ScreenVars.CurrentRow = SaveCursor.Row; bd.Terminal.TermOptions.Autowrap = SaveCursor.Wrap; VTFlags.fOrigin = SaveCursor.Origin; VTFlags.SelectErasable = SaveCursor.SelectErase; CharacterSet0 = SaveCursor.Chars0; CharacterSet1 = SaveCursor.Chars1; if(SaveCursor.CurrentSet) CurrentSet = &CharacterSet1; else CurrentSet = &CharacterSet0; fBold = SaveCursor.Bold; fUnderscore = SaveCursor.Under; fBlink = SaveCursor.Blink; fReverse = SaveCursor.Reverse; break; case HTS: ScreenVars.TabStops[ScreenVars.CurrentCol] = 1; break; case 'N': case 'O': fOneNormal = TRUE; break; case 'Z': #ifdef TERM320 if(tWatch) SendDataBlock(IdentifyString,16,FALSE); #endif PutComm(hWndMain,(LPSTR)IdentifyString, 16,(LPBUTTONDATA)&bd,(LPCOMMSVARS)&CommsVars,(BOOL FAR *)&fWaitingConnection); break; case DECSWL: /* reset to a single width line */ DecDWL[ScreenVars.CurrentRow].LineUsed = FALSE; DecDWL[ScreenVars.CurrentRow].LineType = ZERO; ScreenVars.LineUpdate[ScreenVars.CurrentRow] = TRUE; break; case DECDHLTOP: case DECDHLBOT: case DECDWL: /* set up for single height double width */ if(!DecDWL[ScreenVars.CurrentRow].LineUsed) ClearEx (&ScreenImage[0][0], /* clear the 2nd hlaf of the line */ &AttributeMap[0][0], NULL, hWndMain, (LPSCREENVARS)&ScreenVars, (LPFONTVARS)&FontVars, ScreenVars.CurrentRow, ScreenVars.EndCol/2, ScreenVars.CurrentRow, ScreenVars.EndCol, FALSE); ScreenVars.LineUpdate[ScreenVars.CurrentRow] = FALSE; DecDWL[ScreenVars.CurrentRow].LineUsed = TRUE; DecDWL[ScreenVars.CurrentRow].LineType = c; /* the type of line */ break; default: if((c < '@') || (c > 0x7e)) State = SQU_ERROR; break; } } /************************************************************************ * VT100Private - Check for VT100 private sequences * *************************************************************************/ VOID VT100Private (c) BYTE c; { extern WORD fOldTermType; extern BOOL ReverseScreen; int line,col; BYTE forecolor, backcolor; LPBYTE tAttri; WORD temp; #define DECSCL 0x70 /* 'p' Selects Terminal type */ #define VT100MODE 61 #define VT200MODE 62 /* the two possible modes */ State = NORMAL; switch (c) { case DECSCL: /* 'p' Terminal type set up */ if(Param[0] == VT100MODE) /* 61 */ { SelectTransmissionStyle('F'); bd.Terminal.TermOptions.TermType = CDK_VT100; } else /* this is 62 but assume any other numer gives us back a VT320 */ { bd.Terminal.TermOptions.TermType = CDK_VT320; if(Param[1] == (BYTE)1) SelectTransmissionStyle('F'); else SelectTransmissionStyle('G'); } break; case 'q': /* DECSCA SELECT CHARACTER ATTRIBUTES VT220 */ if(bd.Terminal.TermOptions.TermType != CDK_VT100) { if((Param[0] == 0) || (Param[0] == 2)) VTFlags.SelectErasable = FALSE; else /* if (Param[0] == 1) */ VTFlags.SelectErasable = TRUE; } break; case ED: /* SELECTIVE DISPLAY ERASE VT220 */ if (Param[0] == 0) /* Erase from cursor to end of screen */ ClearEx (&ScreenImage[0][0], &AttributeMap[0][0], NULL, hWndMain, (LPSCREENVARS)&ScreenVars, (LPFONTVARS)&FontVars, ScreenVars.CurrentRow, ScreenVars.CurrentCol, ScreenVars.EndRow, ScreenVars.EndCol, TRUE); else if (Param[0] == 1) /* Erase from screen start to cursor */ ClearEx (&ScreenImage[0][0], &AttributeMap[0][0], NULL, hWndMain, (LPSCREENVARS)&ScreenVars, (LPFONTVARS)&FontVars, 0, 0, ScreenVars.CurrentRow, ScreenVars.CurrentCol - 1, TRUE); else if (Param[0] == 2) /* Erase all of screen */ ClearEx (&ScreenImage[0][0], &AttributeMap[0][0], NULL, hWndMain, (LPSCREENVARS)&ScreenVars, (LPFONTVARS)&FontVars, 0, 0, ScreenVars.EndRow, ScreenVars.EndCol, TRUE); break; case EL: /* SELECTIVE LINE ERASE VT220 */ if (Param[0] == 0) /* Erase from cursor to EOL */ ClearEx (&ScreenImage[0][0], &AttributeMap[0][0], NULL, hWndMain, (LPSCREENVARS)&ScreenVars, (LPFONTVARS)&FontVars, ScreenVars.CurrentRow, ScreenVars.CurrentCol, ScreenVars.CurrentRow, ScreenVars.EndCol, TRUE); else if (Param[0] == 1) /* Erase from line start to cursor */ ClearEx (&ScreenImage[0][0], &AttributeMap[0][0], NULL, hWndMain, (LPSCREENVARS)&ScreenVars, (LPFONTVARS)&FontVars, ScreenVars.CurrentRow, 0, ScreenVars.CurrentRow, ScreenVars.CurrentCol, TRUE); else if (Param[0] == 2) /* Erase whole line */ ClearEx (&ScreenImage[0][0], &AttributeMap[0][0], NULL, hWndMain, (LPSCREENVARS)&ScreenVars, (LPFONTVARS)&FontVars, ScreenVars.CurrentRow, 0, ScreenVars.CurrentRow, ScreenVars.EndCol, TRUE); break; case DECSTBM: NewScrollSet(TRUE); /* SET Scrolling Region */ break; case 'l': case 'h': case 'i': switch (Param[0]) { case DECCKM: /* APPLICATION / CURSOR MODES */ DecCkm(c); break; case DECANM: /* VT52 Mode */ if (c == 'l') { fOldTermType = bd.Terminal.TermOptions.TermType; bd.Terminal.TermOptions.TermType = CDK_VT52; } break; case DECCOLM: /* COLUMN */ if (!bd.Terminal.TermOptions.Cursor) HideCaret (NULL); if((bd.Terminal.TermOptions.TermType == CDK_VT320)&&(bd.Terminal.TermOptions.StatusLine == CDK_HOSTWRITE)) { tEndRow = ScreenVars.EndRow; ScreenVars.EndRow = LASTLINE; } if (c == 'h') { ChangeColMode (&ScreenImage[0][0], &AttributeMap[0][0], NULL, hWndMain, (LPFONTVARS)&FontVars, (LPSCREENVARS)&ScreenVars, (LPVTFLAGS)&VTFlags, LASTCOL,(LPBUTTONDATA)&bd); if (VTFlags.fMaximized) /* if we're maximized then add the scroll bar */ SetScrollBars (TRUE); ChangeVTFont(hWndMain,AutoFontSelection((LPFONTSIZE)&FontSize[0], /* MID 29/06/1989 */ hWndMain, (LPSCREENVARS)&ScreenVars, FontVars.FontsLoaded)); InvalidateRect(hWndMain,NULL,TRUE); } else if (c == 'l') { ChangeColMode (&ScreenImage[0][0], &AttributeMap[0][0], NULL, hWndMain, (LPFONTVARS)&FontVars, (LPSCREENVARS)&ScreenVars, (LPVTFLAGS)&VTFlags, ID_80COL,(LPBUTTONDATA)&bd); if (VTFlags.fMaximized) /* if we're maximized then remove the scroll bar */ SetScrollBars (FALSE); ChangeVTFont(hWndMain,AutoFontSelection((LPFONTSIZE)&FontSize[0], /* MID 29/06/1989 */ hWndMain, (LPSCREENVARS)&ScreenVars, FontVars.FontsLoaded)); InvalidateRect(hWndMain,NULL,TRUE); } if((bd.Terminal.TermOptions.TermType == CDK_VT320)&&(bd.Terminal.TermOptions.StatusLine == CDK_HOSTWRITE)) ScreenVars.EndRow = tEndRow; if(!bd.Terminal.TermOptions.Cursor && (GetFocus() == hWndMain)) { SetCaretPos (CaretXPos(), CaretYPos()); ShowCaret(NULL); } /* set Param array before resetting the scroll region */ Param[0] = 1; Param[1] = 24; /* default vals */ Pnum = 2; NewScrollSet(TRUE); break; case DECSCNM: /* SCREEN REVERSE / NORMAL */ if (c == 'i') StartAutoPrint(DO_TITLE); /* Start the auto print mode */ else { if((!ReverseScreen && (c == 'h')) || (ReverseScreen && (c == 'l'))) { ReverseScreenEx((LPSTR)bd.Data,(LPSTR)AttributeMap,(LPSCREENVARS)&ScreenVars); for (line = 0; line < LASTLINE; line++) if(!DecDWL[line].LineUsed) ScreenVars.LineUpdate[line] = TRUE; if(c == 'h') ReverseScreen = TRUE; /* set up for soft terminal reset */ else ReverseScreen = FALSE; } } break; case DECOM: /* ORIGIN */ DecOm(c); break; case DECAWM: /* AUTOWRAP */ DecAwm(c); break; case DECTCEM: /* Hide and show cursor */ DecTcem(c); break; case AUTOPRNOFF: /* Auto Print Off */ if(c == 'i') EndAutoPrint(DO_TITLE); /* End the auto print mode */ break; case DECPFF: /* MID 12/09/1988 FF after print screen */ if (c == 'h') VTFlags.fFormFeed = TRUE; else if (c == 'l') VTFlags.fFormFeed = FALSE; break; case DECPEX: /* MID 12/09/1988 Print region */ if (c == 'h') VTFlags.fPrintExtent = FALSE; else if (c == 'l') VTFlags.fPrintExtent = TRUE; break; } break; default: if((c < '@') || (c > 0x7e)) State = SQU_ERROR; break; } }
Desenvolvimento
Advanced DLL Injection
Segunda-feira, 13/08/2012 \340s 11h00, por Alexey Lyashko
Neste artigo, irei abordar um assunto trivial (como pode parecer) como a inje\347\343o de DLL. Por alguma raz\343o, a maioria dos tutoriais na web nos d\341 apenas uma breve pincelada do tema, principalmente se limitando \340 chamada da fun\347\343o LoadLibraryA/W da API do Windows no espa\347o de endere\347o de outro processo. Embora n\343o seja de todo ruim, isso nos fornece a solu\347\343o menos flex\355vel. O que significa que toda a l\363gica deve ser codificada no DLL que queremos injetar. Por outro lado, podemos incorporar toda a gest\343o de configura\347\343o (carregar os arquivos de configura\347\343o, a an\341lise do mesmo etc.) em nossa DLL. Isso \351 melhor, mas ainda a enche com c\363digo que s\363 vai ser executado uma vez.
Vamos tentar outra abordagem. O que faremos \351 escrever um carregador (um execut\341vel que vai injetar nossa DLL em outro processo) e uma DLL pequeno, que ser\341 injetada. Para simplificar, o carregador tamb\351m criar\341 o processo que ser\341 o alvo. Sendo um usu\341rio do Linux, eu usei Flat Assembler e mingw32 para essa tarefa, mas voc\352 pode ajustar o c\363digo para qualquer ambiente que preferir. Uma breve observa\347\343o para os nerds antes de come\347armos. O c\363digo neste artigo n\343o cont\351m quaisquer verifica\347\365es de seguran\347a (por exemplo, corre\347\343o de verifica\347\343o do valor retornado pela fun\347\343o espec\355fica), a menos que seja necess\341rio como um exemplo. Caso decida experiment\341-lo, voc\352 estar\341 fazendo isso por sua pr\363pria conta e risco.
Ent\343o, vamos come\347ar a divers\343o. Cria\347\343o do processo que ser\341 o alvo Vamos supor que o carregador j\341 passou a fase de carregamento e an\341lise de arquivos de configura\347\343o e est\341 pronto para iniciar o trabalho real.
O Windows nos fornece todas as ferramentas que precisamos para iniciar um processo. H\341 mais de uma maneira de fazer isso, mas vamos usar a mais simples: a fun\347\343o de API do CreateProcess. Sua declara\347\343o parece muito assustadora, mas vamos faz\352-la da maneira mais f\341cil poss\355vel:
BOOL WINAPI CreateProcess( __in_opt LPCTSTR lpApplicationName, __inout_opt LPTSTR lpCommandLine, __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes, __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, __in BOOL bInheritHandles, __in DWORD dwCreationFlags, __in_opt LPVOID lpEnvironment, __in_opt LPCTSTR lpCurrentDirectory, __in LPSTARTUPINFO lpStartupInfo, __out LPPROCESS_INFORMATION lpProcessInformation );
Ao chamar essa fun\347\343o, n\363s s\363 temos que especificar metade dos par\342metros e definir todo o resto para NULL. Essa fun\347\343o possui duas variantes CreateProcessA e CreateProcessW como vers\365es ASCII e Unicode, respectivamente. Vamos continuar fi\351is ao ASCII o tempo todo, ent\343o, o nosso c\363digo ficaria assim (devido ao fato de que "CreateProcess" \351 mais um macro do que nome de fun\347\343o, devemos especificar a vers\343o A, pois alguns compiladores tendem a usar como padr\343o a vers\343o W):
CreateProcessA(nameOfTheFile, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startupInfo, &processInformation); N\343o se esque\347a de definir o campo cb do startupInfo para (DWORD) sizeof (STARTUPINFO), caso contr\341rio ele n\343o ir\341 funcionar.
Se a fun\347\343o tiver \352xito, obteremos todas as informa\347\365es sobre o processo (handles e IDs) na estrutura processInformation, que tem o seguinte prot\363tipo:
typedef struct _PROCESS_INFORMATION { HANDLE hProcess; //Handle to the process HANDLE hThread; //Handle to the main thread of the process DWORD dwProcessId; //ID of the new process DWORD dwThreadId; //ID of the main thread of the process }PROCESS_INFORMATION, *LPPROCESS_INFORMATION;
A essa altura, o processo foi criado, mas est\341 suspenso. Isso significa que ele ainda n\343o iniciou a sua execu\347\343o e n\343o vai at\351 chamarmos ResumeThread (processInformation.dwThreadId) dizendo ao sistema operacional para retomar a thread principal do processo, mas essa vai ser a \372ltima a\347\343o realizada pelo nosso carregador.
Lancet
Pode-se cham\341-lo de shellcode, mas n\343o tem nada a ver com a carga viral ou qualquer outra inten\347\343o maliciosa (a menos que algu\351m diga que invadir o espa\347o de endere\347os de outro processo \351 malicioso). \311 o c\363digo que injetaremos no processo que ser\341 o alvo. Ele, teoricamente, pode ser escrito em qualquer linguagem, desde que seja independente da posi\347\343o e compilado em instru\347\365es nativas (em nosso caso, instru\347\365es x86), mas eu prefiro fazer essas coisas em linguagem Assembly. \311 sempre uma boa ideia pensar no que o seu c\363digo se destina a fazer antes de escrever uma \372nica linha dele. Nesse caso, \351 uma ideia de ouro. O c\363digo precisa ser pequeno, de prefer\352ncia r\341pido e est\341vel, pois pode dar um pouco de dor de cabe\347a para depurar, uma vez que tiver sido injetado.
Existem duas tarefas b\341sicas que voc\352 vai querer atribuir a esse c\363digo:
DWORD WINAPI ThreadProc( __in LPVOID lpParameter);o que significa que ele tem que retornar um valor do tipo DWORD. Ele aceita um par\342metro, que pode ser um valor real (mas voc\352 precisa convert\352-lo para tipo LPVOID) ou um ponteiro para um array de par\342metros. S\363 mais uma coisa sobre essa fun\347\343o (a \372ltima mas n\343o menos importante!): \351 uma fun\347\343o stdcall macro WINAPI definida como __declspec (stdcall). Isso significa que nossa fun\347\343o precisa cuidar da limpeza da pilha antes do retorno. No nosso caso, \351 muito f\341cil, basta usar ret 0x04 (assumindo que o tamanho de LPVOID seja de 4 bytes).
Outra coisa importante a mencionar - voc\352 vai, obviamente, precisar saber quantos bytes sua fun\347\343o ocupa, a fim de alocar mem\363ria no espa\347o de endere\347o do processo que ser\341 o alvo e mover seu c\363digo corretamente l\341. Al\351m da aloca\347\343o de um bloco de mem\363ria execut\341vel para nossa fun\347\343o, voc\352 precisa alocar um bloco de dados - defini\347\365es de configura\347\343o a serem passadas para a DLL injetada. \311 f\341cil passar o endere\347o dos par\342metros como um argumento para a nossa ThreadProc.
O esqueleto da fun\347\343o ficaria assim:
lancet: push ebp mov ebp, esp sub esp, as_much_space_as_you_need_for_variables push registers_you_are_planning_to_use ;function body pop registers_you_used mov esp, ebp pop ebp ret 0x04 lancet_size = $-lancet
A \372ltima linha nos d\341 o tamanho exato da fun\347\343o em bytes. A seguir est\341 o template de c\363digo fonte:
format MS COFF ;as we are going to link this file with our loader public lancet as '_lancet' section '.text' readable executable lancet: ;our function goes here ;followed by data loadLibraryA db 'LoadLibraryA',0 init db 'name_of_the_initialization_function',0 ourDll db 'name_of_our_dll',0 kernel32 db 'kernel32.dll',0 lancet_size = $-lancet public lsize as '_lancet_size' section '.data' readable writeable lsize dd lancet_size
Ent\343o, o que \351 que vamos inserir no "corpo da fun\347\343o"? Primeiramente, como o nosso c\363digo, uma vez injetado, n\343o tem ideia de onde ele est\341 na mem\363ria, devemos salvar o nosso "endere\347o base" e calcular todas os offsets relativos a esse endere\347o. Isso \351 feito de uma maneira simples. Chamamos o pr\363ximo endere\347o e colocamos o endere\347o de retorno em nossa vari\341vel local.
call @f @@: pop dword [ebp-4] sub dword [ebp-4], @b-lancet
\311 isso. Agora a vari\341vel em [ebp-4] cont\351m o nosso "endere\347o base". Cada vez quisermos chamar outra fun\347\343o ou acessar nossos dados (strings com nomes, lembra?) n\363s devemos fazer o seguinte:
mov ebx, [ebp-4] add ebx, ourDll-lancet push ebx mov ebx, [ebp-8] ;assume that we stored the address of LoadLibraryA at [ebp-8] call dword ebx
O c\363digo acima \351 um equivalente a LoadLibraryA ("name_of_our_dll"). Agora vamos falar sobre a execu\347\343o propriamente dita. Embora saibamos onde estamos, n\343o temos idsia de qual \351 o endere\347o de LoadLibraryA. H\341, pelo menos, duas maneiras de obter o endere\347o direitinho. A primeira foi descrita neste meu artigo. A segunda tamb\351m \351 interessante - PEB. Sim, n\363s estamos indo acessar o Process Environment Block, encontrar a estrutura LDR_MODULE que se refere ao KERNEL32.DLL, e obter o seu endere\347o de base (que \351 tamb\351m um handle para a biblioteca). Alguns podem dizer que essa forma n\343o \351 confi\341vel, n\343o \351 est\341vel e que \351 at\351 perigosa, mas eu digo que declara\347\365es como essas n\343o s\343o s\351rias. N\343o vamos mudar nada nessas estruturas. N\363s s\363 vamos analis\341-las. De que jeito podemos encontrar o PEB? Isso \351 muito simples. Ele est\341 localizado em [FS:0x30]. Uma vez que o temos, estamos a caminho do endere\347o PEB_LDR_DATA, que est\341 no PEB+0x0C. A fim de analisar a estrutura PEB_LDR_DATA, devemos declarar o seguinte em nosso c\363digo Assembly:
struc list_entry { .flink dd ? ;pointer to next list_entry structure .blink dd ? ;pointer to previous list_entry structure } struc peb_ldr_data { .length dd ? .initialized db ? db ? db ? db ? .ssHandle dd ? .inLoadOrderModuleList list_entry ;we are going to use this list .inMemoryOrderModuleList list_entry .inInitializationOrderModuleList list_entry } struc ldr_module { .inLoadOrderModuleList list_entry ;pointers to previous and next modules in list .inMemoryOrderModuleList list_entry .inInitializationOrderModuleList list_entry .baseAddress dd ? ;This is what we need! .entryPoint dd ? .sizeOfImage dd ? .fullDllName unicode_string ;full path to the module file .baseDllName unicode_string ;name of the module file .flags dd ? .loadCount dw ? .tlsIndex dw ? .hashTable list_entry .timeDateStamp dd ? }
Vou deixar a implementa\347\343o da fun\347\343o de an\341lise de m\363dulo de lista pra voc\352. Voc\352 s\363 tem que manter em mente que a string que voc\352 vai verificar \351 representada pela estrutura UNICODE_STRING (descrita no artigo mencionado acima). Outra coisa para lembrar \351 que \351 melhor implementar a fun\347\343o case insensitive de compara\347\343o de strings. Depois de encontrar o LDR_MODULE, cuja baseDllName \351 "kernel32.dll", voc\352 tem seu handle (apenas no campo baseAddress). Voc\352 pode usar a fun\347\343o _get_proc_address do mesmo artigo (mencionado acima) para obter o endere\347o da fun\347\343o LoadLibraryA. Tendo esse endere\347o, voc\352 est\341 pronto para carregar sua DLL (fa\347a a inje\347\343o real). Sugest\343o pessoal - n\343o colocar um monte de c\363digo para a fun\347\343o DllMain.
LoadLibraryA retorna um handle para a DLL rec\351m-carregada, que voc\352 pode usar para localizar a fun\347\343o de inicializa\347\343o (lembre que ela tem que ser exportada por sua DLL e de prefer\352ncia usar a conven\347\343o stdcall). Depois que voc\352 usar _get_proc_address em sua fun\347\343o de inicializa\347\343o, chame-a e passe o endere\347o do bloco de dados como um par\342metro (que foi passado para a nossa fun\347\343o lancet como um par\342metro na pilha):
push dword [ebp+8] ;parameter passed to lancet is here call dword [ebp-12] ;assume that you stored the address of the initialization ;function here
\311 isso a\355. O seu c\363digo agora pode retornar. A DLL foi injetada e inicializada. Inje\347\343o
De alguma forma, perdemos o empolgante processo de inje\347\343o de nosso c\363digo lancet. N\343o se preocupe, n\343o me esqueci disso. Como j\341 mencionei acima, temos que alocar dois blocos - para c\363digo e para dados. Isso pode ser feito chamando a fun\347\343o VirtualAllocEx, o que permite aloca\347\365es de mem\363ria no espa\347o de endere\347o de outro processo.
LPVOID WINAPI VirtualAllocEx( __in HANDLE hProcess, __in_opt LPVOID lpAddress, __in SIZE_T dwSize, __in DWORD flAllocationType, __in DWORD flProtect );
Use MEM_COMMIT como flAllocationType, e PAGE_EXECUTE_READWRITE e PAGE_READWRITE para aloca\347\343o de c\363digo e bloco de dados, respectivamente. Essa fun\347\343o retorna o endere\347o do bloco alocado no espa\347o de endere\347o do processo especificado ou NULL. A fun\347\343o WriteProcessMemory da API \351 usada para copiar o c\363digo e os dados no espa\347o de endere\347o do processo que ser\341 o alvo.
BOOL WINAPI WriteProcessMemory( __in HANDLE hProcess, __in LPVOID lpBaseAddress, __in LPCVOID lpBuffer, __in SIZE_T nSize, __out SIZE_T*lpNumberOfBytesWritten );
Depois de ter copiado tanto o dado quanto o c\363digo, voc\352 vai querer chamar sua fun\347\343o thread. A \372nica maneira de chamar uma fun\347\343o que reside na mem\363ria de outro processo \351 chamando a API CreateRemoteThread.
HANDLE WINAPI CreateRemoteThread( __in HANDLE hProcess, //the handle to our process __in LPSECURITY_ATTRIBUTES lpThreadAttributes, //may be NULL __in SIZE_T dwStackSize, //may be 0 __in LPTHREAD_START_ROUTINE, //the address of our code block __in LPVOID lpParameter, //the address of our data block __in DWORD dwCreationFlags, //may be 0 __out LPDWORD lpThreadId //may be NULL );
Essa fun\347\343o retorna um handle para a thread remota, que, por sua vez, pode ser passada para a fun\347\343o WaiForSingleObject da API, para que possamos receber uma notifica\347\343o em seu retorno.
Eu decidi n\343o abordar as possibilidades que sua DLL pode fazer enquanto estiver anexada ao processo que ser\341 o alvo. Deixo isso pra voc\352. Espero que este artigo n\343o tenha sido muito confuso, e sim \372til.
\u2042
Texto original dispon\355vel em http://syprog.blogspot.com.br/2011/11/advanced-dll-injection.html
. Dia: 14/08/2012
http://syprog.blogspot.com.br/?m=1
System Programming
This blog is dedicated to low level programming in Assembler and C/C++ (although, C++ is unwelcome) in either *Nix or Windows based operating systems.
Saturday, November 26, 2011
.NET
Melhorando desempenho de aplicativos .NET
Parte 12
Segunda-feira, 13/08/2012 \340s 10h30, por Hovhannes Avoyan
Em nosso artigo anterior, n\363s discutimos sobre \u201cItera\347\343o e Looping\u201d. Hoje iremos nos concentrar na otimiza\347\343o de opera\347\365es de string.
O Framework .NET fornece o tipo de dado System.String para representar uma string. A manipula\347\343o intensiva de strings pode degradar o desempenho. Toda vez que voc\352 executar uma opera\347\343o para mudar os dados da strings, a string original na mem\363ria \351 descartada para um coletor de lixo posterior e uma nova \351 criada para armazenar os dados de sequ\352ncia de novos caracteres. Observe tamb\351m que o tipo String \351 um tipo de refer\352ncia, por isso os conte\372dos dela s\343o armazenados em um heap gerenciado. Como resultado, as strings devem ser coletadas para serem limpas.
Esta se\347\343o resume as recomenda\347\365es que voc\352 deve levar em conta quando se trabalha com strings.
Evite concatena\347\343o de string ineficiente A concatena\347\343o excessiva de strings resulta em muitas opera\347\365es de aloca\347\343o e desaloca\347\343o, pois cada vez que voc\352 realiza uma opera\347\343o para alterar a string, uma nova \351 criada e a antiga \351 posteriormente coletada pelo coletor de lixo.
Se voc\352 concatenar strings literais, o compilador ir\341 concaten\341-las no tempo de compila\347\343o. //\u2019Hello\u2019 and \u2018world\u2019 are string literals String str = \u201cHello\u201d + \u201cworld\u201d;
Se voc\352 concatenar as sequ\352ncias n\343o-literais, fa\347a uma concatena\347\343o neles em tempo de execu\347\343o. Ent\343o, usar o operador + cria v\341rios objetos strings no heap gerenciado. Use o StringBuilder para manipula\347\365es de strings complexas e tamb\351m quando precisar concatenar strings v\341rias vezes.
// using String and \u2018+\u2019 to append String str = \u201cSome Text\u201d; for ( \u2026 loop several times to build the string \u2026) { str = str + \u201d additional text \u201c; } // using String and .Append method to append StringBuilder strBuilder = new StringBuilder(\u201cSome Text \u201c); for ( \u2026 loop several times to build the string \u2026) { strBuilder.Append(\u201d additional text \u201c); }
Use + quando o n\372mero de Appends for conhecido
Se voc\352 souba o n\372mero de appends a ser feito e voc\352 est\341 concatenando as strings de uma vez s\363, prefira o operador + para a concatena\347\343o. String str = str1+str2+str3;
Caso voc\352 concatene as strings em uma \372nica express\343o, ent\343o ser\341 preciso fazer apenas uma chamada para String.Concat. Isso n\343o resulta em nenhuma string tempor\341ria (para combina\347\365es parciais das strings a serem concatenadas).
Evite usar + em strings dentro de um loop ou de m\372ltiplas itera\347\365es. Use StringBuilder no lugar. Use StringBuilder quando o n\372mero de Appends for desconhecido
Se voc\352 n\343o souber o n\372mero de appends que precisa ser feito, o que poderia ser o caso quando iterar por um loop ou a constru\347\343o de pesquisas din\342micas de SQL, use a classe StringBuilder, como mostrado no exemplo seguinte:
for (int i=0; i< Results.Count; i++) { StringBuilder.Append (Results[i]); }
A classe StringBuilder come\347a com uma capacidade inicial padr\343o de 16. Strings inferiores a capacidade inicial s\343o armazenadas no objeto StringBuilder. A capacidade inicial do buffer pode ser definida usando o seguinte construtor sobrecarregado.
public StringBuilder (int capacidade); Voc\352 pode continuar a concatenar sem aloca\347\365es adicionais at\351 voc\352 consumir o buffer alocado pr\351viamente. Como resultado, usar um objeto StringBuilder \351 muitas vezes mais eficiente do que usar objetos string para concatena\347\343o. Se voc\352 concatenar mais ainda, a classe StringBuilder vai gerar um novo buffer com o dobro da da capacidade atual.
Ent\343o, se voc\352 come\347ar com um StringBuilder de tamanho 16 e exceder o limite, o StringBuilder vai aloca um novo buffer de tamanho 32 e copiar a sequ\352ncia antiga para o novo buffer. O antigo ser\341 inacess\355vel e se tornar\341 eleg\355vel para garbage collection. Voc\352 deve tentar configurar a capacidade inicial do StringBuilder a um valor ideal para reduzir o custo de novas atribui\347\365es. A melhor maneira para controlar o consumo de mem\363ria usando o CLR Profiler \351 determinar o valor ideal para o seu caso.
Trate StringBuilder como um acumulador Voc\352 pode tratar o StringBuilder como um acumulador ou buffer reutiliz\341vel. Isso ajuda a evitar as aloca\347\365es de sequ\352ncias tempor\341rias durante as m\372ltiplas itera\347\365es append. Aqui est\343o algumas das situa\347\365es onde isso pode ajudar:
StringBuilder sb; sb.Append(str1); sb.Append(str2); Use the preceding code rather than the following. sb.Append(str1+str2);
Isso \351 porque voc\352 n\343o precisa fazer a str1 + str2 tempor\341ria para anexar str1 e str2. Concatenando as strings de v\341rias fun\347\365es O seguinte c\363digo mostra um exemplo disso:
StringBuilder sb; sb.Append(f1()); sb.Append(f2()); sb.Append(f3());
O fragmento de c\363digo anterior resulta em aloca\347\365es de string tempor\341rios para os valores de retorno por parte das fun\347\365es f1 (...), f2 (), f3 (). Voc\352 pode evitar essas aloca\347\365es tempor\341rias usando o seguinte padr\343o:
void f1( sb,); void f2( sb,); void f3( sb,);
Neste caso, a inst\342ncia StringBuilder \351 diretamente passada como um par\342metro de entrada para os m\351todos. O sb.Append \351 chamado diretamente no corpo da fun\347\343o, o que evita a atribui\347\343o de strings tempor\341rias. Use o m\351todo sobrecarregado de compara\347\343o para compara\347\365es de strings Case-Insensitive Considere cuidadosamente como voc\352 executa as compara\347\365es entre strings case-insensitive. Evite usar o ToLower, como mostrado no c\363digo a seguir, porque voc\352 acaba criando objetos string tempor\341rios.
// Bad way for insensitive operations because ToLower creates temporary strings String str=\u201dNew York\u201d; String str2 = \u201cNew york\u201d; if (str.ToLower()==str2.ToLower()) // do something
The more efficient way to perform case-insensitive string comparisons is to use the Compare method.
str.Compare(str,str2,false);
O m\351todo String.Compare utiliza a informa\347\343o na propriedade CultureInfo.CompareInfo para comparar as strings culture-sensitives.
***
Artigo original dispon\355vel em: http://blog.monitis.com/index.php/2012/05/04/improving-net-application-performance-part-12-string-operations/
Com o Rational Team Concert a sua equipe de TI pode trabalhar e colaborar melhor. Avalie! Fa\347a o download gratuito.
Hovhannes Avoyan
\351 CEO do Monitis, um provedor de gerenciamento de sistemas e de monitoramento de software on-demand.
P\341gina do autor Email Leia os \372ltimos artigos publicados por hovhannesavoyan
Melhorando o desempenho de aplicativos .NET - Parte 11
Melhorando o desempenho de aplicativos .NET - Parte 10
Melhorando desempenho de aplicativos .NET - Parte 09
Melhorando desempenho de aplicativos .NET - Parte 08
COMENTE TAMB\311M
1 COMENT\301RIO
Felipe Franco
Vamos supor que eu tenho uma query "Select * from Clientes" o que \351 mais vantajoso em termos de concatena\347\343o?
Dim sb as New StringBuilder() sp.Append("Select").AppendLine() sp.Append(" *").AppendLine() sp.Append("From").AppendLine() sp.Append(" Clientes")
Ou
Dim sb as String = "Select" & _ " *" & VbCrLf & _ "From" & VbCrLf & _ " Cliente"
Aguardo resposta. H\341 19 horas Responder QUAL A SUA OPINI\303O?
Dia: 15/08/2012
Oracle libera primeira atualiza\347\343o com suporte total ao Java SE 7 no OS X
Publicado em agosto 14th, 2012
Escrito por: Eduardo Marques
Em abril passado, a Oracle liberou a primeira vers\343o do Java SE 7 Update 4 e do JavaFX 2.1 para a comunidade Mac. Hoje a empresa anunciou o Java SE 7 Update 6, o qual traz pela primeira vez suporte total ao Java SE 7 para a plataforma desktop da Apple.
A atualiza\347\343o \351 acompanhada do Java Runtime Environment (JRE) , do Java Development Kit (JDK), do JavaFX 2.2 e do JavaFX Scene Builder .
A empresa ressaltou tamb\351m que consumidores em breve poder\343o fazer o download do JRE para OS X atrav\351s do site java.com, assim como fazem j\341 acontece para todos os outros sistemas operacionais. Al\351m disso, ela ir\341 fornecer atualiza\347\365es autom\341ticas para o sistema operacional da Apple ao mesmo tempo do Windows \u2014 uma das principais raz\365es de a Ma\347\343 ter passado a responsabilidade do Java para a Oracle.
Massachusetts Institute of Technology alega que iOS \351 quase inviol\341vel
Publicado em agosto 13th, 2012
Escrito por: Hugo Bessa
. Sem d\372vida, os sistemas operacionais da Apple d\343o uma certa sensa\347\343o de seguran\347a para seus clientes. O novo Gatekeeper do Mountain Lion e a App Store do iOS s\343o meios de praticamente blindar os sistemas de v\355rus e outros softwares mal intencionados. Para refor\347ar toda essa ideia de \u201csistema inviol\341vel\u201d, o Massachusetts Institute of Technology (MIT) falou um pouco sobre a seguran\347a no iOS.
As tecnologias adotadas pela companhia protegem os dados dos clientes da Apple t\343o bem, que em certas ocasi\365es, \351 imposs\355vel para a pol\355cia realizar exames forenses em dispositivos apreendidos de criminosos.
O mais significante \351 o aumento no uso da criptografia, que est\341 causando problemas para os \363rg\343os policiais, quando eles enfrentam sistemas com drivers criptografados.
\311 muito bom escutar uma not\355cia desta vinda de uma respeitada institui\347\343o, mas aten\347\343o nunca \351 demais. At\351 pela rigorosa revis\343o da Apple acabam passando alguns aplicativos mal intencionados. Nenhum sistema \351 perfeitamente blindado.
Desenvolvimento
Feature Detection vs Browser Detection
Quarta-feira, 15/08/2012 \340s 11h00, por Joe Zim
N\363s todos ouvimos que, quando voc\352 est\341 tentando determinar as capacidades JavaScript de um navegador, voc\352 deve evitar analisar string user agent como se fosse uma praga horr\355vel; em vez disso, invista sua confian\347a em uma coisa chamada browser detection. Mas por que dever\355amos? O que h\341 de t\343o ruim sobre a confian\347a em string user agent? Al\351m disso, como eu uso browser detection? Continue lendo e voc\352 encontrar\341 a resposta para todas essas perguntas.
Browser Detection via User Agent Strings H\341 muito tempo, em um planeta muito parecido com o nosso voc\352 pensaria que ele realmente era nosso, criaturas conhecidas como seres humanos usariam JavaScript para analisar strings user agent para determinar qual era o browser que o usu\341rio estava usando para navegar em seu site. Ok, ok, n\343o foi h\341 muito tempo (na verdade, in\372meras pessoas ainda fazem isso) e era na verdade este planeta que chamamos Terra.
A browser detection \351 geralmente feita lendo uma propriedade conhecida como navigator.userAgent, que cont\351m uma string com v\341rias informa\347\365es sobre o navegador que est\341 sendo usado para visitar a p\341gina. Isso pode - e muitas vezes \351 - ser bastante utilizado de forma confi\341vel para determinar realmente o navegador e a vers\343o que est\343o sendo usados, mas ele possui v\341rias advert\352ncias em rela\347\343o ao seu uso para fazer determina\347\365es sobre como funciona o seu c\363digo.
A string userAgent pode ser falsificada com bastante facilidade, e apesar de essa raz\343o ser citada muitas vezes, ela parece menos relevante, porque as \372nicas pessoas que falsificariam essa string seriam pessoas que sabem o que est\343o fazendo e provavelmente esperam que a mudan\347a de valor possa criar problemas.
N\343o \351 poss\355vel saber quais caracter\355sticas os futuros browsers fornecer\343o, ent\343o no momento em que um navegador \351 desenvolvido e muda as features de uma forma que voc\352 n\343o previu, seu script se quebra.
Voc\352 tem que saber que navegadores suportam quais caracter\355sticas. \300s vezes isso \351 muito simples quando se trata de uma caracter\355stica que todos conhecem, mas \351 dif\355cil de usar quando voc\352 precisa manter o controle de v\341rios recursos.
Browse detection
A browse detection \351 muito mais confi\341vel para determinar se voc\352 pode usar um recurso porque ela vai direto nele e pergunta se ele existe. Bem, n\343o \351 literalmente assim, mas \351 mais ou menos isso. A browse detection utiliza o fato de que se voc\352 tenta acessar uma propriedade de um objeto que n\343o existe, ela ser\341 undefined. Ent\343o, se a propriedade ou a fun\347\343o que voc\352 precisa n\343o \351 undefined, ent\343o voc\352 est\341 livre para us\341-la; caso contr\341rio, voc\352 pode verificar as alternativas para ver se elas est\343o dispon\355veis.
Se voc\352 precisa verificar a exist\352ncia de uma caracter\355stica diretamente no escopo global, certifique-se de verific\341-la como uma propriedade window (por exemplo, window.propertyName), porque se uma propriedade n\343o existe quando voc\352 tenta verific\341-la sem o prefixo window, voc\352 vai acabar com um ReferenceError. Confuso? Aqui est\341 um exemplo, a verifica\347\343o de capacidades XHR.
// Does the browser use the standard XHR? if ( window.XMLHttpRequest ) { xhr = new XMLHttpRequest(); } // If the browser isn't using the standard, let's // check for the alternative else if ( window.ActiveXObject ) { xhr = new ActiveXObject( "Microsoft.XMLHTTP" ); } // Here, we're not using window in front. If // XMLHttpRequest doesn't exist, this will // result in an error instead of moving on to // the else if if ( XMLHttpRequest ) { xhr = new XMLHttpRequest(); } // This will never run because the top will either // be true or will be an error. else if ( ActiveXObject ) { xhr = new ActiveXObject( "Microsoft.XMLHTTP" ); }
Al\351m dos motivos listados acima como raz\365es para n\343o usar a an\341lise de user agent, h\341 uma raz\343o adicional para usar feature detection: ela testa para as caracter\355sticas que voc\352 deseja usar em vez de testar para um navegador. Isso faz muito mais sentido para mim.
Feature Detection FTW
Ent\343o, como voc\352 pode ver, browse detection \351 simples e sensata. Por outro lado, \351 cheia de buracos. Eu posso ver a browse detection funcionando para casos de uso em que voc\352 realmente precisa saber qual navegador est\341 sendo usado, mas utiliz\341-la para determinar quais recursos s\343o implementados pelo navegador n\343o \351 uma grande ideia. Espero que a maioria de voc\352s j\341 saiba disso, mas eu tenho certeza de que h\341 muita gente por a\355 que aprendeu agora. Deus o aben\347oe e feliz codifica\347\343o!
\u2042
Texto original dispon\355vel em http://www.joezimjs.com/javascript/feature-detection-vs-browser-detection/
Joe Zim
\351 desenvolvedor web e j\341 trabalhou em v\341rias frentes: HTML, CSS, JavaScript, jQuery, PHP, Joomla, WordPress, Node.js. Atualmente, trabalha via home office para a empresa eInnov8/Vistelar/Martial Arts Marketing Network.
P\341gina do autor Email
Leia os \372ltimos artigos publicados por joezim
$script.js vs RequireJS: compara\347\365es do gerenciamento de depend\352ncia
Desvendando o Plugin jQuery Boilerplate
Tr\352s maneiras para analisar uma string de consulta em uma URL
Padr\343o de projeto de software - JavaScript: Chain of Responsibility
QUAL A SUA OPINI\303O?
iOS + Desenvolvimento + Mobile
Trabalhando o conceito de Introspec\347\343o no iOS Quarta-feira, 15/08/2012 \340s 10h30, por Mauro Thiago da Silva
O nome pode at\351 parecer meio psicod\351lico e fora do nosso contexto informatizado, mas a \u201cintrospection\u201d - ou melhor dizendo, instrospec\347\343o - \351 a capacidade de obtermos informa\347\365es ou cadeias de objetos relacionados dentro de um grupo. Hoje iremos ver como isso funciona no iOS. Normalmente, esse tipo de a\347\343o \351 utilizada para ler, manipular ou at\351 mesmo desalocar o objeto nesses eventos da programa\347\343o.
Vou relacionar abaixo uma lista de m\351todos documentos pela Apple Developer Program: -(BOOL)isKindOfClass:classType Is the object a member of that class or a subclass?
-(BOOL)isMemberOfClass:classType Is the object a member of that class? -(BOOL)respondsToSelector:aSelector Does an object (class or instance) respond to the selector\u2019s method? +(BOOL)instancesRespondToSelector:aSelector Do instances of the class respond to the selector\u2019s method? +(BOOL)isSubclassOfClass:classType Is this class a subclass of the specified class? -(id)performSelector:aSelector Ask the class to perform the method specified by the selector, returning whatever the method is intended to return. -(id)performSelector:aSelector withObject:someObject Performs the selector, passing in someObject as the first (and hopefully only) argument, returning whatever the method is designed to return. -(id)performSelector:aSelector withObject:someObject withObject:anotherObject Perform the selector, passing in the two arguments in that order to the (assumed) two parameters defined by the selector, returning whatever the method is designed to return.
Figura. 01 \u2013 iOs Developer Library \u2013 Cocoa Core Competencies
Irei exemplificar algumas formas de visualizar todo esse processo maluco de introspec\347\343o. Vou demonstrar utilizando o \u201cLabel\u201d e o \u201cButton\u201d em nosso projeto de exemplo.
Figura. 02 - Tela de cria\347\343o do App
Iremos criar um simples App para entendermos o funcionamento desse processo.
Figura. 03 - Detalhes para gerar App
O nome do produto e o company identifier ficam a seu crit\351rio. Eu selecionei a fam\355lia de dispositivo iPhone e estou utilizando Storyboard e ARC. Para aqueles que ainda n\343o se familiarizaram com o Storyboard, em um pr\363ximo artigo aprofundarei mais o tema. Depois de criado o projeto, vamos ao que interessa!
Iremos adicionar em nossa interface uma View a mais, quatro bot\365es e cinco label\u2019s para que possamos entender a ideia. Abaixo, um \u201cscreenshot\u201d de como ir\341 ficar nossa tela no App:
Figura. 04 - Tela de nosso prot\363tipo
Depois de montado todo o visual de nosso App, vamos partir para a codifica\347\343o e liga\347\343o dos bot\365es criados.
Figura. 05 - Tela do prot\363tipo pronta
Em nosso arquivo \u201cViewController.h\u201d temos os seguintes dados:
#import@interface ViewController : UIViewController - (IBAction)btn01:(UIButton *)sender; - (IBAction)btn02:(UIButton *)sender; - (IBAction)btn03:(UIButton *)sender; - (IBAction)btn04:(UIButton *)sender; @end
S\343o declarados todos os bot\365es que estamos utilizando, para utiliza\347\343o na liga\347\365es, conforme imagem anterior de interface.
No arquivo \u201cViewController.m\u201d teremos os seguintes m\351todos implementados:
#import "ViewController.h" @interface ViewController () @end @implementation ViewController - (IBAction)btn01:(UIButton *)sender { for (UIView *aView in self.view.subviews) { if ([aView isMemberOfClass:[UILabel class]]) { aView.backgroundColor = [UIColor redColor]; } } } - (IBAction)btn02:(UIButton *)sender { for (UIView *aView in self.view.subviews) { if ([aView isKindOfClass:[UIView class]]) { aView.backgroundColor = [UIColor blueColor]; } } } - (IBAction)btn03:(UIButton *)sender { for (id aView in self.view.subviews) { if ([aView canPerformAction:@selector(setText:) withSender:sender]) { [aView setText:@"Introspection iOS"]; [aView setBackgroundColor:[UIColor blueColor]]; [aView setTextColor:[UIColor whiteColor]]; } } } - (IBAction)btn04:(UIButton *)sender { for (UIView *aView in self.view.subviews) { if ([aView isMemberOfClass:[UIView class]]) { for (UILabel *aLabel in aView.subviews) { aLabel.backgroundColor = [UIColor greenColor]; } } } }
Depois de declarados os m\351todos no arquivo .H, basta efetuar as liga\347\365es e iniciar os testes, como iremos ver logo abaixo:
Figura. 06 - Tela de liga\347\343o do objeto
.
Figura. 07 - Tela de liga\347\343o, escolhendo tipo de a\347\343o no bot\343o
Iniciando o processo de testes, veremos que cada bot\343o ir\341 efetuar a\347\365es diferentes para os objetos relacionados em tela. Esse processo s\363 \351 poss\355vel devido \340 utiliza\347\343o da introspec\347\343o. O processo que utilizamos torna poss\355vel a manipula\347\343o dos objetos ali integrados. Em nossos dia-a-dia, isso melhora a nossa forma de manipular objetos que est\343o sendo utilizados em tempo de execu\347\343o.
O Core Foundation permite uma variedade de introspec\347\343o, principalmente para fins de depura\347\343o e manipula\347\343o. Veja com mais detalhes nas documenta\347\365es em \u201cApple Developer Program\u201d.
Espero ter contribu\355do um pouco mais com o aprendizado de voc\352s. Como disse anteriormente, iremos abordar um pouco mais sobre \u201cStoryBoard\u201d no pr\363ximo artigo. At\351 l\341 e obrigado!
Refer\352ncias:
Cocoa Fundamentals Guide. 2010 Apple Inc. P\341ginas 131-136
Learning the iOS 4 SDK for Javascript
Programmers. C\341pitulo 8, Objective-C Touch
Fundamentals. P\341gina 198
http://developer.apple.com
http://developer.apple.com/library/ios/navigation/
http://developer.apple.com/library/ios/#DOCUMENTATION/General/Conceptual/DevPedia-CocoaCore/Introspection.html
Mauro Thiago da Silva
\351 formado em Ci\352ncia da Computa\347\343o, pelo Uninove, Jovem Empreendedor SEBRAE-SP, d\341 consultoria ao mercado corporativo na administra\347\343o e manuten\347\343o de computadores, e \351 desenvolvedor para iOS (iPhone, iPod, iPad), PHP/MySQL. Trabalha atualmente em uma empresa de m\355dia para conte\372do customizado, no qual participa com desenvolvimento e gest\343o de produtos para dispositivos da Apple. Tamb\351m possui experi\352ncia na \341rea de DTP (Desktop Publishing).
P\341gina do autor Email
Leia os \372ltimos artigos publicados por mauro_thiago_da_silva
Reproduzindo arquivos MP4 em aplicativos para iOS
Convertendo imagens com ImageMagick
Relat\363rio de Hardware
Listar Arquivos e Pastas
QUAL A SUA OPINI\303O?
Confira dois infogr\341ficos sobre a Apple: um focado em apps educativos, o outro nas suas lojas de varejo
Publicado em 16 de agosto de 2012
Escrito por: Rafael Fischmann
J\341 o segundo foi criado pelo Online MBA e fala sobre as Apple Retail Stores: quantas existem atualmente, compara\347\365es de receitas, n\372meros de visitantes e de empregados, entre outros.
Descobriram algo de legal que n\343o sabiam antes? ;-)
Desenvolvimento
Fazendo deploys sem dificuldade
Quinta-feira, 16/08/2012 \340s 11h00, por Alex Tatiyants
O objetivo de cada equipe de software \351 entregar o produto acabado ao cliente final. Contudo, fazer isso \351 surpreendentemente dif\355cil para muitas equipes.
Em um mundo perfeito, o ato do deploy de software para produ\347\343o \351 sem dificuldade e confi\341vel. Para saber se voc\352 vive de fato em tal mundo, tente responder \340 seguinte pergunta: algu\351m na equipe pode executar um \372nico comando e implementar o aplicativo inteiro para a produ\347\343o?
Se voc\352 pode dizer "sim" honestamente, parab\351ns e obrigado por ter vindo. Para o resto de n\363s, vamos considerar sobre como fazer seu deploy da produ\347\343o sem dificuldade e confi\341vel. Fa\347a o deploy, e fa\347a de novo e frequentemente Como em muitas coisas na vida, quanto mais voc\352 faz alguma coisa, mais f\341cil fica. N\343o \351 s\363 isso: voc\352 tamb\351m confia mais nos resultados. Se quiser deploys de produ\347\343o sem dificuldade e confi\341veis, voc\352 precisa implementar seu aplicativo de produ\347\343o o mais frequentemente poss\355vel. Na verdade, os f\343s do Continuous Delivery defendem a implanta\347\343o de cada mudan\347a que voc\352 faz para o seu aplicativo imediatamente para o cliente final. Embora isso possa ser um pouco demais para algumas organiza\347\365es (a maioria?), a capacidade de faz\352-lo \351 bastante atraente.
A pr\341tica frequente \351 importante, mas n\343o \351 suficiente. Com o risco de afirmar o \363bvio, os passos que voc\352 tomar para implementar n\343o devem mudar de acordo com o ambiente que voc\352 est\341 implantando. Em outras palavras, o deploy no seu CI, teste, plataforma, regress\343o e, o mais importante, na produ\347\343o deve ser a mesma. Os mesmos scripts devem rodar, os mesmos passos devem ser tomados etc. Profissionais Agile chamam isso de um caminho de implanta\347\343o repet\355vel.
Voc\352 pode estar pensando: "Bem, \351 claro que eu deveria fazer a mesma coisa em produ\347\343o como em qualquer outro lugar", "Por que n\343o faria?". Bem, o fato \351 que a produ\347\343o \351 \372nica. Ao contr\341rio de todos os outros ambientes, voc\352 provavelmente vai querer minimizar ou eliminar o tempo de inatividade. Isso significa que voc\352 n\343o pode simplesmente destruir tudo e reconstruir a partir do zero. A n\343o ser que queira clientes irritados.
"Hit me baby one more time"
Uma maneira de lidar com esse problema \351 o deploy de um servidor existente. Em outras palavras, voc\352 pode apenas pegar o seu script de deploy automatizado, apont\341-lo para um servidor de produ\347\343o existente e comece os trabalhos! O script, presumivelmente, passaria e atualizaria, removeria ou adicionaria todos os componentes necess\341rios para o seu aplicativo. Essa estrat\351gia pode funcionar, mas s\363 se voc\352 fizer exatamente a mesma coisa em ambientes de n\343o produ\347\343o. Se, por outro lado, voc\352 est\341 implementando a partir do zero em qualquer outro lugar, essa estrat\351gia n\343o \351 boa, porque voc\352 est\341 fazendo as coisas de maneira diferente em ambientes distintos.
O bom e velho troca-troca
Outra forma de fazer o deploy da produ\347\343o \351 por meio de implementa\347\365es A-B (Jez Humble e Dave Farley chamam isso de implementa\347\365es azul-verdes). A ideia \351 que voc\352 levanta um ambiente id\352ntico ao de produ\347\343o e simplesmente troca para ele. Essa abordagem \351 interessante, porque voc\352 n\343o tem que se preocupar em derrubar servidores.
Essa abordagem faz com que voc\352 se aproxime muito do objetivo de ter um caminho de deploy que pode ser repetido, pois voc\352 pode literalmente fazer a mesma coisa em todos os ambientes.
Conclus\343o
Deploys de produ\347\343o sem dificuldade s\343o poss\355veis. N\343o s\363 isso, elas valem o investimento. A receita \351 simples e requer apenas dois ingredientes: automa\347\343o e consist\352ncia. Boa sorte.
\u2042
Texto original dispon\355vel em http://tatiyants.com/painless-production-deployments/
Com o Rational Team Concert a sua equipe de TI pode trabalhar e colaborar melhor. Avalie! Fa\347a o download gratuito.
Alex Tatiyants
gosta de escrever coisas engra\347as sobre tecnologia. \311 criador do JS.js e do movimento MoreSQL, al\351m de inventor do Guilt Driven Development.
P\341gina do autor Email
Leia os \372ltimos artigos publicados por alextatiyants
Ent\343o voc\352 pensa que pode liderar?
O valor da documenta\347\343o
Aplicando controle de vers\343o em banco de dados
As pr\341ticas de desenvolvimento Agile s\343o necess\341rias?
QUAL A SUA OPINI\303O?
Dia: 17/08/202
Em parceria com o MacMagazine, Distimo traz dados bem interessantes sobre a Mac App Store brasileira
Vira e mexe n\363s trazemos aqui para o site artigos sobre pesquisas realizadas pela Distimo, em sua maioria focadas no mundo mobile. Recentemente fomos contatados diretamente por ela a fim de realizarmos um estudo espec\355fico, juntos.
Para fugir um pouco do padr\343o, indagamos a Distimo sobre algumas coisas referentes \340 Mac App Store brasileira. Com dados recentes sobre os 200 aplicativos mais populares da loja, ela tra\347ou o seguinte gr\341fico:
Como d\341 pra ver, apps gratuitos t\352m uma m\351dia consolidada de 3.300 downloads di\341rios, enquanto que nos pagos esse n\372mero despenca para 500. \311 interessante notar que a receita m\351dia consolidada dos aplicativos pagos (incluindo compras diretas e In-App Purchases) \351 de US$8.500 por dia \u2014 n\372mero bastante elevado, mas que nem chega ao chul\351 da loja americana, que supera os US$320.000.
Por falar em In-App Purchases, observem a seguir como a ado\347\343o delas \351 pequena nos softwares pagos para Mac: 93% dos aplicativos mais populares s\343o de pagamento \372nico, 4% s\343o pagos e tamb\351m t\352m In-App Purchases e apenas 2% s\343o gratuitos com In-App Purchases.
Comparativamente, no mundo iPhone a Distimo apurou uma m\351dia de 80% das receitas vindo de In-App Purchases \u2014 um cen\341rio praticamente todo oposto ao visto acima.
Para terminar, confira a seguir a lista dos apps gratuitos mais populares da Mac App Store brasileira:
Dica Git da Semana: Git Notes
Sexta-feira, 17/08/2012 \340s 11h00, por Alex Blewitt
Em uma palestra do ano passado para a Comunidade Java de Londres, apresentei Git e Gerrit (com base nos screencasts que fiz anteriormente). Uma das coisas que eu demonstrei foi o uso de git notes, ent\343o eu pensei em escrever isso.
Quando os arquivos s\343o comitados em um reposit\363rio Git, eles s\343o abordados por um hash do conte\372do. O mesmo \351 verdadeiro para trees e commits. Um dos benef\355cios dessa estrutura \351 que os objetos n\343o podem ser modificados depois de terem sido comitados (pois isso mudaria esse hash).
No entanto, \340s vezes \351 desej\341vel ser capaz de adicionar metadados a um commit depois de ele j\341 ter sido comitado. Existem tr\352s formas de fazer isso:
Corrigir a mensagem de commit para adicionar metadados adicionais, aceitando que isso mudar\341 o branch.
Criar um merge node com um commit mais detalhado, e fazer um push (para que o commit anterior seja retido e possa ser fast-foward). Adicionar metadados adicionais na forma de git notes.
Dessas tr\352s op\347\365es, apenas a \372ltima n\343o mudar\341 o branch atual.
Git Notes
Git Notes s\343o, na verdade, um 'branch' separado do reposit\363rio (armazenado em .git/refs/notes). Eles n\343o aparecem no comando git branch (que lista .git/refs/heads por padr\343o). No entanto, embora voc\352 possa conferi-lo e atualiz\341-lo manualmente, existe um comando que ajuda voc\352 a fazer isso; git notes.
(master) $ git log --oneline 056ca11 More Stuff Again 9defb31 MoreStuff 0c7ff4f Additional 19b6cdf Initial (master) $ git notes show (master) $ git notes add -m "ToDo: Fix stuff" (master) $ git notes show ToDo: Fix stuff (master) $ git log (master) $ git log commit 056ca11c01b47e2bfe1e51178b65c80bbdeef7b0 \u2026 More Stuff Again Notes: ToDo: Fix stuff
Quando voc\352 olha para a sa\355da do git log, ela confere para ver se h\341 um note associado e, se houver, imprime-o como se ele fosse um ap\352ndice do commit. Al\351m disso, as notas s\343o mut\341veis e podem ser atualizados ao longo do tempo:
(master) $ git notes add --force -m "ToDone: Fixed stuff" Overwriting existing notes for object 056ca11c01b47e2bfe1e51178b65c80bbdeef7b0 (master) $ git notes show ToDone: Fixed stuff
A vantagem das notas \351 que elas podem ser atualizadas sem alterar a mensagem de commit (e, portanto, o hash) do item ao qual se referem. Claro, isso pode ser usado tanto para o bem quanto para o mal, mas \351 importante ter em mente a mutabilidade caso precise depender dos conte\372dos das notas.
Gits at\351 o fim...
Na verdade, um t\355tulo melhor poderia ter sido "objetos at\351 o fim", mas eu gostei mais desse.
Uma vez que o Git \351 um banco de dados de conte\372do endere\347\341vel, os pr\363prios notes s\343o objetos do git. Voc\352 pode at\351 mesmo ver o hist\363rico do branch usando git log e at\351 verific\341-lo. Mas como as notas s\343o armazenadas?
(master) $ git log --oneline notes/commits d6ac2b2 Notes added by 'git notes add' 5eb0ee5 Notes added by 'git notes add' (master) $ git checkout notes/commits Note: checking out 'notes/commits'. You are in 'detached HEAD' state. You can look around, make experimental \u2026 HEAD is now at d6ac2b2... Notes added by 'git notes add' ((d6ac2b2...)) $ ls 056ca11c01b47e2bfe1e51178b65c80bbdeef7b0 ((d6ac2b2...)) $ cat 056ca11c01b47e2bfe1e51178b65c80bbdeef7b0 ToDone: Fixed stuff O branch cont\351m uma lista de notas, com nomes de arquivos referenciados pela ID do commit (ou outro objeto) \340 qual correspondem. N\363s podemos fazer uma mudan\347a aqui e atualizar nossas notas: ((d6ac2b2...)) $ echo Note: Git notes are just objects >> 056ca11c01b47e2bfe1e51178b65c80bbdeef7b0 ((d6ac2b2...)) $ git commit -a -m "Note added by me" [detached HEAD 89e6afa] Note added by me 1 files changed, 1 insertions(+), 0 deletions(-) ((89e6afa...)) $ git checkout master Warning: you are leaving 1 commit behind, not connected to any of your branches: 89e6afa Note added by me \u2026 (master) $ git log HEAD^..HEAD commit 056ca11c01b47e2bfe1e51178b65c80bbdeef7b0 \u2026 More Stuff Again Notes: ToDone: Fixed stuff(BARE:master) $ git show refs/notes/review commit bb7cba258eaaf4851b20b66c7ef56775f0cb4367 \u2026 Update notes for submitted changes * Goodbye world diff --git a/f7f38314247063271631cfddf560ea99214cd438 b/\u2026 @@ -0,0 +1,7 @@ +Code-Review+2: Alex Blewitt +Verified+1: Jenkins +Submitted-by: Alex Blewitt +Submitted-at: Thu, 20 Oct 2011 20:11:16 +0100 +Reviewed-on: http://localhost:9080/7 +Project: SkillsMatter +Branch: refs/heads/master (BARE:master) $ git log HEAD^..HEAD commit f7f38314247063271631cfddf560ea99214cd438 \u2026 Goodbye world Change-Id: I692f8de08938f22da9d6e26005ba44c95a1479d7 (BARE:master) $ git log --show-notes=* HEAD^..HEAD commit f7f38314247063271631cfddf560ea99214cd438 \u2026 Goodbye world Change-Id: I692f8de08938f22da9d6e26005ba44c95a1479d7 Notes (review): Code-Review+2: Alex Blewitt Verified+1: Jenkins Submitted-by: Alex Blewitt Submitted-at: Thu, 20 Oct 2011 20:11:16 +0100 Reviewed-on: http://localhost:9080/7 Project: SkillsMatter Branch: refs/heads/masterEnt\343o, n\363s adicionamos um novo commit e depois voltamos para o master; mas como a mensagem de aviso nos disse, isso deixou o commit para tr\341s. N\363s precisamos realmente de atualizar a refer\352ncia refs/notes/comits se quisermos ver os novos valores:
(master) $ git update-ref refs/notes/commits 89e6afa (master) $ git log HEAD^..HEAD commit 056ca11c01b47e2bfe1e51178b65c80bbdeef7b0 \u2026 More Stuff Again Notes: ToDone: Fixed stuff Note: Git notes are just objectsAqui, o git update-ref est\341 atribuindo ao conte\372do de refs/notes/commits o valor 89e6afa... (embora esteja resolvendo-o para um hash completo de 40 caracteres e verificando que ele existe primeiro).
Conven\347\365es
Apenas uma nota r\341pida sobre conven\347\365es; uma vez que o arquivo de git notes est\341 essencialmente no seu branch, o conte\372do n\343o foi mesclado com merges entre os branches. Caso queira fazer um merge com git notes, ent\343o seguir Key: Value em linhas separadas \351 o caminho para alcan\347ar o nirvana do merge do git note. As op\347\365es de merge para git notes permitem anexar notas (ou seja, semelhante a cat noteV1 noteV2) ou classificar e unificar os dados (ou seja, cat noteV1 noteV2 | sort | uniq).
No entanto, as notas n\343o t\352m que ser textuais, nem precisam ser algo que seja mescl\341vel. Elas nem sequer precisam estar nos notes/commits ref, voc\352 pode criar notas com base em qualquer refer\352ncia.
Na verdade, \351 assim que o Gerrit funciona. Ele armazena suas informa\347\365es de revis\343o no reposit\363rio Git em notes/review. Normalmente, isso n\343o aparece (o git log s\363 mostra notas nos notes/commits refspace), mas se quiser voc\352 pode fazer com que ele fa\347a isto:
Nesse caso, eu revisei o commit com dois meus, e um de Jenkins e ele \351 armazenado no reposit\363rio Git, juntamente com tudo mais. Normalmente, ele n\343o \351 recebido pelo usu\341rio quando se faz pulli ou clone, mas \351 um registro permanente no reposit\363rio (e ser\341 vis\355vel se voc\352 fizer, por exemplo, um clone git --mirror). No entanto, se voc\352 quiser fazer um fetch nas notas, ent\343o voc\352 pode fazer isto:
[remote "origin"] fetch = +refs/notes/*:refs/notes/* fetch = +refs/heads/*:refs/remotes/origin/* url = ssh://localhost:29418/SkillsMatter.git push = refs/heads/master:refs/for/master
O fetch refspec em negrito me permite fazer um pull de quaisquer/todos os reviews a partir do reposit\363rio e disponibiliz\341-los no meu clone local.
Exerc\355cio para o leitor...
Uma vez que as notas Git podem conter qualquer blob e n\343o s\343o clonadas por padr\343o (a menos que voc\352 revise especificamente), voc\352 pode criar uma distribui\347\343o e verific\341-la em um reposit\363rio. Em vez de armazen\341-lo em refs/notes/commit, armazene em refs/notes/dist e tenha o bin\341rio gerado a partir do seu sistema de compila\347\343o; exporte como uma Git Note apontando para a tag. Dessa forma, se voc\352 quiser conferir o pacote pr\351-constru\355do para uma determinada tag, voc\352 pode usar refs/notes/dist para apontar para a tag que deseja e extrair o bin\341rio completo.
Claro, voc\352 realmente n\343o precisa usar git notes para armazenar qualquer blob no reposit\363rio em todos os casos; n\343o h\341 nenhuma raz\343o para que voc\352 n\343o possa ter uma tree refs/dists, com um arquivo por tag.
Git notes demonstra o fato de que o Git n\343o \351 apenas um sistema de controle de c\363digo, como Hg ou Bzr. Em vez disso, ele \351 um sistema de arquivos de conte\372do endere\347\341vel, que por acaso \351 capaz de representar trees e arquivos (blobs) de uma maneira f\341cil. Como resultado, o Git ser\341 sempre capaz de ser estendido com funcionalidade como Gerrit e git notes, porque n\343o se limita ao que ele pode armazenar em um reposit\363rio - al\351m disso, a clonagem do reposit\363rio pode ainda ser eficiente, uma vez que os dados que voc\352 puxa a partir de um clone s\343o apenas os objetos acess\355veis a partir de um commit espec\355fico. Como resultado, review notes (e/ou distribui\347\365es bin\341rias) nunca precisam fazer parte de um reposit\363rio clonado, mesmo se forem persistentes e disponibilizadas no mesmo back-end do Git.
\u2042
P\341gina do autorLeia os \372ltimos artigos publicados por alexblewitt
Dica Git da Semana: Git Flow
Dica Git da semana: Revendo Merging
Dica Git da semana: Revendo \315ndices
Dica Git da Semana: entendendo o \355ndice