yutopp's blog

サンドバッグになりたい

読み方 of "セキュリティキャンプ2012に参加しました"

一つ前の記事で,セキュリティキャンプ2012についての感想文をexeに詰めて置いておいたのですが,そのままにしておくのも悲しいので,ザックリと解き方を書いておこうと思います.
未だに不慣れなので,変な箇所がありましたら是非教えて下さい><

先に,出てくる文章を貼っておきますね。

セキュリティキャンプ2012 ソフトウェア・セキュリティ・クラスに参加してきました!
とってもとっても楽しく、刺激的なキャンプでした。

応募用紙は、締め切りの日の午前1時過ぎからtwitterで"#moudamedaJP"などと呟きながら、朝までずっと書いてました。
応募用紙締め切りの前日まで東京にいたりと色々あり (来年応募しよう...) などと勝手に思っていたのですが、いざ考えてみると高校の時に一度応募して落ちてしまったことなどを思い出して、
やっぱり今年行きたい!とテンションが上がってきたのでウオオオオオと書いてました。
内容はいろいろ書いた気がします。一人では気付けないことも沢山あるので、沢山の知識を持った凄い方々が集まるセキュキャンで、脆弱性やバイナリについて学びたかったのでございました。
深夜の勢いで書いたので色々アレでしたが、応募して良かったです。

そして、受かった後はkadaiがでます。kadai。
私はkadaiの解答がほんとmoudamedaだったので、もっと頑張ろうと思いました...

さて、スケジュールはこんな感じでした!
1日目:開校式や共通講義。サイバー犯罪についてなどの特別講義がありました。オリエンテーションやグループワークなども。
2日目:ついにクラス別講義です。解析ツールの使い方などを学び、アセンブラ読経やマルウェア解析を行いました。
3日目:マルウェア解析についての発表。脆弱性あふれるプログラムの修正と、各々で修正されたプログラムを攻撃し合いました。夜はヒューリスティック検知エンジンについて学び、その改良を行いました。その後、BoFとチューターの方の発表がありました。
4日目:ヒューリスティック検知エンジンについての続き、発表。その後CTFです!
5日目:グループワークや各クラスの発表、CTFの模範解答の発表。閉校式。本の争奪戦。

簡単にまとめても重量感ありますね・・・。それだけ内容が濃かったです。
講義は本当にどれも楽しく、ためになりました。これを礎にこの先、頑張りたいです。
また、普段お会いすることの出来ないような方々からお話を伺うことが出来てとても幸せでした!うおお。
twitterで交流があった方とも実際にお会いできて楽しかったです!

問題のCTFではあまり役に立てませんでした・・・。いや、とても楽しかったですけどね!
課題の問題が解けなかった悔しさがあったので、ひたすらソフトウェアの分野の問題を解いていました。5問しか解けなかったので悔しいでござる・・・。

セキュリティキャンプの良いと思ったところは、自分のクラスの分野の知識の足りない所だけでなく、他のクラスの方々からも自分の知らない知識について沢山学ぶ事ができるところですね!
全てのクラスの教材が頂ける上に、CTFでは全ての分野の問題が出題されるので、他のクラスの方から問題の解説を受けるとoh...となりました。調べることが沢山増えましたね!

頂けた本の中で一番嬉しかったのは、"12ステップで作る 組込みOS自作入門"です!さっそく読みます。

要するに内容も濃いし楽しいし最高でした!モチベーションもめちゃくちゃ高まりました。知識の無さも痛感しました...
俺俺ライブラリもどんどん開発したいですね。あとはアウトプットを増やしたいです。

余談:
朝起きるのがめちゃくちゃ辛かったです。あとは これはPyhtonという言語で、アパ水とスリッパ。

うああ,読み返すと普通に恥ずかしいですね!
まあ,ではではいってみましょー

読み方

お手元にOllyDbgとダウンロードしてきたa.zipをご用意下さい.
ちなみに,Windows 7(x64)にて,OllyDbg 1.10を使っています.
では,

