质数筛法的分析

前言

同学写了一小段C代码,编译成Release版给大家玩.
在IDA中分析了一次, 记录一下.

记录

.text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401000 _main           proc near               ; CODE XREF: start+AFp
.text:00401000
.text:00401000 var_4           = dword ptr -4
.text:00401000 argc            = dword ptr  4
.text:00401000 argv            = dword ptr  8
.text:00401000 envp            = dword ptr  0Ch
.text:00401000
.text:00401000                 push    ecx
.text:00401001                 push    ebx
.text:00401002                 push    ebp
.text:00401003                 push    esi
.text:00401004                 push    edi
.text:00401005 ;save
.text:00401005                 push    100             ; unsigned int
.text:00401007 pcAry100 = ebp                          ; new出来的char数组, 100个字节
.text:00401007                 call    ??2@YAPAXI@Z    ; operator new(uint)
.text:0040100C ;new出来的数组清0
.text:0040100C                 mov     pcAry100, eax
.text:0040100E                 mov     ecx, 25
.text:00401013                 xor     eax, eax
.text:00401015                 mov     edi, pcAry100
.text:00401017                 rep stosd
.text:00401019 ;开始用数组
.text:00401019 piAry_25_cur = ebx
.text:00401019 iIndex = eax
.text:00401019                 mov     iIndex, 1
.text:0040101E                 add     esp, 4          ; new 函数的平栈, 流水线优化时放在这的
.text:00401021                 sub     iIndex, pcAry100
.text:00401023                 mov     piAry_25_cur, pcAry100
.text:00401025                 mov     edi, 3          ; 筛法,从3开始
.text:0040102A                 mov     [esp+10h], iIndex ; 保存数组首地址取反+1,加上数组首地址, 作为位置索引
.text:0040102A                                         ; 等于是一个间接的位置索引,加上数组首地址后,就是位置索引
.text:0040102E
.text:0040102E L_JUDGE_PRIME:                          ; CODE XREF: _main+6Cj
.text:0040102E                 cmp     byte ptr [piAry_25_cur], 0
.text:00401031                 jnz     short L_GET_NEXT_ODD_NUMBER ; char数组初始化时,全部初始化为0,假设全部为质数
.text:00401031                                         ; 标记为1时, 说明不是质数, 已经判断过.
.text:00401033                 lea     ecx, [iIndex+piAry_25_cur]
.text:00401036                 cmp     ecx, 100        ; 本程序判断的是0~99之间,从3开始的奇数是否为质数
.text:00401036                                         ; 如果 i >= 100, 过了char数组的上界, 不判断了
.text:00401039                 jge     short L_GET_NEXT_ODD_NUMBER
.text:0040103B ;数组当前值为0, 且Index < 100
.text:0040103B                 lea     esi, [piAry_25_cur+1] ; ary next position
.text:0040103E                 lea     ecx, [edi+2]    ; 偶数不是质数,只有奇数才有可能是质数.从3开始,判断每次加2的数是否为质数
.text:00401041 用奇数试除来确定质数
.text:00401041
.text:00401041 L_TEST_DIV_ODD:                         ; CODE XREF: _main+5Cj
.text:00401041                 cmp     byte ptr [esi], 1
.text:00401044                 jz      short L_NEED_NOT_JUDGE ; 为1时,说明已经判断过,不是质数
.text:00401046                 mov     iIndex, ecx
.text:00401048                 cdq
.text:00401049                 idiv    edi             ; eax / edi => eax:edx => 商:余数
.text:0040104B                 test    edx, edx
.text:0040104D                 jnz     short L_NEED_NOT_JUDGE ; 如果非0, 说明是不是能整除的数,有可能是质数,去试除下一个奇数
.text:0040104F                 mov     byte ptr [esi], 1 ; 余数为0, 被整除了, 不是质数
.text:0040104F                                         ; 将筛法数组中,能被当前奇数整除的奇数全部标记为非质数
.text:00401052
.text:00401052 L_NEED_NOT_JUDGE:                       ; CODE XREF: _main+44j
.text:00401052                                         ; _main+4Dj
.text:00401052                 add     ecx, 2
.text:00401055                 inc     esi
.text:00401056                 cmp     ecx, 203        ; 这个最大奇数怎么确定的? 如果不是公式搞出来的, 就没意义了.
.text:0040105C                 jl      short L_TEST_DIV_ODD
.text:0040105E                 mov     iIndex, [esp+10h] ; 恢复数组索引
.text:00401062 ;计算下一个位置
.text:00401062
.text:00401062 L_GET_NEXT_ODD_NUMBER:                  ; CODE XREF: _main+31j
.text:00401062                                         ; _main+39j
.text:00401062                 add     edi, 2
.text:00401065                 inc     piAry_25_cur
.text:00401066                 cmp     edi, 203
.text:0040106C                 jl      short L_JUDGE_PRIME
.text:0040106E ;开始打印了
.text:0040106E iIndex1 = edi
.text:0040106E                 xor     iIndex1, iIndex1
.text:00401070                 mov     esi, 3          ; 偶数位置肯定不是质数, 从3开始
.text:00401075
.text:00401075 L_PRINT_PRIME:                          ; CODE XREF: _main+93j
.text:00401075                 cmp     byte ptr [iIndex1+pcAry100], 0 ; 用的筛法么?
.text:00401075                                         ; 该数组元素为1,不是质数
.text:00401075                                         ; 该数组元素为0,是质数
.text:00401075                                         ; 作者定义了2个宏
.text:00401079                 jnz     short L_FIND_NEXT_PRIME ; 偶数位置肯定不是质数, 从3开始,每次+2的数组元素做质数判断
.text:0040107B                 push    esi
.text:0040107C                 push    offset Format   ; "%d\n"
.text:00401081                 call    _printf         ; 打印质数
.text:00401086                 add     esp, 8
.text:00401089
.text:00401089 L_FIND_NEXT_PRIME:                      ; CODE XREF: _main+79j
.text:00401089                 add     esi, 2          ; 偶数位置肯定不是质数, 从3开始,每次+2的数组元素做质数判断
.text:0040108C                 inc     iIndex1
.text:0040108D                 cmp     esi, 203        ; 最大打印的质数为203
.text:00401093                 jl      short L_PRINT_PRIME ; 用的筛法么?
.text:00401093                                         ; 该数组元素为1,不是质数
.text:00401093                                         ; 该数组元素为0,是质数
.text:00401093                                         ; 作者定义了2个宏
.text:00401095 ;释放资源
.text:00401095                 push    pcAry100        ; lpMem
.text:00401096                 call    ??3@YAXPAX@Z    ; operator delete(void *)
.text:0040109B                 add     esp, 4
.text:0040109E ;restore
.text:0040109E                 pop     edi
.text:0040109F                 pop     esi
.text:004010A0                 pop     ebp
.text:004010A1                 pop     ebx
.text:004010A2                 pop     ecx
.text:004010A3                 retn
.text:004010A3 _main           endp

