저번에 말씀드린 ICMP패킷을 이용한 스캔 방법을 말씀드리겠습니다.

오늘은 정상적인 ICMP를 통해 응답에 따른  공격대상의 존재 유무를 파악하는 것을 해 볼 것인데요. ICMP 타입중에서도 0 과 8인  request와 reply를 이용해 보도록 하겠습니다. 
그리고 다음에 올려질 부분에 타입에 따라서 차이점을 설명하고 소스를 올리도록 하겠습니다.


저번에 ICMP를 올려본것과 큰 차이점은 없고 단지 툴의 형식을 좀 갖추었다는 것 인데요. 뭐 해킹 툴은 아니고 그냥 Jpcap 프로그래밍에 초점을 맞추어서 이 글을 읽으시면 .... 더 맞을것 같습니다. 

달라진 점은 다음과 같습니다.
1. 공격자의 IP와 MAC주소 자동 인지
2. 공격자의 디폴트게이트웨이 맥주소 자동인지
3. 희생자의 네트워크 서브넷 환경에 따른 범위 설정가능
4. CheckSum자동계산


보완해야할 부분
1. Padding관련 부분 구현안함
2. Request 패킷만 생성가능 
3. Type변화에 따른 헤더길이 변화 불가.  >>>>>>>차후 소스라우팅이나, 타임스탬프 가 적용가능토록 가변적인 헤더길이 요구
4. 스래드구현시 매끄럽지 못한부분들..



소스 구성
sfping.java                                패킷세부구성, 인터페이스에 따른 처리, IP와 맥주소 자동 획득
dump_for_thread.java              Jpcap에서 제공되는 메소드를 처리하기 위해 Runnable클래스로 감싸기 위한 작업
 dump.java                               패킷 캡쳐 및 , 조건문에 의한 특정 패턴 파악후 처리


================================      sfping.java===================================
import java.util.Scanner;
import jpcap.*;
import java.net.URL;
import java.util.Arrays;

import jpcap.packet.*;
import java.net.InetAddress;

import jpcap.NetworkInterfaceAddress;

