首页
社区
课程
招聘
UPX代码buildloader函数分析
发表于: 2024-9-28 15:24 2120

UPX代码buildloader函数分析

2024-9-28 15:24
2120

buildloader函数代码存在于 src/p_w64pe_amd64.cpp文件中,主要功能是构建加载器,对PE文件后续的加载和运行提供良好的环境基础,因此研究UPX加壳逻辑,从这部分开始是一个很好的切入点。这个执行过程在PE文件打包过程中构建加载器,根据文件名可知是针对amd64架构下的PE文件。代码分析如下:

1.重新计算tlsindex

tlsindex 是一个与 TLS(Thread Local Storage,线程局部存储)有关的索引值。通过重新计算以保证TLS的正确性和有效性,确保能正确且安全地访问存储。

1
2
3
4
5
unsigned tmp_tlsindex = tlsindex;
const unsigned oam1 = ih.objectalign - 1;
const unsigned newvsize = (ph.u_len + rvamin + ph.overlap_overhead + oam1) & ~oam1;
if (tlsindex && ((newvsize - ph.c_len - 1024 + oam1) & ~oam1) > tlsindex + 4)
    tmp_tlsindex = 0;

ih.objectalign 表示对齐的边界,而ih.objectalign - 1 的操作是为了计算出对齐掩码,这样在后续的位运算(~oam1)中可以方便地进行对齐计算。
ph.u_len:表示程序头(或节)在内存中占用的长度,用于表示加载到内存后该节的大小。
rvamin:一般代表基础地址(base address),是 PE 文件在内存中加载的起始地址。
ph.overlap_overhead:指的是重叠开销(overlap overhead),用于表示在加载过程中的额外空间需求,例如由于对齐或重叠导致的额外字节。
这些变量的组合用于计算新的虚拟大小 (newvsize),确保在分配内存时考虑到所有的开销和对齐需求。
然后在一个判断语句中,验证tlsindex的有效性;而(newvsize - ph.c_len - 1024 + oam1) & ~oam1)计算潜在的内存使用量(并满足对齐要求),并检查是否大于当前 tlsindex 加上一个安全值 4(这里的 +4 是为了确保在使用 TLS 时有足够的空间,防止可能的溢出)。
如果 tlsindex 有效,并且经过计算后新的对齐值超过了 tlsindex + 4,则说明当前的 TLS 索引不再适用,因此清除或重置 TLS 索引,以避免在后续使用中出错。

2.初始化加载器并处理加载逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
initLoader(stub_amd64_win64_pe, sizeof(stub_amd64_win64_pe), 2);
    addLoader("START");
    if (ih.entry && isdll)
        addLoader("PEISDLL0");
    if (isefi)
        addLoader("PEISEFI0");
addLoader(isdll ? "PEISDLL1" : "", "PEMAIN01",
              icondir_count > 1 ? (icondir_count == 2 ? "PEICONS1" : "PEICONS2") : "",
              tmp_tlsindex ? "PETLSHAK" : "", "PEMAIN02",
              // ph.first_offset_found == 1 ? "PEMAIN03" : "",
              M_IS_LZMA(ph.method)    ? "LZMA_HEAD,LZMA_ELF00,LZMA_DEC20,LZMA_TAIL"
              : M_IS_NRV2B(ph.method) ? "NRV_HEAD,NRV2B"
              : M_IS_NRV2D(ph.method) ? "NRV_HEAD,NRV2D"
              : M_IS_NRV2E(ph.method) ? "NRV_HEAD,NRV2E"
                                      : "UNKNOWN_COMPRESSION_METHOD",
              // getDecompressorSections(),
              /*multipass ? "PEMULTIP" */ "", "PEMAIN10");
if (ft->id) {
        const unsigned texv = ih.codebase - rvamin;
        assert(ft->calls > 0);
        addLoader(texv ? "PECTTPOS" : "PECTTNUL");
        addLoader("PEFILTER49");
    }

