あしのあしあと

2017-10-21

もろもろ

| 20:53 | もろもろを含むブックマーク

参加

SANS Tokyo Autumn 2017 に参加してきた。

完全に、清水の舞台から飛び降りた。終盤は、ついていけなくって、失禁しそうだった。

退会

早くも、セキュ塾ホワイトハッカー育成コースを退会した。さすがに、手が回らなくなった。

選挙

初めて期日前に投票した。すごく混んでいた。少なくとも、投票数の下一桁は、変えられた。

2017-10-01

2017 OWASP World Tour Tokyo

| 22:31 |  2017 OWASP World Tour Tokyoを含むブックマーク

久しぶりの Web。周回遅れになっているので、情報収集のために参加した。思っていたのとは、ちょっと違った*1が、参加してよかった。充実した 1 日になった。

後日、参照できるよう、最低限のメモを残しておく。


Opening "OWASP Project Overview for Developers"

紹介していただいたプロジェクトは、いずれも FLAGSHIP であったが、ほとんどが知らないプロジェクトだった。

https://speakerdeck.com/owaspjapan/owt2017jp-owasp-project-overview-for-developers


Training 1 "OWASP TOP 10 を用いた脆弱性対応"

恥ずかしいことに、「Proactive Controls」すら知らなかった。話の流れからして、「TOP 10」とのマッピングは、誰もやっていなかったのだろう。

印象に残ったのは、「ソースコード静的解析では、アクセス制御、認可制御など、機能仕様に関係する脆弱性は検出できない」旨。これは、以前から言われていることであるし、静的解析ツールの原理を考えても、できなさそうだ。だが、今回は説得力があった。資料 35 ページ目の右下にしれっと書かれているのだが、静的解析ツールが「コベリティ」だったからだ(最近では、オージス総研さんのソリューションにも加わっている)。一流の開発者であっても、似たような悩みをかかえているのかも。

https://speakerdeck.com/owaspjapan/owt2017jp-owasp-top10


Training 2 "最小権限の具体的な実現方法"

「具体的な実現方法」についてのプレゼンを初めて聞いた。「最小権限は、セキュリティの原理原則である」という一方で、「最小権限の設計や実装は、業務要件次第」と言って、具体的なやり方を一切語らない。そんなコンサルタントなら、自分にだってできる。このセッションは、そこを一歩踏み出し、「Web アプリケーションにおける最小権限の設計のガイドライン」となっている。

前のセッションでも話があったように、権限設計や、アクセス制御の問題は、業務要件次第であるため、コレといった決定的な解決策を提示しにくい。

悪いことに、アクセス制御の設計を、画面中心で行っている案件は少なくないだろう。それは、画面設計書(あるいは、UI 部分のみを実装したモック)が、プロダクトオーナーとの最良のコミュニケーションツールだからではないか(特に、業務要件を整理するときは、確かに便利)。最も頻繁に更新され、最も信頼できるドキュメントなどになるため、どうしても画面が中心になってしまうのではないかと。こういう人たちに、「ローカルプロキシ」を使ってもらいたい。一発で目が覚めるから。

セッションでも、画面はただの UI で、リスクアセスメントと同様、情報やデータを起点に設計してほしい旨が述べられていた。その通りだと思う。

こんな状態で、ペネトレーションテストを実施すると、やはり、アクセス制御の問題が挙がってくるようだ。一般には、危険度が高いと評価されるため、脆弱なアプリケーションだとお墨付きをいただける。これは恥ずかしい。

残念ながら、アクセス制御の問題は、後付けが困難である。全ての処理をインターセプトし、権限表に従って、権限の有無をチェックするようにしなければならないからだ。そんな仕組みが全くないところに?

となると、プログラム中にハードコーディングする対策がとられたりして、収拾がつかなくなる。

こうであっても、「たまたま権限設計がシンプルだった」という奇跡に救われることもある。そうすると、同じ設計の仕方を横展開されてしまうのではないかと、夜も眠れなくなる。ウソ。ぐっすり。

と、いくらでも書けてしまうので、そろそろやめよう。

正直、自分の中では、インジェクション系は、どうでもいい。はっきり言って、インジェクション系の脆弱性は、作り込みにくい。SQLi とか、見たことない。たとえあったとしても、比較的簡単につぶせる(対、アクセス制御)。

https://speakerdeck.com/owaspjapan/owt2017jp-least-privilege


Training 3 "開発者・運用担当者に向けた、OWASP ZAP を用いた脆弱性診断手法"

開発者、受け入れ担当者、運用担当者などに向けた発表というところが、本当に素晴らしいと思う。何年も、モノをつくっている人たちにアプローチする姿勢には、頭が下がる。

自分も、開発をしている人に(保守・運用の要員までは考えていなかった)、どんな形でもよいから、広く 「ローカルプロキシ」 を使ってもらおうとしている。なぜか。HTTP のリクエストは、「いつでも何でも送れる」 ということをわかってほしいから。好きなタイミングで送信できるし、好きな内容で送信できるということを、体で理解してほしいから。そうなれば、変な設計しないでしょ。上記の最小権限とかアクセス制御とか、まじめに考えるでしょ。こと設計においては、ガイドや規約が、なかなか役に立たない。体に叩き込むのが早そうでしょ。

で、ローカルプロキシは、シンプルなツールだし、うったえる内容も 「いつでも何でも」 だけだし。簡単に浸透していくと思っていた。のだが、これが意外に進まない。

ローカルプロキシを、本当に使ってほしい人、例えば、設計のレビューする人だったり、テストの計画を作る人だったりするのだが、こういうエライ人たちは、構築した Web アプリケーションを実際に動かすことが、ほとんどなかったりする。なぜか。自分の経験的には、そういうエライ人は、だいたいまわっていない。てんてこ舞いしている。調整。

ドキュメント作っても、読んでもらえない。トレーニング立ち上げても、受講してもらえない。完全に、読みが甘かった。

もちろん、色々なチャネルでアプローチをしていて、一部の若手には、使ってもらい出している。草の根的な活動も大事だと思っているので、これはこれで続けていこうと思う。

とはいっても、やはり、前述した人たちにアプローチするのが手っ取り早いし、そうできないのがもどかしい。もう少し、頭を使いマス。

で、OWASP ZAP の話。OWASP ZAP のスキャナも、ゴリゴリ開発している人に使ってもらいたい。ということで、組織内に展開するために、OWASP ZAP を色々動かしていたときのこと。あるプラグインAlpha)を追加したら、ウイルス対策ソフトが吠えた。これが、変な跳ね方をして、しばらく、おとなしくすることになってしまった。これも、完全に想定外。

改めて、何年も、モノをつくっている人たちにアプローチする姿勢には、頭が下がる。

https://speakerdeck.com/owaspjapan/owt2017jp-owaspzap


Training 4 "OWASP BWA を用いた学生および職員向けトレーニング"

自分も、組織内で、Web アプリケーションセキュリティに関するトレーニングを立ち上げ、実施している。立ち上がったのは、10 年くらい前だが。

そういえば、トレーニングを立ち上げた話って、初めて聞いた。確かに、民間の人では、語りにくい。

他の仕事をかかえていて、2 週間程度で演習を作り上げたという話があったが、えっと、5 時間/日 × 14 日 = 70 時間。うん。とうていムリ。

自分の場合、企画した当初は、定時後にしか作業できなかったし、やられサイトも自作したということもあったが、たった 2 日間のコンテンツをつくるのに、半年近くかかった。満足いくクォリティになったが、やりすぎなのかもしれない。

XSS を 「分解して」 教えるところは、「なるほど」 と思った。自分のアプローチとは違ったが、前提知識を小出しにして積み上げていくという点は、一致していた。確かに、トレーニングを作成すると、こういうノウハウは蓄積する。ここらあたりの話は、心の中で、ウンウンと頷きながら聞いていた。

最後に、職員向けに、1.5 時間/週 でゆっくりやったという話だったが、コンテンツをどうやって分割したのか、受講者が前回の学習内容を覚えているものなのか、あたりも知りたかった。

https://speakerdeck.com/owaspjapan/owt2017jp-owasp-bwa


"開発プロジェクトの現状を把握する OWASP SAMM の活用"

TODO: 後で書く → 2017/10/10 ようやく書いた。途中までだが。

最後にキタ。

悲しいかな、「OWASP SAMM」を知らなかった。そんな自分には、最も有益な情報となった。