main関数まで

  1. a.zipを解凍して下さい.普通のzipファイルです d=(^o^)=b
  2. a.exeは実行しても何も表示してくれないので,a.exeをOllyDbgで開きます.
  3. メモリウィンドウを開いてみてみると,UPXでパックされていますね!なのでアンパックしましょう(今回は手動で).f:id:yutopp:20120906140343p:plain
  4. プログラムの実行(F9)か,ステップオーバー(F8)を数回かカチカチしてntdllを抜けると,a.exeの中身です.いきなりPUSHADが見つかるので,メモリにブレークポイントを仕掛けます。(左下のカラムのHex dumpと書いてある所で,Ctrl+gを押し,出てきたダイアログにespと入力しokを選択すると,スタックと同じアドレスの位置に移動してくれるので,任意の場所を右クリックし,BreakpointメニューのHardware, on accessのByteを選択します.このスクリーンショットでは灰色っぽくなっているところに仕掛けてあります.)f:id:yutopp:20120906140356p:plainf:id:yutopp:20120906140400p:plain
  5. おもむろにプログラムを実行させます(F9).するとPOPADとJMPがある良い感じのところでブレークポイントが引っかかりますね!うまいことアンパックされているはずです.(途中で止まってしまう場合も,スクリーンショットの場所にたどり着くまで何回か試してみて下さい!)f:id:yutopp:20120906140434p:plain
  6. 次回から楽できるようにJMP命令の位置にプレークポイントを仕掛けときましょう.JMP命令のある行を選択し,F2を押します(右クリック,BreakpointにあるToggleです).赤くなればうまくいってます.f:id:yutopp:20120906134332p:plain
  7. マニュアルアンパックっぽいサムシングも終わったので,JMP命令をステップオーバーで進めます.すると本来の中身が展開されているはずです.(あってるのか・・・?)f:id:yutopp:20120906140657p:plain
  8. 段々読みにくくなってきました.そこで,右クリック,AppearanceのHighlightingにあるJumps'n'callsを選択します.カラフルになってコードが追いやすくなると思います.
  9. 次は,main関数を探します.mainにはargc,argv,envp(環境変数)の3つの引数が渡されるので,コマンドラインに関連するっぽい部分とPUSH 3つを探してみます.その付近のcallがmainの呼び出しのはずなので,ステップイン(F7)します.f:id:yutopp:20120906140914p:plain
  10. では,mainの中身を読み進めましょう!f:id:yutopp:20120906140743p:plain

mainから最後まで

mainの中身のネタばらしをすると,GetLocalTimeで特定の日付の時だけ特別な動きをするようになってることと,各ルーチンで謎の演算をしていること,callに対してPOP EAXとJMP EAXで戻っていること,それっぽい文字列の"seccamp 2012!!!"付近のコードはダミーだということくらいです d=(^o^)=b
上のスクリーンショットの例だと,003D1C44から003D1C59の間の小細工と,GetLocalTimeの分岐に続くPOP EAX // ADD EAX, 2 // JMP EAX がキモです.
003D1C4F E8 0A000000 CALL a.003D1C5E の下にある,003D1C54 B8 4258FFE0 MOV EAX,E0FF5842 を見ればすぐ分かると思います.

  1. とりあえず,特定の日付の時だけに飛ぶルーチンにいつでも飛べるように,機械語を書き換えてしまいましょう.GetLocalTimeの下,MOVZXの行を選択し,アセンブル(スペースキー)します(書き換えるアドレスは個々で違うと思うので,適宜読み替えて下さい.赤い四角で囲んだところがキーです).f:id:yutopp:20120906143808p:plain
  2. JMP命令をステップオーバーします.
  3. すると,戻り先のアドレスが2Byteずれたことによって,MOV EAX,E0FF5842 が POP EAX // JMP EAX に変わります.もう終わりが近いですね,JMP命令をステップオーバーします.f:id:yutopp:20120906150045p:plain
  4. 残りは,VirtualAllocが確保しているアドレスを監視,展開された内容をダンプし,utf-8の文字列として読み込むだけです!f:id:yutopp:20120906150902p:plain

おわりに