public class sfping {
//////////////채크섬   채크과정, 
public static short cksum(byte[] buf, int len) {

int sum = 0;                   ////////2바이트씩 끊어읽을때, 사용되는 값, 2바이트만 사용합니다. 
int x = 0;                      //sum이 더해지는 변수 , 2바이트이상넘어가는 캐리가 발생되므로 4바이트형
int cmask = 0xffff0000;     //캐리만을 살려두기 위한 마스크
byte temp = 0;  //캐리가 임시저장될 공간
short CK = 0;  //최종 채크섬 값이 리턴될 변수

for (int i = 0; i < len; i++) {
sum = (((int) buf[i]) << 8) & 0xFF00 | ((int) buf[i+1])&0xFF;        //// 이부분에서 자바 처리가 AND연산을 꼭 해줘야
x = x + sum; 
i += 1;  //2바이트씩 끊어읽기위해 증가
}
temp = (byte) ((x & cmask) >> 16);   //캐리획득
CK = (short) ~(x + temp);  //1의 보수
return CK;
}
public static void main(String[] args) throws java.io.IOException {
if (args.length !=2) {
System.out.println("Usage: java Arscan NetworkAddress NetMask");           //이렇게 사용하세요
System.exit(0);
} else {

// Jpcap init static
NetworkInterface[] devices = JpcapCaptor.getDeviceList();  
// interface list display
for (int i = 0; i < devices.length; i++)
System.out.println(i + ":" + devices[i].name + "("
+ devices[i].description + ")");

// select interface   
System.out.print("\n=================================\nSelect Your Network Interface => ");
Scanner scan = new Scanner(System.in);  ///자바에선 이런식으로 입력받음.. C보다 귀찮음.
int device = scan.nextInt();                   ///선택한 인터페이스번호를 저장

//get My IP  & MAC  auto,... 자동으로 내 맥주소와 IP를 획득한다. Jpcap에서 제공하는 NetworkInterfaceAddress
//이다. 자바에서 제공하는거랑 헷갈리지 않도록. 
byte[] myIp = new byte[4];
byte[] myMac = new byte[6];
for (NetworkInterfaceAddress a : devices[device].addresses){     >>>요부분을 다른식으로 표현하고 싶은데 패스~
///devices[device]  ..요기서 device  는 위에서 선택한 인터페이스 번호임
InetAddress intaddr = a.address;         ///한번 찍어주고
myIp = intaddr.getAddress();     /// IP   자동 획득, byte[] 형식
myMac = devices[device].mac_address;// Mac   자동 획득, byte[] 형식
break;                       ////just one time  이게 위에서 for문을 통해 두번돌게 되있다. 첫번재돌대는 IPv4.
/// 두번째 돌대는 IPv6버전으로 나타나는게 첫번째꺼 사용을 위해 한번돌고
//break;
}
/////////////////////  Get the gateway mac....auto..........  ///// 이부분은 Jpcap에서 제공해주는 microsoft로 임의의 패킷을 보
//내서 거기서 디폴트게이트웨이의 맥주소를 잡아낸다. 그냥 갖다 써먹자. 
JpcapCaptor captor=JpcapCaptor.openDevice(devices[device],2000,false,5000);
InetAddress pingAddr=InetAddress.getByName("www.microsoft.com");
captor.setFilter("tcp and dst host "+pingAddr.getHostAddress(),true);
byte[] gwmac=null;
while(true){
new URL("http://www.microsoft.com").openStream().close();
Packet ping=captor.getPacket();
if(ping==null){
System.out.println("cannot obtain MAC address of default gateway.");
System.exit(-1);
}else if(Arrays.equals(((EthernetPacket)ping.datalink).dst_mac,devices[device].mac_address))
continue;
gwmac=((EthernetPacket)ping.datalink).dst_mac;  ////   요렇게 gwmac[] 에 byte형식으로 저장된다. 
break;
}
// capture start
Dump_for_thread t = new Dump_for_thread();           ///스레드 생성해서 sending과 capture를 동시에...
Thread thd1 = new Thread(t);
thd1.start();
try {        //just wait.. because interval..no reasonrseinInt
      Thread.sleep(2 * 1000);                 ///보내는 시작 속도가 캡쳐시작속도보다 빨라서 2초기다리고
    } catch (InterruptedException e) { }
    
  //Target IP setting              //커맨드 형식에 따라 IP를 받아서 셋팅가능토록 배열에 저장해두고
    byte[] TarIp = InetAddress.getByName(args[0]).getAddress();
   
    //Target mask setting       //커맨드 형식에 따라 마스크 정보를 입력받아 스캔할 범위를 자동으로 지정
    byte startIp=0;            //마스크에 따른 시작 IP
    byte endIp=0;           //마스크에 따른 마지막IP
    int mask = Integer.parseInt(args[1]);
    if(mask==24)
    {
     startIp = 0;
     endIp = (byte)255;
    }
    else if(mask ==24)
    {
     if(!(TarIp[3] == 0 )) { System.out.println("Check the Subnet and IP");  System.exit(0);}
     startIp = (byte)(TarIp[3]+1);
     endIp = (byte)(TarIp[3]+255); System.out.println(myIp[0]+""+ myIp[1]);
    
    }     
    else if(mask ==25)
    {
     if(!(TarIp[3] == 0 | TarIp[3]==128)) { System.out.println("Check the Subnet and IP");  System.exit(0);}
     startIp = (byte)(TarIp[3]+1);
     endIp = (byte)(TarIp[3]+127);
    }
    else if(mask ==26)
    {
     if(!(TarIp[3] == 0 | TarIp[3]==64 | TarIp[3]==128 |TarIp[3]==192 )) { System.out.println("Check the Subnet and IP");  System.exit(0);}
     startIp = (byte)(TarIp[3]+1);
     endIp = (byte)(TarIp[3]+63);
    }
    else if(mask ==27)
    {
     if(!(TarIp[3] == 0 | TarIp[3]==32 | TarIp[3]==64 |TarIp[3]==96| TarIp[3]==128 
     |TarIp[3]==160 |TarIp[3]==192 | TarIp[3]==224)) { System.out.println("Check the Subnet and IP");  System.exit(0);}
     startIp = (byte)(TarIp[3]+1);
     endIp = (byte)(TarIp[3]+31);
    }
    

    // sender init
JpcapSender sender = JpcapSender.openDevice(devices[device]);
// make raw packet
Packet sendPack = new Packet();
byte pb[] = new byte[64];
// Dst Mac            //위에서 자동획득한 gw 맥주소 입력
pb[0] = gwmac[0];
pb[1] = gwmac[1];
pb[2] = gwmac[2];
pb[3] = gwmac[3];
pb[4] = gwmac[4];
pb[5] = gwmac[5];
// Src_Mac
/////// 내꺼 Mac주소 입력
pb[6] = myMac[0];
pb[7] = myMac[1];
pb[8] = myMac[2];
pb[9] = myMac[3];
pb[10] = myMac[4];
pb[11] = myMac[5];
// Ether Type(byte)(cksum(IPHeader,((pb[14] & 0x0f) * 4))>>8);
pb[12] = (byte) 0x08;
pb[13] = (byte) 0x00;
////////////////////////////////////////////////////////////           IP
// Header Ver, Length
pb[14] = (byte) 0x45;                 ///헤더 길이 5*4 =20    type변경시 헤더길이 변경해줘야함
//TOS                              //상황에따라 수정가능 .. 보통건드리지 않고
pb[15] = (byte) 0x00;
// Total Length0                   //전체 데이터의 길이를 구하기 위해 계산 16비트이므로 최대 2^16
pb[16] = (byte) 0x00;        
pb[17] = (byte) 0x32;
// ID                                  //조각화 되었을때 동일 패킷임을 입증하는 ID 같은 ID안에서 아래 offset로 찾는다
pb[18] = (byte) 0x00;
pb[19] = (byte) 0x00;
// Flags, Fragment offset           //////플레그멘테이션으로 공격시 수정되는 부분   
pb[20] = (byte) 0x40;
pb[21] = (byte) 0x00;
//TTL                                  // traceroute 구현시 수정되는 부분
pb[22] = (byte) 0x80;
//Protocol
//tcp 6 udp 17   icmp 1               //상위계층의 정보
pb[23] = (byte) 0x01;
//Header Checksum             //일단 0 으로 채우고 iP해더 다 채워지면 그때 계산해서 나중에채움
pb[24] = 0;
pb[25] = 0;
//source IP                  //아까 자동획득했던
pb[26] = myIp[0];
pb[27] = myIp[1];
pb[28] = myIp[2];
pb[29] = myIp[3];

//Dst IP                      마지막 부분은 Mask정보에 따라 가변적이므로 0으로 일단채워둠
pb[30] = TarIp[0];
pb[31] = TarIp[1];
pb[32] = TarIp[2];
pb[33] = 0;
/////// IP checksum을 위한 IPHeader배열 생성   여기에는 IP헤더만 들어감, 나중 채크섬계산시 길이와함께 전달
byte IPHeader[] = new byte[20];
for(int i=0; i<((pb[14] & 0x0f) * 4); i++)
IPHeader[i] = pb[i+14];
////////////////////////////////////////////////////////   ICMP

//TYPE
pb[34] =  (byte)0x08;           //request
//Code
pb[35] =  (byte)0x00;           //
//Checksum
pb[36] = 0;             //일단 비워두고 나중에 계산해서 채움
pb[37] = 0;
//IDint
pb[38] =  0;          //보내는 쪽이므로 신경쓸필요없음
pb[39] =  0;
//Sequence Number
pb[40] = 0;            //보내는 쪽이므로 신경쓸필요없음
pb[41] = 0;
///data              임의데이터 생성
for (int i = 0; i < 22; i++) 
pb[42+i] = (byte)i;
//Checksum                                             
byte ICMPHeader[] = new byte[30];      ///IPHeader과 마찬가지로 
for(int i=0; i<30; i++)
{
ICMPHeader[i] = pb[i+34];
}
//////////ICMP Checksum 
pb[36] = (byte)(cksum(ICMPHeader,30) >>8);             //Checksum메소드에 헤더와 길이를 넘겨줌
pb[37] = (byte)(cksum(ICMPHeader,30));                       ///계산된 값이 리턴되어 돌아옴, 2바이트르 리턴하므로
//나눠서 한바이트씩 저장
////////////IP checksum
//   ICMPchsecksum을 계산할 때와는 달리 매번 변경되는 IP주소를 반영해서 Checksum을 계산해야 하기 때문에
//ip하나 바꾸고 채크섬 계산하고 보내고, ip하나 바꾸고 채크섬 계산하고 보내고,... 이런식으로 구현해야한다.

for(int i=startIp; i<=endIp; i++ )         //Mask에 따른 시작주소와 끝주소결정된것을 사용
{
IPHeader[19] = (byte)i;         //checksum을 위한 정보에도 
pb[33]=(byte)i;
pb[24] = (byte)(cksum(IPHeader,((pb[14] & 0x0f) * 4))>>8); //       헤더에 선언된 길이정보를 가지고 넘겨줄
pb[25] = (byte)(cksum(IPHeader,((pb[14] & 0x0f) * 4))); //길이를 자동선출

sendPack.data = pb;  
sender.sendPacket(sendPack);
}
sender.close();
}
}
}


