29 Ağustos 2009 Cumartesi

JNI (Java Native Interface)

JNI nedir? Ne değildir?
JNI, Java uygulamalarınıza sanal makine de uygulanmamış ve saf Java ile uygulanması imkansız yada çok zor olan yetenekleri katabilmenize yarar. Java, felsefesi gereği platform spesifik fonksiyonelliği bünyesinde barındırmaz. Bundan ötürüdür ki JNI, Java kullanarak platform spesifik işleri halledebileceğiniz neredeyse tek yoldur. Ayrıca JNI farklı diller ile geliştirilmiş, C altından çağrılabilir uygulama ve kütüphanelerin Java üzerinden erişilebilmesini mümkün kılar. Tüm bunların yanında JNI performans gerektiren işlerin native kod kullanılarak JVM’in sağlayabileceğinden daha yüksek verim ile halledilebilmesine imkan tanır. Peki bütün bunların yanında JNI’nin zararlı yan etkileri yokmudur? Evet vardır. Öncelikle native bağlantısı olan Java programları JVM’nin sunduğu yüksek taşınabilirlik yeteneğini kaybederler. Dolayısıyla programların platformlar arasında taşınabilirliği daha zor bir iş haline gelir. Ayrıca JNI üzerinden Java sanal makinesine bağlanmış kodlarda bulunan bir hatanın saptanması zordur, daha da kötüsü bu hata tüm sanal makine de etkili olabilecek ciddi hatalara ve çökmeye yol açabilir. Artık JNI’nin ne işe yaradığını avantajlarını-dezavantajlarını bildiğimize göre nasıl kullanıldığını bir örnekle inceleyebiliriz:

Native.java dosyasının içeriği:

public class Native {

static {
System.loadLibrary("native_library");
}

public static native int sum(int x, int y);

public static void main(String[] args) {
System.out.println(sum(3, 5));
}

}


Yukarıda son derece sade bir Java sınıfı görüyoruz. Bu sınıf, sınıf yükleyicisi tarafından sınıfın yüklendiği anda çalıştırılacak static tanımlı blok, bir native metod deklerasyonu ve Java programının çalışmaya başlayacağı main metoduna sahip. Sınıfımızı javac ile derliyoruz:

javac Native.java

Elde ettiğimiz byte kodun(Native.class dosyası) içinde geçen native metodun(sum) javah kullanarak C deklerasyonunu elde ediyoruz:

javah -jni Native

Şimdi elimizde iki adet dosya var: Native.class ve Native.h

Burda Native.class bilindiği üzere JVM üzerinde işletilecek bytecode, Native.h ise Native.class içinde çağrıda bulunulan native metodun(sum) C deklerasyonu. Şimdi yapmamız gereken Native.h dosyasında deklare edilmiş c fonksiyonu uygulamak.

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Native */

#ifndef _Included_Native
#define _Included_Native
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Native
* Method: sum
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_Native_sum
(JNIEnv *, jclass, jint, jint);

#ifdef __cplusplus
}
#endif
#endif


Yukarıda gördüğümüz Native.h dosyasının içeriğidir. Görüldüğü üzere bu başlık dosyası

JNIEXPORT jint JNICALL Java_Native_sum
(JNIEnv *, jclass, jint, jint);


biçminde bir fonksiyon prototipi içeriyor.
Native.cpp adında bir C++ kaynak kod dosyası oluşturdum ve dosya içinde

#include "Native.h"

JNIEXPORT jint JNICALL
Java_Native_sum(JNIEnv *env, jclass cls, jint num1, jint num2) {
return num1 + num2;
}


yukarıda görüldüğü gibi Java_Native_sum fonksiyonunu implemente ettim. Bu işi de hallettiğime göre geriye yalnız herşeyi birleştirmek kaldı.

Linux yada Mingw kurulu ve Path konfigurasyonu yapılmış bir Windows üzerinde aşağıda ki kabuk komutu ile derleme işini halledebiliriz.

gcc -Wall -D_JNI_IMPLEMENTATION_ -Wl,--kill-at -I"D:/Program Files/Java/jdk1.6.0_14/include" -I"D:/Program Files/Java/jdk1.6.0_14/include/win32" -shared Native.cpp -o native_library.dll


Burada üç noktaya dikkat etmelisiniz. Birincisi -I parametresi ile gcc'ye bildirilen jni.h ve jni_md.h dosyalarının gerçek lokasyonları sizin makinanızda kurulum farklılıklarına göre değişebilir. İkincisi -Wl,--kill-at bağlayıcı(linker) direktifi verilmediği taktirde JVM üretilen shared kütüphanede ki tanımlara erişemez. Üçüncüsü kullandığınız işletim sistemine göre -o parametresinde verilen derleyici çıktı dosyası adını native_library.dll yada native_library.so biçminde düzenlemelisiniz.

Evet herşeyi doğru yaptıysak elimizde java kaynak kodu ile beraber Native.class, Native.h, Native.cpp ve native_library.so yada native_library.dll dosyaları bulunmaktadır. Artık java Native deyip ekranda 8 yazdığını görebiliriz :D Küçük fakat iyi bir başlangıç yapmış olduk. Bununla beraber JNI tabiki iki sayıyı toplamak için geliştirilmemiştir. Aksine genellikle Java'nın tasarımı ve felsefesi gereği uygun olmadığı işleri halletmek yada farklı dillerde yazılmış kütüphanelerin Java altından erişilebilir hale getirilmesinde kullanılır ve oldukça ayrıntılı bir programlama arayüzüdür. Şimdilik bu yazımı zaman sıkıntım dolayısı ile bitirmek zorundayım. Fakat yakın bir zaman içerisinde temel JNI fonksiyonelliğine değinen bir yazı daha hazırlama planım var. Umarım zaman bulabilirim. Hoşçakalın.

23 Ağustos 2009 Pazar

Iframe malware - Iframe virüsü

Uzun zamandır web tabanlı yazılım geliştiricileri rahatsız eden bir malware türü var ortalıkta. Bu malware'ın tipik davranışı bir activex açığından yararlanıp kişisel bilgisayarlara yerleşmek, kişinin sunucuya erişmekte kullandığı ftp bilgilerini elde etmek ve ardından bu ftp bilgilerini kullanıp sunucu üzerinde ki index dosyalarına kendisinin diğer pclere yayılmasını sağlacak bir iframe yerleştirmek. Bir iki gün önce bir telefon aldım. Önemli bir web projemin index dosyasında malware iframe bulunduğunu bana haber verdiler. Birden çok kişi pclerini kullanarak proje sunucusunun ftp hizmetiyle eriştiğinden index dosyasına iframe'i atan infekte olmuş pc belirsizdi. Bu durumda önümde iki seçenek vardı. Birincisi sunucuya ftp üzerinden erişimde bulunulan tüm makinelerin güvenliğini sağlamak, ikincisi sunucunun güvenliğini sağlamak. İlk önce hemen index içinde ki iframe kodunu el ile temizledim. Ardından olası infekte olmuş pclerin temizlenmesi için diğerlerini bu konuda uyardım. Fakat nihayi olarak herkezin kendi makinesinin güvenliğini sağlayabilmesi mümkün olmayabilirdi. Bu durumda ikinci seçeneğin daha kesin bir çözüm sağladığına karar verdim. Ama bunu yapmak pratikte pek de kolay gözükmüyordu. Nihayetinde birileri infekte olmuş bilgisayarı ile farkında olmadan yine index'e iframe yerleştirmesine neden olabilirdi. Sunucu tarafında ise herşey olağan gözüktüğünden bu durumun sunucu tarafından yakalanması da en azından anlık olarak zor gözüküyordu. Dosya yetkilerini ftp tarafından yazılamaz hale getirmek aklıma geldiysede üzerinde sürekli geliştirme uygulanan bir proje de bu hiç pratik olmazdı. Fakat bir şekilde bu duruma bir çözüm bulmam gerekiyordu, zamanım son derece kısıtlıydı. O gece aklıma infekte olmuş dosyaları tarayan ve içinde belirli bir formda iframe taşıyanları temizleyen küçük bir tarayıcı programlamak geldi. Bir kaç saat içerisinde elimde somut bişey oluşmuştu. Testleri de yapıp sonucun pozitif olduğunu görmemin ardından rahat bir uykuya daldım.

Bu gün ise aklıma diğer meslektaşlarımın da bu tür bir çözümü kullanmak isteyebilecekleri geldi ve iframe avlayıcısının nasıl çalıştığını anlatmaya koyuldum :

Öncelikle iframe avlayıcısı iki parçadan meydana geliyor
1) Küçük bir C programı
2) Küçük bir Bash scripti

C programı C'nin C99 sürümünü destekleyen tüm C derleyicilerinde derlenebilirdir ve aşağıda kaynak kodu bulunmaktadır :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#define LINE_BUFFER_LEN 32768
#define PATTERN_ELEMENT_LENGTH 128

const char IFRAME_VIRUS_PATTERN[][PATTERN_ELEMENT_LENGTH] = {
"<iframe",
"src=\"http://",
"width=",
"height=",
"style=\"visibility: hidden\">",
"</iframe>"
};

typedef struct {
const char *start;
const char *end;
} VirusLocation;

bool isLineInfected(const char *line, VirusLocation *vloc);
void clearInfectedLine(char *line, const VirusLocation *vloc);

int
main(int argc, char *argv[]) {
FILE * sourceFile;
const char *filePath = NULL;

if (argc == 2) {
filePath = argv[1];
} else {
printf("Usage : aiframe [filename]\n");
return EXIT_FAILURE;
}

sourceFile = fopen(filePath, "r");

if (sourceFile != NULL) {
char lineBuffer[LINE_BUFFER_LEN];
VirusLocation vloc;
while (!feof(sourceFile)) {
fgets(lineBuffer, LINE_BUFFER_LEN, sourceFile);
if (isLineInfected(lineBuffer, &vloc)) {
clearInfectedLine(lineBuffer, &vloc);
}
if (!feof(sourceFile)) {
printf("%s", lineBuffer);
}
}
fclose (sourceFile);
} else {
printf("Can not open file : %s\n", filePath);
}

return EXIT_SUCCESS;
}

bool
isLineInfected(const char *line, VirusLocation *vloc) {
const char *linePtr = line;
const int patternLen = sizeof(IFRAME_VIRUS_PATTERN) / PATTERN_ELEMENT_LENGTH;

int i;
for (i = 0; i < patternLen; i++) {
if ((linePtr = strstr(linePtr, IFRAME_VIRUS_PATTERN[i])) == NULL) {
return false;
} else {
if (i == 0) {
vloc->start = linePtr;
} else if (i == (patternLen - 1)) {
vloc->end =
linePtr + strlen(IFRAME_VIRUS_PATTERN[patternLen - 1]) - 1;
}
}
}
return true;
}

void
clearInfectedLine(char *line, const VirusLocation *vloc) {
const char *linePtr = line;
char clearedLine[LINE_BUFFER_LEN];

int i = 0;
while (*linePtr != '\0') {
if (linePtr < vloc->start || linePtr > vloc->end) {
clearedLine[i++] = *linePtr;
}
linePtr++;
}
strcpy(line, clearedLine);
}


Bash scripti ise yukarda kaynak kodu verilmiş C programını kullanır
(C programının derlenmiş halinin adı "aiframe" olmalı) :

#!/bin/bash

SOURCE_FILE=$1
SCANNED_FILE=$1.tmp
aiframe $SOURCE_FILE > $SCANNED_FILE

SOURCE_FILE_SIZE=$(du -b $SOURCE_FILE | awk '{ print $1 }')
SCANNED_FILE_SIZE=$(du -b $SCANNED_FILE | awk '{ print $1 }')

