首页
社区
课程
招聘
[讨论][原创]第二题 lelfeiCM的writeup
2017-6-4 09:27 4522

[讨论][原创]第二题 lelfeiCM的writeup

2017-6-4 09:27
4522

使用IDA工具装入lelfeiCM.exe很容易来到main函数,粗略阅读汇编代码,得出注册序号的格式的长度满足整数区间[8,20],且ASCII码满足整数区间[1,9]。

我使用InputKey辅助标记输入的字符串变量

.text:00401044                 lea     eax, [esp+4148h+InputKey]
.text:00401048                 push    104h            ; int
.text:0040104D                 push    eax             ; char *
.text:0040104E                 call    _fgets

先观察第一个重要的函数调用

.text:004010AC                 lea     ecx, [esp+4138h+bignumber] ;使用bignumber辅助标记该局部变量
.text:004010B3                 call    Construct_void

发现使用了ecx,并且接下来的汇编大量使用了ecx寄存器传输数据结构地址,可以反推出这个数据结构是一个类,猜测如下:

class BigNumber 
{
private:
	DWORD magic;                     //mov     dword ptr [ecx], offset off_4080C8, 这行汇编取相当于SetMagic函数
	DWORD length;                    //mov     eax, [ecx+4],相当于GetLength函数
	DWORD val[1024];                 //保存十进制数字的实际值
	DWORD idx[1024];                 //十进制数字的索引,使用GetVal(INDEX)取对应位上的数字,即val[idx[INDEX]]
	DWORD seed;                      
	DWORD tickcount;                 //保存两个GetTickCount值,两个用途,1.Hash映射的seed,2.时间戳校验
public:
	BigNumber(void) {};              //void参数 构造函数
	BigNumber(DWORD number) {};      //Int参数 构造函数
	BigNumber(BigNumber* number) {}; //BigNumber 复制构造函数
	...当然还有其他成员函数,
};

先来看bignumber变量与InputKey的第一次交集,最后一行标记的Assign_char_调用的作用是将InputKey表示的数字依次放到BigNumber数据结构中,

还原一下函数形式BigNumber::Assign(char* input), 表现为bignumber = '123456789'

.text:004010AC                 lea     ecx, [esp+4138h+bignumber]
.text:004010B3                 call    Construct_void ;BigNumber(void)
.text:004010B8                 lea     ecx, [esp+4138h+InputKey]
.text:004010BC                 mov     [esp+4138h+Bool], 0
.text:004010C7                 push    ecx
.text:004010C8                 lea     ecx, [esp+413Ch+BigNumber_A]
.text:004010CF                 call    Assign_char_

进入Assign_char_发现有两个直接调用,

第一个调用的作用就是SetVal(INDEX,VALUE),val [ idx [ Index ] ] = InputKey [ Index ] - '0'

第二个调用就是为了进位计算的,当某一位大于或等于10时,向前进位

.text:00401525                 call    sub_401580 ;ComputeBit
 ...
.text:00401531                 call    sub_401970 ;ComputeCarry

来到第一个关键的调用,经过分析,该函数作用是计算BigNumber与数字乘法运算的

还原一下形式BigNumber::Multiply(int multiplier)

必然还有另外一个函数BigNumber::Multiply(BigNumber multiplier)

.text:004010E0                 push    9
.text:004010E2                 lea     ecx, [esp+413Ch+bignumber]
.text:004010E9                 call    MultiplyWith_9           ;这里用MultiplyWith_9代替Multiply__int
 ...
.text:00401774                 mov     eax, [esp+403Ch+arg_0]   ;在这里arg_0 = 9
 ...
