Win32ASM学习[6]: PTR、OFFSET、ADDR、THIS

本文详细解析了数据尺寸指定、内存赋值、偏移与地址的区别及使用场景,通过具体代码实例深入探讨了如何高效进行数据操作与内存赋值,包括对变量的读取、修改和内存地址的正确应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

PTR: 指定要操作的数据尺寸

------------------------------------------------------------------------------------------------------------------------------------------

.386
.model flat, stdcall

include    windows.inc
include    kernel32.inc
include    masm32.inc
include    debug.inc
includelib kernel32.lib
includelib masm32.lib
includelib debug.lib

.data
    val db 11h, 22h, 33h, 44h, 55h, 66h, 77h, 88h

.code
main proc
    xor eax, eax                    ;清空 EAX, 同 mov eax, 0
    mov eax, dword ptr val   ;
    PrintHex eax                   ;44332211
   
    xor eax, eax                      ;
    mov eax, dword ptr val+1 ;
    PrintHex eax                      ;55443322
   
    xor eax, eax             ;
    mov ax, word ptr val     ;
    PrintHex eax             ;00002211
   
    xor eax, eax             ;
    mov al, byte ptr val     ;
    PrintHex eax             ;00000011
    ret
main endp
end main

------------------------------------------------------------------------------------------------------------------------------------------

OFFSET 和 ADDR 的异同:
1、offset 不能获取局部变量的地址;
2、addr 只能用于调用函数(invoke)时, 不能用于赋值操作;
3、addr 面对局部变量时会转换为 lea 等指令, addr 面对全局变量时则直接调用 offset;
4、在 invoke 中应尽量使用 addr, 其他只用 offset.

------------------------------------------------------------------------------------------------------------------------------------------

.386
.modelflat,
stdcall

include
   windows.inc

include
   kernel32.inc
include
   masm32.inc
include
   debug.inc
includelib
kernel32.lib
includelib
masm32.lib
includelib
debug.lib

.data
    TextAddr equ this byte  ;伪指令 this 可让当前变量和下一个变量同址
    szText db'Asm', 0
.code
main proc
    PrintHex offset szText  ;00403000
    PrintHex offset TextAddr ;00403000
   
    PrintString szText      ;Asm
    mov [TextAddr], 'a'     ;给 TextAddr 赋值
    PrintString szText      ;asm
    ret
main
endp
end
main

--------------------------------------------------------------------------------------------------------------------

同址异名