という訳で簡単ながらも遊んでみました.これからは,複雑なプログラムの内容も読み解けるように努力していきたいです!
おまけに,このプログラムを作るのに使ったソースコードを載せておきますね!
VC10でコンパイル出来ます.

encode.cpp

#include <fstream>
#include <iterator>
#include <algorithm>
#include <vector>
#include <numeric>

int main()
{
	std::ifstream ifs( "src.txt", std::ios::binary );
	if ( !ifs ) {
		return -1;
	}
	std::istreambuf_iterator<char> begin = ifs, end;

	std::vector<char> bin;
	std::copy( begin, end, std::back_inserter( bin ) );

	std::for_each( bin.begin(), bin.end(), []( char& c ) { c = c ^ 0xcc; } );

	std::ofstream ofs( "encoded.csv", std::ios::binary );
	ofs << std::hex;
	std::for_each( bin.cbegin(), bin.cend(), [&ofs]( char const& c ) mutable {
		ofs << "0x" << static_cast<int>( c ) << ", ";
	} );
	ofs << "0x0";
}

a.cpp

#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator>
#include <cstdint>

#include <Windows.h>

unsigned char const src[] = {
	#include "encoded.csv"
};

void a() { std::cout << "a!!" << std::endl; }
void b() { std::cout << "b!!" << std::endl; }
void c() { std::cout << "c!!" << std::endl; }
void d() { std::cout << "d!!" << std::endl; }
void e() { std::cout << "e!!" << std::endl; }
void f() { std::cout << "f!!" << std::endl; }
void g() { std::cout << "g!!" << std::endl; }
void h() { std::cout << "h!!" << std::endl; }
void i() { std::cout << "i!!" << std::endl; }
void j() { std::cout << "j!!" << std::endl; }

int key = 53, sign = 0;

int main()
{
	__asm {
		push ok;
		call f_hoge;
		_emit 0xB8;	// mov eax
		_emit 0x42;     // d=(^o^)=b
		pop eax;
		jmp eax;
	}

	goto l_last;

f_hoge:
	SYSTEMTIME st;
	::GetLocalTime( &st );

	if ( st.wYear == 2012 && st.wMonth == 8 && st.wDay == 18 ) {
		goto f_hoge_ok;
	}

//f_hoge_ng:
	__asm {
		mov eax, 0xdeadbeef;
		shr eax, 4;
		mov key, eax;
	}

	__asm {
		pop eax;
		jmp eax;
	}

f_hoge_ok:
	__asm {
		mov eax, 100;
		shr eax, 2;
		add eax, eax;
		lea eax, [eax + eax * 2];
		add key, eax;
		inc key;
	}

	__asm {
		pop eax;
		add eax, 2;
		jmp eax;
	}

	
//dummy:
	goto f_hoge;
	std::cout << "seccamp 2012!!!" << std::endl;

	int num = 0;
	std::cin >> num;
	if ( num == 1 )
		a();
	else if ( num == 1 )
		b();
	else if ( num == 2 )
		c();
	else if ( num == 3 )
		d();
	else if ( num == 4 )
		e();
	else if ( num == 5 )
		f();
	else if ( num == 6 )
		g();
	else if ( num == 7 )
		h();
	else if ( num == 8 )
		i();
	else if ( num == 9 )
		j();

	{
		std::vector<int> v;
		for( int i=0; i<0xdeadbeef; ++i ) {
			v.push_back( i );
		}

		std::copy( v.cbegin(), v.cend(), std::ostream_iterator<char>( std::cout, ", " ) );
	}

	{
		struct h
		{
			int fact(int n)
			{
				return n == 0 ? 1 : n * fact( n - 1);
			}
		};

		h i;
		i.fact( 500 );
	}

ok:
	char *const p = reinterpret_cast<char *>( ::VirtualAlloc( nullptr, sizeof( src ), MEM_COMMIT, PAGE_READWRITE ) );
	if ( !p ) {
		return -1;
	}

	::Sleep( 500 );

	for( std::size_t i=0; i<sizeof( src ); ++i ) {
		p[i] = src[i] ^ key;
	}

	std::fill( p, p + sizeof( src ), 0 );
	::VirtualFree( p, sizeof( src ), MEM_DECOMMIT );

	__asm mov eax, 0;

l_last:
	int i;
	__asm {
		mov i, eax
	}

	return i;
}