セッションの前半では、OWASP SAMM の骨子を、丁寧に解説いただいた。4 つのビジネス機能、ビジネス機能ごとに 3 つのセキュリティ対策、初めて聞く整理に驚いた。と同時に、コレは使えると思った(たとえ、教科書通りに使えないとしても)。また、各対策で活用できる OWASP の成果物が取り上げられていた。ほとんど知らない成果物であったため、たくさんの情報がいただけた。

セッションの後半では、具体的な活用事例が語られた。さすがに時間が短く(+ 大部分が社外秘の情報だろうから)、あまり細かいことはわからなかった。しかし、「アドバイス集の作成」や「タイムリーな情報提供」からスモールスタートするとよいなど、ノウハウも語られた。

すごく感じたのは、自分が所属している組織と比べて、「現場との連携が強い」ということ。一定のガバナンスが機能しているということ。

プロジェクトの開始から終了まで、一定の粒度で状況を把握することは、簡単にできることではない。プロジェクトの開始を確実にフックすることも大変だし、プロジェクトの途中経過を適宜知ることも、また大変*2

もとい。JPCERT/CC が、1.0 版を翻訳してくれているとのことで、さっそくダウンロードしてみた。

https://www.jpcert.or.jp/research/2010/SAMM_20100407.pdf

TODO: もう、ぐだぐだだ。もう少しだけ書きたい。

https://speakerdeck.com/owaspjapan/owt2017jp-owasp-samm


Closing "The shift left path"

SHIFT LEFT。言葉は違えど、昔から、そう言われていたような気がする。まだまだ実現できていない、ということなのだろう。

TODO: 後で書く

https://speakerdeck.com/owaspjapan/owt2017jp-the-shift-left-path


「被害者を産むようなアプリケーションを、システムを、世にリリースしてはならない」と思っている(だろう)人がたくさんいて、勝手に励まされた。


最後に。

今回、これだけのイベントを開催していただいたのだが、なんと参加費用は 0 円。無償だった。> 主催者のみなさま、講師のみなさま、ありがとうございました。

また、非常によい環境で、気持ちよく受講できた。> サテライト TOKYO-C 会場のスタッフのみなさま、ありがとうございました。

*1:「BOOTCAMP」 でも、「トレーニング」 でもないような気がした。

*2:こういう取り組みは開発現場の理解が不可欠だと思う。しかし、それを取りつけるのは、なかなかハードルが高い。トップダウンで降ろしてもらうには、まだ、体力が足りない。ボトムアップで、できるだけ現場の負担にならないように、「打ち合わせ時間を短くする」や「現場で対応しなければならない箇所を減らす」など、工夫をしてはいる。が、まだうまく機能はしていない(ボスのおかげで、少しずつは成果が出ている)。

2017-09-01

サイバーセキュリティモニタリング(マルウェア解析編)

| 05:55 |  サイバーセキュリティモニタリング(マルウェア解析編)を含むブックマーク

サイバーセキュリティモニタリング(ハニーポット編 2) - あしのあしあと」の続き。

久しぶりに 「サイバーセキュリティモニタリング」。何もしていなかったわけではないが、なかなか書けなかった。

仕事しんどいス。最近の Web アプリケーションに、ついていけてないス。


もとい。いよいよマルウェア解析(の入門)。表層解析と動的解析を飛ばして(いや、やってあるのだが、満足できていないだけ)、p.157 の設問【8】(唯一の静的解析の問題)に取り組んだ記録を残す。うち、アンパックについては、本編 5.4.4 の演習とほとんど変わらないので、作業だけであれば、ぜんぜん難しくない。


仮想環境で対象となる検体を実行してみると、パスワードの入力画面が起動する。入力したパスワードが正しいかどうかチェックして終了する。特に、ファイルやレジストリへのアクセスや、通信なども発生していない。これだけのプログラムに見える。このプログラムの正しいパスワードを見つけなさいという問題。CTF な雰囲気。全然マルウェアっぽくない。


本編の演習と同様、まずは Detect It Easy で、パッキングされているかどうか確認する。

f:id:higher_tomorrow:20170831205418p:image:w400


これで、UPX でパッキングされていることがわかる。ここが唯一、マルウェアっぽいところ。それだけなのに、ウイルス対策ソフトにひっかかり、消される。むきー。