逆向结果

如果逆向的结果可以被正确理解&&可以自由调试, 可以整理的可读性好些.
如果算法不可理解,逆向结果整理一下就可以了.

// hw.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include 
#include 
#include 

#define MAX_PRIME 203
#define SECOND_ODD 3 // 第2个奇数, 质数从3开始求的,如果要打印3之前的质数,直接打印,不用算了
#define TWO 2 // 一次加2个, 从 SECOND_ODD 开始

int main(int argc, char* argv[])
{
    int iIndexOffset = 0; ///< 当前位置的间接索引
    int iTemp = 0;

    char* pcPrime = new char[100]; ///< 存放的是奇数位置索引
    ZeroMemory(pcPrime, 100); ///< 假设初始都是质数

    char* pcPrimeCur = pcPrime;
    char* pcPrimeNext = NULL; ///< pcPrimeCur的下一个位置
    iIndexOffset = ~(int)pcPrime + 1;

    // 筛法求质数
    for (int iDivisor = SECOND_ODD; 
        iDivisor < MAX_PRIME; 
        iDivisor += TWO, pcPrimeCur++)
    {
        if ((0 == *pcPrimeCur) && (((int)pcPrimeCur + iIndexOffset) < 100)) {
            for (pcPrimeNext = pcPrimeCur + 1, iTemp = iDivisor + TWO;
                iTemp < MAX_PRIME;
                iTemp += TWO, pcPrimeNext++)
            {
                if (1 != *pcPrimeNext) {
                    if (0 == (iTemp % iDivisor)) {
                        // 被整除,已经排除是质数
                        *pcPrimeNext = 1;
                    }
                }
            }
        }
    }

    // 打印质数
    for (iIndexOffset = 0, iTemp = SECOND_ODD;
        iTemp < MAX_PRIME, iIndexOffset < 100; 
        iIndexOffset++, iTemp += TWO)
    {
        if (0 == *(pcPrime + iIndexOffset)) {
            printf("%d\n", iTemp);
        }
    }

    if (NULL != pcPrime) {
        delete pcPrime;
        pcPrime = NULL;
    }

    system("pause");
    return 0;
}

你可能感兴趣的:(质数筛法的分析)