では!

セキュリティキャンプ2012に参加しました

参加しました!ソフトウェアセキュリティ組です。
とにかく楽しかったです!うおお。

なんと言いますか、感想を実行形式に突っ込んでみました!今回のキャンプで学んだ事を詰め込んだので、マルウェア解析っぽい気分で解析してみて下さい。あ、マルウェアじゃないですよ!
CTFの問題を参考にしているので、某問題を解けた方には簡単だと思います。
加えて、時間を割いても出てくるのは私の感想文だけです!!!

a.zip

それでは! d=(^o^)=b

少しですが、写真も載せておきますね!アパホテル最高でした。
f:id:yutopp:20120818154616j:plain
f:id:yutopp:20120815063456j:plain

DAizu #1 に参加してきた。

f:id:yutopp:20120430185737j:plain:w340
DAizu #1 - [PARTAKE]

会津大で行われたD言語勉強会に参加してきました!
実際D言語の他に、Dの付く言語であれば今日はOKとのことだったので、Dartも布教してきました。
未完成のスライドで挑んでしまったので、次回は完成させてリベンジしたい。

感想としては、実際にD言語についてのお話を生で聞けてとても勉強になりました。
初回ということで人数もそれほど多くなかったため個人個人が質問しやすかったこともあり、気になる点をバンバン聞くことが出来たのも良かったです。
発表者の方々、ご丁寧に答えて下さってありがとうございました。

具体的には、「簡単なD言語の紹介」の後に「ctpg - Compile Time Parser Generator」といった発展系の発表があり、D言語の文法や機能を実例とともに学ぶことができました。
また、C APID言語用にバインドしたDeimos(ダイモス)ライブラリの発表もあり、「こんなのもあるんだ!」と驚きでした。DartのAPIも移植して下さい。
D言語erに「これはC++でも・・・出来る?」といった質問をされるといったいじめを受けたことも報告しておきますね。

さて、D言語はコンパイル時に何でも出来る言語といった認識があったため、D言語の勉強会であるDAizuも開始と同時にスライドを渡されて終了するのではないかと心配していましたが、そんなことは無く安心しました。
そして、次回もDAizuやることになりました!そのうちPARTAKEにヒョッと立つと思います。

D言語・Dの付く言語の "D" という意味だけでなく、"Developer" といった意味も含めて色々な内容にしても良いね、みたいな話もしてきました。
皆さんもぜひぜひ次回は参加してみてはいかがでしょうか!

DartVM (rev5804) に渡せるオプション

rev5804時点のDartVMに渡せるオプションを纏めてみました。
違う点がありましたら、ぜひ教えて下さい><

