Reverse Engineering Bible 6
6章よく使われるパターンについて【if文】if(x > 0x100){ printf("x > 100\n");}を逆アセンブルすると以下のようになるcmp eax, 100hjle short loc_401038 ; 左の引数≦右の引数のときジャンプ(ZF=1 | SF ! OF)push offset aX100 ; "x > 100\n"call sub_401050 ; printfadd esp, 4loc_401038:pop ecxretn上記のif文をif(x == 0x100)に変えるとjle short loc_401038の部分が以下のように変わるjnz short loc_401038 ; x≠100hのときジャンプ(ZF=0)if(x < 0x100)に変えると以下のように変わるjge short loc_401038 ; 左の引数≧右の引数のときジャンプ(SD=OF)else printf("else");を加えると以下の通りcmp eax, 100hjbe short loc_401218 ; 左の引数≦右の引数のときジャンプ(CF=1 | ZF=1)push offset aX100 ; "x > 100\n"call sub_401260 ; printfadd esp, 4retnloc_401218: ; CODE XREF: sub_401200+8 jpush offset aElse ; "else"call sub_401260pop ecxretnsub_401200 endp if(x > 0x100 && x < 0x200)のような場合、分岐条件が2つになるcmp eax, 100hjbe short loc_401220 ; 左の引数≦右の引数のときジャンプ(CF=1 | ZF=1)cmp eax, 200hjnb short loc_401220 ; 左の引数≧右の引数のときジャンプ(CF=0)push offset aX100call sub_401260add esp, 4retn「ll」についてもjbeとjnbの部分がそれぞれjaとjbに変わるだけ。【ループ】int loop(int c){ int d=0; for(int i=0; i<=0x100; i++){ printf("loop : %d\n", d); d++; } return c+d;}push ebpmov ebp, espsub esp, 8mov [ebp+var_4], 0 ; d=0mov [ebp+var_8], 0 ; i=0jmp short loc_401331loc_401328:mov eax, [ebp+var_8] ; eaxにiをadd eax, 1 ; eax++(i++)mov [ebp+var_8], eax ; eaxをiにloc_401331:cmp [ebp+var_8], 100h ; iと0x100の比較jg short loc_401356 ; i>0x100のときジャンプmov ecx, [ebp+var_4] ; dをecxレジスタにpush ecx ; ecxをスタックpush offset aLoopD ; "loop : %d\n"call sub_4013B8 ; printfadd esp, 8 ; esp+8mov edx, [ebp+var_4] ; dをedxレジスタにadd edx, 1 ; edx++(d++)mov [ebp+var_4], edx ; edxレジスタをdにjmp short loc_401328 ; 無条件ジャンプloc_401356:mov eax, [ebp+arg_0]add eax, [ebp+var_4] mov esp, ebppop ebpretnsub_401312 endpbreak文を入れた場合、jnzの次にjmpが続くようなジャンプ命令が連続する形になる。movcmpjnz ; if文の処理jmp ; breakcontinue文もほぼ同じでjmpのジャンプ先がループの最初に変わるだけ。途中にreturnが入った場合、xor eax, eaxでeaxをクリアし、jmpでループを抜ける。【文字列制御】・strcpychar *szSource = "Hello World !";char szTarget[MAX_PATH];strcpy(szTarget, szSource);printf("%s\n", szTarget);↓逆アセンブルor ecx, FFFFFFFFxor eax, eax ; eaxで何かをカウントするための準備lea edx, dword ptr ss:[esp] ; espのアドレスをedxに入れるpush esi ; 変数使用のため(ソースアドレス)push edi ; 変数使用のため(デスティネーションアドレス)mov edi, example.00407034 ; ediに"Hello"を入れるrepne scas byte ptr es:[edi] ; eaxとediを比較(ZF=0, ecx>0のあいだ繰り返し)not ecx ; 1と0を変換(null文字含め14文字(0xE)が入った)sub edi, ecx ; ポインタ位置修正(Helloの先頭文字を指す)mov eax, ecx ; 元のカウンタ値をeaxに保存mov esi, edi ; edi→esi(Hello World !)mov edi, edx ; edx→edi(espのアドレスを入れる)shr ecx, 2 ; ecxに3が入る(14/4の商)rep movs dword ptr es:[edi], dword ptr ds:[esi] ; esi→ediコピー(12文字だけ)mov ecx, eax ; repを使うために元のecxの値を取ってくるand ecx, 3 ; 0xEが0x2になる(残りの2文字もコピーするため)rep movs byte ptr es:[edi], byte ptr ds:[esi] ; 2文字コピー・strcatchar szMalware[MAX_PATH];GetSystemDirectory(szMalware, MAX_PATH); //system32フォルダの場所を取得strcat(szMalware, "\\expllorer.dll"); //C:\Windows\System32\explorer.dllprintf("szMalware: %s\n", szMalware);↓逆アセンブルlea eax, dword ptr ss:[esp] ; espのアドレスをeaxにpush ebxpush esipush edi push 104 ; 第2引数。MAX_PATH分の大きさを指定push eax ; 第1引数。system32のパスを出力するアドレスcall near dword ptr ds:[<&KERNEL32.GetSystemDirectoryA>]mov edi, example.00407064 ; "\\explorer.dll"or ecx, FFFFFFFF ; 0x13とFFFFFFFFをorxor eax, eax ; eax初期化lea edx,dword ptr ss:[esp+C]repne scas byte ptr es:[edi]not ecx ; 0xEsub edi, ecx ; 元の位置に戻すmov esi, edi ; \\explorer.dllmov ebx, ecx ; ecxの値をebxに保存mov edi, edxor ecx, FFFFFFFFrepne scas byte ptr es:[edi] ; C:\Windows\System32の長さを求めるmov ecx, ebx ; 文字列の長さdec edi ; 文字を連結するためにnull文字の1つ前を指すようにするshr ecx, 2 ; 文字列の長さ/4rep movs dword ptr es:[edi], dword ptr ds:[esi] ; 商のぶんだけ4バイトずつコピーmov ecx, ebxlea eax, dword ptr ss:[esp+C]and ecx, 3 ; 残りをコピーpush eaxrep movs byte ptr es: [edi], byte ptr ds:[esi] ; 残りをコピー・strlwrchar *_strlwr(char *string);↓逆アセンブルmov cl, byte ptr ds:[edx]cmp cl, 0x41 ; 0x41はAjl short example.00405B93cmp cl, 0x5A ; 0x5AはZjg short example.00405B93add cl, 0x20 ; 小文字に変換(ASCIIで小文字は大文字+20)mov byte ptr ds:[edx], clinc edxcmp byte ptr ds:[edx], bljnz short example.00405B82