.text:00401787                 cdq
.text:00401788                 mov     ecx, 0Ah
.text:0040178D                 idiv    ecx
.text:0040178F                 mov     esi, edx                  ;余数9%A = 9
.text:00401791                 mov     ebx, eax                  ;商9/A=0
.text:004017A1                 push    edi
.text:004017A2                 lea     ecx, [esp+4040h+Handler_B]
.text:004017A6                 call    ShiftRight                ;edi作为参数,将Handler_B(class BigNumber)向高位移动edi步
.text:004017AB                 push    esi
.text:004017AC                 lea     ecx, [esp+4040h+Handler_B]
.text:004017B0                 call    Multiply_LA               ;余数作为参数,将Handler_B与less than A大小的数相乘

恰好下一部分就是对BigNumber::Multiply(BigNumber multiplier)的调用,

首先使用构造函数初始化TmpHandler,然后调用 this->MultiplySelfKey(TmpHandler),MultiplySelfKey就是乘法运算的实现

MultiplySelfKey与MultiplyWith_9的汇编代码大同小异。

紧接着又是一个MultiplyWith_9

.text:004010FA                 lea     edx, [esp+4138h+InputKey]
.text:004010FE                 lea     ecx, [esp+4138h+TmpHandler]
.text:00401105                 push    edx
.text:00401106                 call    Construct_Char
.text:0040110B                 lea     eax, [esp+4138h+TmpHandler]
.text:00401112                 lea     ecx, [esp+4138h+bignumber]
.text:00401119                 push    eax
.text:0040111A                 mov     byte ptr [esp+413Ch+Bool], 1
.text:00401122                 call    MultiplySelfKey
.text:00401127                 push    9
.text:00401129                 lea     ecx, [esp+413Ch+bignumber]
.text:00401130                 mov     esi, eax
.text:00401132                 call    MultiplyWith_

看到这里就是为了计算 Number = Origin*9*Origin*9,然后判断大数长度是否是奇数,

.text:0040114D                 lea     ecx, [esp+4138h+bignumber]
.text:00401154                 call    GetLength
.text:00401159                 and     eax, 80000001h
.text:0040115E                 jns     short loc_401165
.text:00401160                 dec     eax
.text:00401161                 or      eax, 0FFFFFFFEh
.text:00401164                 inc     eax
.text:00401165
.text:00401165 loc_401165:                             ; CODE XREF: _main+15Ej
.text:00401165                 cmp     eax, 1                               ;长度是否是奇数
.text:00401168                 jnz     loc_401215
.text:0040116E                 lea     ecx, [esp+4138h+bignumber]
.text:00401175                 call    GetLength
.text:0040117A                 sar     eax, 1
.text:0040117C                 push    eax
.text:0040117D                 lea     ecx, [esp+413Ch+bignumber]
.text:00401184                 call    GetVal                               ;取中位数
.text:00401189                 push    0
.text:0040118B                 lea     ecx, [esp+413Ch+TmpHandler]
.text:00401192                 mov     edi, eax
.text:00401194                 call    GetVal
.text:00401199                 cmp     edi, eax                            ;取首(个)位数,进行比较,需要满足相等
.text:0040119B                 lea     ecx, [esp+4138h+TmpHandler]
.text:004011A2                 jnz     short loc_40121C

最后是判断两个子数字的海明距离 HammingDist

.text:004011D0                 lea     ecx, [esp+4144h+TmpHandler]
.text:004011D7                 push    esi
.text:004011D8                 push    ecx
.text:004011D9                 lea     ecx, [esp+414Ch+bignumber]
.text:004011E0                 call    HammingDist
.text:004011E5                 push    1
.text:004011E7                 lea     ecx, [esp+413Ch+TmpHandler]
.text:004011EE                 mov     esi, eax
.text:004011F0                 call    GetLength
.text:004011F5                 dec     eax
.text:004011F6                 lea     edx, [esp+413Ch+TmpHandler]
.text:004011FD                 push    eax
.text:004011FE                 push    1
.text:00401200                 push    0
.text:00401202                 push    edx
.text:00401203                 lea     ecx, [esp+414Ch+bignumber]
.text:0040120A                 call    HammingDist
.text:0040120F                 add     esi, eax
.text:00401211                 jz      short loc_401257

