読み方 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関数まで
- a.zipを解凍して下さい.普通のzipファイルです d=(^o^)=b
- a.exeは実行しても何も表示してくれないので,a.exeをOllyDbgで開きます.
- メモリウィンドウを開いてみてみると,UPXでパックされていますね!なのでアンパックしましょう(今回は手動で).
- プログラムの実行(F9)か,ステップオーバー(F8)を数回かカチカチしてntdllを抜けると,a.exeの中身です.いきなりPUSHADが見つかるので,メモリにブレークポイントを仕掛けます。(左下のカラムのHex dumpと書いてある所で,Ctrl+gを押し,出てきたダイアログにespと入力しokを選択すると,スタックと同じアドレスの位置に移動してくれるので,任意の場所を右クリックし,BreakpointメニューのHardware, on accessのByteを選択します.このスクリーンショットでは灰色っぽくなっているところに仕掛けてあります.)
- おもむろにプログラムを実行させます(F9).するとPOPADとJMPがある良い感じのところでブレークポイントが引っかかりますね!うまいことアンパックされているはずです.(途中で止まってしまう場合も,スクリーンショットの場所にたどり着くまで何回か試してみて下さい!)
- 次回から楽できるようにJMP命令の位置にプレークポイントを仕掛けときましょう.JMP命令のある行を選択し,F2を押します(右クリック,BreakpointにあるToggleです).赤くなればうまくいってます.
- マニュアルアンパックっぽいサムシングも終わったので,JMP命令をステップオーバーで進めます.すると本来の中身が展開されているはずです.(あってるのか・・・?)
- 段々読みにくくなってきました.そこで,右クリック,AppearanceのHighlightingにあるJumps'n'callsを選択します.カラフルになってコードが追いやすくなると思います.
- 次は,main関数を探します.mainにはargc,argv,envp(環境変数)の3つの引数が渡されるので,コマンドラインに関連するっぽい部分とPUSH 3つを探してみます.その付近のcallがmainの呼び出しのはずなので,ステップイン(F7)します.
- では,mainの中身を読み進めましょう!
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 を見ればすぐ分かると思います.
- とりあえず,特定の日付の時だけに飛ぶルーチンにいつでも飛べるように,機械語を書き換えてしまいましょう.GetLocalTimeの下,MOVZXの行を選択し,アセンブル(スペースキー)します(書き換えるアドレスは個々で違うと思うので,適宜読み替えて下さい.赤い四角で囲んだところがキーです).
- JMP命令をステップオーバーします.
- すると,戻り先のアドレスが2Byteずれたことによって,MOV EAX,E0FF5842 が POP EAX // JMP EAX に変わります.もう終わりが近いですね,JMP命令をステップオーバーします.
- 残りは,VirtualAllocが確保しているアドレスを監視,展開された内容をダンプし,utf-8の文字列として読み込むだけです!
おわりに
という訳で簡単ながらも遊んでみました.これからは,複雑なプログラムの内容も読み解けるように努力していきたいです!
おまけに,このプログラムを作るのに使ったソースコードを載せておきますね!
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; }
では!
DAizu #1 に参加してきた。
会津大で行われたD言語勉強会に参加してきました!
実際D言語の他に、Dの付く言語であれば今日はOKとのことだったので、Dartも布教してきました。
未完成のスライドで挑んでしまったので、次回は完成させてリベンジしたい。
感想としては、実際にD言語についてのお話を生で聞けてとても勉強になりました。
初回ということで人数もそれほど多くなかったため個人個人が質問しやすかったこともあり、気になる点をバンバン聞くことが出来たのも良かったです。
発表者の方々、ご丁寧に答えて下さってありがとうございました。
具体的には、「簡単なD言語の紹介」の後に「ctpg - Compile Time Parser Generator」といった発展系の発表があり、D言語の文法や機能を実例とともに学ぶことができました。
また、C APIをD言語用にバインドした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の設定を忘れずに~)
(追記)
これもっとDartらしいコードにして欲しいですね > yutopp.hateblo.jp/entry/2012/03/…
— Mr. Fiberさん (@repeatedly) 3月 23, 2012
こうですか、分かりません><
#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. 一番下のInstructions、1.Non-cygwinのリンクのzipを落とし展開。
2. cmd.exeから、depot_toolsに含まれているgclientを一度実行。
3. depot_toolsへのパスを通しておく。
後は、 http://code.google.com/p/dart/wiki/GettingTheSource のGetting 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#