调用 initLoader 函数初始化加载器,将 AMD64 架构的 Windows PE 文件的初始模板(stub_amd64_win64_pe)作为参数。
添加加载器的启动点START,并对不同的情况处理不同的加载逻辑:如果是dll文件,添加PEISDLL0;如果是efi文件,则添加PEISEFI0。
然后通过addLoader添加多个阶段的加载操作:PEMAINO1是主程序的入口点;根据图标数量添加对应的指令(PEICONS1、PEICONS2);如果 tmp_tlsindex 有效(不为 0),则添加指令 "PETLSHAK",用于处理 TLS,否则不添加;添加PEMAIN02,作为程序的下一个阶段;基于压缩方法的类型,根据多个条件判断,为头部添加相应的解压缩指令;添加PEMAIN10,表示主程序的最后阶段。
检查过滤器对象(ft)是否有效,如果有效,继续下一块代码。texv表示从代码基址(ih.codebase)到某个参考点(rvamin)的偏移量。检查 ft->calls (过滤器调用次数)是否大于 0,如果条件不成立(即无调用次数),程序将在调试模式终止。根据 texv 的值,添加不同的指令:如果 texv 非零,表示代码基址与参考点之间存在有效的偏移,则添加指令 "PECTTPOS";如果 texv 为零,则添加指令 "PECTTNUL",表示没有有效的偏移,不需要特殊处理。添加PEFILTER49指令,处理过滤器。

3.处理导入表和重定位表

1
2
3
4
5
6
7
8
9
10
11
12
if (soimport)
        addLoader("PEIMPORT", importbyordinal ? "PEIBYORD" : "", kernel32ordinal ? "PEK32ORD" : "",
                  importbyordinal ? "PEIMORD1" : "", "PEIMPOR2", isdll ? "PEIERDLL" : "PEIEREXE",
                  "PEIMDONE");
    if (sorelocs) {
        addLoader(soimport == 0 || soimport + cimports != crelocs ? "PERELOC1" : "PERELOC2",
                  "PERELOC3", big_relocs ? "REL64BIG" : "", "RELOC64J");
        if (0) {
            addLoader(big_relocs & 6 ? "PERLOHI0" : "", big_relocs & 4 ? "PERELLO0" : "",
                      big_relocs & 2 ? "PERELHI0" : "");
        }
    }

soimport表示导入表,如果存在则处理导入表部分,并根据是否按序号导入决定使用不同的加载器。
sorelocs表示导出表,如果存在则处理导出表部分,用于程序加载到内存中后修正地址。
都是对加载器执行初始化和处理逻辑。

4.其他加载部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if (use_dep_hack)
        addLoader("PEDEPHAK");
 
    // NEW: TLS callback support PART 1, the callback handler installation - Stefan Widmann
    if (use_tls_callbacks)
        addLoader("PETLSC");
 
    addLoader("PEMAIN20");
    if (use_clear_dirty_stack)
        addLoader("CLEARSTACK");
    addLoader("PEMAIN21");
 
    if (ih.entry && isdll)
        addLoader("PEISDLL9");
    if (isefi)
        addLoader("PEISEFI9");
    addLoader(ih.entry || !ilinker ? "PEDOJUMP" : "PERETURN");
 
    // NEW: TLS callback support PART 2, the callback handler - Stefan Widmann
    if (use_tls_callbacks)
        addLoader("PETLSC2");
 
    addLoader("IDENTSTR,UPX1HEAD");

在加载器中添加与依赖处理、TLS(线程局部存储)回调和程序执行相关的指令。分别有:
1)检查是否使用依赖处理黑客技术
2)检查是否启用 TLS 回调
3)添加指令 "PEMAIN20",标志着主程序的某一阶段
4)检查是否需要清理脏堆栈
5)添加指令 "PEMAIN21",继续主程序的执行流程
6)检测入口点且是 DLL 文件
7)检测 EFI 文件
8)检测入口点 (ih.entry) 或者没有链接器 (!ilinker),则添加指令 "PEDOJUMP";否则添加指令 "PERETURN"
9)再次检查是否启用 TLS 回调
10)添加指令 "IDENTSTR,UPX1HEAD",用于标识字符串和 UPX 压缩相关的处理。


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2024-9-28 20:06 被Bogger编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//