返回介绍

18.3 结构体 tm

发布于 2025-02-22 14:00:47 字数 7776 浏览 0 评论 0 收藏 0

18.3.1 linux

在 Linux 下,我们看看 time.h 中的 tm 结构体是什么样子的:

#!cpp
#include <stdio.h>
#include <time.h>
void main()
{
    struct tm t;
    time_t unix_time;
    unix_time=time(NULL);
    localtime_r (&unix_time, &t);
    printf ("Year: %d
", t.tm_year+1900);
    printf ("Month: %d
", t.tm_mon);
    printf ("Day: %d
", t.tm_mday);
    printf ("Hour: %d
", t.tm_hour);
    printf ("Minutes: %d
", t.tm_min);
    printf ("Seconds: %d
", t.tm_sec);
};

在 GCC 4.4.1 下编译得到:

清单 18.6:GCC 4.4.1

#!bash
main proc near
    push ebp
    mov ebp, esp
    and esp, 0FFFFFFF0h
    sub esp, 40h
    mov dword ptr [esp], 0 ; first argument for time()
    call time
    mov [esp+3Ch], eax
    lea eax, [esp+3Ch] ; take pointer to what time() returned
    lea edx, [esp+10h] ; at ESP+10h struct tm will begin
    mov [esp+4], edx ; pass pointer to the structure begin
    mov [esp], eax ; pass pointer to result of time()
    call localtime_r
    mov eax, [esp+24h] ; tm_year
    lea edx, [eax+76Ch] ; edx=eax+1900
    mov eax, offset format ; "Year: %d
"
    mov [esp+4], edx
    mov [esp], eax
    call printf
    mov edx, [esp+20h] ; tm_mon
    mov eax, offset aMonthD ; "Month: %d
"
    mov [esp+4], edx
    mov [esp], eax
    call printf
    mov edx, [esp+1Ch] ; tm_mday
    mov eax, offset aDayD ; "Day: %d
"
    mov [esp+4], edx
    mov [esp], eax
    call printf
    mov edx, [esp+18h] ; tm_hour
    mov eax, offset aHourD ; "Hour: %d
"
    mov [esp+4], edx
    mov [esp], eax
    call printf
    mov edx, [esp+14h] ; tm_min
    mov eax, offset aMinutesD ; "Minutes: %d
"
    mov [esp+4], edx
    mov [esp], eax
    call printf
    mov edx, [esp+10h]
    mov eax, offset aSecondsD ; "Seconds: %d
"
    mov [esp+4], edx ; tm_sec
    mov [esp], eax
    call printf
    leave
    retn
main endp

可是,IDA 并没有为本地栈上变量建立本地变量名。但是因为我们已经学了汇编了,我们也不需要在这么简单的例子里面如此依赖它。

请也注意一下 lea edx, [eax+76ch],这个指令把 eax 的值加上 0x76c,但是并不修改任何标记位。请也参考 LEA 的相关章节(B.6.2 节)

为了表现出结构体只是一个个的变量连续排列的东西,让我们重新测试一下这个例子,我们看看 time.h: 清单 18.7 time.h

#!cpp
struct tm
{
    int tm_sec;
    int tm_min;
    int tm_hour;
    int tm_mday;
    int tm_mon;
    int tm_year;
    int tm_wday;
    int tm_yday;
    int tm_isdst;
};
#include <stdio.h>
#include <time.h>
void main()
{
    int tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday, tm_isdst;
    time_t unix_time;
    unix_time=time(NULL);
    localtime_r (&unix_time, &tm_sec);
    printf ("Year: %d
", tm_year+1900);
    printf ("Month: %d
", tm_mon);
    printf ("Day: %d
", tm_mday);
    printf ("Hour: %d
", tm_hour);
    printf ("Minutes: %d
", tm_min);
    printf ("Seconds: %d
", tm_sec);
};

注:指向 tm_sec 的指针会传递给 localtime_r,或者说第一个“结构体”元素。 编译器会这么警告我们

清单 18.8 GCC4.7.3

#!bash
GCC_tm2.c: In function ’main’:
GCC_tm2.c:11:5: warning: passing argument 2 of ’localtime_r’ from incompatible pointer type [
enabled by default]
In file included from GCC_tm2.c:2:0:
/usr/include/time.h:59:12: note: expected ’struct tm *’ but argument is of type ’int *’

但是至少,它会生成这段代码:

清单 18.9 GCC 4.7.3

#!bash
main proc near
    var_30 = dword ptr -30h
    var_2C = dword ptr -2Ch
    unix_time = dword ptr -1Ch
    tm_sec = dword ptr -18h
    tm_min = dword ptr -14h
    tm_hour = dword ptr -10h
    tm_mday = dword ptr -0Ch
    tm_mon = dword ptr -8
    tm_year = dword ptr -4
    push ebp
    mov ebp, esp
    and esp, 0FFFFFFF0h
    sub esp, 30h
    call __main
    mov [esp+30h+var_30], 0 ; arg 0
    mov [esp+30h+unix_time], eax
    lea eax, [esp+30h+tm_sec]
    mov [esp+30h+var_2C], eax
    lea eax, [esp+30h+unix_time]
    mov [esp+30h+var_30], eax
    call localtime_r
    mov eax, [esp+30h+tm_year]
    add eax, 1900
    mov [esp+30h+var_2C], eax
    mov [esp+30h+var_30], offset aYearD ; "Year: %d
"
    call printf
    mov eax, [esp+30h+tm_mon]
    mov [esp+30h+var_2C], eax
    mov [esp+30h+var_30], offset aMonthD ; "Month: %d
"
    call printf
    mov eax, [esp+30h+tm_mday]
    mov [esp+30h+var_2C], eax
    mov [esp+30h+var_30], offset aDayD ; "Day: %d
"
    call printf
    mov eax, [esp+30h+tm_hour]
    mov [esp+30h+var_2C], eax
    mov [esp+30h+var_30], offset aHourD ; "Hour: %d
"
    call printf
    mov eax, [esp+30h+tm_min]
    mov [esp+30h+var_2C], eax
    mov [esp+30h+var_30], offset aMinutesD ; "Minutes: %d
"
    call printf
    mov eax, [esp+30h+tm_sec]
    mov [esp+30h+var_2C], eax
    mov [esp+30h+var_30], offset aSecondsD ; "Seconds: %d
"
    call printf
    leave
    retn
main endp

这个代码和我们之前看到的一样,依然无法分辨出源代码是用了结构体还是只是数组而已。

当然这样也是可以运行的,但是实际操作中还是不建议用这种晦涩的方法。因为通常,编译器会在栈上按照声明顺序分配变量空间,但是并不能保证每次都是这样。

还有,其他编译器可能会警告 tm_year,tm_mon, tm_mday, tm_hour, tm_min 变量而不是 tm_sec 使用时未初始化。事实上,计算机并不知道调用 localtime_r() 的时候他们会被自动填充上。

我选择了这个例子来解释是因为他们都是 int 类型的,而 SYSTEMTIME 的所有成员是 16 位的 WORD,如果把它们作为本地变量来声明的话,他们会按照 32 位的边界值来对齐,因此什么都用不了了(因为由于数据对齐,此时 GetSystemTime() 会把它们错误的填充起来)。请继续读下一节的内容:“结构体的成员封装”。

所以,结构体只是把一组变量封装到一个位置上,数据是一个接一个的。我可以说结构体是一个语法糖,因为它只是用来让编译器把一组变量保存在一个地方。但是,我不是编程方面的专家,所以更有可能的是,我可能会误读这个术语。还有,在早期(1972 年以前)的时候,C 是不支持结构体的。

18.3.2 ARM+优化 Keil+thumb 模式

同样的例子: 清单 18.10: 优化 Keil+thumb 模式

#!bash
var_38 = -0x38
var_34 = -0x34
var_30 = -0x30
var_2C = -0x2C
var_28 = -0x28
var_24 = -0x24
timer = -0xC
    PUSH {LR}
    MOVS R0, #0 ; timer
    SUB SP, SP, #0x34
    BL time
    STR R0, [SP,#0x38+timer]
    MOV R1, SP ; tp
    ADD R0, SP, #0x38+timer ; timer
    BL localtime_r
    LDR R1, =0x76C
    LDR R0, [SP,#0x38+var_24]
    ADDS R1, R0, R1
    ADR R0, aYearD ; "Year: %d
"
    BL __2printf
    LDR R1, [SP,#0x38+var_28]
    ADR R0, aMonthD ; "Month: %d
"
    BL __2printf
    LDR R1, [SP,#0x38+var_2C]
    ADR R0, aDayD ; "Day: %d
"
    BL __2printf
    LDR R1, [SP,#0x38+var_30]
    ADR R0, aHourD ; "Hour: %d
"
    BL __2printf
    LDR R1, [SP,#0x38+var_34]
    ADR R0, aMinutesD ; "Minutes: %d
"
    BL __2printf
    LDR R1, [SP,#0x38+var_38]
    ADR R0, aSecondsD ; "Seconds: %d
"
    BL __2printf
    ADD SP, SP, #0x34
    POP {PC}

18.3.3 ARM+优化 Xcode(LLVM)+thumb-2 模式

IDA“碰巧知道”tm 结构体(因为 IDA“知道”例如 localtime_r() 这些库函数的参数类型),所以他把这里的结构变量的名字也显示出来了。

#!bash
var_38 = -0x38
var_34 = -0x34
    PUSH {R7,LR}
    MOV R7, SP
    SUB SP, SP, #0x30
    MOVS R0, #0 ; time_t *
    BLX _time
    ADD R1, SP, #0x38+var_34 ; struct tm *
    STR R0, [SP,#0x38+var_38]
    MOV R0, SP ; time_t *
    BLX _localtime_r
    LDR R1, [SP,#0x38+var_34.tm_year]
    MOV R0, 0xF44 ; "Year: %d
"
    ADD R0, PC ; char *
    ADDW R1, R1, #0x76C
    BLX _printf
    LDR R1, [SP,#0x38+var_34.tm_mon]
    MOV R0, 0xF3A ; "Month: %d
"
    ADD R0, PC ; char *
    BLX _printf
    LDR R1, [SP,#0x38+var_34.tm_mday]
    MOV R0, 0xF35 ; "Day: %d
"
    ADD R0, PC ; char *
    BLX _printf
    LDR R1, [SP,#0x38+var_34.tm_hour]
    MOV R0, 0xF2E ; "Hour: %d
"
    ADD R0, PC ; char *
    BLX _printf
    LDR R1, [SP,#0x38+var_34.tm_min]
    MOV R0, 0xF28 ; "Minutes: %d
"
    ADD R0, PC ; char *
    BLX _printf
    LDR R1, [SP,#0x38+var_34]
    MOV R0, 0xF25 ; "Seconds: %d
"
    ADD R0, PC ; char *
    BLX _printf
    ADD SP, SP, #0x30
    POP {R7,PC}
...
00000000 tm struc ; (sizeof=0x2C, standard type)
00000000 tm_sec DCD ?
00000004 tm_min DCD ?
00000008 tm_hour DCD ?
0000000C tm_mday DCD ?
00000010 tm_mon DCD ?
00000014 tm_year DCD ?
00000018 tm_wday DCD ?
0000001C tm_yday DCD ?
00000020 tm_isdst DCD ?
00000024 tm_gmtoff DCD ?
00000028 tm_zone DCD ? ; offset
0000002C tm ends

清单 18.11: ARM+优化 Xcode(LLVM)+thumb-2 模式

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文