#pragma

プログラムに関するすべての情報はソースコード上にあるのが好きだ。プロジェクトのプロパティ(あるいはmakefile)に情報が分散されているのは好きじゃないので、コンパイラやリンカへのオプションはできるだけpragmaを使おうと努めている。
でまあ、またちょっと調べたりしたのでメモ。特に、暗黙的な関数呼び出しを生成してしまうオプションの操作についてまとめる。

  1. 「バッファセキュリティチェック」で__security_check_cookie関数の呼び出しを制御できる
  2. 「基本ランタイムチェック」で_RTC_CheckEsp等の関数の呼び出しを制御できる
  3. 「基本ランタイムチェック」を操作するには#pragma runtime_checksを使う
  4. 「組み込み関数を使用する」で一部関数をインライン化できる
  5. 「組み込み関数を使用する」を操作するには#pragma intrinsic#pragma function を使う

_RTC_CheckEsp / _RTC_CheckStackVars関数の呼び出しを抑止

before

void enables_stackframe_verification()
{
012D1040  push        ebp  
012D1041  mov         ebp,esp 
012D1043  sub         esp,0Ch 
012D1046  mov         dword ptr [ebp-0Ch],0CCCCCCCCh 
012D104D  mov         dword ptr [ebp-8],0CCCCCCCCh 
012D1054  mov         dword ptr [ebp-4],0CCCCCCCCh 
  char c[2] = {0};
012D105B  mov         byte ptr [c],0 
012D105F  xor         eax,eax 
012D1061  mov         byte ptr [ebp-7],al 
  memset(&c, 1, sizeof(c));
012D1064  push        2    
012D1066  push        1    
012D1068  lea         ecx,[c] 
012D106B  push        ecx  
012D106C  call        memset (12D1630h) 
012D1071  add         esp,0Ch 
}
012D1074  push        edx  
012D1075  mov         ecx,ebp 
012D1077  push        eax  
012D1078  lea         edx,[ (12D1094h)] 
012D107E  call        _RTC_CheckStackVars (12D16E0h) 
012D1083  pop         eax  
012D1084  pop         edx  
012D1085  add         esp,0Ch 
012D1088  cmp         ebp,esp 
012D108A  call        _RTC_CheckEsp (12D16B0h) 
012D108F  mov         esp,ebp 
012D1091  pop         ebp  
012D1092  ret              
012D1093  nop                   

after

#pragma runtime_checks("s", off)
void disables_stackframe_verification()
{
012D10B0  push        ebp  
012D10B1  mov         ebp,esp 
012D10B3  push        ecx  
  char c[2] = {0};
012D10B4  mov         byte ptr [c],0 
012D10B8  xor         eax,eax 
012D10BA  mov         byte ptr [ebp-3],al 
  memset(&c, 1, sizeof(c));
012D10BD  push        2    
012D10BF  push        1    
012D10C1  lea         ecx,[c] 
012D10C4  push        ecx  
012D10C5  call        memset (12D1630h) 
012D10CA  add         esp,0Ch 
}
012D10CD  mov         esp,ebp 
012D10CF  pop         ebp  
012D10D0  ret                 

元に戻すときは restore を指定する。

#pragma runtime_checks("s", restore)

組み込みの関数を使用する

これが通常のmemset呼び出し。

...
  memset(&c, 1, sizeof(c));
012D10BD  push        2    
012D10BF  push        1    
012D10C1  lea         ecx,[c] 
012D10C4  push        ecx  
012D10C5  call        memset (12D1630h) 
...

#pragma intrinsicを使うと関数がインライン化される。ただし宣言時に初期化するコードには影響しない。

#pragma intrinsic(memset)
void intrinsic_memset()
{
012D10E0  push        ebp  
012D10E1  mov         ebp,esp 
012D10E3  push        ecx  
  char c[2] = {0};
012D10E4  mov         byte ptr [c],0 
012D10E8  xor         eax,eax 
012D10EA  mov         byte ptr [ebp-3],al 
  memset(&c, 1, sizeof(c));
012D10ED  mov         ecx,1010101h 
012D10F2  mov         word ptr [c],cx 
}
012D10F6  mov         esp,ebp 
012D10F8  pop         ebp  
012D10F9  ret     

関数呼び出しにしたいときは #pragma function を使う。

#pragma function(memset)

__security_check_cookie関数の呼び出しを抑止する

「バッファセキュリティチェック」で /GS- を指定すればよい。#pragma strict_gs_check(off) でうまくいくかと思ったが、これはなぜか機能しなかった。

参考と使ったコード

参考:Visual C++ の防御機能でコードを保護する

#include <windows.h>

void enables_stackframe_verification()
{
  char c[2] = {0};
  memset(&c, 1, sizeof(c));
}

#pragma runtime_checks("s", off)
void disables_stackframe_verification()
{
  char c[2] = {0};
  memset(&c, 1, sizeof(c));
}

#pragma intrinsic(memset)
void intrinsic_memset()
{
  char c[2] = {0};
  memset(&c, 1, sizeof(c));
}
#pragma function(memset)

void enables_check_stack()
{
  char c[100];
  memset(&c, 1, sizeof(c));
}

#pragma strict_gs_check(off) 
void disables_check_stack()
{
  char c[100];
  memset(&c, 1, sizeof(c));
}

int main()
{
  enables_stackframe_verification();
  disables_stackframe_verification();
  intrinsic_memset();
  enables_check_stack();
  disables_check_stack();
  return 0;
}