穴を開ける方法

Daisukeh 2009/02/19 10:47 初稿
Daisukeh 2009/08/09 10:57 移設

記事一覧

 いきなり「穴を開ける方法」と言われてもピンとこないかもしれないので説明しよう。ある環境下ではそのパソコンをLANに接続しても、外部のネットワークへ接続したくない場合がある。たとえば重要なデータを管理しているサーバーだとか、OSの更新を行う必要がない(またはしたくない)場合などである。こういうパソコンでは、(特にWindowsに限定するが)ゲートウェイ設定や DNS設定を無効にすることで、パソコン内部から外部ネットワークへの接続を拒否することができる。同様に外部からはファイアーウォール設定を設定することで、特定のポートに対して処理を受け付けないことが可能だ。たとえば、Telnetは一般には23番を使用することが多いが、サービスとしてTelnetを起動していても、ファイアーウォールでそのポートを閉じていれば、外部から進入することはできない。(Telnetを不特定の相手に許可してしまうと、何でもできてしまうから。)逆に先述のゲートウェイDNSの設定が行われていない場合、意図した外部接続だからファイアーウォールの管轄外となるが、WANに接続している相手のアドレスを検索する方法がない(正しく言えば、アドレスを照会してくれるパソコンがいない(=登録されていない)ので、調べようがない)から、ウェブもメールもインターネットの世界には入り込めないということになる。

 そこで、そういうパソコンに一時的に「穴を開ける」ことで、外部へデータを送信する方法を試みてみた。

 これは一歩間違えればバックドア的なクラッキング行為であることは否めない。だが、たとえばクローズドな制御システムで、動作履歴を定期的に遠隔監視したり、限定された特別な接続で遠隔操作を行うといった用途には、非常に有用な技術だと思う。もう一度言うが、そういう行為は裏を返せば不正アクセスとやっていることはなんら変わりは無いかもしれないが。

ゲートウェイの操作

 ゲートウェイの機能を簡単に説明すると、プロトコル変換(アドレス変換やパケット送受信の調停)である。一般に(ほとんどの場合)WANLANでは流れている情報が微妙に異なる。もっとも異なるのがアドレスで、WAN側からLAN側のアドレスを見ることはできない。このことは逆にも言えて、LAN側からWAN側のウェブサーバーやメールサーバーにアクセスする場合に、その相手の本当の(ローカルの)アドレスはわからないし、わからなくてもいいのだ。

 Windowsではネットワークプロパティにゲートウェイアドレス(ルーターなどのアドレス)を指定することで、ゲートウェイの役目を果たしてくれる機器を指定することになっている。指定しない場合、外部は見えないということだ。プログラムでそのゲートウェイを設定するためには、下に示す手続きが必要になる。

setGateway()

private void setGateway(String adapter, String gateway)
{
  Process p = new Process();
  p.StartInfo.FileName = "netsh";
  gateway = (gateway != "") ? (gateway + " gwmetric=1") : "none";
  p.StartInfo.Arguments = "interface ip set address \"" + adapter + "\" gateway=" + gateway;
  p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
  p.Start();
  p.WaitForExit(10 * 1000);
  p.Close();
}

使う場合にはコツが必要で、しかもかなりローテクなのだが、

setGatewayのサンプル

  // ゲートウェイの設定
  setGateway("ローカル エリア接続", "192.168.0.1");
 
  // ...
 
  // ゲートウェイの解除
  setGateway("ローカル エリア接続", "");

となる。”ローカル エリア接続”っていう識別名がローテクだよね。もうひとつ、実はゲートウェイが設定されている、という場合をチェックする方法は以下の様になる。

getDefaultGateways()

public List<string> getDefaultGateways()
{
  List<string> gwlist = new List<string>();
  try
  {
    string query = "SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled=TRUE";
    ManagementObjectSearcher mos = new ManagementObjectSearcher(query);
    ManagementObjectCollection moc = mos.Get();
    foreach(ManagementObject mo in moc)
    {
      string[] gateways = (string[])mo["DefaultIPGateway"];
      foreach(string gateway in gateways) gwlist.Add(gateway);
    }
  }
  catch(Exception) {}
  return gwlist;
}
 
private void foo()
{
  List<string> gateways = getDefaultGateways();
  if(gateways.Count == 0)
  {
    // ゲートウェイが設定されていない場合の処理
  }
}