さて、本編 5.4.4 と同様に、アンパックを行う。アンパックに、UPX や Themida のようなツールは使わない。マニュアルアンパックが、マルウェア解析のはじめの一歩として大事なところなのだろう。

まずは、デバッガを用い、OEP(Original Entry Point)を探す。パッキングされ、圧縮・難読化されたコードであっても、どこかのタイミングで、オリジナルコードをメモリ上に展開することになる。このオリジナルコードの開始位置が、OEP。

次のような箇所を探し、OEP を推測するところからスタート。

  • パックを展開する(OEP 到達する)前に、パックを展開するためのコードが実行される。これにあたって、レジスタの退避と復元が行われるはず。この復元処理の後に、OEP があるのでは?
  • OEP の移動は、PE フォーマットのセクションをまたいだ移動になる。このような JMP, RET, CALL 命令が実行された後に、OEP があるのでは?

本編の演習と同様、デバッガ OllyDbg を起動して、解析していくことにする。しかし、どうしても OllyDump のところが、うまくいかない。次のようなエラーが出る。本編の演習の方でも同じエラーが出たので、環境まわりがあやしい。

  • デバッグプロセスのメモリ読み込みができません(00400000...00412fff)
  • Bad DOS Signature!!

結果、環境面は、うまく解決できなかった。Immunity Debugger を使ってみたら、こっちは問題なく動作したので、以下は、Immunity Debugger を使って実施した記録。

まず、ターゲットのファイルをロードする。すると、本編の演習と同様、PUSHAD 命令から始まっている。PUSHAD 命令は、全ての汎用レジスタESP のみを除く)を、いっきにスタックに退避する命令。「F8」ボタンで 1 ステップ進めると、レジスタの値が、ごそっとスタックに退避される。

f:id:higher_tomorrow:20170831210747p:image:w560


この退避した値を「復元する」箇所の近くに、EOP があるはず。次に、この PUSHAD で退避した値を復元する POPAD 命令を探す。

レジスタの値を復元するとなると、何らかの形で、今、退避した値にアクセスすることになる。そこで、スタック上にある退避したレジスタの値に、ハードウェアブレイクポイントを設定しておく。

設定の仕方は、本編の演習(p.153)の箇所に、詳しく書いてある。右上(レジスタペイン)の ESP を選択し、右クリックから Follow in Dump を選択する。これで、左下(ダンプペイン)に、スタックの状態が表示される。このうち、汎用レジスタ(上の画像では EAX の値)を選択し、右クリックして BreakPoint -> Hardware -> on access -> DWORD を選択する。

さてさて、準備が整ったので、「F9」ボタンで再開する。予想通り、POPAD 命令の箇所で停止する。その後に JMP 命令が見える。

f:id:higher_tomorrow:20170831211557p:image:w560


この JMP 命令を実行すると、セクションをまたいだ移動(UPX1 セクションから UPX2 セクションまで、大きくジャンプ)をしている。まず間違いなく、ココ 0x01091655 が OEP だ。

f:id:higher_tomorrow:20170831211552p:image:w560


ここで、メモリのダンプを取得するのだが、前述したとおり、OllyDump ではうまくいかなかった。ImportREC を使ってみるも、やはりダメだった。Immunity Debugger の OllyDumpEx だけ、うまくいった。助かった。

f:id:higher_tomorrow:20170831211954p:image:w400


これで、オリジナルコードが展開されているメモリのダンプは取れた。そして、Scylla(スキラ)を使って IAT(Import Address Table)を再構築する。この部分を手動でやれと言われれば、間違いなくできない。

f:id:higher_tomorrow:20170831212228p:image:w400


これで、アンパックが完了。

再度、Detect It Easy で、ファイルの内容を確認すると、アンパックされたことがわかる。


これで、ようやく本丸のコードが読めるようになった。IDA(アイダ)を使って静的解析を行ってみる。まずは、IDA に、アンパックしたファイルを読み込ませる。きわめて小さいプログラムなのだが、そこそこ分量がある(涙)。全部読むのは骨が折れる、というか、自分のスキルでは不可能なので、入力値とパスワードを比較しているところを探す。