struct task_struct_offset { int16_t pid_offset; int16_t tgid_offset; int16_t thread_pid_offset; int16_t ptracer_cred_offset; int16_t real_cred_offset; int16_t cred_offset; int16_t comm_offset; int16_t fs_offset; int16_t files_offset; int16_t loginuid_offset; int16_t sessionid_offset; int16_t seccomp_offset; int16_t security_offset; int16_t stack_offset; int16_t tasks_offset; int16_t mm_offset; int16_t active_mm_offset; }; int resolve_current() { uint64_t sp_el0, sp; asm volatile("mrs %0, sp_el0" : "=r"(sp_el0)); asm volatile("mov %0, sp" : "=r"(sp)); sp_el0_is_current = 0; sp_el0_is_thread_info = 0; init_task = (struct task_struct *)kallsyms_lookup_name("init_task"); uint64_t init_thread_union_addr = kallsyms_lookup_name("init_thread_union"); if (is_kimg_range(sp_el0)) { if (sp_el0 == init_thread_union_addr) { sp_el0_is_thread_info = 1; } else if ((uint64_t)init_task == sp_el0 || (sp_el0 & (page_size - 1)) || (task_struct_offset.comm_offset = find_swapper_comm_offset(sp_el0, TASK_STRUCT_MAX_SIZE)) > 0) { sp_el0_is_current = 1; init_task = (struct task_struct *)sp_el0; } else { sp_el0_is_thread_info = 1; } } int thread_shift_cand[] = { 14, 15, 16 }; for (int i = 0; i < sizeof(thread_shift_cand) / sizeof(thread_shift_cand[0]); i++) { int tsz = 1 << thread_shift_cand[i]; uint64_t sp_low = sp & ~(tsz - 1); uint64_t psp = sp_low; for (; psp < sp_low + THREAD_INFO_MAX_SIZE; psp += 8) { if (*(uint64_t *)psp == STACK_END_MAGIC) { if (psp == sp_low) { thread_size = tsz; stack_end_offset = 0; thread_info_in_task = 1; } else { thread_size = tsz; stack_end_offset = psp - sp_low; thread_info_in_task = 0; } break; } } } if (!thread_info_in_task) { uint64_t thread_info_addr = (uint64_t)current_thread_info_sp(); if (init_task) { for (uint64_t ptr = thread_info_addr; ptr < thread_info_addr + stack_end_offset; ptr += sizeof(uint64_t)) { uint64_t pv = *(uint64_t *)ptr; if (pv == (uint64_t)init_task) { task_in_thread_info_offset = ptr - thread_info_addr; break; } } } else { for (uint64_t ptr = thread_info_addr; ptr < thread_info_addr + stack_end_offset; ptr += sizeof(uint64_t)) { uint64_t pv = *(uint64_t *)ptr; task_struct_offset.comm_offset = find_swapper_comm_offset(pv, TASK_STRUCT_MAX_SIZE); if (task_struct_offset.comm_offset > 0) { init_task = (struct task_struct *)pv; task_in_thread_info_offset = ptr - thread_info_addr; } } } } if (task_struct_offset.comm_offset <= 0) { task_struct_offset.comm_offset = find_swapper_comm_offset((uint64_t)init_task, TASK_STRUCT_MAX_SIZE); } uint64_t stack_base = (sp & ~(thread_size - 1)); for (uintptr_t i = (uintptr_t)init_task; i < (uintptr_t)init_task + TASK_STRUCT_MAX_SIZE; i += sizeof(uintptr_t)) { uintptr_t val = *(uintptr_t *)i; if (stack_base == val) { stack_in_task_offset = i - (uintptr_t)init_task; task_struct_offset.stack_offset = stack_in_task_offset; break; } } return 0; }请问tasks_offset应该按照这样的代码方法获取呢?
03-23
<think>嗯,用户问的是关于如何获取`tasks_offset`的代码方法,根据提供的代码来看,用户可能是在处理Linux内核中的任务结构体`task_struct`的偏移量。首先,我需要理解用户提供的代码结构,特别是`resolve_current`函数的作用。 首先,`struct task_struct_offset`包含多个字段的偏移量,比如`pid_offset`、`tgid_offset`等,用户想知道其中的`tasks_offset`应该如何获取。在提供的代码中,`resolve_current`函数似乎在通过不同的方法确定这些偏移量,比如通过查找`init_task`、检查堆栈指针等。 接下来,我需要回忆Linux内核中`task_struct`的结构。`tasks`字段是一个链表节点,用于将所有的任务结构体连接起来,形成双向链表。每个`task_struct`中的`tasks`字段的偏移量可能因内核版本或配置不同而有所变化,因此需要动态获取。 在提供的代码中,`resolve_current`函数通过查找`init_task`(即初始进程的`task_struct`)并分析其内存布局来确定各个字段的偏移量。例如,`comm_offset`是通过查找"swapper"字符串的位置来确定的。类似地,`stack_offset`是通过匹配堆栈基地址来找到的。 那么,`tasks_offset`的获取可能需要类似的方法。可能的步骤包括: 1. **遍历`task_struct`内存区域**:从`init_task`开始,扫描其内存,寻找指向其他`task_struct`的`tasks`节点的指针。因为`tasks`是一个`list_head`结构,包含`next`和`prev`指针,可以检查这些指针是否指向有效的`task_struct`地址。 2. **验证链表结构**:确认找到的`tasks`节点确实属于`init_task`的下一个或上一个进程,例如`init_task`的下一个任务可能是它自己,因为初始进程的链表可能是一个循环。 3. **计算偏移量**:一旦找到`list_head`结构在`task_struct`中的位置,就可以计算出`tasks_offset`的值。 此外,可能需要考虑内核的链表实现方式,`list_head`通常包含两个指针,所以需要确保找到的是正确的结构体成员。同时,要考虑内核的可能的对齐方式和填充,这可能会影响偏移量的计算。 在用户提供的代码中,`resolve_current`已经处理了`comm_offset`和`stack_offset`,类似的方法可能适用于`tasks_offset`。可能需要添加类似`find_tasks_offset`的函数,遍历`init_task`的内存,查找指向其他任务结构体的指针,从而确定`tasks`字段的位置。 需要注意的是,这种方法可能依赖于内核符号的可用性,例如`init_task`的地址是否可以通过`kallsyms_lookup_name`获取。此外,不同内核版本中`task_struct`的布局可能不同,因此动态确定偏移量是必要的。 总结来说,获取`tasks_offset`的方法可能包括: - 使用`init_task`作为起点。 - 遍历其内存区域,查找符合`list_head`结构的字段。 - 验证该字段是否指向有效的任务结构体。 - 计算该字段相对于`task_struct`起始地址的偏移量,并赋值给`tasks_offset`。 可能需要编写类似于`find_swapper_comm_offset`的辅助函数,专门用于定位`tasks`字段的位置。同时,处理不同内核版本和配置的兼容性问题,确保偏移量获取的准确性。</think>根据提供的代码逻辑,获取`tasks_offset`需要结合内核`task_struct`结构特征和动态探测技术。以下是逐步分析及实现方法: --- ### **一、原理分析** `tasks_offset`对应`struct task_struct`中`tasks`成员的偏移量。该成员是`list_head`类型,用于链接所有进程的链表。关键点如下: 1. **链表结构特性** `tasks`字段是双向链表节点,满足以下条件: $$tasks.next->prev == tasks.prev->next == tasks$$ 2. **init_task的特殊性** 内核初始进程`init_task`的`tasks`链表通常指向自身,形成循环: $$init_task.tasks.next = init_task.tasks.prev = &init_task.tasks$$ --- ### **二、实现步骤** #### **步骤 1:定位`init_task`地址** 代码已通过`kallsyms_lookup_name("init_task")`获取: ```c init_task = (struct task_struct *)kallsyms_lookup_name("init_task"); ``` #### **步骤 2:遍历`task_struct`内存区域** 从`init_task`起始地址开始,逐字节扫描可能包含`list_head`结构的位置: ```c for (uintptr_t ptr = (uintptr_t)init_task; ptr < (uintptr_t)init_task + TASK_STRUCT_MAX_SIZE; ptr += sizeof(uintptr_t)) { // 检查当前指针是否指向有效的list_head结构 } ``` #### **步骤 3:验证链表有效性** 检查`next`和`prev`指针是否满足链表特性: ```c struct list_head *candidate = (struct list_head *)ptr; if (candidate->next->prev == candidate && candidate->prev->next == candidate) { // 进一步验证是否属于init_task的tasks链表 if (candidate->next == &init_task->tasks || candidate->prev == &init_task->tasks) { task_struct_offset.tasks_offset = ptr - (uintptr_t)init_task; break; } } ``` #### **步骤 4:处理特殊边界** - **内核版本差异**:某些内核可能通过`container_of`宏隐藏实际链表地址,需调整计算方式。 - **内存对齐**:优先检查按`sizeof(struct list_head)`对齐的地址。 --- ### **三、代码整合建议** 在现有`resolve_current()`函数中补充以下逻辑: ```c // 在确定init_task后添加: if (init_task) { for (uintptr_t ptr = (uintptr_t)init_task; ptr < (uintptr_t)init_task + TASK_STRUCT_MAX_SIZE; ptr += sizeof(uintptr_t)) { struct list_head *candidate = (struct list_head *)ptr; if (candidate->next->prev == candidate && candidate->prev->next == candidate) { // 确认链表指向init_task自身 if (candidate->next == candidate->prev && candidate->next == (struct list_head *)(ptr)) { task_struct_offset.tasks_offset = ptr - (uintptr_t)init_task; break; } } } } ``` --- ### **四、注意事项** 1. **内存安全性** 直接访问内核内存需确保地址有效性,避免触发缺页异常。可通过`probe_kernel_read()`等安全函数替代直接解引用。 2. **性能优化** 实际实现中可结合`comm`、`stack`等已知偏移量缩小搜索范围。 3. **跨版本兼容性** 不同内核版本中`tasks`位置可能变化,建议结合多个特征字段交叉验证。 --- 通过上述方法,可动态获取`tasks_offset`值,适配不同内核配置和版本。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值