if [ $SOURCE_FILE_SIZE != $SCANNED_FILE_SIZE ]; then
echo File sizes not equal : $SOURCE_FILE_SIZE, $SCANNED_FILE_SIZE
echo File is possible to infected : $SOURCE_FILE
SOURCE_FILE_OWNER=$(ls -l $SOURCE_FILE | awk '{print $3}')
SOURCE_FILE_GROUP=$(ls -l $SOURCE_FILE | awk '{print $4}')
rm -f $SOURCE_FILE
mv $SCANNED_FILE $SOURCE_FILE
chown $SOURCE_FILE_OWNER:$SOURCE_FILE_GROUP $SOURCE_FILE
fi

rm -f $SCANNED_FILE


Peki bu programı nasıl kullanıyoruz?

Öncelikle C programımızın IFRAME_VIRUS_PATTERN kısmını size bela olmuş iframe malware'ına göre düzenleyin.

Eğer index dosyalarınıza malware tarafından eklenmekte olan iframe kodu :

<iframe src="http://iambad.com/" height=123 width=343 style="visibility:hidden"></iframe>

gibiyse IFRAME_VIRUS_PATTERN kısmını sırası ile

const char IFRAME_VIRUS_PATTERN[][PATTERN_ELEMENT_LENGTH] = {
"<iframe",
"src=\"http://iambad.com/",
"height=",
"width=",
"style=\"visibility: hidden\">",
"</iframe>"
};


gibi düzenleyebilirsiniz. Burada amaç malware'ın eklediği iframe'in sabit olan kısımlarını sırası ile belirlemek(ortalıkta farklı ve değişken formları mevcut). Ben yukarıda ki iframe tanımında "<iframe", "src=\"http://iambad.com/" .. gibi kısımlarının ne olursa olsun sabit olduğunu varsaydım. Burada kesinlikle dikkat etmenizin gerektiği bir nokta var. Eğer index dosyalarınızın içinde daha önceden eklemiş olduğunuz iframeleriniz bulunuyorsa IFRAME_VIRUS_PATTERN'in onlarla uyuşmamasını sağlamalısınız, aksi taktirde onlarda malware olarak algılanıp kaldırılırlar.

IFRAME_VIRUS_PATTERN kısmını düzenlediyseniz programı derlemek için;
gcc aiframe_kaynak_kodu.c -o aiframe
bash komutunu kullanabilirsiniz.

Ardından bash scriptini de aiframe_run.sh dosyası altında kayıt edin ve aiframe_run.sh ile birlikte derlenmiş aiframe adında ki C programımızı /usr/bin dizini altına atın.

Artık tek yapmanız gereken
find /web/dosyalarının/kök/dizini -type f -name index.* -exec aiframe_run.sh '{}' \;
gibi tüm web dökümanlarını tarayan bir bash komutunu belirli aralıklar ile çalışacak hale getirmek. Bunun için crontab'ı kullanabilirsiniz. Bu sayede artık index dosyalarımıza malware'ın eklemiş olduğu iframe kodları otomatik olarak çok kısa bir süre içerisinde kaldırılacaktır.

Tüm bunların yanında belirtmeliyim ki. Iframe avlayıcısından kaynaklanabilecek herhangi bir zarardan ötürü sorumluluk kabul edilmemektedir. Ne yaptığınızı bilerek ve sorumluluğu alarak yapın. Tüm web kök dizinine uygulamadan önce güvenli bir iki test yapmanızı öneririm.

22 Ağustos 2009 Cumartesi

Dinamik programlama

Uzun zamandır birşeyler yazamadım. Bir yerlerde dinamik programlama üzerine birşeyler okumuştum, açlığın tavana vurduğu iftar saatine doğru birden aklıma geldi. Ben de açlığımı bastırma, zamanı hızlandırma niyetiyle bir örnek program yazmaya karar verdim. Aşağıda dinamik programlama tekniği ile yazılmış küçük bir C programı görüyorsunuz :

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

typedef unsigned long long ull_int;