当满足bigNumber.HammingDist(TmpHandler,...)判断大数前部分和后部分与原始数的海明距离之和为0时则转到well done的打印输出。

最后分析得到满足的条件是 Origin*9*Origin*9 = Origin[0:7] __ Origin[8] __ Origin[7:0]           条件 (1)

Origin是小端在前的表示方式,这里表述方式可能不太恰当。

即便 知道条件(1)-数字回文,想要计算出Origin仍然是比较困难的。我想到去搜索下“数字回文”

事实上,这个答案是缺8数,

12345679 × 81 x 12345679 12345679 x 999999999=1234567 8 9 8 7654321

缺8数乘以9的任意倍数可以得到该倍数的清一色

并且缺8数乘以多个9能到得到回文数字。

当缺8数乘以9x9能够得到清一色的999999999,

清一色的999999999与缺8数相乘,得到回文数字12345678987654321。

答案是12345679 ,要遵循BigNumber低位在前方式输入,97654321,well done。



[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

收藏
点赞1
打赏
分享
打赏 + 1.00雪花
打赏次数 1 雪花 + 1.00
 
赞赏  CCkicker   +1.00 2017/06/06
最新回复 (3)
雪    币: 3757
活跃值: (1757)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
梦野间 4 2017-6-5 17:06
2
0
楼主你好,我想问一下,这个类的结构是怎么分析出来的?
我分析的时候也是分析出有一个类,但对于怎么分析它的结构就没办法了
雪    币: 187
活跃值: (70)
能力值: ( LV5,RANK:155 )
在线值:
发帖
回帖
粉丝
流年似风 1 2017-6-7 12:51
3
0

C++和C我理解中的类class与结构structure的成员变量的内存布局是没有任何区别的,类多了一个通过编译器控制类的成员变量的访问。

先假设这个structure是一个很大的数组,这个数组可以存放任意类型的值。

类的地址通常是通过ecx传值的,

.text:004012C1                 mov     esi, ecx                                ;esi = this_class
.text:004012C3                 mov     dword ptr [esi], offset off_4080C8      ;this_class[0] = offset off_4080C8      
.text:004012C9                 call    ds:GetTickCount                          
.text:004012CF                 mov     ecx, esi                                 
.text:004012D1                 mov     [esi+200Ch], eax                        ;this_class[200C] = eax, tickcount
.text:004012D7                 mov     [esi+2008h], eax                        ;this_class[2008] = eax, tickcount
.text:004012DD                 call    shuffle_2_array

接下来分析 shuffle_2_array

.text:00401A66                 lea     edi, [esi+1008h]                        ;this_class[1008]
.text:00401A6C                 mov     ecx, edi
.text:00401A6E
.text:00401A6E loc_401A6E:                             ; CODE XREF: shuffle_2_array+22j
.text:00401A6E                 mov     edx, eax                                ;接下来的几行就是从this_class+1008开始使用0-400H依次初始化
.text:00401A70                 add     ecx, 4
.text:00401A73                 and     edx, 3FFh
.text:00401A79                 inc     eax
.text:00401A7A                 mov     [ecx-4], edx
.text:00401A7D                 cmp     eax, 400h
.text:00401A82                 jl      short loc_401A6E

分析类结构的关键是不要把它看成是一个类,要知道C++的类原型就是C的结构,C++的类就是C结构的语法升级版。

C++类的成员变量布局是按照类定义的顺序,加上对齐布局的,访问某一个变量时,通常是通过 [esi+offset member]的形式

当访问成员变量数组的某个位置时,通过 [esi + offset array + 4* index]的形式

当访问成员变量指针指向的对象时,通过 [ [ esi + offset pointer] ]的形式

等等

雪    币: 2
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zhuzhuxiaei 2017-11-26 14:08
4
0
厉害
游客
登录 | 注册 方可回帖
返回