【软件逆向-Windows10代码还原汇编特征汇总(附NTDLL CreateHeap还原代码)】此文章归类为:软件逆向。
大家好,我是CR49期的一名学员,我写这篇文章的目的是想帮助刚开始或者准备开始研究windows系统的人员,此篇文章仅作为经验分享,是我在逆向还原windows10堆API CreateHeap
和AllocateHeap
以及AllocateHeapInternal
源代码时总结的一些经验,如有错误请各位前辈斧正。我们仅分享其windows10 ntdll中常见的一些汇编特征优化,如若不涉及的优化请各位自行翻阅资料学习。
分析环境: Windows11
工具: Windbg, IDA7.5
分析目标平台: Windows10
分析目标文件: C:\Windows\SysWow64\ntdll.dll(32位)
版本信息:10.0.19041.5007 (WinBuild.160101.0800)
SHA-1:9C3A55D17C022D7B32EE558E8941C4C9938696CA
对于常见的Release版编译的优化此篇文章涉及到的有
CPU流水线优化
真正的CPU流水线优化有许多概念且十分复杂,但是体现在代码中的我们只需要关注一些会影响我们分析的内容,比如乱序执行我们以最经典的三级流水线为例举出例子方便大家简易回顾一下流水线优化。如下三个操作分别由三个不同的CPU组件同步执行
● 取指令
● 译码
● 执行
在CPU中形如如下流程,但是流水线优化只会优化无相关关联的代码譬如:mov eax, imm32 mov ebx, imm32 等,而如果出现一个指令序列其中的前后代码相关联则会破坏该处的流水线优化,对于3级流水线来讲最好情况是3行代码互相无关。
下面我们给出相关图例辅助理解
一旦出现的一组代码(A组)可以组成流水线优化则意味着该组代码A的执行顺序与组内其他代码无关,且改组代码也有可能与其他组代码无关,此时编译器有可能将该组代码提前(延后)到某组代码(B组)后执行且不会影响程序的逻辑,最简单的情况就是__cdecl
后的调用方的平栈代码add esp, imm
在ntdll中如果出现调用函数为__cdecl
就有可能出现这种优化可能会在后面的代码中穿插一条平栈代码,在还原代码时我们只需要跳过即可。
加法优化(比例因子优化)
对于加法通常我们能看到利用lea
指令进行优化,譬如eax + eax + 1
在优化版中会出现形如lea eax, [eax+eax+1]
等优化类型但是在此次还原中出现较少。
在逆向的过程中我们经常能看到如下片段
对于这种情况我们在初次见到时如果发现前面的esi原先为某个变量值但是被赋值覆盖掉了原值,然后中间经过一系列操作运算传入了某个函数或者放入了某个变量,则有可能是编译过程编译器将临时变量优化掉了导致的譬如如下C代码
1 2 3 4 5 | DWORD add(DWORD a, DWORD b) { DWORD dwRet = a + b; return dwRet; } |
在这种情况下就有可能将中间代码dwRet当作临时变量优化掉变成如下汇编形式
1 2 | mov eax, [ebp + a] add eax, [ebp + b] |
当我们出现如下情况时考虑出现了临时变量优化尽量看此寄存器是否用作他用,如果是传参则可以还原为传入参数写表达式,如果是变量赋值则可以写作将表达式赋值为变量。
对于0值寄存器传递是我在逆向CreateHeap API的时候发现的,
这里的EBX寄存器在整个函数中都被当作0值进行赋值操作,譬如在返回失败空值的时候对返回值esi的赋0处理
对于这个应该也属于是常见优化里面的但是笔者还是提出来单独分析一下,对于大家习惯用的ida如果F5之后发现函数参数不对多半就是因为编译器将原本的__stdcall
优化为__fastcall
譬如
对于见到在调用前给EDX和ECX等赋值的行为我们需要进入其调用的函数中查看
如果被调用的函数中直接使用了EDX或者ECX寄存器则考虑将该函数翻译为__stdcall
或者照例翻译为__fastcall
具体情况应该视可读性和可维护性而定。
在讨论这个汇编特征之前我们先看一下VS2019对if语句的优化如下
1 2 3 4 5 6 7 8 9 10 11 12 13 | ; if (a > = 0 ) ; { ; printf( "hello world" ); ; } mov edx, [a] test edx, edx jns xxxx ; 这里会取反 ; 这里则是语句块中的代码 push str_xxx ;HelloWorld call subxxxxx;printf add esp, 8 ; 平栈 xxxx: ;这里一般为后面的代码 |
图示
在正常的VS2019编译器中Release将会把汇编优化为取反跳的形式但是在ntdll中如果出现了跳转分支在平坦流片段中的情况则需要翻译为条件不取反如果跳转后的平坦流片段中紧接判断还可以考虑将其整合翻译为条件表达式 + if语句的形式。
在还原过程中还有一种情况就是条件表达式融合到平坦流中,在这种情况下同样需要翻译为条件不去反然后将平坦流中的代码翻译到条件语句块中,一般这种情况会经历两次跳转,一次是跳入另一次是在平坦流跳出,如果有else语句则有可能跳出到出口前面。
我们来看下面的例子,首先是外层
然后是内层
在ntdll中我们会遇到大量这种形式的代码,唯独在较为短小的条件判断语句中能看到正常取反跳的优化身影,我们需要多往后看看比对跳转的汇编标签是否和平坦流内部的跳转相同,辅以上下文依据可读性进行判断。
对于这种情况多半是寄存器不够用了,这种情况后面多半会接一个call或者较为复杂的比较逻辑,如下示例
对于windows的逆向需要熟悉其内部编译器的汇编代码优化特征才能更好的进行还原,我仅PO出我逆向还原的CreateHeap API代码,而AllocateHeap等其他API代码就不发了。注意:此代码仅作学习用途,且由于是项目原因,所以我给出粗糙版的代码,某些结构体就不发出来了(偏移还在),如果各位感兴趣可以自己进行逆向 还原
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 | typedef struct _SYSTEM_BASIC_INFORMATION { ULONG Reserved; / / 保留字段,通常为 0 ULONG TimerResolution; / / 系统定时器的分辨率(以微秒为单位) ULONG PageSize; / / 页面大小(以字节为单位) ULONG NumberOfPhysicalPages; / / 物理页面的数量 ULONG LowestPhysicalPageNumber; / / 物理页面的最低编号 ULONG HighestPhysicalPageNumber; / / 物理页面的最高编号 ULONG AllocationGranularity; / / 内存分配粒度 ULONG MinimumUserModeAddress; / / 用户模式下的最小地址 ULONG MaximumUserModeAddress; / / 用户模式下的最大地址 ULONG ActiveProcessorsAffinityMask; / / 活动处理器的亲和性掩码 UCHAR NumberOfProcessors; / / 处理器的数量 } SYSTEM_BASIC_INFORMATION; / * * Flags : 指定堆的可选属性的标志。 这些选项会影响通过调用堆函数(RtlAllocateHeap和RtlFreeHeap)对新堆的后续访问。 一共有 3 个值 1. HEAP_GENERATE_EXCEPTIONS 指定系统引发异常而不是通过返回空值即异常堆 2. HEAP_GROWABLE 可增长堆如果HeapBase为空必须指定 3. HEAP_NO_SERIALIZE 指定当堆函数从此堆分配和释放内存时不使用互斥。 当未指定 HEAP_NO_SERIALIZE 时,默认是序列化对堆的访问。 序列化堆访问允许两个或多个线程同时从同一堆分配和释放内存。 * HeapBase: 非空情况下就是将指定分配的地址,如果空的情况下则会在进程空间随机分配 * ReserveSize: * * * / ULONG g_initVar1ByInitalizeProccess = 0 ; ULONG g_initVar2ByAvrfLoadDll = 0 ; typedef int (__thiscall * PFN_4B3A32F4)(DWORD, DWORD, DWORD, DWORD, DWORD, PVOID); PFN_4B3A32F4 g_4B3A32F4; PVOID RtlCreateHeap( [ in ] ULONG Flags, [ in , optional] PVOID HeapBase, [ in , optional] SIZE_T ReserveSize, [ in , optional] SIZE_T CommitSize, [ in , optional] PVOID Lock, [ in , optional] PRTL_HEAP_PARAMETERS Parameters ) { DWORD var_12C; DWORD var_120; DWORD var_110[ 10 ]; PVOID pLock = Lock; ULONG ulFlags = Flags; PVOID pBase = HeapBase; PVOID pBase2 = HeapBase; SIZE_T stReserver = ReserveSize; DWORD var_BC; SIZE_T stCommit = CommitSize; PVOID pLock2 = Lock; DWORD var_CC; DWORD CriticalSectionFlag; DWORD var_D8; DWORD BaseAddress; BOOL ntGlobalFlag = ((PPEB)__readfsdword( 0x30 )) - >NtGlobalFlag; ULONG ulUnknow1 = 0 ; DWORD dwRegionSize = 0 ; ULONG ulUnknow2 = 0 ; ULONG ulUnknow3 = 0 ; DWORD pHeapHandle2; BYTE var_A8[ 0x30 ]; DWORD dwCommiteSize; SYSTEM_BASIC_INFORMATION SystemInformation; DWORD var_58; CPPEH_RECORD ms_exc; / / 模拟组 DWORD eax = 0 ; DWORD ecx = 0 ; DWORD edx = (DWORD)Flags; DWORD ebx = 0 ; DWORD esi = 0 ; DWORD edi = (DWORD)Parameters; / / 进程默认堆 if (g_initVar1ByInitalizeProccess ! = NULL && pBase = = NULL && pLock = = NULL) { / / 不允许应用程序更改策略 RtlpHpAppCompatDontChangePolicy(); / / 如果用户设置了Commit指针则通过Commit指针来获取堆结果 esi = g_4B3A32F4(ulFlags, 0 , stReserver, stCommit, 0 , Parameters); if (esi ! = 0 ) { goto RELEASE_SRC; } if (edi ! = 0xFFFFFFFF ) { edi = (DWORD)pBase; esi = 0 ; goto RELEASE_SRC; } } else { if (g_initVar2ByAvrfLoadDll ! = 0 && (DWORD)Parameters = = 1 ) { / * mov eax, edx and eax, 100h ; if ((eax & 100h ) ! = 0 ) ; eax = 100h ; if ((eax & 100h ) = = 0 ) ; eax = 0 neg eax ; if ((eax & 100h ) ! = 0 ) ; eax = 100h - > FFFF FEFF ; if ((eax & 100h ) = = 0 ) ; eax = 0 - > 0 sbb eax, eax ; if ((eax & 100h ) ! = 0 ) ; eax = 100h - > FFFF FEFF - > - 1 ; if ((eax & 100h ) = = 0 ) ; eax = 0 - > 0 - > 0 not eax ; if ((eax & 100h ) ! = 0 ) ; eax = 100h - > FFFF FEFF - > - 1 - > 0 ; if ((eax & 100h ) = = 0 ) ; eax = 0 - > 0 - > 0 - > - 1 and edi, eax ; if ((eax & 100h ) ! = 0 ) ; eax = 100h - > FFFF FEFF - > - 1 - > 0 - > 0 ; if ((eax & 100h ) = = 0 ) ; eax = 0 - > 0 - > 0 - > - 1 - > edi * / edi = (edx & 100 ) = = 0 ? 0 : edi; } } / / edx = edx & 0xF1FFFFFF ; ulFlags = ulFlags & 0xF1FFFFFF ; if ((ulFlags & 0x100 ) ! = 0 ) { if (!(LOWORD(ulFlags) & 2 ) || (DWORD)pBase ! = ebx || ecx || stCommit ! = ebx || (DWORD)pLock ! = ebx) { goto RELEASE_SRC; } if (edi = = 0xFFFFFFFF ) { edi = !g_initVar2ByAvrfLoadDll ? 0 : edi; } if (edi ! = 0 ) { esi = edi; / / 参数的验证 if (!RtlpHpParametersVerify(edi)) { / / goto } edx = ulFlags; } else { esi = pHeapHandle2; } } else if (_RtlpHpHeapFeatures ! = 1 ) { if (((((LOWORD(edx) & 2 ) && (DWORD)pBase = = ebx)) / / 判断堆参数是否支持 && RtlpHpParametersSupported(edi)) || edi = = 0 ) { eax = 2 ; if ((DWORD)pLock2 = = ebx) { esi = (DWORD)&pHeapHandle2; } } } if (esi ! = 0 ) { eax = (DWORD)&pHeapHandle2; if (esi = = eax) { memset((PBYTE)esi, 0 , 0x30u ); * (WORD * )esi = 2 ; * (WORD * )((BYTE * )esi + 2 ) = 0x30 ; * (DWORD * )((BYTE * )esi + 0xC ) = 1 ; * (DWORD * )((BYTE * )esi + 0x10 ) | = 0xFFFFFFFF ; } if (( * ((BYTE * )(esi + 0x4 )) & 1 )) { if (g_initVar2ByAvrfLoadDll = = 0 ) { esi = 0 ; goto RELEASE_SRC; } RtlpHpAppCompatDontChangePolicy(); esi = g_4B3A32F4(ulFlags, 0 , stReserver, stCommit, 0 , Parameters); goto RELEASE_SRC; } else { eax = (DWORD)RtlpHpEnvGetEnvHandleFromParams(esi); edi = (DWORD)stReserver; if (edi = = 0 ) / / 如果Reserver 是 0 则给出Commit的大小 { edi = stCommit; } if (eax > edi) { eax = edi; } / / 通过RtlpHpHeapCreate创建堆然后将堆移动到堆链表中去 if (!RtlpHpHeapCreate( RtlpHpConvertCreationFlags(ulFlags, ntGlobalFlag), edi, eax, ecx, edx)) { goto RELEASE_SRC; } / / 移动链表 RtlpMoveHeapBetweenLists(esi, edx, 1 , ebx); if ( * (WORD * )(esi + 0x14 ) = = LOWORD(ebx)) { goto RELEASE_SRC; } RtlpHpHeapDestroy(esi); esi = ebx; goto RELEASE_SRC; } } if (!(ulFlags & 0x10000000 )) { if (_RtlpHeapErrorHandlerThreshold > = 2 && (ulFlags & 0xFFF80C00 )) { if (((PPEB)__readfsdword( 0x30 )) - >Ldr) { / / _DbgPrint(); / / 这里正常来讲应该是一个结构体而不是强转 + 偏移 / / 但是为了赶项目进度暂时翻译为偏移的形式 printf( "HEAP[%wZ]: " , * (DWORD * )( * (DWORD * )(__readfsdword( 0x30 ) + 0xc ) + 0xc ) + 0x2c ); } else { / / dbg p printf( "HEAP: " ); } / / dbg p printf( "!(CheckedFlags & ~HEAP_CREATE_VALID_MASK)" ); / / if (byte_4B3A5DA8 = = 0 ) { RtlpReportHeapFailure( 2 ); } / / edx = ulFlags; } / * if (edx & 0xFFF80C00 ) { edx & = 0x7F3FF ; } * / if (ulFlags & 0xFFF80C00 ) { ulFlags & = 0x7F3FF ; } } / / ebx 万年老 0 , 这里推断var_A8是个结构体 / / 或许初始化为 STRUCT var_A8 { 0 }; / / 不过考虑到WINDOWS是C语言开发的 / / 也不清楚微软内部编译器支持的语言规范版本 / / 暂时设置为memset memset(var_A8, 0 , 0x30 ); if (edi ! = 0 ) { / / 这里对SEH进行一些设置 ms_exc.registration.TryLevel = 0 ; if (edi = = esi) { memcpy((PBYTE)edi, var_A8, 0xc ); } ms_exc.registration.TryLevel = 0xFFFFFFFE ; } / / 这里的汇编为test cl, 20h 但是ntGlobalFlag / / 为 4 字节我不想翻译为强转只能祈祷编译器对取低位有优化 if (ntGlobalFlag & 0xff & 0x10 ) { ulFlags | = 0x20 ; } if (ntGlobalFlag & 0xff & 0x20 ) { ulFlags | = 0x40 ; } if (ntGlobalFlag & 0x200000 ) { ulFlags | = 0x80 ; } if (ntGlobalFlag & 0xff & 0x40 ) { ulFlags | = 0x40000000 ; } if (ntGlobalFlag & 0xff & 0x80 ) { ulFlags | = 0x20000000 ; } if (ntGlobalFlag & 0x1000 ) { ulFlags | = 0x8000000 ; } ecx = __readfsdword( 0x30 ); / / 这里的var_a8应该是个结构体之后要改一下可以通过PEB的成员反推 / / var_a8的成员 if (!var_A8[ 1 ]) { var_A8[ 1 ] = * (DWORD * )(ecx + 0x78 ); } if (!var_A8[ 2 ]) { var_A8[ 2 ] = * (DWORD * )(ecx + 0x7c ); } if (!var_A8[ 3 ]) { var_A8[ 3 ] = * (DWORD * )(ecx + 0x84 ); } if (!var_A8[ 4 ]) { var_A8[ 4 ] = * (DWORD * )(ecx + 0x80 ); } if (!dword_4B3A6940) { dword_4B3A6944 = 0x10000 ; / / 首先是查询系统信息 if (NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS::SystemBasicInformation, &SystemInformation, 0x2c , 0 ) < 0 ) { goto RELEASE_SRC; } dword_4B3A6940 = var_58; edx = 0x1000 ; } / / 接下来是对页段的操作 / / 貌似这里是在对页进行处理 - 0x1000 的操作正是在将 / / 前面的设置为页首 if (!var_A8[ 5 ]) { var_A8[ 5 ] = dword_4B3A6940 - dword_4B3A6944 - 0x1000 ; } / / 最大值为 0x7F000 if (!var_A8[ 6 ] || var_A8[ 6 ] > 0x7F000 ) { var_A8[ 6 ] = 0x7F000 ; } / / 加了一些什么值 if (stCommit) { stCommit = (stCommit + 0xFFF ) & 0xFFFFF000 ; } / * edi = stReserver; if (edi ! = 0 ) { ecx = edi + 0XFFF ; ecx & = 0xFFFFF000 ; var_BC = ecx; } * / if (stReserver) { var_BC = (stReserver + 0xFFF ) & 0xFFFFF000 ; } else { var_BC = (stCommit + 0xFFFF ) & 0xFFFF0000 ; } / * mov [ebp + var_BC], ecx ; ecx = var_BC mov esi, edx ; edx = dwCommiteSize ; esi = dwCommiteSize cmp edx, ecx ja markif19 * / / / 计算得到最大堆块大小:最大堆块大小实际上是 0x7f000 ,即 0x80000 减去一个页面。 / / 最大块大小为 0xfe00 ,粒度偏移为 3 if (stCommit > var_BC) { stCommit = var_BC; esi = var_BC; } / / 获取传入的堆参数块,根据PEB设置堆参数块的值, / / 根据PEB - >NtGlobalFlag设置堆块的标志 / / ebx = 0 if (!(ulFlags & 0xff & 2 ) && pBase ! = NULL) { ntGlobalFlag = 0 ; } else { ntGlobalFlag = 0x1000 ; ulUnknow1 = 2 ; / / eax = var_BC - 0x1000 ; if (stCommit < var_BC - 0x1000 ) { var_BC = (var_BC + 0x10fff ) & 0xffff0000 ; } } if (!stCommit || !var_BC) { esi = 0 ; goto RELEASE_SRC; } / / 返回调试堆 if (ulFlags & 0x61000000 ) { if (!(ulFlags & 0x10000000 )) { return (PVOID)RtlDebugCreateHeap(ulFlags, pBase, var_BC, stCommit, pLock2, &var_A8); } } stReserver = 0x258 ; if (!(ulFlags & 0xff & 1 )) { if (pLock2) { ulFlags | = 0x80000000 ; } CriticalSectionFlag = (DWORD)(pLock2 ? pLock2 : 0 ); stReserver = !pLock2 ? 0x270 : 0x252 ; } else { if (pLock2 ! = 0 ) { esi = 0 ; goto RELEASE_SRC; } } / / 如果调用者提供的堆基地址为 0 ,调用ZwAllocateVirtualMemory从内存管理器分配内存 if (pBase = = 0 ) { BaseAddress = 0 ; if (var_A8[ 9 ] ! = (DWORD)pBase) { esi = 0 ; goto RELEASE_SRC; } pLock = (PVOID)RtlpHeapGenerateRandomValue32(); var_BC = ebx; / / eax = RtlpSecMemFreeVirtualMemory(ecx, edx, &var_BC, 0x8000 ) / / | BaseAddress & 0x1f << 0x10 ; var_D8 = RtlpHeapGenerateRandomValue32() | BaseAddress & 0x1f << 0x10 ; dwRegionSize = var_D8 + var_BC; if (dwRegionSize < var_BC) { dwRegionSize = var_BC; var_D8 = BaseAddress; } if (NtAllocateVirtualMemory( - 1 , &BaseAddress, 0 , &dwRegionSize, 0x2000 , (ulFlags & 0x40000 ) = = 0 ? 4 : 64 ) < 0 ) { esi = 0 ; goto RELEASE_SRC; } pHeapHandle2 = BaseAddress; var_BC = dwRegionSize; if (var_D8 ! = 0 ) { RtlpSecMemFreeVirtualMemory(var_BC, &BaseAddress, &var_D8, 0x8000 ); pHeapHandle2 = BaseAddress + var_D8; var_BC = dwRegionSize - var_D8; } var_CC = BaseAddress + var_D8; stCommit = BaseAddress + var_D8; } else { if (var_A8[ 0 ] ! = 0 ) { if (var_A8[ 6 ] = = 0 || var_A8[ 7 ] = = 0 || var_A8[ 6 ] > var_A8[ 7 ] || ulFlags & 0xff ! = 2 ) { esi = 0 ; goto RELEASE_SRC; } var_CC = (DWORD)pBase; stCommit = (DWORD)pBase + (DWORD)pLock2; var_BC = var_A8[ 7 ]; memset(pBase, 0 , 0x1000 ); esi = ulFlags; } else { / / pBase2需要切换为结构体 if (NtQueryVirtualMemory( - 1 , pBase, 0 , &var_110, 0x1c , 0 ) < 0 || var_110[ 0 ] ! = stCommit || var_110[ 3 ] = = 0x10000 ) { esi = 0 ; goto RELEASE_SRC; } / / 根据传入的ReserveSize和CommitSize设置堆块的保留页面和提交页面 var_CC = (DWORD)var_110; if (var_110[ 3 ] = = 0x1000 ) { var_BC = var_110[ 2 ]; if (stCommit > var_BC) { stCommit = var_BC; } if (stCommit < 0x1000 ) { esi = 0 ; goto RELEASE_SRC; } esi = ulFlags; } else { if ((ulFlags & 0x40000 )) { if (var_110[ 4 ] & 0x40 ) { goto RELEASE_SRC; } } / / 此时,已获得一个堆指针、已提交的基址、未提交的基址、段标志、 / / 提交大小和保留大小。 / / 如果已提交和未提交的基地址相同,那么我们需要调用ZwAllocateVirtualMemory提交由ComitSize指定的数量 memset(var_110, 0 , 0x1000 ); if (NtQueryVirtualMemory( - 1 , pBase, 3 , &var_12C, 0x1c , 0 ) < 0 ) { esi = 0 ; goto RELEASE_SRC; } var_BC = var_120; stCommit = var_110[ 2 ]; dwCommiteSize = stCommit + var_CC; } } var_110[ 10 ] | = 1 ; pHeapHandle2 = (DWORD)pBase; ulFlags & = 0x40000 ; ecx = stCommit; eax = var_CC; } / / edi = pBase if (var_CC = = stCommit) { if (NtAllocateVirtualMemory( - 1 , &var_CC, 0 , &dwCommiteSize, 0x1000 , (ulFlags & 40000 ) = = 0 ? 4 : 64 ) < 0 ) { esi = 0 ; goto RELEASE_SRC; } if (RtlGetCurrentServiceSessionId()) { eax = __readfsdword( 0x30 ); ecx = * (DWORD * )(eax + 0x50 ); ecx + = 0x226 ; } else { / / 从这里进入 0 环 ecx = 0x7FFE0380 ; } if ( * (BYTE * )ecx ! = 0 ) { if ( * (BYTE * )(__readfsdword( 0x30 ) + 0x240 ) ! = 1 ) { RtlpLogHeapCommit(pHeapHandle2, var_CC, dwCommiteSize, 1 ); } } stCommit = stCommit + dwCommiteSize; } / / edi = HeapHandle + 258h if ( * (DWORD * )(__readfsdword( 0x30 ) + 0x68 ) ! = 0x800 ) { / * ;ecx = * (DWORD * )(pHeapHandle2 + 0x258 + 0x7 ) & 0xFFFFFFF8 ; * (DWORD * )(pHeapHandle2 + 0xBC ) = ecx; stReserver = ecx; edi = * (DWORD * )(pHeapHandle2 + 0xBC ) + ecx; ulFlags | = 0x4000000 ; esi = ulFlags; * / * (DWORD * )(pHeapHandle2 + 0xBC ) = * (DWORD * )(pHeapHandle2 + 0x258 + 0x7 ) & 0xFFFFFFF8 ; stReserver = * (DWORD * )(pHeapHandle2 + 0x258 + 0x7 ) & 0xFFFFFFF8 ; edi = * (DWORD * )(pHeapHandle2 + 0xBC ) + * (DWORD * )(pHeapHandle2 + 0x258 + 0x7 ) & 0xFFFFFFF8 ;; ulFlags | = 0x4000000 ; esi = ulFlags; } var_110[ 9 ] = (stReserver + 7 ) & 0xFFFFFFF8 ; / / ecx = var_110[ 9 ] / 8 ; * (WORD * )pHeapHandle2 = var_110[ 9 ] / 8 ; * (WORD * )(pHeapHandle2 + 2 ) = 1 ; * (WORD * )(pHeapHandle2 + 7 ) = 1 ; * (DWORD * )(pHeapHandle2 + 0x60 ) = 0xEEFFEEFF ; * (DWORD * )(pHeapHandle2 + 0x40 ) = ulFlags & 0xEFFFFFFF ; * (DWORD * )(pHeapHandle2 + 0x58 ) = 0 ; memset((BYTE * )pHeapHandle2 + 0x1F4 , 0 , 0x5C ); / / 实际上这里的参数应该为 * _HEAP_HANDLE / / 这里是对堆头进行加密 RtlpCreateHeapEncoding((PVOID)pHeapHandle2); * (DWORD * )(pHeapHandle2 + 0x234 ) = 1 ; if ( * (DWORD * )(pHeapHandle2 + 0x40 ) & 0x8000000 ) { / / 这里或许是获取拦截器索引 / * 基于此猜测我们给出如下的一些定义 typedef DWORD(__stdcall * INTERCEPTOR_PFN)(DWORD, DWORD, DWORD, DWORD); DWORD NTAPI RtlpGetHeapInterceptorIndex(INTERCEPTOR_PFN); INTERCEPTOR_PFN RtlpStackTraceDatabaseLogPrefix; * / / * mov ecx, offset _RtlpStackTraceDatabaseLogPrefix@ 16 ; RtlpStackTraceDatabaseLogPrefix(x,x,x,x) call _RtlpGetHeapInterceptorIndex@ 4 ; RtlpGetHeapInterceptorIndex(x) movzx eax, ax 值得注意的是这里用了movzx但是对于movzx来讲是取用了低位所以我们有必要考虑 / / 返回的是一个 4 字节的结构体而非一个DWORD,但是我们不知道语义所以这里给出LOWORD来取代结构体 mov [esi + 58h ], eax * / * (DWORD * )(pHeapHandle2 + 0x58 ) = (DWORD)LOWORD(RtlpGetHeapInterceptorIndex(RtlpStackTraceDatabaseLogPrefix)); * (DWORD * )(pHeapHandle2 + 0x40 ) = 0xFFFFFFBF ; } * (DWORD * )(pHeapHandle2 + 0x44 ) = ulFlags & 0x6001007D ; / / 这里的还原存疑 / * edi 来源 lea edi, [edx + 258h ] ; edx = heapHandle ... mov ecx, edi ; edi = heapHandle + 0x258 mov eax, [ebp + HeapHandle] sub ecx, eax ; ecx = 0x258 mov [eax + 7Eh ], cx mov eax, [ebp + HeapHandle] * / * (WORD * )(pHeapHandle2 + 0x7E ) = LOWORD( 0x258 ); / / ebx 万年老 0 * (DWORD * )(pHeapHandle2 + 0x80 ) = 0 ; / / eax = pHeapHandle2 + 0xc0 ; / / * (DWORD * )(eax + 4 ) = eax; * (DWORD * )(pHeapHandle2 + 0xc4 ) = pHeapHandle2 + 0xc0 ; * (DWORD * )(pHeapHandle2 + 0xc0 ) = pHeapHandle2 + 0xc0 ; / / eax = pHeapHandle2 + 0x9c ; / / * (DWORD * )(eax + 4 ) = eax; / / * (DWORD * )(eax) = eax; * (DWORD * )(pHeapHandle2 + 0x9c + 4 ) = pHeapHandle2 + 0x9c ; * (DWORD * )(pHeapHandle2 + 0x9c ) = pHeapHandle2 + 0x9c ; * (DWORD * )(pHeapHandle2 + 0xA4 + 4 ) = pHeapHandle2 + 0xA4 ; * (DWORD * )(pHeapHandle2 + 0xA4 ) = pHeapHandle2 + 0xA4 ; * (DWORD * )(pHeapHandle2 + 0x8C + 4 ) = pHeapHandle2 + 0x8C ; * (DWORD * )(pHeapHandle2 + 0x8C ) = pHeapHandle2 + 0x8C ; if (!CriticalSectionFlag && !(ulFlags & 0xff & 1 )) { / / CriticalSectionFlag = edi; CriticalSectionFlag = pHeapHandle2 + 0x258 ; if (RtlInitializeCriticalSectionEx(CriticalSectionFlag, 0 , 0x10000000 ) < 0 ) { / / return 0 ; esi = 0 ; goto RELEASE_SRC; } edi + = 0x18 ; ecx = CriticalSectionFlag; } * (DWORD * )(pHeapHandle2 + 0xC8 ) = CriticalSectionFlag; * (DWORD * )(pHeapHandle2 + 0x48 ) | = 0x80000000 ; if (!(RtlpInitializeHeapSegment(pHeapHandle2, pHeapHandle2, var_110[ 9 ] + 0x238 , CriticalSectionFlag, var_110[ 0xA ], var_CC, stCommit, var_CC - var_110[ 0xE ] + var_BC) & 0xff )) { esi = 0 ; goto RELEASE_SRC; } esi = 0x80 ; if (pBase ! = 0 ) { memset((BYTE * )pHeapHandle2 + 0x258 + 0x18 , 0 , 0x80 ); } * (DWORD * )(pHeapHandle2 + 0x258 + 0x4 ) = 0x80 ; * (DWORD * )(pHeapHandle2 + 0x258 + 0x1c ) = pHeapHandle2 + 0x258 + 0x24 ; * (DWORD * )(pHeapHandle2 + 0x258 + 0x18 ) = pHeapHandle2 + 0xc0 ; * (DWORD * )(pHeapHandle2 + 0x258 + 0x20 ) = pHeapHandle2 + 0x258 + 0x24 + 0x10 ; RtlpPopulateListIndex(pHeapHandle2, pHeapHandle2 + 0x258 ); * (WORD * )(pHeapHandle2 + 0x7c ) = 0 ; * (DWORD * )(pHeapHandle2 + 0x64 ) = var_A8[ 0 ]; * (DWORD * )(pHeapHandle2 + 0x68 ) = var_A8[ 1 ]; * (DWORD * )(pHeapHandle2 + 0x6c ) = var_A8[ 2 ] / 8 ; * (DWORD * )(pHeapHandle2 + 0x70 ) = var_A8[ 3 ] / 8 ; * (DWORD * )(pHeapHandle2 + 0x78 ) = var_A8[ 4 ] / 8 ; * (DWORD * )(pHeapHandle2 + 0x5c ) = (var_A8[ 5 ] + 7 ) / 8 ; * (DWORD * )(pHeapHandle2 + 0xcc ) = (var_A8[ 8 ] ^ _RtlpHeapKey); * (DWORD * )(pHeapHandle2 + 0x250 ) = 4 ; * (DWORD * )(pHeapHandle2 + 0x254 ) = 0xFE000 ; if (_RtlpDisableHeapLookaside ! = 1 ) { / / test byte ptr _RtlpDisableHeapLookaside, dl * (DWORD * )(pHeapHandle2 + 0x48 ) = 1 ; } if (!(ulFlags & 0x10000 )) { * (DWORD * )(pHeapHandle2 + 0x94 ) = 0xf ; * (DWORD * )(pHeapHandle2 + 0x98 ) = 0xFFFFFFF8 ; } if ( * (DWORD * )(pHeapHandle2 + 0x40 ) & 0x20 ) { * (DWORD * )(pHeapHandle2 + 0x94 ) + = 8 ; } * (DWORD * )(pHeapHandle2 + 0xE4 ) = 0 ; * (WORD * )(pHeapHandle2 + 0xE8 ) = 0 ; * (WORD * )(pHeapHandle2 + 0xEA ) = 0 ; * (WORD * )(pHeapHandle2 + 0xEB ) = 0 ; * (DWORD * )(pHeapHandle2 + 0xE8 ) = 0 ; / / 根据快表的标志初始化块表 if (((ulFlags & 3 ) = = 2 ) && ((_RtlpDisableHeapLookaside & 1 ) = = 0 )) { * (DWORD * )(pHeapHandle2 + 0xEC ) = RtlAllocateHeap((PMY_HEAPSTRUCT)pHeapHandle2, 0x80000A , 0x100 ); if (!( * (DWORD * )(pHeapHandle2 + 0xEC ))) { goto RELEASE_SRC; } * (BYTE * )(pHeapHandle2 - 1 ) = 1 ; * (BYTE * )(pHeapHandle2 + 0xf0 ) = 0x80 ; } / / 函数指针传入 / * * 暂时给出如下定义 typedef DWORD(__stdcall * RtlpProcessHeapsListLock_PFN)(); DWORD NTAPI RtlEnterCriticalSection(RtlpProcessHeapsListLock_PFN); RtlpProcessHeapsListLock_PFN _RtlpProcessHeapsListLock; * / RtlEnterCriticalSection(_RtlpProcessHeapsListLock); / / 添加堆到链表 RtlpAddHeapToUnprotectedList(pHeapHandle2); / / 离开临界区 RtlLeaveCriticalSection(_RtlpProcessHeapsListLock); if (! * (DWORD * )(pHeapHandle2 + 0x7c )) { esi = 0 ; goto RELEASE_SRC; } if (RtlGetCurrentServiceSessionId()) { eax = __readfsdword( 0x30 ); eax = * (DWORD * )(eax + 0x50 ); eax + = 0x226 ; } else { eax = 0x7FFE0380 ; } if ( * (BYTE * )eax ! = 0 && ( * (BYTE * )(__readfsdword( 0x30 ) + 0x240 ) & 1 )) { if (RtlGetCurrentServiceSessionId()) { esi = * (DWORD * )(__readfsdword( 0x30 ) + 0x50 ) + 0x226 ; } RtlpLogHeapCreateEvent(pHeapHandle2, ulFlags, var_BC, dwCommiteSize, (DWORD) * (BYTE * )esi); } else { esi = ulFlags; } if (RtlGetCurrentServiceSessionId()) { eax = __readfsdword( 0x30 ); eax = * (DWORD * )(eax + 0x50 ); eax + = 0x230 ; } else { eax = 0x7FFE0388 ; } if ( * (BYTE * )eax ! = 0 ) { / / 记录创建的堆范围 RtlpHeapLogRangeCreate(pHeapHandle2, var_BC, ulFlags); } * (DWORD * )(pHeapHandle2 + 0x48 ) & = 0x7FFFFFFF ; * (DWORD * )(pHeapHandle2 + 0xd0 ) = 0 ; esi = pHeapHandle2; / / 返回值 pHeapHandle2 = 0 ; CriticalSectionFlag = 0 ; / * * 这里为统一出口用于释放资源与返回值 此函数返回值为HeapHandle * / RELEASE_SRC: / / todo edi = (DWORD)pBase; edx = (DWORD)pLock2; eax = ulUnknow3; if (eax ! = 0 && eax ! = edx) { RtlDeleteCriticalSection(eax); } if (ulUnknow2 ! = 0 && edi = = 0 ) { var_BC = 0 ; / / eax = RtlpSecMemFreeVirtualMemory(ecx, edx, &var_BC, 0x8000 ) / / | BaseAddress & 0x1f << 0x10 ; var_D8 = RtlpSecMemFreeVirtualMemory(ecx, (PVOID)edx, &var_BC, 0x8000 ) | BaseAddress & 0x1f << 0x10 ; } RET: return (PVOID)esi; } |
更多【软件逆向-Windows10代码还原汇编特征汇总(附NTDLL CreateHeap还原代码)】相关视频教程:www.yxfzedu.com