ull_int fiboCalc(ull_int x);
void fiboBuildCache(size_t size);
void fiboLoadCache(size_t prevCacheSize);
void fiboFreeCache(void);

ull_int *fiboCache = NULL;
size_t fiboCacheSize = 0;

int main(int argc, char *argv[]) {
srand(time(NULL));
int i;
for (i = 0; i < 100000000; i++) {
int randNum = (abs(rand()) % 40);
// printf("f(%d)\t= %d\n", randNum, fiboCalc(randNum));
}
fiboFreeCache();

system("PAUSE");
return EXIT_SUCCESS;
}

ull_int
fiboCalc(ull_int x) {
if (x < 2) {
return x;
}
size_t requiredCacheSize = (x + 1) * sizeof(ull_int);
if (fiboCache == NULL || fiboCacheSize < requiredCacheSize) {
fiboBuildCache(requiredCacheSize);
}
return fiboCache[x];
}

void
fiboBuildCache(size_t size) {
size_t prevCacheSize = fiboCacheSize;
if (fiboCache == NULL) {
fiboCache = malloc(size);
fiboCacheSize = size;
fiboLoadCache(prevCacheSize);
} else if (fiboCacheSize < size) {
fiboCache = realloc(fiboCache, size);
fiboCacheSize = size;
fiboLoadCache(prevCacheSize);
}
}

void
fiboLoadCache(size_t prevCacheSize) {
int i, end;
if (prevCacheSize == 0) {
fiboCache[0] = 0;
fiboCache[1] = 1;
i = 2;
} else {
i = prevCacheSize / sizeof(ull_int);
}
end = fiboCacheSize / sizeof(ull_int);
for (; i < end; i++) {
fiboCache[i] = fiboCache[i - 2] + fiboCache[i - 1];
}
}

void
fiboFreeCache(void) {
if (fiboCache != NULL) {
free(fiboCache);
fiboCache = NULL;
fiboCacheSize = 0;
}
}


Programa göz gezdirdiğiniz de işlevinin fibonacci sayılarını hesaplamak olduğunu fark etmişsinizdir. Peki bu programı dinamik yapan nedir? Dinamik programlamayı bir problemin çözümünde, problemin kapsadığı alt problemleri birden fazla defa çözmemizi engellemeye dayanan bir teknik olarak ifade edebiliriz. Dolayısı ile dinamik programlama bir başka açıdan bakıldığında bir optimizasyon tekniğidir. Bu açıdan yukarıda ki programın yaptığı tam da budur. Problemimiz fibonacci sayılarını hesaplamaktır ve fibonacci sayıları fib(n) = fib(n-1) + fib(n-2) formülü ile hesaplanır. Dolayısı ise fibonacci sayılanın hesaplanması problemi alt problemlerin sonuçlarının tekrar tekrar kullanılabileceği bir türdedir. Yukarıda ki programda 0 ila 40 arasında rastgele oluşturulan 100.000.000 sayının fibonacci dizisinde ki karşılıkları hesaplanmaktadır. Bu program da aslında önemli bir kaç iyileştirme daha yapılabilir fakat bu haliyle dahi bir hayli verimlidir ve bunda dinamik programlama tekniğinin kullanılmasının önemi çok büyük. Kısacası alt problemlerin tekrarlandığı problem çözümlerinde alt problemlerin sonuçlarının saklanması ve tekrar tekrar kullanılması algoritmalarınızı çok hızlandırabilir.

5 Nisan 2009 Pazar

Yeni diller ve Groovy

Webte dolaşırken bir yazı ile karşılaştım. Php platformunu kullanan ve gücüne inanan bir meslektaşım son zamanlarda o kadar çok dilcik tasarlandı ki artık bunun sonu nereye varır diye yazmış ve Groovy'e "dilcik" yakıştırmasında bulunmuştu.

Son yıllarda çok sayıda dilin tasarlanıp uygulandığı hepimizin bildiği bir gerçek olduğu düşüncesindeyim. Fakat bu dillerin biri hakkında birşeyler yazmak istedim. Bu dil Groovy.

