Logo WP

EzRand

和seed爆了!

文件{:download="ez_rand.exe"}

扔进IDA里分析,发现是一个简单的异或运算比较

if (加密的flag[i] != 加密后的输入[i]) 退出程序

但是这里的加密算法比较奇怪,是“随机的”

srand(time(NULL)); // 设置随机种子

for (...) {
    v = rand();
    加密后的输入 = 函数(v)
}

经过李师傅的提醒

.text:00007FF763EA1189 xor     ecx, ecx        ; Time
.text:00007FF763EA118B mov     [rsp+70h+arg_0], rsi
.text:00007FF763EA1193 call    cs:_time64      ; Indirect Call Near Procedure
.text:00007FF763EA1199 movzx   ecx, ax         ; Seed
.text:00007FF763EA119C call    cs:srand        ; Indirect Call Near Procedure

这里的 ax 是个16位寄存器,即范围是 0~FFFF

以及这里是个 movzx,可以将源操作数的内容拷贝到目的操作数,并将该值0扩展至16位或者32位

所以完全可以爆破 srand

补充知识

  1. 对于前8个通用寄存器,即eax, ebx, ecx, edx, esi, edi, esp和ebp
  • 不同的前缀用于区分寄存器的位数(64, 32或16)。

    64位寄存器以r前缀, 32位则以e为前缀,16位则无前缀。

    例如,ax的64位寄存器是rax,32位寄存器是eax,16位则是ax。

  • 不同的后缀用于区分寄存器低16位中的高8位和低8位。

    高8位以h为后缀,低8位以l为后缀。例如, ax的高8位是ah, 低8位是al

  1. 对于r8至r15的寄存器,无后缀表示64位,32位以d为后缀,16位以w为后缀,8位以b为后缀。

例如, r15表示64位,r15d表示32位,r15w表示16位,r16b表示8位。

有一点需要注意,r8至r15不像前8个通用寄存器是没有低16位中高8位的表示法的。

需要注意的点#

正确的编译工具#

同样的代码,不同编译工具下的程序运行结果可能完全不同,特别是像 rand()

这里使用的是 MSVC 19.35 工具链,使用 VS。

最后的代码#

其实大部分是从IDA F5复制过来的

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

int main()
{
    int v9[7];
    v9[0] = -362017699;
    v9[1] = 888936774;
    v9[2] = 119759538;
    v9[3] = -76668318;
    v9[4] = -1443698508;
    v9[5] = -2044652911;
    v9[6] = 1139379931;
    char* cv9 = (char*)v9;
    char result[31];
    for (int seed = 0; seed <= 0xFFFF; seed++)
    {
        srand(seed);

        for (int i = 0; i < 30; i++) 
        {
            int v7 = rand();
            unsigned __int8 num = (unsigned __int8)(v7
                + ((((unsigned __int64)(2155905153i64 * v7) >> 32) & 0x80000000) != 0i64)
                + ((int)((unsigned __int64)(2155905153i64 * v7) >> 32) >> 7));
            result[i] = cv9[i] ^ num;
        }

        if (strstr(result, "XYCTF"))
        {
            printf("%X-%s\n", seed, result);
        }
    }
}

小知识#

2024-04-11更新

这里的一大坨运算代码

(unsigned __int8)(v7
                + ((((unsigned __int64)(2155905153i64 * v7) >> 32) & 0x80000000) != 0i64)
                + ((int)((unsigned __int64)(2155905153i64 * v7) >> 32) >> 7))

其实是MSVC编译器对 % 255 优化后的结果

汇编代码为:

.text:00007FF763EA11B9                 mov     eax, 80808081h
.text:00007FF763EA11BE                 imul    r8d             ; Signed Multiply
.text:00007FF763EA11C1                 add     edx, r8d        ; Add
.text:00007FF763EA11C4                 sar     edx, 7          ; Shift Arithmetic Right
.text:00007FF763EA11C7                 mov     ecx, edx
.text:00007FF763EA11C9                 shr     ecx, 1Fh        ; Shift Logical Right
.text:00007FF763EA11CC                 add     edx, ecx        ; Add
.text:00007FF763EA11CE                 imul    ecx, edx, 0FFh  ; Signed Multiply
.text:00007FF763EA11D4                 sub     r8d, ecx        ; Integer Subtraction
.text:00007FF763EA11D7                 xor     r8b, byte ptr [rbp+rsi+var_30] ; Logical Exclusive OR

使用 CompilerExplorer 对比

#include <stdio.h>

int main() {
    int b = 0;
    scanf("%d", &b);
    int a = b % 255;
    printf("%d", a);
}

编译器选择 x64 msvc v19.35,编译优化选择 /Ox 或以上(默认 /Od 不会有优化)

mov     ecx, DWORD PTR b$[rsp]
mov     eax, -2139062143              ; ffffffff80808081H
imul    ecx
add     edx, ecx
sar     edx, 7
mov     eax, edx
shr     eax, 31
add     edx, eax
imul    eax, edx, 255                     ; 000000ffH
sub     ecx, eax

一毛一样