オプション名 デフォルト値 コメント
deoptimization_counter_threshold int 5 "How many times we allow deoptimization before we disallow certain optimizations"
disassemble bool false "Disassemble dart code."
code_heap_size int Heap::kCodeHeapSizeInMB "code heap size in MB, e.g: --code_heap_size=8 allocates a 8MB old gen heap"
compiler_stats bool false "Compiler stat counters."
disable_privacy bool false "Disable library privacy."
disassemble_stubs bool false "Disassemble generated stubs."
enable_asserts bool false "Enable assert statements."
enable_checked_mode bool false "Enabled checked mode." (注釈: このオプションは、--enable_assertsと--enable_type_checksを同時に設定するのと同じ意味。)
enable_type_checks bool false "Enable type checks."
gc_at_alloc bool false "GC at every allocation."
generate_gdb_symbols bool false "Generate symbols of generated dart functions for debugging with GDB"
ignore_unrecognized_flags bool false "Ignore unrecognized flags."
inline_alloc bool true "Inline allocation of objects."
inline_cache bool true "enable inline caches"
intrinsify bool true "Instrinsify when possible"
new_gen_heap_size int 32 "new gen heap size in MB, e.g: --new_gen_heap_size=64 allocates a 64MB new gen heap"
old_gen_heap_size int Heap::kHeapSizeInMB "old gen heap size in MB, e.g: --old_gen_heap_size=1024 allocates a 1024MB old gen heap"
optimization_counter_threshold int 2000 "function's usage-counter value before it is optimized, -1 means never."
print_ast bool false "Print abstract syntax tree."
print_bootstrap bool false "Print the bootstrap source."
print_classes bool false "Prints details about loaded classes."
print_flags bool false "Print flags as they are being parsed."
print_flow_graph bool false "Print the IR flow graph."
print_ic_in_optimized
(TARGET_ARCH_IA32が定義されているときのみ)
bool false "Debugging helper to identify potential performance pitfalls."
print_scopes bool false "Print scopes of local variables."
print_stack_trace_at_throw bool false "Prints a stack trace everytime a throw occurs."
print_stop_message bool true "Print stop message."
print_tokens bool false "Print scanned tokens."
report_usage_count bool false "Track function usage and report."
silent_warnings bool false "Silence warnings."
time_all bool false "Time all functionality"
trace_bailout bool false "Print bailout from new compiler."
trace_class_finalization bool false "Trace class finalization."
trace_compiler bool false "Trace compiler operations."
trace_deopt bool false "Trace deoptimization"
trace_functions bool false "Trace entry of each function."
trace_ic bool false "trace IC handling"
trace_intrinsified_natives bool false "Report if any of the intrinsified natives are called"
trace_isolates bool false "Trace isolate creation and shut down."
trace_natives bool false "Trace invocation of natives"
trace_optimization
(TARGET_ARCH_IA32が定義されているときのみ)
bool false "Trace optimizations."
trace_parser bool false "Trace parser operations."
trace_patching bool false "Trace patching of code."
trace_resolving bool false "Trace resolving."
trace_runtime_calls bool false "Trace runtime calls."
trace_type_checks bool false "Trace runtime type checks."
trace_type_finalization bool false "Trace type finalization."
use_new_compiler bool TARGET_ARCH_X64が定義されているときtrue。そうでないときにfalse "Try to use the new compiler backend."
use_slow_path bool false "Set to true for debugging & verifying the slow paths."
verbose_gc bool false "Enables verbose GC."
verify_after_gc bool false "Enables heap verification after GC."
verify_before_gc bool false "Enables heap verification before GC."
verify_implements bool false "Verify that all classes implement their interface."
verify_on_transition bool false "Verify on dart <==> VM."
warning_as_error bool false "Treat warnings as errors."
worker_timeout_millis int 5000 "Free workers when they have been idle for this amount of time."


おまけ:テスト用オプション(テスト順)

オプション名 デフォルト値 コメント
basic_flag bool true "Testing of a basic boolean flag."
parse_flag_bool_test bool true "Flags::Parse (bool) testing"
string_opt_test charp NULL "Testing: string option."
entrypoint_test charp "main" "Testing: entrypoint"
counter int 100 "Testing: int flag"

いかにしておっぱい画像をダウンロードするか~2012 Dart編

(こんなこと書いてブログのカテゴリ変わったりしないよね?ね?)

元凶

いかにしておっぱい画像をダウンロードするか〜2012 - ゆーすけべー日記
いかにしておっぱい画像をダウンロードするか〜2012 Haskell編 - 厨二病患者のプログラミング入門
D言語でいかにしておっぱい画像をダウンロードするか〜2012 — Gist
いかにしておっぱい画像をダウンロードするか〜2012 をC#で書きました - モデラート - C#とゲーム開発と雑記

そんなわけで

最近流行り(ステマ)のDartで書けないかなーと試してみました!背徳感がすごいですね!
所々エラー処理は省きました。
(DartEditor build5759、Server applicationで動かせます。dataフォルダの作成と、Bing APIのAppIdの設定を忘れずに~)

(追記)

こうですか、分かりません><

#import("dart:io");
#import("dart:uri");
#import("dart:utf",prefix:"utf");
#import("dart:json");

