ret指令返回的地址,为什么不是dll导出函数导出表的地址

api hook后查看汇编代码,其中ret指令返回的地址,为什么不是dll导出函数导出表的地址呢?两者之间是什么关系?

1个回答

ret返回的是调用它的地方,当然不是导出表的地址。。。。。。

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
其他相关推荐
ret指令的返回地址,有哪些限制?
做一个buffer overflow的练习时遇到的问题,rnvoid main()rn rn int val; rn printf( "Type Hex string: "); rn val = getbuf(); rn printf( "getbuf returned 0x%x\n ", val); rn rnrn int getbuf() rn rn char buf[12]; rn getxs(buf); rn return 1; rn rn向buf[]中写入8byte数据,覆盖saved %ebp和返回地址,将ret的返回地址覆盖为已有的任意instruction 地址,成功rn但如果将ret的返回地址覆盖为当前程序(getbuf)的栈桢(stack frame)中的数据的地址(如buf[]的地址,假设从buf[0]处开始输入了一些机器指令),则程序在getbuf的返回指令ret处报错,segment faultrnrn注:在gdb下运行该程序,在getbuf处设断点,打印出%ebp,%esp,buf[]地址,再continue,继续执行,我是这样来得到buf[]地址的,运行到断点后,print /x ($ebp-24),因为从getbuf的汇编结果看,buf[]地址是这样得到的 leal 0xffffffe8(%ebp),%eaxrn push %eax rnrn这里没有搞明白,想修改返回地址让程序转去执行我们在buf[]中输入的机器指令,到底该怎么做呢?rn
如何修改dll导出函数的地址.
比如我在a.dll中导出了function1(),经过编译联接后它的导出地址为0xXXXXXXrn但我现在想把它的导出地址定为0xYYYYYY,该如何做呢?rn我查了下msdn好象没有哪个link选项可以完成该项任务,rn 望各位大虾赐教!
DLL导出函数地址相同问题
新建一个简单的DLL工程,编译后函数的地址相同,请大神解答rn下面是代码rnrn[code=c]rn#pragma oncernrn#ifndef __nvs_H__rn#define __nvs_H__rnrn#ifdef NVS_EXPORTSrn#define NVS_API_EXTERN __declspec(dllexport)rn#elsern#define NVS_API_EXTERN __declspec(dllimport)rn#endifrnrn#ifdef __cplusplusrnextern "C"rnrn#endifrnrn int NVS_API_EXTERN nvs_init();rn int NVS_API_EXTERN nvs_unit();rn int NVS_API_EXTERN nvs_start();rn int NVS_API_EXTERN nvs_stop();rn int NVS_API_EXTERN nvs_restart();rn int NVS_API_EXTERN nvs_getlasterror();rn int NVS_API_EXTERN nvs_gtest(int argv, const char** argc);rnrn#ifdef __cplusplusrn;rn#endifrnrn#endif // __nvs_H__[/code]rnrn[code=c]rn#include "stdafx.h"rn#include "nvs.h"rnrn int NVS_API_EXTERN nvs_init()rnrn int i = 0;rn return i;rnrn int NVS_API_EXTERN nvs_unit()rnrn int i = 0;rn return i;rnrn int NVS_API_EXTERN nvs_start()rnrn int i = 0;rn return i;rnrn int NVS_API_EXTERN nvs_stop()rnrn int i = 0;rn return i;rnrn int NVS_API_EXTERN nvs_restart()rnrn int i = 0;rn return i;rnrn int NVS_API_EXTERN nvs_getlasterror()rnrn int i = 0;rn return i;rnrn int NVS_API_EXTERN nvs_gtest(int argv, const char** argc)rnrn int i = 0;rn return i;rnrn[/code]rnrn就上面这点简单的代码,编译出dll用depends打开发现函数的地址都一样rnrn请问是什么原因?
为什么显示的不是地址?
代码如下:rn#include rn#include rnusing namespace std;rnrnint main(void)rnrn char *start;rn char *end;rn int len;rn char str[] = "This is a test of swap string";rn len = strlen(str);rn start = str;rn end = &str[len - 1];rnrn while (start < end)rn rn char temp;rn temp = *start;rn *start = *end;rn *end = temp;rnrn cout << "temp: " << temp << " start: " << start << " end: " << end << endl;rnrn start++;rn end--;rn rn return 0;rnrnrn输出结果如下:rntemp: T start: ghis is a test of swap strinT end: Trntemp: h start: nis is a test of swap strihT end: hTrntemp: i start: is is a test of swap strihT end: ihTrntemp: s start: r is a test of swap stsihT end: sihTrntemp: start: tis a test of swap s sihT end: sihTrntemp: i start: ss a test of swap i sihT end: i sihTrntemp: s start: a test of swapsi sihT end: si sihTrntemp: start: pa test of swa si sihT end: si sihTrntemp: a start: a test of swa si sihT end: a si sihTrntemp: start: wtest of s a si sihT end: a si sihTrntemp: t start: sest of t a si sihT end: t a si sihTrntemp: e start: st ofet a si sihT end: et a si sihTrntemp: s start: ft oset a si sihT end: set a si sihTrntemp: t start: o tset a si sihT end: tset a si sihTrnrn为什么显示的不是地址,而是字符串呢?rn
VC++ 如何让DLL的导出函数的地址不变
在写消息钩子的时候遇到一个崩溃的问题,原因是我setwindowhook之后,没unwindoshook之前,去用一个新的dll替换掉旧的dll,这样就崩溃了,大家很奇怪说,setwindowhook之后dll是绑定的啊!不能替换或者删除,其实不是,只要别的程序没有窗口,那么这个dll是不会被加载的,现在问题就是别的程序里面已经保存了hook的dll的某个函数地址,换了新dll,这个地址就无效了,导致崩溃,我以前听别人讲过,说可以固定dll里面的首个函数的地址,那样无论你怎么替换dll,只要地址不变就不会有问题了,请问大家怎么固定呢?
]根据PE文件格式从导出表中获取指定导出函数的地址
背景 了解 PE 文件格式,对于做一些数据分析都是比较重要的基础。在 PE 文件格式中,理解导入表以及导出表的工作原理,又是重中之重。理解了 PE 格式的导入表,就可以修改 PE 格式进行 DLL 注入,也可以修改导入表实现 API HOOK 等。理解了 PE 格式的导出表,可以不需要 WIN32 API 函数就可以根据 DLL 加载基址定位出导出函数的名称和导出函数的地址,这在 SHELLCO...
如何得到注入DLL中的导出函数地址
如何得到注入DLL中的导出函数地址
RET指令
;ret指令的作用是 栈顶字单元出栈,其值赋值给IP寄存器。 ;实现了一个程序的转移,SP需要减去一个子单元的位置 assume cs:codesg stack segment db 16 dup(0) stack ends codesg segment mov ax,4c00h int 21h start: mov ax,stack ;stack 会被编译成栈段的段地址 mov s
call指令和ret指令
参考文章: http://www.cppblog.com/luqingfei/archive/2010/08/04/122170.aspx
这输出的为什么不是地址 ? ? ? ??
[code=C/C++] int main()rn rn char a = 'a';rn char *p= &a;rn *(p+1) = '\0'; rn cout << p;//这输出的为什么不是地址rnrn system("pause"); rn return 0;rn [/code]rn[color=#FF0000]讲解时: 最好从浅到深。 [/color]rnrn在这 谢谢大家了
动态取得Ntoskrnl.exe导出函数地址
动态取得Ntoskrnl.exe导出函数地址
模块加载地址不是默认地址
下面这个模块是我的一个程序中的,我在改这个模块的IAT表时,发现IAT表为空。在VS2005的模块窗口中查看该模块,发现提示我"未在默认加载地址加载该模块"。rn//===VVV==== rnBCGCBPRO100d80.dll D:\VC_Project\HookLoadLibary\src\BCGCBPRO100d80.dll N/A N/A 已加载符号。 c:\Program Files\BCGSoft\BCGControlBarPro\Bin\BCGCBPRO100d80.pdb 16 10, 0, 0, 0 2009-6-13 13:01 00440000-00F86000* [3968] HookLoadLibary.exe: 本机 rn//===^^^===rn我如何获得该模块的IAT表?
堆栈上的返回地址看不懂,怎么看堆栈上返回地址指向的是导出表的什么函数?
堆栈上的返回地址看不懂,怎么看堆栈上返回地址指向的是导出表的什么函数?使用windbg工具怎么去解读堆栈数据?
call指令和ret指令(1001)
本文为《汇编语言程序设计》1001小节例程。点击链接…进课程主页。 用ret返回 assume cs:codesg, ss:stack stack segment db 16 dup (0) stack ends codesg segment mov ax,4c00h int 21h sta...
leave指令和ret指令的解释
介绍了leave和ret汇编指令在使用中的难点
hostname -i 返回的不是本机地址解决方案
hostname -i 返回的地址不是该虚拟机的ip,按以下步骤操作即可解决   1.编辑network vim /etc/sysconfig/network 编辑HOSTNAME=newname 保存   2.编辑housts vim /etc/hosts 注释掉::1 localhost......这行 添加 内网IP localhost.localdomain ne...
ret指令替换成jmp指令
大家好,我想请教个问题。rnrnrn一般函数末尾像下面:rnrn[code=c]rn002B18F0 pop edi rn002B18F1 pop esi rn002B18F2 pop ebx rn002B18F3 mov esp,ebp rn002B18F5 pop ebp rn002B18F6 ret rn[/code]rnrnrn我想在函数的返回指令前添加代码,如下:rnrn[code=c]rn002B18F0 pop edi rn002B18F1 pop esi rn002B18F2 pop ebx rn002B18F3 mov esp,ebp rn002B18F5 pop ebp rnrn我想在这里添加自己的代码 pop esi ,但添加了这样的pop指令后,ret就不能正确返回,rnrn002B18F6 ret rn[/code]rnrn因为汇编我不是很了解,在网上查了资料,好像说返回地址保存在esp中,那我可否这样rnrn[code=c]rn002B18F0 pop edi rn002B18F1 pop esi rn002B18F2 pop ebx rn002B18F3 mov esp,ebp rn002B18F5 pop ebp rnrnpop esp rnmov [returnaddress] esp //我想先把保存在棧里的返回地址取出来保存在一个变量里rnrn这里添加我自己的pop指令,rnpop edi //我自己想添加的pop指令rnpop esi //我自己想添加的pop指令rnrnjmp [returnaddress] //跳转到返回地址rnrn[/code]rnrn这个其实就是如何把ret替换成jmp指令,rn希望大家给些意见。谢谢!rnrn
若地址不是4KB对齐,则返回
IN DWORD dwAddress, //要目标地址,要求4KB对齐rn[color=#FF0000]if((dwAddress & 0x0fff ))//若地址不是4KB对齐,则返回[/color] rn rn return FALSE; rn rn[size=16px]怎么解释?[/size]rndwAddress & 0x0fff [color=#FF0000]这句话重点解释[/color]???
打印CALL [地址] 指令中的地址出错
typedef struct _TOP5CODErnrn UCHAR instruction; //指令rn ULONG address; //地址rnrnTOP5CODE,*PTOP5CODE;rnrnrn假设p=0x11111111;rn0x11111111 E8 22334455rn0x11111116 66778899rnrntop5code = (TOP5CODE*)p;rnrnKdPrint(("[crkTP_100] 指令: %X\n",top5code->instruction)); //指令: E8rnKdPrint(("[crkTP_100] 地址: %X\n",top5code->address)); //本应是地址: 55443322rn//实际却是:88776655rnrn请问这是为什么?rnrn---------------rn2楼我已经找到答案,是朋友告诉我,他说是积累的。这些编译问题很烦人的,不知道这个知识点在书上有没?谁能说下原因?
ret/retn人为改变执行地址
  1、CALL和RET/RETN是一对指令,CALL把返回地址压入堆栈,RET/RETN把返回地址从堆栈取出,然后将IP寄存器改为该返回地址。  2、不使用CALL,而是人为地把地址放入堆栈即可实现。如:  push edi  retn  从edi处开始执行 3、加壳利用这个特点,在启动源文件文件前,将代码解密还原至原始位置  ...