DNSの操作

 DNSはその名が示すとおり、指定されたドメイン名(daisukeh.ddo.jpなど)を、WAN側で使われている本当のアドレス(124.27.164.137など⇐適当だけど)に翻訳する機能だ。世界にはルートサーバーと呼ばれるDNSが13個あり、その多くがインターネット発祥の地であるアメリカに集中しているが、そこの情報を中継点となるサーバーがコピーして使用している。プロバイダなどもそのひとつだし、家庭や会社のルーターにも小規模ながら一時的に名前とアドレスの住所録を管理する機能があると言う訳だ。ルーターの場合、自分の中にキャッシュされた住所録の中に名前が載っていなければ、より上位のサーバーに名前を照会してアドレスをもらってくる。インターネットの仕組みは基本的にはバケツリレーなので、データが無ければ他のマシンにデータをもらうということである。ルートサーバーでなくても大規模なネットワークサーバーは、抱えている住所録が必然的に多いし、バケツリレーは頻繁に行われているから、ルートサーバーまで照会に行くことは滅多に(というか絶対に)ないだろう。そういえばWindowsの中にも簡易的な住所録機能があって、一度ネットワーク接続した相手は、その接続が終了しても一定期間接続情報を保持するようになっている。これはネットワークモニタツールでみれば一目瞭然である。

 そのDNSを設定するプログラムは下に示す手続きが必要だ。

setDNS()

private void setDNS(String adapter, String primary)
{
  Process p = new Process();
  p.StartInfo.FileName = "netsh";
  if(primary == "") primary = "none";
  p.StartInfo.Arguments = "interface ip set dns \"" + adapter + "\" static " + primary;
  p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
  p.Start();
  p.WaitForExit(10 * 1000);
  p.Close();
}

使い方はゲートウェイの時と同じになる。

setDNSのサンプル

  // DNSの設定
  setDNS("ローカル エリア接続", "192.168.0.1");
 
  // ...
 
  // DNSの解除
  setDNS("ローカル エリア接続", "");

 ここまで来て気付いた人もいるかもしれないが、シェルコマンドの netsh を呼び出しているだけなのだ。Windowsも所詮はコマンドプロンプトでほとんどの操作をすることが可能だといういい例である。ちなみに、サービスプログラムの登録や初期状態、起動・停止などもシェルコマンドで行うことができる。

LAN内の接続状況の確認(おまけ)

 最後におまけとしてLANに接続されている機器のアドレスと名前(Windows限定だが)を取得するプログラムを紹介する。原理的にはpingプロトコルの応答をチェックして、応答ありの場合には名前の取得を試みるという処理である。

pingテスト

String hostName = Dns.GetHostName();
IPAddress[] adrs = Dns.GetHostAddresses(hostName);
IPAddress hostAddress = adrs[0];
for(int i = 0; i < adrs.Length; i++)
  if(!adrs[i].ToString().StartsWith("127")) hostAddress = adrs[i];
String[] ip = hostAddress.ToString().Split(new char[] { '.' });
String netAddress = String.Format("{0}.{1}.{2}.", ip[0], ip[1], ip[2]);
Ping ping = new Ping();
String query = "";
for(int ip = 0; ip < 256; ip++)
{
  String adrs = netAddress + ip.ToString();
  PingReply pr = ping.Send(adrs, 100);
  if(pr.Status == IPStatus.Success)
  {
    addLog(pr.Address);
    String name = "";
    try
    {
      name = (Dns.GetHostEntry(pr.Address)).HostName;
      if(name == adrs)
        name = (Dns.GetHostByAddress(pr.Address)).HostName;
    }
    catch(Exception ex)
    {
      addLog(ex);
      if(ex is ThreadAbortException) return;
      name = adrs;
    }
    if(name == "")
      name = adrs;
    query += String.Format("{0}={1} ", adrs, name);
  }
}

 穴が開いたら覗けばいいし、手を伸ばしてもいい。簡単なことである。

穴から手を伸ばして答えを得る!

WebRequest request = WebRequest.Create("http://xxx.yyy.zzz/xyz.cgi");
try
{
  WebResponse response = request.GetResponse();
  StreamReader reader = new StreamReader(response.GetResponseStream());
  String answer = reader.ReadToEnd();
  reader.Close();
  response.Close();
}
catch(Exception ex)
{
  if(ex is ThreadAbortException) return;
}

掲示板

Enter your comment
 
 
programming/network/穴を開ける方法.txt · 最終更新: 2009/08/09 10:57 by daisukeh
 

Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS
Driven by DokuWiki Powered by Google do yourself a favour and use a real browser - get firefox ! GIMP is the GNU Image Manipulation Program. Adobe Flex smarty : Template Engine