class Url {
  static String encodeUtf8(final String src) => _encode(src, (s)=>utf.encodeUtf8(s));

  static String _encode(final String rawSrc, List f(final String)) =>
    new String.fromCharCodes((final List src, List dst) {
          src.forEach(
            (c) =>
              dst.addAll(
                ((c >= 65/*'A'*/ && c <= 90/*'Z'*/) || (c >= 97/*'a'*/ && c <= 122/*'z'*/) || (c >= 48/*'0'*/ && c <= 57/*'9'*/) || c == 45/*'-'*/ || c == 95/*'_'*/ || c == 46/*'.'*/ || c == 126/*'~'*/)
                ? [c]
                : [37/*%*/, c.toRadixString(16).charCodes()[0], c.toRadixString(16).charCodes()[1]]
                )
              );
          return dst;
        }(f(rawSrc), new List())
      );
}

void main() {
  final String baseDir = "data";
  final String AppId = "~ここにAppIdを入力~";
  final int perDownloadNum = 10;
  final int maxDownloadNum = 50;
  final String word = "おっぱい";

  int downloadedCount = 0;
  for (int i=0; i<maxDownloadNum/perDownloadNum; ++i) {
    final String queryString = (Map<String, String> q) {
      StringBuffer sb = new StringBuffer();
      q.forEach((k,v) => sb.add("${k}=${v}&"));
      return sb.toString().substring(0, sb.length - 1);
    }({
      "AppId" : AppId,
      "Version" : "2.2",
      "Markert" : "ja-JP",
      "Sources" : "Image",
      "Image.Count" : "${Math.min(maxDownloadNum-i*perDownloadNum, perDownloadNum)}",
      "Image.Offset" : "${i*perDownloadNum}",
      "Adult" : "off",
      "Query" : Url.encodeUtf8( word )
    });
    
    final client = new HttpClient().getUrl(new Uri(scheme: "http", domain: "api.bing.net", path: "/json.aspx", query: queryString));
    client.onResponse = (HttpClientResponse res) {
      StringBuffer resStringBuf = new StringBuffer();
      
      res.inputStream.onData = () =>
        resStringBuf.add(new String.fromCharCodes(res.inputStream.read()));
        
      res.inputStream.onClosed = () =>
        JSON.parse(resStringBuf.toString())["SearchResponse"]["Image"]["Results"].forEach((v) =>
          ((Uri path) =>
            new HttpClient().getUrl(path).onResponse = (HttpClientResponse r) {
              try {
                final ext = const RegExp(@"\w+$").firstMatch(r.headers["content-type"])[0];
                if (ext == "jpeg" || ext == "png" || ext == "gif") {
                  final String filename = const RegExp(@".+/(.+?)\..+$").firstMatch(path.toString())[1];
                  final File file = new File("$baseDir/$filename.$ext");
                  final fileStream = file.openOutputStream();
                  r.inputStream.pipe(fileStream);
                  r.inputStream.onClosed = () => file.create(() => fileStream.close());
                  
                  print("${++downloadedCount}. ダウンロード! : $baseDir/$filename.$ext");
                }
              } catch(NullPointerException e) {
                print("error.");
              }
            }
          )(new Uri.fromString(v["MediaUrl"]))
        );
    };
  }
}

いい感じですね!さぁ、Dart使いましょう!!


(昔の)

#import("dart:io");
#import("dart:uri");
#import("dart:utf");
#import("dart:json");

class Url {
  static String encode( final String src )
  {
    List ls = <int>[];
    for( final int c in encodeUtf8( src ) ) {
      if ( ( c >= 65/*'A'*/ && c <= 90/*'Z'*/ ) || ( c >= 97/*'a'*/ && c <= 122/*'z'*/ ) ||
          ( c >= 48/*'0'*/ && c <= 57/*'9'*/ ) ||
            c == 45/*'-'*/ || c == 95/*'_'*/ || c == 46/*'.'*/ || c == 126/*'~'*/ )
      {
        ls.add( c );
      } else {
        ls.add( 37/*%*/ );
        ls.addAll( c.toRadixString(16).charCodes() );
      }
    }

    return new String.fromCharCodes( ls );
  }
}

