├── README.md ├── README ├── image-20220304130302359.png ├── image-20220304130326063.png └── image-20220304130353628.png ├── rpcscan.cna ├── rpcscan.dll └── rpcscan ├── Backup └── reflective_dll.sln ├── reflective_dll.sln ├── reflective_dll.vcproj ├── reflective_dll.vcxproj ├── reflective_dll.vcxproj.filters ├── reflective_dll.vcxproj.user ├── rpc.c ├── rpc.h ├── src ├── ReflectiveDLLInjection.h ├── ReflectiveDll.c ├── ReflectiveLoader.c └── ReflectiveLoader.h └── thread_pool.hpp /README.md: -------------------------------------------------------------------------------- 1 | # RPCSCAN 2 | 3 | RPC远程主机信息匿名扫描工具。通过此工具,能在没有帐号密码的情况下,获取远程主机的RPC映射图,通过uuid来匹配相关进程达到主机信息收集的目的。(当前支持360.exe和VPN api的探测,后续将继续添加更多的程序支持) 4 | 5 | ## Install 6 | 7 | 将rpcscan.dll和rpcscan.cna放同一个目录,后用CS导入CNA即可 8 | 9 | ## Usage 10 | 11 | ``` 12 | beacon> ? rpcscan 13 | Use: rpcscan [ips] [thread num] 14 | 15 | rpc info scan and get process 16 | ``` 17 | 18 | 扫描单ip:`rpcscan 172.16.178.5` 19 | 20 | ![image-20220304130326063](./README/image-20220304130326063.png) 21 | 22 | 100线程扫描172.16.178.5的C段:`rpcscan 172.16.178.5/24 100` 23 | 24 | ![image-20220304130302359](./README/image-20220304130302359.png) 25 | 26 | -------------------------------------------------------------------------------- /README/image-20220304130302359.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JDArmy/RPCSCAN/627ab955d4c97a06b33c56faf31e9843bd19a7fa/README/image-20220304130302359.png -------------------------------------------------------------------------------- /README/image-20220304130326063.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JDArmy/RPCSCAN/627ab955d4c97a06b33c56faf31e9843bd19a7fa/README/image-20220304130326063.png -------------------------------------------------------------------------------- /README/image-20220304130353628.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JDArmy/RPCSCAN/627ab955d4c97a06b33c56faf31e9843bd19a7fa/README/image-20220304130353628.png -------------------------------------------------------------------------------- /rpcscan.cna: -------------------------------------------------------------------------------- 1 | $uuid = @(); 2 | $uuid[0] = "ac109027-2eb9-4d3e-ab82-d2f8da000d5d"; 3 | $uuid[1] = "650a7e26-eab8-5533-ce43-9c1dfce11511"; 4 | $uuid[2] = "0d3c7f20-1c8d-4654-a1b3-51563b298bda"; 5 | $uuid[3] = "12345678-1234-ABCD-EF00-0123456789AB"; 6 | 7 | $process = @(); 8 | $process[0] = "360.exe"; 9 | $process[1] = "vpn api"; 10 | $process[2] = "UserMgrCli"; 11 | $process[3] = "Spoolsv"; 12 | 13 | alias rpcscan { 14 | if (-is64 $1) { 15 | ($cmd, $host, $threadnum) = split(' ', $0); 16 | # println($0); 17 | # println($host); 18 | # println($threadnum); 19 | 20 | $bufstr = ""; 21 | if ($threadnum == ""){ 22 | $threadnum = 5; 23 | } 24 | 25 | foreach $index => $value ($uuid) 26 | { 27 | $p = $process[$index]; 28 | $u = $value; 29 | $paddingp = chr(0x00) x (40 - strlen($p)); 30 | $pathcu = chr(0x00) x (40 - strlen($u)); 31 | $bufstr = "$bufstr$p$paddingp$u$pathcu"; 32 | } 33 | 34 | $hostpadding = chr(0x00) x (20 - strlen($host)); 35 | $host = "$host$hostpadding"; 36 | 37 | $buffer = allocate(1024); 38 | 39 | writeb($buffer, $host); 40 | 41 | writeb($buffer, pack("i-", 135)); 42 | 43 | writeb($buffer, pack("i-", $index + 1)); 44 | 45 | writeb($buffer, pack("i-", $threadnum)); 46 | 47 | writeb($buffer, $bufstr); 48 | 49 | closef($buffer); 50 | $b = readb($buffer, -1); 51 | 52 | bdllspawn($1, script_resource("rpcscan.dll"), "$[1024]b", "rpcscan.dll", 5000, false); 53 | }else{ 54 | println("\c4[-]\c0 rpcscan: x64 system support"); 55 | } 56 | } 57 | 58 | 59 | beacon_command_register( 60 | "rpcscan", 61 | "rpc info scan and get process", 62 | "Use: rpcscan [ips] [thread num]\n\nrpc info scan and get process"); 63 | -------------------------------------------------------------------------------- /rpcscan.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JDArmy/RPCSCAN/627ab955d4c97a06b33c56faf31e9843bd19a7fa/rpcscan.dll -------------------------------------------------------------------------------- /rpcscan/Backup/reflective_dll.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual C++ Express 2008 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "reflective_dll", "reflective_dll.vcproj", "{3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|Win32.ActiveCfg = Release|Win32 13 | {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|Win32.Build.0 = Release|Win32 14 | {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|Win32.ActiveCfg = Release|Win32 15 | {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /rpcscan/reflective_dll.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31019.35 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "reflective_dll", "reflective_dll.vcxproj", "{3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Debug|x64 = Debug|x64 12 | Release|Win32 = Release|Win32 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|Win32.ActiveCfg = Release|x64 17 | {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|Win32.Build.0 = Release|x64 18 | {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|x64.ActiveCfg = Debug|x64 19 | {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|x64.Build.0 = Debug|x64 20 | {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|Win32.ActiveCfg = Release|Win32 21 | {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|Win32.Build.0 = Release|Win32 22 | {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|x64.ActiveCfg = Release|Win32 23 | {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|x64.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {C1819E38-7BF6-4108-8B06-221DA9D3582E} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /rpcscan/reflective_dll.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 18 | 19 | 20 | 21 | 22 | 29 | 32 | 35 | 38 | 41 | 44 | 55 | 58 | 61 | 64 | 71 | 74 | 77 | 80 | 83 | 86 | 89 | 92 | 93 | 100 | 103 | 106 | 109 | 112 | 116 | 127 | 130 | 133 | 136 | 143 | 146 | 149 | 152 | 155 | 158 | 161 | 164 | 165 | 173 | 176 | 179 | 182 | 185 | 188 | 200 | 203 | 206 | 209 | 218 | 221 | 224 | 227 | 230 | 233 | 236 | 240 | 241 | 249 | 252 | 255 | 258 | 261 | 265 | 280 | 283 | 286 | 289 | 299 | 302 | 305 | 308 | 311 | 314 | 317 | 321 | 322 | 323 | 324 | 325 | 326 | 331 | 334 | 335 | 338 | 339 | 340 | 345 | 348 | 349 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | -------------------------------------------------------------------------------- /rpcscan/reflective_dll.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | ARM 7 | 8 | 9 | Debug 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | ARM 19 | 20 | 21 | Release 22 | Win32 23 | 24 | 25 | Release 26 | x64 27 | 28 | 29 | 30 | {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949} 31 | reflective_dll 32 | Win32Proj 33 | 10.0 34 | 35 | 36 | 37 | DynamicLibrary 38 | v142 39 | MultiByte 40 | true 41 | 42 | 43 | DynamicLibrary 44 | v142 45 | MultiByte 46 | true 47 | 48 | 49 | DynamicLibrary 50 | v142 51 | Unicode 52 | 53 | 54 | DynamicLibrary 55 | v142 56 | Unicode 57 | 58 | 59 | DynamicLibrary 60 | v142 61 | MultiByte 62 | false 63 | 64 | 65 | DynamicLibrary 66 | v142 67 | Unicode 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | <_ProjectFileVersion>11.0.50727.1 93 | 94 | 95 | $(SolutionDir)$(Configuration)\ 96 | $(Configuration)\ 97 | true 98 | MinimumRecommendedRules.ruleset 99 | 100 | 101 | 102 | 103 | true 104 | MinimumRecommendedRules.ruleset 105 | 106 | 107 | 108 | 109 | $(SolutionDir)$(Platform)\$(Configuration)\ 110 | $(Platform)\$(Configuration)\ 111 | true 112 | MinimumRecommendedRules.ruleset 113 | 114 | 115 | 116 | 117 | $(SolutionDir)$(Configuration)\ 118 | $(Configuration)\ 119 | false 120 | MinimumRecommendedRules.ruleset 121 | 122 | 123 | 124 | 125 | false 126 | MinimumRecommendedRules.ruleset 127 | 128 | 129 | 130 | 131 | $(SolutionDir)$(Platform)\$(Configuration)\ 132 | $(Platform)\$(Configuration)\ 133 | false 134 | MinimumRecommendedRules.ruleset 135 | 136 | 137 | $(IncludePath) 138 | 139 | 140 | 141 | Disabled 142 | WIN32;_DEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;%(PreprocessorDefinitions) 143 | true 144 | EnableFastChecks 145 | MultiThreadedDebugDLL 146 | 147 | Level3 148 | EditAndContinue 149 | stdcpp17 150 | 151 | 152 | true 153 | Windows 154 | MachineX86 155 | 156 | 157 | 158 | 159 | Disabled 160 | WIN32;_DEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;%(PreprocessorDefinitions) 161 | true 162 | EnableFastChecks 163 | MultiThreadedDebugDLL 164 | 165 | 166 | Level3 167 | EditAndContinue 168 | stdcpp17 169 | 170 | 171 | true 172 | Windows 173 | 174 | 175 | 176 | 177 | X64 178 | 179 | 180 | Disabled 181 | WIN32;_DEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;%(PreprocessorDefinitions) 182 | true 183 | EnableFastChecks 184 | MultiThreadedDebugDLL 185 | 186 | Level3 187 | ProgramDatabase 188 | stdcpp17 189 | 190 | 191 | false 192 | Windows 193 | MachineX64 194 | 195 | 196 | 197 | 198 | MaxSpeed 199 | OnlyExplicitInline 200 | true 201 | WIN32;NDEBUG;_WINDOWS;_USRDLL;WIN_X86;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions) 202 | MultiThreaded 203 | true 204 | 205 | Level3 206 | ProgramDatabase 207 | stdcpp17 208 | 209 | 210 | true 211 | Windows 212 | true 213 | true 214 | MachineX86 215 | 216 | 217 | copy ..\Release\reflective_dll.dll ..\bin\ 218 | 219 | 220 | 221 | 222 | MinSpace 223 | OnlyExplicitInline 224 | true 225 | WIN32;NDEBUG;_WINDOWS;_USRDLL;WIN_ARM;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions) 226 | MultiThreaded 227 | true 228 | 229 | 230 | Level3 231 | ProgramDatabase 232 | true 233 | Default 234 | stdcpp17 235 | 236 | 237 | true 238 | Windows 239 | true 240 | true 241 | $(OutDir)$(ProjectName).arm.dll 242 | 243 | 244 | copy ..\ARM\Release\reflective_dll.arm.dll ..\bin\ 245 | 246 | 247 | 248 | 249 | X64 250 | 251 | 252 | MaxSpeed 253 | OnlyExplicitInline 254 | true 255 | Size 256 | false 257 | WIN64;NDEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;WIN_X64;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions) 258 | MultiThreaded 259 | true 260 | 261 | Level3 262 | ProgramDatabase 263 | CompileAsCpp 264 | stdcpp17 265 | 266 | 267 | $(OutDir)$(ProjectName).x64.dll 268 | false 269 | Windows 270 | true 271 | true 272 | MachineX64 273 | 274 | 275 | copy $(OutDir)$(ProjectName).x64.dll ..\bin\ 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | -------------------------------------------------------------------------------- /rpcscan/reflective_dll.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | 14 | 15 | Source Files 16 | 17 | 18 | Source Files 19 | 20 | 21 | Source Files 22 | 23 | 24 | 25 | 26 | Header Files 27 | 28 | 29 | Header Files 30 | 31 | 32 | Header Files 33 | 34 | 35 | Header Files 36 | 37 | 38 | -------------------------------------------------------------------------------- /rpcscan/reflective_dll.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /rpcscan/rpc.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JDArmy/RPCSCAN/627ab955d4c97a06b33c56faf31e9843bd19a7fa/rpcscan/rpc.c -------------------------------------------------------------------------------- /rpcscan/rpc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #pragma once 3 | #ifndef WIN32_LEAN_AND_MEAN 4 | #define WIN32_LEAN_AND_MEAN 5 | #endif 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "thread_pool.hpp" 18 | 19 | 20 | typedef unsigned char byte; 21 | 22 | typedef struct { 23 | char processNmae[40]; 24 | char uuid[40]; 25 | }UPB; 26 | 27 | typedef struct { 28 | char host[20]; 29 | int port; 30 | int length; 31 | int thread; 32 | UPB upbdata[]; 33 | }PBINFO; 34 | 35 | typedef struct uuid { 36 | uint32_t time_low; 37 | uint16_t time_mid; 38 | uint16_t time_hi_and_version; 39 | uint8_t clock_seq_hi_and_reserved; 40 | uint8_t clock_seq_low; 41 | byte node[6]; 42 | } uuid_t, * uuid_p_t; 43 | 44 | #define UUID_SIZE 16 45 | 46 | typedef uint16_t p_context_id_t; 47 | 48 | typedef struct p_syntax_id { 49 | uuid_t if_uuid; 50 | uint32_t if_version; /* Major/Minor version. */ 51 | } p_syntax_id_t; 52 | 53 | /* Elements in a presentation context list. */ 54 | typedef struct _p_cont_elem { 55 | p_context_id_t p_cont_id; 56 | uint8_t n_transfer_syn; 57 | uint8_t reserved; 58 | p_syntax_id_t abstract_syntax; 59 | p_syntax_id_t* transfer_syntaxes; 60 | } p_cont_elem_t; 61 | 62 | /* Presentation context list. */ 63 | typedef struct p_cont_list { 64 | uint8_t n_context_elem; 65 | uint8_t reserved; 66 | uint16_t reserved2; 67 | p_cont_elem_t* p_cont_elem; 68 | } p_cont_list_t; 69 | 70 | /* Common Authentication Verifier. */ 71 | typedef struct auth_verifier_co { 72 | /* Restore 4-byte alignment */ 73 | /* uint8_t auth_pad[]; */ 74 | uint8_t auth_type; 75 | uint8_t auth_level; 76 | uint8_t auth_pad_length; 77 | uint8_t auth_reserved; 78 | uint8_t auth_context_id; 79 | uint8_t* auth_value; /* Size is auth_length. */ 80 | } auth_verifier_co_t; 81 | 82 | /* BIND PDU header */ 83 | typedef struct rpcconn_bind_hdr { 84 | uint8_t rpc_vers; /* RPC version. */ 85 | uint8_t rpc_vers_minor; /* Minor version. */ 86 | uint8_t ptype; /* Bind PDU. */ 87 | uint8_t pfc_flags; /* PFC Flags. */ 88 | uint8_t packed_drep[4]; /* NDR data representation format label. */ 89 | uint16_t frag_length; /* Total length of fragment. */ 90 | uint16_t auth_length; /* Length of auth_value. */ 91 | uint32_t call_id; /* Call identifier. */ 92 | uint16_t max_xmit_frag; /* Max transmit fragment size. */ 93 | uint16_t max_recv_frag; /* Max receive fragment size. */ 94 | uint32_t assoc_group_id; /* Incarnation of client-server assoc group. */ 95 | p_cont_list_t p_context_elem; /* Presentation context list. */ 96 | auth_verifier_co_t auth_verifier; /* if auth_length != 0. */ 97 | } rpcconn_bind_hdr_t; 98 | 99 | typedef struct port_any { 100 | uint16_t length; 101 | char* port_spec; 102 | } port_any_t; 103 | 104 | /* Result of a presentation context negotiation. */ 105 | typedef uint16_t p_cont_def_result_t; 106 | /* Reason for rejection of a context element. */ 107 | typedef uint16_t p_provider_reason_t; 108 | 109 | typedef struct p_result { 110 | p_cont_def_result_t result; 111 | p_provider_reason_t reason; 112 | p_syntax_id_t transfer_syntax; 113 | } p_result_t; 114 | 115 | typedef struct p_result_list { 116 | uint8_t n_results; 117 | uint8_t reserved; 118 | uint16_t reserved2; 119 | p_result_t* p_results; 120 | } p_result_list_t; 121 | 122 | /* BIND_ACK PDU header */ 123 | typedef struct rpcconn_bind_ack_hdr { 124 | uint8_t rpc_vers; 125 | uint8_t rpc_vers_minor; 126 | uint8_t ptype; 127 | uint8_t pfc_flags; 128 | byte packed_drep[4]; 129 | uint16_t frag_length; 130 | uint16_t auth_length; 131 | uint32_t call_id; 132 | uint16_t max_xmit_frag; 133 | uint16_t max_recv_frag; 134 | uint32_t assoc_group_id; /* Returned assoc_group_id. */ 135 | port_any_t sec_addr; /* Optional secondary address. */ 136 | /* Restore 8-octet alignment. */ 137 | /* uint8_t pad2[]; */ 138 | p_result_list_t p_result_list; /* Variable size. */ 139 | auth_verifier_co_t auth_verifier; /* if auth_length != 0. */ 140 | } rpcconn_bind_ack_hdr_t; 141 | 142 | /* Presentation context reject */ 143 | typedef uint16_t p_reject_reason_t; 144 | 145 | typedef struct version { 146 | uint8_t major; 147 | uint8_t minor; 148 | } version_t; 149 | 150 | typedef version_t p_rt_version_t; 151 | 152 | typedef struct p_rt_versions_supported { 153 | uint8_t n_protocols; 154 | p_rt_version_t* p_protocols; 155 | } p_rt_versions_supported_t; 156 | 157 | /* BIND NAK PDU header */ 158 | typedef struct rpcconn_bind_nak_hdr { 159 | uint8_t rpc_vers; 160 | uint8_t rpc_vers_minor; 161 | uint8_t ptype; 162 | uint8_t pfc_flags; 163 | byte packed_drep[4]; 164 | uint16_t frag_length; 165 | uint16_t auth_length; 166 | uint32_t call_id; 167 | p_reject_reason_t provider_reject_reason; 168 | p_rt_versions_supported_t* versions; /* if reject reason is 4. */ 169 | } rpcconn_bind_nak_hdr_t; 170 | 171 | /* Reasons for rejection of an association in the bind nak PDU. */ 172 | #define REASON_NOT_SPECIFIED 0 173 | #define TEMPORARY_CONGESTION 1 174 | #define LOCAL_LIMIT_EXCEEDED 2 175 | #define CALLED_PADDR_UNKNOWN 3 /* Not used. */ 176 | #define PROTOCOL_VERSION_NOT_SUPPORTED 4 177 | #define DEFAULT_CONTEXT_NOT_SUPPORTED 5 /* Not used. */ 178 | #define USER_DATA_NOT_READABLE 6 /* Not used. */ 179 | #define NO_PSAP_AVAILABLE 7 /* Not used. */ 180 | 181 | /* REQUEST PDU header */ 182 | typedef struct rpcconn_request_hdr { 183 | uint8_t rpc_vers; /* RPC version. */ 184 | uint8_t rpc_vers_minor; /* Minor version. */ 185 | uint8_t ptype; /* Request PDU. */ 186 | uint8_t pfc_flags; /* PFC Flags. */ 187 | uint8_t packed_drep[4]; /* NDR data representation format label. */ 188 | uint16_t frag_length; /* Total length of fragment. */ 189 | uint16_t auth_length; /* Length of auth_value. */ 190 | uint32_t call_id; /* Call identifier. */ 191 | uint32_t alloc_hint; /* Allocation hint. */ 192 | p_context_id_t p_cont_id; /* Presentation context. */ 193 | uint16_t opnum; /* Operation # within the interface! */ 194 | uuid_t object; /* Only present if the PFC_OBJECT_UUID field is non zero. */ 195 | /* Stub data, 8-octet aligned. */ 196 | auth_verifier_co_t auth_verifier; /* if auth_length != 0 */ 197 | } rpcconn_request_hdr_t; 198 | 199 | /* RESPONSE PDU header */ 200 | typedef struct rpcconn_response { 201 | uint8_t rpc_vers; 202 | uint8_t rpc_vers_minor; 203 | uint8_t ptype; 204 | uint8_t pfc_flags; 205 | uint8_t packed_drep[4]; 206 | uint16_t frag_length; 207 | uint16_t auth_length; 208 | uint32_t call_id; 209 | uint32_t alloc_hint; 210 | p_context_id_t p_cont_id; 211 | uint8_t cancel_count; 212 | uint8_t reserved; 213 | /* Stub data here, 8-octet aligned. */ 214 | auth_verifier_co_t auth_verifier; /* if auth_length != 0. */ 215 | } rpcconn_response_hdr_t; 216 | 217 | /* FAULT PDU header */ 218 | typedef struct rpcconn_fault_hdr { 219 | uint8_t rpc_vers; 220 | uint8_t rpc_vers_minor; 221 | uint8_t ptype; 222 | uint8_t pfc_flags; 223 | byte packed_drep[4]; 224 | uint16_t frag_length; 225 | uint16_t auth_length; 226 | uint32_t call_id; 227 | uint32_t alloc_hint; 228 | p_context_id_t p_cont_id; /* Presentation context. */ 229 | uint8_t cancel_count; /* Received cancel count. */ 230 | uint8_t reserved; 231 | uint32_t status; /* Run-time fault code or zero. */ 232 | uint8_t reserved2[4]; 233 | /* Stub data here, 8-octet aligned. */ 234 | auth_verifier_co_t auth_verifier; /* if auth_length != 0. */ 235 | } rpcconn_fault_hdr_t; 236 | 237 | /* SHUTDOWN PDU header */ 238 | typedef struct rpcconn_shutdown_hdr { 239 | uint8_t rpc_vers; 240 | uint8_t rpc_vers_minor; 241 | uint8_t ptype; 242 | uint8_t pfc_flags; 243 | byte packed_drep[4]; 244 | uint16_t frag_length; 245 | uint16_t auth_length; 246 | uint32_t call_id; 247 | } rpcconn_shutdown_hdr_t; 248 | 249 | #define DEFAULT_EPMAP_PORT 135 250 | 251 | /* PDU types */ 252 | #define RPC_PTYPE_REQUEST 0x00 /* CO/CL */ 253 | #define RPC_PTYPE_PING 0x01 /* CL */ 254 | #define RPC_PTYPE_RESPONSE 0x02 /* CO/CL */ 255 | #define RPC_PTYPE_FAULT 0x03 /* CO/CL */ 256 | #define RPC_PTYPE_WORKING 0x04 /* CL */ 257 | #define RPC_PTYPE_NOCALL 0x05 /* CL */ 258 | #define RPC_PTYPE_REJECT 0x06 /* CL */ 259 | #define RPC_PTYPE_ACK 0x07 /* CL */ 260 | #define RPC_PTYPE_CL_CANCEL 0x08 /* CL */ 261 | #define RPC_PTYPE_FACK 0x09 /* CL */ 262 | #define RPC_PTYPE_CANCEL_ACK 0x0a /* CL */ 263 | #define RPC_PTYPE_BIND 0x0b /* CO */ 264 | #define RPC_PTYPE_BIND_ACK 0x0c /* CO */ 265 | #define RPC_PTYPE_BIND_NAK 0x0d /* CO */ 266 | #define RPC_PTYPE_ALTER_CTX 0x0e /* CO */ 267 | #define RPC_PTYPE_ALTER_CTX_RESP 0x0f /* CO */ 268 | #define RPC_PTYPE_SHUTDOWN 0x11 /* CO */ 269 | #define RPC_PTYPE_CO_CANCEL 0x12 /* CO */ 270 | #define RPC_PTYPE_ORPHANED 0x13 /* CO */ 271 | 272 | /* PFC FLAGS */ 273 | #define PFC_FIRST_FRAG 0x01 /* First fragment. */ 274 | #define PFC_LAST_FRAG 0x02 /* Last fragment. */ 275 | #define PFC_PENDING_CANCEL 0x04 /* Cancel was pending at sender. */ 276 | #define PFC_RESERVED_1 0x08 277 | #define PFC_CONC_MPX 0x10 /* Supports concurrent multiplexing.*/ 278 | #define PFC_DID_NOT_EXECUTE 0x20 /* Fault packet. */ 279 | #define PFC_MAYBE 0x40 /* "Maybe" call semantics requested. */ 280 | #define PFC_OBJECT_UUID 0x80 /* A non-nil object UUID is present. */ 281 | 282 | 283 | /* DCE/RPC Endpoint Mapper Protocol. */ 284 | /* Reference: pubs.opengroup.org/onlinepubs/009629399/apdxo.htm */ 285 | 286 | #define EPT_INSERT 0 287 | #define EPT_DELETE 1 288 | #define EPT_LOOKUP 2 /* Lookup entries in an endpoint map. */ 289 | #define EPT_MAP 4 290 | #define EPT_LOOKUP_FREE 5 291 | 292 | typedef struct ept_lookup_handle { 293 | uint32_t attributes; 294 | uuid_t uuid; 295 | } ept_lookup_handle_t; 296 | 297 | /********************************** 298 | typedef struct ndr_context_handle { 299 | uint32_t context_handle_attributes; 300 | uuid_t context_handle_uuid; 301 | } ndr_context_handle_t; 302 | **********************************/ 303 | 304 | /* Inquiry types. */ 305 | #define RPC_C_EP_ALL_ELTS 0 /* Return all elements from the endpoint map. */ 306 | #define RPC_C_EP_MATCH_BY_IF 1 307 | #define RPC_C_EP_MATCH_BY_OBJ 2 308 | #define RPC_C_EP_MATCH_BY_BOTH 3 309 | 310 | #define RPC_C_VERS_ALL 1 311 | #define RPC_C_VERS_COMPATIBLE 2 312 | #define RPC_C_VERS_EXACT 3 313 | #define RPC_C_VERS_MAJOR_ONLY 4 314 | #define RPC_C_VERS_UPTO 5 315 | #define RPC_C_ERROR_STRING_LEN 256 316 | 317 | #define RPC_C_MGMT_INQ_IF_IDS 0 318 | #define RPC_C_MGMT_INQ_PRINC_NAME 1 319 | #define RPC_C_MGMT_INQ_STATS 2 320 | #define RPC_C_MGMT_IS_SERVER_LISTEN 3 321 | #define RPC_C_MGMT_STOP_SERVER_LISTEN 4 322 | 323 | typedef struct ept_lookup { 324 | uint32_t inquiry_type; 325 | uint32_t object_referent_id; 326 | uuid_t object_uuid; 327 | uint32_t interface_referent_id; 328 | uuid_t interface_uuid; 329 | uint16_t version_major; 330 | uint16_t version_minor; 331 | uint32_t vers_option; 332 | uuid_t handle; 333 | uint32_t max_entries; 334 | } ept_lookup_t; 335 | 336 | #define EPT_MAX_ANNOTATION_SIZE 64 337 | 338 | typedef struct ept_entry { 339 | uuid_t object; 340 | int tower; 341 | char annotation[EPT_MAX_ANNOTATION_SIZE]; 342 | } ept_entry_t, * ept_entry_p_t; 343 | 344 | 345 | /* Internal "opaque" objects. */ 346 | typedef struct buffer { 347 | void* data; /* SND and RCV buffers. */ 348 | size_t bufsize; /* Allocated bytes. */ 349 | size_t offset; 350 | size_t length; /* Length of the marshalled packet. */ 351 | int eof; 352 | uint32_t index; 353 | } buffer_t; 354 | 355 | /* Internal state. */ 356 | typedef struct epmap { 357 | SOCKET sockfd; 358 | char* server; 359 | uint16_t port; 360 | struct sockaddr_in sin; 361 | buffer_t buffer[2]; 362 | uint32_t call_id; 363 | uint32_t assoc_group; /* This is usually ignored. */ 364 | ept_lookup_handle_t handle; 365 | int state; 366 | 367 | p_reject_reason_t reason; /* Rejection reason code in the bind_nak PDU. */ 368 | uint32_t status; /* Run-time fault code or zero (fault PDU). */ 369 | int wsacode; /* wsa code */ 370 | } epmap_t; 371 | 372 | /* Error and status codes. $fixme */ 373 | 374 | #define EPMAP_EOK 0x000 /* Operation completed successfully. */ 375 | #define EPMAP_ENOMEM 0x200 /* A call to malloc() failed. */ 376 | #define EPMAP_EINVAL 0x201 /* An invalid argument was passed to a library function. */ 377 | #define EPMAP_EBADPTR 0x202 /* An invalid pointer was detected. */ 378 | #define EPMAP_EWSAINIT 0x203 /* WSAStartup() initialization failed. */ 379 | #define EPMAP_EDNSFAIL 0x204 /* Could not resolve host name. */ 380 | #define EPMAP_ESOCKET 0x205 /* Could not create socket or connect to server. */ 381 | #define EPMAP_ESEND 0x206 /* A call to send() failed. */ 382 | #define EPMAP_ERECV 0x207 /* A call to recv() failed. */ 383 | 384 | #define EPMAP_EACK 0x300 /* BIND-ACK PDU */ 385 | #define EPMAP_ENAK 0x301 /* BIND-NAK PDU */ 386 | #define EPMAP_EFAULT 0x302 /* Received a FAULT PDU. */ 387 | #define EPMAP_EPROTO 0x303 /* Generic protocol error. */ 388 | #define EPMAP_ENODATA 0x304 /* Status returned by response $fixme */ 389 | 390 | #define EPMAP_EDEBUG 0x400 391 | 392 | #define EPMAPAPI extern 393 | int resolve(PBINFO* pbinfo); 394 | -------------------------------------------------------------------------------- /rpcscan/src/ReflectiveDLLInjection.h: -------------------------------------------------------------------------------- 1 | //===============================================================================================// 2 | // Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com) 3 | // All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, are permitted 6 | // provided that the following conditions are met: 7 | // 8 | // * Redistributions of source code must retain the above copyright notice, this list of 9 | // conditions and the following disclaimer. 10 | // 11 | // * Redistributions in binary form must reproduce the above copyright notice, this list of 12 | // conditions and the following disclaimer in the documentation and/or other materials provided 13 | // with the distribution. 14 | // 15 | // * Neither the name of Harmony Security nor the names of its contributors may be used to 16 | // endorse or promote products derived from this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 19 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 | // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | //===============================================================================================// 28 | #ifndef _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H 29 | #define _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H 30 | //===============================================================================================// 31 | #define WIN32_LEAN_AND_MEAN 32 | #include 33 | 34 | // we declare some common stuff in here... 35 | 36 | #define DLL_QUERY_HMODULE 6 37 | 38 | #define DEREF( name )*(UINT_PTR *)(name) 39 | #define DEREF_64( name )*(DWORD64 *)(name) 40 | #define DEREF_32( name )*(DWORD *)(name) 41 | #define DEREF_16( name )*(WORD *)(name) 42 | #define DEREF_8( name )*(BYTE *)(name) 43 | 44 | typedef ULONG_PTR (WINAPI * REFLECTIVELOADER)( VOID ); 45 | typedef BOOL (WINAPI * DLLMAIN)( HINSTANCE, DWORD, LPVOID ); 46 | 47 | #define DLLEXPORT __declspec( dllexport ) 48 | 49 | //===============================================================================================// 50 | #endif 51 | //===============================================================================================// -------------------------------------------------------------------------------- /rpcscan/src/ReflectiveDll.c: -------------------------------------------------------------------------------- 1 | //===============================================================================================// 2 | // This is a stub for the actuall functionality of the DLL. 3 | //===============================================================================================// 4 | #include "ReflectiveLoader.h" 5 | #include 6 | #include "../rpc.h" 7 | 8 | // Note: REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR and REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN are 9 | // defined in the project properties (Properties->C++->Preprocessor) so as we can specify our own 10 | // DllMain and use the LoadRemoteLibraryR() API to inject this DLL. 11 | 12 | // You can use this value as a pseudo hinstDLL value (defined and set via ReflectiveLoader.c) 13 | extern HINSTANCE hAppInstance; 14 | //===============================================================================================// 15 | 16 | //typedef struct { 17 | // char processName[40]; 18 | // char uuid[40]; 19 | //} UPB; 20 | // 21 | //typedef struct { 22 | // char host[20]; 23 | // int port; 24 | // int lengthUPB; 25 | // UPB upbdata[]; 26 | //} PBINFO; 27 | 28 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved) { 29 | BOOL bReturnValue = TRUE; 30 | switch (dwReason) { 31 | case DLL_QUERY_HMODULE: 32 | if (lpReserved != NULL) 33 | *(HMODULE*)lpReserved = hAppInstance; 34 | break; 35 | case DLL_PROCESS_ATTACH: 36 | hAppInstance = hinstDLL; 37 | 38 | /* print some output to the operator */ 39 | // MessageBoxA(NULL, "123", "1123", MB_OK); 40 | if (lpReserved != NULL) { 41 | PBINFO* para = (PBINFO*)lpReserved; 42 | resolve(para); 43 | //printf("len is %d\n", para->length); 44 | //printf("len is %d\n", para->thread); 45 | //printf("port is %d\n", para->port); 46 | //printf("host is %s\n", para->host); 47 | ////ips(*para); 48 | //for (int i = 0; i < para->lengthUPB;i++) { 49 | // printf("proc is %s\n", para->upbdata[i].processName); 50 | // printf("uuid is %s\n", para->upbdata[i].uuid); 51 | //} 52 | 53 | //MessageBox(NULL, para->host, "test", MB_OK); 54 | //HANDLE hFile = CreateFile("c:\\users\\public\\1.txt", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 55 | //DWORD dwWrite; 56 | 57 | //WriteFile(hFile, lpReserved, 1024, &dwWrite, NULL); 58 | //CloseHandle(hFile); 59 | //printf("Hello from test.dll. Parameter is '%s'\n", (char*)lpReserved); 60 | } 61 | else { 62 | printf("There is no parameter\n"); 63 | } 64 | 65 | /* flush STDOUT */ 66 | fflush(stdout); 67 | 68 | /* we're done, so let's exit */ 69 | ExitProcess(0); 70 | break; 71 | case DLL_PROCESS_DETACH: 72 | case DLL_THREAD_ATTACH: 73 | case DLL_THREAD_DETACH: 74 | break; 75 | } 76 | return bReturnValue; 77 | } -------------------------------------------------------------------------------- /rpcscan/src/ReflectiveLoader.c: -------------------------------------------------------------------------------- 1 | //===============================================================================================// 2 | // Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com) 3 | // All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, are permitted 6 | // provided that the following conditions are met: 7 | // 8 | // * Redistributions of source code must retain the above copyright notice, this list of 9 | // conditions and the following disclaimer. 10 | // 11 | // * Redistributions in binary form must reproduce the above copyright notice, this list of 12 | // conditions and the following disclaimer in the documentation and/or other materials provided 13 | // with the distribution. 14 | // 15 | // * Neither the name of Harmony Security nor the names of its contributors may be used to 16 | // endorse or promote products derived from this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 19 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 | // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | //===============================================================================================// 28 | #include "ReflectiveLoader.h" 29 | //===============================================================================================// 30 | // Our loader will set this to a pseudo correct HINSTANCE/HMODULE value 31 | HINSTANCE hAppInstance = NULL; 32 | //===============================================================================================// 33 | #pragma intrinsic( _ReturnAddress ) 34 | // This function can not be inlined by the compiler or we will not get the address we expect. Ideally 35 | // this code will be compiled with the /O2 and /Ob1 switches. Bonus points if we could take advantage of 36 | // RIP relative addressing in this instance but I dont believe we can do so with the compiler intrinsics 37 | // available (and no inline asm available under x64). 38 | __declspec(noinline) ULONG_PTR caller( VOID ) { return (ULONG_PTR)_ReturnAddress(); } 39 | //===============================================================================================// 40 | 41 | // Note 1: If you want to have your own DllMain, define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN, 42 | // otherwise the DllMain at the end of this file will be used. 43 | 44 | // Note 2: If you are injecting the DLL via LoadRemoteLibraryR, define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR, 45 | // otherwise it is assumed you are calling the ReflectiveLoader via a stub. 46 | 47 | // This is our position independent reflective DLL loader/injector 48 | #ifdef REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR 49 | DLLEXPORT ULONG_PTR WINAPI ReflectiveLoader( LPVOID lpParameter ) 50 | #else 51 | DLLEXPORT ULONG_PTR WINAPI ReflectiveLoader( VOID ) 52 | #endif 53 | { 54 | // the functions we need 55 | LOADLIBRARYA pLoadLibraryA = NULL; 56 | GETPROCADDRESS pGetProcAddress = NULL; 57 | VIRTUALALLOC pVirtualAlloc = NULL; 58 | NTFLUSHINSTRUCTIONCACHE pNtFlushInstructionCache = NULL; 59 | 60 | USHORT usCounter; 61 | 62 | // the initial location of this image in memory 63 | ULONG_PTR uiLibraryAddress; 64 | // the kernels base address and later this images newly loaded base address 65 | ULONG_PTR uiBaseAddress; 66 | 67 | // variables for processing the kernels export table 68 | ULONG_PTR uiAddressArray; 69 | ULONG_PTR uiNameArray; 70 | ULONG_PTR uiExportDir; 71 | ULONG_PTR uiNameOrdinals; 72 | DWORD dwHashValue; 73 | 74 | // variables for loading this image 75 | ULONG_PTR uiHeaderValue; 76 | ULONG_PTR uiValueA; 77 | ULONG_PTR uiValueB; 78 | ULONG_PTR uiValueC; 79 | ULONG_PTR uiValueD; 80 | ULONG_PTR uiValueE; 81 | 82 | // STEP 0: calculate our images current base address 83 | 84 | // we will start searching backwards from our callers return address. 85 | uiLibraryAddress = caller(); 86 | 87 | // loop through memory backwards searching for our images base address 88 | // we dont need SEH style search as we shouldnt generate any access violations with this 89 | while( TRUE ) 90 | { 91 | if( ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE ) 92 | { 93 | uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew; 94 | // some x64 dll's can trigger a bogus signature (IMAGE_DOS_SIGNATURE == 'POP r10'), 95 | // we sanity check the e_lfanew with an upper threshold value of 1024 to avoid problems. 96 | if( uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024 ) 97 | { 98 | uiHeaderValue += uiLibraryAddress; 99 | // break if we have found a valid MZ/PE header 100 | if( ((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE ) 101 | break; 102 | } 103 | } 104 | uiLibraryAddress--; 105 | } 106 | 107 | // STEP 1: process the kernels exports for the functions our loader needs... 108 | 109 | // get the Process Enviroment Block 110 | #ifdef WIN_X64 111 | uiBaseAddress = __readgsqword( 0x60 ); 112 | #else 113 | #ifdef WIN_X86 114 | uiBaseAddress = __readfsdword( 0x30 ); 115 | #else WIN_ARM 116 | uiBaseAddress = *(DWORD *)( (BYTE *)_MoveFromCoprocessor( 15, 0, 13, 0, 2 ) + 0x30 ); 117 | #endif 118 | #endif 119 | 120 | // get the processes loaded modules. ref: http://msdn.microsoft.com/en-us/library/aa813708(VS.85).aspx 121 | uiBaseAddress = (ULONG_PTR)((_PPEB)uiBaseAddress)->pLdr; 122 | 123 | // get the first entry of the InMemoryOrder module list 124 | uiValueA = (ULONG_PTR)((PPEB_LDR_DATA)uiBaseAddress)->InMemoryOrderModuleList.Flink; 125 | while( uiValueA ) 126 | { 127 | // get pointer to current modules name (unicode string) 128 | uiValueB = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.pBuffer; 129 | // set bCounter to the length for the loop 130 | usCounter = ((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.Length; 131 | // clear uiValueC which will store the hash of the module name 132 | uiValueC = 0; 133 | 134 | // compute the hash of the module name... 135 | do 136 | { 137 | uiValueC = ror( (DWORD)uiValueC ); 138 | // normalize to uppercase if the madule name is in lowercase 139 | if( *((BYTE *)uiValueB) >= 'a' ) 140 | uiValueC += *((BYTE *)uiValueB) - 0x20; 141 | else 142 | uiValueC += *((BYTE *)uiValueB); 143 | uiValueB++; 144 | } while( --usCounter ); 145 | 146 | // compare the hash with that of kernel32.dll 147 | if( (DWORD)uiValueC == KERNEL32DLL_HASH ) 148 | { 149 | // get this modules base address 150 | uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase; 151 | 152 | // get the VA of the modules NT Header 153 | uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew; 154 | 155 | // uiNameArray = the address of the modules export directory entry 156 | uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ]; 157 | 158 | // get the VA of the export directory 159 | uiExportDir = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress ); 160 | 161 | // get the VA for the array of name pointers 162 | uiNameArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNames ); 163 | 164 | // get the VA for the array of name ordinals 165 | uiNameOrdinals = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNameOrdinals ); 166 | 167 | usCounter = 3; 168 | 169 | // loop while we still have imports to find 170 | while( usCounter > 0 ) 171 | { 172 | // compute the hash values for this function name 173 | dwHashValue = hash( (char *)( uiBaseAddress + DEREF_32( uiNameArray ) ) ); 174 | 175 | // if we have found a function we want we get its virtual address 176 | if( dwHashValue == LOADLIBRARYA_HASH || dwHashValue == GETPROCADDRESS_HASH || dwHashValue == VIRTUALALLOC_HASH ) 177 | { 178 | // get the VA for the array of addresses 179 | uiAddressArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions ); 180 | 181 | // use this functions name ordinal as an index into the array of name pointers 182 | uiAddressArray += ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) ); 183 | 184 | // store this functions VA 185 | if( dwHashValue == LOADLIBRARYA_HASH ) 186 | pLoadLibraryA = (LOADLIBRARYA)( uiBaseAddress + DEREF_32( uiAddressArray ) ); 187 | else if( dwHashValue == GETPROCADDRESS_HASH ) 188 | pGetProcAddress = (GETPROCADDRESS)( uiBaseAddress + DEREF_32( uiAddressArray ) ); 189 | else if( dwHashValue == VIRTUALALLOC_HASH ) 190 | pVirtualAlloc = (VIRTUALALLOC)( uiBaseAddress + DEREF_32( uiAddressArray ) ); 191 | 192 | // decrement our counter 193 | usCounter--; 194 | } 195 | 196 | // get the next exported function name 197 | uiNameArray += sizeof(DWORD); 198 | 199 | // get the next exported function name ordinal 200 | uiNameOrdinals += sizeof(WORD); 201 | } 202 | } 203 | else if( (DWORD)uiValueC == NTDLLDLL_HASH ) 204 | { 205 | // get this modules base address 206 | uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase; 207 | 208 | // get the VA of the modules NT Header 209 | uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew; 210 | 211 | // uiNameArray = the address of the modules export directory entry 212 | uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ]; 213 | 214 | // get the VA of the export directory 215 | uiExportDir = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress ); 216 | 217 | // get the VA for the array of name pointers 218 | uiNameArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNames ); 219 | 220 | // get the VA for the array of name ordinals 221 | uiNameOrdinals = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNameOrdinals ); 222 | 223 | usCounter = 1; 224 | 225 | // loop while we still have imports to find 226 | while( usCounter > 0 ) 227 | { 228 | // compute the hash values for this function name 229 | dwHashValue = hash( (char *)( uiBaseAddress + DEREF_32( uiNameArray ) ) ); 230 | 231 | // if we have found a function we want we get its virtual address 232 | if( dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH ) 233 | { 234 | // get the VA for the array of addresses 235 | uiAddressArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions ); 236 | 237 | // use this functions name ordinal as an index into the array of name pointers 238 | uiAddressArray += ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) ); 239 | 240 | // store this functions VA 241 | if( dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH ) 242 | pNtFlushInstructionCache = (NTFLUSHINSTRUCTIONCACHE)( uiBaseAddress + DEREF_32( uiAddressArray ) ); 243 | 244 | // decrement our counter 245 | usCounter--; 246 | } 247 | 248 | // get the next exported function name 249 | uiNameArray += sizeof(DWORD); 250 | 251 | // get the next exported function name ordinal 252 | uiNameOrdinals += sizeof(WORD); 253 | } 254 | } 255 | 256 | // we stop searching when we have found everything we need. 257 | if( pLoadLibraryA && pGetProcAddress && pVirtualAlloc && pNtFlushInstructionCache ) 258 | break; 259 | 260 | // get the next entry 261 | uiValueA = DEREF( uiValueA ); 262 | } 263 | 264 | // STEP 2: load our image into a new permanent location in memory... 265 | 266 | // get the VA of the NT Header for the PE to be loaded 267 | uiHeaderValue = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew; 268 | 269 | // allocate all the memory for the DLL to be loaded into. we can load at any address because we will 270 | // relocate the image. Also zeros all memory and marks it as READ, WRITE and EXECUTE to avoid any problems. 271 | uiBaseAddress = (ULONG_PTR)pVirtualAlloc( NULL, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE ); 272 | 273 | // we must now copy over the headers 274 | uiValueA = ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfHeaders; 275 | uiValueB = uiLibraryAddress; 276 | uiValueC = uiBaseAddress; 277 | 278 | while( uiValueA-- ) 279 | *(BYTE *)uiValueC++ = *(BYTE *)uiValueB++; 280 | 281 | // STEP 3: load in all of our sections... 282 | 283 | // uiValueA = the VA of the first section 284 | uiValueA = ( (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader + ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.SizeOfOptionalHeader ); 285 | 286 | // itterate through all sections, loading them into memory. 287 | uiValueE = ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.NumberOfSections; 288 | while( uiValueE-- ) 289 | { 290 | // uiValueB is the VA for this section 291 | uiValueB = ( uiBaseAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->VirtualAddress ); 292 | 293 | // uiValueC if the VA for this sections data 294 | uiValueC = ( uiLibraryAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->PointerToRawData ); 295 | 296 | // copy the section over 297 | uiValueD = ((PIMAGE_SECTION_HEADER)uiValueA)->SizeOfRawData; 298 | 299 | while( uiValueD-- ) 300 | *(BYTE *)uiValueB++ = *(BYTE *)uiValueC++; 301 | 302 | // get the VA of the next section 303 | uiValueA += sizeof( IMAGE_SECTION_HEADER ); 304 | } 305 | 306 | // STEP 4: process our images import table... 307 | 308 | // uiValueB = the address of the import directory 309 | uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ]; 310 | 311 | // we assume their is an import table to process 312 | // uiValueC is the first entry in the import table 313 | uiValueC = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress ); 314 | 315 | // itterate through all imports 316 | while( ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name ) 317 | { 318 | // use LoadLibraryA to load the imported module into memory 319 | uiLibraryAddress = (ULONG_PTR)pLoadLibraryA( (LPCSTR)( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name ) ); 320 | 321 | // uiValueD = VA of the OriginalFirstThunk 322 | uiValueD = ( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->OriginalFirstThunk ); 323 | 324 | // uiValueA = VA of the IAT (via first thunk not origionalfirstthunk) 325 | uiValueA = ( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->FirstThunk ); 326 | 327 | // itterate through all imported functions, importing by ordinal if no name present 328 | while( DEREF(uiValueA) ) 329 | { 330 | // sanity check uiValueD as some compilers only import by FirstThunk 331 | if( uiValueD && ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal & IMAGE_ORDINAL_FLAG ) 332 | { 333 | // get the VA of the modules NT Header 334 | uiExportDir = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew; 335 | 336 | // uiNameArray = the address of the modules export directory entry 337 | uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ]; 338 | 339 | // get the VA of the export directory 340 | uiExportDir = ( uiLibraryAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress ); 341 | 342 | // get the VA for the array of addresses 343 | uiAddressArray = ( uiLibraryAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions ); 344 | 345 | // use the import ordinal (- export ordinal base) as an index into the array of addresses 346 | uiAddressArray += ( ( IMAGE_ORDINAL( ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal ) - ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->Base ) * sizeof(DWORD) ); 347 | 348 | // patch in the address for this imported function 349 | DEREF(uiValueA) = ( uiLibraryAddress + DEREF_32(uiAddressArray) ); 350 | } 351 | else 352 | { 353 | // get the VA of this functions import by name struct 354 | uiValueB = ( uiBaseAddress + DEREF(uiValueA) ); 355 | 356 | // use GetProcAddress and patch in the address for this imported function 357 | DEREF(uiValueA) = (ULONG_PTR)pGetProcAddress( (HMODULE)uiLibraryAddress, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)uiValueB)->Name ); 358 | } 359 | // get the next imported function 360 | uiValueA += sizeof( ULONG_PTR ); 361 | if( uiValueD ) 362 | uiValueD += sizeof( ULONG_PTR ); 363 | } 364 | 365 | // get the next import 366 | uiValueC += sizeof( IMAGE_IMPORT_DESCRIPTOR ); 367 | } 368 | 369 | // STEP 5: process all of our images relocations... 370 | 371 | // calculate the base address delta and perform relocations (even if we load at desired image base) 372 | uiLibraryAddress = uiBaseAddress - ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.ImageBase; 373 | 374 | // uiValueB = the address of the relocation directory 375 | uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_BASERELOC ]; 376 | 377 | // check if their are any relocations present 378 | if( ((PIMAGE_DATA_DIRECTORY)uiValueB)->Size ) 379 | { 380 | // uiValueC is now the first entry (IMAGE_BASE_RELOCATION) 381 | uiValueC = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress ); 382 | 383 | // and we itterate through all entries... 384 | while( ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock ) 385 | { 386 | // uiValueA = the VA for this relocation block 387 | uiValueA = ( uiBaseAddress + ((PIMAGE_BASE_RELOCATION)uiValueC)->VirtualAddress ); 388 | 389 | // uiValueB = number of entries in this relocation block 390 | uiValueB = ( ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION) ) / sizeof( IMAGE_RELOC ); 391 | 392 | // uiValueD is now the first entry in the current relocation block 393 | uiValueD = uiValueC + sizeof(IMAGE_BASE_RELOCATION); 394 | 395 | // we itterate through all the entries in the current block... 396 | while( uiValueB-- ) 397 | { 398 | // perform the relocation, skipping IMAGE_REL_BASED_ABSOLUTE as required. 399 | // we dont use a switch statement to avoid the compiler building a jump table 400 | // which would not be very position independent! 401 | if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_DIR64 ) 402 | *(ULONG_PTR *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += uiLibraryAddress; 403 | else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGHLOW ) 404 | *(DWORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += (DWORD)uiLibraryAddress; 405 | #ifdef WIN_ARM 406 | // Note: On ARM, the compiler optimization /O2 seems to introduce an off by one issue, possibly a code gen bug. Using /O1 instead avoids this problem. 407 | else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_ARM_MOV32T ) 408 | { 409 | register DWORD dwInstruction; 410 | register DWORD dwAddress; 411 | register WORD wImm; 412 | // get the MOV.T instructions DWORD value (We add 4 to the offset to go past the first MOV.W which handles the low word) 413 | dwInstruction = *(DWORD *)( uiValueA + ((PIMAGE_RELOC)uiValueD)->offset + sizeof(DWORD) ); 414 | // flip the words to get the instruction as expected 415 | dwInstruction = MAKELONG( HIWORD(dwInstruction), LOWORD(dwInstruction) ); 416 | // sanity chack we are processing a MOV instruction... 417 | if( (dwInstruction & ARM_MOV_MASK) == ARM_MOVT ) 418 | { 419 | // pull out the encoded 16bit value (the high portion of the address-to-relocate) 420 | wImm = (WORD)( dwInstruction & 0x000000FF); 421 | wImm |= (WORD)((dwInstruction & 0x00007000) >> 4); 422 | wImm |= (WORD)((dwInstruction & 0x04000000) >> 15); 423 | wImm |= (WORD)((dwInstruction & 0x000F0000) >> 4); 424 | // apply the relocation to the target address 425 | dwAddress = ( (WORD)HIWORD(uiLibraryAddress) + wImm ) & 0xFFFF; 426 | // now create a new instruction with the same opcode and register param. 427 | dwInstruction = (DWORD)( dwInstruction & ARM_MOV_MASK2 ); 428 | // patch in the relocated address... 429 | dwInstruction |= (DWORD)(dwAddress & 0x00FF); 430 | dwInstruction |= (DWORD)(dwAddress & 0x0700) << 4; 431 | dwInstruction |= (DWORD)(dwAddress & 0x0800) << 15; 432 | dwInstruction |= (DWORD)(dwAddress & 0xF000) << 4; 433 | // now flip the instructions words and patch back into the code... 434 | *(DWORD *)( uiValueA + ((PIMAGE_RELOC)uiValueD)->offset + sizeof(DWORD) ) = MAKELONG( HIWORD(dwInstruction), LOWORD(dwInstruction) ); 435 | } 436 | } 437 | #endif 438 | else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGH ) 439 | *(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += HIWORD(uiLibraryAddress); 440 | else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_LOW ) 441 | *(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += LOWORD(uiLibraryAddress); 442 | 443 | // get the next entry in the current relocation block 444 | uiValueD += sizeof( IMAGE_RELOC ); 445 | } 446 | 447 | // get the next entry in the relocation directory 448 | uiValueC = uiValueC + ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock; 449 | } 450 | } 451 | 452 | // STEP 6: call our images entry point 453 | 454 | // uiValueA = the VA of our newly loaded DLL/EXE's entry point 455 | uiValueA = ( uiBaseAddress + ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.AddressOfEntryPoint ); 456 | 457 | // We must flush the instruction cache to avoid stale code being used which was updated by our relocation processing. 458 | pNtFlushInstructionCache( (HANDLE)-1, NULL, 0 ); 459 | 460 | // call our respective entry point, fudging our hInstance value 461 | #ifdef REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR 462 | // if we are injecting a DLL via LoadRemoteLibraryR we call DllMain and pass in our parameter (via the DllMain lpReserved parameter) 463 | ((DLLMAIN)uiValueA)( (HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, lpParameter ); 464 | #else 465 | // if we are injecting an DLL via a stub we call DllMain with no parameter 466 | ((DLLMAIN)uiValueA)( (HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, NULL ); 467 | #endif 468 | 469 | // STEP 8: return our new entry point address so whatever called us can call DllMain() if needed. 470 | return uiValueA; 471 | } 472 | //===============================================================================================// 473 | #ifndef REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN 474 | 475 | BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved ) 476 | { 477 | BOOL bReturnValue = TRUE; 478 | switch( dwReason ) 479 | { 480 | case DLL_QUERY_HMODULE: 481 | if( lpReserved != NULL ) 482 | *(HMODULE *)lpReserved = hAppInstance; 483 | break; 484 | case DLL_PROCESS_ATTACH: 485 | hAppInstance = hinstDLL; 486 | break; 487 | case DLL_PROCESS_DETACH: 488 | case DLL_THREAD_ATTACH: 489 | case DLL_THREAD_DETACH: 490 | break; 491 | } 492 | return bReturnValue; 493 | } 494 | 495 | #endif 496 | //===============================================================================================// 497 | -------------------------------------------------------------------------------- /rpcscan/src/ReflectiveLoader.h: -------------------------------------------------------------------------------- 1 | //===============================================================================================// 2 | // Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com) 3 | // All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, are permitted 6 | // provided that the following conditions are met: 7 | // 8 | // * Redistributions of source code must retain the above copyright notice, this list of 9 | // conditions and the following disclaimer. 10 | // 11 | // * Redistributions in binary form must reproduce the above copyright notice, this list of 12 | // conditions and the following disclaimer in the documentation and/or other materials provided 13 | // with the distribution. 14 | // 15 | // * Neither the name of Harmony Security nor the names of its contributors may be used to 16 | // endorse or promote products derived from this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 19 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 | // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | //===============================================================================================// 28 | #ifndef _REFLECTIVEDLLINJECTION_REFLECTIVELOADER_H 29 | #define _REFLECTIVEDLLINJECTION_REFLECTIVELOADER_H 30 | //===============================================================================================// 31 | #define WIN32_LEAN_AND_MEAN 32 | #include 33 | #include 34 | #include 35 | 36 | #include "ReflectiveDLLInjection.h" 37 | 38 | typedef HMODULE (WINAPI * LOADLIBRARYA)( LPCSTR ); 39 | typedef FARPROC (WINAPI * GETPROCADDRESS)( HMODULE, LPCSTR ); 40 | typedef LPVOID (WINAPI * VIRTUALALLOC)( LPVOID, SIZE_T, DWORD, DWORD ); 41 | typedef DWORD (NTAPI * NTFLUSHINSTRUCTIONCACHE)( HANDLE, PVOID, ULONG ); 42 | 43 | #define KERNEL32DLL_HASH 0x6A4ABC5B 44 | #define NTDLLDLL_HASH 0x3CFA685D 45 | 46 | #define LOADLIBRARYA_HASH 0xEC0E4E8E 47 | #define GETPROCADDRESS_HASH 0x7C0DFCAA 48 | #define VIRTUALALLOC_HASH 0x91AFCA54 49 | #define NTFLUSHINSTRUCTIONCACHE_HASH 0x534C0AB8 50 | 51 | #define IMAGE_REL_BASED_ARM_MOV32A 5 52 | #define IMAGE_REL_BASED_ARM_MOV32T 7 53 | 54 | #define ARM_MOV_MASK (DWORD)(0xFBF08000) 55 | #define ARM_MOV_MASK2 (DWORD)(0xFBF08F00) 56 | #define ARM_MOVW 0xF2400000 57 | #define ARM_MOVT 0xF2C00000 58 | 59 | #define HASH_KEY 13 60 | //===============================================================================================// 61 | #pragma intrinsic( _rotr ) 62 | 63 | __forceinline DWORD ror( DWORD d ) 64 | { 65 | return _rotr( d, HASH_KEY ); 66 | } 67 | 68 | __forceinline DWORD hash( char * c ) 69 | { 70 | register DWORD h = 0; 71 | do 72 | { 73 | h = ror( h ); 74 | h += *c; 75 | } while( *++c ); 76 | 77 | return h; 78 | } 79 | //===============================================================================================// 80 | typedef struct _UNICODE_STR 81 | { 82 | USHORT Length; 83 | USHORT MaximumLength; 84 | PWSTR pBuffer; 85 | } UNICODE_STR, *PUNICODE_STR; 86 | 87 | // WinDbg> dt -v ntdll!_LDR_DATA_TABLE_ENTRY 88 | //__declspec( align(8) ) 89 | typedef struct _LDR_DATA_TABLE_ENTRY 90 | { 91 | //LIST_ENTRY InLoadOrderLinks; // As we search from PPEB_LDR_DATA->InMemoryOrderModuleList we dont use the first entry. 92 | LIST_ENTRY InMemoryOrderModuleList; 93 | LIST_ENTRY InInitializationOrderModuleList; 94 | PVOID DllBase; 95 | PVOID EntryPoint; 96 | ULONG SizeOfImage; 97 | UNICODE_STR FullDllName; 98 | UNICODE_STR BaseDllName; 99 | ULONG Flags; 100 | SHORT LoadCount; 101 | SHORT TlsIndex; 102 | LIST_ENTRY HashTableEntry; 103 | ULONG TimeDateStamp; 104 | } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; 105 | 106 | // WinDbg> dt -v ntdll!_PEB_LDR_DATA 107 | typedef struct _PEB_LDR_DATA //, 7 elements, 0x28 bytes 108 | { 109 | DWORD dwLength; 110 | DWORD dwInitialized; 111 | LPVOID lpSsHandle; 112 | LIST_ENTRY InLoadOrderModuleList; 113 | LIST_ENTRY InMemoryOrderModuleList; 114 | LIST_ENTRY InInitializationOrderModuleList; 115 | LPVOID lpEntryInProgress; 116 | } PEB_LDR_DATA, * PPEB_LDR_DATA; 117 | 118 | // WinDbg> dt -v ntdll!_PEB_FREE_BLOCK 119 | typedef struct _PEB_FREE_BLOCK // 2 elements, 0x8 bytes 120 | { 121 | struct _PEB_FREE_BLOCK * pNext; 122 | DWORD dwSize; 123 | } PEB_FREE_BLOCK, * PPEB_FREE_BLOCK; 124 | 125 | // struct _PEB is defined in Winternl.h but it is incomplete 126 | // WinDbg> dt -v ntdll!_PEB 127 | typedef struct __PEB // 65 elements, 0x210 bytes 128 | { 129 | BYTE bInheritedAddressSpace; 130 | BYTE bReadImageFileExecOptions; 131 | BYTE bBeingDebugged; 132 | BYTE bSpareBool; 133 | LPVOID lpMutant; 134 | LPVOID lpImageBaseAddress; 135 | PPEB_LDR_DATA pLdr; 136 | LPVOID lpProcessParameters; 137 | LPVOID lpSubSystemData; 138 | LPVOID lpProcessHeap; 139 | PRTL_CRITICAL_SECTION pFastPebLock; 140 | LPVOID lpFastPebLockRoutine; 141 | LPVOID lpFastPebUnlockRoutine; 142 | DWORD dwEnvironmentUpdateCount; 143 | LPVOID lpKernelCallbackTable; 144 | DWORD dwSystemReserved; 145 | DWORD dwAtlThunkSListPtr32; 146 | PPEB_FREE_BLOCK pFreeList; 147 | DWORD dwTlsExpansionCounter; 148 | LPVOID lpTlsBitmap; 149 | DWORD dwTlsBitmapBits[2]; 150 | LPVOID lpReadOnlySharedMemoryBase; 151 | LPVOID lpReadOnlySharedMemoryHeap; 152 | LPVOID lpReadOnlyStaticServerData; 153 | LPVOID lpAnsiCodePageData; 154 | LPVOID lpOemCodePageData; 155 | LPVOID lpUnicodeCaseTableData; 156 | DWORD dwNumberOfProcessors; 157 | DWORD dwNtGlobalFlag; 158 | LARGE_INTEGER liCriticalSectionTimeout; 159 | DWORD dwHeapSegmentReserve; 160 | DWORD dwHeapSegmentCommit; 161 | DWORD dwHeapDeCommitTotalFreeThreshold; 162 | DWORD dwHeapDeCommitFreeBlockThreshold; 163 | DWORD dwNumberOfHeaps; 164 | DWORD dwMaximumNumberOfHeaps; 165 | LPVOID lpProcessHeaps; 166 | LPVOID lpGdiSharedHandleTable; 167 | LPVOID lpProcessStarterHelper; 168 | DWORD dwGdiDCAttributeList; 169 | LPVOID lpLoaderLock; 170 | DWORD dwOSMajorVersion; 171 | DWORD dwOSMinorVersion; 172 | WORD wOSBuildNumber; 173 | WORD wOSCSDVersion; 174 | DWORD dwOSPlatformId; 175 | DWORD dwImageSubsystem; 176 | DWORD dwImageSubsystemMajorVersion; 177 | DWORD dwImageSubsystemMinorVersion; 178 | DWORD dwImageProcessAffinityMask; 179 | DWORD dwGdiHandleBuffer[34]; 180 | LPVOID lpPostProcessInitRoutine; 181 | LPVOID lpTlsExpansionBitmap; 182 | DWORD dwTlsExpansionBitmapBits[32]; 183 | DWORD dwSessionId; 184 | ULARGE_INTEGER liAppCompatFlags; 185 | ULARGE_INTEGER liAppCompatFlagsUser; 186 | LPVOID lppShimData; 187 | LPVOID lpAppCompatInfo; 188 | UNICODE_STR usCSDVersion; 189 | LPVOID lpActivationContextData; 190 | LPVOID lpProcessAssemblyStorageMap; 191 | LPVOID lpSystemDefaultActivationContextData; 192 | LPVOID lpSystemAssemblyStorageMap; 193 | DWORD dwMinimumStackCommit; 194 | } _PEB, * _PPEB; 195 | 196 | typedef struct 197 | { 198 | WORD offset:12; 199 | WORD type:4; 200 | } IMAGE_RELOC, *PIMAGE_RELOC; 201 | //===============================================================================================// 202 | #endif 203 | //===============================================================================================// 204 | -------------------------------------------------------------------------------- /rpcscan/thread_pool.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * @file thread_pool.hpp 5 | * @author Barak Shoshany (baraksh@gmail.com) (http://baraksh.com) 6 | * @version 2.0.0 7 | * @date 2021-08-14 8 | * @copyright Copyright (c) 2021 Barak Shoshany. Licensed under the MIT license. If you use this library in published research, please cite it as follows: 9 | * - Barak Shoshany, "A C++17 Thread Pool for High-Performance Scientific Computing", doi:10.5281/zenodo.4742687, arXiv:2105.00613 (May 2021) 10 | * 11 | * @brief A C++17 thread pool for high-performance scientific computing. 12 | * @details A modern C++17-compatible thread pool implementation, built from scratch with high-performance scientific computing in mind. The thread pool is implemented as a single lightweight and self-contained class, and does not have any dependencies other than the C++17 standard library, thus allowing a great degree of portability. In particular, this implementation does not utilize OpenMP or any other high-level multithreading APIs, and thus gives the programmer precise low-level control over the details of the parallelization, which permits more robust optimizations. The thread pool was extensively tested on both AMD and Intel CPUs with up to 40 cores and 80 threads. Other features include automatic generation of futures and easy parallelization of loops. Two helper classes enable synchronizing printing to an output stream by different threads and measuring execution time for benchmarking purposes. Please visit the GitHub repository at https://github.com/bshoshany/thread-pool for documentation and updates, or to submit feature requests and bug reports. 13 | */ 14 | 15 | #define THREAD_POOL_VERSION "v2.0.0 (2021-08-14)" 16 | 17 | #include // std::atomic 18 | #include // std::chrono 19 | #include // std::int_fast64_t, std::uint_fast32_t 20 | #include // std::function 21 | #include // std::future, std::promise 22 | #include // std::cout, std::ostream 23 | #include // std::shared_ptr, std::unique_ptr 24 | #include // std::mutex, std::scoped_lock 25 | #include // std::queue 26 | #include // std::this_thread, std::thread 27 | #include // std::common_type_t, std::decay_t, std::enable_if_t, std::is_void_v, std::invoke_result_t 28 | #include // std::move 29 | 30 | // ============================================================================================= // 31 | // Begin class thread_pool // 32 | 33 | /** 34 | * @brief A C++17 thread pool class. The user submits tasks to be executed into a queue. Whenever a thread becomes available, it pops a task from the queue and executes it. Each task is automatically assigned a future, which can be used to wait for the task to finish executing and/or obtain its eventual return value. 35 | */ 36 | class thread_pool 37 | { 38 | typedef std::uint_fast32_t ui32; 39 | typedef std::uint_fast64_t ui64; 40 | 41 | public: 42 | // ============================ 43 | // Constructors and destructors 44 | // ============================ 45 | 46 | /** 47 | * @brief Construct a new thread pool. 48 | * 49 | * @param _thread_count The number of threads to use. The default value is the total number of hardware threads available, as reported by the implementation. With a hyperthreaded CPU, this will be twice the number of CPU cores. If the argument is zero, the default value will be used instead. 50 | */ 51 | thread_pool(const ui32& _thread_count = std::thread::hardware_concurrency()) 52 | : thread_count(_thread_count ? _thread_count : std::thread::hardware_concurrency()), threads(new std::thread[_thread_count ? _thread_count : std::thread::hardware_concurrency()]) 53 | { 54 | create_threads(); 55 | } 56 | 57 | /** 58 | * @brief Destruct the thread pool. Waits for all tasks to complete, then destroys all threads. Note that if the variable paused is set to true, then any tasks still in the queue will never be executed. 59 | */ 60 | ~thread_pool() 61 | { 62 | wait_for_tasks(); 63 | running = false; 64 | destroy_threads(); 65 | } 66 | 67 | // ======================= 68 | // Public member functions 69 | // ======================= 70 | 71 | /** 72 | * @brief Get the number of tasks currently waiting in the queue to be executed by the threads. 73 | * 74 | * @return The number of queued tasks. 75 | */ 76 | ui64 get_tasks_queued() const 77 | { 78 | const std::scoped_lock lock(queue_mutex); 79 | return tasks.size(); 80 | } 81 | 82 | /** 83 | * @brief Get the number of tasks currently being executed by the threads. 84 | * 85 | * @return The number of running tasks. 86 | */ 87 | ui32 get_tasks_running() const 88 | { 89 | return tasks_total - (ui32)get_tasks_queued(); 90 | } 91 | 92 | /** 93 | * @brief Get the total number of unfinished tasks - either still in the queue, or running in a thread. 94 | * 95 | * @return The total number of tasks. 96 | */ 97 | ui32 get_tasks_total() const 98 | { 99 | return tasks_total; 100 | } 101 | 102 | /** 103 | * @brief Get the number of threads in the pool. 104 | * 105 | * @return The number of threads. 106 | */ 107 | ui32 get_thread_count() const 108 | { 109 | return thread_count; 110 | } 111 | 112 | /** 113 | * @brief Parallelize a loop by splitting it into blocks, submitting each block separately to the thread pool, and waiting for all blocks to finish executing. The user supplies a loop function, which will be called once per block and should iterate over the block's range. 114 | * 115 | * @tparam T1 The type of the first index in the loop. Should be a signed or unsigned integer. 116 | * @tparam T2 The type of the index after the last index in the loop. Should be a signed or unsigned integer. If T1 is not the same as T2, a common type will be automatically inferred. 117 | * @tparam F The type of the function to loop through. 118 | * @param first_index The first index in the loop. 119 | * @param index_after_last The index after the last index in the loop. The loop will iterate from first_index to (index_after_last - 1) inclusive. In other words, it will be equivalent to "for (T i = first_index; i < index_after_last; i++)". Note that if first_index == index_after_last, the function will terminate without doing anything. 120 | * @param loop The function to loop through. Will be called once per block. Should take exactly two arguments: the first index in the block and the index after the last index in the block. loop(start, end) should typically involve a loop of the form "for (T i = start; i < end; i++)". 121 | * @param num_blocks The maximum number of blocks to split the loop into. The default is to use the number of threads in the pool. 122 | */ 123 | template 124 | void parallelize_loop(const T1& first_index, const T2& index_after_last, const F& loop, ui32 num_blocks = 0) 125 | { 126 | typedef std::common_type_t T; 127 | T the_first_index = (T)first_index; 128 | T last_index = (T)index_after_last; 129 | if (the_first_index == last_index) 130 | return; 131 | if (last_index < the_first_index) 132 | { 133 | T temp = last_index; 134 | last_index = the_first_index; 135 | the_first_index = temp; 136 | } 137 | last_index--; 138 | if (num_blocks == 0) 139 | num_blocks = thread_count; 140 | ui64 total_size = (ui64)(last_index - the_first_index + 1); 141 | ui64 block_size = (ui64)(total_size / num_blocks); 142 | if (block_size == 0) 143 | { 144 | block_size = 1; 145 | num_blocks = (ui32)total_size > 1 ? (ui32)total_size : 1; 146 | } 147 | std::atomic blocks_running = 0; 148 | for (ui32 t = 0; t < num_blocks; t++) 149 | { 150 | T start = ((T)(t * block_size) + the_first_index); 151 | T end = (t == num_blocks - 1) ? last_index + 1 : ((T)((t + 1) * block_size) + the_first_index); 152 | blocks_running++; 153 | push_task([start, end, &loop, &blocks_running] 154 | { 155 | loop(start, end); 156 | blocks_running--; 157 | }); 158 | } 159 | while (blocks_running != 0) 160 | { 161 | sleep_or_yield(); 162 | } 163 | } 164 | 165 | /** 166 | * @brief Push a function with no arguments or return value into the task queue. 167 | * 168 | * @tparam F The type of the function. 169 | * @param task The function to push. 170 | */ 171 | template 172 | void push_task(const F& task) 173 | { 174 | tasks_total++; 175 | { 176 | const std::scoped_lock lock(queue_mutex); 177 | tasks.push(std::function(task)); 178 | } 179 | } 180 | 181 | /** 182 | * @brief Push a function with arguments, but no return value, into the task queue. 183 | * @details The function is wrapped inside a lambda in order to hide the arguments, as the tasks in the queue must be of type std::function, so they cannot have any arguments or return value. If no arguments are provided, the other overload will be used, in order to avoid the (slight) overhead of using a lambda. 184 | * 185 | * @tparam F The type of the function. 186 | * @tparam A The types of the arguments. 187 | * @param task The function to push. 188 | * @param args The arguments to pass to the function. 189 | */ 190 | template 191 | void push_task(const F& task, const A &...args) 192 | { 193 | push_task([task, args...] 194 | { task(args...); }); 195 | } 196 | 197 | /** 198 | * @brief Reset the number of threads in the pool. Waits for all currently running tasks to be completed, then destroys all threads in the pool and creates a new thread pool with the new number of threads. Any tasks that were waiting in the queue before the pool was reset will then be executed by the new threads. If the pool was paused before resetting it, the new pool will be paused as well. 199 | * 200 | * @param _thread_count The number of threads to use. The default value is the total number of hardware threads available, as reported by the implementation. With a hyperthreaded CPU, this will be twice the number of CPU cores. If the argument is zero, the default value will be used instead. 201 | */ 202 | void reset(const ui32& _thread_count = std::thread::hardware_concurrency()) 203 | { 204 | bool was_paused = paused; 205 | paused = true; 206 | wait_for_tasks(); 207 | running = false; 208 | destroy_threads(); 209 | thread_count = _thread_count ? _thread_count : std::thread::hardware_concurrency(); 210 | threads.reset(new std::thread[thread_count]); 211 | paused = was_paused; 212 | running = true; 213 | create_threads(); 214 | } 215 | 216 | /** 217 | * @brief Submit a function with zero or more arguments and no return value into the task queue, and get an std::future that will be set to true upon completion of the task. 218 | * 219 | * @tparam F The type of the function. 220 | * @tparam A The types of the zero or more arguments to pass to the function. 221 | * @param task The function to submit. 222 | * @param args The zero or more arguments to pass to the function. 223 | * @return A future to be used later to check if the function has finished its execution. 224 | */ 225 | template , std::decay_t...>>>> 226 | std::future submit(const F& task, const A &...args) 227 | { 228 | std::shared_ptr> task_promise(new std::promise); 229 | std::future future = task_promise->get_future(); 230 | push_task([task, args..., task_promise] 231 | { 232 | try 233 | { 234 | task(args...); 235 | task_promise->set_value(true); 236 | } 237 | catch (...) 238 | { 239 | try 240 | { 241 | task_promise->set_exception(std::current_exception()); 242 | } 243 | catch (...) 244 | { 245 | } 246 | } 247 | }); 248 | return future; 249 | } 250 | 251 | /** 252 | * @brief Submit a function with zero or more arguments and a return value into the task queue, and get a future for its eventual returned value. 253 | * 254 | * @tparam F The type of the function. 255 | * @tparam A The types of the zero or more arguments to pass to the function. 256 | * @tparam R The return type of the function. 257 | * @param task The function to submit. 258 | * @param args The zero or more arguments to pass to the function. 259 | * @return A future to be used later to obtain the function's returned value, waiting for it to finish its execution if needed. 260 | */ 261 | template , std::decay_t...>, typename = std::enable_if_t>> 262 | std::future submit(const F& task, const A &...args) 263 | { 264 | std::shared_ptr> task_promise(new std::promise); 265 | std::future future = task_promise->get_future(); 266 | push_task([task, args..., task_promise] 267 | { 268 | try 269 | { 270 | task_promise->set_value(task(args...)); 271 | } 272 | catch (...) 273 | { 274 | try 275 | { 276 | task_promise->set_exception(std::current_exception()); 277 | } 278 | catch (...) 279 | { 280 | } 281 | } 282 | }); 283 | return future; 284 | } 285 | 286 | /** 287 | * @brief Wait for tasks to be completed. Normally, this function waits for all tasks, both those that are currently running in the threads and those that are still waiting in the queue. However, if the variable paused is set to true, this function only waits for the currently running tasks (otherwise it would wait forever). To wait for a specific task, use submit() instead, and call the wait() member function of the generated future. 288 | */ 289 | void wait_for_tasks() 290 | { 291 | while (true) 292 | { 293 | if (!paused) 294 | { 295 | if (tasks_total == 0) 296 | break; 297 | } 298 | else 299 | { 300 | if (get_tasks_running() == 0) 301 | break; 302 | } 303 | sleep_or_yield(); 304 | } 305 | } 306 | 307 | // =========== 308 | // Public data 309 | // =========== 310 | 311 | /** 312 | * @brief An atomic variable indicating to the workers to pause. When set to true, the workers temporarily stop popping new tasks out of the queue, although any tasks already executed will keep running until they are done. Set to false again to resume popping tasks. 313 | */ 314 | std::atomic paused = false; 315 | 316 | /** 317 | * @brief The duration, in microseconds, that the worker function should sleep for when it cannot find any tasks in the queue. If set to 0, then instead of sleeping, the worker function will execute std::this_thread::yield() if there are no tasks in the queue. The default value is 1000. 318 | */ 319 | ui32 sleep_duration = 1000; 320 | 321 | private: 322 | // ======================== 323 | // Private member functions 324 | // ======================== 325 | 326 | /** 327 | * @brief Create the threads in the pool and assign a worker to each thread. 328 | */ 329 | void create_threads() 330 | { 331 | for (ui32 i = 0; i < thread_count; i++) 332 | { 333 | threads[i] = std::thread(&thread_pool::worker, this); 334 | } 335 | } 336 | 337 | /** 338 | * @brief Destroy the threads in the pool by joining them. 339 | */ 340 | void destroy_threads() 341 | { 342 | for (ui32 i = 0; i < thread_count; i++) 343 | { 344 | threads[i].join(); 345 | } 346 | } 347 | 348 | /** 349 | * @brief Try to pop a new task out of the queue. 350 | * 351 | * @param task A reference to the task. Will be populated with a function if the queue is not empty. 352 | * @return true if a task was found, false if the queue is empty. 353 | */ 354 | bool pop_task(std::function& task) 355 | { 356 | const std::scoped_lock lock(queue_mutex); 357 | if (tasks.empty()) 358 | return false; 359 | else 360 | { 361 | task = std::move(tasks.front()); 362 | tasks.pop(); 363 | return true; 364 | } 365 | } 366 | 367 | /** 368 | * @brief Sleep for sleep_duration microseconds. If that variable is set to zero, yield instead. 369 | * 370 | */ 371 | void sleep_or_yield() 372 | { 373 | if (sleep_duration) 374 | std::this_thread::sleep_for(std::chrono::microseconds(sleep_duration)); 375 | else 376 | std::this_thread::yield(); 377 | } 378 | 379 | /** 380 | * @brief A worker function to be assigned to each thread in the pool. Continuously pops tasks out of the queue and executes them, as long as the atomic variable running is set to true. 381 | */ 382 | void worker() 383 | { 384 | while (running) 385 | { 386 | std::function task; 387 | if (!paused && pop_task(task)) 388 | { 389 | task(); 390 | tasks_total--; 391 | } 392 | else 393 | { 394 | sleep_or_yield(); 395 | } 396 | } 397 | } 398 | 399 | // ============ 400 | // Private data 401 | // ============ 402 | 403 | /** 404 | * @brief A mutex to synchronize access to the task queue by different threads. 405 | */ 406 | mutable std::mutex queue_mutex = {}; 407 | 408 | /** 409 | * @brief An atomic variable indicating to the workers to keep running. When set to false, the workers permanently stop working. 410 | */ 411 | std::atomic running = true; 412 | 413 | /** 414 | * @brief A queue of tasks to be executed by the threads. 415 | */ 416 | std::queue> tasks = {}; 417 | 418 | /** 419 | * @brief The number of threads in the pool. 420 | */ 421 | ui32 thread_count; 422 | 423 | /** 424 | * @brief A smart pointer to manage the memory allocated for the threads. 425 | */ 426 | std::unique_ptr threads; 427 | 428 | /** 429 | * @brief An atomic variable to keep track of the total number of unfinished tasks - either still in the queue, or running in a thread. 430 | */ 431 | std::atomic tasks_total = 0; 432 | }; 433 | 434 | // End class thread_pool // 435 | // ============================================================================================= // 436 | 437 | // ============================================================================================= // 438 | // Begin class synced_stream // 439 | 440 | /** 441 | * @brief A helper class to synchronize printing to an output stream by different threads. 442 | */ 443 | class synced_stream 444 | { 445 | public: 446 | /** 447 | * @brief Construct a new synced stream. 448 | * 449 | * @param _out_stream The output stream to print to. The default value is std::cout. 450 | */ 451 | synced_stream(std::ostream& _out_stream = std::cout) 452 | : out_stream(_out_stream) {}; 453 | 454 | /** 455 | * @brief Print any number of items into the output stream. Ensures that no other threads print to this stream simultaneously, as long as they all exclusively use this synced_stream object to print. 456 | * 457 | * @tparam T The types of the items 458 | * @param items The items to print. 459 | */ 460 | template 461 | void print(const T &...items) 462 | { 463 | const std::scoped_lock lock(stream_mutex); 464 | (out_stream << ... << items); 465 | } 466 | 467 | /** 468 | * @brief Print any number of items into the output stream, followed by a newline character. Ensures that no other threads print to this stream simultaneously, as long as they all exclusively use this synced_stream object to print. 469 | * 470 | * @tparam T The types of the items 471 | * @param items The items to print. 472 | */ 473 | template 474 | void println(const T &...items) 475 | { 476 | print(items..., '\n'); 477 | } 478 | 479 | private: 480 | /** 481 | * @brief A mutex to synchronize printing. 482 | */ 483 | mutable std::mutex stream_mutex = {}; 484 | 485 | /** 486 | * @brief The output stream to print to. 487 | */ 488 | std::ostream& out_stream; 489 | }; 490 | 491 | // End class synced_stream // 492 | // ============================================================================================= // 493 | 494 | // ============================================================================================= // 495 | // Begin class timer // 496 | 497 | /** 498 | * @brief A helper class to measure execution time for benchmarking purposes. 499 | */ 500 | class timer 501 | { 502 | typedef std::int_fast64_t i64; 503 | 504 | public: 505 | /** 506 | * @brief Start (or restart) measuring time. 507 | */ 508 | void start() 509 | { 510 | start_time = std::chrono::steady_clock::now(); 511 | } 512 | 513 | /** 514 | * @brief Stop measuring time and store the elapsed time since start(). 515 | */ 516 | void stop() 517 | { 518 | elapsed_time = std::chrono::steady_clock::now() - start_time; 519 | } 520 | 521 | /** 522 | * @brief Get the number of milliseconds that have elapsed between start() and stop(). 523 | * 524 | * @return The number of milliseconds. 525 | */ 526 | i64 ms() const 527 | { 528 | return (std::chrono::duration_cast(elapsed_time)).count(); 529 | } 530 | 531 | private: 532 | /** 533 | * @brief The time point when measuring started. 534 | */ 535 | std::chrono::time_point start_time = std::chrono::steady_clock::now(); 536 | 537 | /** 538 | * @brief The duration that has elapsed between start() and stop(). 539 | */ 540 | std::chrono::duration elapsed_time = std::chrono::duration::zero(); 541 | }; 542 | 543 | // End class timer // 544 | // ============================================================================================= // --------------------------------------------------------------------------------