开发环境:vs 2019 c工程里面 添加asm 文件,执行64位汇编代码。
代码做了 点什么:
1,通过gs寄存器获取模块基地址
2,通过基地址获取导出函数----api 地址
3,通过api做点其他的事情
4,关于ROP的实验
参考:
1,通过gs 寄存器获取模块基地址 Windows x64位通过PEB获得Kernel32基地址 - ciyze - 博客园
2,64位汇编 传参 特点 vc在x64体系的一般传参数方式_weixin_30725315的博客-CSDN博客
3,通过汇编解析pe文件得到LoadLibraryA地址后,加载dll导致0xc000005(这个问题偶尔出现,测试是在1909上没有发现,在22000上出现)。原因是stack 要8字节对齐,https://stackoverflow.com/questions/32160792/loadlibrarya-with-user32-dll-crash-in-ntdll-dll-x64-assemblyhttps://stackoverflow.com/questions/32160792/loadlibrarya-with-user32-dll-crash-in-ntdll-dll-x64-assembly
(2022/03/31)发现这个问题是自己定义的函数体中栈对其的标准不一致,有的是sub rsp,128h,
有的是sub rsp,120h;统一之后函数都能正常调用。在10240,18363,22000上都测试可运行。
asm 文件
EXTERN numb:DQ
EXTERN GetFib:PROC
.DATA
data db 48h,8bh,0c4h,0fah,48h,83h,0ech,10h,50h,09ch,6ah,10h,48h,8dh
kernel32Base dq 0h
ws2_32Base dq 0h
ws2_32_name db 'ws2_32.dll',0h,0h,0h,0h
aOutputDebugStringA db 'OutputDebugStringA',0h,0h
fOutputDebugStringA dq 0h
aLoadLibraryA db 'LoadLibraryA',0h
fLoadLibraryA dq 0h
;file operation functions
logFileName db 'c:\llo.log',0h,0h,0h
wlogFileName dw 63h,3ah,5ch,6ch,6ch,6fh,2eh,6ch,6fh,67h,0h,0h,0h
logFileIsDeleted db 'file_is_deleted!',0h,0h,0h
aCreateFile2 db 'CreateFile2',0h,0h,0h
aCreateFileA db 'CreateFileA',0h,0h,0h
aCreateFileW db 'CreateFileW',0h,0h
fCreateFileW dq 0h
fCreateFileA dq 0h
aWriteFile db 'WriteFile',0h,0h
fWriteFile dq 0h
aDeleteFile db 'DeleteFileA',0h,0h,0h ;DeleteFIleA(FilePath);
fDeleteFile dq 0h
aCloseHandle db 'CloseHandle',0h,0h
fCloseHandle dq 0h
aGetLastError db 'GetLastError',0h,0h
fGetLastError dq 0h
.CODE
;get string length
;rdi points to the string
;lea rdi,OutputDebugStringA
;call strlen
strlen PROC
push rdi
push rcx
xor rcx,rcx
mov ecx,0ffffffffh
xor eax,eax
repnz scasb
not ecx
dec ecx
mov eax,ecx
pop rcx
pop rdi
ret
strlen ENDP
;rdi and rsi the string to compare
;rcx ,length of rsi
strcmp PROC
push rbx
xor rbx,rbx
test rcx,rcx
jz return
loo:
mov bl,byte ptr [rsi+rcx]
cmp bl,byte ptr [rdi+rcx]
jnz return
dec rcx
jge loo
inc rcx ;set rcx to zero,and indicates the same of strings
return:
pop rbx
ret
strcmp ENDP
;return kernel32 base in rbx,if eax==1,get it ;else fails to.
GetKernel32Base PROC
push rbx
push rdx
push r9
xor rcx,rcx
mov rbx,gs:[60h]
test rbx,rbx
jz return
mov rbx,qword ptr [rbx+18h]
test rbx,rbx
jz return
mov rbx,qword ptr [rbx+30h]
test rbx,rbx
jz return
search_kernel32:
inc rcx
mov r9,rbx
add r9,38h
mov dx,word ptr [r9]
cmp dx,18h ;kernel32.dll ,wide char ,length is 0x18
jnz next_one
mov r9,qword ptr [r9+8h]
mov edx,dword ptr [r9+0ch]
cmp edx,320033h ;WIDE CHAR '32' in memory is [32 00 33 00]
jz tt
next_one:
mov rbx,qword ptr [rbx] ;;ListEntry->Flink
test rbx,rbx
jnz search_kernel32
xor rax,rax ;set rax to zero ,fails to get kernel32 base
jmp return
tt:
mov rbx,qword ptr [rbx+10h]
mov kernel32Base,rbx
mov rax,1
return:
pop r9
pop rdx
pop rbx
ret
GetKernel32Base ENDP
;rbx is the module base
;rsi points to the function name
GetProcAddressByName PROC
;int 3
mov rax,rbx ;rbx is the module base
mov bx,word ptr [rax]
xor rdx,rdx ;set rdx to zero
xor rcx,rcx
cmp bx,5a4dh ;MZ标示
jnz retu
mov ebx,dword ptr [rax+3ch];获取PE头偏移
test ebx,ebx
jz retu
mov ecx,dword ptr [rax+rbx]
cmp ecx,4550h;PE标示
jnz retu
;直接读的结构体里面的偏移,这个用winDbg看可能比较直观一点
find:
xor rdx,rdx
xor rcx,rcx
mov edx,dword ptr [rax+rbx+88h];因为模块已经加载到内存,这里可以直接用这个偏移加模块基址,和从本地文件读取有点差别
mov ecx,dword ptr [rax+rdx+18h];ImageExportDirectory.NumberOfNames
mov edx,dword ptr [rax+rdx+20h];ImageExportDirectory.AddressOfNames;存函数名称字符串的偏移,这里相当于一个数组
push rbx
push rcx
add rdx,rax;加上基址
;lea rbx,sLoadLibraryA
;mov rsi,rbx
;the rsi points to the function name string.
xor rbx,rbx
find_func_name:
push rax
mov rdi,rsi
call strlen ;mov ecx,0ch
mov ecx,eax
pop rax
xor rdi,rdi
mov edi,dword ptr [rdx+4*rbx];从字符串数组中取值比较
push rsi
add rdi,rax
;repe cmpsb ;字符串循环比较,ecx为0或不等,结束比较
call strcmp
test ecx,ecx
jz get_func
pop rsi
inc ebx
pop rcx
cmp ebx,ecx
push rcx
jl find_func_name
jmp search_done
get_func:
mov edi,ebx
pop rsi
pop rcx
pop rbx
xor rdx,rdx
xor rcx,rcx
mov edx,dword ptr [rax+rbx+88h];
mov edx,dword ptr [rax+rdx+1ch];ImageExportDirectory.AddressofFunctions
add rdx,rax
mov ecx,dword ptr [rdx+rdi*4];从函数地址数组中取值(是偏移)
add rcx,rax
mov rbx,rcx
jmp retu
search_done:
pop rcx
pop rbx
retu:
mov rax,rbx
ret
GetProcAddressByName ENDP
mm PROC
sub rsp,128h ;amazing! use 'sub rsp,28h' to expand the local stack,and the code do not crash!
call GetKernel32Base ;base address is in rbx,
test eax,eax
jz return
lea rsi,aLoadLibraryA
mov rbx,kernel32Base
call GetProcAddressByName
test rax,rax
jz return
mov fLoadLibraryA,rax
lea rsi,aOutputDebugStringA
mov rbx,kernel32Base
call GetProcAddressByName
test rax,rax
jz return
mov fOutputDebugStringA,rax
lea rsi,aCreateFileA
mov rbx,kernel32Base
call GetProcAddressByName
test rax,rax
jz return
mov fCreateFileA,rax
lea rsi,aWriteFile
mov rbx,kernel32Base
call GetProcAddressByName
test rax,rax
jz return
mov fWriteFile,rax
lea rsi,aDeleteFile
mov rbx,kernel32Base
call GetProcAddressByName
test rax,rax
jz return
mov fDeleteFile,rax
lea rsi,aCloseHandle
mov rbx,kernel32Base
call GetProcAddressByName
test rax,rax
jz return
mov fCloseHandle,rax
lea rsi,aGetLastError
mov rbx,kernel32Base
call GetProcAddressByName
test rax,rax
jz return
mov fGetLastError,rax
lea rsi,aCreateFileW
mov rbx,kernel32Base
call GetProcAddressByName
test rax,rax
jz return
mov fCreateFileW,rax
lea rcx,ws2_32_name
call fLoadLibraryA
mov ws2_32Base,rax
return:
add rsp,128h
ret
mm ENDP
file_op PROC
sub rsp,128h
lea rcx,wlogFileName ;1
mov rdx,0c0000000h ;2 GENERIC_READ|GENERIC_WRITE
mov r8,1 ;3 FILE_SHARE_READ
mov r9,0 ;4
;the rest parameters move into stack
mov qword ptr [rsp+20h],1 ;5
mov qword ptr [rsp+28h],80h ;6
mov qword ptr [rsp+30h],0 ;7
call fCreateFileW ;create a new file with FileName
test rax,rax
js return
;
;do read/write file
;
mov rsi,rax
mov rcx,rax
lea rdx,aCreateFileA
mov r8,7h
lea r9,aCreateFileA
mov qword ptr[rsp+20h],0
call fWriteFile ;write something into file
;call fGetLastError
;close handle
mov rcx,rsi
call fCloseHandle;close file handle
return:
; call fGetLastError
;delete file
lea rcx,logFileName
call fDeleteFile
lea rcx,logFileIsDeleted
call cOutputDebugStringA
add rsp,128h
ret
file_op ENDP
;jz
;returns a number
;CheckJz() will return 123
;after add dl,dl
;the zero flag will be set to true
CheckJz PROC
mov dl,080h
add dl,dl
jnz l_not_zero
mov rax,123
jmp l_return
l_not_zero:
mov rax,111
l_return:
ret
CheckJz ENDP
cOutputDebugStringA PROC
sub rsp,128h
mov rax,fOutputDebugStringA
cmp rax,0
jbe l_return
;lea rsi,aOutputDebugStringA
;mov rcx,rsi
call fOutputDebugStringA
l_return:
add rsp,128h
ret
cOutputDebugStringA ENDP
;play with rop gadgets
;
;
LittleRop_set_eax PROC
pop rax;pop out return address
pop rax
ret
LittleRop_set_eax ENDP
LittleRop_set_ebx PROC
pop rbx
pop rbx
ret
LittleRop_set_ebx ENDP
LittleRop_set_ecx PROC
pop rcx
pop rcx
ret
LittleRop_set_ecx ENDP
LittleRop_set_edx PROC
push rcx
ret
LittleRop_set_edx ENDP
; http://expdev-kiuhnm.rhcloud.com - 133 -
;By now it should be clear why this technique is called ROP: the instruction RET is used to jump from one
;piece of code to the next. The pieces of code are usually called gadgets. A gadget is just a sequence of
;instructions which ends with a RET instruction.
;The hard part is finding and chaining together the right gadgets to achieve our goals
PlayRop PROC
sub rsp,128h
mov rdx,rcx;保存rcx,跳转地址
;push 00000000h
;push 11111111h
lea rax,offset loc_ret
push rax
push 33333333h
lea rax,offset loc_call_set_ecx
push rax
push 44444444h
lea rax,offset loc_call_set_ebx
push rax
push 55555555h
;call LittleRop_set_eax
call LittleRop_set_eax ;eax=0x55555555
loc_call_set_ebx:
;call LittleRop_set_ebx
call LittleRop_set_ebx;ebx=0x44444444
loc_call_set_ecx:
;call LittleRop_set_ecx
call LittleRop_set_ecx;ecx=0x33333333
loc_ret:
;call int 3 to interrupt the debugger to see register data
;int 3
mov rcx,rdx;跳转地址作为参数
call LittleRop_set_edx
add rsp,128h
ret
PlayRop ENDP
;https://stackoverflow.com/questions/32160792/loadlibrarya-with-user32-dll-crash-in-ntdll-dll-x64-assembly
;
;有的函数中用sub rsp,120h,有的函数中用sub rsp,128h
;导致冲突,应该与最开始的一个sub rsp,保持一直;,sub rsp,128h
;
;
setnum PROC
;INT 3
sub rsp,128h ; NOTE: The stack needs to be 16 byte aligned and starts out
; 8 byte aligned
lea rcx,offset loc_work
call PlayRop ;执行PlayRop会将执行流程衔接到loc_work,跳过下面的'jmp loc_setnum_ret’指令
jmp loc_setnum_ret
loc_work:
pop rcx ;平衡stack,上面有个push rcx没有对应的pop
;add rsp,128h
mov numb,12345678h
call mm
;load ws2_32.dll
lea rcx,ws2_32_name
call fLoadLibraryA
mov ws2_32Base,rax
lea rsi,ws2_32_name
mov rcx,rsi
call cOutputDebugStringA
lea rsi,logFileName
mov rcx,rsi
call cOutputDebugStringA
call file_op
mov rcx,9
call GetFib
mov numb,rax
loc_setnum_ret:
add rsp,128h
ret
setnum ENDP
END