10 全局变量初始化,数组的识别
1. 全局变量通过函数初始化
初始化时机在main函数之前,对于VC6,具体来说是在mainCRTStartup -> _cinit -> 第2个_initterm中。此函数接收2个参数,通过遍历,找到函数指针并调用。逆向时找到push两个参数的地方,以这2个参数作为地址的上下边界,寻找其中非0的值,在反汇编窗口中查看函数逻辑,判断是否是我们需要的函数。
#ifdef CRTDLL
void __cdecl _initterm (
#else /* CRTDLL */
static void __cdecl _initterm (
#endif /* CRTDLL */
_PVFV * pfbegin,
_PVFV * pfend
)
{
while ( pfbegin < pfend )
{
if ( *pfbegin != NULL )
(**pfbegin)();
++pfbegin;
}
}
1. 全局变量通过函数初始化
初始化时机在main函数之前,对于VC6,具体来说是在mainCRTStartup -> _cinit -> 第2个_initterm中。此函数接收2个参数,通过遍历,找到函数指针并调用。逆向时找到push两个参数的地方,以这2个参数作为地址的上下边界,寻找其中非0的值,在反汇编窗口中查看函数逻辑,判断是否是我们需要的函数。
#ifdef CRTDLL
void __cdecl _initterm (
#else /* CRTDLL */
static void __cdecl _initterm (
#endif /* CRTDLL */
_PVFV * pfbegin,
_PVFV * pfend
)
{
while ( pfbegin < pfend )
{
if ( *pfbegin != NULL )
(**pfbegin)();
++pfbegin;
}
}
VS2019:mainCRTStartup -> 第2个_initterm中,同样在地址的上下边界寻找非0的值,可能会发现不止一个非0值,一般第一个非0值都是系统相关的代码,可略过。
2. 识别数组
2.1 数组特性:
(1) 连续性:相邻数据无重叠、无对齐
(2) 一致性:类型一致,作用(业务逻辑)一致
论证上述两点的方法:
(1) 使用比例因子寻址方式:[ebp + XXX * ecx*4],利用对ecx所做的检查确定数组边界。也可能存在寄存器化的指针,每次解引用指针并递增指针到下一个位置,此时通过指针递增的步长找到数组元素的size,通过循环次数确定数组元素个数。
(2) 循环结构处理连续空间中的相邻元素:利用循环边界确定数组的边界。
(3) 在高版本编译器中,往往会使用媒体指令一次复制多个数据来初始化数组。
2.2 数组寻址公式
type ary[M] = ...
&ary[i] = (int)ary + sizeof(type) * i
IDA中识别出数组首地址后,对首地址命名,再按*指定数组元素个数,可令其显示为:-00000014 ary dd 5 dup(?)
同时反汇编窗口也会更新显示效果:
2.3 二维数组
type ary[M][N] = ...
&ary[i][j] = (int)ary + sizeof(type[N])*i + sizeof(type)*j
= (int)ary + sizeof(type)*i*N + sizeof(type)*j
= (int)ary + sizeof(type) * (N*i+j)
debug:
release:
VS2019使用媒体指令初始化数组,其他和VC6一致。
2.4 三维数组
type ary[L][M][N] = ...
&ary[i][j][k] = int(ary) + sizeof(type [M][N])*i
+ sizeof(type [N])*j
+ sizeof(type)*k
= int(ary) + sizeof(type)*i*M*N
+ sizeof(type)*j*N
+ sizeof(type)*k
= int(ary) + sizeof(type)(i*M*N + j*N +k)
= int(ary) + sizeof(type)(N*(i*M + j) +k)
debug:
release:
根据分析推测出三维数组 type ary[L][M][N]:
i*24 => sizeof(type[M][N])==24
j*12 => sizeof(type[N])=12
k*4 => sizeof(type)==4
暂停type为int,整理得
sizeof(int)*M*N == 24
sizeof(int)*N == 12
即:N=3,M=2,即int ary[][2][3]=...
同样,遍历时依然当作一维数组,循环2*2*3次。
3. 指针的识别
3.1 识别方法:
(1) 是否保存了地址
(2) 某个寄存器或内存是否为间接访问的来源
4. 字符串操作
4.1 strlen
VS2019的release:简单的循环结构
VC6:
lea edi, addr szBuf
or ecx, -1
xor eax, eax
repne scasb ;ecx=-1-length-1
not ecx
dec ecx
length = -2-ecx
= -2+neg(ecx)
= -2 + not ecx + 1
= not ecx - 1
4.2 strcpy
debug版是循环结构
release版:
4.3 strcmp
VC6的release:
VS2019的release: