SMB1Command.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. /* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
  2. *
  3. * You can redistribute this program and/or modify it under the terms of
  4. * the GNU Lesser Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. */
  7. using System;
  8. using System.Collections.Generic;
  9. using System.IO;
  10. using Utilities;
  11. namespace SMBLibrary.SMB1
  12. {
  13. public abstract class SMB1Command
  14. {
  15. protected byte[] SMBParameters; // SMB_Parameters
  16. protected byte[] SMBData; // SMB_Data
  17. public SMB1Command()
  18. {
  19. SMBParameters = new byte[0];
  20. SMBData = new byte[0];
  21. }
  22. public SMB1Command(byte[] buffer, int offset, bool isUnicode)
  23. {
  24. byte wordCount = ByteReader.ReadByte(buffer, ref offset);
  25. if (CommandName == CommandName.SMB_COM_NT_CREATE_ANDX && wordCount == NTCreateAndXResponseExtended.DeclaredParametersLength / 2)
  26. {
  27. // [MS-SMB] Section 2.2.4.9.2 and Note <49>:
  28. // Windows-based SMB servers send 50 (0x32) words in the extended response although they set the WordCount field to 0x2A.
  29. wordCount = NTCreateAndXResponseExtended.ParametersLength / 2;
  30. }
  31. SMBParameters = ByteReader.ReadBytes(buffer, ref offset, wordCount * 2);
  32. ushort byteCount = LittleEndianReader.ReadUInt16(buffer, ref offset);
  33. SMBData = ByteReader.ReadBytes(buffer, ref offset, byteCount);
  34. }
  35. public abstract CommandName CommandName
  36. {
  37. get;
  38. }
  39. public virtual byte[] GetBytes(bool isUnicode)
  40. {
  41. if (SMBParameters.Length % 2 > 0)
  42. {
  43. throw new Exception("SMB_Parameters Length must be a multiple of 2");
  44. }
  45. int length = 1 + SMBParameters.Length + 2 + SMBData.Length;
  46. byte[] buffer = new byte[length];
  47. byte wordCount = (byte)(SMBParameters.Length / 2);
  48. if (this is NTCreateAndXResponseExtended)
  49. {
  50. // [MS-SMB] Section 2.2.4.9.2 and Note <49>:
  51. // Windows-based SMB servers send 50 (0x32) words in the extended response although they set the WordCount field to 0x2A.
  52. // WordCount SHOULD be set to 0x2A.
  53. wordCount = NTCreateAndXResponseExtended.DeclaredParametersLength / 2;
  54. }
  55. ushort byteCount = (ushort)SMBData.Length;
  56. int offset = 0;
  57. ByteWriter.WriteByte(buffer, ref offset, wordCount);
  58. ByteWriter.WriteBytes(buffer, ref offset, SMBParameters);
  59. LittleEndianWriter.WriteUInt16(buffer, ref offset, byteCount);
  60. ByteWriter.WriteBytes(buffer, ref offset, SMBData);
  61. return buffer;
  62. }
  63. public static SMB1Command ReadCommand(byte[] buffer, int offset, CommandName commandName, SMB1Header header)
  64. {
  65. if ((header.Flags & HeaderFlags.Reply) > 0)
  66. {
  67. return ReadCommandResponse(buffer, offset, commandName, header.UnicodeFlag);
  68. }
  69. else
  70. {
  71. return ReadCommandRequest(buffer, offset, commandName, header.UnicodeFlag);
  72. }
  73. }
  74. public static SMB1Command ReadCommandRequest(byte[] buffer, int offset, CommandName commandName, bool isUnicode)
  75. {
  76. switch (commandName)
  77. {
  78. case CommandName.SMB_COM_CREATE_DIRECTORY:
  79. return new CreateDirectoryRequest(buffer, offset, isUnicode);
  80. case CommandName.SMB_COM_DELETE_DIRECTORY:
  81. return new DeleteDirectoryRequest(buffer, offset, isUnicode);
  82. case CommandName.SMB_COM_CLOSE:
  83. return new CloseRequest(buffer, offset);
  84. case CommandName.SMB_COM_FLUSH:
  85. return new FlushRequest(buffer, offset);
  86. case CommandName.SMB_COM_DELETE:
  87. return new DeleteRequest(buffer, offset, isUnicode);
  88. case CommandName.SMB_COM_RENAME:
  89. return new RenameRequest(buffer, offset, isUnicode);
  90. case CommandName.SMB_COM_QUERY_INFORMATION:
  91. return new QueryInformationRequest(buffer, offset, isUnicode);
  92. case CommandName.SMB_COM_SET_INFORMATION:
  93. return new SetInformationRequest(buffer, offset, isUnicode);
  94. case CommandName.SMB_COM_READ:
  95. return new ReadRequest(buffer, offset);
  96. case CommandName.SMB_COM_WRITE:
  97. return new WriteRequest(buffer, offset);
  98. case CommandName.SMB_COM_CHECK_DIRECTORY:
  99. return new CheckDirectoryRequest(buffer, offset, isUnicode);
  100. case CommandName.SMB_COM_WRITE_RAW:
  101. return new WriteRawRequest(buffer, offset);
  102. case CommandName.SMB_COM_SET_INFORMATION2:
  103. return new SetInformation2Request(buffer, offset);
  104. case CommandName.SMB_COM_LOCKING_ANDX:
  105. return new LockingAndXRequest(buffer, offset);
  106. case CommandName.SMB_COM_TRANSACTION:
  107. return new TransactionRequest(buffer, offset, isUnicode);
  108. case CommandName.SMB_COM_TRANSACTION_SECONDARY:
  109. return new TransactionSecondaryRequest(buffer, offset);
  110. case CommandName.SMB_COM_ECHO:
  111. return new EchoRequest(buffer, offset);
  112. case CommandName.SMB_COM_OPEN_ANDX:
  113. return new OpenAndXRequest(buffer, offset, isUnicode);
  114. case CommandName.SMB_COM_READ_ANDX:
  115. return new ReadAndXRequest(buffer, offset);
  116. case CommandName.SMB_COM_WRITE_ANDX:
  117. return new WriteAndXRequest(buffer, offset, isUnicode);
  118. case CommandName.SMB_COM_TRANSACTION2:
  119. return new Transaction2Request(buffer, offset, isUnicode);
  120. case CommandName.SMB_COM_TRANSACTION2_SECONDARY:
  121. return new Transaction2SecondaryRequest(buffer, offset);
  122. case CommandName.SMB_COM_FIND_CLOSE2:
  123. return new FindClose2Request(buffer, offset);
  124. case CommandName.SMB_COM_TREE_DISCONNECT:
  125. return new TreeDisconnectRequest(buffer, offset);
  126. case CommandName.SMB_COM_NEGOTIATE:
  127. return new NegotiateRequest(buffer, offset);
  128. case CommandName.SMB_COM_SESSION_SETUP_ANDX:
  129. {
  130. byte wordCount = ByteReader.ReadByte(buffer, offset);
  131. if (wordCount * 2 == SessionSetupAndXRequest.ParametersLength)
  132. {
  133. return new SessionSetupAndXRequest(buffer, offset, isUnicode);
  134. }
  135. else if (wordCount * 2 == SessionSetupAndXRequestExtended.ParametersLength)
  136. {
  137. return new SessionSetupAndXRequestExtended(buffer, offset, isUnicode);
  138. }
  139. else
  140. {
  141. throw new InvalidDataException();
  142. }
  143. }
  144. case CommandName.SMB_COM_LOGOFF_ANDX:
  145. return new LogoffAndXRequest(buffer, offset);
  146. case CommandName.SMB_COM_TREE_CONNECT_ANDX:
  147. return new TreeConnectAndXRequest(buffer, offset, isUnicode);
  148. case CommandName.SMB_COM_NT_TRANSACT:
  149. return new NTTransactRequest(buffer, offset);
  150. case CommandName.SMB_COM_NT_TRANSACT_SECONDARY:
  151. return new NTTransactSecondaryRequest(buffer, offset);
  152. case CommandName.SMB_COM_NT_CREATE_ANDX:
  153. return new NTCreateAndXRequest(buffer, offset, isUnicode);
  154. case CommandName.SMB_COM_NT_CANCEL:
  155. return new NTCancelRequest(buffer, offset);
  156. default:
  157. throw new InvalidDataException("Invalid SMB command 0x" + ((byte)commandName).ToString("X2"));
  158. }
  159. }
  160. public static SMB1Command ReadCommandResponse(byte[] buffer, int offset, CommandName commandName, bool isUnicode)
  161. {
  162. byte wordCount = ByteReader.ReadByte(buffer, offset);
  163. switch (commandName)
  164. {
  165. case CommandName.SMB_COM_CREATE_DIRECTORY:
  166. return new CreateDirectoryResponse(buffer, offset);
  167. case CommandName.SMB_COM_DELETE_DIRECTORY:
  168. return new DeleteDirectoryResponse(buffer, offset);
  169. case CommandName.SMB_COM_CLOSE:
  170. return new CloseResponse(buffer, offset);
  171. case CommandName.SMB_COM_FLUSH:
  172. return new FlushResponse(buffer, offset);
  173. case CommandName.SMB_COM_DELETE:
  174. return new DeleteResponse(buffer, offset);
  175. case CommandName.SMB_COM_RENAME:
  176. return new RenameResponse(buffer, offset);
  177. case CommandName.SMB_COM_QUERY_INFORMATION:
  178. {
  179. if (wordCount * 2 == QueryInformationResponse.ParameterLength)
  180. {
  181. return new QueryInformationResponse(buffer, offset);
  182. }
  183. else if (wordCount == 0)
  184. {
  185. return new ErrorResponse(commandName);
  186. }
  187. else
  188. {
  189. throw new InvalidDataException();
  190. }
  191. }
  192. case CommandName.SMB_COM_SET_INFORMATION:
  193. return new SetInformationResponse(buffer, offset);
  194. case CommandName.SMB_COM_READ:
  195. {
  196. if (wordCount * 2 == ReadResponse.ParametersLength)
  197. {
  198. return new ReadResponse(buffer, offset);
  199. }
  200. else if (wordCount == 0)
  201. {
  202. return new ErrorResponse(commandName);
  203. }
  204. else
  205. {
  206. throw new InvalidDataException();
  207. }
  208. }
  209. case CommandName.SMB_COM_WRITE:
  210. {
  211. if (wordCount * 2 == WriteResponse.ParametersLength)
  212. {
  213. return new WriteResponse(buffer, offset);
  214. }
  215. else if (wordCount == 0)
  216. {
  217. return new ErrorResponse(commandName);
  218. }
  219. else
  220. {
  221. throw new InvalidDataException();
  222. }
  223. }
  224. case CommandName.SMB_COM_CHECK_DIRECTORY:
  225. return new CheckDirectoryResponse(buffer, offset);
  226. case CommandName.SMB_COM_WRITE_RAW:
  227. {
  228. if (wordCount * 2 == WriteRawInterimResponse.ParametersLength)
  229. {
  230. return new WriteRawInterimResponse(buffer, offset);
  231. }
  232. else if (wordCount == 0)
  233. {
  234. return new ErrorResponse(commandName);
  235. }
  236. else
  237. {
  238. throw new InvalidDataException();
  239. }
  240. }
  241. case CommandName.SMB_COM_WRITE_COMPLETE:
  242. {
  243. if (wordCount * 2 == WriteRawFinalResponse.ParametersLength)
  244. {
  245. return new WriteRawFinalResponse(buffer, offset);
  246. }
  247. else if (wordCount == 0)
  248. {
  249. return new ErrorResponse(commandName);
  250. }
  251. else
  252. {
  253. throw new InvalidDataException();
  254. }
  255. }
  256. case CommandName.SMB_COM_SET_INFORMATION2:
  257. return new SetInformation2Response(buffer, offset);
  258. case CommandName.SMB_COM_LOCKING_ANDX:
  259. {
  260. if (wordCount * 2 == LockingAndXResponse.ParametersLength)
  261. {
  262. return new LockingAndXResponse(buffer, offset);
  263. }
  264. else if (wordCount == 0)
  265. {
  266. return new ErrorResponse(commandName);
  267. }
  268. else
  269. {
  270. throw new InvalidDataException();
  271. }
  272. }
  273. case CommandName.SMB_COM_TRANSACTION:
  274. {
  275. if (wordCount * 2 == TransactionInterimResponse.ParametersLength)
  276. {
  277. return new TransactionInterimResponse(buffer, offset);
  278. }
  279. else
  280. {
  281. return new TransactionResponse(buffer, offset);
  282. }
  283. }
  284. case CommandName.SMB_COM_ECHO:
  285. {
  286. if (wordCount * 2 == EchoResponse.ParametersLength)
  287. {
  288. return new EchoResponse(buffer, offset);
  289. }
  290. else if (wordCount == 0)
  291. {
  292. return new ErrorResponse(commandName);
  293. }
  294. else
  295. {
  296. throw new InvalidDataException();
  297. }
  298. }
  299. case CommandName.SMB_COM_OPEN_ANDX:
  300. {
  301. if (wordCount * 2 == OpenAndXResponse.ParametersLength)
  302. {
  303. return new OpenAndXResponse(buffer, offset);
  304. }
  305. else if (wordCount * 2 == OpenAndXResponseExtended.ParametersLength)
  306. {
  307. return new OpenAndXResponseExtended(buffer, offset);
  308. }
  309. else if (wordCount == 0)
  310. {
  311. return new ErrorResponse(commandName);
  312. }
  313. else
  314. {
  315. throw new InvalidDataException();
  316. }
  317. }
  318. case CommandName.SMB_COM_READ_ANDX:
  319. {
  320. if (wordCount * 2 == ReadAndXResponse.ParametersLength)
  321. {
  322. return new ReadAndXResponse(buffer, offset, isUnicode);
  323. }
  324. else if (wordCount == 0)
  325. {
  326. return new ErrorResponse(commandName);
  327. }
  328. else
  329. {
  330. throw new InvalidDataException();
  331. }
  332. }
  333. case CommandName.SMB_COM_WRITE_ANDX:
  334. {
  335. if (wordCount * 2 == WriteAndXResponse.ParametersLength)
  336. {
  337. return new WriteAndXResponse(buffer, offset);
  338. }
  339. else if (wordCount == 0)
  340. {
  341. return new ErrorResponse(commandName);
  342. }
  343. else
  344. {
  345. throw new InvalidDataException();
  346. }
  347. }
  348. case CommandName.SMB_COM_TRANSACTION2:
  349. {
  350. if (wordCount * 2 == Transaction2InterimResponse.ParametersLength)
  351. {
  352. return new Transaction2InterimResponse(buffer, offset);
  353. }
  354. else
  355. {
  356. return new Transaction2Response(buffer, offset);
  357. }
  358. }
  359. case CommandName.SMB_COM_FIND_CLOSE2:
  360. return new FindClose2Response(buffer, offset);
  361. case CommandName.SMB_COM_TREE_DISCONNECT:
  362. return new TreeDisconnectResponse(buffer, offset);
  363. case CommandName.SMB_COM_NEGOTIATE:
  364. {
  365. // Both NegotiateResponse and NegotiateResponseExtended have WordCount set to 17
  366. if (wordCount * 2 == NegotiateResponse.ParametersLength)
  367. {
  368. Capabilities capabilities = (Capabilities)LittleEndianConverter.ToUInt32(buffer, offset + 20);
  369. if ((capabilities & Capabilities.ExtendedSecurity) > 0)
  370. {
  371. return new NegotiateResponseExtended(buffer, offset);
  372. }
  373. else
  374. {
  375. return new NegotiateResponse(buffer, offset, isUnicode);
  376. }
  377. }
  378. if (wordCount == 0)
  379. {
  380. return new ErrorResponse(commandName);
  381. }
  382. else
  383. {
  384. throw new InvalidDataException();
  385. }
  386. }
  387. case CommandName.SMB_COM_SESSION_SETUP_ANDX:
  388. {
  389. if (wordCount * 2 == SessionSetupAndXResponse.ParametersLength)
  390. {
  391. return new SessionSetupAndXResponse(buffer, offset, isUnicode);
  392. }
  393. else if (wordCount * 2 == SessionSetupAndXResponseExtended.ParametersLength)
  394. {
  395. return new SessionSetupAndXResponseExtended(buffer, offset, isUnicode);
  396. }
  397. else if (wordCount == 0)
  398. {
  399. return new ErrorResponse(commandName);
  400. }
  401. else
  402. {
  403. throw new InvalidDataException();
  404. }
  405. }
  406. case CommandName.SMB_COM_LOGOFF_ANDX:
  407. {
  408. if (wordCount * 2 == LogoffAndXResponse.ParametersLength)
  409. {
  410. return new LogoffAndXResponse(buffer, offset);
  411. }
  412. else if (wordCount == 0)
  413. {
  414. return new ErrorResponse(commandName);
  415. }
  416. else
  417. {
  418. throw new InvalidDataException();
  419. }
  420. }
  421. case CommandName.SMB_COM_TREE_CONNECT_ANDX:
  422. {
  423. if (wordCount * 2 == TreeConnectAndXResponse.ParametersLength)
  424. {
  425. return new TreeConnectAndXResponse(buffer, offset, isUnicode);
  426. }
  427. else if (wordCount == 0)
  428. {
  429. return new ErrorResponse(commandName);
  430. }
  431. else
  432. {
  433. throw new InvalidDataException();
  434. }
  435. }
  436. case CommandName.SMB_COM_NT_TRANSACT:
  437. {
  438. if (wordCount * 2 == NTTransactInterimResponse.ParametersLength)
  439. {
  440. return new NTTransactInterimResponse(buffer, offset);
  441. }
  442. else
  443. {
  444. return new NTTransactResponse(buffer, offset);
  445. }
  446. }
  447. case CommandName.SMB_COM_NT_CREATE_ANDX:
  448. {
  449. if (wordCount * 2 == NTCreateAndXResponse.ParametersLength)
  450. {
  451. return new NTCreateAndXResponse(buffer, offset);
  452. }
  453. else if (wordCount * 2 == NTCreateAndXResponseExtended.ParametersLength ||
  454. wordCount * 2 == NTCreateAndXResponseExtended.DeclaredParametersLength)
  455. {
  456. return new NTCreateAndXResponseExtended(buffer, offset);
  457. }
  458. else if (wordCount == 0)
  459. {
  460. return new ErrorResponse(commandName);
  461. }
  462. else
  463. {
  464. throw new InvalidDataException();
  465. }
  466. }
  467. default:
  468. throw new InvalidDataException("Invalid SMB command 0x" + ((byte)commandName).ToString("X2"));
  469. }
  470. }
  471. public static implicit operator List<SMB1Command>(SMB1Command command)
  472. {
  473. List<SMB1Command> result = new List<SMB1Command>();
  474. result.Add(command);
  475. return result;
  476. }
  477. }
  478. }