普通にプログラムを起動し、パスワードを入力してみると、Invalid password と怒られる。この、文字列 Invalid password を表示している直前が、パスワードを比較しているところだろうと推測できる。なので、IDA に戻り、文字列 Invalid password で検索をかける。「x(エックス)」を押して、この文字列の呼び出し元へ行ってみると、それっぽいサブルーチンが出てきた。IDA が、分岐などを見やすく整理してくれているので、このレベルのプログラムであれば、アセンブラの知識があまりなくても、なんとか読み解くことができる。

f:id:higher_tomorrow:20170831213052p:image:w560


さて、このサブルーチンの先頭を見ると、変数(アドレス)が定義されていて、それぞれのアドレスの先を初期化している。var_5 の指す先だけ byte なので、1 文字が入る。なんとなく、入力したパスワードの 1 文字が入るんだろうな。

そして、とても怪しい文字列 N%qt{j%wj{jwxj%jslnsjjwnsl3 がいる。パスワードっぽい。「これだ!」と思って実行してみると、案の定、うまくいかない。

    sub_1261010 proc near

    loc_1261094:var_10= dword ptr -10h
    loc_1261094:var_C= dword ptr -0Ch
    loc_1261094:var_5= byte ptr -5
    loc_1261094:var_4= dword ptr -4

    loc_1261094:push    ebp
    loc_1261094:mov     ebp, esp
    loc_1261094:sub     esp, 10h
    loc_1261094:push    esi
    loc_1261094:mov     [ebp+var_C], 0
    loc_1261094:mov     [ebp+var_10], 0
    loc_1261094:push    offset aNQtJWjJwxjJsln ; "N%qt{j%wj{jwxj%jslnsjjwnsl3"
    loc_1261094:call    sub_1261440
    loc_1261094:add     esp, 4
    loc_1261094:mov     [ebp+var_10], eax
    loc_1261094:push    offset dword_126C000
    loc_1261094:call    sub_126137A
    loc_1261094:add     esp, 4
    loc_1261094:mov     [ebp+var_4], 0
    loc_1261094:jmp     short loc_1261054

解析を再開。この文字列を使っているところを、もう少し細かく見ていく。それが、次の箇所。

    loc_1261094:
    mov     ecx, [ebp+var_4]
    movsx   esi, byte ptr aNQtJWjJwxjJsln[ecx] ; "N%qt{j%wj{jwxj%jslnsjjwnsl3"
    movzx   edx, [ebp+var_5]
    push    edx
    call    sub_1261000
    add     esp, 4
    movsx   eax, al
    cmp     esi, eax
    jnz     short loc_12610BB

EBP + var_4 にある値は、ループのたび、インクリメントされる。なので、怪しい文字列の ECX 番目の文字が、ESI に入る。EDX には、パスワードの 1 文字が入ったと思われる。そいつを引数に sub_1261000(引数に 5 を足すだけ)をコールし、EAX へ。ESI と EAX を比較している。

パスワードに 5 を足した値が、"N%qt{j%wj{jwxj%jslnsjjwnsl3" になっているのだろう。

そうしたら、Python で、、と言ったらかっこいいのだが、Python は勉強中。PowerShell で、次のように実行する。

foreach($ascii in [int][char]"N%qt{j%wj{jwxj%jslnsjjwnsl3") { Write-Host -NoNewline ([char]($ascii - 5)) }

出てきた値を使うと、今度は、うまくいった。

一応の達成感は得られたものの、なんか、モヤモヤしっぱなし。PE フォーマットから、きちんと勉強しろということだ。

2017-08-17

NEIGHBORS COMPLAIN

| 21:00 |  NEIGHBORS COMPLAINを含むブックマーク

CD 買ったの、「相も変わらずカッコイイ - あしのあしあと」以来だと思う。

まだ回復しているんだなぁ。

NBCP

NBCP

やっぱ、日本の歌を聞きたいよね。日本人だし。

2017-08-16

最も簡単な逆アセンブル(線形探索)

| 08:18 |  最も簡単な逆アセンブル(線形探索)を含むブックマーク

今日は、線形探索(番兵法)。簡単そうだから。

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

int search(int list[], int length, int target) {
    // 番兵(sentinel)
    list[length] = target;

    int i = 0;
    for (; i < length + 1; i++) {
        if (list[i] == target) break;
    }

    if (i != length + 1) {
        return i;  // 見つかった
    } else {
        return -1;  // 見つからなかった
    }
}

int main(void) {
    // target を探索
    int target = 30;

    // 探索対象のリスト
    int length = 8;

    int *list;
    list = calloc(length + 1, sizeof(int));
    if (list == NULL) return -1;

    list[0] = 23; list[1] = 43; list[2] = 11; list[3] = 64;
    list[4] = 91; list[5] = 75; list[6] = 30; list[7] = 58;

    // 線形探索
    int index = search(list, length, target);

    free(list);

    // 結果の表示
    if (index < length) {
        printf("%d 番目に見つかりました。\r\n", index + 1);
        return 0;
    } else if (index == length) {
        printf("見つかりませんでした。\r\n");
        return 0;
    } else {
        printf("エラーが発生しました [index = %d]\r\n", index);
        return -1;
    }
}

今日は、main 関数も読む。デバッグ用のコードを、ごりごり読む。

_main:
00D717A0  push        ebp                       ; EBP を退避
00D717A1  mov         ebp,esp                   ; ESP を退避(EBP ← ESP)
00D717A3  sub         esp,0F0h                  ; ESP ← ESP−0xF0
00D717A9  push        ebx                       ; EBX を退避
00D717AA  push        esi                       ; ESI を退避
00D717AB  push        edi                       ; EDI を退避
00D717AC  lea         edi,[ebp+FFFFFF10h]       ; EDI ← EDI+0xF0

00D717B2  mov         ecx,3Ch                   ; ECX ← 0x3C
00D717B7  mov         eax,0CCCCCCCCh            ; EAX ← 0xCCCCCCCC
00D717BC  rep stos    dword ptr es:[edi]        ; EDI 番地 ← EAX(EDI が 4 減る)
                                                ; これを、ECX(0x3C)回繰り返す

00D717BE  mov         dword ptr [ebp-8],1Eh     ; EBP−0x08 番地(target) ← 30
00D717C5  mov         dword ptr [ebp-14h],8     ; EBP−0x14 番地(length) ← 8
00D717CC  mov         esi,esp                   ; ESI ← ESP
00D717CE  push        4                         ; 第 2 引数 sizeof(int)
00D717D0  mov         eax,dword ptr [ebp-14h]   ; EAX ← length
00D717D3  add         eax,1                     ; EAX ← length+1
00D717D6  push        eax                       ; 第 1 引数 length+1
00D717D7  call        dword ptr ds:[00D7B16Ch]  ; [__imp__calloc]
00D717DD  add         esp,8                     ; ESP を 2 段下へ(引数 2 つ分)
00D717E0  cmp         esi,esp                   ; ESP がずれていないことを確認
00D717E2  call        00D71122                  ; __RTC_CheckEsp
00D717E7  mov         dword ptr [ebp-20h],eax   ; EBP−0x20 番地(list)← EAX 
00D717EA  cmp         dword ptr [ebp-20h],0     ; list と 0 を比較
00D717EE  jne         00D717F8                  ; 以降の処理へ進む
00D717F0  or          eax,0FFFFFFFFh            ; EAX ← −1
00D717F3  jmp         00D71905                  ; 終了へ進む

00D717F8  mov         eax,4                     ; EAX ← 4
00D717FD  imul        ecx,eax,0                 ; ECX ← EAX × 0
00D71800  mov         edx,dword ptr [ebp-20h]   ; EDX ← EBP−0x20(list)番地の値
00D71803  mov         dword ptr [edx+ecx],17h   ; EDX+ECX 番地(list[0]) ← 23

00D7180A  mov         eax,4                     ; EAX ← 4
00D7180F  shl         eax,0                     ; EAX ← EAX × 1(0 ビット左にずらす)
00D71812  mov         ecx,dword ptr [ebp-20h]   ; ECX ← EBP−0x20(list)番地の値
00D71815  mov         dword ptr [ecx+eax],2Bh   ; ECX+EAX 番地(list[1]) ← 43

........  ...         .......................   ; 途中略

00D71875  mov         eax,4                     ; EAX ← 4
00D7187A  imul        ecx,eax,7                 ; ECX ← EAX × 7
00D7187D  mov         edx,dword ptr [ebp-20h]   ; EDX ← EBP−0x20(list)番地の値
00D71880  mov         dword ptr [edx+ecx],3Ah   ; EDX+ECX 番地(list[7]) ← 58

00D71887  mov         eax,dword ptr [ebp-8]     ; EAX ← target
00D7188A  push        eax                       ; 第 3 引数
00D7188B  mov         ecx,dword ptr [ebp-14h]   ; ECX ← length
00D7188E  push        ecx                       ; 第 2 引数
00D7188F  mov         edx,dword ptr [ebp-20h]   ; EDX ← list
00D71892  push        edx                       ; 第 1 引数
00D71893  call        00D71041                  ; _search 
00D71898  add         esp,0Ch                   ; ESP を 3 段(引数 3 つ分)下へ
00D7189B  mov         dword ptr [ebp-2Ch],eax   ; index 番地 ← EAX
00D7189E  mov         esi,esp                   ; ESI ← ESP
00D718A0  mov         eax,dword ptr [ebp-20h]   ; EAX ← list
00D718A3  push        eax                       ; 引数
00D718A4  call        dword ptr ds:[00D7B168h]  ; [__imp__free]
00D718AA  add         esp,4                     ; ESP を 1 段下へ
00D718AD  cmp         esi,esp                   ; ESI と ESP を比較
00D718AF  call        00D71122                  ; __RTC_CheckEsp
00D718B4  mov         eax,dword ptr [ebp-2Ch]   ; EAX ← EBP−2C(index)番地の値
00D718B7  cmp         eax,dword ptr [ebp-14h]   ; EAX(index)と length を比較
00D718BA  jge         00D718D6                  ; Jump if Greater or Equal
00D718BC  mov         eax,dword ptr [ebp-2Ch]   ; EAX ← EBP−2C(index)番地の値
00D718BF  add         eax,1                     ; EAX ← EAX+1
00D718C2  push        eax                       ; 引数 index+1
00D718C3  push        0D77C08h                  ; 引数 "%d 番目に見つかりました。\r\n"
00D718C8  call        00D7132F                  ; _printf
00D718CD  add         esp,8                     ; ESP を 2 段下へ(引数 2 つ分)
00D718D0  xor         eax,eax                   ; EAX ← 1(戻り値)
00D718D2  jmp         00D71905                  ; 終了へ進む
00D718D4  jmp         00D71905                  ; (なぜ jmp が 2 回ある?)
00D718D6  mov         eax,dword ptr [ebp-2Ch]   ; EAX ← EBP−2C(index)番地の値
00D718D9  cmp         eax,dword ptr [ebp-14h]   ; EAX(index)と length を比較
00D718DC  jne         00D718F1                  ;
00D718DE  push        0D77B30h                  ; 引数 "見つかりませんでした。\r\n" 
00D718E3  call        00D7132F                  ; _printf
00D718E8  add         esp,4                     ; ESP を 1 段下へ(引数 1 つ分)
00D718EB  xor         eax,eax                   ; EAX ← 1(戻り値)
00D718ED  jmp         00D71905                  ; 終了へ進む
00D718EF  jmp         00D71905                  ; (なぜ jmp が 2 回ある?)

00D718F1  mov         eax,dword ptr [ebp-2Ch]   ; EAX ← EBP−2C(index)番地の値
00D718F4  push        eax                       ; 引数 index
00D718F5  push        0D77D08h                  ; 引数 "エラーが発生しました [index = %d]\r\n"
00D718FA  call        00D7132F                  ; _printf
00D718FF  add         esp,8                     ; ESP を 2 段下へ(引数 2 つ分)
00D71902  or          eax,0FFFFFFFFh            ; EAX ← −1(戻り値)

00D71905  pop         edi                       ; EDI を戻す
00D71906  pop         esi                       ; ESI を戻る
00D71907  pop         ebx                       ; EBX を戻す
00D71908  add         esp,0F0h                  ; ESP ← ESP+0xF0
00D7190E  cmp         ebp,esp                   ; EBP と ESP を比較する
00D71910  call        00D71122                  ; __RTC_CheckEsp
00D71915  mov         esp,ebp                   ; ESP ← EBP
00D71917  pop         ebp                       ; EBP を戻す
00D71918  ret                                   ; EAX の値(リターンコード)で終了

もちろん search 関数も読む。もりもり読む。

_search:
; 前処理
00D71A10  push        ebp                      ; EBP を退避
00D71A11  mov         ebp,esp                  ; ESP を退避(EBP ← ESP)
00D71A13  sub         esp,0CCh                 ; ESP ← ESP−0xCC
00D71A19  push        ebx                      ; EBX を退避
00D71A1A  push        esi                      ; ESI を退避
00D71A1B  push        edi                      ; EDI を退避
00D71A1C  lea         edi,[ebp+FFFFFF34h]      ; EDI ← EBP+0xCC

00D71A22  mov         ecx,33h                  ; ECX ← 0x33
00D71A27  mov         eax,0CCCCCCCCh           ; EAX ← 0xCCCCCCCC
00D71A2C  rep stos    dword ptr es:[edi]       ; EDI 番地 ← EAX(EDI が 4 減る)
                                               ; これを、ECX(0x33)回繰り返す
; 初期値の設定
00D71A2E  mov         eax,dword ptr [ebp+0Ch]  ; EAX ← EBP+0x0C(length)番地の値
00D71A31  mov         ecx,dword ptr [ebp+8]    ; ECX ← EBP+0x08(list)番地の値
00D71A34  mov         edx,dword ptr [ebp+10h]  ; EDX ← EBP+0x10(target)番地の値
00D71A37  mov         dword ptr [ecx+eax*4],edx; ECX+EAX×4 番地 ← EDX
00D71A3A  mov         dword ptr [ebp-8],0      ; EBX−8(i)← 0
00D71A41  jmp         00D71A4C                 ; list と target のマッチングへ進む

; ループの処理(i のインクリメント)
00D71A43  mov         eax,dword ptr [ebp-8]    ; EAX ← i
00D71A46  add         eax,1                    ; i++
00D71A49  mov         dword ptr [ebp-8],eax    ; EBP−8(i)番地 ← EAX(i++)

; ループの処理(list と target のマッチング)
00D71A4C  mov         eax,dword ptr [ebp+0Ch]  ; EAX ← EBP+0x0C(length)番地の値
00D71A4F  add         eax,1                    ; length+1
00D71A52  cmp         dword ptr [ebp-8],eax    ; EBP−8(i)の値と EAX を比較
00D71A55  jge         00D71A69                 ; 見つかったかどうかの判定に進む
00D71A57  mov         eax,dword ptr [ebp-8]    ; EAX ← EBP−8(i)番地の値
00D71A5A  mov         ecx,dword ptr [ebp+8]    ; ECX ← EBP+0x08(list)番地の値
00D71A5D  mov         edx,dword ptr [ecx+eax*4]; EDX ← list 番地から i 段下
00D71A60  cmp         edx,dword ptr [ebp+10h]  ; EDX と target を比較
00D71A63  jne         00D71A67                 ; 2 行下へ進む
00D71A65  jmp         00D71A69                 ; 見つかったかどうかの判定に進む
00D71A67  jmp         00D71A43                 ; i のインクリメントへ進む

; ループの処理(見つかったかどうかの判定)
00D71A69  mov         eax,dword ptr [ebp+0Ch]  ; EAX ← EBP+0x0C(length)番地の値
00D71A6C  add         eax,1                    ; length+1
00D71A6F  cmp         dword ptr [ebp-8],eax    ; i の値と length+1 の値を比較
00D71A72  je          00D71A7B                 ; 4 行下へ進む
00D71A74  mov         eax,dword ptr [ebp-8]    ; EAX ← i
00D71A77  jmp         00D71A7E                 ; 後処理へ進む
00D71A79  jmp         00D71A7E                 ;(なぜ jmp が 2 回ある?)
00D71A7B  or          eax,0FFFFFFFFh           ; EAX ← −1

; 後処理
00D71A7E  pop         edi                      ; EDI を戻す
00D71A7F  pop         esi                      ; ESI を戻す
00D71A80  pop         ebx                      ; EBX を戻す
00D71A81  mov         esp,ebp                  ; ESP を戻す(ESB ← EBP)
00D71A83  pop         ebp                      ; EBP を戻す
00D71A84  ret                                  ; EAX の値(リターンコード)で終了

急いでいるので、コメント控えめで。