[==============================================================================] echo $hack. "FREEDOM OF SPEECH, USE IT OR LOSE IT!" - = == ===- -===] - = = = = === === == === == = = =========== -===- =] ___ ___ __ __________.__ / | \ ____ | | __\____ /|__| ____ ____ / ~ \_/ ___\| |/ / / / | |/ \_/ __ \ \ Y /\ \___| < / /_ | | | \ ___/ \___|_ / \___ >__|_ \/_______ \|__|___| /\___ > \/ \/ \/ \/ \/ 0x02\/ ================================================================================ Index ================================================================================ 0x00 Intro .............................................................swestres 0x01 Bitmapssteganografi................................................swestres 0x02 dwm - En fönsterhanterare som håller måttet........................swestres 0x03 Självmodifierande kod..............................................swestres 0x04 Varför jag inte gillar BackTrack och Metasploit....................swestres 0x05 Introduktion till C#, del 1..........................................Retard 0x06 Introduktion till C#, del 1 - Uppgifter..............................Retard 0x07 Introduktion till C#, del 2..........................................Retard 0x08 Introduktion till C#, del 2 - Uppgifter..............................Retard 0xFF The End/Errata............................................................. ================================================================================ 0x00 - Intro swestres ================================================================================ Ho ho ho. Nu är utgåva #2 av hckzine ute! Den digitala verklighetens tryckerier går på högvarv för att tillfredställa efterfrågan. Vid varje gatuhörn i cyberspace står en fattigpojke och skanderar ut "nya hckzine, läs allt om'et!". Moralens väktare köper försynt ett ex, rättar till mononkeln och brister ut i ett "kära nån då!". Feedbacken på förra numret har sträckt sig via ett brett spektrum. Vissa (även kända som fittor) har reagerat på det stundvis vulgära språkbruket, andra har gett tummen upp. En förfrågan på nakenhet har förekommit. Men viss avsaknad i konstruktiv kritik har gjort sig påmind. Ett par individer har påpekat faktafel (bra feedback!) som möjliggjort skrivandet av en kort errata. En del stavfel och särskrivningar har även påpekats, något som är bra för framtiden men som vi inte kommer att peka ut och rätta till retroaktivt. I detta nummer får vi bland annat läsa en introduktionsguide till C#, en liten artikel som behandlar digital steganografi i bitmappar samt mycket mer. Gillar du det vi gör eller tycker du att kvalitén är för låg? Bidra! Vi är och kommer att vara i behov av skribenter, folk som ger feedback, folk som bidrar med hosting och framför allt, folk som sprider ordet! Mycket händer nu i våra liv. Vi som skriver har annat för oss. Jobb, flytt, vägval i livet har kantat tiden från (och lite innan) den första utgåvan. Icke desto mindre är vi fast beslutna att ge er fortlöpande utgåvor av detta förträffliga uttryck för och spridning av kreativitet. Inga artiklar är för enkla eller för svåra för oss och våra läsare. Brett djup über alles. Så, utan att dra ut på tiden allt för mycket, förbered er nu för denna utgåvas innehåll! Eller vänta lite förresten. Jag är ju tvungen att meddela att lösningen på chiffret i artikel 0x05 är inte löst än. Kanske för liten mängd data för att ge en rättvisande frekvenstabell, kanske bristande intresse. Hur som helst vill vi gärna se en lösning på den. Maila in den till hckzine@gmail.com ! ================================================================================ 0x01 - Bitmapssteganografi swestres ================================================================================ Inledning ========= En bitmap (när man pratar om bilddata) var ursprungligen ett bitfält av en fastställd storlek där varje bit antingen var svart eller vit (eller vilken färg man nu hade på sin monitor eller skrivare). Utvecklingen gick framåt, man kunde ha fler färger, större färgdjup. Idag är en bitmap mer eller mindre synonymt med filformatet BMP. Vanliga färgdjup är 8, 16 och 24 bitar. Steganografi är vetenskapen om hur man döljer meddelanden utan att de som inte är behöriga till att läsa meddelandet uppfattar att det är ett meddelande. Man döljer alltså existensen av ett meddelande istället för, som med kryptografi, meddelandets innebörd. Jag kommer i denna artikel att gå igenom två sätt att dölja information i filer av formatet .bmp. Ett av sätten kommer att gå ut på att dölja ett meddelande i själva bilddatan medans det andra kommer att infoga meddelandet i själva filen. En genomgång av filformatet BMP, med mera ========================================= BMP är ett filformat som används för att lagra bilddata. Varje pixel representeras av ett förutbestämt antal bitar. Hur många bitar som representerar en pixel anges i enheten bits per pixel (bpp) och måttet kallas på svenska för färgdjup. Är färgdjupet åtta bitar används åtta bitar för att representera en pixel, och pixeln kan anta 256 (2^8) olika färger som bestäms av en palett, en tabell där varje värde som förekommer associeras med en färgkod i RGB format. RGB-formatet (för er som inte är bekanta med det redan) använder 24 bitar för att representera de tre färgerna röd, grön och blå som tillsammans kan representera vilken färg som helst. Viktigt att tänka på här är att x86-maskiner är av little endian typ, det vill säga att i ett tal så kommer den minst signifikanta byten att komma först. RGB-värdet 0x0000FF kommer därför, när man delar upp det i tre lika stora delar att vara 0xFF, 0x00, 0x00 i datorns minne. Jag kommer inte att använda bitmapper med paletter i denna artikel, istället kommer jag använda 24-bitars bitmappar, där varje pixel representeras av ett RGB-värde (istället för ett index i ett fält som håller RGB-värdet). Till själva filformatet då. Om vi bortser från paletter och fokuserar på hur en 24-bitars .bmp fil ser ut internt så är den i princip uppdelad i tre delar: * en sk. File Header med bl.a. offset till bilddata, och filstorlek. * en sk. Info Header med information om bilddatan, såsom höjd, bredd, färgdjup * själva bilddatan I windows API är våra headers namngivna som BITMAPFILEHEADER respektive BITMAPINFOHEADER. Hade vi haft en palett hade vi också använt oss av strukturen RGBQUAD. File headern ser ut såhär: Namn | Offset | Storlek | Beskrivning ------------ +---------+---------+----------------------------------------- bfType | 0| 2| Har värdet 0x4d42 ('BM'), används för ID bfSize | 2| 4| Anger filstorleken bfReserved1 | 6| 2| Reserverat av någon anledning, sätt till 0 bfReserved2 | 8| 2| Som ovan, sätt till 0 bfOffBits | 10| 4| Offset från filstart till bitmapsdatan 14 byte in alles. Det som vi behöver bry oss om här är bfType, bfSize och bfOffBits. BMP är ett av de få filformat som anger filstorleken i en header. Man kan ju undra vad som skulle hända om filsystemet säger en storlek och filheadern säger en annan. Lite för redundant för sitt eget bästa. Över till info headern då. Offset är givet från början av info headern. Namn | Offset | Storlek | Beskrivning ----------------+--------+---------+------------------------------------------ biSize | 0| 4| Anger storlek på info headern biWidth | 4| 4| Bildens bredd biHeight | 8| 4| Bildens höjd biPlanes | 12| 2| Antal plan, sätt till 1 biBitCount | 14| 2| Färgdjup, 24 i vårt fall biCompression | 16| 4| Typ av kompression, 0 (ingen) biSizeImage | 20| 4| Storlek på uppackad, fd. komprimerad bild biXPelsPerMeter | 24| 4| Pixel per meter längs X axeln, sätt till 0 biYPelsPerMeter | 28| 4| Som ovan fast Y axeln, sätt till 0 biClrUsed | 32| 4| Färger som används, sätt till 0 biClrImportant | 36 4| Viktiga färger, sätt till 0 Det finns en del udda fält i info headern som antingen inte används men som finns kvar av kompabilitetsskäl, eller som inte används i 24-bitars BMPs. Det vi behöver bry oss om är biSize, biWidth, biHeight och biBitCount. I övrigt sätts allt till 0 förutom biPlanes som sätts till 1. biSizeImage används inte då bilddatan inte är komprimerad. Efter våra headers följer den råa bilddatan. Man behöver egentligen bara två verktyg i en dator. NASM och Emacs. Från dessa två underbara verktyg (som absolut inte kan ersättas av t.ex. FASM och vi(m)) kan man skapa allt, inklusive bilder. Låt mig presentera mitt senaste konstverk som jag kallar "Ocean". ---- KOD BITS 32 %define IMAGE_WIDTH 256 %define IMAGE_HEIGHT 256 org 0 BITMAPFILEHEADER: dw 'BM' ;bfType dd EOF-BITMAPFILEHEADER ;bfSize dw 0 ;bfReserved1 dw 0 ;bfReserved2 dd bmpData ;bfOffBits BITMAPINFOHEADER: dd bmpData-BITMAPINFOHEADER dd IMAGE_WIDTH ;biWidth dd IMAGE_HEIGHT ;biHeight dw 1 ;biPlanes dw 24 ;biBitCount (bpp) dd 0 ;biCompression dd 0 ;biSizeImage dd 0 ;biXPelsPerMeter dd 0 ;biYPelsPerMeter dd 0 ;biClrUsed dd 0 ;biClrImportant bmpData: times (IMAGE_WIDTH*IMAGE_HEIGHT) db 0xff, 0x00, 0x00 EOF: ---- KOD Låt oss spara denna kod i en fil med namnet ocean.asm. Sen använder vi nasm såhär (förutsatt att du har nasm): nasm -fbin -o ocean.bmp ocean.asm Sen öppnar vi bilden ocean.bmp med ImageMagick eller valfritt bildvisningsprogram och så får vi se det, en blå ruta med måtten 256x256 pixlar. Det är så riktiga hackers ritar sina bilder, så kasta Gimp eller Photoshop, starta upp Emacs och börja rita! Notera det jag nämnde innan, RGB blir BGR när det anges byte för byte. En annan rolig detalj är att bitmapsdatan sparas "nerifrån och upp", så försöker du rita något vackert får du antingen tänka upp och ner eller vända på skärmen när du ska titta på det. Det spelar mindre roll för steganografin dock. Jag hoppas assemblerfilen gav dig som läsare en lite klarare bild över hur filformatet BMP är uppbyggt. Har du aldrig använt NASM förr så kanske det förvirrar mer än det hjälper, men då vet du vad du ska lära dig efter du läst klart denna artikel. Steganografin i det hela ======================== Så, var kan vi gömma saker? Det första sättet är att infoga vårt meddelande mellan slutet på info headern och början på bitmapsdatan. Här kan man i princip lägga vad som helst. Om vi modifierar assemblerfilen som gavs tidigare ser det ut såhär: BITS 32 %define IMAGE_WIDTH 256 %define IMAGE_HEIGHT 256 org 0 BITMAPFILEHEADER: dw 'BM' ;bfType dd EOF-BITMAPFILEHEADER ;bfSize dw 0 ;bfReserved1 dw 0 ;bfReserved2 dd bmpData ;bfOffBits BITMAPINFOHEADER: dd RGBQUAD-BITMAPINFOHEADER ;biSize dd IMAGE_WIDTH ;biWidth dd IMAGE_HEIGHT ;biHeight dw 1 ;biPlanes dw 24 ;biBitCount (bpp) dd 0 ;biCompression dd 0 ;biSizeImage dd 0 ;biXPelsPerMeter dd 0 ;biYPelsPerMeter dd 0 ;biClrUsed dd 0 ;biClrImportant RGBQUAD: db 'LEVE TENGIL, VÅR BEFRIARE! ATTACKERA ONSDAGEN DEN 3e MAJ 0800' bmpData: times (IMAGE_WIDTH*IMAGE_HEIGHT) db 0xff, 0x00, 0x00 EOF: RGBQUAD är i det här fallet utrymmet som skulle ha använts för vår palett, om vi hade haft något sådant. Nackdelen med den metoden är uppenbar. Bilden visas i och för sig på samma sätt som den gjorts utan någon text (i alla fall i de bildvisningsprogram jag testat) så genom att titta på den märker vi ingen skillnad. Däremot kan man lätt se det om man öppnar filen i en hex editor eller liknande. Det fungerar bra i wargames eller för att gömma porrnovellen man skrev i en oskyldig bild på en hundvalp och spara den på på familjens dator. Det fungerar mindre bra för statshemligheter. Det andra sättet är bättre. Inte för stadshemligheter dock. Det går ut på att använda de två lägsta bitarna i varje byte av bilddatan för den dolda datan. Den lilla skillnaden detta gör på färgvärdet är marginell och kan ignoreras. Om vi har RGB-värdet 0x0000FF (klarblå), så kan det, efter det att vi ändrat de två lägsta bitarna för varje byte bli: 0x0102FC, som ett exempel. Testa rita i dessa två färger och se om du kan se någon skillnad. Det krävs alltså fyra byte av bilddata för att gömma en byte av vårt meddelande. I "Ocean" har vi 256*256*3=196608 byte bilddata. Vi kan alltså gömma ett meddelande på 196608/4=49152 byte. Meddelande och meddelande, vi kan gömma godtycklig data för 49152 B i "Ocean". Skulle vi gömma en DNA-sekvens i bilden skulle vi kunna gömma 196608 kvävebaser, då det endast krävs två bitar för att representera en sådan. Det kan vara nyttigt. Blir ganska stora bilder om man ska gömma det mänskliga genomet, men det får man leva med. Okej, så i teorin vet vi hur man gömmer godtycklig binär data i BMP filer. Det är ungefär lika nyttigt som att i teorin kunna åka till månen. Det är kul att veta hur man gör, men likt förbannat är man kvar på jorden. Inte för att svårighetsgraden är i närheten av varandra i exemplet, men det finns ändå en viktig skillnad mellan teori och praktik som måste poängteras. I princip kommer vårt steganografiprogram (som självklart kommer att vara skrivet i C) att se ut såhär: * acceptera namn på infil och meddelande från skalet (argv) * Validera infilen (24-bitars BMP, tillräckligt stor) * Öppna infilen * För varje byte från infilen: - Separera ut två bitar från meddelandet - Sätt de två lägsta bitarna till noll (AND 0xFC) - Lägg till de två bitarna från meddelandet med OR - Skriv byte till utfil (standardnamn) * vid EOM, stäng filströmmar Givetvis måste vi kunna reversera processen, få ut meddelandet ur bilden. Det kommer att ske genom att använda AND 0x03 följt av en bitskift och addering till utdatan. Och här kommer koden: =========== KOD /*bs.c Bitmap Steganography swestres, 2007. No rights reserved. */ #include #include #include #include #include #include #include #include #include #define DEBUG #ifdef DEBUG #define ERROR_MSG(x) fputs(x, stderr); #else #define ERROR_MSG(x) #endif // file_t, data type used to hold information on files mapped to memory typedef struct { int fd; size_t size; char *memptr; int prot; int flags; } file_t; void *pack(void *dest, void *src, int len); void *unpack(void *dest, void *src, int len); int checkBitmap(file_t *bmp); file_t *loadFile(char *file, file_t *ft); void unloadFile(file_t *ft); int doPack(char *img, char *msg); int doExtract(char *img, char *msg); int main(int argc, char *argv[]) { if (argc != 4) { printf("bs - Bitmap Steganography\n" "by swestres, 2007\n" "Usage: %s \n" "modes:\n" " p - pack message into image\n" " x - eXtract data from image\n\n" "[*] When run in pack mode, the message will be packed into the" " image and a new\n" " image called imgdata.bmp will be created\n" "[*] When run in extract mode, the message in the image will be " "saved to the file\n" " given as the \n", argv[0]); return 1; } if (*argv[1] == 'p') { //pack mode return doPack(argv[2], argv[3]); } else if (*argv[1] == 'x') { //extract mode return doExtract(argv[2], argv[3]); } else { //Invalid mode printf("Invalid mode\n"); return 1; } return 0; } /* pack -- Packs data into the lower two bits of the destination buffer args: - dest: A pointer to the destination buffer - src: A pointer to the data that should be packed into the destination buffer - len: The length of the data pointed at by src returns a pointer to (uint8_t*)dest[len*4+1], first unused byte in destination buffer NOTE: No boundary checking! The destination buffer *MUST* be at least len*4 bytes! */ void *pack(void *dest, void *src, int len) { int i, stage; uint8_t msgByte; for(i=0; i= 0; stage -= 2) { *(uint8_t*)dest = *(uint8_t*)dest & 0xFC; *(uint8_t*)dest = *(uint8_t*)dest | ((msgByte >> stage) & 0x03); dest = (uint8_t*)dest+1; } } return dest; } /* unpack -- Unpacks data from the lower two bits of the source buffer args: - dest: Pointer to destination buffer - src: Pointer to source buffer - len: Length of the source buffer returns a pointer to (uint8_t*)src[len*4+1], the next position in the source buffer that might contain data NOTE: The destination buffer *MUST* be at least len*4 bytes! */ void *unpack(void *dest, void *src, int len) { int i, stage; for(i=0; i= 0; stage -= 2) { *(int8_t*)dest |= ((*(uint8_t*)src & 0x03) << stage); src = (uint8_t*)src+1; } dest = (uint8_t*)dest+1; } return src; } /* checkBitmap -- validates a loaded file as a 24-bit bitmap. returns -1 on error, size of bitmap data on success */ int checkBitmap(file_t *bmp) { uint32_t dsize; // Magic number correct? if (*(uint16_t*)bmp->memptr != 0x4d42) { ERROR_MSG("loaded file not bitmap"); return -1; } // 24 bit image? if (*(uint16_t*)(&bmp->memptr[28]) != 24) { ERROR_MSG("non 24-bit bitmap"); return -1; } //OK, calculate and return size of image data dsize = (bmp->size-*((uint32_t*)(&bmp->memptr[10]))); if ((dsize+54) > bmp->size) { ERROR_MSG("Bitmap offset field is invalid"); return -1; } return dsize; } /* loadFile -- Loads a file to memory given its file name. */ file_t *loadFile(char *file, file_t *ft) { int fd; struct stat fs; fd = open(file, O_RDWR); if (fd == -1) { perror("open"); return NULL; } if (fstat(fd, &fs) == -1) { perror("fstat"); close(fd); return NULL; } ft->memptr = mmap(NULL, fs.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (ft->memptr == MAP_FAILED) { perror("mmap"); close(fd); return NULL; } ft->fd = fd; ft->size = fs.st_size; return ft; } /* unloadFile -- Unloads a file from memory, saving the changes */ void unloadFile(file_t *ft) { munmap(ft->memptr, ft->size); } /* doPack -- Does some error checking and calls packMsg that packs a message into a bitmap image, return 0 on success, > 0 on failure */ int doPack(char *img, char *msg) { file_t imgfile, msgfile; int bmpsize, imgdataoffs; char *curr; if (!loadFile(img, &imgfile)) { ERROR_MSG("Couldn't load bitmap\n"); return 1; } if (!loadFile(msg, &msgfile)) { ERROR_MSG("couldn't load the message file\n"); unloadFile(&imgfile); return 1; } if ((bmpsize = checkBitmap(&imgfile)) == -1) { //invalid bitmap error message inside checkBitmap unloadFile(&imgfile); unloadFile(&msgfile); return 1; } // Does the message fit in the image? if ( msgfile.size > (bmpsize/4) ) { ERROR_MSG("the image is too small to hold the message\n"); unloadFile(&imgfile); unloadFile(&msgfile); return 1; } // Check if the size of image data field is OK imgdataoffs = *((uint32_t*)&imgfile.memptr[10]); if (imgdataoffs >= bmpsize) { ERROR_MSG("The image data field of the bitmap is incorrect\n"); unloadFile(&imgfile); unloadFile(&msgfile); return 1; } // pack the size of the data curr = pack(imgfile.memptr+imgdataoffs, &msgfile.size, 4); pack(curr, msgfile.memptr, msgfile.size); // pack the data itself unloadFile(&imgfile); unloadFile(&msgfile); return 0; } /* doExtract -- Extracts a message from a bitmap image return 0 on success, > 0 on failure */ int doExtract(char *img, char *msg) { file_t imgfile; int bmpsize, msgsize = 0; char *curr, *dst; FILE *outf; if (!loadFile(img, &imgfile)) { ERROR_MSG("Couldn't load bitmap\n"); return 1; } if ((bmpsize = checkBitmap(&imgfile)) == -1) { //invalid bitmap error message inside checkBitmap unloadFile(&imgfile); return 1; } if (bmpsize <= 16) { ERROR_MSG("Bitmap size is too small to carry a message"); unloadFile(&imgfile); return 1; } // extract size from bitmap curr = unpack(&msgsize, imgfile.memptr+(imgfile.size-bmpsize), 4); if (msgsize > bmpsize/4) { ERROR_MSG("The size of the message is bigger than the bitmap" " permits (is there really a message?\n"); unloadFile(&imgfile); return 1; } dst = malloc(msgsize); if (!dst) { ERROR_MSG("malloc error\n"); return 1; } unpack(dst, curr, msgsize); unloadFile(&imgfile); outf=fopen(msg, "wb"); if (!outf) { ERROR_MSG("couldn't open output file for writing\n"); free(dst); return 1; } fwrite(dst, msgsize, 1, outf); fclose(outf); free(dst); return 0; } =========== KOD Koden är skriven för POSIX-system. Den lägger först till storleken på den dolda datan i bilddatan, för att sedan lägga in del dolda datan i sig. Tänk på att plattformarna som hanterar bilderna måste ha samma endianess. Låt oss testköra: $ nasm -fbin -o ocean.bmp ocean.asm $ hexdump -C ocean.bmp | head 00000000 42 4d 36 00 03 00 00 00 00 00 36 00 00 00 28 00 |BM6.......6...(.| 00000010 00 00 00 01 00 00 00 01 00 00 01 00 18 00 00 00 |................| 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000030 00 00 00 00 00 00 ff 00 00 ff 00 00 ff 00 00 ff |................| 00000040 00 00 ff 00 00 ff 00 00 ff 00 00 ff 00 00 ff 00 |................| 00000050 00 ff 00 00 ff 00 00 ff 00 00 ff 00 00 ff 00 00 |................| 00000060 ff 00 00 ff 00 00 ff 00 00 ff 00 00 ff 00 00 ff |................| 00000070 00 00 ff 00 00 ff 00 00 ff 00 00 ff 00 00 ff 00 |................| 00000080 00 ff 00 00 ff 00 00 ff 00 00 ff 00 00 ff 00 00 |................| 00000090 ff 00 00 ff 00 00 ff 00 00 ff 00 00 ff 00 00 ff |................| $ gcc -o bs bs.c $ cat msg.txt this is a message $ ./bs p ocean.bmp msg.txt $ hexdump -C ocean.bmp | head 00000000 42 4d 36 00 03 00 00 00 00 00 36 00 00 00 28 00 |BM6.......6...(.| 00000010 00 00 00 01 00 00 00 01 00 00 01 00 18 00 00 00 |................| 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000030 00 00 00 00 00 00 fc 01 00 fe 00 00 fc 00 00 fc |................| 00000040 00 00 fc 00 00 fc 01 03 fd 00 01 fe 02 00 fd 02 |................| 00000050 02 fd 01 03 fc 03 00 fe 00 00 fd 02 02 fd 01 03 |................| 00000060 fc 03 00 fe 00 00 fd 02 00 fd 00 02 fc 00 01 fe |................| 00000070 03 01 fd 02 01 fd 01 03 fc 03 01 ff 00 03 fd 02 |................| 00000080 00 fd 01 02 fd 03 01 fe 01 01 fc 00 02 fe 00 00 |................| 00000090 ff 00 00 ff 00 00 ff 00 00 ff 00 00 ff 00 00 ff |................| $ ./bs x ocean.bmp xmsg.txt $ cat xmsg.txt this is a message Det fungerar fint. Jag måste dock erkänna att jag inte lade ner någon större tid på indatavalideringen i bs.c. Det är mer ett demo än något annat. Kompression, kryptografi och annat att tänka på =============================================== Eftersom vi är begränsade i hur mycket data vi kan lagra i filen kan det vara lämpligt att komprimera vårt meddelande innan vi lägger in det i vår bild. I Linux är det ganska enkelt. Pipe'a bara meddelandet genom bzip2 eller gzip såhär: $ cat message.txt | bzip2 -zc9 > message.packed Och lägg sen in message.packed i bilden. Detta lönar sig bara för större meddelanden, för mindre (~100-200 B) är det inte så lönt. För att sen packa upp den görs såhär: $ cat imgdata.bin | bzip2 -dc > message.txt Efter att meddelandet extraherats från bilden till filen imgdata.bin. Att kryptera den komprimerade datan med något symmetriskt blockchiffer är bra. Då är inte bara meddelandet dolt, utan även dess innebörd. Jag tror dock inte att det finns något standardverktyg i stil med gzip eller bzip2 motsv. för kryptering, men jag vet att de flesta scriptspråk har implementeringar av kända algoritmer i stil med AES och Blowfish. Perl har Crypt::Blowfish, Python har Crypto.Cipher.* (AES, Blowfish, DES3 och några andra), Tcl har (i Tcllib) aes. Använd helst färdiga implementeringar som är vältestade istället för att skriva något eget. Det är säkrast så. Ett användningsområde för steganografi är C&C för bottar, zombies motsv. Man skulle kunna tänka sig ett program som, så fort webbläsaren öppnas, injicerar en bit kod med ptrace eller WriteProcessMemory (beroende av OS förstås) som laddar ner en bild från en webbsida. Denna bild behandlas senare av botten, som utför de kommandon som finns inbakade i bilddatan. Detta skulle kunna fungera som en bootstrap procedur, om bilden innehåller det fiktiva kommandot "snuff" med innebörden "anslut till irc-server X och var beredd på att ta emot kommandon" gör botten just det, annars väntar den några minuter eller tills nästa gång webbläsaren startas och hämtar bilden igen. Man kan också tänka sig att den endast gör det en gång per dag, innehåller bilden ingen kommandodata stängs botten av. På så sätt kan man ha ett vilande botnet som gör väldigt lite stök omkring sig, för att sedan, en dag när man behöver det, aktivera nätverket och utnyttja resurserna. Det finns många möjligheter med steganografi, ta vara på dem. ================================================================================ 0x02 - dwm - En fönsterhanterare som håller måttet swestres ================================================================================ Effektivitet och dwm ==================== Jag vill ha ett användbart operativsystem. Om operativsystemet tar upp stora systemresurser och startar upp sig själv lika snabbt (läs: långsamt) som det tar för mig att tömma mitt tarminnehåll och rensa den berörda kroppsöppningen så håller det inte måttet. En viktig komponent i operativsystemet är fönsterhanteraren. Fönsterhanteraren är det gränssnitt jag använder mest, därför ställer jag stora krav på det. Tidigare har jag föredragit Fluxbox. Det har även funnits perioder då jag använt Xfce. GNOME är fint, men jag gillar helt enkelt inte gwm. De andra fönsterhanterarna som jag har provat har jag förkastat. Fram tills nu dvs. En dag (idag faktiskt) slösurfade jag och kollade runt lite på Wikipedia. Från fönsterhanterare klickade jag mig vidare och kom av en slump in på dwm. suckless.org, låter bra. Det visar sig vara en fönsterhanterare där en av riktlinjerna (kraven?) för projektet är att källkoden skall hållas under 2000 rader kod. Säga vad man vill om det, men det uppmanar i alla fall inte till skapandet av bloatware som vi nog alla känner igen i en viss komersiell produkt. Jag laddade ner källkoden, lekte med den, testade lite olika konfigurationer och är nu i extas över dess skönhet. Som fönsterhanterare är dwm minimalistisk. Trots detta är den väldigt flexibel, dynamisk. Källkoden är uppdelad i två filer. En konfigurationsheader där man kan ändra allt från knappkombinationer till antalet arbetsutrymmen, och en källkodsfil som innehåller hela projektets kod. Fönster organiseras på skärmen enligt två modeller, flytande och "tiled", den senare modellen är den du vill använda, det garanterar jag. Huvudprojektet får störst skärmutrymme medans resterande fönster delas upp vid sidan av. Att byta fokus mellan fönster är en fråga om antingen att flytta musen eller att trycka in en given tangentkombination. Skärmen består utan två områden. En "status bar" där arbetsutrymmena står listade, tillsammans med fönsternamn och ett område där dwms standard input skrivs ut till. Det andra området är arbetsytan där fönsterna visas. Nerladdning och installation ============================ dwm kan laddas ner från projektets officiella hemsida, suckless.org. Här kan man även hitta andra intressanta projekt, till och med en annan fönsterhanterare. Den senaste versionen vid tidpunkten då detta skrevs var dwm-4.7, och resten av det tekniska kommer att utgå från denna version. Du kommer även att vilja ha dmenu som kan fås från suckless.org. Detta är som namnet antyder en meny för X som används av dwm för att köra program. dwm kommer som en "gzipped tarball" med filändelsen .tar.gz. För att packa upp skriver du: $ tar -zxvf dwm-4.7.tar.gz innehållet kommer att packas upp till mappen dwm-4.7 i samma mapp som arkivet ligger i. När du packat upp går du in i mappen och skriver (som root): $ make clean install Då kompileras projektet och kopieras till /usr/local/bin om inget annat anges i filen Makefile. Efter du gjort detta gör du samma sak för dmenu. Nu har du installerat dwm och dmenu, men du vill att det ska starta när du (antingen explicit eller via ett script) kör startx. Då lägger du till följande rad i ~/.xinitrc : exec dwm Finns inte filen ~/.xinitrc skapar du den. Det du gör nu är att starta om X. Om inget oväntat händer kommer du att mötas av dwm. De kommandon du behöver kunna för att göra något vettigt finns i den medföljande manualsidan. "man dwm" för mer info. Konfiguration ============= Det vore dumt av oss om vi inte tog vara på dwms flexibilitet redan vid konfigurationen av densamma. Jag rekommenderar att testa först och sedan ändra det som är fel. När jag först använde dwm hade jag två problem: * dwm använder Alt (M hädanefter) för kommandosekvenser vilket inte passar så bra ihop med Emacs som råkar vara det program jag använder mest * Kommandosekvensen M-Shift-Return startar (u)xterm, jag föredrar aterm. När jag fixat det kom jag även på att det skulle vara trevligt att ha ett eget kommando för att starta emacs. Efter detta dök det upp i mitt huvud att jag skulle vilja ha något sätt att kunna skriva till dwms stdin för att visa diverse information i dess status bar. Den första punkten var lätt att ändra. I dwms config.h finns raden: #define MODKEY Mod1Mask MODKEY är det tangent som inleder alla kommandosekvenser. Mod1Mask är en bitmask definierad i . Vilka tangenter som representeras av denna bitmask kan vi se med kommandot xmodmap: $ xmodmap xmodmap: up to 3 keys per modifier, (keycodes in parentheses): shift Shift_L (0x32), Shift_R (0x3e) lock Caps_Lock (0x42) control Control_L (0x25), Control_R (0x6d) mod1 Alt_L (0x40), Alt_L (0x7d), Meta_L (0x9c) mod2 Num_Lock (0x4d) mod3 mod4 Super_L (0x7f), Hyper_L (0x80) mod5 Mode_switch (0x5d), ISO_Level3_Shift (0x71),ISO_Level3_Shift (0x7c) Mod1 är alltså tangenten Alt (vilket inte bör vara någon överraskning). Vilken knapp bör vi använda i dwm istället? Det finns en liten knapp som sällan används i Linux, Win-Key, tangenten med flaggan. Även känd som Super_L. Mod4 alltså. Sagt och gjort, vår nya MODKEY får värdet Mod4Mask. Att ändra sekvensen M-Shift-Return är en fråga om att byta ut dess argument i keys[] fältet. Istället för raden: { MODKEY|ShiftMask, XK_Return, spawn, "exec uxterm" }, låter vi det stå: { MODKEY|ShiftMask, XK_Return, spawn, "exec aterm" }, När vi ändå är igång kan vi lägga in raden: { MODKEY, XK_e, spawn, "exec emacs"}, som gör det möjligt för oss att starta emacs genom att trycka Win+E. Då var det den sista punkten kvar, göra dwms stdin lite mer åtkomlig för oss. Vi kommer att använda oss av en FIFO, även känd som named pipe. En FIFO är (i detta kontext) en speciell fil i filsystemet som kan användas för att skicka information mellan två processer. Den ena processen skriver medans den andra läser. Finns där ingen som läser från vår FIFO blockas skrivningen till den tills någon sådan finns, och likadant på andra hållet. En FIFO skapas med kommandot mkfifo: mkfifo ourfifothing Då skapas en FIFO med namnet ourfifothing i den nuvarande arbetskatalogen. Nog om POSIX IPC-saker. Öppna ~/.xinitrc, byt ut "exec dwm" mot följande: mkfifo ~/.dwminput while [ 1 ] do cat .dwminput done | exec dwm Först skapas en fifo, sedan binder vi stdin på dwm till ett litet script som om och om igen läser från vår fifo. Skriver vi, när dwm körs: echo Du suger kuk :D > ~/.dwminput I en instans av xterm (eller aterm) så syns detta uppe i det högra hörnet. Detta kan även användas för att visa uptime, serverbelastning, IRC meddelanden eller aktiekurser. Du bestämmer. Allt du behöver göra är att skriva ett program eller ett script som skriver denna information till .dwminput på ett lämpligt sätt. Resten är upp till din fantasi. ================================================================================ 0x03 - Självmodifierande kod swestres ================================================================================ Introduktion ------------ Självmodifierande kod är kod som ändrar på sig själv under körning. Det är ett ganska vitt begrepp och kan innebära allt från strängsubstitution i tolkade (eng. interpreted) språk till maskinkod som är skriven för att ändra operander eller opcodes i sig själv. Denna artikel kommer att behandla det senare. Plattformen kommer att utgöras av x86 Linux, programmen som kommer att användas är gcc med tillbehör samt nasm. Filformatet som kommer att förutsättas för exekverbara binärer är ELF. Även om källkod kommer att presenteras är artiklen mer teoretiskt inriktad och många av exemplen som presenteras är inte så användbara i "riktiga" sammanhang. Varför självmodifierande kod? ----------------------------- Självmodifierande kod är viktigt inom två större områden: * kodobfuskering, där självmodifierande kod används för att dölja programflödet eller programmets funktion i syfte att försvåra programanalys * kodoptimering, där självmodifierande kod används för att göra program mindre och/eller snabbare. En stor fördel med självmodifierande kod är att programmet kan behålla ett tillstånd utan hjälp av globala variabler. I programmeringsspråket Forth finns en variabel med namnet BASE som håller den aktuella talbasen. Lisp har en likadan variabel som heter *print-base*. De används när ett tal ska omvandlas till en textsträng för t.ex. utskrift till skärmen. Internt är det ju alltid bas 2 som gäller, men i strängar är det en annan historia. Vi vill ju kunna ha minnesadresser i hexadecimalt format, filrättigheter i oktalt etc. Om vi använder oss av självmodifierande kod skulle BASE kunna vara en Forth-literal som ändras med ! (store), och vara en del av tal->text rutinen. Det skulle eliminera behovet av variablen. ! används för att sätta en minnescell (nästan uteslutande alltid ett dword i 32-bitars forth för x86). Om Forthtolken är implementerad med hjälp av en teknik som kallas för threaded code så är en literal två celler, medans en variabel är betydligt större. Varför inte självmodifierande kod? ---------------------------------- Att använda sig utav självmodifierande kod är lite kontroversiellt, då det ofta associeras till virus/malware. Antivirusprogram under windows brukar varna om de finner program som ändrar sin egen programkod, vilket leder till att programmerare avstår från att använda sig utav denna metod. En annan nackdel med självmodifierande kod (beroende på hur man ser det) är att det är lätt att göra misstag som kan få ödesdigra följder. Säg att du har ett program som modifierar sig själv att hoppa till en viss position i sig själv, var den hoppar är beroende av indatan den får. Skulle indatan vara manipulerad och/eller programmet vara dåligt skrivet skulle det kunna möjliggöra att hoppet sker till en godtycklig position inom processens minnesrymd, eller att koden som ändrar hoppinstruktionen istället skulle arbeta mot någon annan instruktion. Detta är direkt o-bra. Självmodifierande kod är också svårt att läsa, då ändringarna inte alltid är så uppenbara. Programkod ligger ofta i en minnesrymd som är skyddad från skrivmöjlighet. Under Linux innebär detta att det segment i ELF-filen som programkoden ligger i är märkt som R E (Readable Executable). Innan vi börjar skriva självmodifierande kod måste vi därför kunna ändra detta. Detta ska vara möjligt att ändra med hjälp av programmet objcopy och flaggan --writable-text, men efter att ha provat det fann jag att flaggan ignorerades och kodsegmentet fortfarande var märkt som R E när jag tittade efter med readelf. Jag slängde därför ihop ett verktyg som ändrar flaggorna på alla LOAD-segment i en ELF till RWE (RWX). Så, till artiklens första kodstycke. Kompileras med gcc som vanligt: ================ KOD // load2rwx.c - tool to make the LOAD segments in a // 32-bit x86 ELF rwx // swestres, 2008 #include #include #include #include #include #include #include #include #include typedef struct { int fd; off_t size; void *start; } file_t; // ret: 0 on success, -1 on failure int mkrwx(file_t *f) { Elf32_Ehdr *ehdr; Elf32_Phdr *phdr; Elf32_Half nPhdr; if (f->size < sizeof(Elf32_Ehdr)) { return -1; } ehdr = f->start; // validate e_ident if (*(uint32_t*)ehdr->e_ident != 0x464c457f) { return -1; } if (ehdr->e_ident[EI_CLASS] != ELFCLASS32 || ehdr->e_ident[EI_DATA] != ELFDATA2LSB || ehdr->e_ident[EI_VERSION] != EV_CURRENT) { return -1; } // validate e_type, e_machine and e_version if (ehdr->e_type != ET_EXEC || ehdr->e_machine != EM_386 || ehdr->e_version != EV_CURRENT) { return -1; } // validate program header if (ehdr->e_phoff < sizeof(Elf32_Ehdr) || ehdr->e_phentsize != sizeof(Elf32_Phdr)) { return -1; } // make sure the size of the file is enough for the phdr if (f->size < ehdr->e_phoff + ehdr->e_phentsize*ehdr->e_phnum) { return -1; } phdr = (Elf32_Phdr*)((uint8_t*)f->start + ehdr->e_phoff); for(nPhdr = ehdr->e_phnum; nPhdr > 0; nPhdr--) { if (phdr->p_type == PT_LOAD) { phdr->p_flags |= (PF_R|PF_W|PF_X); } phdr++; } return 0; } file_t *loadFile(char *name, file_t *f) { struct stat st; f->fd = open(name, O_RDWR); if (f->fd == -1) { return NULL; } if (fstat(f->fd, &st) == -1) { close(f->fd); return NULL; } f->size = st.st_size; f->start = mmap(NULL, f->size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED, f->fd, 0); if (f->start == MAP_FAILED) { close(f->fd); return NULL; } return f; } int main(int argc, char *argv[]) { file_t f; if (argc != 2) { fprintf(stderr, "load2rwx - make 32-bit ELF LOAD segments rwx\n" "usage: %s \n", argv[0]); return 1; } if (!loadFile(argv[1], &f)) { fprintf(stderr, "unable to load file %s (errno: %d)\n", argv[1], errno); return 1; } if (mkrwx(&f) == -1) { fprintf(stderr, "Unable to change the program headers\n"); return 1; } return 0; } ================ KOD Det är lite av ett fulhack, men det får jobbet gjort. Det skulle ju förstås vara smidigare att använda objcopy om --writable-text fungerade. Jag antar att det kan finnas någon liknande flagga till ld. Fast om jag hade tagit reda på det hade jag inte haft någon ursäkt till att leka lite extra. Ändring av omedelbara värden ---------------------------- Jag fick faktiskt idéen till denna artikel när jag läste Paul Grahams bok "ANSI Common Lisp". I denna bok finns det ett exempel på "closures" i Lisp: > (setf fn (let ((i 3)) #'(lambda (x) (+ x i)))) > (funcall fn 2) 5 Det inspirerade mig att göra något med en liknande funktion i assembler, fast istället för att addera ett godtyckligt värde nöjde jag mig med att öka utgångsvärdet (i Lispexemplet 3, i koden nedan 0) med 1. Och här är resultatet: ================ KOD BITS 32 global counter section .text counter: mov eax, 0 ; B8 00000000 inc dword [counter+1] ; FF 05 counter+1 (dword) ret ; C3 ================ KOD Maskinkoden för assemblerinstruktionerna är utskrivna längst till höger. Hela funktionen är 12 byte, inklusive vår i-variabel som är ett omedelbart värde. Vid första körningen sätts eax till noll, därefter ökas det omedelbara värdet med ett. eax är returvärdet. Vid nästa körning kommer eax sättas till ett och det omedelbara värdet ökas ännu en gång, och så vidare i det stuket. För att ge en jämförelse i C presenterar jag följande exempel: ================ KOD unsigned int counter() { static unsigned int i = 0; return i++; } ================ KOD Kompilerat i gcc med flaggan -O2 blir koden följande: 80483b0: a1 38 96 04 08 mov 0x8049638,%eax 80483b5: 55 push %ebp 80483b6: 89 e5 mov %esp,%ebp 80483b8: 5d pop %ebp 80483b9: 8d 50 01 lea 0x1(%eax),%edx 80483bc: 89 15 38 96 04 08 mov %edx,0x8049638 80483c2: c3 ret Fråga mig inte vad den gör med ebp, man tycker ju den borde optimera bort det. C-versionen, utan självmodifierande kod blir 23 byte totalt, inklusive i-variablen. Det är en ganska dramatisk procentuell ökning. Ett annat Lispexempel från samma bok som också inspirerade skapandet av artiklen och counter-exemplet i assembler är följande: (let ((counter 0)) (defun reset () (setf counter 0)) (defun stamp () (setf counter (+ counter 1)))) Här skapas två funktioner, reset och stamp. stamp ökar värdet på counter med ett och returnerar det nya värdet (precis som counter i det tidigare asm-exemplet) medans reset nollställer countervärdet. Detta kan inte skrivas i C, även om man skulle kunna koka ihop något liknande med hjälp av en global variabel. Assemblerversionen för detta skulle bli: ================ KOD BITS 32 global stamp global reset section .text reset: xor eax,eax ; 31 C0 mov dword [stamp+1], eax ; A3 stamp+1 (dword) ret ; C3 stamp: mov eax, 0 ; B8 00000000 inc dword [stamp+1] ; FF 05 stamp+1 (dword) ret ; C3 ================ KOD Ovanstående exempel väger in på en storlek av 20 byte. Nu har vi sett hur man, med hjälp av självmodifierande kod kan uppnå vissa optimeringar när det gäller kodstorlek. Exemplet som gavs var ganska trivialt. Ett mer praktiskt användningsområde för att modifiera omedelbara värden som ges som operander i koden är när man vill ändra tillståndet på sitt program. Man kanske har en funktion som skriver data till en viss file descriptor, där den aktuella fd-n sätts på ett liknande sätt som counter-värdet i tidigare exempel. Detta eliminerar behovet av en variabel på stacken eller i programmets datasektion vilket gör koden mindre i vissa sammanhang. Ändring av operatorer/opcodes ----------------------------- Självmodifierande kod är förstås inte begränsat till att ändra operander i form av omedelbara värden. Säg att vi vill generera en serie nummer som ökar för varje anrop till vår funktion tills den når ett övre värde för att sedan minska tills det når ett under värde. Vi får då ut en talföljd i stil med 0 1 2 3 4 3 2 1 0 1 2 3 4 ... Ett användningsområde för detta kan vara att generera en PWM-ton till ljudkortet. Nedan följer en bit kod som genererar en sådan talföljd. ================ KOD wave: xor eax,eax ; 31 C0 mov al, 0 ; B0 00 inc byte [wave+3] ; FF 05 wave+3 (dword) cmp al, 31 ; 3C 1F jae .toDec ; 73 05 cmp al, 1 ; 3C 01 jbe .toInc ; 76 09 ret ; C3 .toDec: mov bl, 0x0D ; B3 0D jmp short .wavemod ; EB 02 .toInc: mov bl, 0x05 ; B3 05 .wavemod: mov byte [wave+5], bl ; 88 1D wave+5 (dword) ret ; C3 ================ KOD 32 byte. Här ser vi en kombination av ändring av en operand i formen av ett omedelbart värde (wave+3) samt ändring av operator (wave+5). Ovanstående kod kommer att returnera 0 första gången den anropas, 1 andra gången,2 tredje gången, etc tills värdet på al når 31. Då ändras inc-instruktionen till en dec-instruktion. Nästa gång al hämtas kommer det vara 32 som returneras, men nu har instruktionen ändrats från inc till dec, vilket betyder att nästa returvärde kommer att vara 31, värdet efter det 30 etc. När värdet 1 påträffas kommer instruktionen att ändras från en dec åter till en inc-instruktion. Vid återkommande anrop kommer en talföljd som går från 0 till och med 32, till och med 0, till och med 32, osv. att genereras. Koden kan troligtvis göras mindre. Både inc och dec modifierar flaggregistret så en cmp borde kunna elimineras. Kan man förutsätta att eax inte innehåller ett större tal än 255 innan anropet till wave kan man ta bort den första xor-instruktionen. En annan ändring som kan göras på ovanstående kodstycke är hur wave+5 ändras. För inc är wave+5 0x05 och för dec är wave+5 0x0D. Här ovan skriver vi helt enkelt över dem med en hel byte. Detta är väldigt onödigt, men det är vanligt att se i kod där programmeraren tänker på en bytenivå. Det ända som skiljer talet 0x05 från 0x0D är bit 3 (index 0 för den minst signifikanta biten förstås). 0x05: 00000101 0x0D: 00001101 Så för att ändra vår inc till en dec-instruktion behöver vi bara sätta bit 3 till 1, och för att ändra dec till inc sätter vi bit 3 till 0. Detta kan vi göra genom att xor'a med 0x08. Just i detta exemplet blir slutprodukten dock inte mindre av att göra det. Eftersom vi får en toggle-funktion när vi xor'ar måste vi fastställa att vi inte kör vår xor vid första nollan vilket tar några instruktioner. Men i fall där man naturligt utgår från en toggle-funktion kan det vara lämpligt att se över om man verkligen skriva kod som säger "om a gör 1, om b gör 2". Det vill säga, om man behöver testa utgångsläget. Kan man undvika det med en xor tjänar man några byte. Detta gäller förstås inte bara inc och dec, add och sub är också lika varandra på samma sätt, likaså shl och shr. Kodmallar --------- Många tolkade högnivåspråk har möjligheten att skapa funktioner vid körning utifrån mallar. I Lisp kan man ha funktioner som returnerar lambda-funktioner vars funktionalitet är bestämd av indatan till funktionen som skapade dem. I Forth kan man skriva sk. definition words som lägger till nya ord i ordlistan vars funktion är satt av indatan som ligger på stacken vid anropet till det ord som skapade dem. För att få detta beteende i assembler kan vi använda oss av kodstycken ("mallar") som byggs upp under kompilering och som ändras under körning. Med hjälp av nops kan vi fylla ut utrymme så att våra instruktioner kan vara så långa eller korta som möjligt utan att behöva anpassa dem till varandras storlek. Det slösar lite utrymme men ger ökad flexibilitet. Vet vi att instruktionerna bara kommer att vara av en viss storlek behöver vi inte göra så. artikelslut ----------- Det blev en ganska kort artikel som tar upp grunderna i självmodifierande kod. Håll till godo. Jag nämnde i början av artiklen att denna teknik kan användas för kodobfuskering, men tog inte upp några konkreta exempel för det. Dock kan de exempel som givits användas för att härleda metoder som uppnår detta syfte. Har ni andra exempel på självmodifierande kod, tveka inte att skicka in dem så kan de komma att publiceras i nästa utgåva. Det är ett intressant ämne. ================================================================================ 0x04 Varför jag inte gillar BackTrack och Metasploit swestres ================================================================================ Vad är BackTrack? ----------------- Backtrack är en Linuxdistribution med fokus på penetrationstestning. I sitt standardutförande är den en sk. LiveCD, den körs från CD-ROM istället för att installeras på datorns hårddisk. Installation till hårddisk är möjlig. BackTrack kommer med ett hundratal verktyg som fokuserar på att erbjuda den hjälp som en penetrationstestare kan tänkas behöva. Allt från program för lösenordsforcering till proxyservrar och fejkade APs finns med på skivan. BackTrack är resultatet av en hopslagning mellan två liknande distributioner vid namn Auditor Security Collection och Whax. Vad är Metasploit? ------------------ Metasploit är ett ramverk för att utveckla, testa och använda exploits. Exploits är som namnet antyder program för att utnyttja sårbarheter. Från början anvädes Perl som scriptspråk, men nylighen har hela ramverket skrivits om för att använda Ruby istället. En av deras slogans som står att läsa på projektets hemsida är "Point, klick, root.", en annan är "The Best a Haxor Can Get". Metasploit följer med BackTrack. Varför gillar jag dem inte? --------------------------- Både Metasploit och BackTrack är enastående projekt med kunniga utvecklare och en bred användarkrets. Det är inte det jag inte gillar. För att illustrera vad jag inte gillar, låt mig börja med en berättelse om snickaren Börje. Börje har jobbat som snickare i ungefär tre decennium och är skicklig i sitt hantverk. Han jobbar på ett företag med ett tiotal anställda där han tillhör de trogna, de som jobbat där längst. Han har jobbat på samma företag sen han slutade yrkesskolan. De verktyg han använder känner han väl till och han kan i princip slutföra ett arbete i sömnen. Inte för att han skulle sova på jobbet, hans yrkesstolthet är allt för stor. En dag kommer det en ny kille till jobbet, Anders. Anders är klar med sin utbildning på gymnasiet och jobbar nu som lärling, Börje blir hans handledare. Anders kommer till jobbet i klädd i ett ställ från Blåkläder, bra tänker Börje. Han blir däremot lite konfunderad när han ser Anders verktygslåda. Den är stor och ser dyr ut. Börje tänker inte mer på det, utan introducerar sig och hälsar Anders välkommen. Han ser lite av sig själv i Anders, och de två kommer väl överens med varandra. Senare samma dag åker de ut på ett jobb hos en villaägare som bygger ett uterum med stora glasfönster och trädäck runt om. Hälften av jobbet är klart, och det börjar nu se riktigt flådigt ut. De börjar lasta ur sina verktyg och tar sig an jobbet. Börje har de verktyg han använder mest i sitt verktygsbälte, medans Anders använder sin nya, fina verktygslåda. Börjes andra verktyg ligger i en plastspann med stänk av cement på. När de båda har jobbat ett tag kommer ägaren ut med en kaffekanna och det blir rast. De dricker det kaffe som bjuds, även om Börje har en egen termos med sig. När de tackat husägaren för kaffet och avslutat konversationen fortsätter de jobba. Det börjar bli sent, men det är sommar och fortfarande ljust. Anders, som är arbetsvillig och visar framåtanda på sitt nya jobb, har svårt att hitta sin hammare. Hans verktygslåda, som tidigare var väldigt välordnad, ser nu rörigare ut än Börjes plastspann. Efter att ha letat efter hammaren i några minuter ger han upp och tar en skiftnyckel istället. "Vad gör du, pojk?" frågar Börje, frågan är retorisk. "Du skadar dina nya fina verktyg!" Anders, som ser lite ställd ut säger, "Jag kunde inte hitta min hammare, och en skiftnyckel går ju att använda för att slå i spik med också". Efter att Börje har lånat ut sin extrahammare till Anders slutför de båda arbetet för dagen och åker tillbaka till kontoret för att avsluta dagen. Ramverk är klumpiga. Ser organiserade ut, men blir lätt röriga. Du får en bunte exploits och shellcodes och använder ett fåtal av dem, resten är bara bloat. För att använda rätt verktyg måste man förstå jobbet som ska göras. Utan att ha läst på sig om en sårbarhet vet man inte vad som sker, och genom att använda en one-size-fits-all payload utnyttjar man inte de möjligheter som kräver specialiserade, one-of-a-kind payloads som utnyttjar programmets interna tillstånd för att fullfölja sitt syfte. Det brukar sluta i att man använder en skiftnyckel som hammare. Det går, men inte så bra, och i slutändan ökar risken för upptäckt. Även om man är en van användare av ramverket och har god förståelse för vad de olika komponenterna gör så hjälper det inte en själv när man står där en dag med fysisk åtkomst till ett läckert system. Systemet har en C-kompilator och diverse programtolkar som ger en möjligheten att interagera med systemet bortom dess användargränssnitt, men kunskapen om hur man ska gå tillväga saknas. Alla månader, år av användande av färdiga verktyg har gjort att man ignorerat de underliggande faktorerna. Varför lära sig skriva exploits, scanners kartläggningsverktyg och bakdörrar när det finns färdiga? Varför bry sig om att leta efter sårbarheter när man lika gärna kan göra en googlesökning och hoppas på det bästa? Varför ska man undersöka källkoden när allt man behöver göra är ./configure && make install? Varför bry sig om ett givet mål när man kan scanna efter tusen slumpmässigt? Metasploit och BackTrack är, i mina ögon, abstraktioner som döljer de bakomliggande faktorerna. Det är inget substitut för äkta kunskap, men kan lätt leda till att man bedrar sig själv och struntar i att söka den information man behöver för att klara jobbet själv. De skapar ett skal, en barriär som blir svår att tränga igenom. Människan är lat av naturen. Jag har testat Metasploit och BackTrack, det är fina verktyg. Jag använder de inte, dock händer det att jag använder enskilda verktyg som är en del av dessa ramverk. Vad jag motsätter mig är förpackningen. Den skapar distans mellan användare och utvecklare. Denna distans är bra när det gäller program som ordbehandlare, datorspel eller webbrowsers, men är direkt skadlig för en grupp människor som både ska vara användare och utvecklare. Föreställ er att ni inte skulle lära er formler i fysiken, att dessa skulle vara inprogrammerade på era grafräknare. Ni skulle kunna se dem, men inte få någon vidare utbildning hur de fungerar eftersom det enda ni behöver göra för att få rätt svar är att skriva in variablerna på räknaren och trycka enter. För att sno en slogan, "Do not learn to hack, hack to learn". ================================================================================ 0x05 Introduktion till C#, del 1 Retard ================================================================================ Förord ------ Jag som skriver detta kan inte påstå att jag är speciellt duktig inom programmering, men jag vet själv hur det är att inte ha någon litteratur tillgänglig som faktiskt visar de grundläggande delarna i programmering, jag har länge strävat efter att tillge andra som varit i min situation den dokumentation jag saknat och behövt. Allt som står här kan vara fel väg att lösa vissa saker på, men jag löser dem på detta vis, finns det någon som kan detta språk bättre än mig får denna person mejla mig på retarded@hush.ai för att upplysa om eventuella fel. Introduktion ------------ Denna serie ska försöka utgå ifrån det som krävs i Programmering A, exkluderat en del av de teoretiska mål som kan krävas i kursplanen. Med detta sagt kommer jag att ta upp de mest grundläggande sakerna inom C# och Microsoft Studio. Jag antar att det finns vissa som vill skriva elaka program och ser att C# kan vara språket för dem, men jag hade inte rekommenderat dig att läsa detta språk om du vill lära dig skriva virus eller andra program. Det finns klart många fler smidigare språk att välja mellan, som inte kräver att t.ex. .NET är installerat. Känner du besviken? Då råder jag dig till att sluta läsa, har du bråttom till att bli en ond hacker kan du ge upp redan nu, det kommer inte gå fort, och C# är troligen inte programmeringsspråket som passar dina [onda] avsikter i alla fall. Och är du redan familjär vid andra språk eller vid C# i sig kommer troligen inte detta vara det bästa för dig heller. Det simplaste är kanske att läsa lite och avgöra själv. Vanliga frågor och svar ----------------------- Fråga: Vad är egentligen C#? Svar: C# (uttalas C-sharp), är ett objektorienterat programspråk som är utvecklat av Microsoft. C# är en del av .NET-plattformen, som i sin tur baseras på bland annat språken Java, C samt C++. Fråga: Varför borde man lära sig C#? Svar: Det finns ett antal anledningar till att man borde lära sig C#, ett par av dessa listar jag nedan: * Språket är eftertraktat på arbetsmarknaden idag * Språket är hyfsat lätt att lära sig * Microsoft har en kompilator som är gratis * Språket liknar andra språk vilket i sin tur leder till att det kan bli lättare att byta språk när man vill lära sig nytt, eller när man kommer ifrån ett annat snarlikt språk och vill lära sig C#. Fråga: Varför borde man INTE lära sig C#? Svar: Även här finns ett par anledningar, listar dem i en lista nedan: * Koden man skriver blir öppen för alla då det finns program som gör om den körbara filen till vanlig kod, utan problem. * För tillfället finns det färre användare med uppdaterad .NET-miljö än personer som har det, detta kommer dock snart att ändras då .NET börjar komma in mer på datorerna. * Eftersom att C# är skapat av Microsoft finns det ännu inget fulländat stöd för UNIX-system ännu, det som finns är Mono, för den som är intresserad. * Om man prioriterar väldigt snabba program (som i 3D-spel och dylikt) borde man inte alltid välja C# då det kan agera slöare än t.ex. c++. Fråga: Jag vill veta mer om vissa saker, vart ska jag vända mig? Svar: Väldigt ofta finns dina frågor redan besvarade, dina felbeskrivningar finns troligen besvarade ett flertal gånger innan; dessutom finns det många exempelkoder ute på Internet. Om du får reda på att det finns någonting vid namn "bool" och vill lära dig mer om just detta finns det ett par saker du kan göra; du kan surfa in på http://msdn.microsoft.com/ och göra en sökning efter "bool" och se vad du får fram. Letar du efter exempelkoder eller saker som redan är kodade kan du använda Google, som har en sökmotor gjord för just sökning bland kod. http://www.google.com/codesearch Nödvändiga program ------------------ När man programmerar i C# finns det en kompilator gjord av Microsoft, denna brukar anses vara den bästa av alla kompilatorer då det är Microsoft som utvecklat språket, således har deras kompilator all syntax och liknande som finns i C#. Vill du ladda ned gratisversionen av Microsoft Visual Studio går du till följande adress som i skrivande stund är den senaste skarpa versionen av Visual Studio Express: http://msdn2.microsoft.com/sv-se/express/aa975050.aspx Välj sedan Visual C# Express Edition 2005 som ska finnas uppe till höger i steg två. Installera Visual C# Express Edition och starta upp det, vi ska nu gå vidare till de mer intressanta delarna, ditt första program. Ett första program ------------------ Det har varit förhållandevis få saker du behövt göra nu; du har bara behövt läsa ett par frågor och svar samt installera Visual-Studio Express edition. Den första gången du startar programmet kan det ta lite längre tid. När du får igång programmet bör du se en rubrik som säger: "Microsoft, Visual C# 2005 Express Edition", under borde du ha en ruta med rubriken "Recent Projects", det är i denna ruta dina senaste projekt kommer att visas. Under och till höger om Recent Projects kommer du att se nyhetsrelaterade saker, för den som är intresserad kan det vara smidigt att ha det samlat där. Personligen använder jag bara startsidan till att öppna och skapa nya projekt. För att komma igång med ditt första program använder vi oss av Recent Projects och trycker på "Project..." bredvid "Create:" genast kommer vi då till en ruta där vi får välja vad för typ av projekt vi ska använda. Som du ser finns det en del att välja mellan, det vi ska köra på kallas för "Console Application". Markera och rikta din fokus på en textruta längst ned som heter "Name:", det är här vi ska döpa vårat projekt. När vi ska döpa projekt ska vi alltid tänka på att döpa dem logiskt, att senare komma fram till att man hade ett intressant projekt på gång för en månad sedan och försöka hitta det projektet mitt bland alla: "test1", "projekt01" och "ahaaa" kommer då att bli extremt jobbigt. Det första programmet vi ska skriva heter "Hello World", således skriver du det i textrutan och klickar sedan på "OK". Efter att ha tryckt på OK kommer du att se en viss färdigskriven kod. Det är här inne vi ska ha roligt tillsammans, men det är inte endast här vi ska rikta vår uppmärksamhet. Att endast använda kodfönstret när man arbetar i Visual Studio är jobbigt, rikta din uppmärksamhet till ett fönster till höger, rubriken på fönstret är "Solution Explorer"; och det är här dina filer som tillhör ditt projekt ligger. Du kan behöva dubbelklicka på "Program.cs" om du startar upp detta projekt igen utan att få fram kodfönstret direkt, som första gången du skapade det. När du arbetar med fler filer samtidigt kan du behöva veta hur du förflyttar dig mellan de olika filerna, om du kollar över kodfönstret kommer du att se ett par flikar. Testa klicka runt lite, imponerande? Kanske inte, men effektivt och bra är det i alla fall. Ovanför flikarna kommer du att hitta verktygsraderna, det är här du sparar dina projekt, testar ditt program och avslutar programmet om någonting går snett. Enough said, vi går vidare till koden. Koden borde se ut någonting som detta: [kod] using System; using System.Collections.Generic; using System.Text; namespace Hello_World { Class Program { Static void Main(string[] args) { } } } [slut på kod] Nu när vi ska skriva den första raden placerar vi vår markör mellan { och } tecknet som finns under Static void Main(String[] args) för att sedan börja skriva. Den första bokstaven vi ska skriva är 'S', det vi ser är att det kommer upp en form av scroll-lista som förklarar att det finns olika saker vi kan skriva, det som finns i listan är det som är möjligt. Om vi fortsätter vår färd, skriver vi ett 'y' för att märka vad som händer. Underbart! Listan följer med vad vi skriver för att enkelt hjälpa oss om vi behöver hjälp. Det vi letade efter var nämligen System. För att då enkelt skriva ut "System" utan att skriva ut resterande "stem" klickar vi på knappen Enter, vår bästa vän. Nu har vi sparat in tre knapptryck, helt underbart! Vi fortsätter i kodens hierarki och skriver en punkt. följt av detta: "WriteLine(". Din kod borde nu se ut som nedan: [kod] using System; using System.Collections.Generic; using System.Text; namespace Hello_World { Class Program { Static void Main(string[] args) { System.Console.WriteLine( } } } [slut på kod] Det vi vill att vårat program ska göra är att skriva ut en textrad i ett konsoll-fönster. Vi har nu skrivit ned vilken funktion vi ska använda och det som finns kvar är att specificera vad för text som ska skrivas, samt att avsluta raden. När vi ska skriva ned en textsträng gör vi detta inom citationstecken, således fortsätter vi med symbolen " följt av vad vi vill skriva. Eftersom att vårat program heter Hello World! är det detta vi ska skriva När vi är klara med strängen som ska skrivas ut avslutar vi med ett till citationstecken och en slutparantes. Vår rad borde nu se ut så här: [kod] System.Console.WriteLine("Hello World!") [slut på kod] Det vi har gjort nu är att berätta för kompilatorn att vi vill skriva ut en sak till konsoll-fönstret, vi har även specifierat exakt vad det är som ska skrivas ut. Nu är det dags att berätta att vår rad med kod är färdig, och detta gör man med hjälp av tecknet vid namn semikolon, det ser ut så här ; och du hittar det bredvid m-tangenten om du använder dig av qwerty-layouten på ditt tangentbord. Detta är all kod som behövs för att skriva ett Hello-World-program i C#. Självklart vill vi se att koden fungerar också. För att testa ditt program kan du trycka på F5, tyvärr kommer du bara att se fönstret en kort stund med hjälp av denna teknik. Således finns det ett kortkommando som hjälper dig att uppfatta vad som skrivs och händer i fönstret. Tryck istället ned Ctrl + F5 för önskat resultat. Får du inget fel? Grattis! Du har skrivit ditt första program i C#, nästan helt själv också. Som nybörjare har man dock en tendens att inte helt förstå den kod som skrivs ned i början. För att hjälpa dig en bit på traven ska jag gå igenom lite vanliga tecken som används. Tecken och liknande ------------------- Längst upp i koden har vi som du ser tre rader innehållandes "using", detta är ett nyckelord som importerar en klass eller ett namespace i din kod. Om du inte vet vad detta är för tillfället är det ingenting att oroa sig för, det kommer med tiden. Ett block kod markeras som sammanhängande med hjälp av { och } operatorerna, allting som finns innanför dessa två tecken tillhör ett och samma "kodblock". Variabler och liknande som skapas innanför dessa block kan inte användas utanför blocket det skapades i. I vårt första program har vi en rad som ser ut så här [kod] System.Console.WriteLine("Hello World!"); [slut på kod] Operatorn '.' (punkt), innebär att vi stiger in i en klasshiearki, klassen System har andra klasser och operationer underordnade sig, det samma med Console som hanterar in- och utmatning i konsolen. För att skriva ut en rad till konsolen använder vi oss av WriteLine, och för att ange värden till en funktion använder vi oss av paranteser och i detta fall när vi vill skriva ut en vanlig text använder vi oss av citationstecken när vi väl är inne i parantesen. För att visa för kompilatorn att raden kod är färdig använder vi oss som tidigare sagt av ett semikolon. Förhoppningsvis har detta gett lite mer insikt i hur det fungerar att skriva ett C#-program med Visual Studio, om inte kommer det att bli klarare med tiden. Avslutning ---------- Detta är min första text i en serie av ett par delar, jag hoppas att jag har tillfört mer än 12288 byte av skräp. Gillas det inte behöver man inte frivilligt läsa nästkommande artikel. Kul att du var med mig i.a.f. Jag vill tacka Martin för all hjälp, allt stöd är extremt uppskattat <3 ================================================================================ Introduktion till C#, del 1 - Uppgifter Retard ================================================================================ Den första lektionens uppgifter, spännande. Ännu har vi bara lärt oss att skriva ut text i ett fönster. Men vi ska ha uppgifter så att du kan leka av dig lite med vad vi lärt oss. Jag ska försöka göra beskrivningarna så bra som möjligt. Jag kommer att göra ett ASCII-fönster som ska emulera ett cmd.exe fönster, det du får upp när du trycker på Ctrl + F5. I detta fönster har jag utformat hur ditt program ska se ut när det körs. Självklart finns det vissa saker man inte kan illustrera här, som man kan göra i ett cmd-fönster med hjälp av kod och dylikt, men när sådana tillfällen tillkommer informerar jag lite extra om hur det ser ut när programmet fungerar. Låt oss öva! Uppgift ett ----------- ---------------------------------------------------+ | cmd.exe _ [] x | ---------------------------------------------------+ |Jag heter Ullis! |^| |Tryck på valfri tangent för att fortsätta... | | | | | | | | | |v| -------------------------------------------------+-+ Förklaring: Skriv ditt egna program som skriver ut ett namn i fönstret. Uppgift två ----------- ---------------------------------------------------+ | cmd.exe _ [] x | ---------------------------------------------------+ |Jag heter Ullis! |^| |Och bor i Stockholm! | | |Tryck på valfri tangent för att fortsätta... | | | | | | |v| ---------------------------------------------------+ Förklaring: Skriv ett program där du skriver ut ditt namn på första raden, på andra raden skriver du ut vart du bor. Uppgift tre ----------- ---------------------------------------------------+ | cmd.exe _ [] x | ---------------------------------------------------+ |* * |^| | * * | | | * * | | | * * | | | * * | | | * * | | | * * | | | * * | | | * * | | | * * | | | * * | | | * * | | |* * | | |Tryck på valfri tangent för att fortsätta... |v| ---------------------------------------------------+ Förklaring: Rita ut ditt egna (snyggare) X i fönstret. Stjärnuppgift ett ----------------- ---------------------------------------------------+ | cmd.exe _ [] x | ---------------------------------------------------+ | ----------------------- |^| | | | | | | | (blå bakgrund här | | | | | inne) | | | | | | | | | | | | | | | | | | | | --- | | | | | | | | | | | | DG | | | | | | | | | | | --- | | | | ----------------------- | | |Tryck på valfri tangent för att fortsätta... | | | |v| ---------------------------------------------------+ Förklaring: Ser du min jättesnygga IDG.se-logga som blev helt misslyckad? Bra! Denna ska du göra, komplett med bakgrundsfärg och allt som följer med. Bakgrundsfärgen i det inramade området ska vara blått, och resten ska vara som det vanliga fönstret, svart. Ett stort I, ASCII-liknande, D och G får vara vanliga små tecken. Vet du inte hur du ska göra? * msdn.microsoft.com * google.com * google.com/codesearch ================================================================================ 0x07 Introduktion till C#, del 2..........................................Retard ================================================================================ Förord ------ Så här är vi igen, i samma nummer och allting. Det kommer förhoppningsvis inte att hända igen. Anledningen till att mitt första nummer inte publicerades var för att jag var på semester och inte kunde renskriva min text som jag hoppats på. Men nu är vi här igen, ingen skada skedd. Datatyper --------- För att vi enkelt ska kunna gå vidare i våra exempel måste jag visa dig vad en datatyp är och när vi kan behöva använda dessa. För att nämna ett par datatyper har vi bland annat: char, float, decimal double, int och string. Det finns självklart fler, men det vi ska gå igenom nu är de två sistnämnda, string och int. Så vad är nu en datatyp? Susning.nu ger oss svaret: "En datatyp används för att klassificera lagrade data i ett datorprogram." Man kan säga att en datatyp är en egenskap hos en variabel, ett attribut som anger vilken typ av data variablen kan innehålla. datatypen int - som står för integer tar hand om våra heltal, medan string tar hand om våra texter. Integer - heltal String - teckensträng När ska man då använda en int istället för en String? Visst, int för heltal och strängar för text. Men även i strängarna kan man skriva in siffror. Där borde man väl kunna eliminera integerns existens? Inte riktigt så, de tal som lagras i en int kan man göra matematiska beräkningar mot, man kan addera, subtrahera och multiplicera och allt vad som tillkommer. Detta kan man dock inte göra i en sträng utan att omvandla värdet däri till en integer. Så fortfarande - int för matte och strängarna till texter. Hur skapar vi oss nu en integer? Om vi tänker oss ett par kakburkar på en hylla så har vi tre stycken sådana burkar, alla har varsin lapp på sig. "Kakor", "Lappar" och "Sylt". Först måste man tillverka dessa kakburkar, sedan ska man döpa dem för att slutligen lägga in kakor eller innehållet. I denna ordning skapar vi våra strängar och integers. [kod] int Kakor = 5; [slut på kod] Ovanstående kod skapar en integer vid namn kakor, och uppenbarligen innehåller kakburken fem stycken kakor. Om vi nu har ett barnbarn hemma hos oss, och barnbarnet knycker sig två kakor. Då måste ju värdet på Kakor ändras. Barnbarnet går fram till burken som det står Kakor på, kollar hur många som finns däri, tar sedan två stycken och går där ifrån. Ungefär så här ska även vi göra. [kod] Kakor = Kakor - 2; [slut på kod] Skillnaden mellan ovanstående är att vi inte behöver inköpa en till kakburk, den har vi redan. Då slipper vi att skriva "int" innan vi ska subtrahera vårat värde. Anledningen till att vi skrev Kakor efter likamedstecknet är för att vi på så vis anger det aktuella värdet i kakburken, för att sedan ta bort två ifrån det. Hade vi inte gjort detta skulle värdet i burken istället blivit -2, det skulle bli ett oönskat resultat. Samma sak när man ska ta division, multiplikation och addition och så vidare. Det är bara att lägga in tecknet för respektive räknesätt. Det var en int det, om vi nu ska lagra en text så använder vi som sagt en vanlig string. Det fungerar på ungefär samma sätt som när vi skapar en integer. Du ska få se ett exempel. [kod] string Kakburk = "text"; [slut på kod] Först säger vi att vi ska ha en string, sedan döper vi den till Kakburk för att sedan bestämma värdet av det som ska finnas inne i strängen. och eftersom att det är text vi själva skriver ned ska det vara inom citationstecken. Vi kan även skapa integers eller strängar som inte har någon information i sig från början. Detta kan man göra så här: [kod] int kakor; string kakburk; [slut på kod] Nu har kakor och kakburk inget värde i sig, om vi senare i programmet vill sätta in ett värde i dessa behållare gör man det på detta vis: [kod] kakor = 5; kakburk = "stekta kakor"; [slut på kod] Output ------ Engelskt uppslagsord output Svensk översättning {data} output, utdata Helt enkelt, det vi vill göra nu är det vi gjorde i Hello World i förra delen av denna serie. Jag tänkte gå igenom hur man kan skriva ut data lite mer ingående. Då börjar vi. När vi skriver ut data till konsolen kommer vi alltid att använda oss av System.Console i början av vår kod. Sedan finns det lite olika tekniker för att skriva ut vår text eller våra siffror. Vi kan göra som i förra delen och använda oss av WriteLine("hej!"); men vi kan även använda oss av Write("Hej!"); för att få ett snarlikt resultat. När vi använder oss av WriteLine är det meningen att vi ska skriva någonting, en text, siffra eller beräkning o.s.v. på en ny rad än på vilken rad markören stod innan. När vi använder oss av Write är det meningen att vi ska skriva någonting direkt efter där markören står för närvarande, med denna metod skapas inte automatiskt en radbrytning innan vår utdata skrivs ut. Vi vet sedan innan hur WriteLine ser ut, men för att uppfriska minnet lite, så ser det ut så här: [kod] using System; using System.Collections.Generic; using System.Text; namespace Exempel01_del2 { Class Program { Static void Main(string[] args) { System.Console.WriteLine("texten"); } } } [slut på kod] När vi vill använda Write använder vi oss av Write, istället. Och det kan då se ut så här: [kod] using System; using System.Collections.Generic; using System.Text; namespace Hello_World { Class Program { Static void Main(string[] args) { System.Console.Write("texten"); } } } [slut på kod] Till synes samma resultat, om du nu testar det. Det börjar hända saker om vi nu börjar blanda dessa, och skriva mer än en sak efter varandra. Om vi jämför detta: [kod] using System; using System.Collections.Generic; using System.Text; namespace Hello_World { Class Program { Static void Main(string[] args) { System.Console.Write("Haha, rtard!"); System.Console.Write("Du är fräck!"); } } } [slut på kod] Med en användning av WriteLine istället: [kod] using System; using System.Collections.Generic; using System.Text; namespace Hello_World { Class Program { Static void Main(string[] args) { System.Console.WriteLine("Haha, rtard!"); System.Console.WriteLine("Du är fräck!"); } } } [slut på kod] Så kommer vi nämnvärt att få olika resultat. För sakens skull kan jag rita upp resultaten åt dig. Här använder jag mig av Write: ---------------------------------------------------- | cmd.exe _ [] x | ---------------------------------------------------- |Haha, rtard!Du är fräck! |^| |Tryck på valfri tangent för att fortsätta... | | | | | | |v| ---------------------------------------------------- Här använder jag mig av WriteLine: ---------------------------------------------------- | cmd.exe _ [] x | ---------------------------------------------------- |Haha, rtard! |^| |Du är fräck! | | |Tryck på valfri tangent för att fortsätta... | | | | | | |v| ---------------------------------------------------- När du kodar kan du använda dig av + tecken om du känner för det. Det fungerar som så att du kan fortsätta med någonting mellan dina Write-satser, du kan t.ex. vilja slänga in en så kallad integer mellan en viss text. För att använda +-tecknet gör du så här: [kod] System.Console.Write("Hej!" + "Du vilde!"); [slut på kod] Resultatet i output skulle bli så här: "Hej!Du vilde!", notera att det inte är ett mellanslag efter utropstecknet, detta är ett vanligt fel man kan göra som nybörjare. Eftersom att Write fortsätter där man sist var så blir det ju logiskt efter det första utropstecknet. Således skriver den ut "Hej!Du vilde!" Lösning? Skriv helt enkelt ett mellanslag innan din första bokstav när du skriver din andra Write-sats. [kod] System.Console.Write("Hej!" + " Du vilde!"); [slut på kod] Om du skulle vilja skriva ut innehållet i en integer eller i en string kan du göra detta på detta vis: [kod] string hej = "Hahu!"; int tal = 123; System.Console.WriteLine(hej); System.Console.WriteLine(tal); [slut på kod] Skillnaden mellan att skriva ut strängar och vanlig text är att du inte skriver ut citationstecken innan du skriver namnet på strängen. Om du hade skrivit med citationstecken i detta fallet skulle du ha fått ut "hej" i konsolen istället för "Hahu!". Detta är ganska grundläggande, personligen hade jag aldrig problem med in- och ut-datan till konsolen, således är det svårt att förklara ytterligare. Om du har frågor finns jag ibland på hackensteins IRC-kanal, jag kan svara på frågor beroende på min sinnesstämning. #hckstein @ EfNet Input ----- Engelsk översättning input Svenskt uppslagsord ingångsdata Input är helt enkelt inmatning i din konsoll, det går ut på att man ber användaren av programmet att skriva in lite information för att sedan kunna hanteras av programmet. Ett input-program kan se ut så här: (Texten inom klamrar är vad användaren matar in) ---------------------------------------------------- | cmd.exe _ [] x | ---------------------------------------------------- |Hejsan! |^| |Vad heter du? | | |[Retard] | | |Hej Retard, hoppas du mår bra! | | |Tryck på valfri tangent för att fortsätta... |v| ---------------------------------------------------- Anledningen till att man har inmatningar är för att man ska kunna skapa interaktiva program istället för program som gör en uppgift och sedan avslutas. På så vis kan man skapa mycket roligare program. För att skriva ut information till konsolen använde vi oss utav System.Console.WriteLine("Hej!"); men om vi nu vill läsa och inte skriva text, borde det logiskt sett finnas någonting som heter så mycket som Console.ReadLine(); eller liknande, och det stämmer. Men att använda detta skiljer sig litet ifrån WriteLine(); i alla fall om vi vill få det fungera till vår fördel. [kod] static void Main(string[] args) { System.Console.WriteLine("Hej vad heter du?"); string namn = Console.ReadLine(); System.Console.WriteLine("Hej " + namn); } [slut på kod] I denna kod kommer vi att få följande utdata, och ännu en gång, det som är markerat innanför [ och ] är det användaren själv kan tänka sig skriva in. ---------------------------------------------------- | cmd.exe _ [] x | ---------------------------------------------------- |Hej vad heter du? |^| |[Retard] | | |Hej Retard | | | | | |Tryck på valfri tangent för att fortsätta... |v| ---------------------------------------------------- Anledningen till att vi stoppar in Console.ReadLine i en sträng först är för att om vi endast skriver System.Console.ReadLine(); kommer programmet vänta på att du trycker på enter utan att behandla det du nyss skrivit. Därför måste vi lägga det vi skriver i en sträng. Men om vi nu vill räkna lite, hur gör vi då? Som vi sa innan kan man inte jobba med strängar om man ska utföra beräkningar. [kod] static void Main(string[] args) { Console.WriteLine("Vilket är ditt favorittal?"); int tal = int.Parse(Console.ReadLine()); Console.WriteLine("Du skrev nyss " + tal); } [slut på kod] Det vi gjorde här ovan var att vi bytte ut string namn mot en integer vid namn tal istället. Men eftersom att du skriver ut text i din konsoll kommer det som tolkas i ReadLine() fortfarande vara en sträng. Därför skriver vi int.Parse för att göra om texten som skrivs till en form av integer. I början kan detta vara svårt att komma ihåg, men man kommer att mötas av felmeddelanden vid dessa fall. För att sedan skriva ut eller behandla talet vi matat in kommer vi att göra precis som förut. Vi använder oss av WriteLine och integern utan citationstecken när värdet ska skrivas ut. ================================================================================ 0x08 Introduktion till C#, del 2 - Uppgifter..............................Retard ================================================================================ Lite mer uppgifter, vi kör igång direkt. Försök att minnas så gott det går, men om du ej kommer ihåg vissa delar kan det vara lämpligt att repetera vissa delar. Låt oss dra igång! Uppgift ett ----------- ---------------------------------------------------- | cmd.exe _ [] x | ---------------------------------------------------- |5 |^| |Tryck på valfri tangent för att fortsätta... | | | | | | | | | |v| ---------------------------------------------------- Förklaring: Skapa ett heltal med värdet 7, minska värdet med två. Skriv sedan ut det på skärmen. Uppgift två ----------- ---------------------------------------------------- | cmd.exe _ [] x | ---------------------------------------------------- |Vad heter du? |^| |[Retard] | | | | | | | | |Tryck på valfri tangent för att fortsätta... |v| ---------------------------------------------------- Förklaring: Ställ en fråga till användaren om dess namn. Låt användaren svara (i detta fallet var svaret Retard) Uppgift tre ----------- ---------------------------------------------------- | cmd.exe _ [] x | ---------------------------------------------------- |Vad gör du? |^| |[programmerar] | | |Du programmerar? Det låter kul, good work! | | | | | |Tryck på valfri tangent för att fortsätta... |v| ---------------------------------------------------- Förklaring: Fråga användaren vad denne gör, vänta på svaret och kontra sedan med en text som innehåller det användaren matade in. Stjärnuppgift ett ----------------- ---------------------------------------------------- | cmd.exe _ [] x | ---------------------------------------------------- |Säg ett tal som ska adderas med 5! |^| |[10] | | |5 + 10 = 15 | | | | | |Tryck på valfri tangent för att fortsätta... |v| ---------------------------------------------------- Förklaring: Be användaren mata in ett tal som ska adderas med siffran fem. Skriv sedan ut beräkningen, och resultatet. ================================================================================ 0xFF - The End/Errata ================================================================================ Innan vi tar förväl så är det några saker som behövs rättas till sen förra releasen. * "jnz/jne rel_addr : Hoppar till rel_addr om ZF är 1, [...]" Självklart hoppar jnz om ZF är 0. Tack till KiTo som påpekade det först. Om mitt minne sviker mig på den punkten kan vi alltid skriva errata på errata. Det var ett annat mindre faktafel, men i min glömska har jag lyckats förtränga det. Kan också bero på visst inmundigande av alkoholhaltig vätska. Så det får vara för den här gången. Jaha, detta är då slutet på denna utgåva. Skicka in era kommentarer, påpekanden och bidrag till hckzine@gmail.com. Som ni antagligen såg i innehållsförteckningen finns det inte så stor variation på författarna. För att få en bredare bild behövs fler artikelförfattare, ingen artikel är för dålig. Och de som är det kommer inte med. Tills nästa gång, stig hai. Mån 10 Mar 2008 21:59:42 CET