void main() {
  final String baseDir = "data";
  final int perDownloadNum = 10;
  final int maxDownloadNum = 50;
  final String word = "おっぱい";
  
  int downloadedCount = 0;
  for ( int i=0; i<maxDownloadNum/perDownloadNum; ++i ) {
    final Map<String, String> query = {
      "AppId" : "~ここにAppIdを入力~",
      "Version" : "2.2",
      "Markert" : "ja-JP",
      "Sources" : "Image",
      "Image.Count" : "${Math.min(maxDownloadNum-i*perDownloadNum, perDownloadNum)}",
      "Image.Offset" : "${i*perDownloadNum}",
      "Adult" : "off",
      "Query" : Url.encode( word )
    };
    
    StringBuffer sb = new StringBuffer();
    query.forEach( (k,v) => sb.add("${k}=${v}&") );
    final String queryString = sb.toString().substring( 0, sb.length - 1 );
    
    final String uri = "http://api.bing.net/json.aspx?${queryString}";    
    final client = new HttpClient().getUrl( new Uri.fromString( uri ) );
    client.onResponse = ( HttpClientResponse res ) {
        StringBuffer resStringBuf = new StringBuffer();
        res.inputStream.onData = () {
          resStringBuf.add( new String.fromCharCodes( res.inputStream.read() ) );
        };
        
        res.inputStream.onClosed = () {       
          final resJson = JSON.parse( resStringBuf.toString() );
          
          for( final Map v in resJson["SearchResponse"]["Image"]["Results"] ) {
            final Uri imageUri = new Uri.fromString( v["MediaUrl"] );
            final picClient = new HttpClient().getUrl( imageUri );
            picClient.onResponse = ( HttpClientResponse r ) {
              try {
                ++downloadedCount;
                final String ext = r.headers["content-type"].replaceAll( const RegExp(@"(\w+)/"), "");
                  
                if ( ext == "jpeg" || ext == "png" || ext == "gif" ) {
                  final String filename = imageUri.path.replaceAll( const RegExp(@"(\w+/)"), "" ).replaceAll( const RegExp(@"/"), "" ).replaceAll( const RegExp(@"(\.\w+)"), "" );
                  print( "$downloadedCount : ダウンロード! : ${baseDir}/${filename}.${ext}" );

                  final File file = new File( "${baseDir}/${filename}.${ext}" );
                  final fileO = file.openOutputStream();
                  r.inputStream.pipe( fileO );
                  r.inputStream.onClosed = () => file.create( () => fileO.close() );
                }
                
              } catch( NullPointerException e ) {
                print( "error." );
                
              }
            };
            
          }
        };
        
      };  
  }
}

Dartのstandalone VMを落としてくる方法

windows 7でのメモ。

まずはgclientを使えるようにする。
Install the depot_tools - The Chromium Projects
1. 一番下のInstructions1.Non-cygwinのリンクのzipを落とし展開。
2. cmd.exeから、depot_toolsに含まれているgclientを一度実行。
3. depot_toolsへのパスを通しておく。

後は、 http://code.google.com/p/dart/wiki/GettingTheSourceGetting the standalone VMの通りにgclientを実行するだけ。
/runtime 下にVS2008用のプロジェクトファイルが出来ている。・・・はず。

型パラメータのインスタンスの生成

Dart 0.07 では型パラメータからインスタンスを生成することは出来ません。(型変数に対してnew・constは使うことが出来ない。)

class hoge<T>
{
  T create() => new T();  // new T()は出来ない。
}

class foo
{
  foo() { print("foo!!"); }
}

main() {
  final a = new hoge<foo>();
  a.create();
}

その対処策としてこのようなものが紹介されていました。

class hoge<T>
{
  hoge( this._creator );

  T create() => _creator();
  final _creator;
}

class foo
{
  foo() { print("foo!!"); }
}

main() {
  final a = new hoge<foo>( () => new foo() );
  a.create();
}

https://groups.google.com/a/dartlang.org/group/misc/browse_thread/thread/1c9d23fcc8480be2#