////////////////dump_for_thread.java          ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
<br />
import jpcap.*;
import java.io.IOException;
<br />
<br />
public class Dump_for_thread implements Runnable{            /// 요거는 Java관련된건데 인터넷찾아보면 잘나옴
public void run(){
NetworkInterface[] devices = JpcapCaptor.getDeviceList();
JpcapCaptor jpcap = null;
try {
jpcap = JpcapCaptor.openDevice(devices[0], 2000, true, 1);            ///요부분을 수정해야함 . 일단은그냥 
///스테틱하게 정해버렸는데,  sfping.java에서 선택된 device값을 받아와서 적용되도록 수정해줘야한다.
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
jpcap.loopPacket(-1, new dump());
}
}
<br />
////////////////               dump.java          ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
mport jpcap.PacketReceiver;
import jpcap.packet.Packet;

class dump implements PacketReceiver
{
public void receivePacket(Packet p) 
{
byte[] bytes = new byte[p.header.length + p.data.length];
System.arraycopy(p.header, 0, bytes, 0, p.header.length);
System.arraycopy(p.data, 0, bytes, p.header.length, p.data.length);
if (bytes[34] == (byte) 0x00 &&bytes[23] ==(byte)0x01) 
///icmp 중 reply인것만
{
System.out.format("%d.%d.%d.%d is alive\n", (0xff)&bytes[26], (0xff)&bytes[27], (0xff)&bytes[28], (0xff)&bytes[29]);
}
}
}
===============================================================================================



이런식으로 수행됩니다 

이 소스를 토대로 다음 ICMP 를 이용한 스캔에서는   수정되는 부분만 올리고 설명하도록 하겠습니다. 

AND