Günümüzde bir çok bilgisayar uzmanı ve programcının kullandığı bir ifade var bu ilginç dönem için "Diller çağı" diye nitelendiriyorlar bu dönemi. Son 10 sene içerisinde o kadar fazla yeni dil tasarlandı ve uygulandı ki takip etmek neredeyse imkansız. Fakat bunun ne anlama geldiği bence başlıbaşına bir tartışma konusu. Tüm bu dillerin arasından geçenlerde Groovy ile tanıştım. Şunu söylemeliyim ki gerçekten etkileyici bir proje Groovy. Pyhton yada Ruby gibi esneklik ve üretkenlikte adından söz ettiren dillerden hem dil bazında hem üretkenlik bazında asla geri kalmıyor Groovy. Günümüz programlama inceliklerinin tamamını yada çok önemli bir kısmını uygulayabilicek kapasiteye sahip olduğunu söylemeliyim. Ayrıca ardında çok güçlü bir platform ve sınırsız kütüphane barındırıyor bu dil. Çünkü groovy ile on yılı aşkın süredir yazılmış ve kullanıma hazır yüzbinlerce java sınıfını sorunsuz kullanabiliyorsunuz. Hem de nokta kadar bir değişiklik gerektirmeden. Ayrıca Groovy çok büyük oranda kendi sintaksına ek olarak java sintaksını da destekleyen bir dil, yani resmen java sintaksında kod yazıyorsunuz ve groovy yorumlayıcısı bu kodu derleme gerektirmeden işletebiliyor. Bu niteliği mevcut Java programcılarının dili öğrenmesinde büyük kolaylık getiriyor. Ayrıca bir özelliğe daha sahip ki değinmeden geçemem. Groovy kodu java sınıflarına(java bycode code) derlenebiliyor ve java ile yazılmış programlarda hiç bir değişiklik gerekmeden bu sınıfları kullanabiliyorsunuz. Bu dil ayrıca yaygın programlama ihtiyaçları için çok başarılı bir utility kütüphanesine sahip. Yani neredeyse Groovy Java dilinin yeni nesil bir uyarlaması diyebiliriz. Doğruyu söylemek gerekirse halen bir çok projesinde Php, C++ gibi dil ve platformları kullanmak zorunda kalan ben birçok defalar keşke diyorum. Keşke Php'de closureları desteklese. Keşke Php dil bazında DOM ve XML parsing ve generating desteklese, keşke Php'ninde temel kütüphane işlevselliği tamamen nesne yönelimli paradigmayı kullansa. Keşkeler böyle sürüyor gidiyor.. Burada asıl değinmek istediğim aslında uzun süredir kullandığımız birçok dil ve platformun artık kendilerini bir nebze olsun modernize etmeleri gerektiğidir. Php'de bunların arasında. Php hala anonim fonsiyonları desteklemiyorsa artık bence birşeylerin sorgulanmasını gerekir. Çünkü Groovy gibi dil ve teknolojilerin arkasındaki kaynaklar çok daha kısıtlı olduğu halde bu niteliklerin tümü Groovy ve Ruby gibi dillerde mevcut. Yaygın deyimle "diller çağı" bence dil tasarımda kalitenin yitirildiği, etrafta bir çok amacsız ve plansız dilin boy gösterdiği bir dönem değil. Aksine programcılık sanatında bir rönesans olarak dahi nitelendirilebilir. Yıllarca kısıtlı dil kümeleri ve kısıtlayıcı programlama yaklaşımları ve tekniklerinin kırıldığı bir dönem olduğu inancındayım bu dönemin. Bence programcı olarak çalışan, düşünen ve yaşayan dostlarımın Groovy gibi genç, heyecanlı ve nitelikli projelere daha iyimser, daha sağ duyulu bakmaları daha güzel bir yaklaşım olurdu.

2 Ocak 2009 Cuma

Hug a developer today...

Programcılar ve yaşadıkları üzerine hoş bir kısa film. İzlemenizi öneriyorum :)

Buyrun izleyin