Noniin, minulla on VB6-sovellus(piirustusohjelma), jota käytän suoraan usb-tikulta Windows pohjaisissa koneissa.
(Halusin aikoinaan sovelluksesta sellaisen, että se ei tarvitse erillistä asennusta vaan toimi itsenäisesti yhtenä .exe failina. Tämä mahdollista asiakkaan koneen käytön tarvittaessa. Ei ocx dll eikä mitään erikoista koodissa, vain ihan perusobjekteja, ei myöskään tietokantaa vaan failien käyttöä randomina.)
Ohjelma toimii ja olen tehnyt sillä sähkösuunnittelua koneisiin jo jonkin aikaa. Muutamalla asiakkaalla ja kolleegalla on ollut kiinnosstusta ohjelmasta, joten olen ajatellut kehittää siihen "käyttöoikeus"-ominaisuutta. Voisin sitten antaa/myydä käyttöoikeutta vaikka vuodeksi kerrallaan tms.??
Kehittelin kokeeksi systeemin, jossa käyttäjä antaa ohjelmalle kaksi tekstiriviä: "Käyttöaika & Nimi & Osoite & MuutYhteystiedot ..." sekä koodi "siansaksaaaaaaaa..." ja näiden tekstien pitää tietyllä algoritmillä pitää kutinsa keskenään. Tähän käytin n. 1MB kokoista siansksataulukkoa josta eka teksti hakee toisen tekstin kirjaimet.
Asiakkaan tiedot(teksti 1.) näkyyvät kaikissa tulostuksissa, joten tämä mahdollisesti ehkäisee luvatonta käyttöä - vai ehkäiseekö?
No, itsestänikin tämä vaikuttaa vähintäänkin ontuvalta ja koen osaamiseni tässä epävarmaksi (ja oletettavasti joku ammattilainen osaa murtaa tämän systeemin muutamassa sekunnissa)
Onko teillä suositusta miten lähtisin asiaa kehittämään? Onko mielestänne ylipäätään mahdollista suojata USB-tikulta suoritettavaa täysin itsenäistä ohjelmaa?
Mitään ohjelmaa, jota suoritetaan käyttäjän koneella, ei ole mahdollista suojata niin, että sitä ei voisi murtaa.
Itse käyttäisin asymmetristä salausta. Eli tekisin niin, että laittaisin lisenssitiedot selväkielisinä ja sitten omalla salaisella avaimellani allekirjoittaisin sen. Ohjelman mukana tulisi julkinen avain, jolla sitten allekirjoitus varmennettaisiin.
Tämänkin toki pystyy kiertämään muokkaamalla ohjelmaa. Mutta avaingeneraattoria ei pysty tekemään, joten muokkailu täytyy tehdä uudestaan jokaiselle uudelle versiolle.
Onkos tietoa voiko USB-muisteja yksilöidä, joten ohjelmaa olisi mahdollista käyttää vain tietyltä tikulta? Ohjelman levitys tapahtuisi siis tikun avulla.
(Yritin etsiä jotakin MAC-osoitteen tapaista yksilöivää tietoa ja joitakin arvoja löytyykin ominaisuus ikkunan kautta, mutta en tiedä mitä ne itseasiassa ovat.)
jtha kirjoitti:
Onkos tietoa voiko USB-muisteja yksilöidä, joten ohjelmaa olisi mahdollista käyttää vain tietyltä tikulta? Ohjelman levitys tapahtuisi siis tikun avulla.
Googlella löytyi ohjelma nimeltään AntiDuplicate, joka väittää osaavansa yksilöidä ohjelman tiettyyn USB-muistiin. Ohjeman sivuilla ei kylläkään sanota, millä tavoin se tarkistaa muistitikun alkuperäisyyden, mutta vastaavanlaisen virityksen saanee itsekin tehtyä.
Kiitos -tossu-! Ehdottomasti pitää tutkia tuo mahdollisuus.
itse tein yhteen sovellutukseen salauksen rijndaelilla, ja valmiita avainpareja parituhatta.
kun asiakas asensi sovellutuksen, ohjelmaan syötettiin avainparin asiakaspuolen koodi.
Tämän jälkeen softa otti yhteyden palvelimelle ja varmensi avaimen parin (ja poisti käyttölistalta).
Jos verkkoyhteyttä ei ollut, asiakas soitti firmaan ja pyysi salauksen avainparin (softa arpoi randomilla yhden avainparin listalta).
tämän jälkeen kyseinen pari poistettiin lisenssilistalta.
Me taas tehtiin jokunen vuosi sitten eräänlainen "suojaus" "multimediarompulle", jonka tarkoitus oli estää kyseisen yritysten myyjiä käyttämästä vanhentunutta versiota rompusta myyntitöissä.
Ajatus olikin kiva aluksi, kunnes huomasimme jok'ikinen vuosi olevamme päivittämässä romppua pahimman joulukiireen alla.
No olisiko ekalla kerralla kannattanut vaihtaa vanhenemispäivä vaikka heinäkuulle?
Sinänsä Leben tapauksessa ei tarvitse kovin monimutkaista "suojausta", kun voidaan olettaa että sitä ei lähde kukaan hakkeroimaan.
Tuosta groovyb:n viestistä tuli minulle enemmän kysymyksiä kuin vastauksia. Oliko siis koko systeemi kryptattuna ja se purettiin muistiin? Jos kerran ekan käyttökerran jälkeen avaimen toinen puolikas poistettiin käyttölistalta, niin oliko se sitten kertakäyttöinen sovellus. Vai menikö se niin, että ekan tarkistuksen yhteydessä avaimen toinen puolikas tallennettiin jemmaan käyttäjän koneelle vai peräti koko softa salaus purettuna?
Moi jtha!
tässä pikku esimerkki USB-tikun tsekkaamiseen...
Private Declare Function GetDriveType Lib "kernel32" _ Alias "GetDriveTypeA" (ByVal nDrive As String) As Long Private Sub Form_Load() Dim root As String root = Left(App.Path, 3) Dim msg As String If GetDriveType(root) <> 2 Then msg = "EI KÄYTTÖOIKEUTTA!" 'Esim. End If Dim driveletter As String driveletter = UCase(Left(root, 1)) Select Case driveletter Case "A", "B" msg = "EI KÄYTTÖOIKEUTTA!" End Select Dim fullpath As String fullpath = root & "mydrive.dat" If Dir(fullpath) <> "" Then Kill fullpath End If Shell "cmd /C dir root >" & fullpath, vbHide JumpBack: On Error Resume Next flen& = FileLen(fullpath) If Err <> 0 Then Err.Clear On Error GoTo 0 GoTo JumpBack End If Do While FileLen(fullpath) = 0 DoEvents: Loop Dim rootdata As String Open fullpath For Input As #1 rootdata = Input$(LOF(1), 1) Close #1 Shell "cmd /C del " & fullpath, vbHide 'Huom! ko. tikun serial If InStr(rootdata, "3B67-0B4F") = 0 Then msg = "EI KÄYTTÖOIKEUTTA!" End If If msg <> "" Then Dim msgresult As Long msg = msg & vbCrLf & _ "Tahdotko siirtyä ohjelman toimittajan Internet sivustolle?" msgresult = MsgBox(msg, vbYesNo, Me.Caption) If msgresult = 6 Then Shell "explorer http://www.sivusto.com", vbNormalFocus End If End End If '... End Sub
Ton käyttöoikeus-jutskan vois järjestää vaikkapa niin, että virittelis jokun ilmasen tahi huokean web-hotellin servulle PHP/MySql viritelmän jonne lähettelis WinInet API:n avulla sivun headereihin ao. tikun seriaalin ja responsena tulis sit tieto siitä onko lisenssi voimassa vaiko eikö...
täältä löytyy sample, jonka avulla tutustua WinInet API'n saloihin jos ei ole ennestään tuttua.
Grez,ohjelman järjestelmätiedot oli kryptattu, ja kryptaus purettiin aina käynnistyksen yhteydessä. Jos ohjelma asennettiin uudelleen,piti myös avaimet uusia. Listalta poisto sen takia ettei verkkovarmenteessa voinut käyttää samaa avainta kahdesti.
Minä tein tämän seuraavasti(toistaiseksi):
Etsin netistä vb6-koodin, jolla saadaan listattua usb-muistit, jotka ovat koneessa kiinni. Samalla saan muisteista luettua mm. valmistajan nimen ja muistin sarjanumeron. Muodostin tikun tiedoista ja lisenssinhaltijasta ym. tiedoista merkkijonon, jolle hain vastineen "siansakasaa" tietyllä algoritmilla. Nyt tallennan sovelluksen ja lisenssi ja siansksat ko. tikulle. Sovellus puolestaan lukee nämä tiedot faileista sekä usb-tikun sarjanumeron yms. ja hakee samalla algoritmillä "siansksa"-koodin, jonka on oltava sama kuin faileihin on upotettu. Nyt tämä softa siis toimii vain ja ainoastaan tällä yhdellä usb-tikulla. Brutaalia ehkä, mutta tuntuu toimivan.
Algoritmi, joka muodostaa siansaksan on mielestäni aika hankalasti arvattavissa, mutta olisi tietysti kiva testauttaa jollakin hakkerilla :-)
jtha kirjoitti:
Algoritmi, joka muodostaa siansaksan on mielestäni aika hankalasti arvattavissa, mutta olisi tietysti kiva testauttaa jollakin hakkerilla :-)
Murtuu 15 minuutissa. Mutta tuskin sillä on merkitystä koska ei varmaan ohjelman kohderyhmää kiinnosta alkaa hakkeroimaan. Tai jos joku niin tekee niin tuskin kovin moni.
neau33:n vinkkiin muuttaisin (todennäköisesti vitseiksi tarkoitetut) "fak juut" oikeasti joksikin vähääkään kuvaavimmiksi.
Muutenkin mm. Ohjelmointiputkan foorumilla näkee keskusteluissa paljon "virheilmoituksia", joista monet ovat oppineet käyttämään niitä väärin. Olen jokusen kerran myös eksynyt ihan vanhentuneesta linkistä sivulle, jossa minua tervehditään lämpimästi "Hacking attempt" -tervehdyksellä.
Eli jottei koskaan enää ikinä vahingossakaan mikään ohjelma ilmoittaisi noin vulgaaristi mistään, niin olisi hyvä, että kokemattomille koodareille annettaisiin vinkkejä, joissa ei heti haistatettaisi värkkejä ja haukuttaisi hakkereiksi, koske monille vinkit opettavat oikeasti koodaamaan virheilmoituksiin noita termejä.
Muutenkin omalla alalla olen saanut lukea paljon juttuja, joissa kolmen aikaan yöllä väliaikaisiksi tarkoitetut "vitsit" ovatkin jääneet kummittelemaan ihan asiakkaan versioihin.
Toteuttaisin Grezin neuvoa, ja en viitsisi nähdä vaivaa kovinkaan monimutkaisen suojauksen tekemiseen, vaan tyytyisin siihen, ettei softaa esim. vahingossa pääsisi levittämään eteenpäin.
Hyviä mielipiteitä.
Kuitenkin, jos joku viitsii vaikka murtaa tuon, niin ilmoittakaa kauanko meni, tarjoan kaljan vaikka sen peliprojektin julkistamistilaisuudessa.
Mitä tarkoitat murtamisella? Eikös tuo ole jo itsessään työkalu mikä tekee sen mitä voisin kuvitella murtamistyökalun tehtävän olevan.
Meinaan että ilman tuota pitäisi saada aikaiseksi selväkielisestä tekstistä sama koodi. Jos onnistuu helposti saamaan vastaavan koodin omin avuin niin sen voi liittää tiedostoihin keksittyjen käyttöoikeuksien kanssa ja alkuperäisen käyttöoikeuden voi helposti kiertää... äh, olenkohan suistumassa ojasta allikkoon tämän kanssa...
Täytyy sanoa että ei ole tullut harrastettua hakkerointia, mutta tiedän suurinpiirtein mitä tapoja kiertää kuvaamasi tyylisiä suojauksia on olemassa.
Ehkä nopein tapa on etsiä koodista kohta joka antaa esimerkiksi virheilmoituksen väärästä lisenssistä ja kelata siitä taaksepäin että missä se tarkistettiin ja sitten vaikka muuttaa koodia siten että tarkistuksen tulos on aina positiivinen.
Jos tarvitsee vaan yksi avain saada, niin voisi debuggerissa ajaa ohjelmaa ja kun ohjelma on laskenut oikean avaimen, jota alkaa vertaamaan käyttäjän syöttämään, niin kopioi sen lasketun oikean avaimen talteen.
Jos haluaa tehdä ihan koodigeneraattorin (mitä siis hait tässä), niin sen tekeminen on tietty hieman työläämpää kuin edellä mainitut suojauksen ohittamiset. Yksi tapa on tuktia mitä ohjelma tekee. Voisin sen tässä tehdä jos olisi ylimääräistä aikaa. (Tästä hassu offtopicci lopussa) Ja sitten kirjoittaa koodin joka tekee saman asian. Tai vielä yksinkertaisemmin, kopioi sen koodin laskevan koodinpätkän ohjelmasta ja upottaa sen omaan avaingeneraattoriin lähes sellaisenaan.
Offtopiccia:
Pitäis varmaan joskus leikkiä enemmän X86 assemblerin kanssa. Tullut puuhailtua lähinnä 68k prossujen kanssa aikanaan ja sitten mikrokontrollereilla.
Miksiköhän toi VB6 kääntäjä tekee tällaista. Siis selvästikin kutsumista varten lataillaan muuttujista arvoja, mutta jos ne kerran pusketaan pinoon lataamisen jälkeen, niin miksi jokainen vedetään eri rekisterin kautta?
Ja miksi tossa ei ole push aina heti lea:n jälkeen vaan se ennakoi yhden lea:n
Jos arvailla pitäisi niin liittyy jotenkin koodin optimointiin.
loc_00402081: lea edx, var_74 loc_00402084: lea eax, var_A4 loc_0040208A: push edx loc_0040208B: lea ecx, var_84 loc_00402091: push eax loc_00402092: push ecx loc_00402093: mov var_9C, 00401920h ; "\el_m_draw_varmuuskoodit.rnd" loc_0040209D: mov var_A4, 00000008h loc_004020A7: call [00401080h]
Hei, Grez.
Tuo esittämäsi koodinpätkä on VB6:ssa seuraava:
("TunnisteKoodi" unohtui siihen kun kopsasin sen piirustusohjelmasta)
Selventäneekö tämä tuon konekielisen?(assembler?)-koodin rakennetta..
Private Sub Command1_Click() Dim Koodi As String n = FreeFile Open CurDir & "\el_m_draw_varmuuskoodit.rnd" For Random As n Len = 1 pit = LOF(n) OtaKoodiTaulukosta n, pit, TunnisteKoodi & Text1.Text, Koodi Close n Text2.Text = Koodi End Sub
Näyttää tosiaan siltä, että koodin tekevän algoritmin voi kopsata .exe failista kun homman osaa. Tämä kaikki selventää ajatuksiani, kiitoksia.
lainaus:
Selventäneekö tämä tuon konekielisen?(assembler?)-koodin rakennetta..
Ei siinä sinänsä mitään epäselvää ollut. En vaan ole itse hirveän harjaantunut x86-koodin lukija niin koko ohjelman toiminnan selvittämiseen mennisi minulta aika paljon aikaa.
Tuo kyseinen laittamani pätkä vastaa suunnilleen osaa:
& "\el_m_draw_varmuuskoodit.rnd"
Curdir oli aikaisemmin koodissa ja open on myöhemmin. Sinänsähän yhdestä VB6 -koodirivistä tulee helposti 20-50 riviä assemblyä, joten ei tuollaisen koodin toiminnan selvittäminen tietenkään hirveän nopeaa ole, varsinkaan jos ei ole harjaantunut assemblyn lukija. Itse piti ihan aluksi tarkistaa mitä tarkoittaa lea :D
Mutta tosiaan ohjelmien murtamiseen harjaantunut hakkeri murtaa tuollaisen suojauksen nopeasti. Kuten ihan alussa sanoin, niin mitään käyttäjän tietokoneella ajettavaa koodia ei ole mahdollista suojata niin, että sitä ei voisi murtaa. Tietenkin työmäärän kasvaessa hyöty pienenee. Ja kannattaa muistaa myös että ohjelmien luvaton käyttö on laitonta, joten asialliset firmat ei sitä tee vaikka ei mitään suojausta olisikaan. Tietty jos pääasiallinen asiakaskunta on pienet pajat ja ohjelman hinta on hirmuinen, niin suojaus voi olla tärkeää.
Heippa taas!
tässä vielä hieman aiheesta...
'Form1 (lisää vb-koodi ohjelmaasi, käännä ja tallenna .exe tikulle) Private Declare Function GetDriveType Lib "kernel32" _ Alias "GetDriveTypeA" (ByVal nDrive As String) As Long Dim xhttp As MyHttpClass Private Sub Form_Load() TsekkaaOikeudet '... End Sub Private Sub TsekkaaOikeudet() If Not Internet.Connected Then MsgBox "Ei Internet-yhteyttä!" End End If Dim root As String root = Left(App.Path, 3) Dim msg As String If GetDriveType(root) <> 2 Then msg = Space(30) & "EI KÄYTTÖOIKEUTTA!" 'Esim. End If Dim driveletter As String driveletter = UCase(Left(root, 1)) Select Case driveletter Case "A", "B" msg = Space(30) & "EI KÄYTTÖOIKEUTTA!" Case Else End Select Dim fullpath As String fullpath = root & "mydrive.dat" If Dir(fullpath) <> "" Then Kill fullpath End If Shell "cmd /C dir root >" & fullpath, vbHide JumpBack: On Error Resume Next flen& = FileLen(fullpath) If Err <> 0 Then Err.Clear On Error GoTo 0 GoTo JumpBack End If Do While FileLen(fullpath) = 0 DoEvents: Loop Dim rootdata As String Open fullpath For Input As #1 Do While Not LOF(1) Input #1, rootdata If InStr(rootdata, "Aseman sarjanumero on") > 0 Then rootdata = Trim(Replace(rootdata, _ "Aseman sarjanumero on", "")) Exit Do End If Loop Close #1 Shell "cmd /C del " & fullpath, vbHide Set xhttp = New MyHttpClass If xhttp.OpenHTTP("localhost") Then Dim response As String response = xhttp.SendRequest( _ "/ohjelmisto/tsekkaa.php?lisenssi=" & rootdata, "POST") If Trim(response) = "nolicense" Then msg = Space(30) & "EI KÄYTTÖOIKEUTTA!" ElseIf Trim(response) = "expired" Then msg = Space(25) & "KÄYTTÖOIKEUS PÄÄTTYNYT!" ElseIf InStr(response, "daysleft") > 0 Then Dim daysleft As String daysleft = Trim(Replace(response, "daysleft", "")) MsgBox "KÄYTTÖOIKEUS VANHENEE " & daysleft & " PÄIVÄN KULUTTUA!" End If End If Set xhttp = Nothing If msg <> "" Then Dim msgresult As Long msg = msg & vbCrLf & vbCrLf & _ "Tahdotko siirtyä ohjelman toimittajan Internet sivustolle?" msgresult = MsgBox(msg, vbYesNo, Me.Caption) If msgresult = 6 Then Shell "explorer http://www.palvelimesi.com/sivustosi", vbNormalFocus End If End End If End Sub
'Module1 Private Declare Function InternetGetConnectedState Lib _ "wininet.dll" (ByRef lpSFlags As Long, _ ByVal dwReserved As Long) As Long Public Type InternetConnection Connected As Boolean End Type Public Function Internet() As InternetConnection Dim cType As Long Internet.Connected = InternetGetConnectedState(cType, 0&) End Function
'MyHttpClass (Class Module) Option Explicit Public Enum ePort INTERNET_DEFAULT_HTTP_PORT = 80 INTERNET_DEFAULT_HTTPS_PORT = 443 End Enum Private Const INTERNET_OPEN_TYPE_DIRECT = 1 Private Const INTERNET_SERVICE_HTTP = 3 Private Const INTERNET_FLAG_PRAGMA_NOCACHE = &H100 Private Const INTERNET_FLAG_KEEP_CONNECTION = &H400000 Private Const INTERNET_FLAG_SECURE = &H800000 Private Const INTERNET_FLAG_FROM_CACHE = &H1000000 Private Const INTERNET_FLAG_NO_CACHE_WRITE = &H4000000 Private Const INTERNET_FLAG_RELOAD = &H80000000 Private Const BUFFER_LENGTH As Long = 1024 Private Declare Function InternetOpen Lib "wininet.dll" Alias "InternetOpenA" _ (ByVal Agent As String, ByVal AccessType As Long, ByVal ProxyName As String, _ ByVal ProxyBypass As String, ByVal Flags As Long) As Long Private Declare Function InternetConnect Lib "wininet.dll" Alias _ "InternetConnectA" (ByVal hInternetSession As Long, ByVal ServerName As String, _ ByVal ServerPort As Integer, ByVal UserName As String, ByVal Password As _ String, ByVal Service As Long, ByVal Flags As Long, ByVal Context As Long) As _ Long Private Declare Function InternetCloseHandle Lib "wininet.dll" (ByVal hInet As _ Long) As Boolean Private Declare Function InternetReadFile Lib "wininet.dll" (ByVal hConnect As _ Long, ByVal Buffer As String, ByVal NumberOfBytesToRead As Long, _ NumberOfBytesRead As Long) As Boolean Private Declare Function HttpOpenRequest Lib "wininet.dll" Alias _ "HttpOpenRequestA" (ByVal hHttpSession As Long, ByVal Verb As String, ByVal _ ObjectName As String, ByVal Version As String, ByVal Referer As String, ByVal _ AcceptTypes As Long, ByVal Flags As Long, Context As Long) As Long Private Declare Function HttpSendRequest Lib "wininet.dll" Alias _ "HttpSendRequestA" (ByVal hHttpRequest As Long, ByVal Headers As String, ByVal _ HeadersLength As Long, ByVal sOptional As String, ByVal OptionalLength As Long) _ As Boolean Private hHTTP As Long Private hConnection As Long Private Const FIELDS_BUFFER_LENGTH As Long = 10 Private Const FIELDS_NAME_INDEX As Long = 0 Private Const FIELDS_VALUE_INDEX As Long = 1 Private DontEncode(255) As Boolean Private FieldCount As Long Private mFields() As String Public Property Let Fields(Name As String, Value As String) mFields(FIELDS_VALUE_INDEX, GetFieldIndex(Name, True)) = Value End Property Public Property Get Fields(Name As String) As String Dim l As Long l = GetFieldIndex(Name, False) If l > -1 Then Fields = mFields(FIELDS_VALUE_INDEX, l) End If End Property Public Function OpenHTTP(Server As String, Optional Port As ePort = _ INTERNET_DEFAULT_HTTP_PORT, Optional UserName As String, Optional Password As _ String) As Boolean CloseHTTP hHTTP = InternetOpen("HTTP Client", INTERNET_OPEN_TYPE_DIRECT, UserName, _ Password, 0) If hHTTP <> 0 Then hConnection = InternetConnect(hHTTP, Server, INTERNET_DEFAULT_HTTP_PORT, _ UserName, Password, INTERNET_SERVICE_HTTP, 0, 0) If hConnection <> 0 Then OpenHTTP = True Else InternetCloseHandle hHTTP hHTTP = 0 End If End If End Function Public Sub CloseHTTP() If hConnection <> 0 Then InternetCloseHandle hConnection End If hConnection = 0 If hHTTP Then InternetCloseHandle hHTTP End If hHTTP = 0 End Sub Public Function SendRequest(ByVal File As String, Optional Method As String = _ "GET", Optional Referer As String, Optional Reload As Boolean = True) As String Dim hRequest As Long Dim r As Boolean Dim Buffer As String Dim Header As String Dim Request As String Dim POSTData As String Dim response As String Dim Read As Long Dim Flags As Long Method = UCase$(Method) Request = BuildRequest Buffer = Space$(BUFFER_LENGTH) If Len(Request) > 0 Then If Method = "POST" Then Header = "Content-Type: application/x-www-form-urlencoded" POSTData = Request Else File = File & "?" & Request End If End If If Reload Then Flags = Flags Or INTERNET_FLAG_PRAGMA_NOCACHE Or INTERNET_FLAG_RELOAD End If hRequest = HttpOpenRequest(hConnection, Method, File, "HTTP/1.1", "", 0, _ Flags, 0) If hRequest <> 0 Then If HttpSendRequest(hRequest, Header, Len(Header), POSTData, _ Len(POSTData)) Then r = InternetReadFile(hRequest, Buffer, BUFFER_LENGTH, Read) While r And (Read <> 0) response = response & Left$(Buffer, Read) r = InternetReadFile(hRequest, Buffer, BUFFER_LENGTH, Read) Wend End If InternetCloseHandle hRequest End If SendRequest = response End Function Private Function GetFieldIndex(Name As String, Optional Add As Boolean) As Long Dim l As Long For l = 0 To FieldCount - 1 If StrComp(Name, mFields(FIELDS_NAME_INDEX, l), vbTextCompare) = 0 Then GetFieldIndex = l Exit Function End If Next If Add Then If FieldCount = UBound(mFields, 2) Then ReDim Preserve mFields(1, UBound(mFields, 2) + FIELDS_BUFFER_LENGTH) End If mFields(FIELDS_NAME_INDEX, FieldCount) = Name GetFieldIndex = FieldCount FieldCount = FieldCount + 1 Else GetFieldIndex = -1 End If End Function Private Function BuildRequest() As String Dim l As Long Dim s As String For l = 0 To FieldCount - 1 s = s & URLEncode(mFields(FIELDS_NAME_INDEX, l)) & "=" & _ URLEncode(mFields(FIELDS_VALUE_INDEX, l)) & "&" Next If Len(s) > 0 Then BuildRequest = Left$(s, Len(s) - 1) End If End Function Public Function URLEncode(Data As String) As String Dim l As Long Dim b() As Byte Dim s As String Dim c As String b = Data For l = 0 To UBound(b) Step 2 If DontEncode(b(l)) Then s = s & Chr(b(l)) Else c = Hex(b(l)) While Len(c) < 2 c = "0" & c Wend s = s & "%" & c End If Next URLEncode = s End Function Private Sub Class_Initialize() Dim l As Long ReDim mFields(1, FIELDS_BUFFER_LENGTH) For l = Asc("0") To Asc("9") DontEncode(l) = True Next For l = Asc("a") To Asc("z") DontEncode(l) = True Next For l = Asc("A") To Asc("Z") DontEncode(l) = True Next End Sub Private Sub Class_Terminate() Erase mFields End Sub
<?php //yksinkertainen malli (tsekkaa.php) //(lätki tämä web-hotellisi servulle) if(!isset($_POST["lisenssi"])) { $serial = $_GET["lisenssi"]; $mysql_host = "mysql_palvelimesi"; //(web-hotelleissa monesti: localhost) $mysql_user = "mysql_tunnuksesi"; $mysql_password = "mysql_salasanasi"; $mysql_database = "Tietokanta"; $conn = mysql_connect($mysql_host, $mysql_user, $mysql_password) or die(mysql_error()); mysql_select_db($mysql_database, $conn) or die(mysql_error()); $sql = "SELECT * FROM taulu WHERE serial='$serial'"; $result = mysql_query($sql, $conn) or die(mysql_error()); if (mysql_fetch_row($result)==null) { echo "nolicense"; } else { $today = date( "Y-m-d H:m:s"); if (mysql_result($result, 0, "started") == null || mysql_result($result, 0, "started") == "0000-00-00 00:00:00" ) { $datex = strtotime(date("Y-m-d H:m:s", strtotime($today)) . "+1 year"); $datex = date('Y-m-d', $datex); $sql = "UPDATE taulu SET started='$datex' WHERE serial='$serial'"; $result2 = mysql_query($sql, $conn) or die(mysql_error()); } else { $expiration = mysql_result($result, 0, "started"); $fullDays = date_diff($today, $expiration); if ((int)$fullDays <= 0) { echo "expired"; } elseif((int)$fullDays > 0 && (int)$fullDays < 11) { echo "daysleft" + $fullDays; } } } } function date_diff($start, $end="NOW") { $sdate = strtotime($start); $edate = strtotime($end); $time = $edate - $sdate; if($time>=86400) { $pday = ($edate - $sdate) / 86400; $preday = explode('.',$pday); $timeshift = $preday[0]; } return $timeshift; } ?>
jtha kirjoitti:
Hyviä mielipiteitä.
Kuitenkin, jos joku viitsii vaikka murtaa tuon, niin ilmoittakaa kauanko meni, tarjoan kaljan vaikka sen peliprojektin julkistamistilaisuudessa.
Selvitin käyttämäsi algoritmin. Sen muuttaminen käänteiseksi (jos tarvitsee) pitäisi olla triviaalia, enkä nyt kerkeä tehdä sitä. Joku tunti meni, ois menny paljon vähemmän jos toi olis ollut koodattu millä tahansa muulla kuin VB:llä (joka sylkee ihan käsittämätöntä binääriä)
http://fpaste.dy.fi/lF/dl binääri. oma "koodaajani", joka tekee saman kuin sinun ohjelmasi
Sorsa:
#include <stdio.h> #include <stdlib.h> #define RND_FILE "el_m_draw_varmuuskoodit.rnd" #define MAGIC1 0x0c350 #define MAGIC2 0x0e7ef0 int main(int arcg, char *argv[]) { FILE *rndf; int sz; unsigned char *rnd; printf("OPtemp.exe keygen\n"); if(!(rndf = fopen(RND_FILE, "r"))) { printf("%s ei aukee\n", RND_FILE); return -1; } fseek(rndf, 0, SEEK_END); sz = ftell(rndf); /*if(sz != 0x0f4240) { printf("väärän kokoinen %s\n", RND_FILE); return -1; }*/ rnd = malloc(sz); fseek(rndf, 0, SEEK_SET); fread(rnd, 1, sz, rndf); fclose(rndf); while(1) { unsigned int i, idx, ebp18, edi; unsigned char input[256]; printf("nimi kiitos: "); scanf("%255s", input); for(ebp18 = i = 0; i < strlen(input); i++) ebp18 = input[i]%0x7b + ebp18; for(i = 0; i < strlen(input); i++) { idx = ((input[i]*(i+1) + ebp18) % sz); edi = MAGIC1; while(edi <= MAGIC2) { idx = (edi - rnd[idx]*(i+1)) % sz; idx = (edi + rnd[idx]*(i+1)) % sz; edi += MAGIC1; } printf("%c", (rnd[idx] % 0x64) + 0x21); } printf("\n"); } return 0; }
HUOM!! Jos koodattuun merkkijonoon tulee erikoismerkkejä (esimerkiksi ääkkösiä), niin jtha:n ohjelma ei näytä niitä oikein.
Kiitos, Deffi. Kaljalasku paisuu.
Nea, kiitos koodista ja vinkistä. Monesti tuolla teollisuudessa ei ole nettiä käytössä ja ohjelmaa käytetään mm. halleissa ja tuotantotiloissa, joten yritän vielä tsempata ja hankaloittaa kopiointia tikulta toiseen tai tikulta koneelle.
Tai sitten unohdat natseilun ja paat koko roskan avoimena lähdekoodina jakoon. "Teollisuuden hallit ja tuotantotilat" erityisesti kuulostaa siltä, että joku saattais tuohon jossain vaiheessa haluta jotain räätälöintiä, sitähän voit sitten myydä hyvään tuntihintaan.
"Teollisuuden hallit ja tuotantotilat" kuulostaa vähän siltä, ettei kukaan lähde kräkkäämään sellaista. Varsinkin jos ohjelma on suomenkielinen. Tulevaisuus näyttää kräkkerien kannalta muutenkin tosi tylsältä, koska kaikki softa on menossa vapaampaan ja ilmaiseen suuntaan! :(
[OHI AIHEEN]
En ole omalla alallani moista huomannut? Kaikesta joutuu maksamaan, niin suunnittelu- kuin ohjelmointisoftistakin(logiikkaohjelmointi).
En osaa kuvitella, että tässä markkinataloudessa kukaan tekisi töitä ilmaiseksi tai jakaisi tuotteita(softaa) ilmaiseksi, ellei siihen sisälly joku muuta kautta saavutettava hyöty.
PS:
En haluaisi viittauksia natsismista tai muuta arvostelua tämän vuoksi. Eikös Ohjelmointiputkan hieno idea ole tässä, että keskustelemme asioista ja opimmekin jotakin. Pysytään vaan asiassa ja jos jokin yksityiskohta ottaa aiheessa/keskustelijassa päähän niin ei tarvitse osallistua ko. keskusteluun - vai mitä?
[/OHI AIHEEN]
Avoimen lähdekoodin ohjelmaa voi aivan hyvin myös myydä, ja lisäksi voi veloittaa tuesta ja pyydettyjen muutosten toteuttamisesta.
jtha kirjoitti:
En haluaisi viittauksia natsismista tai muuta arvostelua tämän vuoksi.
Jep jep, natseilullahan toki ei voi viitata muuhun kuin natsismiin, siksi kielioppinatsikin on aina tällaisia http://www.youtube.com/watch?v=N4vf8N6GpdM
Itse en ainakaan tee open sourcea teollisuuteen (tai ylipäänsä ammattikayttöön). Niillä on varaa myös maksaa,ja vähentävät vielä verotuksessa. Sen päälle sitten vielä tuki,päivitykset ja räätälöinnit. Lahjoitan ennemmin vaikka spr:än tilille jos siltä tuntuu.
Aihe on jo aika vanha, joten et voi enää vastata siihen.