除算演算逆解析
10796 ワード
除算演算逆解析除算演算逆解析 関連数学公式 除数2のべき乗 除数が負の2のべき乗 除数非2のべき乗 除数が負の非2のべき乗
除法命令の命令周期が長く,効率が低いため,コンパイラは除法命令の代わりに他の命令の組み合わせを工夫した.そこでC/C++除算演算の逆解析は他の演算よりも複雑であることをまとめた.
相関数式
b>0の場合、
⌊ab⌋=⌈a−b+1b⌉
さらに
⌈ab⌉=⌊a+b−1b⌋
b<0の場合、
⌊ab⌋=⌈a−b−1b⌉
さらに
⌈ab⌉=⌊a+b+1b⌋
1.除数2のべき乗
sar命令は下向き整列,すなわち⌊x 2 n⌋に相当し,C言語除算結果はゼロ方向整列,すなわち[x 2 n]である.次のようになります.
[x2n]=⎧⎩⎨⎪⎪⎪⎪⎪⎪⌊x2n⌋⌈x2n⌉=⌊x+2n−12n⌋x≥0x<0
次に例を示します.
コンパイル後の逆アセンブリ:
2.除数が負の2のべき乗
直接例をあげます.
コンパイル後の逆アセンブリ:
最後の文を除いては上記と同じです.
次に難点3と4について議論する
3.除数2以外のべき乗
次に、Cコードをアセンブリによって逆プッシュする例を示す.
シナリオ1(MagicNumber⩽0 x 7 FFFFFFF)
入手しやすい公式
xo⇔x∗2no∗12n
令
c=2 noであり,この値をMagic Numberと呼ぶ.そしてある
xo⇔x∗2no∗12n⇔x∗c2n
ここで,
[xo]=⌈x∗c2n⌉=⌊x∗c2n⌋+1
(ここではx<0であればコンパイラが確認できると思います
x∗c 2 nここでは必ず整数ではありません)
⑪o=2 nc=23338 E 38 E 39 h=8.99999……≒9,Cコードを導出し、
まとめ:
以上の命令シーケンスに遭遇すると,基本的に除算最適化後のコードであると判定できる.MagicNumber<=7 fffffffhであり、コンパイラはimulとsarの間に調整指令を発生していないため、除数を正数と認定する.統計的に右シフトの総回数は式中のn値を決定し,式o=2 nc(マジック数)を用いて除数oの近似値を得た.除算原型を復元できます.
シナリオ2(MagicNumber⩽0 x 7 FFFFFFF)
入手しやすい公式
xo⇔x∗232+232+no−232232+n
この式では、魔数
c=232+no−232
x−x∗c2322+x∗c23222
簡略化:
x∗232+c235
⑪232+n=235⇒n=3,o=232+n 232+c=235232+24925 h=6.99999……≒7,Cコードを出す:
まとめ:
以上の命令シーケンスに遭遇した場合,基本的に除算最適化後のコードであると判定できる.右シフト総回数を統計して式中のn値を決定し、式o=232+n 232+c(魔数)を用いて除数oを解くと、除法プロトタイプを復元することができる.
シナリオ3(MagicNumber≧0 x 8000000)
コンパイラはMagicNumberを計算する際に符号なしとして処理し,imul命令は符号付きとして処理する.したがって、マジック数≧0 x 8000000の場合、実際に乗算に参加するのは負の数であり、マジック数は数学式の「大きな定数」の意味と一致しない.
y真<0の場合、符号化計算式により、y補=232−|y真|=232+y真∈y真=y補−232‰x
入手しやすい公式
xo⇒x∗2 no∗12 n⇒(x∗(2 no−232)+x∗232)∗12 nすなわちxo⇒x∗y補(y無)∗12 n⇒(x∗y真+x∗232)∗12 n
上記のコードを数式に変換します.
(esi∗eax+232∗esi)∗1234‰cは、コンパイラ求魔数演算が式c=2 no符号なしで演算されたものである.⑪式o=2 nc=234992492493 h=6.99999…≒7
逆押し出しCコード:
まとめ:
以上の命令シーケンスに遭遇した場合,基本的に除算最適化後のコードであると判定できる.MagicNumber≧8000000 hの場合、コンパイラはimulとsarの間に調整用のadd命令を生成するので、除数は正と判断できます.*右シフトの合計回数を統計して式中のn値を決定し、式o=2 nc(魔数)を用いて解除数oを求めると、除法原型が復元される.
4.除数が負の2以外のべき乗
入手可能な数式:
xo=x∗c∗12 n(c<0)c=−2 n|o|=2 n|o|補完=232−2 n|o|
シナリオ1(MagicNumber≧0 x 8000000)
コード表現:
edx=ecx∗eax233|o|=2n232−c=233232−99999999h=4.999999……≈5
そこで、Cコードを逆発売しました.
まとめ:
以上の命令シーケンスに遭遇すると,基本的に除算最適化後のコードであると判定できる.MagicNumber≧8000,000,000 h、コンパイラはimulとsarの間に調整命令を発生していないため、除数が負であると認定できます.*合計右シフト数を統計して式中のn値を決定し、式|o|=2 n 232−c(マジック数)を用いて解除数|o|を求めると、除法プロトタイプが復元される.
シナリオ2(MagicNumber⩽0 x 7 FFFFFFF)MagicNumber<=7 FFFFFFFFhの場合、除数も負になる可能性がある.(なぜこのようなシナリオがあるのか?数学式ではc=2 noで表される範囲をより大きくすることができる)x∗c 2 nではc=2 no(o<0)でより小さな負数を表すために、コンパイラは3中シナリオ3のような方法で、
p=−oとし,(p>0)xo=−xp⇒−−(x∗2 np∗12 n)⇒−((x∗(2 np−232)+ x∗232)⇒( x∗(−(2 np−232))−x∗232)⇒( x∗(−(2 np−232))−x∗232))⇒( x∗(232−2−2−2 n|o|)−x∗232)−n∗12n⇒(x∗(232−−2−−2 n|o|)−x∗232)−n n n n n∗12n⇒(n n n)⇒(n⇒x∗c−2322 n(コード変換対応式)−c=2 np−232<0(3中シナリオ3のy真)c=−(2 np−232)=232−2 np=232−2 n|o|>0
上のコードは数式に変換されます.
edx=edx∗eax232−ecx22=ecx∗eax−232∗ecx234=ecx∗eax−232234ecxo=ecx∗eax−232234|o|=2n232−c=234232−6DB6DB6Dh=6.999999……≈7
そこでCコードを反転:
まとめ:
以上の命令シーケンスに遭遇した場合,基本判定は除算最適化後のコードである.MagicNumber 7 fffffffh,imulとsarの間にsub命令があり積を調整するので,除数は負であると認定した.合計右シフト数を統計して式中のn値を決定し、式|o|=2 n 232−c(マジック数)を用いて解除数|o|を求めると、除法プロトタイプが復元される.
アセンブリコードから正負の除数を区別するにはどうすればいいですか?∙MagicNumberの最高位が1の場合(≧8000000 h)、正除数に対してMagicNumberは元のコード形式であり、コンパイラはimulとsarの間に調整作用のあるadd命令を生成する.ない場合は、MagicNumberが符号化されます.∙MagicNumberの最上位が0の場合(⩽7 FFFFFh)、負の除数の場合、コンパイラはimulとsarの間で調整作用のあるsub命令を生成します.これらは負の除数を区別する重要な根拠とすべきである.
除法命令の命令周期が長く,効率が低いため,コンパイラは除法命令の代わりに他の命令の組み合わせを工夫した.そこでC/C++除算演算の逆解析は他の演算よりも複雑であることをまとめた.
相関数式
b>0の場合、
⌊ab⌋=⌈a−b+1b⌉
さらに
⌈ab⌉=⌊a+b−1b⌋
b<0の場合、
⌊ab⌋=⌈a−b−1b⌉
さらに
⌈ab⌉=⌊a+b+1b⌋
1.除数2のべき乗
sar命令は下向き整列,すなわち⌊x 2 n⌋に相当し,C言語除算結果はゼロ方向整列,すなわち[x 2 n]である.次のようになります.
[x2n]=⎧⎩⎨⎪⎪⎪⎪⎪⎪⌊x2n⌋⌈x2n⌉=⌊x+2n−12n⌋x≥0x<0
次に例を示します.
//.c
printf("nVarOne/8=%d",nVarOne/8);
コンパイル後の逆アセンブリ:
;.asm
mov eax,dword ptr [ebp-4]
cdq
and edx,7
add eax,edx
sar eax,3
...
and edx,7
は、nVarOneが負の場合、edxの内容が2 n−1となり、nVarOneが正の場合、edxが0となり、最後にsar eax,3
が2 nで除算されることに相当する.2.除数が負の2のべき乗
直接例をあげます.
//.c
printf("nVarOne/-8=%d",nVarOne/-8);
コンパイル後の逆アセンブリ:
;.asm
mov eax, [esp+4]
cdq
and edx,7
add eax,edx
sar eax,3
neg eax
...
最後の文を除いては上記と同じです.
neg eax
は計算結果に負を取ることに相当します.次に難点3と4について議論する
3.除数2以外のべき乗
次に、Cコードをアセンブリによって逆プッシュする例を示す.
シナリオ1(MagicNumber⩽0 x 7 FFFFFFF)
入手しやすい公式
xo⇔x∗2no∗12n
令
c=2 noであり,この値をMagic Numberと呼ぶ.そしてある
xo⇔x∗2no∗12n⇔x∗c2n
_main proc near
arg_0= dword ptr 4
mov ecx,[esp+arg_0]
mov eax,38E38E39h
imul ecx
sar edx,1
mov eax,edx
shr eax,1Fh
add edx,eax
push edx
push offset Format ;"%d
call _printf
add esp,8
retn
_main endp
ここで,
sar edx,1
算術右シフト1ビット,shr eax,1Fh
論理右シフト31ビットである.まず、ecx取得パラメータ、eax取得魔数、(edx,eax)=eax*ecx、積低4 Bのeaxコンテンツは捨てられ、積高4 Bのedxコンテンツのみを使用することで、積右シフト32ビットに相当し、sar edx,1
右シフトの1ビットを加えて、共に右シフト33ビット、すなわちn=33、mov eax,edx
とshr eax,1Fh
を加算して積結果シンボルビットを取得し、積はタイミングeax格納00000000 h、積算ビットが負の場合は00000001 hを格納する.最後に、add edx,eax
の理由は、x<0であり、[xo]が整数でない場合、[xo]=⌈x∗c2n⌉=⌊x∗c2n⌋+1
(ここではx<0であればコンパイラが確認できると思います
x∗c 2 nここでは必ず整数ではありません)
⑪o=2 nc=23338 E 38 E 39 h=8.99999……≒9,Cコードを導出し、
printf("%d",argc/9);
まとめ:
;x*(2^n/o)
mov eax,MagicNumber
imul ...
;/2^n
sar edx, ...
mov reg,edx
shr reg,1Fh
;
add edx,reg
以上の命令シーケンスに遭遇すると,基本的に除算最適化後のコードであると判定できる.MagicNumber<=7 fffffffhであり、コンパイラはimulとsarの間に調整指令を発生していないため、除数を正数と認定する.統計的に右シフトの総回数は式中のn値を決定し,式o=2 nc(マジック数)を用いて除数oの近似値を得た.除算原型を復元できます.
シナリオ2(MagicNumber⩽0 x 7 FFFFFFF)
入手しやすい公式
xo⇔x∗232+232+no−232232+n
この式では、魔数
c=232+no−232
_main proc near
arg_0= dword ptr 4
mov ecx,[esp+arg_0]
mov eax,24924925h
mul ecx
sub ecx,edx
shr ecx,1
add ecx,edx
shr ecx,2
push ecx
push offset Format ;"nVarTwo/7=%d\r
"
call _printf
add esp,8
xor eax,eax
retn
_main endp
sub ecx,edx
,shr ecx,1
,add ecx,edx
,shr ecx,2
の4文は1つの計算式で表すことができる.x−x∗c2322+x∗c23222
簡略化:
x∗232+c235
⑪232+n=235⇒n=3,o=232+n 232+c=235232+24925 h=6.99999……≒7,Cコードを出す:
printf("nVarTwo/7="%d\r
",argc/7);
まとめ:
mov eax,MagicNumber
mul reg
sub reg,edx
shr reg,1
add reg,edx
(shr reg,A)
以上の命令シーケンスに遭遇した場合,基本的に除算最適化後のコードであると判定できる.右シフト総回数を統計して式中のn値を決定し、式o=232+n 232+c(魔数)を用いて除数oを解くと、除法プロトタイプを復元することができる.
シナリオ3(MagicNumber≧0 x 8000000)
コンパイラはMagicNumberを計算する際に符号なしとして処理し,imul命令は符号付きとして処理する.したがって、マジック数≧0 x 8000000の場合、実際に乗算に参加するのは負の数であり、マジック数は数学式の「大きな定数」の意味と一致しない.
y真<0の場合、符号化計算式により、y補=232−|y真|=232+y真∈y真=y補−232‰x
入手しやすい公式
xo⇒x∗2 no∗12 n⇒(x∗(2 no−232)+x∗232)∗12 nすなわちxo⇒x∗y補(y無)∗12 n⇒(x∗y真+x∗232)∗12 n
_main proc near
arg_0= dword ptr 4
mov esi,[esp+arg_0]
mov eax,92492493h
imul esi
add edx,esi
sar edx,2
;...
上記のコードを数式に変換します.
(esi∗eax+232∗esi)∗1234‰cは、コンパイラ求魔数演算が式c=2 no符号なしで演算されたものである.⑪式o=2 nc=234992492493 h=6.99999…≒7
逆押し出しCコード:
printf("%d",argc/7);
まとめ:
mov eax,MagicNumber;MagicNumber>7fffffffh
imul reg
add edx,reg
sar edx,...
mov reg,edx
shr reg,1Fh
add edx,reg
以上の命令シーケンスに遭遇した場合,基本的に除算最適化後のコードであると判定できる.MagicNumber≧8000000 hの場合、コンパイラはimulとsarの間に調整用のadd命令を生成するので、除数は正と判断できます.*右シフトの合計回数を統計して式中のn値を決定し、式o=2 nc(魔数)を用いて解除数oを求めると、除法原型が復元される.
4.除数が負の2以外のべき乗
入手可能な数式:
xo=x∗c∗12 n(c<0)c=−2 n|o|=2 n|o|補完=232−2 n|o|
シナリオ1(MagicNumber≧0 x 8000000)
_main proc near
arg_0= dword ptr 4
mov ecx,[esp+arg_0]
mov eax,99999999h
imul ecx
sar edx,1
mov eax,edx
shr eax,1Fh
add edx,eax
push edx
push offset Format ;"%d"
call _printf
add esp,8
xor eax,eax
retn
_main endp
コード表現:
edx=ecx∗eax233|o|=2n232−c=233232−99999999h=4.999999……≈5
そこで、Cコードを逆発売しました.
printf("%d",argc/-5);
まとめ:
mov eax,MagicNumber(>=0x7fffffff)
imul reg
sar edx,...
mov reg,edx
shr reg,1Fh
add edx,reg
以上の命令シーケンスに遭遇すると,基本的に除算最適化後のコードであると判定できる.MagicNumber≧8000,000,000 h、コンパイラはimulとsarの間に調整命令を発生していないため、除数が負であると認定できます.*合計右シフト数を統計して式中のn値を決定し、式|o|=2 n 232−c(マジック数)を用いて解除数|o|を求めると、除法プロトタイプが復元される.
シナリオ2(MagicNumber⩽0 x 7 FFFFFFF)MagicNumber<=7 FFFFFFFFhの場合、除数も負になる可能性がある.(なぜこのようなシナリオがあるのか?数学式ではc=2 noで表される範囲をより大きくすることができる)x∗c 2 nではc=2 no(o<0)でより小さな負数を表すために、コンパイラは3中シナリオ3のような方法で、
p=−oとし,(p>0)xo=−xp⇒−−(x∗2 np∗12 n)⇒−((x∗(2 np−232)+ x∗232)⇒( x∗(−(2 np−232))−x∗232)⇒( x∗(−(2 np−232))−x∗232))⇒( x∗(232−2−2−2 n|o|)−x∗232)−n∗12n⇒(x∗(232−−2−−2 n|o|)−x∗232)−n n n n n∗12n⇒(n n n)⇒(n⇒x∗c−2322 n(コード変換対応式)−c=2 np−232<0(3中シナリオ3のy真)c=−(2 np−232)=232−2 np=232−2 n|o|>0
_main proc near
arg_0= dword ptr 4
mov ecx,[esp+arg_0]
mov eax,6DB6DB6Dh
imul ecx
sub edx,ecx
sar edx,2
mov eax,edx
shr eax,1Fh
add edx,eax
push edx
push offset Format ;"%d"
call _printf
add esp,8
retn
_main endp
上のコードは数式に変換されます.
edx=edx∗eax232−ecx22=ecx∗eax−232∗ecx234=ecx∗eax−232234ecxo=ecx∗eax−232234|o|=2n232−c=234232−6DB6DB6Dh=6.999999……≈7
そこでCコードを反転:
printf("%d",argc/-7);
まとめ:
mov eax,MagicNumber(<=7fffffffh)
imul reg
sub edx,reg
sar edx,...
mov reg,edx
shr reg,1Fh
add edx,reg
以上の命令シーケンスに遭遇した場合,基本判定は除算最適化後のコードである.MagicNumber 7 fffffffh,imulとsarの間にsub命令があり積を調整するので,除数は負であると認定した.合計右シフト数を統計して式中のn値を決定し、式|o|=2 n 232−c(マジック数)を用いて解除数|o|を求めると、除法プロトタイプが復元される.
アセンブリコードから正負の除数を区別するにはどうすればいいですか?∙MagicNumberの最高位が1の場合(≧8000000 h)、正除数に対してMagicNumberは元のコード形式であり、コンパイラはimulとsarの間に調整作用のあるadd命令を生成する.ない場合は、MagicNumberが符号化されます.∙MagicNumberの最上位が0の場合(⩽7 FFFFFh)、負の除数の場合、コンパイラはimulとsarの間で調整作用のあるsub命令を生成します.これらは負の除数を区別する重要な根拠とすべきである.