Windows PE导出表编程2(重组导出表函数地址)
Windows PE导出表编程2(重组带出表函数地址)
第十章 CALL和RET指令
本博文系列参考自&lt;&lt;汇编语言&gt;&gt;第三版,作者:王爽 call与ret都是转移指令,它们可以改变IP值,或者同时改变CS与IP的值,往往在程序中使用它们进行子程序模块的设计。 1 指令介绍 在介绍call和ret指令之前,我们先总结以下前两章的学习思路。这个思路同样可以扩展到第10章。 第八章 数据处理的两个问题:数据的位置和长度。 mov ax,bx | ...
call和ret指令用法
call和ret都是转移指令,它们都可以修改IP或者同时修改CS:IP,它们被共同用来实现子程序的设计 ret和retf区别 ret是用栈中的数据修改IP的值,实现近转移(段内转移) retf是用栈中的数据修改cs:IP的值,实现远转移(段间转移) 1cpu执行ret时,执行以下操作 (IP)=((ss)*16+(sp)) (sp)=(sp)+2 相当于pop ip 2c
RET指令怎么用
[code=Assembly]rn;****************************rn;bubble sort冒泡法排序rn;****************************rndata SEGMENTrn a DW 2,3,27,18,11,9 rndata ENDSrn rncode SEGMENTrnmain PROC farrn assume CS:code,DS:datarnstart:rn MOV AX,datarn MOV DS,AXrn rn MOV CX,4rn DEC CXrn rnloop1:rn MOV DX,1rn MOV DI,CXrn MOV BX,0rn rnloop2: rn MOV AX,a[BX]rn CMP a[BX+2],AXrn JGE continuern XCHG AX,a[BX+2]rn MOV a[BX],AXrn SUB DX,DXrn rncontinue:rn ADD BX,2rn LOOP loop2rn rn CMP DX,1rn JE exitrn MOV CX,DIrn rn LOOP loop1rnexit: rn RET rnmain ENDPrncode ENDSrn END startrn[/code]rn我用DEBUG功能跟踪调试,执行到exit程序结果没有错误。但执行完RET指令后内存的内容就不是期望的了。RET指令到底该怎么用呢?
ret 指令是什么意思?
如题?
汇编子程序ret不到正确的地址
assume cs:code,ds:data,ss:stackrnrndata segmentrnx dw 1234rny dw 100 dup(?) ;存放数字的ASCII码rndata endsrnrnstack segmentrndw 16 dup(?)rnstack endsrnrncode segmentrnstart: rn mov ax,stackrn mov ss,axrn mov ax,datarn mov ds,axrnrnmov dx,0 rn mov ax,x rn mov si,0rn call srnrnmov ax,4c00hrn int 21hrnrns proc nearrnres:mov cx,10rn rn push axrn mov ax,dxrn mov dx,0rn div cxrn mov bx,axrn pop axrn div cxrn mov cx,dxrn mov dx,bxrn rn push dxrn add cl,30hrn mov ds:[si+4],cl rn pop dxrn push axrn push dxrn add ax,dxrn jz goOutrn pop dxrn pop axrn inc sirn jmp resrn rn goOut: rn pop dxrn pop axrn mov dl,ds:[si+4]rn mov ah,2rn int 21hrn cmp si,0rn jz okrn dec sirn jmp goOut rn rn ok:rn ret //这个地方使用ret回不到原来的地方,因为程序并未返回dosrn ;mov ax,4c00h //把ret直接改成返回dos就可以正常运行,请问是怎么一回事rn ;int 21hrnrn s endprnrncode endsrnend start
动态调用dll,GetProcAddress返回不了地址的问题。
各位大虾,本人测试 自己编写的dll函数,静态调用时没有问题,但如下动态调用时,getprocessaddress 返回null,本人取得的错误吗是127,"can't find the procedure in the dll",rn不知道为什么?请各位大虾鼎力相助!rnvoid __fastcall TForm1::Button1Click(TObject *Sender)rnrnint ( *test01)(); //定义函数指针。rn HINSTANCE hDLL=LoadLibrary("D:\\newdlltest\\Project2.dll");rn if(!hDLL)rn rn ShowMessage("Can not load DLL");rn rn elsern rn (void *)test01=GetProcAddress(hDLL,"test");rn int echo=GetLastError();rn if(test01 == NULL)rn rn ShowMessage("the address can't be load");rn return ;rn rn int a=test01();rn rn FreeLibrary(hDLL);rn}rn创建dll源代码:rnextern "C" __declspec(dllexport) int test();rn#pragma argsusedrnint WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)rnrn return 1;rnrnint test()rn return 3;rn
为什么mov指令不能取局部变量的地址?
我在看一本叫《PC Assembly Language》的汇编教程,对其中一段不明白,希望有人给个解释,rn原文如下:rn"rnConsider the case of passing the address of a variable (let’s call it x) to a functionrn(let’s call it foo). If x is located at EBP − 8 on the stack, one cannot just use:rn mov eax, ebp - 8rn rnWhy? The value that MOV stores into EAX must be computed by the assemblerrn(that is, it must in the end be a constant).However, there is an instruction that does the desired calculation. rnIt is called LEA (for Load Effective Address). rnrnThe following would calculate the address of x and store it into EAX:rnlea eax, [ebp - 8]rn"rn作者说“MOV到EAX的数据必须由Assembler(汇编器)计算,MOV指令的第二个操作数必须是一个常量”rn这点我不是很清楚。rnrn看到有人说,将它改为:rn mov eax, ebprn sub eax, 8rn执行结果和lea指令相同,只是效率低,是这样吗?
急:调用dll返回地址错误
程序调用自己编写的扩展dll,debug版本没有错误,release版本,无任何错误退出。rn在主程序中调用函数dll中的SendSmsMsg(CString OrgAddr,CString sFeeMoney,unsigned long* SM_ID,int *Ret)rn在函数最后一行MessageBox有响应,而在主程序中调用之后MessageBox未执行就退出程序了.rn请教是什么原因。rnrn
汇编--call和ret指令
CALL和RET指令: RET指令 ret指令:用栈中的数据修改IP,从而实现近转移。使用ret时,相当于pop IP。 retf指令:用栈中的数据修改CS和IP,从而实现远转移。相当于 pop ip pop cs。 CALL指令 根据位移进行的转移:CALL 标号 。 这个指令会将当前IP内的值压入栈,然后再转移到标号处进行指令。相当于 push ip jmp near ptr...
汇编语言之CALL和RET指令
欢迎关注博主的公众号:薛定谔的小鱼儿1.ret和retf指令ret指令用栈中的数据,修改IP内容,从而实现近转移retf指令用栈中数据,修改CS和IP的内容,从而实现远转移CPU执行ret指令相当于:pop IPCPU执行retf指令相当于:pop IP                                      pop CS...
call和ret指令
call调用print函数.但没有正常退出 ** call和ret指令的本质
怎样获得一个DLL中未导出函数的地址
不知道有没有发错地方。。。rn现在我自己编写了一个简单的DLL,导出了一个函数f1,另一个函数f2未导出rn怎样实现获得这个DLL的f2函数的地址呢?rn我看了一些inline hook的知识,不知道行不行
关于获取注入后DLL中导出函数地址的问题?
我写了一个DLL,并在DLL里定义了一个导出函数extern "C" __declspec(dllexport) int setDeshWnd(char *hWnd),rn我是通过CreateRemoteThread将此DLL注入其它程序,经测试注入成功.我还想在注入之后通过CreateRemoteThread让重执行此DLL中的函数setDeshWnd接收程序传入的参数,主要功能是通知DLL进行数据传回时的hwnd,不知此法是否可行,或有什么更好的方法,望指正,注入源码如下:rnrn#includern#include rn#includern#includernint InjectDll(DWORD dwProcessId, LPTSTR lpDllName) rn rn PTHREAD_START_ROUTINE pfnRemote =(PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("Kernel32"), "LoadLibraryA");rn if(pfnRemote ==NULL) rn return -1; rnrn HANDLE hProcess =OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); rn if(hProcess ==NULL) rn rn return -1; rn rn int iMemSize = (int)strlen(lpDllName)+1; rn void *pRemoteMem =VirtualAllocEx(hProcess, NULL, iMemSize, MEM_COMMIT, PAGE_READWRITE); rn if(pRemoteMem ==NULL) rn rn CloseHandle(hProcess); rn return -1; rn rn if (!WriteProcessMemory(hProcess, pRemoteMem, lpDllName, iMemSize,NULL)) rn rn VirtualFreeEx(hProcess, pRemoteMem, 0, MEM_RELEASE); rn CloseHandle(hProcess); rn return -1; rn rn rn iMemSize = sizeof(int) ; rn pRemoteMem =VirtualAllocEx(hProcess, NULL, iMemSize, MEM_COMMIT, PAGE_READWRITE); rn if(pRemoteMem ==NULL) rn rn CloseHandle(hProcess); rn return -1; rn rn if (!WriteProcessMemory(hProcess, pRemoteMem,"100", iMemSize,NULL)) rn rn VirtualFreeEx(hProcess, pRemoteMem, 0, MEM_RELEASE); rn CloseHandle(hProcess); rn return -1; rn rnrn HANDLE hThread =CreateRemoteThread(hProcess, NULL, 0, pfnRemote, pRemoteMem, 0, NULL); rn if(hThread ==NULL) rn rn VirtualFreeEx(hProcess, pRemoteMem, 0, MEM_RELEASE); rn CloseHandle(hProcess); rn return -1; rn rn WaitForSingleObject(hThread, INFINITE); rnrn PTHREAD_START_ROUTINE pfnRemote2 =(PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(lpDllName), "setDeshWnd");rn if(pfnRemote2 ==NULL) rn [color=#FF0000]return -1;[/color]//执行到此就退出了rn MessageBox(NULL,"我的DLL函数地址获取成功!","",MB_OK);rnrnrn VirtualFreeEx(hProcess, pRemoteMem, 0, MEM_RELEASE); rn CloseHandle(hProcess); rn CloseHandle(hThread); rn return 0; rn rnrnrnvoid main()rn int PID;rn char x;rn char *DllName="D:\\Backup\\ÎÒµÄÎĵµ\\C++\\HookAPI\\HookAPI.dll";rn cout<<"Input the PID of Test.exe:";rn cin>>PID;rn if(InjectDll(PID,DllName)==-1)rn rn cout<<"DLL注入失败!";rn x=getchar();rn return;rn rn elsern rn cout<<"Hook成功!"<
DLL导出函数
各位大侠:rn DLL中的类成员函数可否作为导出函数?rn 如果可以,客户程序怎样引出?
dll 导出函数
今天在帮同事弄DLL导出的函数的时候,发现一个问题,因为他之前的函数有重载,我原计划希望他原来所有的函数都不动, 而重载的函数都新建一个函数代替使用到,这样既保持了向下兼容向上扩展,例子如下: void Func1(int ) void Func1(const char* ) Func1 和Func1 是一对重载函数,我打算新建一个函数 void Func1_New(const ch
为什么打印String对象,不是地址值?
首先看一下API中String类的toString()方法: toString public String toString() 返回此对象本身(它已经是一个字符串!)。 指定者:接口 CharSequence 中的 toString覆盖:类 Object 中的 toString 返回:字符串本身。 String类的equals()方法: Object类的
GetMemory(str);为什么不是传地址
void GetMemory(char* p) rnrn p = (char *)malloc(100);rnrnint main(void) rn rnrn char *str = NULL; rn GetMemory(str); rn strcpy(str, "hello"); rn printf(str); rn
ARM3级流水线和中断返回地址地址
    ARM处理器使用流水线来增加处理器指令流的速度,这样可使几个操作同时进行,并使处理与存储器系统之间的操作更加流畅,连续,能提供0.9MIPS/MHZ的指令执行速度    PC代表程序计数器,流水线使用三个阶段,因此指令分为三个阶段执行:1、取指(从存储器装载一条指令);2、译码(识别将要被执行的指令,得到下一指令占据数据路径,寄存器被读取,操作数在桶式移位器中被以为。ALU产生相应的运算结...
如何在EXE中导出函数(不是DLL)?
怎样在可执行文件EXE中导出函数让别的模块能够使用rn目前,单纯导数函数已经可以,但是函数在显式被调用的时候(隐式调用没有试过),导出函数内部使用的导入函数指针全部非法,如何才能正常的从EXE中导出函数?我使用DEF文件导出函数EXE中的函数。rn各位看有没有办法?
相关热词 c#检测非法字符 c#双屏截图 c#中怎么关闭线程 c# 显示服务器上的图片 api嵌入窗口 c# c# 控制网页 c# encrypt c#微信网页版登录 c# login 居中 c# 考试软件